From 1e6f21ed41e83670488677de0b159748eb02a365 Mon Sep 17 00:00:00 2001 From: wollman Date: Fri, 2 Dec 1994 23:23:01 +0000 Subject: Cronyx/Sigma sync/async serial driver with PPP support from Serge Vakulenko --- sys/i386/isa/cx.c | 898 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 898 insertions(+) create mode 100644 sys/i386/isa/cx.c (limited to 'sys/i386/isa/cx.c') 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, + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __FreeBSD__ +# if __FreeBSD__ < 2 +# include +# define RB_GETC(q) getc(q) +# else /* BSD 4.4 Lite */ +# include +# include +# endif +# define oproc_func_t void(*)(struct tty*) +#endif +#ifdef __bsdi__ +# include +# include +# 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 +#include + +#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; it_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; it_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; iboard->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; ittyp; + 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; bchan; cchan+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 */ -- cgit v1.1