summaryrefslogtreecommitdiffstats
path: root/sys/i386/isa/cx.c
diff options
context:
space:
mode:
authorwollman <wollman@FreeBSD.org>1994-12-02 23:23:01 +0000
committerwollman <wollman@FreeBSD.org>1994-12-02 23:23:01 +0000
commit1e6f21ed41e83670488677de0b159748eb02a365 (patch)
tree60003a2ecb3238a23ed3424937623de0225e5255 /sys/i386/isa/cx.c
downloadFreeBSD-src-1e6f21ed41e83670488677de0b159748eb02a365.zip
FreeBSD-src-1e6f21ed41e83670488677de0b159748eb02a365.tar.gz
Cronyx/Sigma sync/async serial driver with PPP support
from Serge Vakulenko
Diffstat (limited to 'sys/i386/isa/cx.c')
-rw-r--r--sys/i386/isa/cx.c898
1 files changed, 898 insertions, 0 deletions
diff --git a/sys/i386/isa/cx.c b/sys/i386/isa/cx.c
new file mode 100644
index 0000000..80ecaa7
--- /dev/null
+++ b/sys/i386/isa/cx.c
@@ -0,0 +1,898 @@
+/*
+ * Cronyx-Sigma adapter driver for FreeBSD.
+ * Supports PPP/HDLC protocol in synchronous mode,
+ * and asyncronous channels with full modem control.
+ *
+ * Copyright (C) 1994 Cronyx Ltd.
+ * Author: Serge Vakulenko, <vak@zebub.msk.su>
+ *
+ * This software is distributed with NO WARRANTIES, not even the implied
+ * warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Authors grant any other persons or organisations permission to use
+ * or modify this software as long as this message is kept with the software,
+ * all derivative works or modified versions.
+ *
+ * Version 1.2, Tue Nov 22 18:57:27 MSK 1994
+ */
+#undef DEBUG
+
+#include "cx.h"
+#if NCX > 0
+
+#include <sys/param.h>
+#include <systm.h>
+#include <kernel.h>
+#include <sys/mbuf.h>
+#include <sys/ioctl.h>
+#include <sys/file.h>
+#include <sys/conf.h>
+#include <sys/proc.h>
+#include <sys/tty.h>
+#include <sys/errno.h>
+#include <sys/syslog.h>
+#include <sys/socket.h>
+#include <net/if.h>
+
+#ifdef __FreeBSD__
+# if __FreeBSD__ < 2
+# include <i386/include/pio.h>
+# define RB_GETC(q) getc(q)
+# else /* BSD 4.4 Lite */
+# include <i386/include/cpufunc.h>
+# include <sys/devconf.h>
+# endif
+# define oproc_func_t void(*)(struct tty*)
+#endif
+#ifdef __bsdi__
+# include <sys/ttystats.h>
+# include <i386/include/inline.h>
+# define tsleep(tp,pri,msg,x) ((tp)->t_state |= TS_WOPEN,\
+ ttysleep (tp, (caddr_t)&tp->t_rawq, pri, msg, x))
+# define oproc_func_t int(*)()
+# define timeout_func_t void(*)()
+#endif
+#if !defined (__FreeBSD__) || __FreeBSD__ >= 2
+# define t_out t_outq
+# define RB_LEN(q) ((q).c_cc)
+# define RB_GETC(q) getc(&q)
+# define TSA_CARR_ON(tp) tp
+# define TSA_OLOWAT(q) ((caddr_t)&(q)->t_out)
+#endif
+
+#include <sys/cronyx.h>
+#include <i386/isa/cxreg.h>
+
+#ifdef DEBUG
+# define print(s) printf s
+#else
+# define print(s) /*void*/
+#endif
+
+#define DMABUFSZ (6*256) /* buffer size */
+#define BYTE *(unsigned char*)&
+#define UNIT(u) ((u) & 077)
+#define UNIT_CTL 077
+
+extern cx_board_t cxboard [NCX]; /* adapter state structures */
+extern cx_chan_t *cxchan [NCX*NCHAN]; /* unit to channel struct pointer */
+#if __FreeBSD__ >= 2
+extern struct kern_devconf kdc_cx [NCX];
+struct tty cx_tty [NCX*NCHAN]; /* tty data */
+#else
+struct tty *cx_tty [NCX*NCHAN]; /* tty data */
+#endif
+
+void cxoproc (struct tty *tp);
+int cxparam (struct tty *tp, struct termios *t);
+void cxswitch (cx_chan_t *c, cx_soft_opt_t new);
+
+int cxopen (dev_t dev, int flag, int mode, struct proc *p)
+{
+ int unit = UNIT (dev);
+ cx_chan_t *c = cxchan[unit];
+ unsigned short port;
+ struct tty *tp;
+ int error = 0;
+
+ if (unit == UNIT_CTL) {
+ print (("cx: cxopen /dev/cronyx\n"));
+ return (0);
+ }
+ if (unit >= NCX*NCHAN || !c || c->type==T_NONE)
+ return (ENXIO);
+ port = c->chip->port;
+ print (("cx%d.%d: cxopen unit=%d\n", c->board->num, c->num, unit));
+ if (c->mode != M_ASYNC)
+ return (EBUSY);
+ if (! c->ttyp) {
+#if __FreeBSD__ >= 2
+ c->ttyp = &cx_tty[unit];
+#else
+ MALLOC (cx_tty[unit], struct tty*, sizeof (struct tty), M_DEVBUF, M_WAITOK);
+ bzero (cx_tty[unit], sizeof (*cx_tty[unit]));
+ c->ttyp = cx_tty[unit];
+#endif
+ c->ttyp->t_oproc = (oproc_func_t) cxoproc;
+ c->ttyp->t_param = cxparam;
+ }
+#ifdef __bsdi__
+ if (! c->ttydev) {
+ MALLOC (c->ttydev, struct ttydevice_tmp*,
+ sizeof (struct ttydevice_tmp), M_DEVBUF, M_WAITOK);
+ bzero (c->ttydev, sizeof (*c->ttydev));
+ strcpy (c->ttydev->tty_name, "cx");
+ c->ttydev->tty_unit = unit;
+ c->ttydev->tty_base = unit;
+ c->ttydev->tty_count = 1;
+ c->ttydev->tty_ttys = c->ttyp;
+ tty_attach (c->ttydev);
+ }
+#endif
+ tp = c->ttyp;
+ tp->t_dev = dev;
+ if ((tp->t_state & TS_ISOPEN) && (tp->t_state & TS_XCLUDE) &&
+ p->p_ucred->cr_uid != 0)
+ return (EBUSY);
+ if (! (tp->t_state & TS_ISOPEN)) {
+ ttychars (tp);
+ if (tp->t_ispeed == 0) {
+#ifdef __bsdi__
+ tp->t_termios = deftermios;
+#else
+ tp->t_iflag = 0;
+ tp->t_oflag = 0;
+ tp->t_lflag = 0;
+ tp->t_cflag = CREAD | CS8 | HUPCL;
+ tp->t_ispeed = c->rxbaud;
+ tp->t_ospeed = c->txbaud;
+#endif
+ }
+ cxparam (tp, &tp->t_termios);
+ ttsetwater (tp);
+ }
+
+ spltty ();
+ if (! (tp->t_state & TS_ISOPEN)) {
+ /*
+ * Compute optimal receiver buffer length.
+ * The best choice is rxbaud/400.
+ * Make it even, to avoid byte-wide DMA transfers.
+ * --------------------------
+ * Baud rate Buffer length
+ * --------------------------
+ * 300 4
+ * 1200 4
+ * 9600 24
+ * 19200 48
+ * 38400 96
+ * 57600 192
+ * 115200 288
+ * --------------------------
+ */
+ int rbsz = (c->rxbaud + 800 - 1) / 800 * 2;
+ if (rbsz < 4)
+ rbsz = 4;
+ else if (rbsz > DMABUFSZ)
+ rbsz = DMABUFSZ;
+
+ /* Initialize channel, enable receiver. */
+ cx_cmd (port, CCR_INITCH | CCR_ENRX);
+ cx_cmd (port, CCR_INITCH | CCR_ENRX);
+
+ /* Start receiver. */
+ outw (ARBCNT(port), rbsz);
+ outw (BRBCNT(port), rbsz);
+ outw (ARBSTS(port), BSTS_OWN24);
+ outw (BRBSTS(port), BSTS_OWN24);
+
+ /* Enable interrupts. */
+ outb (IER(port), IER_RXD | IER_RET | IER_TXD | IER_MDM);
+
+ cx_chan_dtr (c, 1);
+ cx_chan_rts (c, 1);
+ }
+ if (cx_chan_cd (c))
+ tp->t_state |= TS_CARR_ON;
+ if (! (flag & O_NONBLOCK)) {
+ /* Lock the channel against cxconfig while we are
+ * waiting for carrier. */
+ c->sopt.lock = 1;
+ while (!(tp->t_cflag & CLOCAL) && !(tp->t_state & TS_CARR_ON))
+ if ((error = tsleep (TSA_CARR_ON(tp), TTIPRI | PCATCH,
+ "cxdcd", 0)))
+ break;
+ c->sopt.lock = 0; /* Unlock the channel. */
+ }
+ print (("cx%d.%d: cxopen done csr=%b\n", c->board->num, c->num,
+ inb(CSR(c->chip->port)), CSRA_BITS));
+ spl0 ();
+ if (error)
+ return (error);
+#if __FreeBSD__ >= 2
+ error = (*linesw[tp->t_line].l_open) (dev, tp);
+ if (tp->t_state & TS_ISOPEN)
+ /* Mark the board busy on the first startup.
+ * Never goes idle. */
+ kdc_cx[c->board->num].kdc_state = DC_BUSY;
+#else
+ error = (*linesw[tp->t_line].l_open) (dev, tp, 0);
+#endif
+ return (error);
+}
+
+int cxclose (dev_t dev, int flag, int mode, struct proc *p)
+{
+ int unit = UNIT (dev);
+ cx_chan_t *c = cxchan[unit];
+ struct tty *tp;
+ int s;
+
+ if (unit == UNIT_CTL)
+ return (0);
+ tp = c->ttyp;
+ (*linesw[tp->t_line].l_close) (tp, flag);
+
+ /* Disable receiver.
+ * Transmitter continues sending the queued data. */
+ s = spltty ();
+ outb (CAR(c->chip->port), c->num & 3);
+ outb (IER(c->chip->port), IER_TXD | IER_MDM);
+ cx_cmd (c->chip->port, CCR_DISRX);
+
+ /* Clear DTR and RTS. */
+ if ((tp->t_cflag & HUPCL) || ! (tp->t_state & TS_ISOPEN)) {
+ cx_chan_dtr (c, 0);
+ cx_chan_rts (c, 0);
+ }
+
+ /* Stop sending break. */
+ if (c->brk == BRK_SEND) {
+ c->brk = BRK_STOP;
+ if (! (tp->t_state & TS_BUSY))
+ cxoproc (tp);
+ }
+ splx (s);
+ ttyclose (tp);
+ return (0);
+}
+
+int cxread (dev_t dev, struct uio *uio, int flag)
+{
+ int unit = UNIT (dev);
+ struct tty *tp;
+
+ if (unit == UNIT_CTL)
+ return (EIO);
+ tp = cxchan[unit]->ttyp;
+ return ((*linesw[tp->t_line].l_read) (tp, uio, flag));
+}
+
+int cxwrite (dev_t dev, struct uio *uio, int flag)
+{
+ int unit = UNIT (dev);
+ struct tty *tp;
+
+ if (unit == UNIT_CTL)
+ return (EIO);
+ tp = cxchan[unit]->ttyp;
+ return ((*linesw[tp->t_line].l_write) (tp, uio, flag));
+}
+
+int cxioctl (dev_t dev, int cmd, caddr_t data, int flag, struct proc *p)
+{
+ int unit = UNIT (dev);
+ cx_chan_t *c;
+ struct tty *tp;
+ int error, s;
+ unsigned char msv;
+
+ if (unit == UNIT_CTL) {
+ /* Process an ioctl request on /dev/cronyx */
+ cx_options_t *o = (cx_options_t*) data;
+
+ if (o->board >= NCX || o->channel >= NCHAN)
+ return (EINVAL);
+ c = &cxboard[o->board].chan[o->channel];
+ if (c->type == T_NONE)
+ return (ENXIO);
+ switch (cmd) {
+ default:
+ return (EINVAL);
+
+ case CXIOCSETMODE:
+ print (("cx%d.%d: CXIOCSETMODE\n", o->board, o->channel));
+ if (c->type == T_NONE)
+ return (EINVAL);
+ if (c->type == T_ASYNC && o->mode != M_ASYNC)
+ return (EINVAL);
+ if (o->mode == M_ASYNC)
+ switch (c->type) {
+ case T_SYNC_RS232:
+ case T_SYNC_V35:
+ case T_SYNC_RS449:
+ return (EINVAL);
+ }
+ /* Somebody is waiting for carrier? */
+ if (c->sopt.lock)
+ return (EBUSY);
+ /* /dev/ttyXX is already opened by someone? */
+ if (c->mode == M_ASYNC && c->ttyp &&
+ (c->ttyp->t_state & TS_ISOPEN))
+ return (EBUSY);
+ /* Network interface is up? */
+ if (c->mode != M_ASYNC && (c->ifp->if_flags & IFF_UP))
+ return (EBUSY);
+ c->mode = o->mode;
+ c->rxbaud = o->rxbaud;
+ c->txbaud = o->txbaud;
+ c->opt = o->opt;
+ c->aopt = o->aopt;
+ c->hopt = o->hopt;
+ c->bopt = o->bopt;
+ c->xopt = o->xopt;
+ switch (c->num) {
+ case 0: c->board->if0type = o->iftype; break;
+ case 8: c->board->if8type = o->iftype; break;
+ }
+ cxswitch (c, o->sopt);
+ s = spltty ();
+ cx_setup_chan (c);
+ outb (IER(c->chip->port), 0);
+ splx (s);
+ break;
+
+ case CXIOCGETMODE:
+ print (("cx%d.%d: CXIOCGETMODE\n", o->board, o->channel));
+ o->type = c->type;
+ o->mode = c->mode;
+ o->rxbaud = c->rxbaud;
+ o->txbaud = c->txbaud;
+ o->opt = c->opt;
+ o->aopt = c->aopt;
+ o->hopt = c->hopt;
+ o->bopt = c->bopt;
+ o->xopt = c->xopt;
+ o->sopt = c->sopt;
+ switch (c->num) {
+ case 0: o->iftype = c->board->if0type; break;
+ case 8: o->iftype = c->board->if8type; break;
+ }
+ break;
+ }
+ return (0);
+ }
+
+ c = cxchan[unit];
+ tp = c->ttyp;
+ if (! tp)
+ return (EINVAL);
+#if __FreeBSD__ >= 2
+ error = (*linesw[tp->t_line].l_ioctl) (tp, cmd, data, flag, p);
+#else
+ error = (*linesw[tp->t_line].l_ioctl) (tp, cmd, data, flag);
+#endif
+ if (error >= 0)
+ return (error);
+ error = ttioctl (tp, cmd, data, flag);
+ if (error >= 0)
+ return (error);
+
+ s = spltty ();
+ switch (cmd) {
+ default:
+ splx (s);
+ return (ENOTTY);
+ case TIOCSBRK: /* Start sending line break */
+ c->brk = BRK_SEND;
+ if (! (tp->t_state & TS_BUSY))
+ cxoproc (tp);
+ break;
+ case TIOCCBRK: /* Stop sending line break */
+ c->brk = BRK_STOP;
+ if (! (tp->t_state & TS_BUSY))
+ cxoproc (tp);
+ break;
+ case TIOCSDTR: /* Set DTR */
+ cx_chan_dtr (c, 1);
+ break;
+ case TIOCCDTR: /* Clear DTR */
+ cx_chan_dtr (c, 0);
+ break;
+ case TIOCMSET: /* Set DTR/RTS */
+ cx_chan_dtr (c, (*(int*)data & TIOCM_DTR) ? 1 : 0);
+ cx_chan_rts (c, (*(int*)data & TIOCM_RTS) ? 1 : 0);
+ break;
+ case TIOCMBIS: /* Add DTR/RTS */
+ if (*(int*)data & TIOCM_DTR) cx_chan_dtr (c, 1);
+ if (*(int*)data & TIOCM_RTS) cx_chan_rts (c, 1);
+ break;
+ case TIOCMBIC: /* Clear DTR/RTS */
+ if (*(int*)data & TIOCM_DTR) cx_chan_dtr (c, 0);
+ if (*(int*)data & TIOCM_RTS) cx_chan_rts (c, 0);
+ break;
+ case TIOCMGET: /* Get modem status */
+ msv = inb (MSVR(c->chip->port));
+ *(int*)data = TIOCM_LE; /* always enabled while open */
+ if (msv & MSV_DSR) *(int*)data |= TIOCM_DSR;
+ if (msv & MSV_CTS) *(int*)data |= TIOCM_CTS;
+ if (msv & MSV_CD) *(int*)data |= TIOCM_CD;
+ if (c->dtr) *(int*)data |= TIOCM_DTR;
+ if (c->rts) *(int*)data |= TIOCM_RTS;
+ break;
+ }
+ splx (s);
+ return (0);
+}
+
+/*
+ * Fill transmitter buffer with data.
+ */
+void cxout (cx_chan_t *c, char b)
+{
+ unsigned char *buf, *p, sym;
+ unsigned short port = c->chip->port, len = 0, cnt_port, sts_port;
+ struct tty *tp = c->ttyp;
+ int i;
+
+ if (! tp)
+ return;
+
+ /* Choose the buffer. */
+ if (b == 'A') {
+ buf = c->atbuf;
+ cnt_port = ATBCNT(port);
+ sts_port = ATBSTS(port);
+ } else {
+ buf = c->btbuf;
+ cnt_port = BTBCNT(port);
+ sts_port = BTBSTS(port);
+ }
+
+ /* Is it busy? */
+ if (inb (sts_port) & BSTS_OWN24) {
+ tp->t_state |= TS_BUSY;
+ return;
+ }
+
+ switch (c->brk) {
+ case BRK_SEND:
+ *buf++ = 0; /* extended transmit command */
+ *buf++ = 0x81; /* send break */
+ *buf++ = 0; /* extended transmit command */
+ *buf++ = 0x82; /* insert delay */
+ *buf++ = 250; /* 1/4 of second */
+ *buf++ = 0; /* extended transmit command */
+ *buf++ = 0x82; /* insert delay */
+ *buf++ = 250; /* + 1/4 of second */
+ len = 8;
+ c->brk = BRK_IDLE;
+ break;
+ case BRK_STOP:
+ *buf++ = 0; /* extended transmit command */
+ *buf++ = 0x83; /* stop break */
+ len = 2;
+ c->brk = BRK_IDLE;
+ break;
+ case BRK_IDLE:
+ len = RB_LEN (tp->t_out);
+ if (tp->t_iflag & IXOFF)
+ for (i=0, p=buf; i<len && p<buf+DMABUFSZ-1; ++i) {
+ sym = RB_GETC (tp->t_out);
+ /* Send XON/XOFF out of band. */
+ if (sym == tp->t_cc[VSTOP]) {
+ outb (STCR(port), STC_SNDSPC|STC_SSPC_2);
+ continue;
+ }
+ if (sym == tp->t_cc[VSTART]) {
+ outb (STCR(port), STC_SNDSPC|STC_SSPC_1);
+ continue;
+ }
+ /* Duplicate NULLs in ETC mode. */
+ if (! sym)
+ *p++ = 0;
+ *p++ = sym;
+ }
+ else
+ for (i=0, p=buf; i<len && p<buf+DMABUFSZ-1; ++i) {
+ sym = RB_GETC (tp->t_out);
+ /* Duplicate NULLs in ETC mode. */
+ if (! sym)
+ *p++ = 0;
+ *p++ = sym;
+ }
+ len = p - buf;
+ break;
+ }
+
+ /* Start transmitter. */
+ if (len) {
+ outw (cnt_port, len);
+ outb (sts_port, BSTS_INTR | BSTS_OWN24);
+ tp->t_state |= TS_BUSY;
+ print (("cx%d.%d: out %d bytes to %c\n",
+ c->board->num, c->num, len, b));
+ }
+}
+
+void cxoproc (struct tty *tp)
+{
+ int unit = UNIT (tp->t_dev);
+ cx_chan_t *c = cxchan[unit];
+ unsigned short port = c->chip->port;
+ int s = spltty ();
+
+ /* Set current channel number */
+ outb (CAR(port), c->num & 3);
+
+ if (! (tp->t_state & (TS_TIMEOUT | TS_TTSTOP))) {
+ /* Start transmitter. */
+ if (! (inb (CSR(port)) & CSRA_TXEN))
+ cx_cmd (port, CCR_ENTX);
+
+ /* Determine the buffer order. */
+ if (inb (DMABSTS(port)) & DMABSTS_NTBUF) {
+ cxout (c, 'B');
+ cxout (c, 'A');
+ } else {
+ cxout (c, 'A');
+ cxout (c, 'B');
+ }
+ }
+#if defined (__FreeBSD__) && __FreeBSD__ < 2
+ if (tp->t_state & (TS_SO_OCOMPLETE | TS_SO_OLOWAT) || tp->t_wsel)
+ ttwwakeup (tp);
+#else /* FreeBSD 2.x and BSDI */
+ if (RB_LEN (tp->t_out) <= tp->t_lowat) {
+ if (tp->t_state & TS_ASLEEP) {
+ tp->t_state &= ~TS_ASLEEP;
+ wakeup(TSA_OLOWAT(tp));
+ }
+ selwakeup(&tp->t_wsel);
+ }
+#endif
+ /*
+ * Enable TXMPTY interrupt,
+ * to catch the case when the second buffer is empty.
+ */
+ if ((inb (ATBSTS(port)) & BSTS_OWN24) &&
+ (inb (BTBSTS(port)) & BSTS_OWN24)) {
+ outb (IER(port), IER_RXD|IER_RET|IER_TXD|IER_TXMPTY|IER_MDM);
+ } else
+ outb (IER(port), IER_RXD|IER_RET|IER_TXD|IER_MDM);
+ splx (s);
+}
+
+int cxparam (struct tty *tp, struct termios *t)
+{
+ int unit = UNIT (tp->t_dev);
+ cx_chan_t *c = cxchan[unit];
+ unsigned short port = c->chip->port;
+ int clock, period, s;
+ cx_cor1_async_t cor1;
+
+ if (t->c_ospeed == 0) {
+ /* Clear DTR and RTS. */
+ s = spltty ();
+ cx_chan_dtr (c, 0);
+ cx_chan_rts (c, 0);
+ splx (s);
+ print (("cx%d.%d: cxparam (hangup)\n", c->board->num, c->num));
+ return (0);
+ }
+ print (("cx%d.%d: cxparam\n", c->board->num, c->num));
+
+ /* Check requested parameters. */
+ if (t->c_ospeed < 300 || t->c_ospeed > 256*1024)
+ return(EINVAL);
+ if (t->c_ispeed && (t->c_ispeed < 300 || t->c_ispeed > 256*1024))
+ return(EINVAL);
+
+#ifdef __bsdi__
+ /* CLOCAL flag set -- wakeup everybody who waits for CD. */
+ /* FreeBSD does this themselves. */
+ if (! (tp->t_cflag & CLOCAL) && (t->c_cflag & CLOCAL))
+ wakeup ((caddr_t) &tp->t_rawq);
+#endif
+ /* And copy them to tty and channel structures. */
+ c->rxbaud = tp->t_ispeed = t->c_ispeed;
+ c->txbaud = tp->t_ospeed = t->c_ospeed;
+ tp->t_cflag = t->c_cflag;
+
+ /* Set character length and parity mode. */
+ BYTE cor1 = 0;
+ switch (t->c_cflag & CSIZE) {
+ default:
+ case CS8: cor1.charlen = 7; break;
+ case CS7: cor1.charlen = 6; break;
+ case CS6: cor1.charlen = 5; break;
+ case CS5: cor1.charlen = 4; break;
+ }
+ if (t->c_cflag & PARENB) {
+ cor1.parmode = PARM_NORMAL;
+ cor1.ignpar = 0;
+ cor1.parity = (t->c_cflag & PARODD) ? PAR_ODD : PAR_EVEN;
+ } else {
+ cor1.parmode = PARM_NOPAR;
+ cor1.ignpar = 1;
+ }
+
+ /* Enable/disable hardware CTS. */
+ c->aopt.cor2.ctsae = (t->c_cflag & CRTSCTS) ? 1 : 0;
+ /* Handle DSR as CTS. */
+ c->aopt.cor2.dsrae = (t->c_cflag & CRTSCTS) ? 1 : 0;
+ /* Enable extended transmit command mode.
+ * Unfortunately, there is no other method for sending break. */
+ c->aopt.cor2.etc = 1;
+ /* Enable/disable hardware XON/XOFF. */
+ c->aopt.cor2.ixon = (t->c_iflag & IXON) ? 1 : 0;
+ c->aopt.cor2.ixany = (t->c_iflag & IXANY) ? 1 : 0;
+
+ /* Set the number of stop bits. */
+ if (t->c_cflag & CSTOPB)
+ c->aopt.cor3.stopb = STOPB_2;
+ else
+ c->aopt.cor3.stopb = STOPB_1;
+ /* Disable/enable passing XON/XOFF chars to the host. */
+ c->aopt.cor3.scde = (t->c_iflag & IXON) ? 1 : 0;
+ c->aopt.cor3.flowct = (t->c_iflag & IXON) ? FLOWCC_NOTPASS : FLOWCC_PASS;
+
+ c->aopt.schr1 = t->c_cc[VSTART]; /* XON */
+ c->aopt.schr2 = t->c_cc[VSTOP]; /* XOFF */
+
+ /* Set current channel number. */
+ s = spltty ();
+ outb (CAR(port), c->num & 3);
+
+ /* Set up receiver clock values. */
+ cx_clock (c->chip->oscfreq, c->rxbaud, &clock, &period);
+ c->opt.rcor.clk = clock;
+ outb (RCOR(port), BYTE c->opt.rcor);
+ outb (RBPR(port), period);
+
+ /* Set up transmitter clock values. */
+ cx_clock (c->chip->oscfreq, c->txbaud, &clock, &period);
+ c->opt.tcor.clk = clock;
+ c->opt.tcor.ext1x = 0;
+ outb (TCOR(port), BYTE c->opt.tcor);
+ outb (TBPR(port), period);
+
+ outb (COR2(port), BYTE c->aopt.cor2);
+ outb (COR3(port), BYTE c->aopt.cor3);
+ outb (SCHR1(port), c->aopt.schr1);
+ outb (SCHR2(port), c->aopt.schr2);
+
+ if (BYTE c->aopt.cor1 != BYTE cor1) {
+ BYTE c->aopt.cor1 = BYTE cor1;
+ outb (COR1(port), BYTE c->aopt.cor1);
+ /* Any change to COR1 require reinitialization. */
+ /* Unfortunately, it may cause transmitter glitches... */
+ cx_cmd (port, CCR_INITCH);
+ }
+ splx (s);
+ return (0);
+}
+
+int cxselect (dev_t dev, int flag, struct proc *p)
+{
+ int unit = UNIT (dev);
+
+ if (unit == UNIT_CTL)
+ return (0);
+#ifdef __FreeBSD__
+ return (ttselect (dev, flag, p));
+#endif
+#ifdef __bsdi__
+ return (ttyselect (cxchan[unit]->ttyp, flag, p));
+#endif
+}
+
+/*
+ * Stop output on a line
+ */
+void cxstop (struct tty *tp, int flag)
+{
+ cx_chan_t *c = cxchan[UNIT(tp->t_dev)];
+ unsigned short port = c->chip->port;
+ int s = spltty ();
+
+ if (tp->t_state & TS_BUSY) {
+ print (("cx%d.%d: cxstop\n", c->board->num, c->num));
+
+ /* Set current channel number */
+ outb (CAR(port), c->num & 3);
+
+ /* Stop transmitter */
+ cx_cmd (port, CCR_DISTX);
+ }
+ splx (s);
+}
+
+/*
+ * Handle receive interrupts, including receive errors and
+ * receive timeout interrupt.
+ */
+int cxrinta (cx_chan_t *c)
+{
+ unsigned short port = c->chip->port;
+ unsigned short len = 0, risr = inw (RISR(port)), reoir = 0;
+ struct tty *tp = c->ttyp;
+
+ /* Compute optimal receiver buffer length. */
+ int rbsz = (c->rxbaud + 800 - 1) / 800 * 2;
+ if (rbsz < 4)
+ rbsz = 4;
+ else if (rbsz > DMABUFSZ)
+ rbsz = DMABUFSZ;
+
+ if (risr & RISA_TIMEOUT) {
+ unsigned long rcbadr = (unsigned short) inw (RCBADRL(port)) |
+ (long) inw (RCBADRU(port)) << 16;
+ unsigned char *buf = 0;
+ unsigned short cnt_port = 0, sts_port = 0;
+ if (rcbadr >= c->brphys && rcbadr < c->brphys+DMABUFSZ) {
+ buf = c->brbuf;
+ len = rcbadr - c->brphys;
+ cnt_port = BRBCNT(port);
+ sts_port = BRBSTS(port);
+ } else if (rcbadr >= c->arphys && rcbadr < c->arphys+DMABUFSZ) {
+ buf = c->arbuf;
+ len = rcbadr - c->arphys;
+ cnt_port = ARBCNT(port);
+ sts_port = ARBSTS(port);
+ } else
+ printf ("cx%d.%d: timeout: invalid buffer address\n",
+ c->board->num, c->num);
+
+ if (len) {
+ print (("cx%d.%d: async receive timeout (%d bytes), risr=%b, arbsts=%b, brbsts=%b\n",
+ c->board->num, c->num, len, risr, RISA_BITS,
+ inb (ARBSTS(port)), BSTS_BITS, inb (BRBSTS(port)), BSTS_BITS));
+ if (tp && (tp->t_state & TS_ISOPEN)) {
+ int i;
+ void (*rint)() = (void(*)())
+ linesw[tp->t_line].l_rint;
+
+ for (i=0; i<len; ++i)
+ (*rint) (buf[i], tp);
+ }
+
+ /* Restart receiver. */
+ outw (cnt_port, rbsz);
+ outb (sts_port, BSTS_OWN24);
+ }
+ return (REOI_TERMBUFF);
+ }
+
+ print (("cx%d.%d: async receive interrupt, risr=%b, arbsts=%b, brbsts=%b\n",
+ c->board->num, c->num, risr, RISA_BITS,
+ inb (ARBSTS(port)), BSTS_BITS, inb (BRBSTS(port)), BSTS_BITS));
+
+ if (risr & RIS_BUSERR)
+ printf ("cx%d.%d: receive bus error\n", c->board->num, c->num);
+
+ if (risr & (RIS_OVERRUN | RISA_PARERR | RISA_FRERR)) {
+ print (("cx%d.%d: receive error\n", c->board->num, c->num));
+ if (tp && (tp->t_state & TS_ISOPEN))
+ (*linesw[tp->t_line].l_rint) (' ' |
+ (risr & RISA_FRERR) ? TTY_FE : TTY_PE, tp);
+ }
+
+ /* Handle line break condition. */
+ if ((risr & RISA_BREAK) && tp && (tp->t_state & TS_ISOPEN))
+ (*linesw[tp->t_line].l_rint) (TTY_FE, tp);
+
+ /* Discard exception characters. */
+ if ((risr & RISA_SCMASK) && (tp->t_iflag & IXON))
+ reoir |= REOI_DISCEXC;
+
+ /* Handle received data. */
+ if ((risr & RIS_EOBUF) && tp && (tp->t_state & TS_ISOPEN)) {
+ void (*rint)() = (void(*)()) linesw[tp->t_line].l_rint;
+ unsigned char *buf;
+ int i;
+
+ len = (risr & RIS_BB) ? inw(BRBCNT(port)) : inw(ARBCNT(port));
+
+ print (("cx%d.%d: async: %d bytes received\n",
+ c->board->num, c->num, len));
+
+ buf = (risr & RIS_BB) ? c->brbuf : c->arbuf;
+ for (i=0; i<len; ++i)
+ (*rint) (buf[i], tp);
+ }
+
+ /* Restart receiver. */
+ if (! (inb (ARBSTS(port)) & BSTS_OWN24)) {
+ outw (ARBCNT(port), rbsz);
+ outb (ARBSTS(port), BSTS_OWN24);
+ }
+ if (! (inb (BRBSTS(port)) & BSTS_OWN24)) {
+ outw (BRBCNT(port), rbsz);
+ outb (BRBSTS(port), BSTS_OWN24);
+ }
+ return (reoir);
+}
+
+/*
+ * Handle transmit interrupt.
+ */
+void cxtinta (cx_chan_t *c)
+{
+ struct tty *tp = c->ttyp;
+ unsigned short port = c->chip->port;
+ unsigned char tisr = inb (TISR(port));
+
+ print (("cx%d.%d: async transmit interrupt, tisr=%b, atbsts=%b, btbsts=%b\n",
+ c->board->num, c->num, tisr, TIS_BITS,
+ inb (ATBSTS(port)), BSTS_BITS, inb (BTBSTS(port)), BSTS_BITS));
+
+ if (tisr & TIS_BUSERR)
+ printf ("cx%d.%d: transmit bus error\n",
+ c->board->num, c->num);
+ else if (tisr & TIS_UNDERRUN)
+ printf ("cx%d.%d: transmit underrun error\n",
+ c->board->num, c->num);
+
+ if (tp) {
+ tp->t_state &= ~(TS_BUSY | TS_FLUSH);
+ if (tp->t_line)
+ (*linesw[tp->t_line].l_start) (tp);
+ else
+ cxoproc (tp);
+ }
+}
+
+/*
+ * Handle modem interrupt.
+ */
+void cxmint (cx_chan_t *c)
+{
+ unsigned short port = c->chip->port;
+ unsigned char misr = inb (MISR(port));
+ unsigned char msvr = inb (MSVR(port));
+ struct tty *tp = c->ttyp;
+
+ if (c->mode != M_ASYNC) {
+ printf ("cx%d.%d: unexpected modem interrupt, misr=%b, msvr=%b\n",
+ c->board->num, c->num, misr, MIS_BITS, msvr, MSV_BITS);
+ return;
+ }
+ print (("cx%d.%d: modem interrupt, misr=%b, msvr=%b\n",
+ c->board->num, c->num, misr, MIS_BITS, msvr, MSV_BITS));
+
+ /* Ignore DSR events. */
+ /* Ignore RTC/CTS events, handled by hardware. */
+ /* Handle carrier detect/loss. */
+ if (tp && (misr & MIS_CCD))
+ (*linesw[tp->t_line].l_modem) (tp, (msvr & MSV_CD) != 0);
+}
+
+/*
+ * Recover after lost transmit interrupts.
+ */
+void cxtimeout (caddr_t a)
+{
+ cx_board_t *b;
+ cx_chan_t *c;
+ struct tty *tp;
+ int s;
+
+ for (b=cxboard; b<cxboard+NCX; ++b)
+ for (c=b->chan; c<b->chan+NCHAN; ++c) {
+ tp = c->ttyp;
+ if (c->type==T_NONE || c->mode!=M_ASYNC || !tp)
+ continue;
+ s = spltty ();
+ if (tp->t_state & TS_BUSY) {
+ tp->t_state &= ~TS_BUSY;
+ if (tp->t_line)
+ (*linesw[tp->t_line].l_start) (tp);
+ else
+ cxoproc (tp);
+ }
+ splx (s);
+ }
+ timeout ((timeout_func_t) cxtimeout, 0, hz*5);
+}
+#endif /* NCX */
OpenPOWER on IntegriCloud