FreeBSD/Linux Kernel Cross Reference
sys/i386/isa/if_cx.c
1 /*
2 * Cronyx-Sigma adapter driver for FreeBSD.
3 * Supports PPP/HDLC and Cisco/HDLC protocol in synchronous mode,
4 * and asyncronous channels with full modem control.
5 * Keepalive protocol implemented in both Cisco and PPP modes.
6 *
7 * Copyright (C) 1994 Cronyx Ltd.
8 * Author: Serge Vakulenko, <vak@zebub.msk.su>
9 *
10 * This software is distributed with NO WARRANTIES, not even the implied
11 * warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 *
13 * Authors grant any other persons or organisations permission to use
14 * or modify this software as long as this message is kept with the software,
15 * all derivative works or modified versions.
16 *
17 * Version 1.9, Wed Oct 4 18:58:15 MSK 1995
18 */
19 #undef DEBUG
20
21 #include "cx.h"
22 #include "bpfilter.h"
23 #include "opt_devfs.h"
24
25 #include "sppp.h"
26 #if NSPPP <= 0
27 #error The device 'cx' requires sppp.
28 #endif
29
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/kernel.h>
33 #include <sys/malloc.h>
34 #include <sys/mbuf.h>
35 #include <sys/sockio.h>
36 #include <sys/socket.h>
37 #include <sys/conf.h>
38
39 #include <net/if.h>
40
41 #if NBPFILTER > 0
42 #include <net/bpf.h>
43 #endif
44
45 #include <i386/isa/isa_device.h>
46 #ifdef DEVFS
47 extern struct cdevsw cx_cdevsw;
48 #include <sys/devfsext.h>
49 #endif /*DEVFS*/
50 #define watchdog_func_t void(*)(struct ifnet *)
51 #define start_func_t void(*)(struct ifnet*)
52
53 #include <net/if_sppp.h>
54 #include <machine/cronyx.h>
55 #include <i386/isa/cxreg.h>
56
57 /* XXX exported. */
58 void cxswitch (cx_chan_t *c, cx_soft_opt_t new);
59
60 static int cxprobe __P((struct isa_device *id));
61 static int cxattach __P((struct isa_device *id));
62 static void cxput __P((cx_chan_t *c, char b));
63 static void cxsend __P((cx_chan_t *c));
64 static void cxrinth __P((cx_chan_t *c));
65 static ointhand2_t cxintr;
66 static int cxtinth __P((cx_chan_t *c));
67
68 #ifdef DEBUG
69 # define print(s) printf s
70 #else
71 # define print(s) {/*void*/}
72 #endif
73
74 #define TXTIMEOUT 10 /* transmit timeout in seconds */
75 #define DMABUFSZ (6*256) /* buffer size */
76 #define PPP_HEADER_LEN 4 /* size of PPP header */
77
78 /*
79 * Under BSDI it's possible to use general p2p protocol scheme,
80 * as well as our own one. Switching is done via IFF_ALTPHYS flag.
81 * Our ifnet pointer holds the buffer large enough to contain
82 * any of sppp and p2p structures.
83 */
84 #define IFSTRUCTSZ (sizeof (struct sppp))
85 #define IFNETSZ (sizeof (struct ifnet))
86
87 static int cxsioctl (struct ifnet *ifp, u_long cmd, caddr_t data);
88 static void cxstart (struct ifnet *ifp);
89 static void cxwatchdog (struct ifnet *ifp);
90 static void cxinput (cx_chan_t *c, void *buf, unsigned len);
91 extern int cxrinta (cx_chan_t *c);
92 extern void cxtinta (cx_chan_t *c);
93 extern void cxmint (cx_chan_t *c);
94 extern timeout_t cxtimeout;
95 static void cxdown (cx_chan_t *c);
96 static void cxup (cx_chan_t *c);
97
98 cx_board_t cxboard [NCX]; /* adapter state structures */
99 cx_chan_t *cxchan [NCX*NCHAN]; /* unit to channel struct pointer */
100
101 static unsigned short irq_valid_values [] = { 3, 5, 7, 10, 11, 12, 15, 0 };
102 static unsigned short drq_valid_values [] = { 5, 6, 7, 0 };
103 static unsigned short port_valid_values [] = {
104 0x240, 0x260, 0x280, 0x300, 0x320, 0x380, 0x3a0, 0,
105 };
106
107 /*
108 * Check that the value is contained in the list of correct values.
109 */
110 static int valid (unsigned short value, unsigned short *list)
111 {
112 while (*list)
113 if (value == *list++)
114 return (1);
115 return (0);
116 }
117
118 /*
119 * Print the mbuf chain, for debug purposes only.
120 */
121 static void printmbuf (struct mbuf *m)
122 {
123 printf ("mbuf:");
124 for (; m; m=m->m_next) {
125 if (m->m_flags & M_PKTHDR)
126 printf (" HDR %d:", m->m_pkthdr.len);
127 if (m->m_flags & M_EXT)
128 printf (" EXT:");
129 printf (" %d", m->m_len);
130 }
131 printf ("\n");
132 }
133
134 /*
135 * Make an mbuf from data.
136 */
137 static struct mbuf *makembuf (void *buf, unsigned len)
138 {
139 struct mbuf *m, *o, *p;
140
141 MGETHDR (m, M_DONTWAIT, MT_DATA);
142 if (! m)
143 return (0);
144 if (len >= MINCLSIZE)
145 MCLGET (m, M_DONTWAIT);
146 m->m_pkthdr.len = len;
147 m->m_len = 0;
148
149 p = m;
150 while (len) {
151 unsigned n = M_TRAILINGSPACE (p);
152 if (n > len)
153 n = len;
154
155 if (! n) {
156 /* Allocate new mbuf. */
157 o = p;
158 MGET (p, M_DONTWAIT, MT_DATA);
159 if (! p) {
160 m_freem (m);
161 return (0);
162 }
163 if (len >= MINCLSIZE)
164 MCLGET (p, M_DONTWAIT);
165 p->m_len = 0;
166 o->m_next = p;
167
168 n = M_TRAILINGSPACE (p);
169 if (n > len)
170 n = len;
171 }
172
173 bcopy (buf, mtod (p, caddr_t) + p->m_len, n);
174
175 p->m_len += n;
176 buf = (char *)buf + n;
177 len -= n;
178 }
179 return (m);
180 }
181
182 /*
183 * Test the presence of the adapter on the given i/o port.
184 */
185 static int
186 cxprobe (struct isa_device *id)
187 {
188 int unit = id->id_unit;
189 int iobase = id->id_iobase;
190 int irq = id->id_irq;
191 int drq = id->id_drq;
192 int irqnum;
193 irqnum = ffs (irq) - 1;
194
195 print (("cx%d: probe iobase=0x%x irq=%d drq=%d\n",
196 unit, iobase, irqnum, drq));
197 if (! valid (irqnum, irq_valid_values)) {
198 printf ("cx%d: Incorrect IRQ: %d\n", unit, irqnum);
199 return (0);
200 }
201 if (! valid (iobase, port_valid_values)) {
202 printf ("cx%d: Incorrect port address: 0x%x\n", unit, iobase);
203 return (0);
204 }
205 if (! valid (drq, drq_valid_values)) {
206 printf ("cx%d: Incorrect DMA channel: %d\n", unit, drq);
207 return (0);
208 }
209 if (! cx_probe_board (iobase))
210 return (0);
211
212 return (1);
213 }
214
215 /*
216 * The adapter is present, initialize the driver structures.
217 */
218
219 #ifdef DEVFS
220 static void *cx_devfs_token;
221 #endif
222
223 static int
224 cxattach (struct isa_device *id)
225 {
226 int unit = id->id_unit;
227 int iobase = id->id_iobase;
228 int irq = id->id_irq;
229 int drq = id->id_drq;
230 cx_board_t *b = cxboard + unit;
231 int i;
232 struct sppp *sp;
233
234 id->id_ointr = cxintr;
235
236 /* Initialize the board structure. */
237 cx_init (b, unit, iobase, ffs(irq)-1, drq);
238
239 for (i=0; i<NCHAN; ++i) {
240 cx_chan_t *c = b->chan + i;
241 int u = b->num*NCHAN + i;
242 cxchan[u] = c;
243
244 if (c->type == T_NONE)
245 continue;
246
247 /* Allocate the buffer memory. */
248 c->arbuf = malloc (DMABUFSZ, M_DEVBUF, M_NOWAIT);
249 c->brbuf = malloc (DMABUFSZ, M_DEVBUF, M_NOWAIT);
250 c->atbuf = malloc (DMABUFSZ, M_DEVBUF, M_NOWAIT);
251 c->btbuf = malloc (DMABUFSZ, M_DEVBUF, M_NOWAIT);
252
253 /* All buffers should be located in lower 16M of memory! */
254 if (!c->arbuf || !c->brbuf || !c->atbuf || !c->btbuf) {
255 printf ("cx%d.%d: No memory for channel buffers\n",
256 c->board->num, c->num);
257 c->type = T_NONE;
258 }
259
260 switch (c->type) {
261 case T_SYNC_RS232:
262 case T_SYNC_V35:
263 case T_SYNC_RS449:
264 case T_UNIV_RS232:
265 case T_UNIV_RS449:
266 case T_UNIV_V35:
267 c->ifp = malloc (IFSTRUCTSZ, M_DEVBUF, M_NOWAIT);
268 if (! c->ifp) {
269 printf ("cx%d.%d: No memory for ifnet buffer\n",
270 c->board->num, c->num);
271 c->type = T_NONE;
272 continue;
273 }
274 bzero (c->ifp, IFSTRUCTSZ);
275 c->master = c->ifp;
276 c->ifp->if_softc = c;
277 c->ifp->if_unit = u;
278 c->ifp->if_name = "cx";
279 c->ifp->if_mtu = PP_MTU;
280 c->ifp->if_flags = IFF_POINTOPOINT | IFF_MULTICAST;
281 c->ifp->if_ioctl = cxsioctl;
282 c->ifp->if_start = (start_func_t) cxstart;
283 c->ifp->if_watchdog = (watchdog_func_t) cxwatchdog;
284 /* Init routine is never called by upper level? */
285 sppp_attach (c->ifp);
286 if_attach (c->ifp);
287 sp = (struct sppp*) c->ifp;
288 #if NBPFILTER > 0
289 /* If BPF is in the kernel, call the attach for it. */
290 bpfattach (c->ifp, DLT_PPP, PPP_HEADER_LEN);
291 #endif
292 }
293 }
294
295 /* Reset the adapter. */
296 cx_setup_board (b);
297
298 /* Activate the timeout routine. */
299 if (unit == 0)
300 timeout (cxtimeout, 0, hz*5);
301
302 printf ("cx%d: <Cronyx-%s>\n", unit, b->name);
303 #ifdef DEVFS
304 cx_devfs_token =
305 devfs_add_devswf(&cx_cdevsw, 0, DV_CHR, 0, 0, 0600, "cx");
306 #endif
307 return (1);
308 }
309
310 struct isa_driver cxdriver = { cxprobe, cxattach, "cx" };
311
312 /*
313 * Process an ioctl request.
314 */
315 static int
316 cxsioctl (struct ifnet *ifp, u_long cmd, caddr_t data)
317 {
318 cx_chan_t *q, *c = ifp->if_softc;
319 int error, s, was_up, should_be_up;
320
321 /*
322 * No socket ioctls while the channel is in async mode.
323 */
324 if (c->type==T_NONE || c->mode==M_ASYNC)
325 return (EINVAL);
326
327 /*
328 * Socket ioctls on slave subchannels are not allowed.
329 */
330 if (c->master != c->ifp)
331 return (EBUSY);
332
333 was_up = (ifp->if_flags & IFF_RUNNING) != 0;
334 error = sppp_ioctl (ifp, cmd, data);
335 if (error)
336 return (error);
337
338 print (("cxioctl (%d.%d, ", c->board->num, c->num));
339 switch (cmd) {
340 default:
341 print (("0x%x)\n", cmd));
342 return (0);
343 case SIOCADDMULTI:
344 print (("SIOCADDMULTI)\n"));
345 return (0);
346 case SIOCDELMULTI:
347 print (("SIOCDELMULTI)\n"));
348 return (0);
349 case SIOCSIFFLAGS:
350 print (("SIOCSIFFLAGS)\n"));
351 break;
352 case SIOCSIFADDR:
353 print (("SIOCSIFADDR)\n"));
354 break;
355 }
356
357 /* We get here only in case of SIFFLAGS or SIFADDR. */
358 s = splimp ();
359 should_be_up = (ifp->if_flags & IFF_RUNNING) != 0;
360 if (!was_up && should_be_up) {
361 /* Interface goes up -- start it. */
362 cxup (c);
363
364 /* Start all slave subchannels. */
365 for (q=c->slaveq; q; q=q->slaveq)
366 cxup (q);
367
368 cxstart (c->ifp);
369 } else if (was_up && !should_be_up) {
370 /* Interface is going down -- stop it. */
371 cxdown (c);
372
373 /* Stop all slave subchannels. */
374 for (q=c->slaveq; q; q=q->slaveq)
375 cxdown (q);
376
377 /* Flush the interface output queue */
378 if (! c->sopt.ext)
379 sppp_flush (c->ifp);
380 }
381 splx (s);
382 return (0);
383 }
384
385 /*
386 * Stop the interface. Called on splimp().
387 */
388 static void
389 cxdown (cx_chan_t *c)
390 {
391 unsigned short port = c->chip->port;
392
393 print (("cx%d.%d: cxdown\n", c->board->num, c->num));
394
395 /* The interface is down, stop it */
396 c->ifp->if_flags &= ~IFF_OACTIVE;
397
398 /* Reset the channel (for sync modes only) */
399 outb (CAR(port), c->num & 3);
400 outb (STCR(port), STC_ABORTTX | STC_SNDSPC);
401
402 cx_setup_chan (c);
403 }
404
405 /*
406 * Start the interface. Called on splimp().
407 */
408 static void
409 cxup (cx_chan_t *c)
410 {
411 unsigned short port = c->chip->port;
412
413 /* The interface is up, start it */
414 print (("cx%d.%d: cxup\n", c->board->num, c->num));
415
416 /* Initialize channel, enable receiver and transmitter */
417 cx_cmd (port, CCR_INITCH | CCR_ENRX | CCR_ENTX);
418 /* Repeat the command, to avoid the rev.H bug */
419 cx_cmd (port, CCR_INITCH | CCR_ENRX | CCR_ENTX);
420
421 /* Start receiver */
422 outw (ARBCNT(port), DMABUFSZ);
423 outb (ARBSTS(port), BSTS_OWN24);
424 outw (BRBCNT(port), DMABUFSZ);
425 outb (BRBSTS(port), BSTS_OWN24);
426
427 /* Raise DTR and RTS */
428 cx_chan_dtr (c, 1);
429 cx_chan_rts (c, 1);
430
431 /* Enable interrupts */
432 outb (IER(port), IER_RXD | IER_TXD);
433 }
434
435 /*
436 * Fill transmitter buffer with data.
437 */
438 static void
439 cxput (cx_chan_t *c, char b)
440 {
441 struct mbuf *m;
442 unsigned char *buf;
443 unsigned short port = c->chip->port, len, cnt_port, sts_port;
444
445 /* Choose the buffer. */
446 if (b == 'A') {
447 buf = c->atbuf;
448 cnt_port = ATBCNT(port);
449 sts_port = ATBSTS(port);
450 } else {
451 buf = c->btbuf;
452 cnt_port = BTBCNT(port);
453 sts_port = BTBSTS(port);
454 }
455
456 /* Is it busy? */
457 if (inb (sts_port) & BSTS_OWN24) {
458 if (c->ifp->if_flags & IFF_DEBUG)
459 print (("cx%d.%d: tbuf %c already busy, bsts=%b\n",
460 c->board->num, c->num, b,
461 inb (sts_port), BSTS_BITS));
462 goto ret;
463 }
464
465 /* Get the packet to send. */
466 m = sppp_dequeue (c->master);
467 if (! m)
468 return;
469 len = m->m_pkthdr.len;
470
471 /* Count the transmitted bytes to the subchannel, not the master. */
472 c->master->if_obytes -= len + 3;
473 c->ifp->if_obytes += len + 3;
474 c->stat->obytes += len + 3;
475
476 if (len >= DMABUFSZ) {
477 printf ("cx%d.%d: too long packet: %d bytes: ",
478 c->board->num, c->num, len);
479 printmbuf (m);
480 m_freem (m);
481 return;
482 }
483 m_copydata (m, 0, len, buf);
484 #if NBPFILTER > 0
485 if (c->ifp->if_bpf)
486 bpf_mtap (c->ifp, m);
487 #endif
488 m_freem (m);
489
490 /* Start transmitter. */
491 outw (cnt_port, len);
492 outb (sts_port, BSTS_EOFR | BSTS_INTR | BSTS_OWN24);
493
494 if (c->ifp->if_flags & IFF_DEBUG)
495 print (("cx%d.%d: enqueue %d bytes to %c\n",
496 c->board->num, c->num, len, buf==c->atbuf ? 'A' : 'B'));
497 ret:
498 c->ifp->if_flags |= IFF_OACTIVE;
499 }
500
501 /*
502 * Start output on the (slave) interface. Get another datagram to send
503 * off of the interface queue, and copy it to the interface
504 * before starting the output.
505 */
506 static void
507 cxsend (cx_chan_t *c)
508 {
509 unsigned short port = c->chip->port;
510
511 if (c->ifp->if_flags & IFF_DEBUG)
512 print (("cx%d.%d: cxsend\n", c->board->num, c->num));
513
514 /* No output if the interface is down. */
515 if (! (c->ifp->if_flags & IFF_RUNNING))
516 return;
517
518 /* Set the current channel number. */
519 outb (CAR(port), c->num & 3);
520
521 /* Determine the buffer order. */
522 if (inb (DMABSTS(port)) & DMABSTS_NTBUF) {
523 cxput (c, 'B');
524 cxput (c, 'A');
525 } else {
526 cxput (c, 'A');
527 cxput (c, 'B');
528 }
529
530 /* Set up transmit timeout. */
531 if (c->master->if_flags & IFF_OACTIVE)
532 c->master->if_timer = TXTIMEOUT;
533
534 /*
535 * Enable TXMPTY interrupt,
536 * to catch the case when the second buffer is empty.
537 */
538 if ((inb (ATBSTS(port)) & BSTS_OWN24) &&
539 (inb (BTBSTS(port)) & BSTS_OWN24)) {
540 outb (IER(port), IER_RXD | IER_TXD | IER_TXMPTY);
541 } else
542 outb (IER(port), IER_RXD | IER_TXD);
543 }
544
545 /*
546 * Start output on the (master) interface and all slave interfaces.
547 * Always called on splimp().
548 */
549 static void
550 cxstart (struct ifnet *ifp)
551 {
552 cx_chan_t *q, *c = ifp->if_softc;
553
554 if (c->ifp->if_flags & IFF_DEBUG)
555 print (("cx%d.%d: cxstart\n", c->board->num, c->num));
556
557 /* Start the master subchannel. */
558 cxsend (c);
559
560 /* Start all slave subchannels. */
561 if (c->slaveq && ! sppp_isempty (c->master))
562 for (q=c->slaveq; q; q=q->slaveq)
563 if ((q->ifp->if_flags & IFF_RUNNING) &&
564 ! (q->ifp->if_flags & IFF_OACTIVE))
565 cxsend (q);
566 }
567
568 /*
569 * Handle transmit timeouts.
570 * Recover after lost transmit interrupts.
571 * Always called on splimp().
572 */
573 static void
574 cxwatchdog (struct ifnet *ifp)
575 {
576 cx_chan_t *q, *c = ifp->if_softc;
577
578 if (! (ifp->if_flags & IFF_RUNNING))
579 return;
580 if (ifp->if_flags & IFF_DEBUG)
581 printf ("cx%d.%d: device timeout\n", c->board->num, c->num);
582
583 cxdown (c);
584 for (q=c->slaveq; q; q=q->slaveq)
585 cxdown (q);
586
587 cxup (c);
588 for (q=c->slaveq; q; q=q->slaveq)
589 cxup (q);
590
591 cxstart (ifp);
592 }
593
594 /*
595 * Handle receive interrupts, including receive errors and
596 * receive timeout interrupt.
597 */
598 static void
599 cxrinth (cx_chan_t *c)
600 {
601 unsigned short port = c->chip->port;
602 unsigned short len, risr = inw (RISR(port));
603
604 /* Receive errors. */
605 if (risr & (RIS_BUSERR | RIS_OVERRUN | RISH_CRCERR | RISH_RXABORT)) {
606 if (c->ifp->if_flags & IFF_DEBUG)
607 printf ("cx%d.%d: receive error, risr=%b\n",
608 c->board->num, c->num, risr, RISH_BITS);
609 ++c->ifp->if_ierrors;
610 ++c->stat->ierrs;
611 if (risr & RIS_OVERRUN)
612 ++c->ifp->if_collisions;
613 } else if (risr & RIS_EOBUF) {
614 if (c->ifp->if_flags & IFF_DEBUG)
615 print (("cx%d.%d: hdlc receive interrupt, risr=%b, arbsts=%b, brbsts=%b\n",
616 c->board->num, c->num, risr, RISH_BITS,
617 inb (ARBSTS(port)), BSTS_BITS,
618 inb (BRBSTS(port)), BSTS_BITS));
619 ++c->stat->ipkts;
620
621 /* Handle received data. */
622 len = (risr & RIS_BB) ? inw(BRBCNT(port)) : inw(ARBCNT(port));
623 c->stat->ibytes += len;
624 if (len > DMABUFSZ) {
625 /* Fatal error: actual DMA transfer size
626 * exceeds our buffer size. It could be caused
627 * by incorrectly programmed DMA register or
628 * hardware fault. Possibly, should panic here. */
629 printf ("cx%d.%d: panic! DMA buffer overflow: %d bytes\n",
630 c->board->num, c->num, len);
631 ++c->ifp->if_ierrors;
632 } else if (! (risr & RIS_EOFR)) {
633 /* The received frame does not fit in the DMA buffer.
634 * It could be caused by serial lie noise,
635 * or if the peer has too big MTU. */
636 if (c->ifp->if_flags & IFF_DEBUG)
637 printf ("cx%d.%d: received frame length exceeds MTU, risr=%b\n",
638 c->board->num, c->num, risr, RISH_BITS);
639 ++c->ifp->if_ierrors;
640 } else {
641 /* Valid frame received. */
642 if (c->ifp->if_flags & IFF_DEBUG)
643 print (("cx%d.%d: hdlc received %d bytes\n",
644 c->board->num, c->num, len));
645 cxinput (c, (risr & RIS_BB) ? c->brbuf : c->arbuf, len);
646 ++c->ifp->if_ipackets;
647 }
648 } else if (c->ifp->if_flags & IFF_DEBUG) {
649 print (("cx%d.%d: unknown hdlc receive interrupt, risr=%b\n",
650 c->board->num, c->num, risr, RISH_BITS));
651 ++c->stat->ierrs;
652 }
653
654 /* Restart receiver. */
655 if (! (inb (ARBSTS(port)) & BSTS_OWN24)) {
656 outw (ARBCNT(port), DMABUFSZ);
657 outb (ARBSTS(port), BSTS_OWN24);
658 }
659 if (! (inb (BRBSTS(port)) & BSTS_OWN24)) {
660 outw (BRBCNT(port), DMABUFSZ);
661 outb (BRBSTS(port), BSTS_OWN24);
662 }
663 }
664
665 /*
666 * Handle transmit interrupt.
667 */
668 static int
669 cxtinth (cx_chan_t *c)
670 {
671 unsigned short port = c->chip->port;
672 unsigned char tisr = inb (TISR(port));
673 unsigned char teoir = 0;
674
675 c->ifp->if_flags &= ~IFF_OACTIVE;
676 if (c->ifp == c->master)
677 c->ifp->if_timer = 0;
678
679 if (tisr & (TIS_BUSERR | TIS_UNDERRUN)) {
680 /* if (c->ifp->if_flags & IFF_DEBUG) */
681 print (("cx%d.%d: transmit error, tisr=%b, atbsts=%b, btbsts=%b\n",
682 c->board->num, c->num, tisr, TIS_BITS,
683 inb (ATBSTS(port)), BSTS_BITS,
684 inb (BTBSTS(port)), BSTS_BITS));
685 ++c->ifp->if_oerrors;
686 ++c->stat->oerrs;
687
688 /* Terminate the failed buffer. */
689 /* teoir = TEOI_TERMBUFF; */
690 } else if (c->ifp->if_flags & IFF_DEBUG)
691 print (("cx%d.%d: hdlc transmit interrupt, tisr=%b, atbsts=%b, btbsts=%b\n",
692 c->board->num, c->num, tisr, TIS_BITS,
693 inb (ATBSTS(port)), BSTS_BITS,
694 inb (BTBSTS(port)), BSTS_BITS));
695
696 if (tisr & TIS_EOFR) {
697 ++c->ifp->if_opackets;
698 ++c->stat->opkts;
699 }
700
701 /* Start output on the (sub-) channel. */
702 cxsend (c);
703
704 return (teoir);
705 }
706
707 static void
708 cxintr (int bnum)
709 {
710 cx_board_t *b = cxboard + bnum;
711 while (! (inw (BSR(b->port)) & BSR_NOINTR)) {
712 /* Acknowledge the interrupt to enter the interrupt context. */
713 /* Read the local interrupt vector register. */
714 unsigned char livr = inb (IACK(b->port, BRD_INTR_LEVEL));
715 cx_chan_t *c = b->chan + (livr>>2 & 0xf);
716 unsigned short port = c->chip->port;
717 unsigned short eoiport = REOIR(port);
718 unsigned char eoi = 0;
719
720 if (c->type == T_NONE) {
721 printf ("cx%d.%d: unexpected interrupt, livr=0x%x\n",
722 c->board->num, c->num, livr);
723 continue; /* incorrect channel number? */
724 }
725 /* print (("cx%d.%d: interrupt, livr=0x%x\n",
726 c->board->num, c->num, livr)); */
727
728 /* Clear RTS to stop receiver data flow while we are busy
729 * processing the interrupt, thus avoiding underruns. */
730 if (! c->sopt.norts) {
731 outb (MSVR_RTS(port), 0);
732 c->rts = 0;
733 }
734
735 switch (livr & 3) {
736 case LIV_EXCEP: /* receive exception */
737 case LIV_RXDATA: /* receive interrupt */
738 ++c->stat->rintr;
739 switch (c->mode) {
740 case M_ASYNC: eoi = cxrinta (c); break;
741 case M_HDLC: cxrinth (c); break;
742 default:; /* No bisync and X.21 yet */
743 }
744 break;
745 case LIV_TXDATA: /* transmit interrupt */
746 ++c->stat->tintr;
747 eoiport = TEOIR(port);
748 switch (c->mode) {
749 case M_ASYNC: cxtinta (c); break;
750 case M_HDLC: eoi = cxtinth (c); break;
751 default:; /* No bisync and X.21 yet */
752 }
753 break;
754 case LIV_MODEM: /* modem/timer interrupt */
755 ++c->stat->mintr;
756 eoiport = MEOIR(port);
757 cxmint (c);
758 break;
759 }
760
761 /* Raise RTS for this channel if and only if
762 * both receive buffers are empty. */
763 if (! c->sopt.norts && (inb (CSR(port)) & CSRA_RXEN) &&
764 (inb (ARBSTS(port)) & BSTS_OWN24) &&
765 (inb (BRBSTS(port)) & BSTS_OWN24)) {
766 outb (MSVR_RTS(port), MSV_RTS);
767 c->rts = 1;
768 }
769
770 /* Exit from interrupt context. */
771 outb (eoiport, eoi);
772
773 /* Master channel - start output on all idle subchannels. */
774 if (c->master == c->ifp && c->slaveq &&
775 (livr & 3) == LIV_TXDATA && c->mode == M_HDLC &&
776 ! sppp_isempty (c->ifp)) {
777 cx_chan_t *q;
778
779 for (q=c->slaveq; q; q=q->slaveq)
780 if ((q->ifp->if_flags & IFF_RUNNING) &&
781 ! (q->ifp->if_flags & IFF_OACTIVE))
782 cxsend (q);
783 }
784 }
785 }
786
787 /*
788 * Process the received packet.
789 */
790 static void
791 cxinput (cx_chan_t *c, void *buf, unsigned len)
792 {
793 /* Make an mbuf. */
794 struct mbuf *m = makembuf (buf, len);
795 if (! m) {
796 if (c->ifp->if_flags & IFF_DEBUG)
797 printf ("cx%d.%d: no memory for packet\n",
798 c->board->num, c->num);
799 ++c->ifp->if_iqdrops;
800 return;
801 }
802 m->m_pkthdr.rcvif = c->master;
803 #ifdef DEBUG
804 if (c->ifp->if_flags & IFF_DEBUG)
805 printmbuf (m);
806 #endif
807
808 #if NBPFILTER > 0
809 /*
810 * Check if there's a BPF listener on this interface.
811 * If so, hand off the raw packet to bpf.
812 */
813 if (c->ifp->if_bpf)
814 bpf_tap (c->ifp, buf, len);
815 #endif
816
817 /* Count the received bytes to the subchannel, not the master. */
818 c->master->if_ibytes -= len + 3;
819 c->ifp->if_ibytes += len + 3;
820
821 sppp_input (c->master, m);
822 }
823
824 void cxswitch (cx_chan_t *c, cx_soft_opt_t new)
825 {
826 new.ext = 0;
827 if (! new.ext) {
828 struct sppp *sp = (struct sppp*) c->ifp;
829
830 if (new.cisco)
831 sp->pp_flags |= PP_CISCO;
832 else
833 sp->pp_flags &= ~PP_CISCO;
834 if (new.keepalive)
835 sp->pp_flags |= PP_KEEPALIVE;
836 else
837 sp->pp_flags &= ~PP_KEEPALIVE;
838 }
839 c->sopt = new;
840 }
Cache object: 71e9dd56337b6ae0a655fe131d0e19af
|