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