9249142e63e62b21dd1052174231224bb2feaddb
[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.20 2006/12/22 23:26:19 swildner 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/priv.h>
33 #include <sys/tty.h>
34 #include <sys/socket.h>
35 #include <sys/thread2.h>
36 #include <net/if.h>
37
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
59
60 #include <machine/cronyx.h>
61 #include "cxreg.h"
62
63 /* XXX imported from if_cx.c. */
64 void cxswitch (cx_chan_t *c, cx_soft_opt_t new);
65
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;
72
73 #ifdef DEBUG
74 #   define print(s)     kprintf s
75 #else
76 #   define print(s)     {/*void*/}
77 #endif
78
79 #define DMABUFSZ        (6*256)         /* buffer size */
80 #define BYTE            *(unsigned char*)&
81 #define UNIT(u)         (minor(u) & 077)
82 #define UNIT_CTL        077
83
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 */
88
89 static  d_open_t        cxopen;
90 static  d_close_t       cxclose;
91 static  d_ioctl_t       cxioctl;
92
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
104 };
105 #else
106 struct tty *cx_tty [NCX*NCHAN];         /* tty data */
107 #endif
108
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);
112
113 int
114 cxopen (struct dev_open_args *ap)
115 {
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;
122
123         if (unit == UNIT_CTL) {
124                 print (("cx: cxopen /dev/cronyx\n"));
125                 return (0);
126         }
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;
148         }
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);
161         }
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
181                 }
182                 cxparam (tp, &tp->t_termios);
183                 ttsetwater (tp);
184         }
185
186         crit_enter();
187         if (! (tp->t_state & TS_ISOPEN)) {
188                 /*
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                  * --------------------------
203                  */
204                 int rbsz = (c->rxbaud + 800 - 1) / 800 * 2;
205                 if (rbsz < 4)
206                         rbsz = 4;
207                 else if (rbsz > DMABUFSZ)
208                         rbsz = DMABUFSZ;
209
210                 /* Initialize channel, enable receiver. */
211                 cx_cmd (port, CCR_INITCH | CCR_ENRX);
212                 cx_cmd (port, CCR_INITCH | CCR_ENRX);
213
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);
219
220                 /* Enable interrupts. */
221                 outb (IER(port), IER_RXD | IER_RET | IER_TXD | IER_MDM);
222
223                 cx_chan_dtr (c, 1);
224                 cx_chan_rts (c, 1);
225         }
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. */
237         }
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);
249 }
250
251 int
252 cxclose (struct dev_close_args *ap)
253 {
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;
258
259         if (unit == UNIT_CTL)
260                 return (0);
261         tp = c->ttyp;
262         (*linesw[tp->t_line].l_close) (tp, ap->a_fflag);
263
264         /* Disable receiver.
265          * Transmitter continues sending the queued data. */
266         crit_enter();
267
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);
271
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);
276         }
277
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);
283         }
284         crit_exit();
285         ttyclose (tp);
286         return (0);
287 }
288
289 int
290 cxioctl (struct dev_ioctl_args *ap)
291 {
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;
301
302         if (unit == UNIT_CTL) {
303                 /* Process an ioctl request on /dev/cronyx */
304                 cx_options_t *o = (cx_options_t*) data;
305
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);
314
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);
327                                 }
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);
338
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];
344
345                         /* Leave the previous master queue. */
346                         if (c->master != c->ifp) {
347                                 cx_chan_t *p = cxchan[c->master->if_dunit];
348
349                                 for (; p; p=p->slaveq)
350                                         if (p->slaveq == c)
351                                                 p->slaveq = c->slaveq;
352                         }
353
354                         /* Set up new master. */
355                         c->master = master;
356                         c->slaveq = 0;
357
358                         /* Join the new master queue. */
359                         if (c->master != c->ifp) {
360                                 c->slaveq = m->slaveq;
361                                 m->slaveq = c;
362                         }
363
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;
375                         }
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;
382
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;
395
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;
411                         }
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;
417                 }
418                 return (0);
419         }
420
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);
432
433         crit_enter();
434
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;
476         }
477
478         crit_exit();
479         return (0);
480 }
481
482 /*
483  * Fill transmitter buffer with data.
484  */
485 static void
486 cxout (cx_chan_t *c, char b)
487 {
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;
491
492         if (! tp)
493                 return;
494
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);
504         }
505
506         /* Is it busy? */
507         if (inb (sts_port) & BSTS_OWN24) {
508                 tp->t_state |= TS_BUSY;
509                 return;
510         }
511
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;
540                                 }
541                                 if (sym == tp->t_cc[VSTART]) {
542                                         outb (STCR(port), STC_SNDSPC|STC_SSPC_1);
543                                         continue;
544                                 }
545                                 /* Duplicate NULLs in ETC mode. */
546                                 if (! sym)
547                                         *p++ = 0;
548                                 *p++ = sym;
549                         }
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;
557                         }
558                 len = p - buf;
559                 break;
560         }
561
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));
570         }
571 }
572
573 void
574 cxoproc (struct tty *tp)
575 {
576         int unit = UNIT (tp->t_dev);
577         cx_chan_t *c = cxchan[unit];
578         unsigned short port = c->chip->port;
579
580         crit_enter();
581
582         /* Set current channel number */
583         outb (CAR(port), c->num & 3);
584
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);
589
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');
597                 }
598         }
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));
606                 }
607                 selwakeup(&tp->t_wsel);
608         }
609 #endif
610         crit_exit();
611 }
612
613 static int
614 cxparam (struct tty *tp, struct termios *t)
615 {
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;
621
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);
630         }
631         print (("cx%d.%d: cxparam\n", c->board->num, c->num));
632
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);
638
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;
649
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;
658         }
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;
666         }
667
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;
678
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;
687
688         c->aopt.schr1 = t->c_cc[VSTART];        /* XON */
689         c->aopt.schr2 = t->c_cc[VSTOP];         /* XOFF */
690
691         /* Set current channel number. */
692         crit_enter();
693         outb (CAR(port), c->num & 3);
694
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);
700
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);
707
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);
712
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);
719         }
720
721         crit_exit();
722         return (0);
723 }
724
725 /*
726  * Stop output on a line
727  */
728 void
729 cxstop (struct tty *tp, int flag)
730 {
731         crit_enter();
732
733         if (tp->t_state & TS_BUSY) {
734                 cx_chan_t *c = cxchan[UNIT(tp->t_dev)];
735                 unsigned short port = c->chip->port;
736
737                 print (("cx%d.%d: cxstop\n", c->board->num, c->num));
738
739                 /* Set current channel number */
740                 outb (CAR(port), c->num & 3);
741
742                 /* Stop transmitter */
743                 cx_cmd (port, CCR_DISTX);
744         }
745
746         crit_exit();
747 }
748
749 /*
750  * Handle receive interrupts, including receive errors and
751  * receive timeout interrupt.
752  */
753 int
754 cxrinta (cx_chan_t *c)
755 {
756         unsigned short port = c->chip->port;
757         unsigned short len = 0, risr = inw (RISR(port)), reoir = 0;
758         struct tty *tp = c->ttyp;
759
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;
766
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);
785
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;
795
796                                 for (i=0; i<len; ++i)
797                                         (*rint) (buf[i], tp);
798                         }
799
800                         /* Restart receiver. */
801                         outw (cnt_port, rbsz);
802                         outb (sts_port, BSTS_OWN24);
803                 }
804                 return (REOI_TERMBUFF);
805         }
806
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));
810
811         if (risr & RIS_BUSERR) {
812                 kprintf ("cx%d.%d: receive bus error\n", c->board->num, c->num);
813                 ++c->stat->ierrs;
814         }
815         if (risr & (RIS_OVERRUN | RISA_PARERR | RISA_FRERR | RISA_BREAK)) {
816                 int err = 0;
817
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;
834         }
835
836         /* Discard exception characters. */
837         if ((risr & RISA_SCMASK) && tp && (tp->t_iflag & IXON))
838                 reoir |= REOI_DISCEXC;
839
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;
845
846                 len = (risr & RIS_BB) ? inw(BRBCNT(port)) : inw(ARBCNT(port));
847
848                 print (("cx%d.%d: async: %d bytes received\n",
849                         c->board->num, c->num, len));
850                 c->stat->ibytes += len;
851
852                 buf = (risr & RIS_BB) ? c->brbuf : c->arbuf;
853                 for (i=0; i<len; ++i)
854                         (*rint) (buf[i], tp);
855         }
856
857         /* Restart receiver. */
858         if (! (inb (ARBSTS(port)) & BSTS_OWN24)) {
859                 outw (ARBCNT(port), rbsz);
860                 outb (ARBSTS(port), BSTS_OWN24);
861         }
862         if (! (inb (BRBSTS(port)) & BSTS_OWN24)) {
863                 outw (BRBCNT(port), rbsz);
864                 outb (BRBSTS(port), BSTS_OWN24);
865         }
866         return (reoir);
867 }
868
869 /*
870  * Handle transmit interrupt.
871  */
872 void cxtinta (cx_chan_t *c)
873 {
874         struct tty *tp = c->ttyp;
875         unsigned short port = c->chip->port;
876         unsigned char tisr = inb (TISR(port));
877
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));
881
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;
890         }
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);
897         }
898 }
899
900 /*
901  * Handle modem interrupt.
902  */
903 void
904 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                 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;
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