FreeBSD/Linux Kernel Cross Reference
sys/i386/isa/cx.c
1 /*
2 * Cronyx-Sigma adapter driver for FreeBSD.
3 * Supports PPP/HDLC protocol in synchronous mode,
4 * and asyncronous channels with full modem control.
5 *
6 * Copyright (C) 1994 Cronyx Ltd.
7 * Author: Serge Vakulenko, <vak@zebub.msk.su>
8 *
9 * This software is distributed with NO WARRANTIES, not even the implied
10 * warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11 *
12 * Authors grant any other persons or organisations permission to use
13 * or modify this software as long as this message is kept with the software,
14 * all derivative works or modified versions.
15 *
16 * Version 1.9, Wed Oct 4 18:58:15 MSK 1995
17 *
18 * $FreeBSD: releng/5.0/sys/i386/isa/cx.c 105224 2002-10-16 10:16:17Z phk $
19 *
20 */
21 #undef DEBUG
22
23 #include "cx.h"
24
25 #include <sys/param.h>
26 #include <sys/systm.h>
27 #include <sys/kernel.h>
28 #include <sys/fcntl.h>
29 #include <sys/conf.h>
30 #include <sys/tty.h>
31 #include <sys/socket.h>
32 #include <net/if.h>
33
34 # define t_out t_outq
35 # define RB_LEN(q) ((q).c_cc)
36 # define RB_GETC(q) getc(&q)
37 #ifndef TSA_CARR_ON /* FreeBSD 2.x before not long after 2.0.5 */
38 # define TSA_CARR_ON(tp) tp
39 # define TSA_OLOWAT(q) ((caddr_t)&(q)->t_out)
40 #endif
41
42 #include <machine/cronyx.h>
43 #include <i386/isa/cxreg.h>
44
45 /* XXX imported from if_cx.c. */
46 void cxswitch (cx_chan_t *c, cx_soft_opt_t new);
47
48 /* XXX exported. */
49 void cxmint (cx_chan_t *c);
50 int cxrinta (cx_chan_t *c);
51 void cxtinta (cx_chan_t *c);
52 timeout_t cxtimeout;
53
54 #ifdef DEBUG
55 # define print(s) printf s
56 #else
57 # define print(s) {/*void*/}
58 #endif
59
60 #define DMABUFSZ (6*256) /* buffer size */
61 #define BYTE *(unsigned char*)&
62 #define UNIT(u) (minor(u) & 077)
63 #define UNIT_CTL 077
64
65 extern cx_board_t cxboard [NCX]; /* adapter state structures */
66 extern cx_chan_t *cxchan [NCX*NCHAN]; /* unit to channel struct pointer */
67 static struct tty cx_tty [NCX*NCHAN]; /* tty data */
68
69 static d_open_t cxopen;
70 static d_close_t cxclose;
71 static d_ioctl_t cxioctl;
72
73 #define CDEV_MAJOR 42
74 /* Don't make this static, since if_cx.c uses it. */
75 struct cdevsw cx_cdevsw = {
76 /* open */ cxopen,
77 /* close */ cxclose,
78 /* read */ ttyread,
79 /* write */ ttywrite,
80 /* ioctl */ cxioctl,
81 /* poll */ ttypoll,
82 /* mmap */ nommap,
83 /* strategy */ nostrategy,
84 /* name */ "cx",
85 /* maj */ CDEV_MAJOR,
86 /* dump */ nodump,
87 /* psize */ nopsize,
88 /* flags */ D_TTY | D_KQFILTER,
89 /* kqfilter */ ttykqfilter,
90 };
91
92 static void cxoproc (struct tty *tp);
93 static void cxstop (struct tty *tp, int flag);
94 static int cxparam (struct tty *tp, struct termios *t);
95
96 static int
97 cxopen (dev_t dev, int flag, int mode, struct thread *td)
98 {
99 int unit = UNIT (dev);
100 cx_chan_t *c = cxchan[unit];
101 unsigned short port;
102 struct tty *tp;
103 int error = 0;
104
105 if (unit == UNIT_CTL) {
106 print (("cx: cxopen /dev/cronyx\n"));
107 return (0);
108 }
109 if (unit >= NCX*NCHAN || !c || c->type==T_NONE)
110 return (ENXIO);
111 port = c->chip->port;
112 print (("cx%d.%d: cxopen unit=%d\n", c->board->num, c->num, unit));
113 if (c->mode != M_ASYNC)
114 return (EBUSY);
115 if (! c->ttyp) {
116 c->ttyp = &cx_tty[unit];
117 c->ttyp->t_oproc = cxoproc;
118 c->ttyp->t_stop = cxstop;
119 c->ttyp->t_param = cxparam;
120 }
121 dev->si_tty = c->ttyp;
122 tp = c->ttyp;
123 tp->t_dev = dev;
124 if ((tp->t_state & TS_ISOPEN) && (tp->t_state & TS_XCLUDE) &&
125 suser(td))
126 return (EBUSY);
127 if (! (tp->t_state & TS_ISOPEN)) {
128 ttychars (tp);
129 if (tp->t_ispeed == 0) {
130 tp->t_iflag = 0;
131 tp->t_oflag = 0;
132 tp->t_lflag = 0;
133 tp->t_cflag = CREAD | CS8 | HUPCL;
134 tp->t_ispeed = c->rxbaud;
135 tp->t_ospeed = c->txbaud;
136 }
137 cxparam (tp, &tp->t_termios);
138 ttsetwater (tp);
139 }
140
141 spltty ();
142 if (! (tp->t_state & TS_ISOPEN)) {
143 /*
144 * Compute optimal receiver buffer length.
145 * The best choice is rxbaud/400.
146 * Make it even, to avoid byte-wide DMA transfers.
147 * --------------------------
148 * Baud rate Buffer length
149 * --------------------------
150 * 300 4
151 * 1200 4
152 * 9600 24
153 * 19200 48
154 * 38400 96
155 * 57600 192
156 * 115200 288
157 * --------------------------
158 */
159 int rbsz = (c->rxbaud + 800 - 1) / 800 * 2;
160 if (rbsz < 4)
161 rbsz = 4;
162 else if (rbsz > DMABUFSZ)
163 rbsz = DMABUFSZ;
164
165 /* Initialize channel, enable receiver. */
166 cx_cmd (port, CCR_INITCH | CCR_ENRX);
167 cx_cmd (port, CCR_INITCH | CCR_ENRX);
168
169 /* Start receiver. */
170 outw (ARBCNT(port), rbsz);
171 outw (BRBCNT(port), rbsz);
172 outw (ARBSTS(port), BSTS_OWN24);
173 outw (BRBSTS(port), BSTS_OWN24);
174
175 /* Enable interrupts. */
176 outb (IER(port), IER_RXD | IER_RET | IER_TXD | IER_MDM);
177
178 cx_chan_dtr (c, 1);
179 cx_chan_rts (c, 1);
180 }
181 if (cx_chan_cd (c))
182 (*linesw[tp->t_line].l_modem)(tp, 1);
183 if (! (flag & O_NONBLOCK)) {
184 /* Lock the channel against cxconfig while we are
185 * waiting for carrier. */
186 c->sopt.lock = 1;
187 while (!(tp->t_cflag & CLOCAL) && !(tp->t_state & TS_CARR_ON))
188 if ((error = tsleep (TSA_CARR_ON(tp), TTIPRI | PCATCH,
189 "cxdcd", 0)))
190 break;
191 c->sopt.lock = 0; /* Unlock the channel. */
192 }
193 print (("cx%d.%d: cxopen done csr=%b\n", c->board->num, c->num,
194 inb(CSR(c->chip->port)), CSRA_BITS));
195 spl0 ();
196 if (error)
197 return (error);
198 error = (*linesw[tp->t_line].l_open) (dev, tp);
199 return (error);
200 }
201
202 static int
203 cxclose (dev_t dev, int flag, int mode, struct thread *td)
204 {
205 int unit = UNIT (dev);
206 cx_chan_t *c = cxchan[unit];
207 struct tty *tp;
208 int s;
209
210 if (unit == UNIT_CTL)
211 return (0);
212 tp = c->ttyp;
213 (*linesw[tp->t_line].l_close) (tp, flag);
214
215 /* Disable receiver.
216 * Transmitter continues sending the queued data. */
217 s = spltty ();
218 outb (CAR(c->chip->port), c->num & 3);
219 outb (IER(c->chip->port), IER_TXD | IER_MDM);
220 cx_cmd (c->chip->port, CCR_DISRX);
221
222 /* Clear DTR and RTS. */
223 if ((tp->t_cflag & HUPCL) || ! (tp->t_state & TS_ISOPEN)) {
224 cx_chan_dtr (c, 0);
225 cx_chan_rts (c, 0);
226 }
227
228 /* Stop sending break. */
229 if (c->brk == BRK_SEND) {
230 c->brk = BRK_STOP;
231 if (! (tp->t_state & TS_BUSY))
232 cxoproc (tp);
233 }
234 splx (s);
235 ttyclose (tp);
236 return (0);
237 }
238
239 static int
240 cxioctl (dev_t dev, u_long cmd, caddr_t data, int flag, struct thread *td)
241 {
242 int unit = UNIT (dev);
243 cx_chan_t *c, *m;
244 cx_stat_t *st;
245 struct tty *tp;
246 int error, s;
247 unsigned char msv;
248 struct ifnet *master;
249
250 if (unit == UNIT_CTL) {
251 /* Process an ioctl request on /dev/cronyx */
252 cx_options_t *o = (cx_options_t*) data;
253
254 if (o->board >= NCX || o->channel >= NCHAN)
255 return (EINVAL);
256 c = &cxboard[o->board].chan[o->channel];
257 if (c->type == T_NONE)
258 return (ENXIO);
259 switch (cmd) {
260 default:
261 return (EINVAL);
262
263 case CXIOCSETMODE:
264 print (("cx%d.%d: CXIOCSETMODE\n", o->board, o->channel));
265 if (c->type == T_NONE)
266 return (EINVAL);
267 if (c->type == T_ASYNC && o->mode != M_ASYNC)
268 return (EINVAL);
269 if (o->mode == M_ASYNC)
270 switch (c->type) {
271 case T_SYNC_RS232:
272 case T_SYNC_V35:
273 case T_SYNC_RS449:
274 return (EINVAL);
275 }
276 /* Somebody is waiting for carrier? */
277 if (c->sopt.lock)
278 return (EBUSY);
279 /* /dev/ttyXX is already opened by someone? */
280 if (c->mode == M_ASYNC && c->ttyp &&
281 (c->ttyp->t_state & TS_ISOPEN))
282 return (EBUSY);
283 /* Network interface is up? */
284 if (c->mode != M_ASYNC && (c->ifp->if_flags & IFF_UP))
285 return (EBUSY);
286
287 /* Find the master interface. */
288 master = *o->master ? ifunit (o->master) : c->ifp;
289 if (! master)
290 return (EINVAL);
291 m = cxchan[master->if_unit];
292
293 /* Leave the previous master queue. */
294 if (c->master != c->ifp) {
295 cx_chan_t *p = cxchan[c->master->if_unit];
296
297 for (; p; p=p->slaveq)
298 if (p->slaveq == c)
299 p->slaveq = c->slaveq;
300 }
301
302 /* Set up new master. */
303 c->master = master;
304 c->slaveq = 0;
305
306 /* Join the new master queue. */
307 if (c->master != c->ifp) {
308 c->slaveq = m->slaveq;
309 m->slaveq = c;
310 }
311
312 c->mode = o->mode;
313 c->rxbaud = o->rxbaud;
314 c->txbaud = o->txbaud;
315 c->opt = o->opt;
316 c->aopt = o->aopt;
317 c->hopt = o->hopt;
318 c->bopt = o->bopt;
319 c->xopt = o->xopt;
320 switch (c->num) {
321 case 0: c->board->if0type = o->iftype; break;
322 case 8: c->board->if8type = o->iftype; break;
323 }
324 s = spltty ();
325 cxswitch (c, o->sopt);
326 cx_setup_chan (c);
327 outb (IER(c->chip->port), 0);
328 splx (s);
329 break;
330
331 case CXIOCGETSTAT:
332 st = (cx_stat_t*) data;
333 st->rintr = c->stat->rintr;
334 st->tintr = c->stat->tintr;
335 st->mintr = c->stat->mintr;
336 st->ibytes = c->stat->ibytes;
337 st->ipkts = c->stat->ipkts;
338 st->ierrs = c->stat->ierrs;
339 st->obytes = c->stat->obytes;
340 st->opkts = c->stat->opkts;
341 st->oerrs = c->stat->oerrs;
342 break;
343
344 case CXIOCGETMODE:
345 print (("cx%d.%d: CXIOCGETMODE\n", o->board, o->channel));
346 o->type = c->type;
347 o->mode = c->mode;
348 o->rxbaud = c->rxbaud;
349 o->txbaud = c->txbaud;
350 o->opt = c->opt;
351 o->aopt = c->aopt;
352 o->hopt = c->hopt;
353 o->bopt = c->bopt;
354 o->xopt = c->xopt;
355 o->sopt = c->sopt;
356 switch (c->num) {
357 case 0: o->iftype = c->board->if0type; break;
358 case 8: o->iftype = c->board->if8type; break;
359 }
360 if (c->master != c->ifp)
361 snprintf (o->master, sizeof(o->master),
362 "%s%d", c->master->if_name,
363 c->master->if_unit);
364 else
365 *o->master = 0;
366 break;
367 }
368 return (0);
369 }
370
371 c = cxchan[unit];
372 tp = c->ttyp;
373 if (! tp)
374 return (EINVAL);
375 error = (*linesw[tp->t_line].l_ioctl) (tp, cmd, data, flag, td);
376 if (error != ENOIOCTL)
377 return (error);
378 error = ttioctl (tp, cmd, data, flag);
379 if (error != ENOIOCTL)
380 return (error);
381
382 s = spltty ();
383 switch (cmd) {
384 default:
385 splx (s);
386 return (ENOTTY);
387 case TIOCSBRK: /* Start sending line break */
388 c->brk = BRK_SEND;
389 if (! (tp->t_state & TS_BUSY))
390 cxoproc (tp);
391 break;
392 case TIOCCBRK: /* Stop sending line break */
393 c->brk = BRK_STOP;
394 if (! (tp->t_state & TS_BUSY))
395 cxoproc (tp);
396 break;
397 case TIOCSDTR: /* Set DTR */
398 cx_chan_dtr (c, 1);
399 break;
400 case TIOCCDTR: /* Clear DTR */
401 cx_chan_dtr (c, 0);
402 break;
403 case TIOCMSET: /* Set DTR/RTS */
404 cx_chan_dtr (c, (*(int*)data & TIOCM_DTR) ? 1 : 0);
405 cx_chan_rts (c, (*(int*)data & TIOCM_RTS) ? 1 : 0);
406 break;
407 case TIOCMBIS: /* Add DTR/RTS */
408 if (*(int*)data & TIOCM_DTR) cx_chan_dtr (c, 1);
409 if (*(int*)data & TIOCM_RTS) cx_chan_rts (c, 1);
410 break;
411 case TIOCMBIC: /* Clear DTR/RTS */
412 if (*(int*)data & TIOCM_DTR) cx_chan_dtr (c, 0);
413 if (*(int*)data & TIOCM_RTS) cx_chan_rts (c, 0);
414 break;
415 case TIOCMGET: /* Get modem status */
416 msv = inb (MSVR(c->chip->port));
417 *(int*)data = TIOCM_LE; /* always enabled while open */
418 if (msv & MSV_DSR) *(int*)data |= TIOCM_DSR;
419 if (msv & MSV_CTS) *(int*)data |= TIOCM_CTS;
420 if (msv & MSV_CD) *(int*)data |= TIOCM_CD;
421 if (c->dtr) *(int*)data |= TIOCM_DTR;
422 if (c->rts) *(int*)data |= TIOCM_RTS;
423 break;
424 }
425 splx (s);
426 return (0);
427 }
428
429 /*
430 * Fill transmitter buffer with data.
431 */
432 static void
433 cxout (cx_chan_t *c, char b)
434 {
435 unsigned char *buf, *p, sym;
436 unsigned short port = c->chip->port, len = 0, cnt_port, sts_port;
437 struct tty *tp = c->ttyp;
438
439 if (! tp)
440 return;
441
442 /* Choose the buffer. */
443 if (b == 'A') {
444 buf = c->atbuf;
445 cnt_port = ATBCNT(port);
446 sts_port = ATBSTS(port);
447 } else {
448 buf = c->btbuf;
449 cnt_port = BTBCNT(port);
450 sts_port = BTBSTS(port);
451 }
452
453 /* Is it busy? */
454 if (inb (sts_port) & BSTS_OWN24) {
455 tp->t_state |= TS_BUSY;
456 return;
457 }
458
459 switch (c->brk) {
460 case BRK_SEND:
461 *buf++ = 0; /* extended transmit command */
462 *buf++ = 0x81; /* send break */
463 *buf++ = 0; /* extended transmit command */
464 *buf++ = 0x82; /* insert delay */
465 *buf++ = 250; /* 1/4 of second */
466 *buf++ = 0; /* extended transmit command */
467 *buf++ = 0x82; /* insert delay */
468 *buf++ = 250; /* + 1/4 of second */
469 len = 8;
470 c->brk = BRK_IDLE;
471 break;
472 case BRK_STOP:
473 *buf++ = 0; /* extended transmit command */
474 *buf++ = 0x83; /* stop break */
475 len = 2;
476 c->brk = BRK_IDLE;
477 break;
478 case BRK_IDLE:
479 p = buf;
480 if (tp->t_iflag & IXOFF)
481 while (RB_LEN (tp->t_out) && p<buf+DMABUFSZ-1) {
482 sym = RB_GETC (tp->t_out);
483 /* Send XON/XOFF out of band. */
484 if (sym == tp->t_cc[VSTOP]) {
485 outb (STCR(port), STC_SNDSPC|STC_SSPC_2);
486 continue;
487 }
488 if (sym == tp->t_cc[VSTART]) {
489 outb (STCR(port), STC_SNDSPC|STC_SSPC_1);
490 continue;
491 }
492 /* Duplicate NULLs in ETC mode. */
493 if (! sym)
494 *p++ = 0;
495 *p++ = sym;
496 }
497 else
498 while (RB_LEN (tp->t_out) && p<buf+DMABUFSZ-1) {
499 sym = RB_GETC (tp->t_out);
500 /* Duplicate NULLs in ETC mode. */
501 if (! sym)
502 *p++ = 0;
503 *p++ = sym;
504 }
505 len = p - buf;
506 break;
507 }
508
509 /* Start transmitter. */
510 if (len) {
511 outw (cnt_port, len);
512 outb (sts_port, BSTS_INTR | BSTS_OWN24);
513 c->stat->obytes += len;
514 tp->t_state |= TS_BUSY;
515 print (("cx%d.%d: out %d bytes to %c\n",
516 c->board->num, c->num, len, b));
517 }
518 }
519
520 static void
521 cxoproc (struct tty *tp)
522 {
523 int unit = UNIT (tp->t_dev);
524 cx_chan_t *c = cxchan[unit];
525 unsigned short port = c->chip->port;
526 int s = spltty ();
527
528 /* Set current channel number */
529 outb (CAR(port), c->num & 3);
530
531 if (! (tp->t_state & (TS_TIMEOUT | TS_TTSTOP))) {
532 /* Start transmitter. */
533 if (! (inb (CSR(port)) & CSRA_TXEN))
534 cx_cmd (port, CCR_ENTX);
535
536 /* Determine the buffer order. */
537 if (inb (DMABSTS(port)) & DMABSTS_NTBUF) {
538 cxout (c, 'B');
539 cxout (c, 'A');
540 } else {
541 cxout (c, 'A');
542 cxout (c, 'B');
543 }
544 }
545 #ifndef TS_ASLEEP /* FreeBSD some time after 2.0.5 */
546 ttwwakeup(tp);
547 #else
548 if (RB_LEN (tp->t_out) <= tp->t_lowat) {
549 if (tp->t_state & TS_ASLEEP) {
550 tp->t_state &= ~TS_ASLEEP;
551 wakeup(TSA_OLOWAT(tp));
552 }
553 selwakeup(&tp->t_wsel);
554 }
555 #endif
556 splx (s);
557 }
558
559 static int
560 cxparam (struct tty *tp, struct termios *t)
561 {
562 int unit = UNIT (tp->t_dev);
563 cx_chan_t *c = cxchan[unit];
564 unsigned short port = c->chip->port;
565 int clock, period, s;
566 cx_cor1_async_t cor1;
567
568 if (t->c_ospeed == 0) {
569 /* Clear DTR and RTS. */
570 s = spltty ();
571 cx_chan_dtr (c, 0);
572 cx_chan_rts (c, 0);
573 splx (s);
574 print (("cx%d.%d: cxparam (hangup)\n", c->board->num, c->num));
575 return (0);
576 }
577 print (("cx%d.%d: cxparam\n", c->board->num, c->num));
578
579 /* Check requested parameters. */
580 if (t->c_ospeed < 300 || t->c_ospeed > 256*1024)
581 return(EINVAL);
582 if (t->c_ispeed && (t->c_ispeed < 300 || t->c_ispeed > 256*1024))
583 return(EINVAL);
584
585 /* And copy them to tty and channel structures. */
586 c->rxbaud = tp->t_ispeed = t->c_ispeed;
587 c->txbaud = tp->t_ospeed = t->c_ospeed;
588 tp->t_cflag = t->c_cflag;
589
590 /* Set character length and parity mode. */
591 BYTE cor1 = 0;
592 switch (t->c_cflag & CSIZE) {
593 default:
594 case CS8: cor1.charlen = 7; break;
595 case CS7: cor1.charlen = 6; break;
596 case CS6: cor1.charlen = 5; break;
597 case CS5: cor1.charlen = 4; break;
598 }
599 if (t->c_cflag & PARENB) {
600 cor1.parmode = PARM_NORMAL;
601 cor1.ignpar = 0;
602 cor1.parity = (t->c_cflag & PARODD) ? PAR_ODD : PAR_EVEN;
603 } else {
604 cor1.parmode = PARM_NOPAR;
605 cor1.ignpar = 1;
606 }
607
608 /* Enable/disable hardware CTS. */
609 c->aopt.cor2.ctsae = (t->c_cflag & CRTSCTS) ? 1 : 0;
610 /* Handle DSR as CTS. */
611 c->aopt.cor2.dsrae = (t->c_cflag & CRTSCTS) ? 1 : 0;
612 /* Enable extended transmit command mode.
613 * Unfortunately, there is no other method for sending break. */
614 c->aopt.cor2.etc = 1;
615 /* Enable/disable hardware XON/XOFF. */
616 c->aopt.cor2.ixon = (t->c_iflag & IXON) ? 1 : 0;
617 c->aopt.cor2.ixany = (t->c_iflag & IXANY) ? 1 : 0;
618
619 /* Set the number of stop bits. */
620 if (t->c_cflag & CSTOPB)
621 c->aopt.cor3.stopb = STOPB_2;
622 else
623 c->aopt.cor3.stopb = STOPB_1;
624 /* Disable/enable passing XON/XOFF chars to the host. */
625 c->aopt.cor3.scde = (t->c_iflag & IXON) ? 1 : 0;
626 c->aopt.cor3.flowct = (t->c_iflag & IXON) ? FLOWCC_NOTPASS : FLOWCC_PASS;
627
628 c->aopt.schr1 = t->c_cc[VSTART]; /* XON */
629 c->aopt.schr2 = t->c_cc[VSTOP]; /* XOFF */
630
631 /* Set current channel number. */
632 s = spltty ();
633 outb (CAR(port), c->num & 3);
634
635 /* Set up receiver clock values. */
636 cx_clock (c->chip->oscfreq, c->rxbaud, &clock, &period);
637 c->opt.rcor.clk = clock;
638 outb (RCOR(port), BYTE c->opt.rcor);
639 outb (RBPR(port), period);
640
641 /* Set up transmitter clock values. */
642 cx_clock (c->chip->oscfreq, c->txbaud, &clock, &period);
643 c->opt.tcor.clk = clock;
644 c->opt.tcor.ext1x = 0;
645 outb (TCOR(port), BYTE c->opt.tcor);
646 outb (TBPR(port), period);
647
648 outb (COR2(port), BYTE c->aopt.cor2);
649 outb (COR3(port), BYTE c->aopt.cor3);
650 outb (SCHR1(port), c->aopt.schr1);
651 outb (SCHR2(port), c->aopt.schr2);
652
653 if (BYTE c->aopt.cor1 != BYTE cor1) {
654 BYTE c->aopt.cor1 = BYTE cor1;
655 outb (COR1(port), BYTE c->aopt.cor1);
656 /* Any change to COR1 require reinitialization. */
657 /* Unfortunately, it may cause transmitter glitches... */
658 cx_cmd (port, CCR_INITCH);
659 }
660 splx (s);
661 return (0);
662 }
663
664 /*
665 * Stop output on a line
666 */
667 static void
668 cxstop (struct tty *tp, int flag)
669 {
670 cx_chan_t *c = cxchan[UNIT(tp->t_dev)];
671 unsigned short port = c->chip->port;
672 int s = spltty ();
673
674 if (tp->t_state & TS_BUSY) {
675 print (("cx%d.%d: cxstop\n", c->board->num, c->num));
676
677 /* Set current channel number */
678 outb (CAR(port), c->num & 3);
679
680 /* Stop transmitter */
681 cx_cmd (port, CCR_DISTX);
682 }
683 splx (s);
684 }
685
686 /*
687 * Handle receive interrupts, including receive errors and
688 * receive timeout interrupt.
689 */
690 int cxrinta (cx_chan_t *c)
691 {
692 unsigned short port = c->chip->port;
693 unsigned short len = 0, risr = inw (RISR(port)), reoir = 0;
694 struct tty *tp = c->ttyp;
695
696 /* Compute optimal receiver buffer length. */
697 int rbsz = (c->rxbaud + 800 - 1) / 800 * 2;
698 if (rbsz < 4)
699 rbsz = 4;
700 else if (rbsz > DMABUFSZ)
701 rbsz = DMABUFSZ;
702
703 if (risr & RISA_TIMEOUT) {
704 unsigned long rcbadr = (unsigned short) inw (RCBADRL(port)) |
705 (long) inw (RCBADRU(port)) << 16;
706 unsigned char *buf = 0;
707 unsigned short cnt_port = 0, sts_port = 0;
708 if (rcbadr >= c->brphys && rcbadr < c->brphys+DMABUFSZ) {
709 buf = c->brbuf;
710 len = rcbadr - c->brphys;
711 cnt_port = BRBCNT(port);
712 sts_port = BRBSTS(port);
713 } else if (rcbadr >= c->arphys && rcbadr < c->arphys+DMABUFSZ) {
714 buf = c->arbuf;
715 len = rcbadr - c->arphys;
716 cnt_port = ARBCNT(port);
717 sts_port = ARBSTS(port);
718 } else
719 printf ("cx%d.%d: timeout: invalid buffer address\n",
720 c->board->num, c->num);
721
722 if (len) {
723 print (("cx%d.%d: async receive timeout (%d bytes), risr=%b, arbsts=%b, brbsts=%b\n",
724 c->board->num, c->num, len, risr, RISA_BITS,
725 inb (ARBSTS(port)), BSTS_BITS, inb (BRBSTS(port)), BSTS_BITS));
726 c->stat->ibytes += len;
727 if (tp && (tp->t_state & TS_ISOPEN)) {
728 int i;
729 int (*rint)(int, struct tty *) =
730 linesw[tp->t_line].l_rint;
731
732 for (i=0; i<len; ++i)
733 (*rint) (buf[i], tp);
734 }
735
736 /* Restart receiver. */
737 outw (cnt_port, rbsz);
738 outb (sts_port, BSTS_OWN24);
739 }
740 return (REOI_TERMBUFF);
741 }
742
743 print (("cx%d.%d: async receive interrupt, risr=%b, arbsts=%b, brbsts=%b\n",
744 c->board->num, c->num, risr, RISA_BITS,
745 inb (ARBSTS(port)), BSTS_BITS, inb (BRBSTS(port)), BSTS_BITS));
746
747 if (risr & RIS_BUSERR) {
748 printf ("cx%d.%d: receive bus error\n", c->board->num, c->num);
749 ++c->stat->ierrs;
750 }
751 if (risr & (RIS_OVERRUN | RISA_PARERR | RISA_FRERR | RISA_BREAK)) {
752 int err = 0;
753
754 if (risr & RISA_PARERR)
755 err |= TTY_PE;
756 if (risr & RISA_FRERR)
757 err |= TTY_FE;
758 #ifdef TTY_OE
759 if (risr & RIS_OVERRUN)
760 err |= TTY_OE;
761 #endif
762 #ifdef TTY_BI
763 if (risr & RISA_BREAK)
764 err |= TTY_BI;
765 #endif
766 print (("cx%d.%d: receive error %x\n", c->board->num, c->num, err));
767 if (tp && (tp->t_state & TS_ISOPEN))
768 (*linesw[tp->t_line].l_rint) (err, tp);
769 ++c->stat->ierrs;
770 }
771
772 /* Discard exception characters. */
773 if ((risr & RISA_SCMASK) && tp && (tp->t_iflag & IXON))
774 reoir |= REOI_DISCEXC;
775
776 /* Handle received data. */
777 if ((risr & RIS_EOBUF) && tp && (tp->t_state & TS_ISOPEN)) {
778 int (*rint)(int, struct tty *) = linesw[tp->t_line].l_rint;
779 unsigned char *buf;
780 int i;
781
782 len = (risr & RIS_BB) ? inw(BRBCNT(port)) : inw(ARBCNT(port));
783
784 print (("cx%d.%d: async: %d bytes received\n",
785 c->board->num, c->num, len));
786 c->stat->ibytes += len;
787
788 buf = (risr & RIS_BB) ? c->brbuf : c->arbuf;
789 for (i=0; i<len; ++i)
790 (*rint) (buf[i], tp);
791 }
792
793 /* Restart receiver. */
794 if (! (inb (ARBSTS(port)) & BSTS_OWN24)) {
795 outw (ARBCNT(port), rbsz);
796 outb (ARBSTS(port), BSTS_OWN24);
797 }
798 if (! (inb (BRBSTS(port)) & BSTS_OWN24)) {
799 outw (BRBCNT(port), rbsz);
800 outb (BRBSTS(port), BSTS_OWN24);
801 }
802 return (reoir);
803 }
804
805 /*
806 * Handle transmit interrupt.
807 */
808 void cxtinta (cx_chan_t *c)
809 {
810 struct tty *tp = c->ttyp;
811 unsigned short port = c->chip->port;
812 unsigned char tisr = inb (TISR(port));
813
814 print (("cx%d.%d: async transmit interrupt, tisr=%b, atbsts=%b, btbsts=%b\n",
815 c->board->num, c->num, tisr, TIS_BITS,
816 inb (ATBSTS(port)), BSTS_BITS, inb (BTBSTS(port)), BSTS_BITS));
817
818 if (tisr & TIS_BUSERR) {
819 printf ("cx%d.%d: transmit bus error\n",
820 c->board->num, c->num);
821 ++c->stat->oerrs;
822 } else if (tisr & TIS_UNDERRUN) {
823 printf ("cx%d.%d: transmit underrun error\n",
824 c->board->num, c->num);
825 ++c->stat->oerrs;
826 }
827 if (tp) {
828 tp->t_state &= ~(TS_BUSY | TS_FLUSH);
829 if (tp->t_line)
830 (*linesw[tp->t_line].l_start) (tp);
831 else
832 cxoproc (tp);
833 }
834 }
835
836 /*
837 * Handle modem interrupt.
838 */
839 void cxmint (cx_chan_t *c)
840 {
841 unsigned short port = c->chip->port;
842 unsigned char misr = inb (MISR(port));
843 unsigned char msvr = inb (MSVR(port));
844 struct tty *tp = c->ttyp;
845
846 if (c->mode != M_ASYNC) {
847 printf ("cx%d.%d: unexpected modem interrupt, misr=%b, msvr=%b\n",
848 c->board->num, c->num, misr, MIS_BITS, msvr, MSV_BITS);
849 return;
850 }
851 print (("cx%d.%d: modem interrupt, misr=%b, msvr=%b\n",
852 c->board->num, c->num, misr, MIS_BITS, msvr, MSV_BITS));
853
854 /* Ignore DSR events. */
855 /* Ignore RTC/CTS events, handled by hardware. */
856 /* Handle carrier detect/loss. */
857 if (tp && (misr & MIS_CCD))
858 (*linesw[tp->t_line].l_modem) (tp, (msvr & MSV_CD) != 0);
859 }
860
861 /*
862 * Recover after lost transmit interrupts.
863 */
864 void cxtimeout (void *a)
865 {
866 cx_board_t *b;
867 cx_chan_t *c;
868 struct tty *tp;
869 int s;
870
871 for (b=cxboard; b<cxboard+NCX; ++b)
872 for (c=b->chan; c<b->chan+NCHAN; ++c) {
873 tp = c->ttyp;
874 if (c->type==T_NONE || c->mode!=M_ASYNC || !tp)
875 continue;
876 s = spltty ();
877 if (tp->t_state & TS_BUSY) {
878 tp->t_state &= ~TS_BUSY;
879 if (tp->t_line)
880 (*linesw[tp->t_line].l_start) (tp);
881 else
882 cxoproc (tp);
883 }
884 splx (s);
885 }
886 timeout (cxtimeout, 0, hz*5);
887 }
888
889
890 static void cx_drvinit(void *unused)
891 {
892
893 cdevsw_add(&cx_cdevsw);
894 }
895
896 SYSINIT(cxdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,cx_drvinit,NULL)
Cache object: 46acc8498fd68e8d1a363de701511df4
|