📄 cx.c
字号:
/* * 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.9, Wed Oct 4 18:58:15 MSK 1995 */#undef DEBUG#include "cx.h"#if NCX > 0#include <sys/param.h>#include <sys/systm.h>#include <sys/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 <machine/pio.h># define RB_GETC(q) getc(q)# else /* BSD 4.4 Lite */# include <machine/cpufunc.h># include <sys/devconf.h># endif# define oproc_func_t void(*)(struct tty*)#endif#ifdef __bsdi__# include <sys/ttystats.h># include <machine/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)#ifndef TSA_CARR_ON /* FreeBSD 2.x before not long after 2.0.5 */# define TSA_CARR_ON(tp) tp# define TSA_OLOWAT(q) ((caddr_t)&(q)->t_out)#endif#endif#include <machine/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 077extern cx_board_t cxboard [NCX]; /* adapter state structures */extern cx_chan_t *cxchan [NCX*NCHAN]; /* unit to channel struct pointer */#if __FreeBSD__ >= 2extern struct kern_devconf kdc_cx [NCX];struct tty cx_tty [NCX*NCHAN]; /* tty data */#elsestruct tty *cx_tty [NCX*NCHAN]; /* tty data */#endifvoid 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) {#ifdef __FreeBSD__#if __FreeBSD__ >= 2 c->ttyp = &cx_tty[unit];#else c->ttyp = cx_tty[unit] = ttymalloc (cx_tty[unit]);#endif#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)) (*linesw[tp->t_line].l_modem)(tp, 1); 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, *m; cx_stat_t *st; struct tty *tp; int error, s; unsigned char msv; struct ifnet *master; 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); /* Find the master interface. */ master = *o->master ? ifunit (o->master) : c->ifp; if (! master) return (EINVAL); m = cxchan[master->if_unit]; /* Leave the previous master queue. */ if (c->master != c->ifp) { cx_chan_t *p = cxchan[c->master->if_unit]; for (; p; p=p->slaveq) if (p->slaveq == c) p->slaveq = c->slaveq; } /* Set up new master. */ c->master = master; c->slaveq = 0; /* Join the new master queue. */ if (c->master != c->ifp) { c->slaveq = m->slaveq; m->slaveq = c; } 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; } s = spltty (); cxswitch (c, o->sopt); cx_setup_chan (c); outb (IER(c->chip->port), 0); splx (s); break; case CXIOCGETSTAT: st = (cx_stat_t*) data; st->rintr = c->stat->rintr; st->tintr = c->stat->tintr; st->mintr = c->stat->mintr; st->ibytes = c->stat->ibytes; st->ipkts = c->stat->ipkts; st->ierrs = c->stat->ierrs; st->obytes = c->stat->obytes; st->opkts = c->stat->opkts; st->oerrs = c->stat->oerrs; 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; } if (c->master != c->ifp) sprintf (o->master, "%s%d", c->master->if_name, c->master->if_unit); else *o->master = 0; 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -