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