fixes for fp context switching
[unix-history] / usr / src / sys / i386 / isa / if_apx.c
CommitLineData
b852041a
KS
1/*
2 * Copyright (c) 1982, 1990 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * %sccs.include.redist.c%
6 *
90e5f293 7 * @(#)if_apx.c 7.2 (Berkeley) %G%
b852041a
KS
8 */
9
10/*
11 * Driver for SGS-THOMSON MK5025 based Link level controller.
12 * The chip will do LAPB in hardware, although this driver only
13 * attempts to use it for HDLC framing.
14 *
15 * Driver written by Keith Sklower, based on lance AMD7990
16 * driver by Van Jacobsen, and information graciously supplied
17 * by the ADAX corporation of Berkeley, CA.
18 */
19
20#include "apx.h"
21#if NAPX > 0
22
23#include "param.h"
24#include "mbuf.h"
25#include "socket.h"
26#include "ioctl.h"
27#include "errno.h"
28#include "syslog.h"
29
30#include "net/if.h"
31#include "net/netisr.h"
90e5f293 32#include "net/if_types.h"
b852041a
KS
33#include "netccitt/x25.h"
34
90e5f293 35#include "if_apxreg.h"
b852041a
KS
36
37int apxprobe(), apxattach(), apxstart(), apx_uprim(), apx_meminit();
90e5f293 38int apxinit(), x25_ifoutput(), x25_rtrequest(), apxioctl(), apxreset();
b852041a
KS
39void apx_ifattach(), apxinput(), apxintr(), apxtint(), apaxrint();
40
41struct apx_softc {
42 struct ifnet apx_if;
43 caddr_t apx_device; /* e.g. isa_device */
44 u_short apx_csr4; /* byte gender, set in mach dep code */
45 struct apc_reg *apx_reg; /* control regs for both subunits */
46 struct apc_mem *apx_hmem; /* Host addr for shared memory */
47 struct apc_mem *apx_dmem; /* Device (chip) addr for shared mem */
48 struct sgcp *apx_sgcp; /* IO control port for this subunit */
49 struct apc_modes apx_modes; /* Parameters, as amended by ioctls */
50 int apx_rxnum; /* Last receiver dx we looked at */
51 int apx_txnum; /* Last tranmistter dx we stomped on */
52 int apx_txcnt; /* Number of packets queued for tx*/
53} apx_softc[2 * NAPX], *apx_lastsoftc = apx_softc;
54
55struct apxstat {
56 int nulltx;
57 int pint;
90e5f293 58} apxstat;
b852041a
KS
59
60/* default operating paramters for devices */
61struct apc_modes apx_default_modes = {
62 { 1, /* apm_sgob.lsaddr; */
63 3, /* apm_sgob.rsaddr; */
64 -SGMTU, /* apm_sgob.n1; */
65 ((-10)<<8), /* apm_sgob.n2_scale; */
66 -1250, /* apm_sgob.t1; */
67 -10000, /* apm_sgob.t3; */
68 -80, /* apm_sgob.tp; */
69 },
70 2, /* apm_txwin; */
71 5, /* apm_apxmode; */
72 0, /* apm_apxaltmode; */
73 IFT_X25, /* apm_iftype; */
74};
75
76/* Begin bus & endian dependence */
77
90e5f293 78#include "isa_device.h"
b852041a
KS
79
80struct isa_driver apxdriver = {
81 apxprobe, apxattach, "apx",
82};
83
84#define SG_RCSR(apx, csrnum) \
90e5f293
KS
85 (outw(&(apx->apx_sgcp->sgcp_rap), csrnum << 1), \
86 inw(&(apx->apx_sgcp->sgcp_rdp)))
b852041a
KS
87
88#define SG_WCSR(apx, csrnum, data) \
90e5f293 89 (outw(&(apx->apx_sgcp->sgcp_rap), csrnum << 1), \
b852041a
KS
90 outw(&(apx->apx_sgcp->sgcp_rdp), data))
91
92#define APX_RCSR(apx, csrname) inb(&(apx->apx_reg->csrname))
93#define APX_WCSR(apx, csrname, data) outb(&(apx->apx_reg->csrname), data)
94
95#define TIMO 10000 /* used in apx_uprim */
96
97apxprobe(id)
98 register struct isa_device *id;
99{
100 int moffset, subunit, unit = id->id_unit << 1;
90e5f293 101 struct apc_reg *reg = (struct apc_reg *)id->id_iobase;
b852041a
KS
102 register struct apx_softc *apx = apx_softc + unit;
103
104 /* Set and read DTR defeat in channel 0 to test presence of apc */
105 outb(&reg->axr_altmode, 4);
106 if (inb(&reg->axr_altmode) == 0)
107 return 0; /* No board present */
108
109 for (subunit = 0; subunit < 2; subunit++, apx++) {
110 /* Set and read DTR mode to test present of SGS thompson chip */
111 apx->apx_if.if_unit = unit++;
90e5f293 112 apx->apx_sgcp = reg->axr_sgcp + subunit;
b852041a 113 SG_WCSR(apx, 5, 0x08);
90e5f293 114 if (((SG_RCSR(apx, 5) & 0xff08) != 0x08)) {
b852041a
KS
115 apxerror(apx, "no mk5025 for channel", subunit);
116 continue;
117 }
118 moffset = subunit ? id->id_msize >> 1 : 0;
119 apx->apx_hmem = (struct apc_mem *) (id->id_maddr + moffset);
120 apx->apx_dmem = (struct apc_mem *) (moffset);
121 apx->apx_modes = apx_default_modes;
122 apx->apx_device = (caddr_t) id;
123 apx->apx_reg = reg;
124 apx->apx_csr4 = 0x0110; /* no byte swapping for PC-AT */
125 }
126 return 1;
127}
128
129apxattach(id)
130 register struct isa_device *id;
131{
132 int unit = id->id_unit + id->id_unit;
133
134 apx_ifattach(unit);
135 apx_ifattach(unit + 1);
136 return (0);
137}
138
139/* End bus & endian dependence */
140
141/*
142 * Interface exists: make available by filling in network interface
143 * record. System will initialize the interface when it is ready
144 * to accept packets.
145 */
90e5f293 146void
b852041a
KS
147apx_ifattach(unit)
148{
149 register struct ifnet *ifp = &(apx_softc[unit].apx_if);
150 /*
151 * Initialize ifnet structure
152 */
153 if (apx_softc[unit].apx_device == 0)
154 return;
155 ifp->if_name = "apc";
156 ifp->if_mtu = SGMTU;
157 ifp->if_init = apxinit;
158 ifp->if_output = x25_ifoutput;
159 ifp->if_start = apxstart;
160 ifp->if_ioctl = apxioctl;
161 ifp->if_reset = apxreset;
90e5f293 162 ifp->if_type = apx_default_modes.apm_iftype;
b852041a
KS
163 ifp->if_hdrlen = 5;
164 ifp->if_addrlen = 8;
165 if_attach(ifp);
166}
167/*
168 * Initialization of interface
169 */
170apxinit(unit)
171 int unit;
172{
173 struct ifnet *ifp = &apx_softc[unit].apx_if;
174 int s = splimp();
175
176 ifp->if_flags &= ~(IFF_RUNNING|IFF_OACTIVE);
177 if (apxreset(unit) && (ifp->if_flags & IFF_UP)) {
178 ifp->if_flags |= IFF_RUNNING;
179 (void)apxstart(ifp);
180 }
181 splx(s);
182 return 0;
183}
184
185apxreset(unit)
186 int unit;
187{
188 register struct apx_softc *apx = &apx_softc[unit ^ 1];
189 u_char apm_apxmode = 0, apm_apxaltmode = 0;
190#define MODE(m) (m |= apx->apx_modes.m << ((apx->apx_if.if_unit & 1) ? 1 : 0))
191
192 MODE(apm_apxmode);
193 MODE(apm_apxaltmode);
194 apx = apx_softc + unit;
195 MODE(apm_apxmode);
196 MODE(apm_apxaltmode);
197 APX_WCSR(apx, axr_mode, apm_apxmode);
198 APX_WCSR(apx, axr_altmode, apm_apxaltmode);
90e5f293 199 apx->apx_txnum = apx->apx_rxnum = apx->apx_txcnt = 0;
b852041a
KS
200
201 if (apx_uprim(apx, SG_STOP, "stop") ||
202 !(apx->apx_if.if_flags & IFF_UP))
203 return 0;
90e5f293 204 apx_meminit(apx->apx_hmem, apx); /* also sets CSR2 */
b852041a
KS
205 SG_WCSR(apx, 3, (int)apx->apx_dmem);
206 SG_WCSR(apx, 4, apx->apx_csr4);
90e5f293 207 if (apx_uprim(apx, SG_INIT, "init request") ||
b852041a
KS
208 apx_uprim(apx, SG_STAT, "status request") ||
209 apx_uprim(apx, SG_TRANS, "transparent mode"))
210 return 0;
211 SG_WCSR(apx, 0, SG_INEA);
90e5f293 212 return 1;
b852041a
KS
213}
214
215apx_uprim(apx, request, ident)
216 int request;
217 char *ident;
218 register struct apx_softc *apx;
219{
220 register int timo = 0;
221 int reply = SG_RCSR(apx, 1);
222
90e5f293
KS
223 if (reply & 0x8040)
224 SG_WCRS(1, 0x8040); /* Magic! */
b852041a
KS
225 SG_WCSR(apx, 1, request | SG_UAV);
226 do {
227 reply = SG_RCRS(1);
228 if (timo >= TIMO | reply & 0x8000) {
229 apxerror(apx, ident, reply);
230 return 1;
231 }
232 } while (reply & SG_UAV);
233 return 0;
234}
235
236apx_meminit(apc, apx)
237 register struct apc_mem *apc;
238 struct apx_softc *apx;
239{
240 register struct apc_mem *apcbase = apx->apx_dmem;
241 register int i;
90e5f293
KS
242#define LOWADDR(e) (((u_long)&(apcbase->e)) & 0xffff)
243#define HIADDR(e) ((((u_long)&(apcbase->e)) >> 16) & 0xff)
b852041a
KS
244#define SET_SGDX(dx, f, a, b, m) \
245 { (dx).sgdx_addr = LOWADDR(a); (dx).sgdx_bcnt = (b);\
246 (dx).sgdx_mcnt = (m); (dx).sgdx_flags = (f) | HIADDR(a); }
247
248 bzero((caddr_t)apc, LOWADDR(apc_rxmd[0]));
249 apc->apc_mode = 0x8040; /* 2 flag spacing, trans mode, 16bit FCS */
250 apc->apc_sgop = apx->apx_modes.apm_sgop;
251 apc->apc_rlen = SG_RLEN | HIADDR(apc_rxmd[0]);
252 apc->apc_rdra = LOWADDR(apc_rxmd[0]);
253 apc->apc_rlen = SG_TLEN | apx->apx_modes.apm_txwin |HIADDR(apc_txmd[0]);
254 apc->apc_tdra = LOWADDR(apc_txmd[0]);
255 SET_SGDX(apc->apc_rxtid, SG_OWN, apc_rxidbuf, -SGMTU, 0);
256 SET_SGDX(apc->apc_txtid, 0, apc_txidbuf, -SGMTU, 0);
257 apc->apc_stathi = HIADDR(apc_sgsb);
258 apc->apc_statlo = LOWADDR(apc_sgsb);
259 for (i = 0; i < SGRBUF; i++)
260 SET_SGDX(apc->apc_rxmd[i], SG_OWN, apc_rbuf[i][0], -SGMTU, 0)
261 for (i = 0; i < SGTBUF; i++)
262 SET_SGDX(apc->apc_txmd[i], SG_TUI, apc_tbuf[i][0], 0, 0)
263 SG_WCSR(apx, 2, SG_UIE | SG_PROM | HIADDR(apc_mode));
264}
265
266/*
267 * Start output on interface. Get another datagram to send
268 * off of the interface queue, and copy it to the interface
269 * before starting the output.
270 */
271apxstart(ifp)
272 struct ifnet *ifp;
273{
274 register struct apx_softc *apx = &apx_softc[ifp->if_unit];
275 register struct sgdx *dx;
90e5f293 276 struct apc_mem *apc = apx->apx_hmem;
b852041a
KS
277 struct mbuf *m;
278 int len;
279
280 if ((ifp->if_flags & IFF_RUNNING) == 0)
281 return (0);
282 do {
90e5f293 283 dx = apc->apc_txmd + apx->apx_txnum;
b852041a
KS
284 if (dx->sgdx_flags & SG_OWN)
285 return (0);
286 IF_DEQUEUE(&ifp->if_snd, m);
287 if (m == 0)
288 return (0);
289 len = min(m->m_pkthdr.len, SGMTU);
90e5f293 290 m_copydata(m, 0, len, apc->apc_tbuf[apx->apx_txnum]);
b852041a
KS
291 dx->sgdx_mcnt = -len;
292 dx->sgdx_flags = SG_OWN | SG_TUI | (0xff & dx->sgdx_flags);
293 SG_WCSR(apx, 0, SG_INEA | SG_TDMD);
294 if (++apx->apx_txnum >= SGTBUF)
295 apx->apx_txnum = 0;
296 } while (++apx->apx_txcnt < SGTBUF);
297 apx->apx_txcnt = SGTBUF;
298 ifp->if_flags |= IFF_OACTIVE;
299 return (0);
300}
301
90e5f293 302void
b852041a
KS
303apxintr()
304{
305 register struct apx_softc *apx = apx_lastsoftc;
306 struct apx_softc *apxlim = apx_softc + NAPX + NAPX;
307 int reply;
308
309 do {
90e5f293 310 if (apx->apx_if.if_flags & IFF_UP)
b852041a
KS
311 /* Try to turn off interrupt cause */
312 while ((reply = SG_RCSR(apx, 0)) & 0xff) {
313 SG_WCSR(apx, 0, SG_INEA | 0xfe);
314 if (reply & (SG_MERR|SG_TUR|SG_ROR)) {
315 apxerror(apx, "mem, rx, or tx error", reply);
316 apxinit(apx->apx_if.if_unit);
317 break;
318 }
319 if (reply & SG_RINT)
320 apxrint(apx);
321 if (reply & SG_TINT)
322 apxtint(apx);
323 if (reply & SG_PINT)
324 apxstat.pint++;
325 }
326 if (++apx >= apxlim)
327 apx = apx_softc;
328 } while (apx != apx_lastsoftc);
329}
330
90e5f293 331void
b852041a
KS
332apxtint(apx)
333 register struct apx_softc *apx;
334{
90e5f293 335 register struct apc_mem *apc = apx->apx_hmem;
b852041a
KS
336 int i, loopcount = 0;
337
338 do {
339 if ((i = apx->apx_txnum - apx->apx_txcnt) < 0)
340 i += SGTBUF;
341 if (apc->apc_txmd[i].sgdx_flags & SG_OWN) {
342 if (loopcount)
343 break;
344 apxstat.nulltx++;
345 return;
346 }
347 loopcount++;
348 apx->apx_if.if_flags &= ~IFF_OACTIVE;
349 } while (--apx->apx_txcnt > 0);
350 apxstart(&apx->apx_if);
351}
352
353apxrint(apx)
354 register struct apx_softc *apx;
355{
90e5f293 356 register struct apc_mem *apc = apx->apx_hmem;
b852041a
KS
357 register struct sgdx *dx = apc->apc_rxmd + apx->apx_rxnum;
358#define SGNEXTRXMD \
359dx = ++apx->apx_rxnum == SGRBUF ? &apc->apc_rxmd[apx->apx_rxnum = 0] : dx + 1;
360
361 /*
362 * Out of sync with hardware, should never happen?
363 */
364 if (dx->sgdx_flags & SG_OWN) {
365 apxerror(apx, "out of sync");
366 return;
367 }
368 /*
369 * Process all buffers with valid data
370 */
371 while ((dx->sgdx_flags & SG_OWN) == 0) {
372 if ((dx->sgdx_flags & (SG_SLF|SG_ELF)) != (SG_SLF|SG_ELF)) {
373 /*
374 * Find the end of the packet so we can see how long
375 * it was. We still throw it away.
376 */
90e5f293 377 apxerror(apx, "chained buffer", dx->sgdx_flags);
b852041a
KS
378 do {
379 dx->sgdx_bcnt = 0;
380 dx->sgdx_flags = SG_OWN | (0xff&dx->sgdx_flags);
381 SGNEXTRXMD;
382 } while (!(dx->sgdx_flags & (SG_OWN|SG_SLF|SG_ELF)));
383 /*
384 * If search terminated without successful completion
385 * we reset the hardware (conservative).
386 */
387 if ((dx->sgdx_flags & (SG_OWN|SG_SLF|SG_ELF)) !=
90e5f293 388 SG_ELF) {
b852041a
KS
389 apxreset(apx->apx_if.if_unit);
390 return;
391 }
392 } else
393 apxinput(&apx->apx_if, apc->apc_rbuf[apx->apx_rxnum],
394 -dx->sgdx_bcnt);
395 dx->sgdx_bcnt = 0;
396 dx->sgdx_flags = SG_OWN | (0xff & dx->sgdx_flags);
397 SGNEXTRXMD;
398 }
399}
400
90e5f293 401void
b852041a
KS
402apxinput(ifp, buffer, len)
403register struct ifnet *ifp;
404caddr_t buffer;
405{
406 register struct ifqueue *inq;
407 struct mbuf *m, *apxget();
408 extern struct ifqueue hdintrq, ipintrq;
409 int isr;
410
411 ifp->if_ipackets++;
412 {
413 register u_char *cp = (u_char *)buffer;
414
415 if (cp[0] == 0xff && cp[1] == 0x3) {
416 /* This is a UI HDLC Packet, so we'll assume PPP
417 protocol. for now, IP only. */
418 buffer += 4;
419 len -= 4;
420 inq = &ipintrq;
421 isr = NETISR_IP;
422 } else {
423 inq = &hdintrq;
424 isr = NETISR_CCITT;
425 }
426 }
427 if (len <= 0)
428 return;
429
430 m = apxget(buffer, len , 0, ifp);
431 if (m == 0)
432 return;
433
434 if(IF_QFULL(inq)) {
435 IF_DROP(inq);
436 m_freem(m);
437 } else {
438 IF_ENQUEUE(inq, m);
439 schednetisr(isr);
440 }
441}
442
443/*
444 * Routine to copy from board local memory into mbufs.
445 */
446struct mbuf *
447apxget(buf, totlen, off0, ifp)
448 char *buf;
449 int totlen, off0;
450 struct ifnet *ifp;
451{
452 register struct mbuf *m;
453 struct mbuf *top = 0, **mp = &top;
454 register int off = off0, len;
455 register char *cp;
456 char *epkt;
457
458 cp = buf;
459 epkt = cp + totlen;
460 if (off) {
461 cp += off + 2 * sizeof(u_short);
462 totlen -= 2 * sizeof(u_short);
463 }
464
465 MGETHDR(m, M_DONTWAIT, MT_DATA);
466 if (m == 0)
467 return (0);
468 m->m_pkthdr.rcvif = ifp;
469 m->m_pkthdr.len = totlen;
470 m->m_len = MHLEN;
471
472 while (totlen > 0) {
473 if (top) {
474 MGET(m, M_DONTWAIT, MT_DATA);
475 if (m == 0) {
476 m_freem(top);
477 return (0);
478 }
479 m->m_len = MLEN;
480 }
481 len = min(totlen, epkt - cp);
482 if (len >= MINCLSIZE) {
483 MCLGET(m, M_DONTWAIT);
484 if (m->m_flags & M_EXT)
485 m->m_len = len = min(len, MCLBYTES);
486 else
487 len = m->m_len;
488 } else {
489 /*
490 * Place initial small packet/header at end of mbuf.
491 */
492 if (len < m->m_len) {
493 if (top == 0 && len + max_linkhdr <= m->m_len)
494 m->m_data += max_linkhdr;
495 m->m_len = len;
496 } else
497 len = m->m_len;
498 }
499 bcopy(cp, mtod(m, caddr_t), (unsigned)len);
500 cp += len;
501 *mp = m;
502 mp = &m->m_next;
503 totlen -= len;
504 if (cp == epkt)
505 cp = buf;
506 }
507 return (top);
508}
509
510/*
511 * Process an ioctl request.
512 */
513apxioctl(ifp, cmd, data)
514 register struct ifnet *ifp;
515 int cmd;
516 caddr_t data;
517{
518 register struct ifaddr *ifa = (struct ifaddr *)data;
519 int s = splimp(), error = 0;
520 struct apx_softc *apx = &apx_softc[ifp->if_unit];
521
522 switch (cmd) {
523 case SIOCSIFCONF_X25:
524 ifp->if_flags |= IFF_UP;
525 error = hd_ctlinput(PRC_IFUP, ifa->ifa_addr);
526 if (error == 0)
527 apxinit(ifp->if_unit);
528 break;
529
530 case SIOCSIFADDR:
531 ifa->ifa_rtrequest = x25_rtrequest;
532 break;
533
534 case SIOCSIFFLAGS:
535 if (((ifp->if_flags & IFF_UP) == 0 &&
536 (ifp->if_flags & IFF_RUNNING)) ||
537 (ifp->if_flags & IFF_UP) &&
538 (ifp->if_flags & IFF_RUNNING) == 0)
539 apxinit(ifp->if_unit);
540 break;
541
542 case SIOCSIFMODE:
543 if ((ifp->if_flags & IFF_UP) == 0)
90e5f293 544 apx->apx_modes = *(struct apc_modes *)data;
b852041a
KS
545 else
546 default:
547 error = EINVAL;
548
549 }
550 splx(s);
551 return (error);
552}
553
554apxerror(apx, msg, data)
555 register struct apx_softc *apx;
556 char *msg;
557{
558 log(LOG_WARNING, "apc%d: %s, stat=0x%x\n",
559 apx->apx_if.if_unit, msg, data);
560}
561#endif /* NAPX */