Merge branches 'master' and 'suser_to_priv'
[dragonfly.git] / sys / dev / netif / cx / cx.c
blob9249142e63e62b21dd1052174231224bb2feaddb
1 /*
2 * Cronyx-Sigma adapter driver for FreeBSD.
3 * Supports PPP/HDLC protocol in synchronous mode,
4 * and asyncronous channels with full modem control.
6 * Copyright (C) 1994 Cronyx Ltd.
7 * Author: Serge Vakulenko, <vak@zebub.msk.su>
9 * This software is distributed with NO WARRANTIES, not even the implied
10 * warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
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.
16 * Version 1.9, Wed Oct 4 18:58:15 MSK 1995
18 * $FreeBSD: src/sys/i386/isa/cx.c,v 1.45.2.1 2001/02/26 04:23:09 jlemon Exp $
19 * $DragonFly: src/sys/dev/netif/cx/cx.c,v 1.20 2006/12/22 23:26:19 swildner Exp $
22 #undef DEBUG
24 #include "use_cx.h"
26 #include <sys/param.h>
27 #include <sys/systm.h>
28 #include <sys/kernel.h>
29 #include <sys/fcntl.h>
30 #include <sys/conf.h>
31 #include <sys/proc.h>
32 #include <sys/priv.h>
33 #include <sys/tty.h>
34 #include <sys/socket.h>
35 #include <sys/thread2.h>
36 #include <net/if.h>
38 #if defined(__DragonFly__) || defined(__FreeBSD__)
39 # if defined(__FreeBSD__) && __FreeBSD__ < 2
40 # include <machine/pio.h>
41 # define RB_GETC(q) getc(q)
42 # endif
43 #endif
44 #ifdef __bsdi__
45 # include <sys/ttystats.h>
46 # include <machine/inline.h>
47 # define tsleep(tp,pri,msg,x) ((tp)->t_state |= TS_WOPEN,\
48 ttysleep (tp, (caddr_t)&tp->t_rawq, pri, msg, x))
49 #endif
50 #if defined(__DragonFly__) || !defined (__FreeBSD__) || __FreeBSD__ >= 2
51 # define t_out t_outq
52 # define RB_LEN(q) ((q).c_cc)
53 # define RB_GETC(q) clist_getc(&q)
54 #ifndef TSA_CARR_ON /* FreeBSD 2.x before not long after 2.0.5 */
55 # define TSA_CARR_ON(tp) tp
56 # define TSA_OLOWAT(q) ((caddr_t)&(q)->t_out)
57 #endif
58 #endif
60 #include <machine/cronyx.h>
61 #include "cxreg.h"
63 /* XXX imported from if_cx.c. */
64 void cxswitch (cx_chan_t *c, cx_soft_opt_t new);
66 /* XXX exported. */
67 void cxmint (cx_chan_t *c);
68 int cxrinta (cx_chan_t *c);
69 void cxtinta (cx_chan_t *c);
70 timeout_t cxtimeout;
71 extern struct callout cxtimeout_ch;
73 #ifdef DEBUG
74 # define print(s) kprintf s
75 #else
76 # define print(s) {/*void*/}
77 #endif
79 #define DMABUFSZ (6*256) /* buffer size */
80 #define BYTE *(unsigned char*)&
81 #define UNIT(u) (minor(u) & 077)
82 #define UNIT_CTL 077
84 extern cx_board_t cxboard [NCX]; /* adapter state structures */
85 extern cx_chan_t *cxchan [NCX*NCHAN]; /* unit to channel struct pointer */
86 #if defined(__DragonFly__) || __FreeBSD__ >= 2
87 static struct tty cx_tty [NCX*NCHAN]; /* tty data */
89 static d_open_t cxopen;
90 static d_close_t cxclose;
91 static d_ioctl_t cxioctl;
93 #define CDEV_MAJOR 42
94 /* Don't make this static, since if_cx.c uses it. */
95 struct dev_ops cx_ops = {
96 { "cx", CDEV_MAJOR, D_TTY | D_KQFILTER },
97 .d_open = cxopen,
98 .d_close = cxclose,
99 .d_read = ttyread,
100 .d_write = ttywrite,
101 .d_ioctl = cxioctl,
102 .d_poll = ttypoll,
103 .d_kqfilter = ttykqfilter
105 #else
106 struct tty *cx_tty [NCX*NCHAN]; /* tty data */
107 #endif
109 static void cxoproc (struct tty *tp);
110 static void cxstop (struct tty *tp, int flag);
111 static int cxparam (struct tty *tp, struct termios *t);
114 cxopen (struct dev_open_args *ap)
116 cdev_t dev = ap->a_head.a_dev;
117 int unit = UNIT (dev);
118 cx_chan_t *c = cxchan[unit];
119 unsigned short port;
120 struct tty *tp;
121 int error = 0;
123 if (unit == UNIT_CTL) {
124 print (("cx: cxopen /dev/cronyx\n"));
125 return (0);
127 if (unit >= NCX*NCHAN || !c || c->type==T_NONE)
128 return (ENXIO);
129 port = c->chip->port;
130 print (("cx%d.%d: cxopen unit=%d\n", c->board->num, c->num, unit));
131 if (c->mode != M_ASYNC)
132 return (EBUSY);
133 if (! c->ttyp) {
134 #if defined(__DragonFly__) || defined(__FreeBSD__)
135 #if defined(__DragonFly__) || __FreeBSD__ >= 2
136 c->ttyp = &cx_tty[unit];
137 #else
138 c->ttyp = cx_tty[unit] = ttymalloc (cx_tty[unit]);
139 #endif
140 #else
141 MALLOC (cx_tty[unit], struct tty*, sizeof (struct tty), M_DEVBUF, M_WAITOK);
142 bzero (cx_tty[unit], sizeof (*cx_tty[unit]));
143 c->ttyp = cx_tty[unit];
144 #endif
145 c->ttyp->t_oproc = cxoproc;
146 c->ttyp->t_stop = cxstop;
147 c->ttyp->t_param = cxparam;
149 dev->si_tty = c->ttyp;
150 #ifdef __bsdi__
151 if (! c->ttydev) {
152 MALLOC (c->ttydev, struct ttydevice_tmp*,
153 sizeof (struct ttydevice_tmp), M_DEVBUF, M_WAITOK);
154 bzero (c->ttydev, sizeof (*c->ttydev));
155 strcpy (c->ttydev->tty_name, "cx");
156 c->ttydev->tty_unit = unit;
157 c->ttydev->tty_base = unit;
158 c->ttydev->tty_count = 1;
159 c->ttydev->tty_ttys = c->ttyp;
160 tty_attach (c->ttydev);
162 #endif
163 tp = c->ttyp;
164 tp->t_dev = dev;
165 if ((tp->t_state & TS_ISOPEN) && (tp->t_state & TS_XCLUDE) &&
166 priv_check_cred(ap->a_cred, PRIV_ROOT, 0))
167 return (EBUSY);
168 if (! (tp->t_state & TS_ISOPEN)) {
169 ttychars (tp);
170 if (tp->t_ispeed == 0) {
171 #ifdef __bsdi__
172 tp->t_termios = deftermios;
173 #else
174 tp->t_iflag = 0;
175 tp->t_oflag = 0;
176 tp->t_lflag = 0;
177 tp->t_cflag = CREAD | CS8 | HUPCL;
178 tp->t_ispeed = c->rxbaud;
179 tp->t_ospeed = c->txbaud;
180 #endif
182 cxparam (tp, &tp->t_termios);
183 ttsetwater (tp);
186 crit_enter();
187 if (! (tp->t_state & TS_ISOPEN)) {
189 * Compute optimal receiver buffer length.
190 * The best choice is rxbaud/400.
191 * Make it even, to avoid byte-wide DMA transfers.
192 * --------------------------
193 * Baud rate Buffer length
194 * --------------------------
195 * 300 4
196 * 1200 4
197 * 9600 24
198 * 19200 48
199 * 38400 96
200 * 57600 192
201 * 115200 288
202 * --------------------------
204 int rbsz = (c->rxbaud + 800 - 1) / 800 * 2;
205 if (rbsz < 4)
206 rbsz = 4;
207 else if (rbsz > DMABUFSZ)
208 rbsz = DMABUFSZ;
210 /* Initialize channel, enable receiver. */
211 cx_cmd (port, CCR_INITCH | CCR_ENRX);
212 cx_cmd (port, CCR_INITCH | CCR_ENRX);
214 /* Start receiver. */
215 outw (ARBCNT(port), rbsz);
216 outw (BRBCNT(port), rbsz);
217 outw (ARBSTS(port), BSTS_OWN24);
218 outw (BRBSTS(port), BSTS_OWN24);
220 /* Enable interrupts. */
221 outb (IER(port), IER_RXD | IER_RET | IER_TXD | IER_MDM);
223 cx_chan_dtr (c, 1);
224 cx_chan_rts (c, 1);
226 if (cx_chan_cd (c))
227 (*linesw[tp->t_line].l_modem)(tp, 1);
228 if (! (ap->a_oflags & O_NONBLOCK)) {
229 /* Lock the channel against cxconfig while we are
230 * waiting for carrier. */
231 c->sopt.lock = 1;
232 while (!(tp->t_cflag & CLOCAL) && !(tp->t_state & TS_CARR_ON))
233 if ((error = tsleep (TSA_CARR_ON(tp), PCATCH,
234 "cxdcd", 0)))
235 break;
236 c->sopt.lock = 0; /* Unlock the channel. */
238 print (("cx%d.%d: cxopen done csr=%b\n", c->board->num, c->num,
239 inb(CSR(c->chip->port)), CSRA_BITS));
240 crit_exit();
241 if (error)
242 return (error);
243 #if defined(__DragonFly__) || __FreeBSD__ >= 2
244 error = (*linesw[tp->t_line].l_open) (dev, tp);
245 #else
246 error = (*linesw[tp->t_line].l_open) (dev, tp, 0);
247 #endif
248 return (error);
252 cxclose (struct dev_close_args *ap)
254 cdev_t dev = ap->a_head.a_dev;
255 int unit = UNIT (dev);
256 cx_chan_t *c = cxchan[unit];
257 struct tty *tp;
259 if (unit == UNIT_CTL)
260 return (0);
261 tp = c->ttyp;
262 (*linesw[tp->t_line].l_close) (tp, ap->a_fflag);
264 /* Disable receiver.
265 * Transmitter continues sending the queued data. */
266 crit_enter();
268 outb (CAR(c->chip->port), c->num & 3);
269 outb (IER(c->chip->port), IER_TXD | IER_MDM);
270 cx_cmd (c->chip->port, CCR_DISRX);
272 /* Clear DTR and RTS. */
273 if ((tp->t_cflag & HUPCL) || ! (tp->t_state & TS_ISOPEN)) {
274 cx_chan_dtr (c, 0);
275 cx_chan_rts (c, 0);
278 /* Stop sending break. */
279 if (c->brk == BRK_SEND) {
280 c->brk = BRK_STOP;
281 if (! (tp->t_state & TS_BUSY))
282 cxoproc (tp);
284 crit_exit();
285 ttyclose (tp);
286 return (0);
290 cxioctl (struct dev_ioctl_args *ap)
292 cdev_t dev = ap->a_head.a_dev;
293 caddr_t data = ap->a_data;
294 int unit = UNIT (dev);
295 cx_chan_t *c, *m;
296 cx_stat_t *st;
297 struct tty *tp;
298 int error;
299 unsigned char msv;
300 struct ifnet *master;
302 if (unit == UNIT_CTL) {
303 /* Process an ioctl request on /dev/cronyx */
304 cx_options_t *o = (cx_options_t*) data;
306 if (o->board >= NCX || o->channel >= NCHAN)
307 return (EINVAL);
308 c = &cxboard[o->board].chan[o->channel];
309 if (c->type == T_NONE)
310 return (ENXIO);
311 switch (ap->a_cmd) {
312 default:
313 return (EINVAL);
315 case CXIOCSETMODE:
316 print (("cx%d.%d: CXIOCSETMODE\n", o->board, o->channel));
317 if (c->type == T_NONE)
318 return (EINVAL);
319 if (c->type == T_ASYNC && o->mode != M_ASYNC)
320 return (EINVAL);
321 if (o->mode == M_ASYNC)
322 switch (c->type) {
323 case T_SYNC_RS232:
324 case T_SYNC_V35:
325 case T_SYNC_RS449:
326 return (EINVAL);
328 /* Somebody is waiting for carrier? */
329 if (c->sopt.lock)
330 return (EBUSY);
331 /* /dev/ttyXX is already opened by someone? */
332 if (c->mode == M_ASYNC && c->ttyp &&
333 (c->ttyp->t_state & TS_ISOPEN))
334 return (EBUSY);
335 /* Network interface is up? */
336 if (c->mode != M_ASYNC && (c->ifp->if_flags & IFF_UP))
337 return (EBUSY);
339 /* Find the master interface. */
340 master = *o->master ? ifunit (o->master) : c->ifp;
341 if (! master)
342 return (EINVAL);
343 m = cxchan[master->if_dunit];
345 /* Leave the previous master queue. */
346 if (c->master != c->ifp) {
347 cx_chan_t *p = cxchan[c->master->if_dunit];
349 for (; p; p=p->slaveq)
350 if (p->slaveq == c)
351 p->slaveq = c->slaveq;
354 /* Set up new master. */
355 c->master = master;
356 c->slaveq = 0;
358 /* Join the new master queue. */
359 if (c->master != c->ifp) {
360 c->slaveq = m->slaveq;
361 m->slaveq = c;
364 c->mode = o->mode;
365 c->rxbaud = o->rxbaud;
366 c->txbaud = o->txbaud;
367 c->opt = o->opt;
368 c->aopt = o->aopt;
369 c->hopt = o->hopt;
370 c->bopt = o->bopt;
371 c->xopt = o->xopt;
372 switch (c->num) {
373 case 0: c->board->if0type = o->iftype; break;
374 case 8: c->board->if8type = o->iftype; break;
376 crit_enter();
377 cxswitch (c, o->sopt);
378 cx_setup_chan (c);
379 outb (IER(c->chip->port), 0);
380 crit_exit();
381 break;
383 case CXIOCGETSTAT:
384 st = (cx_stat_t*) data;
385 st->rintr = c->stat->rintr;
386 st->tintr = c->stat->tintr;
387 st->mintr = c->stat->mintr;
388 st->ibytes = c->stat->ibytes;
389 st->ipkts = c->stat->ipkts;
390 st->ierrs = c->stat->ierrs;
391 st->obytes = c->stat->obytes;
392 st->opkts = c->stat->opkts;
393 st->oerrs = c->stat->oerrs;
394 break;
396 case CXIOCGETMODE:
397 print (("cx%d.%d: CXIOCGETMODE\n", o->board, o->channel));
398 o->type = c->type;
399 o->mode = c->mode;
400 o->rxbaud = c->rxbaud;
401 o->txbaud = c->txbaud;
402 o->opt = c->opt;
403 o->aopt = c->aopt;
404 o->hopt = c->hopt;
405 o->bopt = c->bopt;
406 o->xopt = c->xopt;
407 o->sopt = c->sopt;
408 switch (c->num) {
409 case 0: o->iftype = c->board->if0type; break;
410 case 8: o->iftype = c->board->if8type; break;
412 if (c->master != c->ifp)
413 strlcpy(o->master, c->master->if_xname, sizeof(o->master));
414 else
415 *o->master = 0;
416 break;
418 return (0);
421 c = cxchan[unit];
422 tp = c->ttyp;
423 if (! tp)
424 return (EINVAL);
425 error = (*linesw[tp->t_line].l_ioctl) (tp, ap->a_cmd, data,
426 ap->a_fflag, ap->a_cred);
427 if (error != ENOIOCTL)
428 return (error);
429 error = ttioctl (tp, ap->a_cmd, data, ap->a_fflag);
430 if (error != ENOIOCTL)
431 return (error);
433 crit_enter();
435 switch (ap->a_cmd) {
436 default:
437 crit_exit();
438 return (ENOTTY);
439 case TIOCSBRK: /* Start sending line break */
440 c->brk = BRK_SEND;
441 if (! (tp->t_state & TS_BUSY))
442 cxoproc (tp);
443 break;
444 case TIOCCBRK: /* Stop sending line break */
445 c->brk = BRK_STOP;
446 if (! (tp->t_state & TS_BUSY))
447 cxoproc (tp);
448 break;
449 case TIOCSDTR: /* Set DTR */
450 cx_chan_dtr (c, 1);
451 break;
452 case TIOCCDTR: /* Clear DTR */
453 cx_chan_dtr (c, 0);
454 break;
455 case TIOCMSET: /* Set DTR/RTS */
456 cx_chan_dtr (c, (*(int*)data & TIOCM_DTR) ? 1 : 0);
457 cx_chan_rts (c, (*(int*)data & TIOCM_RTS) ? 1 : 0);
458 break;
459 case TIOCMBIS: /* Add DTR/RTS */
460 if (*(int*)data & TIOCM_DTR) cx_chan_dtr (c, 1);
461 if (*(int*)data & TIOCM_RTS) cx_chan_rts (c, 1);
462 break;
463 case TIOCMBIC: /* Clear DTR/RTS */
464 if (*(int*)data & TIOCM_DTR) cx_chan_dtr (c, 0);
465 if (*(int*)data & TIOCM_RTS) cx_chan_rts (c, 0);
466 break;
467 case TIOCMGET: /* Get modem status */
468 msv = inb (MSVR(c->chip->port));
469 *(int*)data = TIOCM_LE; /* always enabled while open */
470 if (msv & MSV_DSR) *(int*)data |= TIOCM_DSR;
471 if (msv & MSV_CTS) *(int*)data |= TIOCM_CTS;
472 if (msv & MSV_CD) *(int*)data |= TIOCM_CD;
473 if (c->dtr) *(int*)data |= TIOCM_DTR;
474 if (c->rts) *(int*)data |= TIOCM_RTS;
475 break;
478 crit_exit();
479 return (0);
483 * Fill transmitter buffer with data.
485 static void
486 cxout (cx_chan_t *c, char b)
488 unsigned char *buf, *p, sym;
489 unsigned short port = c->chip->port, len = 0, cnt_port, sts_port;
490 struct tty *tp = c->ttyp;
492 if (! tp)
493 return;
495 /* Choose the buffer. */
496 if (b == 'A') {
497 buf = c->atbuf;
498 cnt_port = ATBCNT(port);
499 sts_port = ATBSTS(port);
500 } else {
501 buf = c->btbuf;
502 cnt_port = BTBCNT(port);
503 sts_port = BTBSTS(port);
506 /* Is it busy? */
507 if (inb (sts_port) & BSTS_OWN24) {
508 tp->t_state |= TS_BUSY;
509 return;
512 switch (c->brk) {
513 case BRK_SEND:
514 *buf++ = 0; /* extended transmit command */
515 *buf++ = 0x81; /* send break */
516 *buf++ = 0; /* extended transmit command */
517 *buf++ = 0x82; /* insert delay */
518 *buf++ = 250; /* 1/4 of second */
519 *buf++ = 0; /* extended transmit command */
520 *buf++ = 0x82; /* insert delay */
521 *buf++ = 250; /* + 1/4 of second */
522 len = 8;
523 c->brk = BRK_IDLE;
524 break;
525 case BRK_STOP:
526 *buf++ = 0; /* extended transmit command */
527 *buf++ = 0x83; /* stop break */
528 len = 2;
529 c->brk = BRK_IDLE;
530 break;
531 case BRK_IDLE:
532 p = buf;
533 if (tp->t_iflag & IXOFF)
534 while (RB_LEN (tp->t_out) && p<buf+DMABUFSZ-1) {
535 sym = RB_GETC (tp->t_out);
536 /* Send XON/XOFF out of band. */
537 if (sym == tp->t_cc[VSTOP]) {
538 outb (STCR(port), STC_SNDSPC|STC_SSPC_2);
539 continue;
541 if (sym == tp->t_cc[VSTART]) {
542 outb (STCR(port), STC_SNDSPC|STC_SSPC_1);
543 continue;
545 /* Duplicate NULLs in ETC mode. */
546 if (! sym)
547 *p++ = 0;
548 *p++ = sym;
550 else
551 while (RB_LEN (tp->t_out) && p<buf+DMABUFSZ-1) {
552 sym = RB_GETC (tp->t_out);
553 /* Duplicate NULLs in ETC mode. */
554 if (! sym)
555 *p++ = 0;
556 *p++ = sym;
558 len = p - buf;
559 break;
562 /* Start transmitter. */
563 if (len) {
564 outw (cnt_port, len);
565 outb (sts_port, BSTS_INTR | BSTS_OWN24);
566 c->stat->obytes += len;
567 tp->t_state |= TS_BUSY;
568 print (("cx%d.%d: out %d bytes to %c\n",
569 c->board->num, c->num, len, b));
573 void
574 cxoproc (struct tty *tp)
576 int unit = UNIT (tp->t_dev);
577 cx_chan_t *c = cxchan[unit];
578 unsigned short port = c->chip->port;
580 crit_enter();
582 /* Set current channel number */
583 outb (CAR(port), c->num & 3);
585 if (! (tp->t_state & (TS_TIMEOUT | TS_TTSTOP))) {
586 /* Start transmitter. */
587 if (! (inb (CSR(port)) & CSRA_TXEN))
588 cx_cmd (port, CCR_ENTX);
590 /* Determine the buffer order. */
591 if (inb (DMABSTS(port)) & DMABSTS_NTBUF) {
592 cxout (c, 'B');
593 cxout (c, 'A');
594 } else {
595 cxout (c, 'A');
596 cxout (c, 'B');
599 #ifndef TS_ASLEEP /* FreeBSD some time after 2.0.5 */
600 ttwwakeup(tp);
601 #else
602 if (RB_LEN (tp->t_out) <= tp->t_lowat) {
603 if (tp->t_state & TS_ASLEEP) {
604 tp->t_state &= ~TS_ASLEEP;
605 wakeup(TSA_OLOWAT(tp));
607 selwakeup(&tp->t_wsel);
609 #endif
610 crit_exit();
613 static int
614 cxparam (struct tty *tp, struct termios *t)
616 int unit = UNIT (tp->t_dev);
617 cx_chan_t *c = cxchan[unit];
618 unsigned short port = c->chip->port;
619 int clock, period;
620 cx_cor1_async_t cor1;
622 if (t->c_ospeed == 0) {
623 /* Clear DTR and RTS. */
624 crit_enter();
625 cx_chan_dtr (c, 0);
626 cx_chan_rts (c, 0);
627 crit_exit();
628 print (("cx%d.%d: cxparam (hangup)\n", c->board->num, c->num));
629 return (0);
631 print (("cx%d.%d: cxparam\n", c->board->num, c->num));
633 /* Check requested parameters. */
634 if (t->c_ospeed < 300 || t->c_ospeed > 256*1024)
635 return(EINVAL);
636 if (t->c_ispeed && (t->c_ispeed < 300 || t->c_ispeed > 256*1024))
637 return(EINVAL);
639 #ifdef __bsdi__
640 /* CLOCAL flag set -- wakeup everybody who waits for CD. */
641 /* FreeBSD does this themselves. */
642 if (! (tp->t_cflag & CLOCAL) && (t->c_cflag & CLOCAL))
643 wakeup ((caddr_t) &tp->t_rawq);
644 #endif
645 /* And copy them to tty and channel structures. */
646 c->rxbaud = tp->t_ispeed = t->c_ispeed;
647 c->txbaud = tp->t_ospeed = t->c_ospeed;
648 tp->t_cflag = t->c_cflag;
650 /* Set character length and parity mode. */
651 BYTE cor1 = 0;
652 switch (t->c_cflag & CSIZE) {
653 default:
654 case CS8: cor1.charlen = 7; break;
655 case CS7: cor1.charlen = 6; break;
656 case CS6: cor1.charlen = 5; break;
657 case CS5: cor1.charlen = 4; break;
659 if (t->c_cflag & PARENB) {
660 cor1.parmode = PARM_NORMAL;
661 cor1.ignpar = 0;
662 cor1.parity = (t->c_cflag & PARODD) ? PAR_ODD : PAR_EVEN;
663 } else {
664 cor1.parmode = PARM_NOPAR;
665 cor1.ignpar = 1;
668 /* Enable/disable hardware CTS. */
669 c->aopt.cor2.ctsae = (t->c_cflag & CRTSCTS) ? 1 : 0;
670 /* Handle DSR as CTS. */
671 c->aopt.cor2.dsrae = (t->c_cflag & CRTSCTS) ? 1 : 0;
672 /* Enable extended transmit command mode.
673 * Unfortunately, there is no other method for sending break. */
674 c->aopt.cor2.etc = 1;
675 /* Enable/disable hardware XON/XOFF. */
676 c->aopt.cor2.ixon = (t->c_iflag & IXON) ? 1 : 0;
677 c->aopt.cor2.ixany = (t->c_iflag & IXANY) ? 1 : 0;
679 /* Set the number of stop bits. */
680 if (t->c_cflag & CSTOPB)
681 c->aopt.cor3.stopb = STOPB_2;
682 else
683 c->aopt.cor3.stopb = STOPB_1;
684 /* Disable/enable passing XON/XOFF chars to the host. */
685 c->aopt.cor3.scde = (t->c_iflag & IXON) ? 1 : 0;
686 c->aopt.cor3.flowct = (t->c_iflag & IXON) ? FLOWCC_NOTPASS : FLOWCC_PASS;
688 c->aopt.schr1 = t->c_cc[VSTART]; /* XON */
689 c->aopt.schr2 = t->c_cc[VSTOP]; /* XOFF */
691 /* Set current channel number. */
692 crit_enter();
693 outb (CAR(port), c->num & 3);
695 /* Set up receiver clock values. */
696 cx_clock (c->chip->oscfreq, c->rxbaud, &clock, &period);
697 c->opt.rcor.clk = clock;
698 outb (RCOR(port), BYTE c->opt.rcor);
699 outb (RBPR(port), period);
701 /* Set up transmitter clock values. */
702 cx_clock (c->chip->oscfreq, c->txbaud, &clock, &period);
703 c->opt.tcor.clk = clock;
704 c->opt.tcor.ext1x = 0;
705 outb (TCOR(port), BYTE c->opt.tcor);
706 outb (TBPR(port), period);
708 outb (COR2(port), BYTE c->aopt.cor2);
709 outb (COR3(port), BYTE c->aopt.cor3);
710 outb (SCHR1(port), c->aopt.schr1);
711 outb (SCHR2(port), c->aopt.schr2);
713 if (BYTE c->aopt.cor1 != BYTE cor1) {
714 BYTE c->aopt.cor1 = BYTE cor1;
715 outb (COR1(port), BYTE c->aopt.cor1);
716 /* Any change to COR1 require reinitialization. */
717 /* Unfortunately, it may cause transmitter glitches... */
718 cx_cmd (port, CCR_INITCH);
721 crit_exit();
722 return (0);
726 * Stop output on a line
728 void
729 cxstop (struct tty *tp, int flag)
731 crit_enter();
733 if (tp->t_state & TS_BUSY) {
734 cx_chan_t *c = cxchan[UNIT(tp->t_dev)];
735 unsigned short port = c->chip->port;
737 print (("cx%d.%d: cxstop\n", c->board->num, c->num));
739 /* Set current channel number */
740 outb (CAR(port), c->num & 3);
742 /* Stop transmitter */
743 cx_cmd (port, CCR_DISTX);
746 crit_exit();
750 * Handle receive interrupts, including receive errors and
751 * receive timeout interrupt.
754 cxrinta (cx_chan_t *c)
756 unsigned short port = c->chip->port;
757 unsigned short len = 0, risr = inw (RISR(port)), reoir = 0;
758 struct tty *tp = c->ttyp;
760 /* Compute optimal receiver buffer length. */
761 int rbsz = (c->rxbaud + 800 - 1) / 800 * 2;
762 if (rbsz < 4)
763 rbsz = 4;
764 else if (rbsz > DMABUFSZ)
765 rbsz = DMABUFSZ;
767 if (risr & RISA_TIMEOUT) {
768 unsigned long rcbadr = (unsigned short) inw (RCBADRL(port)) |
769 (long) inw (RCBADRU(port)) << 16;
770 unsigned char *buf = 0;
771 unsigned short cnt_port = 0, sts_port = 0;
772 if (rcbadr >= c->brphys && rcbadr < c->brphys+DMABUFSZ) {
773 buf = c->brbuf;
774 len = rcbadr - c->brphys;
775 cnt_port = BRBCNT(port);
776 sts_port = BRBSTS(port);
777 } else if (rcbadr >= c->arphys && rcbadr < c->arphys+DMABUFSZ) {
778 buf = c->arbuf;
779 len = rcbadr - c->arphys;
780 cnt_port = ARBCNT(port);
781 sts_port = ARBSTS(port);
782 } else
783 kprintf ("cx%d.%d: timeout: invalid buffer address\n",
784 c->board->num, c->num);
786 if (len) {
787 print (("cx%d.%d: async receive timeout (%d bytes), risr=%b, arbsts=%b, brbsts=%b\n",
788 c->board->num, c->num, len, risr, RISA_BITS,
789 inb (ARBSTS(port)), BSTS_BITS, inb (BRBSTS(port)), BSTS_BITS));
790 c->stat->ibytes += len;
791 if (tp && (tp->t_state & TS_ISOPEN)) {
792 int i;
793 int (*rint)(int, struct tty *) =
794 linesw[tp->t_line].l_rint;
796 for (i=0; i<len; ++i)
797 (*rint) (buf[i], tp);
800 /* Restart receiver. */
801 outw (cnt_port, rbsz);
802 outb (sts_port, BSTS_OWN24);
804 return (REOI_TERMBUFF);
807 print (("cx%d.%d: async receive interrupt, risr=%b, arbsts=%b, brbsts=%b\n",
808 c->board->num, c->num, risr, RISA_BITS,
809 inb (ARBSTS(port)), BSTS_BITS, inb (BRBSTS(port)), BSTS_BITS));
811 if (risr & RIS_BUSERR) {
812 kprintf ("cx%d.%d: receive bus error\n", c->board->num, c->num);
813 ++c->stat->ierrs;
815 if (risr & (RIS_OVERRUN | RISA_PARERR | RISA_FRERR | RISA_BREAK)) {
816 int err = 0;
818 if (risr & RISA_PARERR)
819 err |= TTY_PE;
820 if (risr & RISA_FRERR)
821 err |= TTY_FE;
822 #ifdef TTY_OE
823 if (risr & RIS_OVERRUN)
824 err |= TTY_OE;
825 #endif
826 #ifdef TTY_BI
827 if (risr & RISA_BREAK)
828 err |= TTY_BI;
829 #endif
830 print (("cx%d.%d: receive error %x\n", c->board->num, c->num, err));
831 if (tp && (tp->t_state & TS_ISOPEN))
832 (*linesw[tp->t_line].l_rint) (err, tp);
833 ++c->stat->ierrs;
836 /* Discard exception characters. */
837 if ((risr & RISA_SCMASK) && tp && (tp->t_iflag & IXON))
838 reoir |= REOI_DISCEXC;
840 /* Handle received data. */
841 if ((risr & RIS_EOBUF) && tp && (tp->t_state & TS_ISOPEN)) {
842 int (*rint)(int, struct tty *) = linesw[tp->t_line].l_rint;
843 unsigned char *buf;
844 int i;
846 len = (risr & RIS_BB) ? inw(BRBCNT(port)) : inw(ARBCNT(port));
848 print (("cx%d.%d: async: %d bytes received\n",
849 c->board->num, c->num, len));
850 c->stat->ibytes += len;
852 buf = (risr & RIS_BB) ? c->brbuf : c->arbuf;
853 for (i=0; i<len; ++i)
854 (*rint) (buf[i], tp);
857 /* Restart receiver. */
858 if (! (inb (ARBSTS(port)) & BSTS_OWN24)) {
859 outw (ARBCNT(port), rbsz);
860 outb (ARBSTS(port), BSTS_OWN24);
862 if (! (inb (BRBSTS(port)) & BSTS_OWN24)) {
863 outw (BRBCNT(port), rbsz);
864 outb (BRBSTS(port), BSTS_OWN24);
866 return (reoir);
870 * Handle transmit interrupt.
872 void cxtinta (cx_chan_t *c)
874 struct tty *tp = c->ttyp;
875 unsigned short port = c->chip->port;
876 unsigned char tisr = inb (TISR(port));
878 print (("cx%d.%d: async transmit interrupt, tisr=%b, atbsts=%b, btbsts=%b\n",
879 c->board->num, c->num, tisr, TIS_BITS,
880 inb (ATBSTS(port)), BSTS_BITS, inb (BTBSTS(port)), BSTS_BITS));
882 if (tisr & TIS_BUSERR) {
883 kprintf ("cx%d.%d: transmit bus error\n",
884 c->board->num, c->num);
885 ++c->stat->oerrs;
886 } else if (tisr & TIS_UNDERRUN) {
887 kprintf ("cx%d.%d: transmit underrun error\n",
888 c->board->num, c->num);
889 ++c->stat->oerrs;
891 if (tp) {
892 tp->t_state &= ~(TS_BUSY | TS_FLUSH);
893 if (tp->t_line)
894 (*linesw[tp->t_line].l_start) (tp);
895 else
896 cxoproc (tp);
901 * Handle modem interrupt.
903 void
904 cxmint (cx_chan_t *c)
906 unsigned short port = c->chip->port;
907 unsigned char misr = inb (MISR(port));
908 unsigned char msvr = inb (MSVR(port));
909 struct tty *tp = c->ttyp;
911 if (c->mode != M_ASYNC) {
912 kprintf ("cx%d.%d: unexpected modem interrupt, misr=%b, msvr=%b\n",
913 c->board->num, c->num, misr, MIS_BITS, msvr, MSV_BITS);
914 return;
916 print (("cx%d.%d: modem interrupt, misr=%b, msvr=%b\n",
917 c->board->num, c->num, misr, MIS_BITS, msvr, MSV_BITS));
919 /* Ignore DSR events. */
920 /* Ignore RTC/CTS events, handled by hardware. */
921 /* Handle carrier detect/loss. */
922 if (tp && (misr & MIS_CCD))
923 (*linesw[tp->t_line].l_modem) (tp, (msvr & MSV_CD) != 0);
927 * Recover after lost transmit interrupts.
929 void
930 cxtimeout (void *a)
932 cx_board_t *b;
933 cx_chan_t *c;
934 struct tty *tp;
936 for (b = cxboard; b < cxboard + NCX; ++b) {
937 for (c = b->chan; c < b->chan + NCHAN; ++c) {
938 tp = c->ttyp;
939 if (c->type == T_NONE || c->mode != M_ASYNC || !tp)
940 continue;
941 crit_enter();
942 if (tp->t_state & TS_BUSY) {
943 tp->t_state &= ~TS_BUSY;
944 if (tp->t_line)
945 (*linesw[tp->t_line].l_start) (tp);
946 else
947 cxoproc (tp);
949 crit_exit();
952 callout_reset (&cxtimeout_ch, hz * 5, cxtimeout, NULL);
956 #if defined(__DragonFly__) || (defined(__FreeBSD__) && (__FreeBSD__ > 1 ))
958 static
959 void
960 cx_drvinit(void *unused)
964 SYSINIT(cxdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,cx_drvinit,NULL)
966 #endif