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