Change the kernel dev_t, representing a pointer to a specinfo structure,
[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 $
b13267a5 19 * $DragonFly: src/sys/dev/netif/cx/cx.c,v 1.19 2006/09/10 01:26:35 dillon Exp $
984263bc
MD
20 *
21 */
22#undef DEBUG
23
1f2de5d4 24#include "use_cx.h"
984263bc
MD
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>
bd86b867 34#include <sys/thread2.h>
984263bc
MD
35#include <net/if.h>
36
929783d0
JS
37#if defined(__DragonFly__) || defined(__FreeBSD__)
38# if defined(__FreeBSD__) && __FreeBSD__ < 2
984263bc
MD
39# include <machine/pio.h>
40# define RB_GETC(q) getc(q)
41# endif
42#endif
43#ifdef __bsdi__
44# include <sys/ttystats.h>
45# include <machine/inline.h>
46# define tsleep(tp,pri,msg,x) ((tp)->t_state |= TS_WOPEN,\
47 ttysleep (tp, (caddr_t)&tp->t_rawq, pri, msg, x))
48#endif
929783d0 49#if defined(__DragonFly__) || !defined (__FreeBSD__) || __FreeBSD__ >= 2
984263bc
MD
50# define t_out t_outq
51# define RB_LEN(q) ((q).c_cc)
84204577 52# define RB_GETC(q) clist_getc(&q)
984263bc
MD
53#ifndef TSA_CARR_ON /* FreeBSD 2.x before not long after 2.0.5 */
54# define TSA_CARR_ON(tp) tp
55# define TSA_OLOWAT(q) ((caddr_t)&(q)->t_out)
56#endif
57#endif
58
59#include <machine/cronyx.h>
1f2de5d4 60#include "cxreg.h"
984263bc
MD
61
62/* XXX imported from if_cx.c. */
63void cxswitch (cx_chan_t *c, cx_soft_opt_t new);
64
65/* XXX exported. */
66void cxmint (cx_chan_t *c);
67int cxrinta (cx_chan_t *c);
68void cxtinta (cx_chan_t *c);
69timeout_t cxtimeout;
ed81d395 70extern struct callout cxtimeout_ch;
984263bc
MD
71
72#ifdef DEBUG
73# define print(s) printf s
74#else
75# define print(s) {/*void*/}
76#endif
77
78#define DMABUFSZ (6*256) /* buffer size */
79#define BYTE *(unsigned char*)&
80#define UNIT(u) (minor(u) & 077)
81#define UNIT_CTL 077
82
83extern cx_board_t cxboard [NCX]; /* adapter state structures */
84extern cx_chan_t *cxchan [NCX*NCHAN]; /* unit to channel struct pointer */
929783d0 85#if defined(__DragonFly__) || __FreeBSD__ >= 2
984263bc
MD
86static struct tty cx_tty [NCX*NCHAN]; /* tty data */
87
88static d_open_t cxopen;
89static d_close_t cxclose;
90static d_ioctl_t cxioctl;
91
92#define CDEV_MAJOR 42
93/* Don't make this static, since if_cx.c uses it. */
fef8985e
MD
94struct dev_ops cx_ops = {
95 { "cx", CDEV_MAJOR, D_TTY | D_KQFILTER },
96 .d_open = cxopen,
97 .d_close = cxclose,
98 .d_read = ttyread,
99 .d_write = ttywrite,
100 .d_ioctl = cxioctl,
101 .d_poll = ttypoll,
102 .d_kqfilter = ttykqfilter
984263bc
MD
103};
104#else
105struct tty *cx_tty [NCX*NCHAN]; /* tty data */
106#endif
107
108static void cxoproc (struct tty *tp);
109static void cxstop (struct tty *tp, int flag);
110static int cxparam (struct tty *tp, struct termios *t);
111
7b9f668c
SW
112int
113cxopen (struct dev_open_args *ap)
984263bc 114{
b13267a5 115 cdev_t dev = ap->a_head.a_dev;
984263bc
MD
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) {
929783d0
JS
133#if defined(__DragonFly__) || defined(__FreeBSD__)
134#if defined(__DragonFly__) || __FreeBSD__ >= 2
984263bc
MD
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) &&
fef8985e 165 suser_cred(ap->a_cred, 0))
984263bc
MD
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
bd86b867 185 crit_enter();
984263bc
MD
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);
fef8985e 227 if (! (ap->a_oflags & O_NONBLOCK)) {
984263bc
MD
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))
377d4740 232 if ((error = tsleep (TSA_CARR_ON(tp), PCATCH,
984263bc
MD
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));
bd86b867 239 crit_exit();
984263bc
MD
240 if (error)
241 return (error);
929783d0 242#if defined(__DragonFly__) || __FreeBSD__ >= 2
984263bc
MD
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
7b9f668c
SW
250int
251cxclose (struct dev_close_args *ap)
984263bc 252{
b13267a5 253 cdev_t dev = ap->a_head.a_dev;
984263bc
MD
254 int unit = UNIT (dev);
255 cx_chan_t *c = cxchan[unit];
256 struct tty *tp;
984263bc
MD
257
258 if (unit == UNIT_CTL)
259 return (0);
260 tp = c->ttyp;
fef8985e 261 (*linesw[tp->t_line].l_close) (tp, ap->a_fflag);
984263bc
MD
262
263 /* Disable receiver.
264 * Transmitter continues sending the queued data. */
bd86b867
JS
265 crit_enter();
266
984263bc
MD
267 outb (CAR(c->chip->port), c->num & 3);
268 outb (IER(c->chip->port), IER_TXD | IER_MDM);
269 cx_cmd (c->chip->port, CCR_DISRX);
270
271 /* Clear DTR and RTS. */
272 if ((tp->t_cflag & HUPCL) || ! (tp->t_state & TS_ISOPEN)) {
273 cx_chan_dtr (c, 0);
274 cx_chan_rts (c, 0);
275 }
276
277 /* Stop sending break. */
278 if (c->brk == BRK_SEND) {
279 c->brk = BRK_STOP;
280 if (! (tp->t_state & TS_BUSY))
281 cxoproc (tp);
282 }
bd86b867 283 crit_exit();
984263bc
MD
284 ttyclose (tp);
285 return (0);
286}
287
7b9f668c
SW
288int
289cxioctl (struct dev_ioctl_args *ap)
984263bc 290{
b13267a5 291 cdev_t dev = ap->a_head.a_dev;
fef8985e 292 caddr_t data = ap->a_data;
984263bc
MD
293 int unit = UNIT (dev);
294 cx_chan_t *c, *m;
295 cx_stat_t *st;
296 struct tty *tp;
bd86b867 297 int error;
984263bc
MD
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);
fef8985e 310 switch (ap->a_cmd) {
984263bc
MD
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);
3e4a09e7 342 m = cxchan[master->if_dunit];
984263bc
MD
343
344 /* Leave the previous master queue. */
345 if (c->master != c->ifp) {
3e4a09e7 346 cx_chan_t *p = cxchan[c->master->if_dunit];
984263bc
MD
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 }
bd86b867 375 crit_enter();
984263bc
MD
376 cxswitch (c, o->sopt);
377 cx_setup_chan (c);
378 outb (IER(c->chip->port), 0);
bd86b867 379 crit_exit();
984263bc
MD
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)
f41533d2 412 strlcpy(o->master, c->master->if_xname, sizeof(o->master));
984263bc
MD
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);
fef8985e
MD
424 error = (*linesw[tp->t_line].l_ioctl) (tp, ap->a_cmd, data,
425 ap->a_fflag, ap->a_cred);
984263bc
MD
426 if (error != ENOIOCTL)
427 return (error);
fef8985e 428 error = ttioctl (tp, ap->a_cmd, data, ap->a_fflag);
984263bc
MD
429 if (error != ENOIOCTL)
430 return (error);
431
bd86b867
JS
432 crit_enter();
433
fef8985e 434 switch (ap->a_cmd) {
984263bc 435 default:
bd86b867 436 crit_exit();
984263bc
MD
437 return (ENOTTY);
438 case TIOCSBRK: /* Start sending line break */
439 c->brk = BRK_SEND;
440 if (! (tp->t_state & TS_BUSY))
441 cxoproc (tp);
442 break;
443 case TIOCCBRK: /* Stop sending line break */
444 c->brk = BRK_STOP;
445 if (! (tp->t_state & TS_BUSY))
446 cxoproc (tp);
447 break;
448 case TIOCSDTR: /* Set DTR */
449 cx_chan_dtr (c, 1);
450 break;
451 case TIOCCDTR: /* Clear DTR */
452 cx_chan_dtr (c, 0);
453 break;
454 case TIOCMSET: /* Set DTR/RTS */
455 cx_chan_dtr (c, (*(int*)data & TIOCM_DTR) ? 1 : 0);
456 cx_chan_rts (c, (*(int*)data & TIOCM_RTS) ? 1 : 0);
457 break;
458 case TIOCMBIS: /* Add DTR/RTS */
459 if (*(int*)data & TIOCM_DTR) cx_chan_dtr (c, 1);
460 if (*(int*)data & TIOCM_RTS) cx_chan_rts (c, 1);
461 break;
462 case TIOCMBIC: /* Clear DTR/RTS */
463 if (*(int*)data & TIOCM_DTR) cx_chan_dtr (c, 0);
464 if (*(int*)data & TIOCM_RTS) cx_chan_rts (c, 0);
465 break;
466 case TIOCMGET: /* Get modem status */
467 msv = inb (MSVR(c->chip->port));
468 *(int*)data = TIOCM_LE; /* always enabled while open */
469 if (msv & MSV_DSR) *(int*)data |= TIOCM_DSR;
470 if (msv & MSV_CTS) *(int*)data |= TIOCM_CTS;
471 if (msv & MSV_CD) *(int*)data |= TIOCM_CD;
472 if (c->dtr) *(int*)data |= TIOCM_DTR;
473 if (c->rts) *(int*)data |= TIOCM_RTS;
474 break;
475 }
bd86b867
JS
476
477 crit_exit();
984263bc
MD
478 return (0);
479}
480
481/*
482 * Fill transmitter buffer with data.
483 */
484static void
485cxout (cx_chan_t *c, char b)
486{
487 unsigned char *buf, *p, sym;
488 unsigned short port = c->chip->port, len = 0, cnt_port, sts_port;
489 struct tty *tp = c->ttyp;
490
491 if (! tp)
492 return;
493
494 /* Choose the buffer. */
495 if (b == 'A') {
496 buf = c->atbuf;
497 cnt_port = ATBCNT(port);
498 sts_port = ATBSTS(port);
499 } else {
500 buf = c->btbuf;
501 cnt_port = BTBCNT(port);
502 sts_port = BTBSTS(port);
503 }
504
505 /* Is it busy? */
506 if (inb (sts_port) & BSTS_OWN24) {
507 tp->t_state |= TS_BUSY;
508 return;
509 }
510
511 switch (c->brk) {
512 case BRK_SEND:
513 *buf++ = 0; /* extended transmit command */
514 *buf++ = 0x81; /* send break */
515 *buf++ = 0; /* extended transmit command */
516 *buf++ = 0x82; /* insert delay */
517 *buf++ = 250; /* 1/4 of second */
518 *buf++ = 0; /* extended transmit command */
519 *buf++ = 0x82; /* insert delay */
520 *buf++ = 250; /* + 1/4 of second */
521 len = 8;
522 c->brk = BRK_IDLE;
523 break;
524 case BRK_STOP:
525 *buf++ = 0; /* extended transmit command */
526 *buf++ = 0x83; /* stop break */
527 len = 2;
528 c->brk = BRK_IDLE;
529 break;
530 case BRK_IDLE:
531 p = buf;
532 if (tp->t_iflag & IXOFF)
533 while (RB_LEN (tp->t_out) && p<buf+DMABUFSZ-1) {
534 sym = RB_GETC (tp->t_out);
535 /* Send XON/XOFF out of band. */
536 if (sym == tp->t_cc[VSTOP]) {
537 outb (STCR(port), STC_SNDSPC|STC_SSPC_2);
538 continue;
539 }
540 if (sym == tp->t_cc[VSTART]) {
541 outb (STCR(port), STC_SNDSPC|STC_SSPC_1);
542 continue;
543 }
544 /* Duplicate NULLs in ETC mode. */
545 if (! sym)
546 *p++ = 0;
547 *p++ = sym;
548 }
549 else
550 while (RB_LEN (tp->t_out) && p<buf+DMABUFSZ-1) {
551 sym = RB_GETC (tp->t_out);
552 /* Duplicate NULLs in ETC mode. */
553 if (! sym)
554 *p++ = 0;
555 *p++ = sym;
556 }
557 len = p - buf;
558 break;
559 }
560
561 /* Start transmitter. */
562 if (len) {
563 outw (cnt_port, len);
564 outb (sts_port, BSTS_INTR | BSTS_OWN24);
565 c->stat->obytes += len;
566 tp->t_state |= TS_BUSY;
567 print (("cx%d.%d: out %d bytes to %c\n",
568 c->board->num, c->num, len, b));
569 }
570}
571
7b9f668c
SW
572void
573cxoproc (struct tty *tp)
984263bc
MD
574{
575 int unit = UNIT (tp->t_dev);
576 cx_chan_t *c = cxchan[unit];
577 unsigned short port = c->chip->port;
bd86b867
JS
578
579 crit_enter();
984263bc
MD
580
581 /* Set current channel number */
582 outb (CAR(port), c->num & 3);
583
584 if (! (tp->t_state & (TS_TIMEOUT | TS_TTSTOP))) {
585 /* Start transmitter. */
586 if (! (inb (CSR(port)) & CSRA_TXEN))
587 cx_cmd (port, CCR_ENTX);
588
589 /* Determine the buffer order. */
590 if (inb (DMABSTS(port)) & DMABSTS_NTBUF) {
591 cxout (c, 'B');
592 cxout (c, 'A');
593 } else {
594 cxout (c, 'A');
595 cxout (c, 'B');
596 }
597 }
598#ifndef TS_ASLEEP /* FreeBSD some time after 2.0.5 */
599 ttwwakeup(tp);
600#else
601 if (RB_LEN (tp->t_out) <= tp->t_lowat) {
602 if (tp->t_state & TS_ASLEEP) {
603 tp->t_state &= ~TS_ASLEEP;
604 wakeup(TSA_OLOWAT(tp));
605 }
606 selwakeup(&tp->t_wsel);
607 }
608#endif
bd86b867 609 crit_exit();
984263bc
MD
610}
611
612static int
613cxparam (struct tty *tp, struct termios *t)
614{
615 int unit = UNIT (tp->t_dev);
616 cx_chan_t *c = cxchan[unit];
617 unsigned short port = c->chip->port;
bd86b867 618 int clock, period;
984263bc
MD
619 cx_cor1_async_t cor1;
620
621 if (t->c_ospeed == 0) {
622 /* Clear DTR and RTS. */
bd86b867 623 crit_enter();
984263bc
MD
624 cx_chan_dtr (c, 0);
625 cx_chan_rts (c, 0);
bd86b867 626 crit_exit();
984263bc
MD
627 print (("cx%d.%d: cxparam (hangup)\n", c->board->num, c->num));
628 return (0);
629 }
630 print (("cx%d.%d: cxparam\n", c->board->num, c->num));
631
632 /* Check requested parameters. */
633 if (t->c_ospeed < 300 || t->c_ospeed > 256*1024)
634 return(EINVAL);
635 if (t->c_ispeed && (t->c_ispeed < 300 || t->c_ispeed > 256*1024))
636 return(EINVAL);
637
638#ifdef __bsdi__
639 /* CLOCAL flag set -- wakeup everybody who waits for CD. */
640 /* FreeBSD does this themselves. */
641 if (! (tp->t_cflag & CLOCAL) && (t->c_cflag & CLOCAL))
642 wakeup ((caddr_t) &tp->t_rawq);
643#endif
644 /* And copy them to tty and channel structures. */
645 c->rxbaud = tp->t_ispeed = t->c_ispeed;
646 c->txbaud = tp->t_ospeed = t->c_ospeed;
647 tp->t_cflag = t->c_cflag;
648
649 /* Set character length and parity mode. */
650 BYTE cor1 = 0;
651 switch (t->c_cflag & CSIZE) {
652 default:
653 case CS8: cor1.charlen = 7; break;
654 case CS7: cor1.charlen = 6; break;
655 case CS6: cor1.charlen = 5; break;
656 case CS5: cor1.charlen = 4; break;
657 }
658 if (t->c_cflag & PARENB) {
659 cor1.parmode = PARM_NORMAL;
660 cor1.ignpar = 0;
661 cor1.parity = (t->c_cflag & PARODD) ? PAR_ODD : PAR_EVEN;
662 } else {
663 cor1.parmode = PARM_NOPAR;
664 cor1.ignpar = 1;
665 }
666
667 /* Enable/disable hardware CTS. */
668 c->aopt.cor2.ctsae = (t->c_cflag & CRTSCTS) ? 1 : 0;
669 /* Handle DSR as CTS. */
670 c->aopt.cor2.dsrae = (t->c_cflag & CRTSCTS) ? 1 : 0;
671 /* Enable extended transmit command mode.
672 * Unfortunately, there is no other method for sending break. */
673 c->aopt.cor2.etc = 1;
674 /* Enable/disable hardware XON/XOFF. */
675 c->aopt.cor2.ixon = (t->c_iflag & IXON) ? 1 : 0;
676 c->aopt.cor2.ixany = (t->c_iflag & IXANY) ? 1 : 0;
677
678 /* Set the number of stop bits. */
679 if (t->c_cflag & CSTOPB)
680 c->aopt.cor3.stopb = STOPB_2;
681 else
682 c->aopt.cor3.stopb = STOPB_1;
683 /* Disable/enable passing XON/XOFF chars to the host. */
684 c->aopt.cor3.scde = (t->c_iflag & IXON) ? 1 : 0;
685 c->aopt.cor3.flowct = (t->c_iflag & IXON) ? FLOWCC_NOTPASS : FLOWCC_PASS;
686
687 c->aopt.schr1 = t->c_cc[VSTART]; /* XON */
688 c->aopt.schr2 = t->c_cc[VSTOP]; /* XOFF */
689
690 /* Set current channel number. */
bd86b867 691 crit_enter();
984263bc
MD
692 outb (CAR(port), c->num & 3);
693
694 /* Set up receiver clock values. */
695 cx_clock (c->chip->oscfreq, c->rxbaud, &clock, &period);
696 c->opt.rcor.clk = clock;
697 outb (RCOR(port), BYTE c->opt.rcor);
698 outb (RBPR(port), period);
699
700 /* Set up transmitter clock values. */
701 cx_clock (c->chip->oscfreq, c->txbaud, &clock, &period);
702 c->opt.tcor.clk = clock;
703 c->opt.tcor.ext1x = 0;
704 outb (TCOR(port), BYTE c->opt.tcor);
705 outb (TBPR(port), period);
706
707 outb (COR2(port), BYTE c->aopt.cor2);
708 outb (COR3(port), BYTE c->aopt.cor3);
709 outb (SCHR1(port), c->aopt.schr1);
710 outb (SCHR2(port), c->aopt.schr2);
711
712 if (BYTE c->aopt.cor1 != BYTE cor1) {
713 BYTE c->aopt.cor1 = BYTE cor1;
714 outb (COR1(port), BYTE c->aopt.cor1);
715 /* Any change to COR1 require reinitialization. */
716 /* Unfortunately, it may cause transmitter glitches... */
717 cx_cmd (port, CCR_INITCH);
718 }
bd86b867
JS
719
720 crit_exit();
984263bc
MD
721 return (0);
722}
723
724/*
725 * Stop output on a line
726 */
7b9f668c
SW
727void
728cxstop (struct tty *tp, int flag)
984263bc
MD
729{
730 cx_chan_t *c = cxchan[UNIT(tp->t_dev)];
731 unsigned short port = c->chip->port;
bd86b867
JS
732
733 crit_enter();
984263bc
MD
734
735 if (tp->t_state & TS_BUSY) {
736 print (("cx%d.%d: cxstop\n", c->board->num, c->num));
737
738 /* Set current channel number */
739 outb (CAR(port), c->num & 3);
740
741 /* Stop transmitter */
742 cx_cmd (port, CCR_DISTX);
743 }
bd86b867
JS
744
745 crit_exit();
984263bc
MD
746}
747
748/*
749 * Handle receive interrupts, including receive errors and
750 * receive timeout interrupt.
751 */
7b9f668c
SW
752int
753cxrinta (cx_chan_t *c)
984263bc
MD
754{
755 unsigned short port = c->chip->port;
756 unsigned short len = 0, risr = inw (RISR(port)), reoir = 0;
757 struct tty *tp = c->ttyp;
758
759 /* Compute optimal receiver buffer length. */
760 int rbsz = (c->rxbaud + 800 - 1) / 800 * 2;
761 if (rbsz < 4)
762 rbsz = 4;
763 else if (rbsz > DMABUFSZ)
764 rbsz = DMABUFSZ;
765
766 if (risr & RISA_TIMEOUT) {
767 unsigned long rcbadr = (unsigned short) inw (RCBADRL(port)) |
768 (long) inw (RCBADRU(port)) << 16;
769 unsigned char *buf = 0;
770 unsigned short cnt_port = 0, sts_port = 0;
771 if (rcbadr >= c->brphys && rcbadr < c->brphys+DMABUFSZ) {
772 buf = c->brbuf;
773 len = rcbadr - c->brphys;
774 cnt_port = BRBCNT(port);
775 sts_port = BRBSTS(port);
776 } else if (rcbadr >= c->arphys && rcbadr < c->arphys+DMABUFSZ) {
777 buf = c->arbuf;
778 len = rcbadr - c->arphys;
779 cnt_port = ARBCNT(port);
780 sts_port = ARBSTS(port);
781 } else
782 printf ("cx%d.%d: timeout: invalid buffer address\n",
783 c->board->num, c->num);
784
785 if (len) {
786 print (("cx%d.%d: async receive timeout (%d bytes), risr=%b, arbsts=%b, brbsts=%b\n",
787 c->board->num, c->num, len, risr, RISA_BITS,
788 inb (ARBSTS(port)), BSTS_BITS, inb (BRBSTS(port)), BSTS_BITS));
789 c->stat->ibytes += len;
790 if (tp && (tp->t_state & TS_ISOPEN)) {
791 int i;
792 int (*rint)(int, struct tty *) =
793 linesw[tp->t_line].l_rint;
794
795 for (i=0; i<len; ++i)
796 (*rint) (buf[i], tp);
797 }
798
799 /* Restart receiver. */
800 outw (cnt_port, rbsz);
801 outb (sts_port, BSTS_OWN24);
802 }
803 return (REOI_TERMBUFF);
804 }
805
806 print (("cx%d.%d: async receive interrupt, risr=%b, arbsts=%b, brbsts=%b\n",
807 c->board->num, c->num, risr, RISA_BITS,
808 inb (ARBSTS(port)), BSTS_BITS, inb (BRBSTS(port)), BSTS_BITS));
809
810 if (risr & RIS_BUSERR) {
811 printf ("cx%d.%d: receive bus error\n", c->board->num, c->num);
812 ++c->stat->ierrs;
813 }
814 if (risr & (RIS_OVERRUN | RISA_PARERR | RISA_FRERR | RISA_BREAK)) {
815 int err = 0;
816
817 if (risr & RISA_PARERR)
818 err |= TTY_PE;
819 if (risr & RISA_FRERR)
820 err |= TTY_FE;
821#ifdef TTY_OE
822 if (risr & RIS_OVERRUN)
823 err |= TTY_OE;
824#endif
825#ifdef TTY_BI
826 if (risr & RISA_BREAK)
827 err |= TTY_BI;
828#endif
829 print (("cx%d.%d: receive error %x\n", c->board->num, c->num, err));
830 if (tp && (tp->t_state & TS_ISOPEN))
831 (*linesw[tp->t_line].l_rint) (err, tp);
832 ++c->stat->ierrs;
833 }
834
835 /* Discard exception characters. */
836 if ((risr & RISA_SCMASK) && tp && (tp->t_iflag & IXON))
837 reoir |= REOI_DISCEXC;
838
839 /* Handle received data. */
840 if ((risr & RIS_EOBUF) && tp && (tp->t_state & TS_ISOPEN)) {
841 int (*rint)(int, struct tty *) = linesw[tp->t_line].l_rint;
842 unsigned char *buf;
843 int i;
844
845 len = (risr & RIS_BB) ? inw(BRBCNT(port)) : inw(ARBCNT(port));
846
847 print (("cx%d.%d: async: %d bytes received\n",
848 c->board->num, c->num, len));
849 c->stat->ibytes += len;
850
851 buf = (risr & RIS_BB) ? c->brbuf : c->arbuf;
852 for (i=0; i<len; ++i)
853 (*rint) (buf[i], tp);
854 }
855
856 /* Restart receiver. */
857 if (! (inb (ARBSTS(port)) & BSTS_OWN24)) {
858 outw (ARBCNT(port), rbsz);
859 outb (ARBSTS(port), BSTS_OWN24);
860 }
861 if (! (inb (BRBSTS(port)) & BSTS_OWN24)) {
862 outw (BRBCNT(port), rbsz);
863 outb (BRBSTS(port), BSTS_OWN24);
864 }
865 return (reoir);
866}
867
868/*
869 * Handle transmit interrupt.
870 */
871void cxtinta (cx_chan_t *c)
872{
873 struct tty *tp = c->ttyp;
874 unsigned short port = c->chip->port;
875 unsigned char tisr = inb (TISR(port));
876
877 print (("cx%d.%d: async transmit interrupt, tisr=%b, atbsts=%b, btbsts=%b\n",
878 c->board->num, c->num, tisr, TIS_BITS,
879 inb (ATBSTS(port)), BSTS_BITS, inb (BTBSTS(port)), BSTS_BITS));
880
881 if (tisr & TIS_BUSERR) {
882 printf ("cx%d.%d: transmit bus error\n",
883 c->board->num, c->num);
884 ++c->stat->oerrs;
885 } else if (tisr & TIS_UNDERRUN) {
886 printf ("cx%d.%d: transmit underrun error\n",
887 c->board->num, c->num);
888 ++c->stat->oerrs;
889 }
890 if (tp) {
891 tp->t_state &= ~(TS_BUSY | TS_FLUSH);
892 if (tp->t_line)
893 (*linesw[tp->t_line].l_start) (tp);
894 else
895 cxoproc (tp);
896 }
897}
898
899/*
900 * Handle modem interrupt.
901 */
7b9f668c
SW
902void
903cxmint (cx_chan_t *c)
984263bc
MD
904{
905 unsigned short port = c->chip->port;
906 unsigned char misr = inb (MISR(port));
907 unsigned char msvr = inb (MSVR(port));
908 struct tty *tp = c->ttyp;
909
910 if (c->mode != M_ASYNC) {
911 printf ("cx%d.%d: unexpected modem interrupt, misr=%b, msvr=%b\n",
912 c->board->num, c->num, misr, MIS_BITS, msvr, MSV_BITS);
913 return;
914 }
915 print (("cx%d.%d: modem interrupt, misr=%b, msvr=%b\n",
916 c->board->num, c->num, misr, MIS_BITS, msvr, MSV_BITS));
917
918 /* Ignore DSR events. */
919 /* Ignore RTC/CTS events, handled by hardware. */
920 /* Handle carrier detect/loss. */
921 if (tp && (misr & MIS_CCD))
922 (*linesw[tp->t_line].l_modem) (tp, (msvr & MSV_CD) != 0);
923}
924
925/*
926 * Recover after lost transmit interrupts.
927 */
793d58b7
MD
928void
929cxtimeout (void *a)
984263bc
MD
930{
931 cx_board_t *b;
932 cx_chan_t *c;
933 struct tty *tp;
984263bc 934
793d58b7
MD
935 for (b = cxboard; b < cxboard + NCX; ++b) {
936 for (c = b->chan; c < b->chan + NCHAN; ++c) {
984263bc 937 tp = c->ttyp;
793d58b7 938 if (c->type == T_NONE || c->mode != M_ASYNC || !tp)
984263bc 939 continue;
bd86b867 940 crit_enter();
984263bc
MD
941 if (tp->t_state & TS_BUSY) {
942 tp->t_state &= ~TS_BUSY;
943 if (tp->t_line)
944 (*linesw[tp->t_line].l_start) (tp);
945 else
946 cxoproc (tp);
947 }
bd86b867 948 crit_exit();
984263bc 949 }
793d58b7 950 }
ed81d395 951 callout_reset (&cxtimeout_ch, hz * 5, cxtimeout, NULL);
984263bc
MD
952}
953
954
929783d0 955#if defined(__DragonFly__) || (defined(__FreeBSD__) && (__FreeBSD__ > 1 ))
984263bc 956
e4c9c0c8
MD
957static
958void
959cx_drvinit(void *unused)
960{
984263bc
MD
961}
962
963SYSINIT(cxdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,cx_drvinit,NULL)
964
984263bc 965#endif