* Copyright (c) 1982, 1990 The Regents of the University of California.
* %sccs.include.redist.c%
* @(#)if_apx.c 7.6 (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*/
struct sgae apx_csr23
; /* 24 bit init addr, as seen by chip */
u_short apx_csr4
; /* byte gender, set in mach dep code */
struct apc_modes apx_modes
; /* Parameters, as amended by ioctls */
int rxnull
; /* no rx bufs ready this interrupt */
int rxnrdy
; /* expected rx buf not ready */
int rx2big
; /* expected rx buf not ready */
int pint
; /* new primitive available interrupt */
int rint
; /* receive interrupts */
int tint
; /* transmit interrupts */
int anyint
; /* note all interrupts */
int queued
; /* got through apxinput */
int nxpctd
; /* received while if was down */
/* 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; */
1, /* apm_apxmode: RS_232 connector and modem clock; */
0, /* apm_apxaltmode: enable dtr, disable X.21 connector; */
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
++) {
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 apx_softc
*apx
= apx_softc
+ (id
->id_unit
<< 1);
apx_ifattach(&((apx
++)->apx_if
));
apx_ifattach(&(apx
->apx_if
));
/* 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
;
* Initialize ifnet structure
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
;
(void) apx_uprim(apx
, SG_STOP
, "stop after probing");
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
);
(void) apx_uprim(apx
, SG_STOP
, "stop to reset");
if ((apx
->apx_if
.if_flags
& IFF_UP
) == 0)
apx_meminit(apx
->apx_hmem
, apx
);
SG_WCSR(apx
, 4, apx
->apx_csr4
);
SG_WCSR(apx
, 2, apx
->apx_csr23
.f_hi
);
SG_WCSR(apx
, 3, apx
->apx_csr23
.lo
);
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
);
int apx_doinit
= 1, apx_dostat
= 1;
int apx_didinit
= 0, apx_didstat
= 0;
apx_uprim(apx
, request
, ident
)
register struct apx_softc
*apx
;
if ((apx
->apx_flags
& APXF_CHIPHERE
) == 0)
return 1; /* maybe even should panic . . . */
if (request
== SG_STAT
) { if (apx_dostat
) apx_didstat
= 1; else return 0;}
if (request
== SG_INIT
) { if (apx_doinit
) apx_didinit
= 1; else return 0;}
if ((reply
= SG_RCSR(apx
, 1)) & 0x8040)
SG_WCSR(apx
, 1, 0x8040); /* Magic! */
SG_WCSR(apx
, 1, request
| SG_UAV
);
if (timo
++ >= TIMO
|| (reply
& 0x8000)) {
if (request
!= SG_STOP
|| apxdebug
)
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_SGAE(d, f, a) {(d).lo = LOWADDR(a); (d).f_hi = (f) | HIADDR(a);}
#define SET_SGDX(d, f, a, b) \
{SET_SGAE((d).sgdx_ae, f, a); (d).sgdx_mcnt = (d).sgdx_bcnt = (b);}
apx
->apx_txnum
= apx
->apx_rxnum
= apx
->apx_txcnt
= 0;
bzero((caddr_t
)apc
, ((caddr_t
)(&apc
->apc_rxmd
[0])) - (caddr_t
)apc
);
apc
->apc_mode
= 0x0008; /* 2 flag spacing, trans mode, 16bit FCS */
apc
->apc_sgop
= apx
->apx_modes
.apm_sgop
;
SET_SGAE(apx
->apx_csr23
, SG_UIE
| SG_PROM
, apc_mode
);
SET_SGAE(apc
->apc_rxdd
, SG_RLEN
, apc_rxmd
[0]);
SET_SGAE(apc
->apc_txdd
, SG_TLEN
|apx
->apx_modes
.apm_txwin
, apc_txmd
[0]);
SET_SGAE(apc
->apc_stdd
, 0, apc_sgsb
);
SET_SGDX(apc
->apc_rxtid
, SG_OWN
, apc_rxidbuf
[0], -SGMTU
);
SET_SGDX(apc
->apc_txtid
, 0, apc_txidbuf
[0], 0);
for (i
= 0; i
< SGRBUF
; i
++)
SET_SGDX(apc
->apc_rxmd
[i
], SG_OWN
, apc_rbuf
[i
][0], -SGMTU
)
for (i
= 0; i
< SGTBUF
; i
++)
SET_SGDX(apc
->apc_txmd
[i
], SG_TUI
, apc_tbuf
[i
][0], 0)
* 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
|SG_SLF
|SG_ELF
) |
SG_WCSR(apx
, 0, SG_INEA
| SG_TDMD
);
if (++apx
->apx_txnum
>= SGTBUF
)
} while (++apx
->apx_txcnt
< SGTBUF
);
apx
->apx_txcnt
= SGTBUF
; /* in case txcnt > SGTBUF by mistake */
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?
while (dx
->sgdx_flags
& SG_OWN
) {
* 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 synch up.
* We throw the data 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
;
extern struct ifqueue hdintrq
, ipintrq
;
register struct ifqueue
*inq
;
register u_char
*cp
= (u_char
*)buffer
;
struct mbuf
*m
, *m_devget();
if ((ifp
->if_flags
& IFF_UP
) == 0) {
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)