* Copyright (c) 1982, 1990 The Regents of the University of California.
* %sccs.include.redist.c%
* @(#)if_apx.c 7.1 (Berkeley) %G%
* Driver for SGS-THOMSON MK5025 based Link level controller.
* The chip will do LAPB in hardware, although this driver only
* attempts to use it for HDLC framing.
* Driver written by Keith Sklower, based on lance AMD7990
* driver by Van Jacobsen, and information graciously supplied
* by the ADAX corporation of Berkeley, CA.
#include "netccitt/x25.h"
int apxprobe(), apxattach(), apxstart(), apx_uprim(), apx_meminit();
int apxinit(), x25_ifoutput(), apxioctl(), apxreset();
void apx_ifattach(), apxinput(), apxintr(), apxtint(), apaxrint();
caddr_t apx_device
; /* e.g. isa_device */
u_short apx_csr4
; /* byte gender, set in mach dep code */
struct apc_reg
*apx_reg
; /* control regs for both subunits */
struct apc_mem
*apx_hmem
; /* Host addr for shared memory */
struct apc_mem
*apx_dmem
; /* Device (chip) addr for shared mem */
struct sgcp
*apx_sgcp
; /* IO control port for this subunit */
struct apc_modes apx_modes
; /* Parameters, as amended by ioctls */
int apx_rxnum
; /* Last receiver dx we looked at */
int apx_txnum
; /* Last tranmistter dx we stomped on */
int apx_txcnt
; /* Number of packets queued for tx*/
} apx_softc
[2 * NAPX
], *apx_lastsoftc
= apx_softc
;
/* default operating paramters for devices */
struct apc_modes apx_default_modes
= {
{ 1, /* apm_sgob.lsaddr; */
3, /* apm_sgob.rsaddr; */
-SGMTU
, /* apm_sgob.n1; */
((-10)<<8), /* apm_sgob.n2_scale; */
-1250, /* apm_sgob.t1; */
-10000, /* apm_sgob.t3; */
IFT_X25
, /* apm_iftype; */
/* Begin bus & endian dependence */
#include "i386/isa/if_apxreg.h"
#include "i386/isa/isa_device.h"
struct isa_driver apxdriver
= {
apxprobe
, apxattach
, "apx",
#define SG_RCSR(apx, csrnum) \
(outw(&(apx->apx_sgcp->sgcp_rap), csrnum << 1),
inw(&(apx
->apx_sgcp
->sgcp_rdp
))
#define SG_WCSR(apx, csrnum, data) \
(outw(&(apx->apx_sgcp->sgcp_rap), csrnum << 1),
outw(&(apx
->apx_sgcp
->sgcp_rdp
), data
))
#define APX_RCSR(apx, csrname) inb(&(apx->apx_reg->csrname))
#define APX_WCSR(apx, csrname, data) outb(&(apx->apx_reg->csrname), data)
#define TIMO 10000 /* used in apx_uprim */
register struct isa_device
*id
;
int moffset
, subunit
, unit
= id
->id_unit
<< 1;
struct apc_reg
*reg
= id
->id_iobase
;
register struct apx_softc
*apx
= apx_softc
+ unit
;
/* Set and read DTR defeat in channel 0 to test presence of apc */
outb(®
->axr_altmode
, 4);
if (inb(®
->axr_altmode
) == 0)
return 0; /* No board present */
for (subunit
= 0; subunit
< 2; subunit
++, apx
++) {
/* Set and read DTR mode to test present of SGS thompson chip */
apx
->apx_if
.if_unit
= unit
++;
apx
->apx_sgcp
= reg
->axr_sgcb
+ subunit
;
if ((SG_RCSR(apx
, 5) & 0xff08) != 0x08)) {
apxerror(apx
, "no mk5025 for channel", subunit
);
moffset
= subunit
? id
->id_msize
>> 1 : 0;
apx
->apx_hmem
= (struct apc_mem
*) (id
->id_maddr
+ moffset
);
apx
->apx_dmem
= (struct apc_mem
*) (moffset
);
apx
->apx_modes
= apx_default_modes
;
apx
->apx_device
= (caddr_t
) id
;
apx
->apx_csr4
= 0x0110; /* no byte swapping for PC-AT */
register struct isa_device
*id
;
int unit
= id
->id_unit
+ id
->id_unit
;
/* End bus & endian dependence */
* Interface exists: make available by filling in network interface
* record. System will initialize the interface when it is ready
register struct ifnet
*ifp
= &(apx_softc
[unit
].apx_if
);
* Initialize ifnet structure
if (apx_softc
[unit
].apx_device
== 0)
ifp
->if_output
= x25_ifoutput
;
ifp
->if_start
= apxstart
;
ifp
->if_ioctl
= apxioctl
;
ifp
->if_reset
= apxreset
;
ifp
->if_type
= apx_default_modes
.axp_iftype
;
* Initialization of interface
struct ifnet
*ifp
= &apx_softc
[unit
].apx_if
;
ifp
->if_flags
&= ~(IFF_RUNNING
|IFF_OACTIVE
);
if (apxreset(unit
) && (ifp
->if_flags
& IFF_UP
)) {
ifp
->if_flags
|= IFF_RUNNING
;
register struct apx_softc
*apx
= &apx_softc
[unit
^ 1];
u_char apm_apxmode
= 0, apm_apxaltmode
= 0;
#define MODE(m) (m |= apx->apx_modes.m << ((apx->apx_if.if_unit & 1) ? 1 : 0))
APX_WCSR(apx
, axr_mode
, apm_apxmode
);
APX_WCSR(apx
, axr_altmode
, apm_apxaltmode
);
apx
->apx_txnum
= apx
->apx_rxnum
= apx
->apx_txcnt
= apx
->apx_rxnt
= 0;
if (apx_uprim(apx
, SG_STOP
, "stop") ||
!(apx
->apx_if
.if_flags
& IFF_UP
))
apx_meminit(apx
->apc_mem
, apx
); /* also sets CSR2 */
SG_WCSR(apx
, 3, (int)apx
->apx_dmem
);
SG_WCSR(apx
, 4, apx
->apx_csr4
);
if (apx_uprim(apx
, SG_INIT
, "init request")) ||
apx_uprim(apx
, SG_STAT
, "status request") ||
apx_uprim(apx
, SG_TRANS
, "transparent mode"))
SG_WCSR(apx
, 0, SG_INEA
);
apx_uprim(apx
, request
, ident
)
register struct apx_softc
*apx
;
int reply
= SG_RCSR(apx
, 1);
SG_WCRS(1, x8040
); /* Magic! */
SG_WCSR(apx
, 1, request
| SG_UAV
);
if (timo
>= TIMO
| reply
& 0x8000) {
apxerror(apx
, ident
, reply
);
} while (reply
& SG_UAV
);
register struct apc_mem
*apc
;
register struct apc_mem
*apcbase
= apx
->apx_dmem
;
#define LOWADDR(e) (((u_long)&(apcbase->(e))) & 0xffff)
#define HIADDR(e) ((((u_long)&(apcbase->(e))) >> 16) & 0xff)
#define SET_SGDX(dx, f, a, b, m) \
{ (dx).sgdx_addr = LOWADDR(a); (dx).sgdx_bcnt = (b);\
(dx).sgdx_mcnt = (m); (dx).sgdx_flags = (f) | HIADDR(a); }
bzero((caddr_t
)apc
, LOWADDR(apc_rxmd
[0]));
apc
->apc_mode
= 0x8040; /* 2 flag spacing, trans mode, 16bit FCS */
apc
->apc_sgop
= apx
->apx_modes
.apm_sgop
;
apc
->apc_rlen
= SG_RLEN
| HIADDR(apc_rxmd
[0]);
apc
->apc_rdra
= LOWADDR(apc_rxmd
[0]);
apc
->apc_rlen
= SG_TLEN
| apx
->apx_modes
.apm_txwin
|HIADDR(apc_txmd
[0]);
apc
->apc_tdra
= LOWADDR(apc_txmd
[0]);
SET_SGDX(apc
->apc_rxtid
, SG_OWN
, apc_rxidbuf
, -SGMTU
, 0);
SET_SGDX(apc
->apc_txtid
, 0, apc_txidbuf
, -SGMTU
, 0);
apc
->apc_stathi
= HIADDR(apc_sgsb
);
apc
->apc_statlo
= LOWADDR(apc_sgsb
);
for (i
= 0; i
< SGRBUF
; i
++)
SET_SGDX(apc
->apc_rxmd
[i
], SG_OWN
, apc_rbuf
[i
][0], -SGMTU
, 0)
for (i
= 0; i
< SGTBUF
; i
++)
SET_SGDX(apc
->apc_txmd
[i
], SG_TUI
, apc_tbuf
[i
][0], 0, 0)
SG_WCSR(apx
, 2, SG_UIE
| SG_PROM
| HIADDR(apc_mode
));
* Start output on interface. Get another datagram to send
* off of the interface queue, and copy it to the interface
* before starting the output.
register struct apx_softc
*apx
= &apx_softc
[ifp
->if_unit
];
register struct sgdx
*dx
;
struct apc_mem
*apc
= apx
->apx_mem
;
if ((ifp
->if_flags
& IFF_RUNNING
) == 0)
dx
= apc
->apc_txmd
+ apc
->apc_txnum
;
if (dx
->sgdx_flags
& SG_OWN
)
IF_DEQUEUE(&ifp
->if_snd
, m
);
len
= min(m
->m_pkthdr
.len
, SGMTU
);
m_copydata(m
, 0, len
, apc
->apc_txbuf
[apx
->apx_txnum
]);
dx
->sgdx_flags
= SG_OWN
| SG_TUI
| (0xff & dx
->sgdx_flags
);
SG_WCSR(apx
, 0, SG_INEA
| SG_TDMD
);
if (++apx
->apx_txnum
>= SGTBUF
)
} while (++apx
->apx_txcnt
< SGTBUF
);
ifp
->if_flags
|= IFF_OACTIVE
;
register struct apx_softc
*apx
= apx_lastsoftc
;
struct apx_softc
*apxlim
= apx_softc
+ NAPX
+ NAPX
;
if (apx
->ap_if
.if_flags
& IFF_UP
)
/* Try to turn off interrupt cause */
while ((reply
= SG_RCSR(apx
, 0)) & 0xff) {
SG_WCSR(apx
, 0, SG_INEA
| 0xfe);
if (reply
& (SG_MERR
|SG_TUR
|SG_ROR
)) {
apxerror(apx
, "mem, rx, or tx error", reply
);
apxinit(apx
->apx_if
.if_unit
);
} while (apx
!= apx_lastsoftc
);
register struct apx_softc
*apx
;
register struct apc_mem
*apc
= apx
->apx_mem
;
if ((i
= apx
->apx_txnum
- apx
->apx_txcnt
) < 0)
if (apc
->apc_txmd
[i
].sgdx_flags
& SG_OWN
) {
apx
->apx_if
.if_flags
&= ~IFF_OACTIVE
;
} while (--apx
->apx_txcnt
> 0);
register struct apx_softc
*apx
;
register struct apc_mem
*apc
= apx
->apx_mem
;
register struct sgdx
*dx
= apc
->apc_rxmd
+ apx
->apx_rxnum
;
dx = ++apx->apx_rxnum == SGRBUF ? &apc->apc_rxmd[apx->apx_rxnum = 0] : dx + 1;
* Out of sync with hardware, should never happen?
if (dx
->sgdx_flags
& SG_OWN
) {
apxerror(apx
, "out of sync");
* Process all buffers with valid data
while ((dx
->sgdx_flags
& SG_OWN
) == 0) {
if ((dx
->sgdx_flags
& (SG_SLF
|SG_ELF
)) != (SG_SLF
|SG_ELF
)) {
* Find the end of the packet so we can see how long
* it was. We still throw it away.
apxerror(apx
, "chained buffer", ds
->sgdx_flags
);
dx
->sgdx_flags
= SG_OWN
| (0xff&dx
->sgdx_flags
);
} while (!(dx
->sgdx_flags
& (SG_OWN
|SG_SLF
|SG_ELF
)));
* If search terminated without successful completion
* we reset the hardware (conservative).
if ((dx
->sgdx_flags
& (SG_OWN
|SG_SLF
|SG_ELF
)) !=
apxreset(apx
->apx_if
.if_unit
);
apxinput(&apx
->apx_if
, apc
->apc_rbuf
[apx
->apx_rxnum
],
dx
->sgdx_flags
= SG_OWN
| (0xff & dx
->sgdx_flags
);
apxinput(ifp
, buffer
, len
)
register struct ifnet
*ifp
;
register struct ifqueue
*inq
;
struct mbuf
*m
, *apxget();
extern struct ifqueue hdintrq
, ipintrq
;
register u_char
*cp
= (u_char
*)buffer
;
if (cp
[0] == 0xff && cp
[1] == 0x3) {
/* This is a UI HDLC Packet, so we'll assume PPP
protocol. for now, IP only. */
m
= apxget(buffer
, len
, 0, ifp
);
* Routine to copy from board local memory into mbufs.
apxget(buf
, totlen
, off0
, ifp
)
struct mbuf
*top
= 0, **mp
= &top
;
register int off
= off0
, len
;
cp
+= off
+ 2 * sizeof(u_short
);
totlen
-= 2 * sizeof(u_short
);
MGETHDR(m
, M_DONTWAIT
, MT_DATA
);
m
->m_pkthdr
.len
= totlen
;
MGET(m
, M_DONTWAIT
, MT_DATA
);
len
= min(totlen
, epkt
- cp
);
m
->m_len
= len
= min(len
, MCLBYTES
);
* Place initial small packet/header at end of mbuf.
if (top
== 0 && len
+ max_linkhdr
<= m
->m_len
)
m
->m_data
+= max_linkhdr
;
bcopy(cp
, mtod(m
, caddr_t
), (unsigned)len
);
* Process an ioctl request.
register struct ifnet
*ifp
;
register struct ifaddr
*ifa
= (struct ifaddr
*)data
;
int s
= splimp(), error
= 0;
struct apx_softc
*apx
= &apx_softc
[ifp
->if_unit
];
error
= hd_ctlinput(PRC_IFUP
, ifa
->ifa_addr
);
ifa
->ifa_rtrequest
= x25_rtrequest
;
if (((ifp
->if_flags
& IFF_UP
) == 0 &&
(ifp
->if_flags
& IFF_RUNNING
)) ||
(ifp
->if_flags
& IFF_UP
) &&
(ifp
->if_flags
& IFF_RUNNING
) == 0)
if ((ifp
->if_flags
& IFF_UP
) == 0)
apx
->apx_modes
= *(struct apx_modes
*)data
;
register struct apx_softc
*apx
;
log(LOG_WARNING
, "apc%d: %s, stat=0x%x\n",
apx
->apx_if
.if_unit
, msg
, data
);