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