proc->thread stage 4: rework the VFS and DEVICE subsystems to take thread
[dragonfly.git] / sys / dev / netif / cx / cx.c
CommitLineData
984263bc
MD
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 $
dadab5e9 19 * $DragonFly: src/sys/dev/netif/cx/cx.c,v 1.3 2003/06/25 03:55:54 dillon Exp $
984263bc
MD
20 *
21 */
22#undef DEBUG
23
24#include "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#ifdef __FreeBSD__
37# if __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 (__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 <i386/isa/cxreg.h>
60
61/* XXX imported from if_cx.c. */
62void cxswitch (cx_chan_t *c, cx_soft_opt_t new);
63
64/* XXX exported. */
65void cxmint (cx_chan_t *c);
66int cxrinta (cx_chan_t *c);
67void cxtinta (cx_chan_t *c);
68timeout_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
81extern cx_board_t cxboard [NCX]; /* adapter state structures */
82extern cx_chan_t *cxchan [NCX*NCHAN]; /* unit to channel struct pointer */
83#if __FreeBSD__ >= 2
84static struct tty cx_tty [NCX*NCHAN]; /* tty data */
85
86static d_open_t cxopen;
87static d_close_t cxclose;
88static d_ioctl_t cxioctl;
89
90#define CDEV_MAJOR 42
91/* Don't make this static, since if_cx.c uses it. */
92struct cdevsw cx_cdevsw = {
93 /* open */ cxopen,
94 /* close */ cxclose,
95 /* read */ ttyread,
96 /* write */ ttywrite,
97 /* ioctl */ cxioctl,
98 /* poll */ ttypoll,
99 /* mmap */ nommap,
100 /* strategy */ nostrategy,
101 /* name */ "cx",
102 /* maj */ CDEV_MAJOR,
103 /* dump */ nodump,
104 /* psize */ nopsize,
105 /* flags */ D_TTY | D_KQFILTER,
106 /* bmaj */ -1,
107 /* kqfilter */ ttykqfilter,
108};
109#else
110struct tty *cx_tty [NCX*NCHAN]; /* tty data */
111#endif
112
113static void cxoproc (struct tty *tp);
114static void cxstop (struct tty *tp, int flag);
115static int cxparam (struct tty *tp, struct termios *t);
116
dadab5e9 117int cxopen (dev_t dev, int flag, int mode, struct thread *td)
984263bc
MD
118{
119 int unit = UNIT (dev);
120 cx_chan_t *c = cxchan[unit];
121 unsigned short port;
122 struct tty *tp;
123 int error = 0;
124
125 if (unit == UNIT_CTL) {
126 print (("cx: cxopen /dev/cronyx\n"));
127 return (0);
128 }
129 if (unit >= NCX*NCHAN || !c || c->type==T_NONE)
130 return (ENXIO);
131 port = c->chip->port;
132 print (("cx%d.%d: cxopen unit=%d\n", c->board->num, c->num, unit));
133 if (c->mode != M_ASYNC)
134 return (EBUSY);
135 if (! c->ttyp) {
136#ifdef __FreeBSD__
137#if __FreeBSD__ >= 2
138 c->ttyp = &cx_tty[unit];
139#else
140 c->ttyp = cx_tty[unit] = ttymalloc (cx_tty[unit]);
141#endif
142#else
143 MALLOC (cx_tty[unit], struct tty*, sizeof (struct tty), M_DEVBUF, M_WAITOK);
144 bzero (cx_tty[unit], sizeof (*cx_tty[unit]));
145 c->ttyp = cx_tty[unit];
146#endif
147 c->ttyp->t_oproc = cxoproc;
148 c->ttyp->t_stop = cxstop;
149 c->ttyp->t_param = cxparam;
150 }
151 dev->si_tty = c->ttyp;
152#ifdef __bsdi__
153 if (! c->ttydev) {
154 MALLOC (c->ttydev, struct ttydevice_tmp*,
155 sizeof (struct ttydevice_tmp), M_DEVBUF, M_WAITOK);
156 bzero (c->ttydev, sizeof (*c->ttydev));
157 strcpy (c->ttydev->tty_name, "cx");
158 c->ttydev->tty_unit = unit;
159 c->ttydev->tty_base = unit;
160 c->ttydev->tty_count = 1;
161 c->ttydev->tty_ttys = c->ttyp;
162 tty_attach (c->ttydev);
163 }
164#endif
165 tp = c->ttyp;
166 tp->t_dev = dev;
167 if ((tp->t_state & TS_ISOPEN) && (tp->t_state & TS_XCLUDE) &&
dadab5e9 168 suser(td))
984263bc
MD
169 return (EBUSY);
170 if (! (tp->t_state & TS_ISOPEN)) {
171 ttychars (tp);
172 if (tp->t_ispeed == 0) {
173#ifdef __bsdi__
174 tp->t_termios = deftermios;
175#else
176 tp->t_iflag = 0;
177 tp->t_oflag = 0;
178 tp->t_lflag = 0;
179 tp->t_cflag = CREAD | CS8 | HUPCL;
180 tp->t_ispeed = c->rxbaud;
181 tp->t_ospeed = c->txbaud;
182#endif
183 }
184 cxparam (tp, &tp->t_termios);
185 ttsetwater (tp);
186 }
187
188 spltty ();
189 if (! (tp->t_state & TS_ISOPEN)) {
190 /*
191 * Compute optimal receiver buffer length.
192 * The best choice is rxbaud/400.
193 * Make it even, to avoid byte-wide DMA transfers.
194 * --------------------------
195 * Baud rate Buffer length
196 * --------------------------
197 * 300 4
198 * 1200 4
199 * 9600 24
200 * 19200 48
201 * 38400 96
202 * 57600 192
203 * 115200 288
204 * --------------------------
205 */
206 int rbsz = (c->rxbaud + 800 - 1) / 800 * 2;
207 if (rbsz < 4)
208 rbsz = 4;
209 else if (rbsz > DMABUFSZ)
210 rbsz = DMABUFSZ;
211
212 /* Initialize channel, enable receiver. */
213 cx_cmd (port, CCR_INITCH | CCR_ENRX);
214 cx_cmd (port, CCR_INITCH | CCR_ENRX);
215
216 /* Start receiver. */
217 outw (ARBCNT(port), rbsz);
218 outw (BRBCNT(port), rbsz);
219 outw (ARBSTS(port), BSTS_OWN24);
220 outw (BRBSTS(port), BSTS_OWN24);
221
222 /* Enable interrupts. */
223 outb (IER(port), IER_RXD | IER_RET | IER_TXD | IER_MDM);
224
225 cx_chan_dtr (c, 1);
226 cx_chan_rts (c, 1);
227 }
228 if (cx_chan_cd (c))
229 (*linesw[tp->t_line].l_modem)(tp, 1);
230 if (! (flag & O_NONBLOCK)) {
231 /* Lock the channel against cxconfig while we are
232 * waiting for carrier. */
233 c->sopt.lock = 1;
234 while (!(tp->t_cflag & CLOCAL) && !(tp->t_state & TS_CARR_ON))
235 if ((error = tsleep (TSA_CARR_ON(tp), TTIPRI | PCATCH,
236 "cxdcd", 0)))
237 break;
238 c->sopt.lock = 0; /* Unlock the channel. */
239 }
240 print (("cx%d.%d: cxopen done csr=%b\n", c->board->num, c->num,
241 inb(CSR(c->chip->port)), CSRA_BITS));
242 spl0 ();
243 if (error)
244 return (error);
245#if __FreeBSD__ >= 2
246 error = (*linesw[tp->t_line].l_open) (dev, tp);
247#else
248 error = (*linesw[tp->t_line].l_open) (dev, tp, 0);
249#endif
250 return (error);
251}
252
dadab5e9 253int cxclose (dev_t dev, int flag, int mode, struct thread *td)
984263bc
MD
254{
255 int unit = UNIT (dev);
256 cx_chan_t *c = cxchan[unit];
257 struct tty *tp;
258 int s;
259
260 if (unit == UNIT_CTL)
261 return (0);
262 tp = c->ttyp;
263 (*linesw[tp->t_line].l_close) (tp, flag);
264
265 /* Disable receiver.
266 * Transmitter continues sending the queued data. */
267 s = spltty ();
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 splx (s);
285 ttyclose (tp);
286 return (0);
287}
288
dadab5e9 289int cxioctl (dev_t dev, u_long cmd, caddr_t data, int flag, struct thread *td)
984263bc
MD
290{
291 int unit = UNIT (dev);
292 cx_chan_t *c, *m;
293 cx_stat_t *st;
294 struct tty *tp;
295 int error, s;
296 unsigned char msv;
297 struct ifnet *master;
298
299 if (unit == UNIT_CTL) {
300 /* Process an ioctl request on /dev/cronyx */
301 cx_options_t *o = (cx_options_t*) data;
302
303 if (o->board >= NCX || o->channel >= NCHAN)
304 return (EINVAL);
305 c = &cxboard[o->board].chan[o->channel];
306 if (c->type == T_NONE)
307 return (ENXIO);
308 switch (cmd) {
309 default:
310 return (EINVAL);
311
312 case CXIOCSETMODE:
313 print (("cx%d.%d: CXIOCSETMODE\n", o->board, o->channel));
314 if (c->type == T_NONE)
315 return (EINVAL);
316 if (c->type == T_ASYNC && o->mode != M_ASYNC)
317 return (EINVAL);
318 if (o->mode == M_ASYNC)
319 switch (c->type) {
320 case T_SYNC_RS232:
321 case T_SYNC_V35:
322 case T_SYNC_RS449:
323 return (EINVAL);
324 }
325 /* Somebody is waiting for carrier? */
326 if (c->sopt.lock)
327 return (EBUSY);
328 /* /dev/ttyXX is already opened by someone? */
329 if (c->mode == M_ASYNC && c->ttyp &&
330 (c->ttyp->t_state & TS_ISOPEN))
331 return (EBUSY);
332 /* Network interface is up? */
333 if (c->mode != M_ASYNC && (c->ifp->if_flags & IFF_UP))
334 return (EBUSY);
335
336 /* Find the master interface. */
337 master = *o->master ? ifunit (o->master) : c->ifp;
338 if (! master)
339 return (EINVAL);
340 m = cxchan[master->if_unit];
341
342 /* Leave the previous master queue. */
343 if (c->master != c->ifp) {
344 cx_chan_t *p = cxchan[c->master->if_unit];
345
346 for (; p; p=p->slaveq)
347 if (p->slaveq == c)
348 p->slaveq = c->slaveq;
349 }
350
351 /* Set up new master. */
352 c->master = master;
353 c->slaveq = 0;
354
355 /* Join the new master queue. */
356 if (c->master != c->ifp) {
357 c->slaveq = m->slaveq;
358 m->slaveq = c;
359 }
360
361 c->mode = o->mode;
362 c->rxbaud = o->rxbaud;
363 c->txbaud = o->txbaud;
364 c->opt = o->opt;
365 c->aopt = o->aopt;
366 c->hopt = o->hopt;
367 c->bopt = o->bopt;
368 c->xopt = o->xopt;
369 switch (c->num) {
370 case 0: c->board->if0type = o->iftype; break;
371 case 8: c->board->if8type = o->iftype; break;
372 }
373 s = spltty ();
374 cxswitch (c, o->sopt);
375 cx_setup_chan (c);
376 outb (IER(c->chip->port), 0);
377 splx (s);
378 break;
379
380 case CXIOCGETSTAT:
381 st = (cx_stat_t*) data;
382 st->rintr = c->stat->rintr;
383 st->tintr = c->stat->tintr;
384 st->mintr = c->stat->mintr;
385 st->ibytes = c->stat->ibytes;
386 st->ipkts = c->stat->ipkts;
387 st->ierrs = c->stat->ierrs;
388 st->obytes = c->stat->obytes;
389 st->opkts = c->stat->opkts;
390 st->oerrs = c->stat->oerrs;
391 break;
392
393 case CXIOCGETMODE:
394 print (("cx%d.%d: CXIOCGETMODE\n", o->board, o->channel));
395 o->type = c->type;
396 o->mode = c->mode;
397 o->rxbaud = c->rxbaud;
398 o->txbaud = c->txbaud;
399 o->opt = c->opt;
400 o->aopt = c->aopt;
401 o->hopt = c->hopt;
402 o->bopt = c->bopt;
403 o->xopt = c->xopt;
404 o->sopt = c->sopt;
405 switch (c->num) {
406 case 0: o->iftype = c->board->if0type; break;
407 case 8: o->iftype = c->board->if8type; break;
408 }
409 if (c->master != c->ifp)
410 snprintf (o->master, sizeof(o->master),
411 "%s%d", c->master->if_name,
412 c->master->if_unit);
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 __FreeBSD__ >= 2
425 error = (*linesw[tp->t_line].l_ioctl) (tp, cmd, data, flag, p);
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 */
485static void
486cxout (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
573void 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
611static int
612cxparam (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 */
725void 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 */
747int 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 */
865void 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 */
896void 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 */
921void 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(__FreeBSD__) && (__FreeBSD__ > 1 )
948static void cx_drvinit(void *unused)
949{
950
951 cdevsw_add(&cx_cdevsw);
952}
953
954SYSINIT(cxdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,cx_drvinit,NULL)
955
956
957#endif