* Copyright (c) 1982, 1990 The Regents of the University of California.
* %sccs.include.redist.c%
* @(#)if_apx.c 7.4 (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 "net/if_types.h"
#include "netccitt/x25.h"
int x25_rtrequest(), x25_ifoutput();
int apxprobe(), apxattach(), apxstart(), apx_uprim(), apx_meminit();
int apxinit(), apxoutput(), apxioctl(), apxreset(), apxdebug
= 1;
void apx_ifattach(), apxtest(), apxinput(), apxintr(), apxtint(), apxrint();
caddr_t apx_device
; /* e.g. isa_device, vme_device, etc. */
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 */
int apx_flags
; /* Flags specific to this driver */
#define APXF_CHIPHERE 0x01 /* mk5025 present */
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*/
u_short apx_csr4
; /* byte gender, set in mach dep code */
struct apc_modes apx_modes
; /* Parameters, as amended by ioctls */
/* 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 */
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
= (struct apc_reg
*)id
->id_iobase
;
register struct apx_softc
*apx
= apx_softc
+ unit
;
for (subunit
= 0; subunit
< 2; subunit
++, apx
++) {
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_device
= (caddr_t
) id
;
apx
->apx_sgcp
= reg
->axr_sgcp
+ subunit
;
apx
->apx_csr4
= 0x0210; /* no byte swapping for PC-AT */
apx
->apx_modes
= apx_default_modes
;
apx
->apx_if
.if_unit
= unit
++;
register struct isa_device
*id
;
int unit
= id
->id_unit
<< 1;
/* 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
= apxoutput
;
ifp
->if_start
= apxstart
;
ifp
->if_ioctl
= apxioctl
;
ifp
->if_reset
= apxreset
;
ifp
->if_type
= apx_default_modes
.apm_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_WCSR(apx
, axr_ccr
, 0xB0); /* select ctr 2, write lsb+msb, mode 0 */
APX_WCSR(apx
, axr_cnt2
, 0x1);
APX_WCSR(apx
, axr_cnt2
, 0x0);
APX_WCSR(apx
, axr_ccr
, 0xE8); /* latch status, ctr 2; */
return (APX_RCSR(apx
, axr_cnt2
));
register struct apx_softc
*apx
;
if ((apx
->apx_if
.if_unit
& 1) == 0 && (i
= apxctr(apx
)) == 0)
apxerror(apx
, "no response from timer chip", 0);
if (SG_RCSR(apx
, 1) & 0x8000)
SG_WCSR(apx
, 4, apx
->apx_csr4
);
apxerror(apx
, "counter 2 value", i
);
apxerror(apx
, "mk5025 csr4 value", SG_RCSR(apx
, 4));
SG_WCSR(apx
, 5, 0x08); /* Set DTR mode in SGS thompson chip */
if (((i
= SG_RCSR(apx
, 5)) & 0xff08) != 0x08)
apxerror(apx
, "no mk5025, csr5 high bits are", i
);
apx
->apx_flags
|= APXF_CHIPHERE
;
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
= 0;
if (apx_uprim(apx
, SG_STOP
, "stop") ||
!(apx
->apx_if
.if_flags
& IFF_UP
))
apx_meminit(apx
->apx_hmem
, 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_WCSR(apx
, 1, 0x8040); /* 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_hmem
;
if ((ifp
->if_flags
& IFF_RUNNING
) == 0)
dx
= apc
->apc_txmd
+ apx
->apx_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_tbuf
[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
;
for (apx
= apx_softc
+ NAPX
+ NAPX
; --apx
>= apx_softc
;) {
if (apx
->apx_flags
& APXF_CHIPHERE
)
/* 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
);
register struct apx_softc
*apx
;
register struct apc_mem
*apc
= apx
->apx_hmem
;
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_hmem
;
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", dx
->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
, *m_devget();
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
= m_devget(buffer
, len
, 0, ifp
, (void (*)())0);
* 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
];
ifa
->ifa_rtrequest
= x25_rtrequest
;
ifp
->if_output
= x25_ifoutput
;
error
= hd_ctlinput(PRC_IFUP
, ifa
->ifa_addr
);
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 apc_modes
*)data
;
register struct apx_softc
*apx
;
log(LOG_WARNING
, "apc%d: %s, stat=0x%x\n",
apx
->apx_if
.if_unit
, msg
, data
);
* For debugging loopback activity.
static char pppheader
[4] = { -1, 3, 0, 0x21 };
apxoutput(ifp
, m
, dst
, rt
)
register struct ifnet
*ifp
;
* Queue message on interface, and start output if interface
int s
= splimp(), error
= 0;
M_PREPEND(m
, sizeof pppheader
, M_DONTWAIT
);
bcopy(pppheader
, mtod(m
, caddr_t
), sizeof pppheader
);
if (IF_QFULL(&ifp
->if_snd
)) {
/* printf("%s%d: HDLC says OK to send but queue full, may hang\n",
ifp->if_name, ifp->if_unit);*/
IF_ENQUEUE(&ifp
->if_snd
, m
);
if ((ifp
->if_flags
& IFF_OACTIVE
) == 0)