diff options
author | bde <bde@FreeBSD.org> | 1995-07-05 12:15:52 +0000 |
---|---|---|
committer | bde <bde@FreeBSD.org> | 1995-07-05 12:15:52 +0000 |
commit | 5cca7ebf45bf5ebb408fa28e2250deb9b5656ab8 (patch) | |
tree | 5de9f56f2672dc4e85c0b38593c5e3e11ed9665d | |
parent | 5f993810e3e669cf015a523fde64e4fc5da584e9 (diff) | |
download | FreeBSD-src-5cca7ebf45bf5ebb408fa28e2250deb9b5656ab8.zip FreeBSD-src-5cca7ebf45bf5ebb408fa28e2250deb9b5656ab8.tar.gz |
Rewrite:
- use pseudo-dma
- provide the same features and interface as sio
- support multiple boards
- fix bugs.
Some compile-time configuration constants are set to support higher
speeds and Cyclom-16Y's at a 30% relative cost in efficiency.
Cyclom-16Y support is untested.
-rw-r--r-- | sys/dev/cy/cy.c | 3406 | ||||
-rw-r--r-- | sys/dev/cy/cy_isa.c | 3406 | ||||
-rw-r--r-- | sys/dev/cy/cyreg.h | 57 | ||||
-rw-r--r-- | sys/dev/ic/cd1400.h | 253 | ||||
-rw-r--r-- | sys/i386/isa/cy.c | 3406 | ||||
-rw-r--r-- | sys/i386/isa/cyreg.h | 57 | ||||
-rw-r--r-- | sys/i386/isa/ic/cd1400.h | 253 |
7 files changed, 6942 insertions, 3896 deletions
diff --git a/sys/dev/cy/cy.c b/sys/dev/cy/cy.c index fd898f2..81f7022 100644 --- a/sys/dev/cy/cy.c +++ b/sys/dev/cy/cy.c @@ -1,4 +1,4 @@ -/* +/*- * cyclades cyclom-y serial driver * Andrew Herbert <andrew@werple.apana.org.au>, 17 August 1993 * @@ -27,1262 +27,1740 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $Id: cy.c,v 1.6 1995/03/28 12:29:11 bde Exp $ + * $Id: cy.c,v 1.7 1995/05/30 08:01:34 rgrimes Exp $ */ +#include "cy.h" +#if NCY > 0 /* - * Device minor number encoding: - * - * c c x x u u u u - bits in the minor device number - * - * bits meaning - * ---- ------- - * uuuu physical serial line (i.e. unit) to use - * 0-7 on a cyclom-8Y, 0-15 on a cyclom-16Y - * xx unused - * cc carrier control mode - * 00 complete hardware carrier control of the tty. - * DCD must be high for the open(2) to complete. - * 01 dialin pseudo-device (not yet implemented) - * 10 carrier ignored until a high->low transition - * 11 carrier completed ignored + * TODO: + * Check that cy16's work. + * Implement BREAK. + * Fix overflows when closing line. + * Atomic COR change. + * Don't report individual ports in devconf; busy flag for board should be + * union of the current individual busy flags. + * Consoles. */ /* - * Known deficiencies: - * - * * no BREAK handling - breaks are ignored, and can't be sent either - * * no support for bad-char reporting, except via PARMRK - * * no support for dialin + dialout devices + * Temporary compile-time configuration options. */ +#define RxFifoThreshold (CD1400_RX_FIFO_SIZE / 2) + /* Number of chars in the receiver FIFO before an + * an interrupt is generated. Should depend on + * line speed. Needs to be about 6 on a 486DX33 + * for 4 active ports at 115200 bps. Why doesn't + * 10 work? + */ +#define PollMode /* Use polling-based irq service routine, not the + * hardware svcack lines. Must be defined for + * Cyclom-16Y boards. Less efficient for Cyclom-8Ys, + * and stops 4 * 115200 bps from working. + */ +#undef Smarts /* Enable slightly more CD1400 intelligence. Mainly + * the output CR/LF processing, plus we can avoid a + * few checks usually done in ttyinput(). + * + * XXX not fully implemented, and not particularly + * worthwhile. + */ +#undef CyDebug /* Include debugging code (not very expensive). */ -#include "cy.h" -#if NCY > 0 - -/* This disgusing hack because we actually have 16 units on one controller */ -#if NCY < 2 -#undef NCY -#define NCY (16) -#endif +/* These will go away. */ +#undef SOFT_CTS_OFLOW +#define SOFT_HOTCHAR #include <sys/param.h> #include <sys/systm.h> -#include <sys/kernel.h> -#include <sys/malloc.h> +#include <sys/reboot.h> #include <sys/ioctl.h> +#define TTYDEFCHARS /* XXX TK2.0 */ #include <sys/tty.h> +#undef TTYDEFCHARS #include <sys/proc.h> #include <sys/user.h> #include <sys/conf.h> +#include <sys/dkstat.h> #include <sys/file.h> #include <sys/uio.h> #include <sys/kernel.h> +#include <sys/malloc.h> #include <sys/syslog.h> +#include <sys/devconf.h> -#include <machine/cpu.h> -#ifdef NetBSD -#include <machine/pio.h> -#include <machine/cpufunc.h> -#else #include <machine/clock.h> -#endif +#include <i386/isa/icu.h> /* XXX just to get at `imen' */ +#include <i386/isa/isa.h> #include <i386/isa/isa_device.h> +#include <i386/isa/cyreg.h> #include <i386/isa/ic/cd1400.h> -#define RxFifoThreshold 3 /* 3 characters (out of 12) in the receive - * FIFO before an interrupt is generated - */ -#define FastRawInput /* bypass the regular char-by-char canonical input - * processing whenever possible - */ -#define PollMode /* use polling-based irq service routine, not the - * hardware svcack lines. Must be defined for - * cyclom-16y boards. - * - * XXX cyclom-8y doesn't work without this defined - * either (!) - */ -#define LogOverruns /* log receive fifo overruns */ -#undef TxBuffer /* buffer driver output, to be slightly more - * efficient - * - * XXX presently buggy - */ -#undef Smarts /* enable slightly more CD1400 intelligence. Mainly - * the output CR/LF processing, plus we can avoid a - * few checks usually done in ttyinput(). - * - * XXX not yet implemented, and not particularly - * worthwhile either. - */ -#define CyDebug /* include debugging code (minimal effect on - * performance) - */ +/* + * Dictionary so that I can name everything *sio* or *com* to compare with + * sio.c. There is also lots of ugly formatting and unnecessary ifdefs to + * simplify the comparision. These will go away. + */ +#define LSR_BI CD1400_RDSR_BREAK +#define LSR_FE CD1400_RDSR_FE +#define LSR_OE CD1400_RDSR_OE +#define LSR_PE CD1400_RDSR_PE +#define MCR_DTR CD1400_MSVR2_DTR +#define MCR_RTS CD1400_MSVR1_RTS +#define MSR_CTS CD1400_MSVR2_CTS +#define MSR_DCD CD1400_MSVR2_CD +#define MSR_DSR CD1400_MSVR2_DSR +#define MSR_RI CD1400_MSVR2_RI +#define NSIO (NCY * CY_MAX_PORTS) +#define comconsole cyconsole +#define comdefaultrate cydefaultrate +#define com_events cy_events +#define comhardclose cyhardclose +#define commajor cymajor +#define commctl cymctl +#define comparam cyparam +#define comspeed cyspeed +#define comstart cystart +#define comwakeup cywakeup +#define kdc_sio kdc_cy +#define nsio_tty ncy_tty +#define p_com_addr p_cy_addr +#define sioattach cyattach +#define sioclose cyclose +#define siodevtotty cydevtotty +#define siodriver cydriver +#define siodtrwakeup cydtrwakeup +#define sioioctl cyioctl +#define siointr cyintr +#define siointr1 cyintr1 +#define siointrts cyintrts +#define sioopen cyopen +#define siopoll cypoll +#define sioprobe cyprobe +#define sioread cyread +#define sioregisterdev cyregisterdev +#define siosettimeout cysettimeout +#define siostop cystop +#define siowrite cywrite +#define sio_timeout cy_timeout +#define sio_timeouts_until_log cy_timeouts_until_log +#define sio_tty cy_tty + +#define CY_MAX_PORTS (CD1400_NO_OF_CHANNELS * CY_MAX_CD1400s) + +/* We encode the cyclom unit number (cyu) in spare bits in the IVR's. */ +#define CD1400_xIVR_CHAN_SHIFT 3 +#define CD1400_xIVR_CHAN 0x0F /* XXX reduce to pack Cyclom-8Ys */ -#define CY_RX_BUFS 2 /* two receive buffers per port */ -#define CY_RX_BUF_SIZE 256 /* bytes per receive buffer */ -#define CY_TX_BUF_SIZE 512 /* bytes per transmit buffer */ +/* + * XXX temporary kludges for 2.0 (XXX TK2.0). + */ +#define TSA_CARR_ON(tp) ((void *)&(tp)->t_rawq) +#define TSA_OCOMPLETE(tp) ((void *)&(tp)->t_outq) +#define TSA_OLOWAT(tp) ((void *)&(tp)->t_outq) +static void termioschars __P((struct termios *t)); +static void +termioschars(t) + struct termios *t; +{ -/* #define CD1400s_PER_CYCLOM 1 */ /* cyclom-4y */ -/* #define CD1400s_PER_CYCLOM 2 */ /* cyclom-8y */ -#define CD1400s_PER_CYCLOM 4 /* cyclom-16y */ + bcopy(ttydefchars, t->c_cc, sizeof t->c_cc); +} -/* FreeBSD's getty doesn't know option for setting RTS/CTS handshake. Its - getty, like a lot of other old cruft, should be replaced with something - which used POSIX tty interfaces which at least allow enabling it. In the - meantime, use the force. */ -#define ALWAYS_RTS_CTS 1 +#define LOTS_OF_EVENTS 64 /* helps separate urgent events from input */ +#define RB_I_HIGH_WATER (TTYHOG - 2 * RS_IBUFSIZE) +#define RS_IBUFSIZE 256 -#if CD1400s_PER_CYCLOM < 4 -#define CD1400_MEMSIZE 0x400 /* 4*256 bytes per chip: cyclom-[48]y */ -#else -#define CD1400_MEMSIZE 0x100 /* 256 bytes per chip: cyclom-16y */ - /* XXX or is it 0x400 like the rest? */ -#define CYCLOM_16 1 /* This is a cyclom-16Y */ -#endif +#define CALLOUT_MASK 0x80 +#define CONTROL_MASK 0x60 +#define CONTROL_INIT_STATE 0x20 +#define CONTROL_LOCK_STATE 0x40 +#define DEV_TO_UNIT(dev) (MINOR_TO_UNIT(minor(dev))) +#define MINOR_MAGIC_MASK (CALLOUT_MASK | CONTROL_MASK) +#define MINOR_TO_UNIT(mynor) ((mynor) & ~MINOR_MAGIC_MASK) -#define PORTS_PER_CYCLOM (CD1400_NO_OF_CHANNELS * CD1400s_PER_CYCLOM) -#define CYCLOM_RESET_16 0x1400 /* cyclom-16y reset */ -#define CYCLOM_CLEAR_INTR 0x1800 /* intr ack address */ -#define CYCLOM_CLOCK 25000000 /* baud rate clock */ +/* + * Input buffer watermarks. + * The external device is asked to stop sending when the buffer exactly reaches + * high water, or when the high level requests it. + * The high level is notified immediately (rather than at a later clock tick) + * when this watermark is reached. + * The buffer size is chosen so the watermark should almost never be reached. + * The low watermark is invisibly 0 since the buffer is always emptied all at + * once. + */ +#define RS_IHIGHWATER (3 * RS_IBUFSIZE / 4) -#define CY_UNITMASK 0x0f -#define CY_CARRIERMASK 0xC0 -#define CY_CARRIERSHIFT 6 +/* + * com state bits. + * (CS_BUSY | CS_TTGO) and (CS_BUSY | CS_TTGO | CS_ODEVREADY) must be higher + * than the other bits so that they can be tested as a group without masking + * off the low bits. + * + * The following com and tty flags correspond closely: + * CS_BUSY = TS_BUSY (maintained by comstart(), siopoll() and + * siostop()) + * CS_TTGO = ~TS_TTSTOP (maintained by comstart() and siostop()) + * CS_CTS_OFLOW = CCTS_OFLOW (maintained by comparam()) + * CS_RTS_IFLOW = CRTS_IFLOW (maintained by comparam()) + * TS_FLUSH is not used. + * XXX I think TIOCSETA doesn't clear TS_TTSTOP when it clears IXON. + * XXX CS_*FLOW should be CF_*FLOW in com->flags (control flags not state). + */ +#define CS_BUSY 0x80 /* output in progress */ +#define CS_TTGO 0x40 /* output not stopped by XOFF */ +#define CS_ODEVREADY 0x20 /* external device h/w ready (CTS) */ +#define CS_CHECKMSR 1 /* check of MSR scheduled */ +#define CS_CTS_OFLOW 2 /* use CTS output flow control */ +#define CS_DTR_OFF 0x10 /* DTR held off */ +#define CS_ODONE 4 /* output completed */ +#define CS_RTS_IFLOW 8 /* use RTS input flow control */ + +static char const * const error_desc[] = { +#define CE_OVERRUN 0 + "silo overflow", +#define CE_INTERRUPT_BUF_OVERFLOW 1 + "interrupt-level buffer overflow", +#define CE_TTY_BUF_OVERFLOW 2 + "tty-level buffer overflow", +}; -#define UNIT(x) (minor(x) & CY_UNITMASK) -#define CARRIER_MODE(x) ((minor(x) & CY_CARRIERMASK) >> CY_CARRIERSHIFT) +#define CE_NTYPES 3 +#define CE_RECORD(com, errnum) (++(com)->delta_error_counts[errnum]) -typedef u_char * volatile cy_addr; +/* types. XXX - should be elsewhere */ +typedef u_char bool_t; /* boolean */ +typedef u_char volatile *cy_addr; -int cyprobe(struct isa_device *dev); -int cyattach(struct isa_device *isdp); -void cystart(struct tty *tp); -int cyparam(struct tty *tp, struct termios *t); -int cyspeed(int speed, int *prescaler_io); -static void cy_channel_init(dev_t dev, int reset); -static void cd1400_channel_cmd(cy_addr base, u_char cmd); +/* queue of linear buffers */ +struct lbq { + u_char *l_head; /* next char to process */ + u_char *l_tail; /* one past the last char to process */ + struct lbq *l_next; /* next in queue */ + bool_t l_queued; /* nonzero if queued */ +}; -/* hsu@clinet.fi: sigh */ -#ifdef __NetBSD__ -#define DELAY(foo) delay(foo) -void delay(int delay); +/* com device structure */ +struct com_s { + u_char state; /* miscellaneous flag bits */ + bool_t active_out; /* nonzero if the callout device is open */ +#if 0 + u_char cfcr_image; /* copy of value written to CFCR */ + u_char ftl; /* current rx fifo trigger level */ + u_char ftl_init; /* ftl_max for next open() */ + u_char ftl_max; /* maximum ftl for curent open() */ + bool_t hasfifo; /* nonzero for 16550 UARTs */ + bool_t loses_outints; /* nonzero if device loses output interrupts */ #endif - -/* Better get rid of this until the core people agree on kernel interfaces. - At least it will then compile on both WhichBSDs. - */ + u_char mcr_image; /* copy of value written to MCR */ #if 0 -extern unsigned int delaycount; /* calibrated 1 ms cpu-spin delay */ +#ifdef COM_MULTIPORT + bool_t multiport; /* is this unit part of a multiport device? */ +#endif /* COM_MULTIPORT */ + bool_t no_irq; /* nonzero if irq is not attached */ + bool_t poll; /* nonzero if polling is required */ + bool_t poll_output; /* nonzero if polling for output is required */ #endif + int unit; /* unit number */ + int dtr_wait; /* time to hold DTR down on close (* 1/hz) */ +#if 0 + u_int tx_fifo_size; +#endif + u_int wopeners; /* # processes waiting for DCD in open() */ -struct isa_driver cydriver = { - cyprobe, cyattach, "cy" -}; + /* + * The high level of the driver never reads status registers directly + * because there would be too many side effects to handle conveniently. + * Instead, it reads copies of the registers stored here by the + * interrupt handler. + */ + u_char last_modem_status; /* last MSR read by intr handler */ + u_char prev_modem_status; /* last MSR handled by high level */ -/* low-level ping-pong buffer structure */ + u_char hotchar; /* ldisc-specific char to be handled ASAP */ + u_char *ibuf; /* start of input buffer */ + u_char *ibufend; /* end of input buffer */ + u_char *ihighwater; /* threshold in input buffer */ + u_char *iptr; /* next free spot in input buffer */ -struct cy_buf { - u_char *next_char; /* location of next char to write */ - u_int free; /* free chars remaining in buffer */ - struct cy_buf *next_buf; /* circular, you know */ - u_char buf[CY_RX_BUF_SIZE]; /* start of the buffer */ -}; + struct lbq obufq; /* head of queue of output buffers */ + struct lbq obufs[2]; /* output buffers */ -/* low-level ring buffer */ + cy_addr cy_iobase; /* base address of this port's cyclom */ + cy_addr iobase; /* base address of this port's cd1400 */ -#ifdef TxBuffer -struct cy_ring { - u_char buf[CY_TX_BUF_SIZE]; - u_char *head; - u_char *tail; /* next pos. to insert char */ - u_char *endish; /* physical end of buf */ - u_int used; /* no. of chars in queue */ -}; + struct tty *tp; /* cross reference */ + + /* Initial state. */ + struct termios it_in; /* should be in struct tty */ + struct termios it_out; + + /* Lock state. */ + struct termios lt_in; /* should be in struct tty */ + struct termios lt_out; + + bool_t do_timestamp; + struct timeval timestamp; + + u_long bytes_in; /* statistics */ + u_long bytes_out; + u_int delta_error_counts[CE_NTYPES]; + u_long error_counts[CE_NTYPES]; + + u_int recv_exception; /* exception chars received */ + u_int mdm; /* modem signal changes */ +#ifdef CyDebug + u_int start_count; /* no. of calls to comstart() */ + u_int start_real; /* no. of calls that did something */ #endif + u_char channel_control;/* CD1400 CCR control command shadow */ + u_char cor[3]; /* CD1400 COR1-3 shadows */ + u_char intr_enable; /* CD1400 SRER shadow */ + /* + * Ping-pong input buffers. The extra factor of 2 in the sizes is + * to allow for an error byte for each input byte. + */ +#define CE_INPUT_OFFSET RS_IBUFSIZE + u_char ibuf1[2 * RS_IBUFSIZE]; + u_char ibuf2[2 * RS_IBUFSIZE]; + + /* + * Data area for output buffers. Someday we should build the output + * buffer queue without copying data. + */ + u_char obuf1[256]; + u_char obuf2[256]; + + struct kern_devconf kdc; +}; /* - * define a structure to keep track of each serial line + * XXX public functions in drivers should be declared in headers produced + * by `config', not here. */ -struct cy { - cy_addr base_addr; /* base address of this port's cd1400 */ - struct tty *tty; - u_int dtrwait; /* time (in ticks) to hold dtr low after close */ - u_int recv_exception; /* exception chars received */ - u_int recv_normal; /* normal chars received */ - u_int xmit; /* chars transmitted */ - u_int mdm; /* modem signal changes */ +/* Interrupt handling entry points. */ +void siointr __P((int unit)); +void siointrts __P((int unit)); +void siopoll __P((void)); + +/* Device switch entry points. */ +int sioopen __P((dev_t dev, int oflags, int devtype, + struct proc *p)); +int sioclose __P((dev_t dev, int fflag, int devtype, + struct proc *p)); +int sioread __P((dev_t dev, struct uio *uio, int ioflag)); +int siowrite __P((dev_t dev, struct uio *uio, int ioflag)); +int sioioctl __P((dev_t dev, int cmd, caddr_t data, + int fflag, struct proc *p)); +void siostop __P((struct tty *tp, int rw)); +#define sioreset noreset +struct tty *siodevtotty __P((dev_t dev)); +#define siommap nommap +#define siostrategy nostrategy + +static int sioattach __P((struct isa_device *dev)); +static void cd1400_channel_cmd __P((cy_addr iobase, int cmd)); +static timeout_t siodtrwakeup; +static void comhardclose __P((struct com_s *com)); +static void siointr1 __P((struct com_s *com)); +static int commctl __P((struct com_s *com, int bits, int how)); +static int comparam __P((struct tty *tp, struct termios *t)); +static int sioprobe __P((struct isa_device *dev)); +static void sioregisterdev __P((struct isa_device *id)); +static void siosettimeout __P((void)); +static int comspeed __P((speed_t speed, int *prescaler_io)); +static void comstart __P((struct tty *tp)); +static timeout_t comwakeup; +static void disc_optim __P((struct tty *tp, struct termios *t, + struct com_s *com)); + #ifdef CyDebug - u_int start_count; /* no. of calls to cystart() */ - u_int start_real; /* no. of calls that did something */ -#endif - u_char carrier_mode; /* hardware carrier handling mode */ - /* - * 0 = always use - * 1 = always use (dialin port) - * 2 = ignore during open, then use it - * 3 = ignore completely - */ - u_char carrier_delta; /* true if carrier has changed state */ - u_char fifo_overrun; /* true if cd1400 receive fifo has... */ - u_char rx_buf_overrun; /* true if low-level buf overflow */ - u_char intr_enable; /* CD1400 SRER shadow */ - u_char modem_sig; /* CD1400 modem signal shadow */ - u_char channel_control;/* CD1400 CCR control command shadow */ - u_char cor[3]; /* CD1400 COR1-3 shadows */ -#ifdef Smarts - u_char spec_char[4]; /* CD1400 SCHR1-4 shadows */ -#endif - struct cy_buf *rx_buf; /* current receive buffer */ - struct cy_buf rx_buf_pool[CY_RX_BUFS];/* receive ping-pong buffers */ -#ifdef TxBuffer - struct cy_ring tx_buf; /* transmit buffer */ +void cystatus __P((int unit)); #endif + +/* table and macro for fast conversion from a unit number to its com struct */ +static struct com_s *p_com_addr[NSIO]; +#define com_addr(unit) (p_com_addr[unit]) + +static struct timeval intr_timestamp; + +struct isa_driver siodriver = { + sioprobe, sioattach, "cy" }; -int cydefaultrate = TTYDEF_SPEED; -cy_addr cyclom_base; /* base address of the card */ -static struct cy *info[NCY*PORTS_PER_CYCLOM]; -#ifdef __FreeBSD__ /* XXX actually only temporarily for 2.1-Development */ -struct tty cy_tty[NCY*PORTS_PER_CYCLOM]; +#ifdef COMCONSOLE +#undef COMCONSOLE +#define COMCONSOLE 1 #else -struct tty *cy_tty[NCY*PORTS_PER_CYCLOM]; +#define COMCONSOLE 0 #endif -static volatile u_char timeout_scheduled = 0; /* true if a timeout has been scheduled */ -#ifdef CyDebug -u_int cy_svrr_probes = 0; /* debugging */ -u_int cy_timeouts = 0; -u_int cy_timeout_req = 0; +#ifndef CONUNIT +#define CONUNIT (0) #endif -/**********************************************************************/ - -int -cyprobe(struct isa_device *dev) -{ - int i, j; - u_char version = 0; /* firmware version */ +static int comconsole = CONUNIT; +static speed_t comdefaultrate = TTYDEF_SPEED; +static u_int com_events; /* input chars + weighted output completions */ +static int commajor; +static int sio_timeout; +static int sio_timeouts_until_log; +#if 0 /* XXX TK2.0 */ +static struct tty *sio_tty[NSIO]; +#else +static struct tty sio_tty[NSIO]; +static int nsio_tty = NSIO; +#endif - /* Cyclom-16Y hardware reset (Cyclom-8Ys don't care) */ - i = *(cy_addr)(dev->id_maddr + CYCLOM_RESET_16); +#ifdef KGDB +#include <machine/remote-sl.h> - DELAY(500); /* wait for the board to get its act together (500 us) */ +extern int kgdb_dev; +extern int kgdb_rate; +extern int kgdb_debug_init; +#endif - for (i = 0; i < CD1400s_PER_CYCLOM; i++) { - cy_addr base = dev->id_maddr + i * CD1400_MEMSIZE; +#ifdef CyDebug +static u_int cd_inbs; +static u_int cy_inbs; +static u_int cd_outbs; +static u_int cy_outbs; +static u_int cy_svrr_probes; +static u_int cy_timeouts; +#endif - /* wait for chip to become ready for new command */ - for (j = 0; j < 100; j += 50) { - DELAY(50); /* wait 50 us */ +static int cy_nr_cd1400s[NCY]; +#undef RxFifoThreshold +static int volatile RxFifoThreshold = (CD1400_RX_FIFO_SIZE / 2); + +static struct kern_devconf kdc_sio[NCY] = { { + 0, 0, 0, /* filled in by dev_attach */ + "cyc", 0, { MDDT_ISA, 0, "tty" }, + isa_generic_externalize, 0, 0, ISA_EXTERNALLEN, + &kdc_isa0, /* parent */ + 0, /* parentdata */ + DC_UNCONFIGURED, /* state */ + "Cyclades multiport board", + DC_CLS_MISC /* just an ordinary device */ +} }; - if (!*(base + CD1400_CCR)) - break; - } +static void +sioregisterdev(id) + struct isa_device *id; +{ + int unit; - /* clear the GFRCR register */ - *(base + CD1400_GFRCR) = 0; + unit = id->id_unit; + if (unit != 0) + kdc_sio[unit] = kdc_sio[0]; + kdc_sio[unit].kdc_unit = unit; + kdc_sio[unit].kdc_isa = id; + dev_attach(&kdc_sio[unit]); +} - /* issue a reset command */ - *(base + CD1400_CCR) = CD1400_CMD_RESET; +static int +sioprobe(dev) + struct isa_device *dev; +{ + int cyu; + u_char firmware_version; + cy_addr iobase; + int unit; - /* wait for the CD1400 to initialise itself */ - for (j = 0; j < 1000; j += 50) { - DELAY(50); /* wait 50 us */ + unit = dev->id_unit; + if ((u_int)unit >= NCY) + return (0); + cy_nr_cd1400s[unit] = 0; + sioregisterdev(dev); - /* retrieve firmware version */ - version = *(base + CD1400_GFRCR); - if (version) - break; - } + /* Cyclom-16Y hardware reset (Cyclom-8Ys don't care) */ + cy_inb((cy_addr)dev->id_maddr, CY16_RESET); /* XXX? */ - /* anything in the 40-4f range is fine */ - if ((version & 0xf0) != 0x40) { - return 0; - } - } + DELAY(500); /* wait for the board to get its act together */ - return 1; /* found */ -} + for (cyu = 0, iobase = (cy_addr)dev->id_maddr; cyu < CY_MAX_CD1400s; + ++cyu, iobase += CY_CD1400_MEMSIZE) { + int i; + /* wait for chip to become ready for new command */ + for (i = 0; i < 100; i += 50) { + DELAY(50); + if (!cd_inb(iobase, CD1400_CCR)) + break; + } -int -cyattach(struct isa_device *isdp) -{ -/* u_char unit = UNIT(isdp->id_unit); */ - int i, j, k; + /* clear the GFRCR register */ + cd_outb(iobase, CD1400_GFRCR, 0); - /* global variable used various routines */ - cyclom_base = (cy_addr)isdp->id_maddr; + /* issue a reset command */ + cd_outb(iobase, CD1400_CCR, + CD1400_CCR_CMDRESET | CD1400_CCR_FULLRESET); - for (i = 0, k = 0; i < CD1400s_PER_CYCLOM; i++) { - cy_addr base = cyclom_base + i * CD1400_MEMSIZE; + /* wait for the CD1400 to initialize itself */ + for (i = 0; i < 1000; i += 50) { + DELAY(50); - /* setup a 1ms clock tick */ - *(base + CD1400_PPR) = CD1400_CLOCK_25_1MS; + /* retrieve firmware version */ + firmware_version = cd_inb(iobase, CD1400_GFRCR); + if (firmware_version != 0) + break; + } - for (j = 0; j < CD1400_NO_OF_CHANNELS; j++, k++) { - struct cy *ip; + /* + * Anything in the 0x40-0x4F range is fine. + * If one CD1400 is bad then we don't support higher + * numbered good ones on this board. + */ + if ((firmware_version & 0xF0) != 0x40) + break; + ++cy_nr_cd1400s[unit]; + } + return (cy_nr_cd1400s[unit] == 0 ? 0 : -1); +} - /* - * grab some space. it'd be more polite to do this in cyopen(), - * but hey. - */ - info[k] = ip = malloc(sizeof(struct cy), M_DEVBUF, M_WAITOK); +static int +sioattach(isdp) + struct isa_device *isdp; +{ + int cyu; + cy_addr cy_iobase; + cy_addr iobase; + int ncyu; + int unit; - /* clear all sorts of junk */ - bzero(ip, sizeof(struct cy)); + unit = isdp->id_unit; + if ((u_int)unit >= NCY) + return (0); + ncyu = cy_nr_cd1400s[unit]; + if (ncyu == 0) + return (0); + isdp->id_ri_flags |= RI_FAST; + + cy_iobase = (cy_addr)isdp->id_maddr; + unit *= CY_MAX_PORTS; + for (cyu = 0, iobase = cy_iobase; cyu < ncyu; + ++cyu, iobase += CY_CD1400_MEMSIZE) { + int cdu; + + /* Set up a receive timeout period of than 1+ ms. */ + cd_outb(iobase, CD1400_PPR, + howmany(CY_CLOCK / CD1400_PPR_PRESCALER, 1000)); + + for (cdu = 0; cdu < CD1400_NO_OF_CHANNELS; ++cdu, ++unit) { + struct com_s *com; + int s; + + com = malloc(sizeof *com, M_DEVBUF, M_NOWAIT); + if (com == NULL) + break; + bzero(com, sizeof *com); + com->unit = unit; + com->dtr_wait = 3 * hz; + com->iptr = com->ibuf = com->ibuf1; + com->ibufend = com->ibuf1 + RS_IBUFSIZE; + com->ihighwater = com->ibuf1 + RS_IHIGHWATER; + com->obufs[0].l_head = com->obuf1; + com->obufs[1].l_head = com->obuf2; + + com->cy_iobase = cy_iobase; + com->iobase = iobase; - ip->base_addr = base; + /* + * We don't use all the flags from <sys/ttydefaults.h> since they + * are only relevant for logins. It's important to have echo off + * initially so that the line doesn't start blathering before the + * echo flag can be turned off. + */ + com->it_in.c_iflag = 0; + com->it_in.c_oflag = 0; + com->it_in.c_cflag = TTYDEF_CFLAG; + com->it_in.c_lflag = 0; + if (unit == comconsole && (COMCONSOLE || boothowto & RB_SERIAL)) { + com->it_in.c_iflag = TTYDEF_IFLAG; + com->it_in.c_oflag = TTYDEF_OFLAG; + com->it_in.c_cflag = TTYDEF_CFLAG | CLOCAL; + com->it_in.c_lflag = TTYDEF_LFLAG; + com->lt_out.c_cflag = com->lt_in.c_cflag = CLOCAL; + } + termioschars(&com->it_in); + com->it_in.c_ispeed = com->it_in.c_ospeed = comdefaultrate; + com->it_out = com->it_in; + + com->kdc = kdc_sio[0]; + com->kdc.kdc_name = "cy"; + com->kdc.kdc_unit = unit; + com->kdc.kdc_isa = isdp; + com->kdc.kdc_parent = &kdc_sio[isdp->id_unit]; + com->kdc.kdc_state = DC_IDLE; + com->kdc.kdc_description = + "Serial port: Cirrus Logic CD1400"; + com->kdc.kdc_class = DC_CLS_SERIAL; + dev_attach(&com->kdc); - /* initialise the channel, without resetting it first */ - cy_channel_init(k, 0); + s = spltty(); + com_addr(unit) = com; + splx(s); + } } - } + kdc_sio[isdp->id_unit].kdc_state = DC_BUSY; /* XXX */ - /* clear interrupts */ - *(cyclom_base + CYCLOM_CLEAR_INTR) = (u_char)0; + /* ensure an edge for the next interrupt */ + cy_outb(cy_iobase, CY_CLEAR_INTR, 0); - return 1; + return (1); } - int -cyopen(dev_t dev, int flag, int mode, struct proc *p) +sioopen(dev, flag, mode, p) + dev_t dev; + int flag; + int mode; + struct proc *p; { - u_int unit = UNIT(dev); - struct cy *infop; - cy_addr base; + struct com_s *com; + int error; + cy_addr iobase; + int mynor; + int s; struct tty *tp; - int error = 0; - u_char carrier; + int unit; - if (unit >= /* NCY * ? */ PORTS_PER_CYCLOM) + mynor = minor(dev); + unit = MINOR_TO_UNIT(mynor); + if ((u_int) unit >= NSIO || (com = com_addr(unit)) == NULL) return (ENXIO); - - infop = info[unit]; - base = infop->base_addr; -#ifdef __FreeBSD__ - infop->tty = &cy_tty[unit]; + if (mynor & CONTROL_MASK) + return (0); +#if 0 /* XXX TK2.0 */ + tp = com->tp = sio_tty[unit] = ttymalloc(sio_tty[unit]); #else - if (!cy_tty[unit]) - infop->tty = cy_tty[unit] = ttymalloc(); -#endif - tp = infop->tty; - - tp->t_oproc = cystart; - tp->t_param = cyparam; - tp->t_dev = dev; - if (!(tp->t_state & TS_ISOPEN)) { - tp->t_state |= TS_WOPEN; - ttychars(tp); - if (tp->t_ispeed == 0) { - tp->t_iflag = TTYDEF_IFLAG; - tp->t_oflag = TTYDEF_OFLAG; - tp->t_cflag = TTYDEF_CFLAG; - tp->t_lflag = TTYDEF_LFLAG; - tp->t_ispeed = tp->t_ospeed = cydefaultrate; + tp = com->tp = &sio_tty[unit]; +#endif + s = spltty(); + /* + * We jump to this label after all non-interrupted sleeps to pick + * up any changes of the device state. + */ +open_top: + while (com->state & CS_DTR_OFF) { + error = tsleep(&com->dtr_wait, TTIPRI | PCATCH, "cydtr", 0); + if (error != 0) + goto out; + } + com->kdc.kdc_state = DC_BUSY; + if (tp->t_state & TS_ISOPEN) { + /* + * The device is open, so everything has been initialized. + * Handle conflicts. + */ + if (mynor & CALLOUT_MASK) { + if (!com->active_out) { + error = EBUSY; + goto out; + } + } else { + if (com->active_out) { + if (flag & O_NONBLOCK) { + error = EBUSY; + goto out; + } + error = tsleep(&com->active_out, + TTIPRI | PCATCH, "cybi", 0); + if (error != 0) + goto out; + goto open_top; + } + } + if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) { + error = EBUSY; + goto out; } + } else { + /* + * The device isn't open, so there are no conflicts. + * Initialize it. Initialization is done twice in many + * cases: to preempt sleeping callin opens if we are + * callout, and to complete a callin open after DCD rises. + */ + tp->t_oproc = comstart; + tp->t_param = comparam; + tp->t_dev = dev; + tp->t_termios = mynor & CALLOUT_MASK + ? com->it_out : com->it_in; +#if 0 + (void)commctl(com, TIOCM_DTR | TIOCM_RTS, DMSET); + com->ftl_max = com->ftl_init; + com->poll = com->no_irq; + com->poll_output = com->loses_outints; +#endif + ++com->wopeners; + iobase = com->iobase; - (void) spltty(); - cy_channel_init(unit, 1); /* reset the hardware */ + /* reset this channel */ + cd_outb(iobase, CD1400_CAR, unit & CD1400_CAR_CHAN); + cd1400_channel_cmd(iobase, CD1400_CCR_CMDRESET); + + /* + * Resetting disables the transmitter and receiver as well as + * flushing the fifos so some of our cached state becomes + * invalid. The documentation suggests that all registers + * for the current channel are reset to defaults, but + * apparently none are. We wouldn't want DTR cleared. + */ + com->channel_control = 0; + + /* Encode per-board unit in LIVR for access in intr routines. */ + cd_outb(iobase, CD1400_LIVR, + (unit & CD1400_xIVR_CHAN) << CD1400_xIVR_CHAN_SHIFT); /* * raise dtr and generally set things up correctly. this * has the side-effect of selecting the appropriate cd1400 * channel, to help us with subsequent channel control stuff */ - cyparam(tp, &tp->t_termios); - - /* check carrier, and set t_state's TS_CARR_ON flag accordingly */ - infop->modem_sig = *(base + CD1400_MSVR); - carrier = infop->modem_sig & CD1400_MSVR_CD; - - if (carrier || (infop->carrier_mode >= 2)) - tp->t_state |= TS_CARR_ON; - else - tp->t_state &=~ TS_CARR_ON; - + error = comparam(tp, &tp->t_termios); + --com->wopeners; + if (error != 0) + goto out; /* - * enable modem & rx interrupts - relies on cyparam() - * having selected the appropriate cd1400 channel + * XXX we should goto open_top if comparam() slept. */ - infop->intr_enable = (1 << 7) | (1 << 4); - *(base + CD1400_SRER) = infop->intr_enable; - ttsetwater(tp); - } else if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) - return (EBUSY); - - if (!(flag & O_NONBLOCK)) - while (!(tp->t_cflag & CLOCAL) && - !(tp->t_state & TS_CARR_ON) && !error) - error = ttysleep(tp, (caddr_t)&tp->t_rawq, - TTIPRI|PCATCH, ttopen, 0); - (void) spl0(); - - if (!error) - error = (*linesw[(u_char)tp->t_line].l_open)(dev, tp); - return (error); -} /* end of cyopen() */ - - -void -cyclose_wakeup(void *arg) -{ - wakeup(arg); -} /* end of cyclose_wakeup() */ +#if 0 + if (com->hasfifo) { + /* + * (Re)enable and drain fifos. + * + * Certain SMC chips cause problems if the fifos + * are enabled while input is ready. Turn off the + * fifo if necessary to clear the input. We test + * the input ready bit after enabling the fifos + * since we've already enabled them in comparam() + * and to handle races between enabling and fresh + * input. + */ + while (TRUE) { + outb(iobase + com_fifo, + FIFO_RCV_RST | FIFO_XMT_RST + | FIFO_ENABLE | com->ftl); + DELAY(100); + if (!(inb(com->line_status_port) & LSR_RXRDY)) + break; + outb(iobase + com_fifo, 0); + DELAY(100); + (void) inb(com->data_port); + } + } + disable_intr(); + (void) inb(com->line_status_port); + (void) inb(com->data_port); + com->prev_modem_status = com->last_modem_status + = inb(com->modem_status_port); + outb(iobase + com_ier, IER_ERXRDY | IER_ETXRDY | IER_ERLS + | IER_EMSC); + enable_intr(); +#else /* !0 */ + /* XXX raise RTS too */ + (void)commctl(com, TIOCM_DTR | TIOCM_RTS, DMSET); + disable_intr(); + com->prev_modem_status = com->last_modem_status + = cd_inb(iobase, CD1400_MSVR2); + cd_outb(iobase, CD1400_SRER, + com->intr_enable + = CD1400_SRER_MDMCH | CD1400_SRER_RXDATA); + enable_intr(); +#endif /* 0 */ + /* + * Handle initial DCD. Callout devices get a fake initial + * DCD (trapdoor DCD). If we are callout, then any sleeping + * callin opens get woken up and resume sleeping on "cybi" + * instead of "cydcd". + */ + /* + * XXX `mynor & CALLOUT_MASK' should be + * `tp->t_cflag & (SOFT_CARRIER | TRAPDOOR_CARRIER) where + * TRAPDOOR_CARRIER is the default initial state for callout + * devices and SOFT_CARRIER is like CLOCAL except it hides + * the true carrier. + */ + if (com->prev_modem_status & MSR_DCD || mynor & CALLOUT_MASK) + (*linesw[tp->t_line].l_modem)(tp, 1); + } + /* + * Wait for DCD if necessary. + */ + if (!(tp->t_state & TS_CARR_ON) && !(mynor & CALLOUT_MASK) + && !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK)) { + ++com->wopeners; + error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "cydcd", 0); + --com->wopeners; + if (error != 0) + goto out; + goto open_top; + } + error = (*linesw[tp->t_line].l_open)(dev, tp); + disc_optim(tp, &tp->t_termios, com); + if (tp->t_state & TS_ISOPEN && mynor & CALLOUT_MASK) + com->active_out = TRUE; + siosettimeout(); +out: + splx(s); + if (!(tp->t_state & TS_ISOPEN) && com->wopeners == 0) + comhardclose(com); + return (error); +} int -cyclose(dev_t dev, int flag, int mode, struct proc *p) +sioclose(dev, flag, mode, p) + dev_t dev; + int flag; + int mode; + struct proc *p; { - u_int unit = UNIT(dev); - struct cy *infop = info[unit]; - struct tty *tp = infop->tty; - cy_addr base = infop->base_addr; + struct com_s *com; + int mynor; int s; + struct tty *tp; - (*linesw[(u_char)tp->t_line].l_close)(tp, flag); - + mynor = minor(dev); + if (mynor & CONTROL_MASK) + return (0); + com = com_addr(MINOR_TO_UNIT(mynor)); + tp = com->tp; s = spltty(); - /* select the appropriate channel on the CD1400 */ - *(base + CD1400_CAR) = (u_char)(unit & 0x03); - - /* disable this channel and lower DTR */ - infop->intr_enable = 0; - *(base + CD1400_SRER) = (u_char)0; /* no intrs */ - *(base + CD1400_DTR) = (u_char)CD1400_DTR_CLEAR; /* no DTR */ - infop->modem_sig &= ~CD1400_MSVR_DTR; - - /* disable receiver (leave transmitter enabled) */ - infop->channel_control = (1 << 4) | (1 << 3) | 1; - cd1400_channel_cmd(base, infop->channel_control); - splx(s); - + (*linesw[tp->t_line].l_close)(tp, flag); + disc_optim(tp, &tp->t_termios, com); + siostop(tp, FREAD | FWRITE); + comhardclose(com); ttyclose(tp); + siosettimeout(); + splx(s); #ifdef broken /* session holds a ref to the tty; can't deallocate */ ttyfree(tp); - infop->tty = cy_tty[unit] = (struct tty *)NULL; + com->tp = sio_tty[unit] = NULL; #endif + return (0); +} - if (infop->dtrwait) { - int error; - - timeout(cyclose_wakeup, (caddr_t)&infop->dtrwait, infop->dtrwait); - do { - error = tsleep((caddr_t)&infop->dtrwait, - TTIPRI|PCATCH, "cyclose", 0); - } while (error == ERESTART); - } +static void +comhardclose(com) + struct com_s *com; +{ + cy_addr iobase; + int s; + struct tty *tp; + int unit; - return 0; -} /* end of cyclose() */ + unit = com->unit; + iobase = com->iobase; + s = spltty(); +#if 0 + com->poll = FALSE; + com->poll_output = FALSE; +#endif + com->do_timestamp = 0; + cd_outb(iobase, CD1400_CAR, unit & CD1400_CAR_CHAN); +#if 0 + outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); +#endif +#ifdef KGDB + /* do not disable interrupts or hang up if debugging */ + if (kgdb_dev != makedev(commajor, unit)) +#endif + { +#if 0 + outb(iobase + com_ier, 0); +#else + disable_intr(); + cd_outb(iobase, CD1400_SRER, com->intr_enable = 0); + enable_intr(); +#endif + tp = com->tp; + if (tp->t_cflag & HUPCL + /* + * XXX we will miss any carrier drop between here and the + * next open. Perhaps we should watch DCD even when the + * port is closed; it is not sufficient to check it at + * the next open because it might go up and down while + * we're not watching. + */ + || !com->active_out + && !(com->prev_modem_status & MSR_DCD) + && !(com->it_in.c_cflag & CLOCAL) + || !(tp->t_state & TS_ISOPEN)) { + (void)commctl(com, TIOCM_DTR, DMBIC); + + /* Disable receiver (leave transmitter enabled). */ + com->channel_control = CD1400_CCR_CMDCHANCTL + | CD1400_CCR_XMTEN + | CD1400_CCR_RCVDIS; + cd1400_channel_cmd(iobase, com->channel_control); + + if (com->dtr_wait != 0) { + timeout(siodtrwakeup, com, com->dtr_wait); + com->state |= CS_DTR_OFF; + } + } + } + com->active_out = FALSE; + wakeup(&com->active_out); + wakeup(TSA_CARR_ON(tp)); /* restart any wopeners */ + if (!(com->state & CS_DTR_OFF) + && !(unit == comconsole && (COMCONSOLE || boothowto & RB_SERIAL))) + com->kdc.kdc_state = DC_IDLE; + splx(s); +} int -cyread(dev_t dev, struct uio *uio, int flag) +sioread(dev, uio, flag) + dev_t dev; + struct uio *uio; + int flag; { - u_int unit = UNIT(dev); - struct tty *tp = info[unit]->tty; - - return (*linesw[(u_char)tp->t_line].l_read)(tp, uio, flag); -} /* end of cyread() */ + int mynor; + struct tty *tp; + mynor = minor(dev); + if (mynor & CONTROL_MASK) + return (ENODEV); + tp = com_addr(MINOR_TO_UNIT(mynor))->tp; + return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); +} int -cywrite(dev_t dev, struct uio *uio, int flag) +siowrite(dev, uio, flag) + dev_t dev; + struct uio *uio; + int flag; { - u_int unit = UNIT(dev); - struct tty *tp = info[unit]->tty; + int mynor; + struct tty *tp; + int unit; + + mynor = minor(dev); + if (mynor & CONTROL_MASK) + return (ENODEV); + unit = MINOR_TO_UNIT(mynor); + tp = com_addr(unit)->tp; + /* + * (XXX) We disallow virtual consoles if the physical console is + * a serial port. This is in case there is a display attached that + * is not the console. In that situation we don't need/want the X + * server taking over the console. + */ + if (constty && unit == comconsole + && (COMCONSOLE || boothowto & RB_SERIAL)) + constty = NULL; #ifdef Smarts /* XXX duplicate ttwrite(), but without so much output processing on * CR & LF chars. Hardly worth the effort, given that high-throughput * sessions are raw anyhow. */ #else - return (*linesw[(u_char)tp->t_line].l_write)(tp, uio, flag); + return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); #endif -} /* end of cywrite() */ - - -#ifdef Smarts -/* standard line discipline input routine */ -int -cyinput(int c, struct tty *tp) -{ - /* XXX duplicate ttyinput(), but without the IXOFF/IXON/ISTRIP/IPARMRK - * bits, as they are done by the CD1400. Hardly worth the effort, - * given that high-throughput sessions are raw anyhow. - */ -} /* end of cyinput() */ -#endif /* Smarts */ - - -inline static void -service_upper_rx(int unit) -{ - struct cy *ip = info[unit]; - struct tty *tp = ip->tty; - struct cy_buf *buf; - int i; - u_char *ch; - - buf = ip->rx_buf; - - /* give service_rx() a new one */ - disable_intr(); /* faster than spltty() */ - ip->rx_buf = buf->next_buf; - enable_intr(); - - if (tp->t_state & TS_ISOPEN) { - ch = buf->buf; - i = buf->next_char - buf->buf; - -#ifdef FastRawInput - /* try to avoid calling the line discipline stuff if we can */ - if ((tp->t_line == 0) && - !(tp->t_iflag & (ICRNL | IMAXBEL | INLCR)) && - !(tp->t_lflag & (ECHO | ECHONL | ICANON | IEXTEN | - ISIG | PENDIN)) && - !(tp->t_state & (TS_CNTTB | TS_LNCH))) { - - i = b_to_q(ch, i, &tp->t_rawq); - if (i) { - /* - * we have no RTS flow control support on cy-8 - * boards, so this is really just tough luck - */ - - log(LOG_WARNING, "cy%d: tty input queue overflow\n", - unit); - } - - ttwakeup(tp); /* notify any readers */ - } - else -#endif /* FastRawInput */ - { - while (i--) - (*linesw[(u_char)tp->t_line].l_rint)((int)*ch++, tp); - } - } - - /* clear the buffer we've just processed */ - buf->next_char = buf->buf; - buf->free = CY_RX_BUF_SIZE; -} /* end of service_upper_rx() */ - +} -#ifdef TxBuffer static void -service_upper_tx(int unit) +siodtrwakeup(chan) + void *chan; { - struct cy *ip = info[unit]; - struct tty *tp = ip->tty; - - tp->t_state &=~ (TS_BUSY|TS_FLUSH); - - if (tp->t_outq.c_cc <= tp->t_lowat) { - if (tp->t_state&TS_ASLEEP) { - tp->t_state &= ~TS_ASLEEP; - wakeup((caddr_t)&tp->t_outq); - } - selwakeup(&tp->t_wsel); - } - - if (tp->t_outq.c_cc > 0) { - struct cy_ring *txq = &ip->tx_buf; - int free_count = CY_TX_BUF_SIZE - ip->tx_buf.used; - u_char *cp = txq->tail; - int count; - int chars_done; - - tp->t_state |= TS_BUSY; - - /* find the largest contig. copy we can do */ - count = ((txq->endish - cp) > free_count) ? - free_count : txq->endish - cp; - - count = ((cp + free_count) > txq->endish) ? - txq->endish - cp : free_count; - - /* copy the first slab */ - chars_done = q_to_b(&tp->t_outq, cp, count); - - /* check for wrap-around time */ - cp += chars_done; - if (cp == txq->endish) - cp = txq->buf; /* back to the start */ - - /* copy anything else, after we've wrapped around */ - if ((chars_done == count) && (count != free_count)) { - /* copy the second slab */ - count = q_to_b(&tp->t_outq, cp, free_count - count); - cp += count; - chars_done += count; - } - - /* - * update queue, protecting ourselves from any rampant - * lower-layers - */ - disable_intr(); - txq->tail = cp; - txq->used += chars_done; - enable_intr(); - } - - if (!tp->t_outq.c_cc) - tp->t_state &=~ TS_BUSY; -} /* end of service_upper_tx() */ -#endif /* TxBuffer */ + struct com_s *com; + com = (struct com_s *)chan; + com->state &= ~CS_DTR_OFF; + if (!(com->unit == comconsole && (COMCONSOLE || boothowto & RB_SERIAL))) + com->kdc.kdc_state = DC_IDLE; + wakeup(&com->dtr_wait); +} -inline static void -service_upper_mdm(int unit) +/* Interrupt routine for timekeeping purposes */ +void +siointrts(unit) + int unit; { - struct cy *ip = info[unit]; - - if (ip->carrier_delta) { - int carrier = ip->modem_sig & CD1400_MSVR_CD; - struct tty *tp = ip->tty; - - if (!(*linesw[(u_char)tp->t_line].l_modem)(tp, carrier)) { - cy_addr base = ip->base_addr; - - /* clear DTR */ - disable_intr(); - *(base + CD1400_CAR) = (u_char)(unit & 0x03); - *(base + CD1400_DTR) = (u_char)CD1400_DTR_CLEAR; - ip->modem_sig &= ~CD1400_MSVR_DTR; - ip->carrier_delta = 0; - enable_intr(); - } - else { - disable_intr(); - ip->carrier_delta = 0; - enable_intr(); - } - } -} /* end of service_upper_mdm() */ + /* + * XXX microtime() reenables CPU interrupts. We can't afford to + * be interrupted and don't want to slow down microtime(), so lock + * out interrupts in another way. + */ + outb(IO_ICU1 + 1, 0xff); + microtime(&intr_timestamp); + disable_intr(); + outb(IO_ICU1 + 1, imen); + siointr(unit); +} -/* upper level character processing routine */ void -cytimeout(void *ptr) -{ +siointr(unit) int unit; - - timeout_scheduled = 0; - +{ + int baseu; + cy_addr cy_iobase; + int cyu; + cy_addr iobase; + u_char status; + + baseu = unit * CY_MAX_PORTS; + cy_iobase = com_addr(baseu)->cy_iobase; + + /* check each CD1400 in turn */ + for (cyu = 0, iobase = cy_iobase; cyu < cy_nr_cd1400s[unit]; + ++cyu, iobase += CY_CD1400_MEMSIZE) { + /* poll to see if it has any work */ + status = cd_inb(iobase, CD1400_SVRR); + if (status == 0) + continue; #ifdef CyDebug - cy_timeouts++; + ++cy_svrr_probes; #endif - - /* check each port in turn */ - for (unit = 0; unit < NCY*PORTS_PER_CYCLOM; unit++) { - struct cy *ip = info[unit]; -#ifndef TxBuffer - struct tty *tp = ip->tty; + /* service requests as appropriate, giving priority to RX */ + if (status & CD1400_SVRR_RXRDY) { + struct com_s *com; + u_int count; + u_char *ioptr; + u_char line_status; + u_char recv_data; + u_char serv_type; +#ifdef PollMode + u_char save_car; + u_char save_rir; #endif - /* ignore anything that is not open */ - if (!ip->tty) - continue; +#ifdef PollMode + save_rir = cd_inb(iobase, CD1400_RIR); + save_car = cd_inb(iobase, CD1400_CAR); - /* - * any received chars to handle? (doesn't matter if intr routine - * kicks in while we're testing this) - */ - if (ip->rx_buf->free != CY_RX_BUF_SIZE) - service_upper_rx(unit); + /* enter rx service */ + cd_outb(iobase, CD1400_CAR, save_rir); -#ifdef TxBuffer - /* anything to add to the transmit buffer (low-water mark)? */ - if (ip->tx_buf.used < CY_TX_BUF_SIZE/2) - service_upper_tx(unit); + serv_type = cd_inb(iobase, CD1400_RIVR); + com = com_addr(baseu + + ((serv_type >> CD1400_xIVR_CHAN_SHIFT) + & CD1400_xIVR_CHAN)); #else - if (tp->t_outq.c_cc <= tp->t_lowat) { - if (tp->t_state&TS_ASLEEP) { - tp->t_state &= ~TS_ASLEEP; - wakeup((caddr_t)&tp->t_outq); - } - selwakeup(&tp->t_wsel); - } -#endif - - /* anything modem signals altered? */ - service_upper_mdm(unit); - - /* any overruns to log? */ -#ifdef LogOverruns - if (ip->fifo_overrun) { - /* - * turn off the alarm - not important enough to bother - * with interrupt protection. - */ - ip->fifo_overrun = 0; + /* ack receive service */ + serv_type = cy_inb(iobase, CY8_SVCACKR); - log(LOG_WARNING, "cy%d: receive fifo overrun\n", unit); - } + com = com_addr(baseu + + + ((serv_type >> CD1400_xIVR_CHAN_SHIFT) + & CD1400_xIVR_CHAN)); #endif - if (ip->rx_buf_overrun) { - /* - * turn off the alarm - not important enough to bother - * with interrupt protection. - */ - ip->rx_buf_overrun = 0; - - log(LOG_WARNING, "cy%d: receive buffer full\n", unit); - } - } -} /* cytimeout() */ - -inline static void -schedule_upper_service(void) -{ -#ifdef CyDebug - cy_timeout_req++; -#endif - - if (!timeout_scheduled) { - timeout(cytimeout, (caddr_t)0, 1); /* call next tick */ - timeout_scheduled = 1; - } -} /* end of schedule_upper_service() */ - - -/* initialise a channel on the cyclom board */ - -static void -cy_channel_init(dev_t dev, int reset) -{ - u_int unit = UNIT(dev); - int carrier_mode = CARRIER_MODE(dev); - struct cy *ip = info[unit]; - cy_addr base = ip->base_addr; - struct tty *tp = ip->tty; - struct cy_buf *buf, *next_buf; - int i; -#ifndef PollMode - u_char cd1400_unit; -#endif - - /* clear the structure and refill it */ - bzero(ip, sizeof(struct cy)); - ip->base_addr = base; - ip->tty = tp; - ip->carrier_mode = carrier_mode; - - /* select channel of the CD1400 */ - *(base + CD1400_CAR) = (u_char)(unit & 0x03); - - if (reset) - cd1400_channel_cmd(base, 0x80); /* reset the channel */ - - /* set LIVR to 0 - intr routines depend on this */ - *(base + CD1400_LIVR) = 0; - -#ifndef PollMode - /* set top four bits of {R,T,M}ICR to the cd1400 - * number, cd1400_unit - */ - cd1400_unit = unit / CD1400_NO_OF_CHANNELS; - *(base + CD1400_RICR) = (u_char)(cd1400_unit << 4); - *(base + CD1400_TICR) = (u_char)(cd1400_unit << 4); - *(base + CD1400_MICR) = (u_char)(cd1400_unit << 4); + if (com->do_timestamp) + /* XXX a little bloat here... */ + com->timestamp = intr_timestamp; + + if (serv_type & CD1400_RIVR_EXCEPTION) { + ++com->recv_exception; + line_status = cd_inb(iobase, CD1400_RDSR); + /* break/unnattached error bits or real input? */ + recv_data = cd_inb(iobase, CD1400_RDSR); +#ifndef SOFT_HOTCHAR + if (line_status & CD1400_RDSR_SPECIAL + && com->hotchar != 0) + setsofttty(); #endif - - ip->dtrwait = hz/4; /* quarter of a second */ - - /* setup low-level buffers */ - i = CY_RX_BUFS; - ip->rx_buf = next_buf = &ip->rx_buf_pool[0]; - while (i--) { - buf = &ip->rx_buf_pool[i]; - - buf->next_char = buf->buf; /* first char to use */ - buf->free = CY_RX_BUF_SIZE; /* i.e. empty */ - buf->next_buf = next_buf; /* where to go next */ - next_buf = buf; - } - -#ifdef TxBuffer - ip->tx_buf.endish = ip->tx_buf.buf + CY_TX_BUF_SIZE; - - /* clear the low-level tx buffer */ - ip->tx_buf.head = ip->tx_buf.tail = ip->tx_buf.buf; - ip->tx_buf.used = 0; -#endif - - /* clear the low-level rx buffer */ - ip->rx_buf->next_char = ip->rx_buf->buf; /* first char to use */ - ip->rx_buf->free = CY_RX_BUF_SIZE; /* completely empty */ -} /* end of cy_channel_init() */ - - -/* service a receive interrupt */ -inline static void -service_rx(int cd, caddr_t base) -{ - struct cy *infop; - unsigned count; - int ch; - u_char serv_type, channel; -#ifdef PollMode - u_char save_rir, save_car; +#if 1 /* XXX "intelligent" PFO error handling would break O error handling */ + if (line_status & (LSR_PE|LSR_FE|LSR_BI)) { + /* + Don't store PE if IGNPAR and BI if IGNBRK, + this hack allows "raw" tty optimization + works even if IGN* is set. + */ + if ( com->tp == NULL + || !(com->tp->t_state & TS_ISOPEN) + || (line_status & (LSR_PE|LSR_FE)) + && (com->tp->t_iflag & IGNPAR) + || (line_status & LSR_BI) + && (com->tp->t_iflag & IGNBRK)) + goto cont; + if ( (line_status & (LSR_PE|LSR_FE)) + && (com->tp->t_state & TS_CAN_BYPASS_L_RINT) + && ((line_status & LSR_FE) + || (line_status & LSR_PE) + && (com->tp->t_iflag & INPCK))) + recv_data = 0; + } +#endif /* 1 */ + ++com->bytes_in; +#ifdef SOFT_HOTCHAR + if (com->hotchar != 0 && recv_data == com->hotchar) + setsofttty(); #endif - - /* setup */ -#ifdef PollMode - save_rir = *(base + CD1400_RIR); - channel = cd * CD1400_NO_OF_CHANNELS + (save_rir & 0x3); - save_car = *(base + CD1400_CAR); - *(base + CD1400_CAR) = save_rir; /* enter modem service */ - serv_type = *(base + CD1400_RIVR); + ioptr = com->iptr; + if (ioptr >= com->ibufend) + CE_RECORD(com, CE_INTERRUPT_BUF_OVERFLOW); + else { + ++com_events; + ioptr[0] = recv_data; + ioptr[CE_INPUT_OFFSET] = line_status; + com->iptr = ++ioptr; + if (ioptr == com->ihighwater + && com->state & CS_RTS_IFLOW) +#if 0 + outb(com->modem_ctl_port, + com->mcr_image &= ~MCR_RTS); #else - serv_type = *(base + CD1400_SVCACKR); /* ack receive service */ - channel = ((u_char)*(base + CD1400_RICR)) >> 2; /* get cyclom channel # */ - -#ifdef CyDebug - if (channel >= PORTS_PER_CYCLOM) { - printf("cy: service_rx - channel %02x\n", channel); - panic("cy: service_rx - bad channel"); - } + cd_outb(iobase, CD1400_MSVR1, + com->mcr_image &= ~MCR_RTS); #endif + if (line_status & LSR_OE) + CE_RECORD(com, CE_OVERRUN); + } + goto cont; + } else { + int ifree; + + count = cd_inb(iobase, CD1400_RDCR); + com->bytes_in += count; + ioptr = com->iptr; + ifree = com->ibufend - ioptr; + if (count > ifree) { + count -= ifree; + com_events += ifree; + while (ifree-- != 0) { + recv_data = cd_inb(iobase, CD1400_RDSR); +#ifdef SOFT_HOTCHAR + if (com->hotchar != 0 + && recv_data == com->hotchar) + setsofttty(); #endif - - infop = info[channel]; - - /* read those chars */ - if (serv_type & CD1400_RIVR_EXCEPTION) { - /* read the exception status */ - u_char status = *(base + CD1400_RDSR); - - /* XXX is it a break? Do something if it is! */ - - /* XXX is IGNPAR not set? Store a null in the buffer. */ - -#ifdef LogOverruns - if (status & CD1400_RDSR_OVERRUN) { + ioptr[0] = recv_data; + ioptr[CE_INPUT_OFFSET] = 0; + ++ioptr; + } + com->delta_error_counts + [CE_INTERRUPT_BUF_OVERFLOW] += count; + do { + recv_data = cd_inb(iobase, CD1400_RDSR); +#ifdef SOFT_HOTCHAR + if (com->hotchar != 0 + && recv_data == com->hotchar) + setsofttty(); +#endif + } while (--count != 0); + } else { + if (ioptr <= com->ihighwater + && ioptr + count > com->ihighwater + && com->state & CS_RTS_IFLOW) #if 0 - ch |= TTY_PE; /* for SLIP */ + outb(com->modem_ctl_port, + com->mcr_image &= ~MCR_RTS); +#else + cd_outb(iobase, CD1400_MSVR1, + com->mcr_image &= ~MCR_RTS); #endif - infop->fifo_overrun++; - } + com_events += count; + do { + recv_data = cd_inb(iobase, CD1400_RDSR); +#ifdef SOFT_HOTCHAR + if (com->hotchar != 0 + && recv_data == com->hotchar) + setsofttty(); #endif - infop->recv_exception++; - } - else { - struct cy_buf *buf = infop->rx_buf; - - count = (u_char)*(base + CD1400_RDCR); /* how many to read? */ - infop->recv_normal += count; - if (buf->free < count) { - infop->rx_buf_overrun += count; - - /* read & discard everything */ - while (count--) - ch = (u_char)*(base + CD1400_RDSR); - } - else { - /* slurp it into our low-level buffer */ - buf->free -= count; - while (count--) { - ch = (u_char)*(base + CD1400_RDSR); /* read the char */ - *(buf->next_char++) = ch; + ioptr[0] = recv_data; + ioptr[CE_INPUT_OFFSET] = 0; + ++ioptr; + } while (--count != 0); } + com->iptr = ioptr; } - } +cont: + /* terminate service context */ #ifdef PollMode - *(base + CD1400_RIR) = (u_char)(save_rir & 0x3f); /* terminate service context */ + cd_outb(iobase, CD1400_RIR, + save_rir + & ~(CD1400_RIR_RDIREQ | CD1400_RIR_RBUSY)); + cd_outb(iobase, CD1400_CAR, save_car); #else - *(base + CD1400_EOSRR) = (u_char)0; /* terminate service context */ + cd_outb(iobase, CD1400_EOSRR, 0); #endif -} /* end of service_rx */ - - -/* service a transmit interrupt */ -inline static void -service_tx(int cd, caddr_t base) -{ - struct cy *ip; -#ifdef TxBuffer - struct cy_ring *txq; -#else - struct tty *tp; -#endif - u_char channel; + } + if (status & CD1400_SVRR_MDMCH) { + struct com_s *com; + u_char modem_status; #ifdef PollMode - u_char save_tir, save_car; + u_char save_car; + u_char save_mir; #else - u_char vector; + u_char vector; #endif - /* setup */ #ifdef PollMode - save_tir = *(base + CD1400_TIR); - channel = cd * CD1400_NO_OF_CHANNELS + (save_tir & 0x3); - save_car = *(base + CD1400_CAR); - *(base + CD1400_CAR) = save_tir; /* enter tx service */ -#else - vector = *(base + CD1400_SVCACKT); /* ack transmit service */ - channel = ((u_char)*(base + CD1400_TICR)) >> 2; /* get cyclom channel # */ - -#ifdef CyDebug - if (channel >= PORTS_PER_CYCLOM) { - printf("cy: service_tx - channel %02x\n", channel); - panic("cy: service_tx - bad channel"); - } -#endif -#endif - - ip = info[channel]; -#ifdef TxBuffer - txq = &ip->tx_buf; + save_mir = cd_inb(iobase, CD1400_MIR); + save_car = cd_inb(iobase, CD1400_CAR); - if (txq->used > 0) { - cy_addr base = ip->base_addr; - int count = min(CD1400_FIFOSIZE, txq->used); - int chars_done = count; - u_char *cp = txq->head; - u_char *buf_end = txq->endish; + /* enter modem service */ + cd_outb(iobase, CD1400_CAR, save_mir); - /* ip->state |= CY_BUSY; */ - while (count--) { - *(base + CD1400_TDR) = *cp++; - if (cp >= buf_end) - cp = txq->buf; - }; - txq->head = cp; - txq->used -= chars_done; /* important that this is atomic */ - ip->xmit += chars_done; - } - - /* - * disable tx intrs if no more chars to send. we re-enable - * them in cystart() - */ - if (!txq->used) { - ip->intr_enable &=~ (1 << 2); - *(base + CD1400_SRER) = ip->intr_enable; - /* ip->state &= ~CY_BUSY; */ - } + com = com_addr(baseu + cyu * CD1400_NO_OF_CHANNELS + + (save_mir & CD1400_MIR_CHAN)); #else - tp = ip->tty; - - if (!(tp->t_state & TS_TTSTOP) && (tp->t_outq.c_cc > 0)) { - cy_addr base = ip->base_addr; - int count = min(CD1400_FIFOSIZE, tp->t_outq.c_cc); + /* ack modem service */ + vector = cy_inb(iobase, CY8_SVCACKM); - ip->xmit += count; - tp->t_state |= TS_BUSY; - while (count--) - *(base + CD1400_TDR) = getc(&tp->t_outq); - } + com = com_addr(baseu + + ((vector >> CD1400_xIVR_CHAN_SHIFT) + & CD1400_xIVR_CHAN)); +#endif + ++com->mdm; + modem_status = cd_inb(iobase, CD1400_MSVR2); + if (modem_status != com->last_modem_status) { + /* + * Schedule high level to handle DCD changes. Note + * that we don't use the delta bits anywhere. Some + * UARTs mess them up, and it's easy to remember the + * previous bits and calculate the delta. + */ + com->last_modem_status = modem_status; + if (!(com->state & CS_CHECKMSR)) { + com_events += LOTS_OF_EVENTS; + com->state |= CS_CHECKMSR; + setsofttty(); + } - /* - * disable tx intrs if no more chars to send. we re-enable them - * in cystart() - */ - if (!tp->t_outq.c_cc) { - ip->intr_enable &=~ (1 << 2); - *(base + CD1400_SRER) = ip->intr_enable; - tp->t_state &= ~TS_BUSY; - } +#ifdef SOFT_CTS_OFLOW + /* handle CTS change immediately for crisp flow ctl */ + if (com->state & CS_CTS_OFLOW) { + if (modem_status & MSR_CTS) { + com->state |= CS_ODEVREADY; + if (com->state >= (CS_BUSY | CS_TTGO + | CS_ODEVREADY) + && !(com->intr_enable + & CD1400_SRER_TXRDY)) + cd_outb(iobase, CD1400_SRER, + com->intr_enable + |= CD1400_SRER_TXRDY); + } else { + com->state &= ~CS_ODEVREADY; + if (com->intr_enable & CD1400_SRER_TXRDY) + cd_outb(iobase, CD1400_SRER, + com->intr_enable + &= ~CD1400_SRER_TXRDY); + } + } #endif + } + /* terminate service context */ #ifdef PollMode - *(base + CD1400_TIR) = (u_char)(save_tir & 0x3f); /* terminate service context */ + cd_outb(iobase, CD1400_MIR, + save_mir + & ~(CD1400_MIR_RDIREQ | CD1400_MIR_RBUSY)); + cd_outb(iobase, CD1400_CAR, save_car); #else - *(base + CD1400_EOSRR) = (u_char)0; /* terminate service context */ + cd_outb(iobase, CD1400_EOSRR, 0); #endif -} /* end of service_tx */ - - -/* service a modem status interrupt */ -inline static void -service_mdm(int cd, caddr_t base) -{ - struct cy *infop; - u_char channel, deltas; + } + if (status & CD1400_SVRR_TXRDY) { + struct com_s *com; #ifdef PollMode - u_char save_mir, save_car; + u_char save_car; + u_char save_tir; #else - u_char vector; + u_char vector; #endif - /* setup */ #ifdef PollMode - save_mir = *(base + CD1400_MIR); - channel = cd * CD1400_NO_OF_CHANNELS + (save_mir & 0x3); - save_car = *(base + CD1400_CAR); - *(base + CD1400_CAR) = save_mir; /* enter modem service */ + save_tir = cd_inb(iobase, CD1400_TIR); + save_car = cd_inb(iobase, CD1400_CAR); + + /* enter tx service */ + cd_outb(iobase, CD1400_CAR, save_tir); + com = com_addr(baseu + + cyu * CD1400_NO_OF_CHANNELS + + (save_tir & CD1400_TIR_CHAN)); #else - vector = *(base + CD1400_SVCACKM); /* ack modem service */ - channel = ((u_char)*(base + CD1400_MICR)) >> 2; /* get cyclom channel # */ + /* ack transmit service */ + vector = cy_inb(iobase, CY8_SVCACKT); -#ifdef CyDebug - if (channel >= PORTS_PER_CYCLOM) { - printf("cy: service_mdm - channel %02x\n", channel); - panic("cy: service_mdm - bad channel"); - } + com = com_addr(baseu + + ((vector >> CD1400_xIVR_CHAN_SHIFT) + & CD1400_xIVR_CHAN)); #endif -#endif - - infop = info[channel]; - - /* read the siggies and see what's changed */ - infop->modem_sig = (u_char)*(base + CD1400_MSVR); - deltas = (u_char)*(base + CD1400_MISR); - if ((infop->carrier_mode <= 2) && (deltas & CD1400_MISR_CDd)) - /* something for the upper layer to deal with */ - infop->carrier_delta = 1; - - infop->mdm++; + if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) { + u_char *ioptr; + u_int ocount; + + ioptr = com->obufq.l_head; + ocount = com->obufq.l_tail - ioptr; + if (ocount > CD1400_TX_FIFO_SIZE) + ocount = CD1400_TX_FIFO_SIZE; + com->bytes_out += ocount; + do + cd_outb(iobase, CD1400_TDR, *ioptr++); + while (--ocount != 0); + com->obufq.l_head = ioptr; + if (ioptr >= com->obufq.l_tail) { + struct lbq *qp; + + qp = com->obufq.l_next; + qp->l_queued = FALSE; + qp = qp->l_next; + if (qp != NULL) { + com->obufq.l_head = qp->l_head; + com->obufq.l_tail = qp->l_tail; + com->obufq.l_next = qp; + } else { + /* output just completed */ + com->state &= ~CS_BUSY; + cd_outb(iobase, CD1400_SRER, + com->intr_enable + &= ~CD1400_SRER_TXRDY); + } + if (!(com->state & CS_ODONE)) { + com_events += LOTS_OF_EVENTS; + com->state |= CS_ODONE; + setsofttty(); /* handle at high level ASAP */ + } + } + } - /* terminate service context */ + /* terminate service context */ #ifdef PollMode - *(base + CD1400_MIR) = (u_char)(save_mir & 0x3f); + cd_outb(iobase, CD1400_TIR, + save_tir + & ~(CD1400_TIR_RDIREQ | CD1400_TIR_RBUSY)); + cd_outb(iobase, CD1400_CAR, save_car); #else - *(base + CD1400_EOSRR) = (u_char)0; + cd_outb(iobase, CD1400_EOSRR, 0); #endif -} /* end of service_mdm */ - - -int -cyintr(int unit) -{ - int cd; - u_char status; - - /* check each CD1400 in turn */ - for (cd = 0; cd < CD1400s_PER_CYCLOM; cd++) { - cy_addr base = cyclom_base + cd*CD1400_MEMSIZE; - - /* poll to see if it has any work */ - while (status = (u_char)*(base + CD1400_SVRR)) { -#ifdef CyDebug - cy_svrr_probes++; -#endif - /* service requests as appropriate, giving priority to RX */ - if (status & CD1400_SVRR_RX) - service_rx(cd, base); - if (status & CD1400_SVRR_TX) - service_tx(cd, base); - if (status & CD1400_SVRR_MDM) - service_mdm(cd, base); + } } - } - /* request upper level service to deal with whatever happened */ - schedule_upper_service(); + /* ensure an edge for the next interrupt */ + cy_outb(cy_iobase, CY_CLEAR_INTR, 0); - /* re-enable interrupts on the cyclom */ - *(cyclom_base + CYCLOM_CLEAR_INTR) = (u_char)0; - - return 1; + schedsofttty(); } +static void +siointr1(com) + struct com_s *com; +{ +} int -cyioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p) +sioioctl(dev, cmd, data, flag, p) + dev_t dev; + int cmd; + caddr_t data; + int flag; + struct proc *p; { - int unit = UNIT(dev); - struct cy *infop = info[unit]; - struct tty *tp = infop->tty; + struct com_s *com; int error; + cy_addr iobase; + int mynor; + int s; + struct tty *tp; +#if defined(COMPAT_43) || defined(COMPAT_SUNOS) + int oldcmd; + struct termios term; +#endif - error = (*linesw[(u_char)tp->t_line].l_ioctl)(tp, cmd, data, flag, p); - if (error >= 0) + mynor = minor(dev); + com = com_addr(MINOR_TO_UNIT(mynor)); + iobase = com->iobase; + if (mynor & CONTROL_MASK) { + struct termios *ct; + + switch (mynor & CONTROL_MASK) { + case CONTROL_INIT_STATE: + ct = mynor & CALLOUT_MASK ? &com->it_out : &com->it_in; + break; + case CONTROL_LOCK_STATE: + ct = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in; + break; + default: + return (ENODEV); /* /dev/nodev */ + } + switch (cmd) { + case TIOCSETA: + error = suser(p->p_ucred, &p->p_acflag); + if (error != 0) + return (error); + *ct = *(struct termios *)data; + return (0); + case TIOCGETA: + *(struct termios *)data = *ct; + return (0); + case TIOCGETD: + *(int *)data = TTYDISC; + return (0); + case TIOCGWINSZ: + bzero(data, sizeof(struct winsize)); + return (0); + default: + return (ENOTTY); + } + } + tp = com->tp; +#if defined(COMPAT_43) || defined(COMPAT_SUNOS) + term = tp->t_termios; + oldcmd = cmd; + error = ttsetcompat(tp, &cmd, data, &term); + if (error != 0) return (error); - error = ttioctl(tp, cmd, data, flag -#ifdef NetBSD - , p + if (cmd != oldcmd) + data = (caddr_t)&term; #endif - ); + if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { + int cc; + struct termios *dt = (struct termios *)data; + struct termios *lt = mynor & CALLOUT_MASK + ? &com->lt_out : &com->lt_in; + + dt->c_iflag = (tp->t_iflag & lt->c_iflag) + | (dt->c_iflag & ~lt->c_iflag); + dt->c_oflag = (tp->t_oflag & lt->c_oflag) + | (dt->c_oflag & ~lt->c_oflag); + dt->c_cflag = (tp->t_cflag & lt->c_cflag) + | (dt->c_cflag & ~lt->c_cflag); + dt->c_lflag = (tp->t_lflag & lt->c_lflag) + | (dt->c_lflag & ~lt->c_lflag); + for (cc = 0; cc < NCCS; ++cc) + if (lt->c_cc[cc] != 0) + dt->c_cc[cc] = tp->t_cc[cc]; + if (lt->c_ispeed != 0) + dt->c_ispeed = tp->t_ispeed; + if (lt->c_ospeed != 0) + dt->c_ospeed = tp->t_ospeed; + } + error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); if (error >= 0) return (error); - + s = spltty(); + error = ttioctl(tp, cmd, data, flag); + disc_optim(tp, &tp->t_termios, com); + if (error >= 0) { + splx(s); + return (error); + } + cd_outb(iobase, CD1400_CAR, MINOR_TO_UNIT(mynor) & CD1400_CAR_CHAN); switch (cmd) { -#ifdef notyet /* sigh - more junk to do XXX */ +#if 0 case TIOCSBRK: + outb(iobase + com_cfcr, com->cfcr_image |= CFCR_SBREAK); break; case TIOCCBRK: + outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); break; +#endif /* 0 */ case TIOCSDTR: + (void)commctl(com, TIOCM_DTR, DMBIS); break; case TIOCCDTR: + (void)commctl(com, TIOCM_DTR, DMBIC); break; - case TIOCMSET: + (void)commctl(com, *(int *)data, DMSET); break; case TIOCMBIS: + (void)commctl(com, *(int *)data, DMBIS); break; case TIOCMBIC: + (void)commctl(com, *(int *)data, DMBIC); break; -#endif /* notyet */ - - case TIOCMGET: { - int bits = 0; - u_char status = infop->modem_sig; - - if (status & CD1400_MSVR_DTR) bits |= TIOCM_DTR | TIOCM_RTS; - if (status & CD1400_MSVR_CD) bits |= TIOCM_CD; - if (status & CD1400_MSVR_CTS) bits |= TIOCM_CTS; - if (status & CD1400_MSVR_DSR) bits |= TIOCM_DSR; -#ifdef CYCLOM_16 - if (status & CD1400_MSVR_RI) bits |= TIOCM_RI; -#endif - if (infop->channel_control & 0x02) bits |= TIOCM_LE; - *(int *)data = bits; + case TIOCMGET: + *(int *)data = commctl(com, 0, DMGET); break; - } - -#ifdef TIOCMSBIDIR - case TIOCMSBIDIR: - return (ENOTTY); -#endif /* TIOCMSBIDIR */ - -#ifdef TIOCMGBIDIR - case TIOCMGBIDIR: - return (ENOTTY); -#endif /* TIOCMGBIDIR */ - -#ifdef TIOCMSDTRWAIT case TIOCMSDTRWAIT: - /* must be root to set dtr delay */ - if (p->p_ucred->cr_uid != 0) - return(EPERM); - - infop->dtrwait = *(u_int *)data; - break; -#endif /* TIOCMSDTRWAIT */ - -#ifdef TIOCMGDTRWAIT + /* must be root since the wait applies to following logins */ + error = suser(p->p_ucred, &p->p_acflag); + if (error != 0) { + splx(s); + return (error); + } + com->dtr_wait = *(int *)data * hz / 100; + break; case TIOCMGDTRWAIT: - *(u_int *)data = infop->dtrwait; + *(int *)data = com->dtr_wait * 100 / hz; + break; + case TIOCTIMESTAMP: + com->do_timestamp = TRUE; + *(struct timeval *)data = com->timestamp; break; -#endif /* TIOCMGDTRWAIT */ - default: + splx(s); return (ENOTTY); } + splx(s); + return (0); +} - return 0; -} /* end of cyioctl() */ - - -int -cyparam(struct tty *tp, struct termios *t) +void +siopoll() { - u_char unit = UNIT(tp->t_dev); - struct cy *infop = info[unit]; - cy_addr base = infop->base_addr; - int cflag = t->c_cflag; - int iflag = t->c_iflag; - int ispeed, ospeed; - int itimeout; - int iprescaler, oprescaler; - int s; - u_char cor_change = 0; - u_char opt; - - if (!t->c_ispeed) - t->c_ispeed = t->c_ospeed; - - s = spltty(); - - /* select the appropriate channel on the CD1400 */ - *(base + CD1400_CAR) = unit & 0x03; - - /* handle DTR drop on speed == 0 trick */ - if (t->c_ospeed == 0) { - *(base + CD1400_DTR) = CD1400_DTR_CLEAR; - infop->modem_sig &= ~CD1400_MSVR_DTR; - } - else { - *(base + CD1400_DTR) = CD1400_DTR_SET; - infop->modem_sig |= CD1400_MSVR_DTR; - } + int unit; - /* set baud rates if they've changed from last time */ - - if ((ospeed = cyspeed(t->c_ospeed, &oprescaler)) < 0) - return EINVAL; - *(base + CD1400_TBPR) = (u_char)ospeed; - *(base + CD1400_TCOR) = (u_char)oprescaler; - - if ((ispeed = cyspeed(t->c_ispeed, &iprescaler)) < 0) - return EINVAL; - *(base + CD1400_RBPR) = (u_char)ispeed; - *(base + CD1400_RCOR) = (u_char)iprescaler; +#ifdef CyDebug + ++cy_timeouts; +#endif + if (com_events == 0) + return; +repeat: + for (unit = 0; unit < NSIO; ++unit) { + u_char *buf; + struct com_s *com; + u_char *ibuf; + cy_addr iobase; + int incc; + struct tty *tp; + + com = com_addr(unit); + if (com == NULL) + continue; + tp = com->tp; + if (tp == NULL) { + /* + * XXX forget any events related to closed devices + * (actually never opened devices) so that we don't + * loop. + */ + disable_intr(); + incc = com->iptr - com->ibuf; + com->iptr = com->ibuf; + if (com->state & CS_CHECKMSR) { + incc += LOTS_OF_EVENTS; + com->state &= ~CS_CHECKMSR; + } + com_events -= incc; + enable_intr(); + if (incc != 0) + log(LOG_DEBUG, + "sio%d: %d events for device with no tp\n", + unit, incc); + continue; + } - /* - * set receive time-out period - * generate a rx interrupt if no new chars are received in - * this many ticks - * don't bother comparing old & new VMIN, VTIME and ispeed - it - * can't be much worse just to calculate and set it each time! - * certainly less hassle. :-) - */ + /* switch the role of the low-level input buffers */ + if (com->iptr == (ibuf = com->ibuf)) { + buf = NULL; /* not used, but compiler can't tell */ + incc = 0; + } else { + buf = ibuf; + disable_intr(); + incc = com->iptr - buf; + com_events -= incc; + if (ibuf == com->ibuf1) + ibuf = com->ibuf2; + else + ibuf = com->ibuf1; + com->ibufend = ibuf + RS_IBUFSIZE; + com->ihighwater = ibuf + RS_IHIGHWATER; + com->iptr = ibuf; - /* - * calculate minimum timeout period: - * 5 ms or the time it takes to receive 1 char, rounded up to the - * next ms, whichever is greater - */ - if (t->c_ispeed > 0) { - itimeout = (t->c_ispeed > 2200) ? 5 : (10000/t->c_ispeed + 1); + /* + * There is now room for another low-level buffer full + * of input, so enable RTS if it is now disabled and + * there is room in the high-level buffer. + */ + /* + * XXX this used not to look at CS_RTS_IFLOW. The + * change is to allow full control of MCR_RTS via + * ioctls after turning CS_RTS_IFLOW off. Check + * for races. We shouldn't allow the ioctls while + * CS_RTS_IFLOW is on. + */ + if ((com->state & CS_RTS_IFLOW) + && !(com->mcr_image & MCR_RTS) + && !(tp->t_state & TS_TBLOCK)) +#if 0 + outb(com->modem_ctl_port, + com->mcr_image |= MCR_RTS); +#else + iobase = com->iobase, + cd_outb(iobase, CD1400_CAR, + unit & CD1400_CAR_CHAN), + cd_outb(iobase, CD1400_MSVR1, + com->mcr_image |= MCR_RTS); +#endif + enable_intr(); + com->ibuf = ibuf; + } - /* if we're using VTIME as an inter-char timeout, and it is set to - * be longer than the minimum calculated above, go for it - */ - if (t->c_cc[VMIN] && t->c_cc[VTIME] && t->c_cc[VTIME]*10 > itimeout) - itimeout = t->c_cc[VTIME]*10; + if (com->state & CS_CHECKMSR) { + u_char delta_modem_status; - /* store it, taking care not to overflow the byte-sized register */ - *(base + CD1400_RTPR) = (u_char)((itimeout <= 255) ? itimeout : 255); + disable_intr(); + delta_modem_status = com->last_modem_status + ^ com->prev_modem_status; + com->prev_modem_status = com->last_modem_status; + com_events -= LOTS_OF_EVENTS; + com->state &= ~CS_CHECKMSR; + enable_intr(); + if (delta_modem_status & MSR_DCD) + (*linesw[tp->t_line].l_modem) + (tp, com->prev_modem_status & MSR_DCD); + } + if (com->state & CS_ODONE) { + disable_intr(); + com_events -= LOTS_OF_EVENTS; + com->state &= ~CS_ODONE; + if (!(com->state & CS_BUSY)) + com->tp->t_state &= ~TS_BUSY; + enable_intr(); + (*linesw[tp->t_line].l_start)(tp); + } + if (incc <= 0 || !(tp->t_state & TS_ISOPEN)) + continue; + /* + * XXX only do this when we bypass ttyinput. + */ + if (tp->t_rawq.c_cc + incc >= RB_I_HIGH_WATER + && (com->state & CS_RTS_IFLOW || tp->t_iflag & IXOFF) + && !(tp->t_state & TS_TBLOCK) + /* + * XXX - need flow control for all line disciplines. + * Only have it in standard one now. + */ + && linesw[tp->t_line].l_rint == ttyinput) { + int putc_status = 0; + + if ((tp->t_iflag & IXOFF + && tp->t_cc[VSTOP] != _POSIX_VDISABLE + && (putc_status = putc(tp->t_cc[VSTOP], + &tp->t_outq)) == 0) + || com->state & CS_RTS_IFLOW) { + tp->t_state |= TS_TBLOCK; + ttstart(tp); + if (putc_status != 0) + /* Try again later. */ + tp->t_state &= ~TS_TBLOCK; + } + } + /* + * Avoid the grotesquely inefficient lineswitch routine + * (ttyinput) in "raw" mode. It usually takes about 450 + * instructions (that's without canonical processing or echo!). + * slinput is reasonably fast (usually 40 instructions plus + * call overhead). + */ + if (tp->t_state & TS_CAN_BYPASS_L_RINT) { + tk_nin += incc; + tk_rawcc += incc; + tp->t_rawcc += incc; + com->delta_error_counts[CE_TTY_BUF_OVERFLOW] + += b_to_q((char *)buf, incc, &tp->t_rawq); + ttwakeup(tp); + if (tp->t_state & TS_TTSTOP + && (tp->t_iflag & IXANY + || tp->t_cc[VSTART] == tp->t_cc[VSTOP])) { + tp->t_state &= ~TS_TTSTOP; + tp->t_lflag &= ~FLUSHO; + ttstart(tp); + } + } else { + do { + u_char line_status; + int recv_data; + + line_status = (u_char) buf[CE_INPUT_OFFSET]; + recv_data = (u_char) *buf++; + if (line_status + & (LSR_BI | LSR_FE | LSR_OE | LSR_PE)) { + if (line_status & LSR_BI) + recv_data |= TTY_BI; + if (line_status & LSR_FE) + recv_data |= TTY_FE; + if (line_status & LSR_OE) + recv_data |= TTY_OE; + if (line_status & LSR_PE) + recv_data |= TTY_PE; + } + (*linesw[tp->t_line].l_rint)(recv_data, tp); + } while (--incc > 0); + } + if (com_events == 0) + break; } + if (com_events >= LOTS_OF_EVENTS) + goto repeat; +} +static int +comparam(tp, t) + struct tty *tp; + struct termios *t; +{ + int bits; + int cflag; + struct com_s *com; + u_char cor_change; + int idivisor; + int iflag; + cy_addr iobase; + int iprescaler; + int itimeout; + int odivisor; + int oprescaler; + u_char opt; + int s; + int unit; + + /* do historical conversions */ + if (t->c_ispeed == 0) + t->c_ispeed = t->c_ospeed; + + /* check requested parameters */ + idivisor = comspeed(t->c_ispeed, &iprescaler); + if (idivisor < 0) + return (EINVAL); + odivisor = comspeed(t->c_ospeed, &oprescaler); + if (odivisor < 0) + return (EINVAL); + + /* parameters are OK, convert them to the com struct and the device */ + unit = DEV_TO_UNIT(tp->t_dev); + com = com_addr(unit); + iobase = com->iobase; + s = spltty(); + cd_outb(iobase, CD1400_CAR, unit & CD1400_CAR_CHAN); + if (odivisor == 0) + (void)commctl(com, TIOCM_DTR, DMBIC); /* hang up line */ + else + (void)commctl(com, TIOCM_DTR, DMBIS); + + cd_outb(iobase, CD1400_RBPR, idivisor); + cd_outb(iobase, CD1400_RCOR, iprescaler); + cd_outb(iobase, CD1400_TBPR, odivisor); + cd_outb(iobase, CD1400_TCOR, oprescaler); /* * channel control * receiver enable * transmitter enable (always set) */ - opt = (1 << 4) | (1 << 3) | ((cflag & CREAD) ? (1 << 1) : 1); - if (opt != infop->channel_control) { - infop->channel_control = opt; - cd1400_channel_cmd(base, opt); + cflag = t->c_cflag; + opt = CD1400_CCR_CMDCHANCTL | CD1400_CCR_XMTEN + | (cflag & CREAD ? CD1400_CCR_RCVEN : CD1400_CCR_RCVDIS); + if (opt != com->channel_control) { + com->channel_control = opt; + cd1400_channel_cmd(iobase, opt); } #ifdef Smarts /* set special chars */ - if (t->c_cc[VSTOP] != _POSIX_VDISABLE && - (t->c_cc[VSTOP] != infop->spec_char[0])) { - *(base + CD1400_SCHR1) = infop->spec_char[0] = t->c_cc[VSTOP]; - } - if (t->c_cc[VSTART] != _POSIX_VDISABLE && - (t->c_cc[VSTART] != infop->spec_char[1])) { - *(base + CD1400_SCHR2) = infop->spec_char[0] = t->c_cc[VSTART]; - } - if (t->c_cc[VINTR] != _POSIX_VDISABLE && - (t->c_cc[VINTR] != infop->spec_char[2])) { - *(base + CD1400_SCHR3) = infop->spec_char[0] = t->c_cc[VINTR]; - } - if (t->c_cc[VSUSP] != _POSIX_VDISABLE && - (t->c_cc[VSUSP] != infop->spec_char[3])) { - *(base + CD1400_SCHR4) = infop->spec_char[0] = t->c_cc[VSUSP]; - } + /* XXX if one is _POSIX_VDISABLE, can't use some others */ + if (t->c_cc[VSTOP] != _POSIX_VDISABLE) + cd_outb(iobase, CD1400_SCHR1, t->c_cc[VSTOP]); + if (t->c_cc[VSTART] != _POSIX_VDISABLE) + cd_outb(iobase, CD1400_SCHR2, t->c_cc[VSTART]); + if (t->c_cc[VINTR] != _POSIX_VDISABLE) + cd_outb(iobase, CD1400_SCHR3, t->c_cc[VINTR]); + if (t->c_cc[VSUSP] != _POSIX_VDISABLE) + cd_outb(iobase, CD1400_SCHR4, t->c_cc[VSUSP]); #endif /* @@ -1294,41 +1772,84 @@ cyparam(struct tty *tp, struct termios *t) opt = 0; /* parity */ if (cflag & PARENB) { - if (cflag & PARODD) - opt |= 1 << 7; - opt |= 2 << 5; /* normal parity mode */ + if (cflag & PARODD) + opt |= CD1400_COR1_PARODD; + opt |= CD1400_COR1_PARNORMAL; } + iflag = t->c_iflag; if (!(iflag & INPCK)) - opt |= 1 << 4; /* ignore parity */ + opt |= CD1400_COR1_NOINPCK; + bits = 1 + 1; /* stop bits */ - if (cflag & CSTOPB) - opt |= 2 << 2; + if (cflag & CSTOPB) { + ++bits; + opt |= CD1400_COR1_STOP2; + } /* char length */ - opt |= (cflag & CSIZE) >> 8; /* nasty, but fast */ - if (opt != infop->cor[0]) { - cor_change |= 1 << 1; - *(base + CD1400_COR1) = opt; + switch (cflag & CSIZE) { + case CS5: + bits += 5; + opt |= CD1400_COR1_CS5; + break; + case CS6: + bits += 6; + opt |= CD1400_COR1_CS6; + break; + case CS7: + bits += 7; + opt |= CD1400_COR1_CS7; + break; + default: + bits += 8; + opt |= CD1400_COR1_CS8; + break; + } + cor_change = 0; + if (opt != com->cor[0]) { + cor_change |= CD1400_CCR_COR1; + cd_outb(iobase, CD1400_COR1, com->cor[0] = opt); } /* + * Set receive time-out period, normally to max(one char time, 5 ms). + */ + if (t->c_ispeed == 0) + itimeout = cd_inb(iobase, CD1400_RTPR); + else { + itimeout = (1000 * bits + t->c_ispeed - 1) / t->c_ispeed; +#ifdef SOFT_HOTCHAR +#define MIN_RTP 1 +#else +#define MIN_RTP 5 +#endif + if (itimeout < MIN_RTP) + itimeout = MIN_RTP; + } + if (!(t->c_lflag & ICANON) && t->c_cc[VMIN] != 0 && t->c_cc[VTIME] != 0 + && t->c_cc[VTIME] * 10 > itimeout) + itimeout = t->c_cc[VTIME] * 10; + if (itimeout > 255) + itimeout = 255; + cd_outb(iobase, CD1400_RTPR, itimeout); + + /* * set channel option register 2 - * flow control */ opt = 0; #ifdef Smarts if (iflag & IXANY) - opt |= 1 << 7; /* auto output restart on any char after XOFF */ + opt |= CD1400_COR2_IXANY; if (iflag & IXOFF) - opt |= 1 << 6; /* auto XOFF output flow-control */ + opt |= CD1400_COR2_IXOFF; #endif -#ifndef ALWAYS_RTS_CTS +#ifndef SOFT_CTS_OFLOW if (cflag & CCTS_OFLOW) + opt |= CD1400_COR2_CCTS_OFLOW; #endif - opt |= 1 << 1; /* auto CTS flow-control */ - - if (opt != infop->cor[1]) { - cor_change |= 1 << 2; - *(base + CD1400_COR2) = opt; + if (opt != com->cor[1]) { + cor_change |= CD1400_CCR_COR2; + cd_outb(iobase, CD1400_COR2, com->cor[1] = opt); } /* @@ -1336,24 +1857,22 @@ cyparam(struct tty *tp, struct termios *t) * receiver FIFO interrupt threshold * flow control */ - opt = RxFifoThreshold; /* rx fifo threshold */ + opt = RxFifoThreshold; #ifdef Smarts if (t->c_lflag & ICANON) - opt |= 1 << 6; /* detect INTR & SUSP chars */ + opt |= CD1400_COR3_SCD34; /* detect INTR & SUSP chars */ if (iflag & IXOFF) - opt |= (1 << 5) | (1 << 4); /* transparent in-band flow control */ + /* detect and transparently handle START and STOP chars */ + opt |= CD1400_COR3_FCT | CD1400_COR3_SCD12; #endif - if (opt != infop->cor[2]) { - cor_change |= 1 << 3; - *(base + CD1400_COR3) = opt; + if (opt != com->cor[2]) { + cor_change |= CD1400_CCR_COR3; + cd_outb(iobase, CD1400_COR3, com->cor[2] = opt); } - /* notify the CD1400 if COR1-3 have changed */ - if (cor_change) { - cor_change |= 1 << 6; /* COR change flag */ - cd1400_channel_cmd(base, cor_change); - } + if (cor_change) + cd1400_channel_cmd(iobase, CD1400_CCR_CMDCORCHG | cor_change); /* * set channel option register 4 - @@ -1363,7 +1882,7 @@ cyparam(struct tty *tp, struct termios *t) */ opt = 0; if (iflag & IGNCR) - opt |= 1 << 7; + opt |= CD1400_COR4_IGNCR; #ifdef Smarts /* * we need a new ttyinput() for this, as we don't want to @@ -1371,305 +1890,712 @@ cyparam(struct tty *tp, struct termios *t) * synchronisation problems */ if (iflag & ICRNL) - opt |= 1 << 6; + opt |= CD1400_COR4_ICRNL; if (iflag & INLCR) - opt |= 1 << 5; + opt |= CD1400_COR4_INLCR; #endif if (iflag & IGNBRK) - opt |= 1 << 4; + opt |= CD1400_COR4_IGNBRK; if (!(iflag & BRKINT)) - opt |= 1 << 3; + opt |= CD1400_COR4_NOBRKINT; +#if 0 + /* XXX using this "intelligence" breaks reporting of overruns. */ if (iflag & IGNPAR) -#ifdef LogOverruns - opt |= 0; /* broken chars cause receive exceptions */ -#else - opt |= 2; /* discard broken chars */ -#endif + opt |= CD1400_COR4_PFO_DISCARD; else { - if (iflag & PARMRK) - opt |= 4; /* precede broken chars with 0xff 0x0 */ - else -#ifdef LogOverruns - opt |= 0; /* broken chars cause receive exceptions */ + if (iflag & PARMRK) + opt |= CD1400_COR4_PFO_ESC; + else + opt |= CD1400_COR4_PFO_NUL; + } #else - opt |= 3; /* convert framing/parity errs to nulls */ + opt |= CD1400_COR4_PFO_EXCEPTION; #endif - } - *(base + CD1400_COR4) = opt; + cd_outb(iobase, CD1400_COR4, opt); /* * set channel option register 5 - */ opt = 0; if (iflag & ISTRIP) - opt |= 1 << 7; - if (t->c_iflag & IEXTEN) { - opt |= 1 << 6; /* enable LNEXT (e.g. ctrl-v quoting) handling */ - } + opt |= CD1400_COR5_ISTRIP; + if (t->c_iflag & IEXTEN) + /* enable LNEXT (e.g. ctrl-v quoting) handling */ + opt |= CD1400_COR5_LNEXT; #ifdef Smarts if (t->c_oflag & ONLCR) - opt |= 1 << 1; + opt |= CD1400_COR5_ONLCR; if (t->c_oflag & OCRNL) - opt |= 1; + opt |= CD1400_COR5_OCRNL; #endif - *(base + CD1400_COR5) = opt; + cd_outb(iobase, CD1400_COR5, opt); /* + * XXX we probably alway want to track carrier changes, so that + * TS_CARR_ON gives the true carrier. If we don't track them, + * then we should set TS_CARR_ON when CLOCAL drops. + */ + /* * set modem change option register 1 * generate modem interrupts on which 1 -> 0 input transitions * also controls auto-DTR output flow-control, which we don't use */ - opt = (cflag & CLOCAL) ? 0 : 1 << 4; /* CD */ - *(base + CD1400_MCOR1) = opt; + opt = cflag & CLOCAL ? 0 : CD1400_MCOR1_CDzd; +#ifdef SOFT_CTS_OFLOW + if (cflag & CCTS_OFLOW) + opt |= CD1400_MCOR1_CTSzd; +#endif + cd_outb(iobase, CD1400_MCOR1, opt); /* * set modem change option register 2 * generate modem interrupts on specific 0 -> 1 input transitions */ - opt = (cflag & CLOCAL) ? 0 : 1 << 4; /* CD */ - *(base + CD1400_MCOR2) = opt; + opt = cflag & CLOCAL ? 0 : CD1400_MCOR2_CDod; +#ifdef SOFT_CTS_OFLOW + if (cflag & CCTS_OFLOW) + opt |= CD1400_MCOR2_CTSod; +#endif + cd_outb(iobase, CD1400_MCOR2, opt); - splx(s); + /* + * XXX should have done this long ago, but there is too much state + * to change all atomically. + */ + disable_intr(); + + com->state &= ~CS_TTGO; + if (!(tp->t_state & TS_TTSTOP)) + com->state |= CS_TTGO; + if (cflag & CRTS_IFLOW) + com->state |= CS_RTS_IFLOW; /* XXX - secondary changes? */ + else + com->state &= ~CS_RTS_IFLOW; - return 0; -} /* end of cyparam */ + /* + * Set up state to handle output flow control. + * XXX - worth handling MDMBUF (DCD) flow control at the lowest level? + * Now has 10+ msec latency, while CTS flow has 50- usec latency. + */ + com->state |= CS_ODEVREADY; +#ifdef SOFT_CTS_OFLOW + com->state &= ~CS_CTS_OFLOW; + if (cflag & CCTS_OFLOW) { + com->state |= CS_CTS_OFLOW; + if (!(com->last_modem_status & MSR_CTS)) + com->state &= ~CS_ODEVREADY; + } +#endif + /* XXX shouldn't call functions while intrs are disabled. */ + disc_optim(tp, t, com); +#if 0 + /* + * Recover from fiddling with CS_TTGO. We used to call siointr1() + * unconditionally, but that defeated the careful discarding of + * stale input in sioopen(). + */ + if (com->state >= (CS_BUSY | CS_TTGO)) + siointr1(com); +#endif + if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) { + if (!(com->intr_enable & CD1400_SRER_TXRDY)) + cd_outb(iobase, CD1400_SRER, + com->intr_enable |= CD1400_SRER_TXRDY); + } else { + if (com->intr_enable & CD1400_SRER_TXRDY) + cd_outb(iobase, CD1400_SRER, + com->intr_enable &= ~CD1400_SRER_TXRDY); + } + enable_intr(); + splx(s); + return (0); +} -void -cystart(struct tty *tp) +static void +comstart(tp) + struct tty *tp; { - u_char unit = UNIT(tp->t_dev); - struct cy *infop = info[unit]; - cy_addr base = infop->base_addr; + struct com_s *com; + cy_addr iobase; int s; +#ifdef CyDebug + bool_t started; +#endif + int unit; + + unit = DEV_TO_UNIT(tp->t_dev); + com = com_addr(unit); + iobase = com->iobase; + s = spltty(); #ifdef CyDebug - infop->start_count++; + ++com->start_count; + started = FALSE; #endif - /* check the flow-control situation */ - if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) + disable_intr(); + cd_outb(iobase, CD1400_CAR, unit & CD1400_CAR_CHAN); + if (tp->t_state & TS_TTSTOP) { + com->state &= ~CS_TTGO; + if (com->intr_enable & CD1400_SRER_TXRDY) + cd_outb(iobase, CD1400_SRER, + com->intr_enable &= ~CD1400_SRER_TXRDY); + } else { + com->state |= CS_TTGO; + if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY) + && !(com->intr_enable & CD1400_SRER_TXRDY)) + cd_outb(iobase, CD1400_SRER, + com->intr_enable |= CD1400_SRER_TXRDY); + } + if (tp->t_state & TS_TBLOCK) { + if (com->mcr_image & MCR_RTS && com->state & CS_RTS_IFLOW) +#if 0 + outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); +#else + cd_outb(iobase, CD1400_MSVR1, + com->mcr_image &= ~MCR_RTS); +#endif + } else { + /* + * XXX don't raise MCR_RTS if CTS_RTS_IFLOW is off. Set it + * appropriately in comparam() if RTS-flow is being changed. + * Check for races. + */ + if (!(com->mcr_image & MCR_RTS) && com->iptr < com->ihighwater) +#if 0 + outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); +#else + cd_outb(iobase, CD1400_MSVR1, + com->mcr_image |= MCR_RTS); +#endif + } + enable_intr(); + if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) { + splx(s); return; + } + if (tp->t_outq.c_cc != 0) { + struct lbq *qp; + struct lbq *next; + if (!com->obufs[0].l_queued) { +#ifdef CyDebug + started = TRUE; +#endif + com->obufs[0].l_tail + = com->obuf1 + q_to_b(&tp->t_outq, com->obuf1, + sizeof com->obuf1); + com->obufs[0].l_next = NULL; + com->obufs[0].l_queued = TRUE; + disable_intr(); + if (com->state & CS_BUSY) { + qp = com->obufq.l_next; + while ((next = qp->l_next) != NULL) + qp = next; + qp->l_next = &com->obufs[0]; + } else { + com->obufq.l_head = com->obufs[0].l_head; + com->obufq.l_tail = com->obufs[0].l_tail; + com->obufq.l_next = &com->obufs[0]; + com->state |= CS_BUSY; + if (com->state >= (CS_BUSY | CS_TTGO + | CS_ODEVREADY)) + cd_outb(iobase, CD1400_SRER, + com->intr_enable + |= CD1400_SRER_TXRDY); + } + enable_intr(); + } + if (tp->t_outq.c_cc != 0 && !com->obufs[1].l_queued) { +#ifdef CyDebug + started = TRUE; +#endif + com->obufs[1].l_tail + = com->obuf2 + q_to_b(&tp->t_outq, com->obuf2, + sizeof com->obuf2); + com->obufs[1].l_next = NULL; + com->obufs[1].l_queued = TRUE; + disable_intr(); + if (com->state & CS_BUSY) { + qp = com->obufq.l_next; + while ((next = qp->l_next) != NULL) + qp = next; + qp->l_next = &com->obufs[1]; + } else { + com->obufq.l_head = com->obufs[1].l_head; + com->obufq.l_tail = com->obufs[1].l_tail; + com->obufq.l_next = &com->obufs[1]; + com->state |= CS_BUSY; + if (com->state >= (CS_BUSY | CS_TTGO + | CS_ODEVREADY)) + cd_outb(iobase, CD1400_SRER, + com->intr_enable + |= CD1400_SRER_TXRDY); + } + enable_intr(); + } + tp->t_state |= TS_BUSY; + } +#ifdef CyDebug + if (started) + ++com->start_real; +#endif +#if 0 + disable_intr(); + if (com->state >= (CS_BUSY | CS_TTGO)) { + siointr1(com); /* fake interrupt to start output */ + enable_intr(); +#endif + +#if 0 /* XXX TK2.0 */ + if (tp->t_state & (TS_SO_OCOMPLETE | TS_SO_OLOWAT) || tp->t_wsel) + ttwwakeup(tp); +#else if (tp->t_outq.c_cc <= tp->t_lowat) { - if (tp->t_state&TS_ASLEEP) { + if (tp->t_state & TS_ASLEEP) { tp->t_state &= ~TS_ASLEEP; - wakeup((caddr_t)&tp->t_outq); + wakeup(TSA_OLOWAT(tp)); } selwakeup(&tp->t_wsel); } - -#ifdef TxBuffer - service_upper_tx(unit); /* feed the monster */ #endif - s = spltty(); + splx(s); +} - if (!(infop->intr_enable & (1 << 2))) { - /* select the channel */ - *(base + CD1400_CAR) = unit & (u_char)3; +void +siostop(tp, rw) + struct tty *tp; + int rw; +{ + struct com_s *com; + cy_addr iobase; + int unit; + + unit = DEV_TO_UNIT(tp->t_dev); + com = com_addr(unit); + iobase = com->iobase; + disable_intr(); + if (rw & FWRITE) { + com->obufs[0].l_queued = FALSE; + com->obufs[1].l_queued = FALSE; + if (com->state & CS_ODONE) + com_events -= LOTS_OF_EVENTS; + com->state &= ~(CS_ODONE | CS_BUSY); + com->tp->t_state &= ~TS_BUSY; + } + if (rw & FREAD) { + com_events -= (com->iptr - com->ibuf); + com->iptr = com->ibuf; + } + if (tp->t_state & TS_TTSTOP) { + com->state &= ~CS_TTGO; + if (com->intr_enable & CD1400_SRER_TXRDY) { + cd_outb(iobase, CD1400_CAR, unit & CD1400_CAR_CHAN); + cd_outb(iobase, CD1400_SRER, + com->intr_enable &= ~CD1400_SRER_TXRDY); + } + } else { + com->state |= CS_TTGO; + if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY) + && !(com->intr_enable & CD1400_SRER_TXRDY)) { + cd_outb(iobase, CD1400_CAR, unit & CD1400_CAR_CHAN); + cd_outb(iobase, CD1400_SRER, + com->intr_enable |= CD1400_SRER_TXRDY); + } + } + enable_intr(); - /* (re)enable interrupts to set things in motion */ - infop->intr_enable |= (1 << 2); - *(base + CD1400_SRER) = infop->intr_enable; + /* XXX should clear h/w fifos too. */ +} + +struct tty * +siodevtotty(dev) + dev_t dev; +{ + int mynor; + int unit; - infop->start_real++; + mynor = minor(dev); + if (mynor & CONTROL_MASK) + return (NULL); + unit = MINOR_TO_UNIT(mynor); + if ((u_int) unit >= NSIO) + return (NULL); + return (&sio_tty[unit]); +} + +static int +commctl(com, bits, how) + struct com_s *com; + int bits; + int how; +{ + cy_addr iobase; + int mcr; + int msr; + + if (how == DMGET) { + if (com->channel_control & CD1400_CCR_RCVEN) + bits |= TIOCM_LE; + mcr = com->mcr_image; + if (mcr & MCR_DTR) + bits |= TIOCM_DTR; + if (mcr & MCR_RTS) + /* XXX wired on for Cyclom-8Ys */ + bits |= TIOCM_RTS; + msr = com->prev_modem_status; + if (msr & MSR_CTS) + bits |= TIOCM_CTS; + if (msr & MSR_DCD) + bits |= TIOCM_CD; + if (msr & MSR_DSR) + bits |= TIOCM_DSR; + if (msr & MSR_RI) + /* XXX not connected except for Cyclom-16Y? */ + bits |= TIOCM_RI; + return (bits); } + iobase = com->iobase; + mcr = 0; + if (bits & TIOCM_DTR) + mcr |= MCR_DTR; + if (bits & TIOCM_RTS) + mcr |= MCR_RTS; + disable_intr(); + switch (how) { + case DMSET: + com->mcr_image = mcr; + cd_outb(iobase, CD1400_MSVR1, mcr); + cd_outb(iobase, CD1400_MSVR2, mcr); + break; + case DMBIS: + com->mcr_image = mcr = com->mcr_image | mcr; + cd_outb(iobase, CD1400_MSVR1, mcr); + cd_outb(iobase, CD1400_MSVR2, mcr); + break; + case DMBIC: + com->mcr_image = mcr = com->mcr_image & ~mcr; + cd_outb(iobase, CD1400_MSVR1, mcr); + cd_outb(iobase, CD1400_MSVR2, mcr); + break; + } + enable_intr(); + return (0); +} - splx(s); -} /* end of cystart() */ +static void +siosettimeout() +{ + struct com_s *com; + bool_t someopen; + int unit; + /* + * Set our timeout period to 1 second if no polled devices are open. + * Otherwise set it to max(1/200, 1/hz). + * Enable timeouts iff some device is open. + */ + untimeout(comwakeup, (void *)NULL); + sio_timeout = hz; + someopen = FALSE; + for (unit = 0; unit < NSIO; ++unit) { + com = com_addr(unit); + if (com != NULL && com->tp != NULL + && com->tp->t_state & TS_ISOPEN) { + someopen = TRUE; +#if 0 + if (com->poll || com->poll_output) { + sio_timeout = hz > 200 ? hz / 200 : 1; + break; + } +#endif + } + } + if (someopen) { + sio_timeouts_until_log = hz / sio_timeout; + timeout(comwakeup, (void *)NULL, sio_timeout); + } else { + /* Flush error messages, if any. */ + sio_timeouts_until_log = 1; + comwakeup((void *)NULL); + untimeout(comwakeup, (void *)NULL); + } +} -int -cystop(struct tty *tp, int flag) +static void +comwakeup(chan) + void *chan; { - u_char unit = UNIT(tp->t_dev); - struct cy *ip = info[unit]; - cy_addr base = ip->base_addr; - int s; + struct com_s *com; + int unit; - s = spltty(); + timeout(comwakeup, (void *)NULL, sio_timeout); - /* select the channel */ - *(base + CD1400_CAR) = unit & 3; +#if 0 + /* + * Recover from lost output interrupts. + * Poll any lines that don't use interrupts. + */ + for (unit = 0; unit < NSIO; ++unit) { + com = com_addr(unit); + if (com != NULL + && (com->state >= (CS_BUSY | CS_TTGO) || com->poll)) { + disable_intr(); + siointr1(com); + enable_intr(); + } + } +#endif - /* halt output by disabling transmit interrupts */ - ip->intr_enable &=~ (1 << 2); - *(base + CD1400_SRER) = ip->intr_enable; + /* + * Check for and log errors, but not too often. + */ + if (--sio_timeouts_until_log > 0) + return; + sio_timeouts_until_log = hz / sio_timeout; + for (unit = 0; unit < NSIO; ++unit) { + int errnum; - splx(s); + com = com_addr(unit); + if (com == NULL) + continue; + for (errnum = 0; errnum < CE_NTYPES; ++errnum) { + u_int delta; + u_long total; - return 0; + disable_intr(); + delta = com->delta_error_counts[errnum]; + com->delta_error_counts[errnum] = 0; + enable_intr(); + if (delta == 0) + continue; + total = com->error_counts[errnum] += delta; + log(LOG_ERR, "cy%d: %u more %s%s (total %lu)\n", + unit, delta, error_desc[errnum], + delta == 1 ? "" : "s", total); +#if 0 + /* + * XXX if we resurrect this then we should move + * the dropping of the ftl to somewhere with less + * latency. + */ + if (errnum == CE_OVERRUN && com->hasfifo + && com->ftl > FIFO_TRIGGER_1) { + static u_char ftl_in_bytes[] = + { 1, 4, 8, 14, }; + + com->ftl_init = FIFO_TRIGGER_8; +#define FIFO_TRIGGER_DELTA FIFO_TRIGGER_4 + com->ftl_max = + com->ftl -= FIFO_TRIGGER_DELTA; + outb(com->iobase + com_fifo, + FIFO_ENABLE | com->ftl); + log(LOG_DEBUG, + "sio%d: reduced fifo trigger level to %d\n", + unit, + ftl_in_bytes[com->ftl + / FIFO_TRIGGER_DELTA]); + } +#endif + } + } } -struct tty * -cydevtotty(dev_t dev) +static void +disc_optim(tp, t, com) + struct tty *tp; + struct termios *t; + struct com_s *com; { - u_char unit = UNIT(dev); - - if (unit >= /* NCY * ? */ PORTS_PER_CYCLOM) - return NULL; +#ifndef SOFT_HOTCHAR + cy_addr iobase; + u_char opt; +#endif - return info[unit]->tty; + /* + * XXX can skip a lot more cases if Smarts. Maybe + * (IGNCR | ISTRIP | IXOFF | IXON) in c_iflag. But perhaps we + * shouldn't skip if (TS_CNTTB | TS_LNCH) is set in t_state. + */ + if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP + | IXOFF | IXON)) + && (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK)) + && (!(t->c_iflag & PARMRK) || + (t->c_iflag & (IGNPAR|IGNBRK)) == (IGNPAR|IGNBRK)) + && !(t->c_lflag & (ECHO | ECHONL | ICANON | IEXTEN | ISIG + | PENDIN)) + && linesw[tp->t_line].l_rint == ttyinput) + tp->t_state |= TS_CAN_BYPASS_L_RINT; + else + tp->t_state &= ~TS_CAN_BYPASS_L_RINT; + /* + * Prepare to reduce input latency for packet + * discplines with a end of packet character. + */ + if (tp->t_line == SLIPDISC) + com->hotchar = 0xc0; + else if (tp->t_line == PPPDISC) + com->hotchar = 0x7e; + else + com->hotchar = 0; +#ifndef SOFT_HOTCHAR + iobase = com->iobase; + cd_outb(iobase, CD1400_CAR, com->unit & CD1400_CAR_CHAN); + opt = com->cor[2] & ~CD1400_COR3_SCD34; + if (com->hotchar != 0) { + cd_outb(iobase, CD1400_SCHR3, com->hotchar); + cd_outb(iobase, CD1400_SCHR4, com->hotchar); + opt |= CD1400_COR3_SCD34; + } + if (opt != com->cor[2]) { + cd_outb(iobase, CD1400_COR3, com->cor[2] = opt); + cd1400_channel_cmd(com->iobase, + CD1400_CCR_CMDCORCHG | CD1400_CCR_COR3); + } +#endif } +#ifdef Smarts +/* standard line discipline input routine */ int -cyspeed(int speed, int *prescaler_io) +cyinput(c, tp) + int c; + struct tty *tp; { - int actual; - int error; - int divider; - int prescaler; - int prescaler_unit; - - if (speed == 0) - return 0; - - if (speed < 0 || speed > 150000) - return -1; - - /* determine which prescaler to use */ - for (prescaler_unit = 4, prescaler = 2048; prescaler_unit; - prescaler_unit--, prescaler >>= 2) { - if (CYCLOM_CLOCK/prescaler/speed > 63) - break; - } - - divider = (CYCLOM_CLOCK/prescaler*2/speed + 1)/2; /* round off */ - if (divider > 255) - divider = 255; - actual = CYCLOM_CLOCK/prescaler/divider; - error = ((actual-speed)*2000/speed +1)/2; /* percentage */ - - /* 3.0% max error tolerance */ - if (error < -30 || error > 30) - return -1; + /* XXX duplicate ttyinput(), but without the IXOFF/IXON/ISTRIP/IPARMRK + * bits, as they are done by the CD1400. Hardly worth the effort, + * given that high-throughput sessions are raw anyhow. + */ +} +#endif /* Smarts */ + +static int +comspeed(speed, prescaler_io) + speed_t speed; + int *prescaler_io; +{ + int actual; + int error; + int divider; + int prescaler; + int prescaler_unit; + + if (speed == 0) + return (0); + if (speed < 0 || speed > 150000) + return (-1); + + /* determine which prescaler to use */ + for (prescaler_unit = 4, prescaler = 2048; prescaler_unit; + prescaler_unit--, prescaler >>= 2) { + if (CY_CLOCK / prescaler / speed > 63) + break; + } + + divider = (CY_CLOCK / prescaler * 2 / speed + 1) / 2; /* round off */ + if (divider > 255) + divider = 255; + actual = CY_CLOCK/prescaler/divider; + error = ((actual - speed) * 2000 / speed + 1) / 2; /* percentage */ + + /* 3.0% max error tolerance */ + if (error < -30 || error > 30) + return (-1); #if 0 - printf("prescaler = %d (%d)\n", prescaler, prescaler_unit); - printf("divider = %d (%x)\n", divider, divider); - printf("actual = %d\n", actual); - printf("error = %d\n", error); + printf("prescaler = %d (%d)\n", prescaler, prescaler_unit); + printf("divider = %d (%x)\n", divider, divider); + printf("actual = %d\n", actual); + printf("error = %d\n", error); #endif - *prescaler_io = prescaler_unit; - return divider; -} /* end of cyspeed() */ - + *prescaler_io = prescaler_unit; + return (divider); +} static void -cd1400_channel_cmd(cy_addr base, u_char cmd) +cd1400_channel_cmd(iobase, cmd) + cy_addr iobase; + int cmd; { - /* XXX hsu@clinet.fi: This is always more dependent on ISA bus speed, + /* XXX hsu@clinet.fi: This is always more dependent on ISA bus speed, as the card is probed every round? Replaced delaycount with 8k. Either delaycount has to be implemented in FreeBSD or more sensible way of doing these should be implemented. DELAY isn't enough here. */ - unsigned maxwait = 5 * 8 * 1024; /* approx. 5 ms */ + u_int maxwait = 5 * 8 * 1024; /* approx. 5 ms */ /* wait for processing of previous command to complete */ - while (*(base + CD1400_CCR) && maxwait--) - ; + while (cd_inb(iobase, CD1400_CCR) && maxwait--) + ; if (!maxwait) log(LOG_ERR, "cy: channel command timeout (%d loops) - arrgh\n", 5 * 8 * 1024); - *(base + CD1400_CCR) = cmd; -} /* end of cd1400_channel_cmd() */ - - -#ifdef CyDebug -/* useful in ddb */ -void -cyclear(void) -{ - /* clear the timeout request */ - disable_intr(); - timeout_scheduled = 0; - enable_intr(); + cd_outb(iobase, CD1400_CCR, cmd); } -void -cyclearintr(void) -{ - /* clear interrupts */ - *(cyclom_base + CYCLOM_CLEAR_INTR) = (u_char)0; -} - -int -cyparam_dummy(struct tty *tp, struct termios *t) -{ - return 0; -} - -void -cyset(int unit, int active) -{ - if (unit < 0 || unit >= /* NCY *? */ PORTS_PER_CYCLOM) { - printf("bad unit number %d\n", unit); - return; - } -#ifdef __FreeBSD__ - cy_tty[unit].t_param = active ? cyparam : cyparam_dummy; -#else - cy_tty[unit]->t_param = active ? cyparam : cyparam_dummy; -#endif -} - - +#ifdef CyDebug /* useful in ddb */ void -cystatus(int unit) +cystatus(unit) + int unit; { - struct cy *infop = info[unit]; - struct tty *tp = infop->tty; - cy_addr base = infop->base_addr; + struct com_s *com; + cy_addr iobase; + u_int ocount; + struct tty *tp; + com = com_addr(unit); printf("info for channel %d\n", unit); printf("------------------\n"); - - printf("cd1400 base address:\t0x%x\n", (int)infop->base_addr); - - /* select the port */ - *(base + CD1400_CAR) = (u_char)unit; - - printf("saved channel_control:\t%02x\n", infop->channel_control); - printf("saved cor1:\t\t%02x\n", infop->cor[0]); - printf("service request enable reg:\t%02x (%02x cached)\n", - (u_char)*(base + CD1400_SRER), infop->intr_enable); - printf("service request register:\t%02x\n", - (u_char)*(base + CD1400_SVRR)); + printf("total cyclom service probes:\t%d\n", cy_svrr_probes); + printf("calls to upper layer:\t\t%d\n", cy_timeouts); + if (com == NULL) + return; + iobase = com->iobase; + printf("\n"); + printf("cd1400 base address:\\tt%p\n", iobase); + cd_outb(iobase, CD1400_CAR, unit & CD1400_CAR_CHAN); + printf("saved channel_control:\t\t0x%02x\n", com->channel_control); + printf("saved cor1-3:\t\t\t0x%02x 0x%02x 0x%02x\n", + com->cor[0], com->cor[1], com->cor[2]); + printf("service request enable reg:\t0x%02x (0x%02x cached)\n", + cd_inb(iobase, CD1400_SRER), com->intr_enable); + printf("service request register:\t0x%02x\n", + cd_inb(iobase, CD1400_SVRR)); + printf("modem status:\t\t\t0x%02x (0x%02x cached)\n", + cd_inb(iobase, CD1400_MSVR2), com->prev_modem_status); + printf("rx/tx/mdm interrupt registers:\t0x%02x 0x%02x 0x%02x\n", + cd_inb(iobase, CD1400_RIR), cd_inb(iobase, CD1400_TIR), + cd_inb(iobase, CD1400_MIR)); printf("\n"); - printf("modem status:\t\t\t%02x (%02x cached)\n", - (u_char)*(base + CD1400_MSVR), infop->modem_sig); - printf("rx/tx/mdm interrupt registers:\t%02x %02x %02x\n", - (u_char)*(base + CD1400_RIR), (u_char)*(base + CD1400_TIR), - (u_char)*(base + CD1400_MIR)); + printf("com state:\t\t\t0x%02x\n", com->state); + printf("calls to comstart():\t\t%d (%d useful)\n", + com->start_count, com->start_real); + printf("rx buffer chars free:\t\t%d\n", com->iptr - com->ibuf); + ocount = 0; + if (com->obufs[0].l_queued) + ocount += com->obufs[0].l_tail - com->obufs[0].l_head; + if (com->obufs[1].l_queued) + ocount += com->obufs[1].l_tail - com->obufs[1].l_head; + printf("tx buffer chars:\t\t%u\n", ocount); + printf("received chars:\t\t\t%d\n", com->bytes_in); + printf("received exceptions:\t\t%d\n", com->recv_exception); + printf("modem signal deltas:\t\t%d\n", com->mdm); + printf("transmitted chars:\t\t%d\n", com->bytes_out); printf("\n"); - if (tp) { - printf("tty state:\t\t\t%04x\n", tp->t_state); + tp = com->tp; + if (tp != NULL) { + printf("tty state:\t\t\t0x%08x\n", tp->t_state); printf("upper layer queue lengths:\t%d raw, %d canon, %d output\n", - tp->t_rawq.c_cc, tp->t_canq.c_cc, tp->t_outq.c_cc); - } - else + tp->t_rawq.c_cc, tp->t_canq.c_cc, tp->t_outq.c_cc); + } else printf("tty state:\t\t\tclosed\n"); - printf("\n"); +} +#endif /* CyDebug */ - printf("calls to cystart():\t\t%d (%d useful)\n", - infop->start_count, infop->start_real); - printf("\n"); - printf("total cyclom service probes:\t%d\n", cy_svrr_probes); - printf("calls to upper layer:\t\t%d\n", cy_timeouts); - printf("rx buffer chars free:\t\t%d\n", infop->rx_buf->free); -#ifdef TxBuffer - printf("tx buffer chars used:\t\t%d\n", infop->tx_buf.used); -#endif - printf("received chars:\t\t\t%d good, %d exception\n", - infop->recv_normal, infop->recv_exception); - printf("transmitted chars:\t\t%d\n", infop->xmit); - printf("modem signal deltas:\t\t%d\n", infop->mdm); - printf("\n"); -} /* end of cystatus() */ -#endif #endif /* NCY > 0 */ diff --git a/sys/dev/cy/cy_isa.c b/sys/dev/cy/cy_isa.c index fd898f2..81f7022 100644 --- a/sys/dev/cy/cy_isa.c +++ b/sys/dev/cy/cy_isa.c @@ -1,4 +1,4 @@ -/* +/*- * cyclades cyclom-y serial driver * Andrew Herbert <andrew@werple.apana.org.au>, 17 August 1993 * @@ -27,1262 +27,1740 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $Id: cy.c,v 1.6 1995/03/28 12:29:11 bde Exp $ + * $Id: cy.c,v 1.7 1995/05/30 08:01:34 rgrimes Exp $ */ +#include "cy.h" +#if NCY > 0 /* - * Device minor number encoding: - * - * c c x x u u u u - bits in the minor device number - * - * bits meaning - * ---- ------- - * uuuu physical serial line (i.e. unit) to use - * 0-7 on a cyclom-8Y, 0-15 on a cyclom-16Y - * xx unused - * cc carrier control mode - * 00 complete hardware carrier control of the tty. - * DCD must be high for the open(2) to complete. - * 01 dialin pseudo-device (not yet implemented) - * 10 carrier ignored until a high->low transition - * 11 carrier completed ignored + * TODO: + * Check that cy16's work. + * Implement BREAK. + * Fix overflows when closing line. + * Atomic COR change. + * Don't report individual ports in devconf; busy flag for board should be + * union of the current individual busy flags. + * Consoles. */ /* - * Known deficiencies: - * - * * no BREAK handling - breaks are ignored, and can't be sent either - * * no support for bad-char reporting, except via PARMRK - * * no support for dialin + dialout devices + * Temporary compile-time configuration options. */ +#define RxFifoThreshold (CD1400_RX_FIFO_SIZE / 2) + /* Number of chars in the receiver FIFO before an + * an interrupt is generated. Should depend on + * line speed. Needs to be about 6 on a 486DX33 + * for 4 active ports at 115200 bps. Why doesn't + * 10 work? + */ +#define PollMode /* Use polling-based irq service routine, not the + * hardware svcack lines. Must be defined for + * Cyclom-16Y boards. Less efficient for Cyclom-8Ys, + * and stops 4 * 115200 bps from working. + */ +#undef Smarts /* Enable slightly more CD1400 intelligence. Mainly + * the output CR/LF processing, plus we can avoid a + * few checks usually done in ttyinput(). + * + * XXX not fully implemented, and not particularly + * worthwhile. + */ +#undef CyDebug /* Include debugging code (not very expensive). */ -#include "cy.h" -#if NCY > 0 - -/* This disgusing hack because we actually have 16 units on one controller */ -#if NCY < 2 -#undef NCY -#define NCY (16) -#endif +/* These will go away. */ +#undef SOFT_CTS_OFLOW +#define SOFT_HOTCHAR #include <sys/param.h> #include <sys/systm.h> -#include <sys/kernel.h> -#include <sys/malloc.h> +#include <sys/reboot.h> #include <sys/ioctl.h> +#define TTYDEFCHARS /* XXX TK2.0 */ #include <sys/tty.h> +#undef TTYDEFCHARS #include <sys/proc.h> #include <sys/user.h> #include <sys/conf.h> +#include <sys/dkstat.h> #include <sys/file.h> #include <sys/uio.h> #include <sys/kernel.h> +#include <sys/malloc.h> #include <sys/syslog.h> +#include <sys/devconf.h> -#include <machine/cpu.h> -#ifdef NetBSD -#include <machine/pio.h> -#include <machine/cpufunc.h> -#else #include <machine/clock.h> -#endif +#include <i386/isa/icu.h> /* XXX just to get at `imen' */ +#include <i386/isa/isa.h> #include <i386/isa/isa_device.h> +#include <i386/isa/cyreg.h> #include <i386/isa/ic/cd1400.h> -#define RxFifoThreshold 3 /* 3 characters (out of 12) in the receive - * FIFO before an interrupt is generated - */ -#define FastRawInput /* bypass the regular char-by-char canonical input - * processing whenever possible - */ -#define PollMode /* use polling-based irq service routine, not the - * hardware svcack lines. Must be defined for - * cyclom-16y boards. - * - * XXX cyclom-8y doesn't work without this defined - * either (!) - */ -#define LogOverruns /* log receive fifo overruns */ -#undef TxBuffer /* buffer driver output, to be slightly more - * efficient - * - * XXX presently buggy - */ -#undef Smarts /* enable slightly more CD1400 intelligence. Mainly - * the output CR/LF processing, plus we can avoid a - * few checks usually done in ttyinput(). - * - * XXX not yet implemented, and not particularly - * worthwhile either. - */ -#define CyDebug /* include debugging code (minimal effect on - * performance) - */ +/* + * Dictionary so that I can name everything *sio* or *com* to compare with + * sio.c. There is also lots of ugly formatting and unnecessary ifdefs to + * simplify the comparision. These will go away. + */ +#define LSR_BI CD1400_RDSR_BREAK +#define LSR_FE CD1400_RDSR_FE +#define LSR_OE CD1400_RDSR_OE +#define LSR_PE CD1400_RDSR_PE +#define MCR_DTR CD1400_MSVR2_DTR +#define MCR_RTS CD1400_MSVR1_RTS +#define MSR_CTS CD1400_MSVR2_CTS +#define MSR_DCD CD1400_MSVR2_CD +#define MSR_DSR CD1400_MSVR2_DSR +#define MSR_RI CD1400_MSVR2_RI +#define NSIO (NCY * CY_MAX_PORTS) +#define comconsole cyconsole +#define comdefaultrate cydefaultrate +#define com_events cy_events +#define comhardclose cyhardclose +#define commajor cymajor +#define commctl cymctl +#define comparam cyparam +#define comspeed cyspeed +#define comstart cystart +#define comwakeup cywakeup +#define kdc_sio kdc_cy +#define nsio_tty ncy_tty +#define p_com_addr p_cy_addr +#define sioattach cyattach +#define sioclose cyclose +#define siodevtotty cydevtotty +#define siodriver cydriver +#define siodtrwakeup cydtrwakeup +#define sioioctl cyioctl +#define siointr cyintr +#define siointr1 cyintr1 +#define siointrts cyintrts +#define sioopen cyopen +#define siopoll cypoll +#define sioprobe cyprobe +#define sioread cyread +#define sioregisterdev cyregisterdev +#define siosettimeout cysettimeout +#define siostop cystop +#define siowrite cywrite +#define sio_timeout cy_timeout +#define sio_timeouts_until_log cy_timeouts_until_log +#define sio_tty cy_tty + +#define CY_MAX_PORTS (CD1400_NO_OF_CHANNELS * CY_MAX_CD1400s) + +/* We encode the cyclom unit number (cyu) in spare bits in the IVR's. */ +#define CD1400_xIVR_CHAN_SHIFT 3 +#define CD1400_xIVR_CHAN 0x0F /* XXX reduce to pack Cyclom-8Ys */ -#define CY_RX_BUFS 2 /* two receive buffers per port */ -#define CY_RX_BUF_SIZE 256 /* bytes per receive buffer */ -#define CY_TX_BUF_SIZE 512 /* bytes per transmit buffer */ +/* + * XXX temporary kludges for 2.0 (XXX TK2.0). + */ +#define TSA_CARR_ON(tp) ((void *)&(tp)->t_rawq) +#define TSA_OCOMPLETE(tp) ((void *)&(tp)->t_outq) +#define TSA_OLOWAT(tp) ((void *)&(tp)->t_outq) +static void termioschars __P((struct termios *t)); +static void +termioschars(t) + struct termios *t; +{ -/* #define CD1400s_PER_CYCLOM 1 */ /* cyclom-4y */ -/* #define CD1400s_PER_CYCLOM 2 */ /* cyclom-8y */ -#define CD1400s_PER_CYCLOM 4 /* cyclom-16y */ + bcopy(ttydefchars, t->c_cc, sizeof t->c_cc); +} -/* FreeBSD's getty doesn't know option for setting RTS/CTS handshake. Its - getty, like a lot of other old cruft, should be replaced with something - which used POSIX tty interfaces which at least allow enabling it. In the - meantime, use the force. */ -#define ALWAYS_RTS_CTS 1 +#define LOTS_OF_EVENTS 64 /* helps separate urgent events from input */ +#define RB_I_HIGH_WATER (TTYHOG - 2 * RS_IBUFSIZE) +#define RS_IBUFSIZE 256 -#if CD1400s_PER_CYCLOM < 4 -#define CD1400_MEMSIZE 0x400 /* 4*256 bytes per chip: cyclom-[48]y */ -#else -#define CD1400_MEMSIZE 0x100 /* 256 bytes per chip: cyclom-16y */ - /* XXX or is it 0x400 like the rest? */ -#define CYCLOM_16 1 /* This is a cyclom-16Y */ -#endif +#define CALLOUT_MASK 0x80 +#define CONTROL_MASK 0x60 +#define CONTROL_INIT_STATE 0x20 +#define CONTROL_LOCK_STATE 0x40 +#define DEV_TO_UNIT(dev) (MINOR_TO_UNIT(minor(dev))) +#define MINOR_MAGIC_MASK (CALLOUT_MASK | CONTROL_MASK) +#define MINOR_TO_UNIT(mynor) ((mynor) & ~MINOR_MAGIC_MASK) -#define PORTS_PER_CYCLOM (CD1400_NO_OF_CHANNELS * CD1400s_PER_CYCLOM) -#define CYCLOM_RESET_16 0x1400 /* cyclom-16y reset */ -#define CYCLOM_CLEAR_INTR 0x1800 /* intr ack address */ -#define CYCLOM_CLOCK 25000000 /* baud rate clock */ +/* + * Input buffer watermarks. + * The external device is asked to stop sending when the buffer exactly reaches + * high water, or when the high level requests it. + * The high level is notified immediately (rather than at a later clock tick) + * when this watermark is reached. + * The buffer size is chosen so the watermark should almost never be reached. + * The low watermark is invisibly 0 since the buffer is always emptied all at + * once. + */ +#define RS_IHIGHWATER (3 * RS_IBUFSIZE / 4) -#define CY_UNITMASK 0x0f -#define CY_CARRIERMASK 0xC0 -#define CY_CARRIERSHIFT 6 +/* + * com state bits. + * (CS_BUSY | CS_TTGO) and (CS_BUSY | CS_TTGO | CS_ODEVREADY) must be higher + * than the other bits so that they can be tested as a group without masking + * off the low bits. + * + * The following com and tty flags correspond closely: + * CS_BUSY = TS_BUSY (maintained by comstart(), siopoll() and + * siostop()) + * CS_TTGO = ~TS_TTSTOP (maintained by comstart() and siostop()) + * CS_CTS_OFLOW = CCTS_OFLOW (maintained by comparam()) + * CS_RTS_IFLOW = CRTS_IFLOW (maintained by comparam()) + * TS_FLUSH is not used. + * XXX I think TIOCSETA doesn't clear TS_TTSTOP when it clears IXON. + * XXX CS_*FLOW should be CF_*FLOW in com->flags (control flags not state). + */ +#define CS_BUSY 0x80 /* output in progress */ +#define CS_TTGO 0x40 /* output not stopped by XOFF */ +#define CS_ODEVREADY 0x20 /* external device h/w ready (CTS) */ +#define CS_CHECKMSR 1 /* check of MSR scheduled */ +#define CS_CTS_OFLOW 2 /* use CTS output flow control */ +#define CS_DTR_OFF 0x10 /* DTR held off */ +#define CS_ODONE 4 /* output completed */ +#define CS_RTS_IFLOW 8 /* use RTS input flow control */ + +static char const * const error_desc[] = { +#define CE_OVERRUN 0 + "silo overflow", +#define CE_INTERRUPT_BUF_OVERFLOW 1 + "interrupt-level buffer overflow", +#define CE_TTY_BUF_OVERFLOW 2 + "tty-level buffer overflow", +}; -#define UNIT(x) (minor(x) & CY_UNITMASK) -#define CARRIER_MODE(x) ((minor(x) & CY_CARRIERMASK) >> CY_CARRIERSHIFT) +#define CE_NTYPES 3 +#define CE_RECORD(com, errnum) (++(com)->delta_error_counts[errnum]) -typedef u_char * volatile cy_addr; +/* types. XXX - should be elsewhere */ +typedef u_char bool_t; /* boolean */ +typedef u_char volatile *cy_addr; -int cyprobe(struct isa_device *dev); -int cyattach(struct isa_device *isdp); -void cystart(struct tty *tp); -int cyparam(struct tty *tp, struct termios *t); -int cyspeed(int speed, int *prescaler_io); -static void cy_channel_init(dev_t dev, int reset); -static void cd1400_channel_cmd(cy_addr base, u_char cmd); +/* queue of linear buffers */ +struct lbq { + u_char *l_head; /* next char to process */ + u_char *l_tail; /* one past the last char to process */ + struct lbq *l_next; /* next in queue */ + bool_t l_queued; /* nonzero if queued */ +}; -/* hsu@clinet.fi: sigh */ -#ifdef __NetBSD__ -#define DELAY(foo) delay(foo) -void delay(int delay); +/* com device structure */ +struct com_s { + u_char state; /* miscellaneous flag bits */ + bool_t active_out; /* nonzero if the callout device is open */ +#if 0 + u_char cfcr_image; /* copy of value written to CFCR */ + u_char ftl; /* current rx fifo trigger level */ + u_char ftl_init; /* ftl_max for next open() */ + u_char ftl_max; /* maximum ftl for curent open() */ + bool_t hasfifo; /* nonzero for 16550 UARTs */ + bool_t loses_outints; /* nonzero if device loses output interrupts */ #endif - -/* Better get rid of this until the core people agree on kernel interfaces. - At least it will then compile on both WhichBSDs. - */ + u_char mcr_image; /* copy of value written to MCR */ #if 0 -extern unsigned int delaycount; /* calibrated 1 ms cpu-spin delay */ +#ifdef COM_MULTIPORT + bool_t multiport; /* is this unit part of a multiport device? */ +#endif /* COM_MULTIPORT */ + bool_t no_irq; /* nonzero if irq is not attached */ + bool_t poll; /* nonzero if polling is required */ + bool_t poll_output; /* nonzero if polling for output is required */ #endif + int unit; /* unit number */ + int dtr_wait; /* time to hold DTR down on close (* 1/hz) */ +#if 0 + u_int tx_fifo_size; +#endif + u_int wopeners; /* # processes waiting for DCD in open() */ -struct isa_driver cydriver = { - cyprobe, cyattach, "cy" -}; + /* + * The high level of the driver never reads status registers directly + * because there would be too many side effects to handle conveniently. + * Instead, it reads copies of the registers stored here by the + * interrupt handler. + */ + u_char last_modem_status; /* last MSR read by intr handler */ + u_char prev_modem_status; /* last MSR handled by high level */ -/* low-level ping-pong buffer structure */ + u_char hotchar; /* ldisc-specific char to be handled ASAP */ + u_char *ibuf; /* start of input buffer */ + u_char *ibufend; /* end of input buffer */ + u_char *ihighwater; /* threshold in input buffer */ + u_char *iptr; /* next free spot in input buffer */ -struct cy_buf { - u_char *next_char; /* location of next char to write */ - u_int free; /* free chars remaining in buffer */ - struct cy_buf *next_buf; /* circular, you know */ - u_char buf[CY_RX_BUF_SIZE]; /* start of the buffer */ -}; + struct lbq obufq; /* head of queue of output buffers */ + struct lbq obufs[2]; /* output buffers */ -/* low-level ring buffer */ + cy_addr cy_iobase; /* base address of this port's cyclom */ + cy_addr iobase; /* base address of this port's cd1400 */ -#ifdef TxBuffer -struct cy_ring { - u_char buf[CY_TX_BUF_SIZE]; - u_char *head; - u_char *tail; /* next pos. to insert char */ - u_char *endish; /* physical end of buf */ - u_int used; /* no. of chars in queue */ -}; + struct tty *tp; /* cross reference */ + + /* Initial state. */ + struct termios it_in; /* should be in struct tty */ + struct termios it_out; + + /* Lock state. */ + struct termios lt_in; /* should be in struct tty */ + struct termios lt_out; + + bool_t do_timestamp; + struct timeval timestamp; + + u_long bytes_in; /* statistics */ + u_long bytes_out; + u_int delta_error_counts[CE_NTYPES]; + u_long error_counts[CE_NTYPES]; + + u_int recv_exception; /* exception chars received */ + u_int mdm; /* modem signal changes */ +#ifdef CyDebug + u_int start_count; /* no. of calls to comstart() */ + u_int start_real; /* no. of calls that did something */ #endif + u_char channel_control;/* CD1400 CCR control command shadow */ + u_char cor[3]; /* CD1400 COR1-3 shadows */ + u_char intr_enable; /* CD1400 SRER shadow */ + /* + * Ping-pong input buffers. The extra factor of 2 in the sizes is + * to allow for an error byte for each input byte. + */ +#define CE_INPUT_OFFSET RS_IBUFSIZE + u_char ibuf1[2 * RS_IBUFSIZE]; + u_char ibuf2[2 * RS_IBUFSIZE]; + + /* + * Data area for output buffers. Someday we should build the output + * buffer queue without copying data. + */ + u_char obuf1[256]; + u_char obuf2[256]; + + struct kern_devconf kdc; +}; /* - * define a structure to keep track of each serial line + * XXX public functions in drivers should be declared in headers produced + * by `config', not here. */ -struct cy { - cy_addr base_addr; /* base address of this port's cd1400 */ - struct tty *tty; - u_int dtrwait; /* time (in ticks) to hold dtr low after close */ - u_int recv_exception; /* exception chars received */ - u_int recv_normal; /* normal chars received */ - u_int xmit; /* chars transmitted */ - u_int mdm; /* modem signal changes */ +/* Interrupt handling entry points. */ +void siointr __P((int unit)); +void siointrts __P((int unit)); +void siopoll __P((void)); + +/* Device switch entry points. */ +int sioopen __P((dev_t dev, int oflags, int devtype, + struct proc *p)); +int sioclose __P((dev_t dev, int fflag, int devtype, + struct proc *p)); +int sioread __P((dev_t dev, struct uio *uio, int ioflag)); +int siowrite __P((dev_t dev, struct uio *uio, int ioflag)); +int sioioctl __P((dev_t dev, int cmd, caddr_t data, + int fflag, struct proc *p)); +void siostop __P((struct tty *tp, int rw)); +#define sioreset noreset +struct tty *siodevtotty __P((dev_t dev)); +#define siommap nommap +#define siostrategy nostrategy + +static int sioattach __P((struct isa_device *dev)); +static void cd1400_channel_cmd __P((cy_addr iobase, int cmd)); +static timeout_t siodtrwakeup; +static void comhardclose __P((struct com_s *com)); +static void siointr1 __P((struct com_s *com)); +static int commctl __P((struct com_s *com, int bits, int how)); +static int comparam __P((struct tty *tp, struct termios *t)); +static int sioprobe __P((struct isa_device *dev)); +static void sioregisterdev __P((struct isa_device *id)); +static void siosettimeout __P((void)); +static int comspeed __P((speed_t speed, int *prescaler_io)); +static void comstart __P((struct tty *tp)); +static timeout_t comwakeup; +static void disc_optim __P((struct tty *tp, struct termios *t, + struct com_s *com)); + #ifdef CyDebug - u_int start_count; /* no. of calls to cystart() */ - u_int start_real; /* no. of calls that did something */ -#endif - u_char carrier_mode; /* hardware carrier handling mode */ - /* - * 0 = always use - * 1 = always use (dialin port) - * 2 = ignore during open, then use it - * 3 = ignore completely - */ - u_char carrier_delta; /* true if carrier has changed state */ - u_char fifo_overrun; /* true if cd1400 receive fifo has... */ - u_char rx_buf_overrun; /* true if low-level buf overflow */ - u_char intr_enable; /* CD1400 SRER shadow */ - u_char modem_sig; /* CD1400 modem signal shadow */ - u_char channel_control;/* CD1400 CCR control command shadow */ - u_char cor[3]; /* CD1400 COR1-3 shadows */ -#ifdef Smarts - u_char spec_char[4]; /* CD1400 SCHR1-4 shadows */ -#endif - struct cy_buf *rx_buf; /* current receive buffer */ - struct cy_buf rx_buf_pool[CY_RX_BUFS];/* receive ping-pong buffers */ -#ifdef TxBuffer - struct cy_ring tx_buf; /* transmit buffer */ +void cystatus __P((int unit)); #endif + +/* table and macro for fast conversion from a unit number to its com struct */ +static struct com_s *p_com_addr[NSIO]; +#define com_addr(unit) (p_com_addr[unit]) + +static struct timeval intr_timestamp; + +struct isa_driver siodriver = { + sioprobe, sioattach, "cy" }; -int cydefaultrate = TTYDEF_SPEED; -cy_addr cyclom_base; /* base address of the card */ -static struct cy *info[NCY*PORTS_PER_CYCLOM]; -#ifdef __FreeBSD__ /* XXX actually only temporarily for 2.1-Development */ -struct tty cy_tty[NCY*PORTS_PER_CYCLOM]; +#ifdef COMCONSOLE +#undef COMCONSOLE +#define COMCONSOLE 1 #else -struct tty *cy_tty[NCY*PORTS_PER_CYCLOM]; +#define COMCONSOLE 0 #endif -static volatile u_char timeout_scheduled = 0; /* true if a timeout has been scheduled */ -#ifdef CyDebug -u_int cy_svrr_probes = 0; /* debugging */ -u_int cy_timeouts = 0; -u_int cy_timeout_req = 0; +#ifndef CONUNIT +#define CONUNIT (0) #endif -/**********************************************************************/ - -int -cyprobe(struct isa_device *dev) -{ - int i, j; - u_char version = 0; /* firmware version */ +static int comconsole = CONUNIT; +static speed_t comdefaultrate = TTYDEF_SPEED; +static u_int com_events; /* input chars + weighted output completions */ +static int commajor; +static int sio_timeout; +static int sio_timeouts_until_log; +#if 0 /* XXX TK2.0 */ +static struct tty *sio_tty[NSIO]; +#else +static struct tty sio_tty[NSIO]; +static int nsio_tty = NSIO; +#endif - /* Cyclom-16Y hardware reset (Cyclom-8Ys don't care) */ - i = *(cy_addr)(dev->id_maddr + CYCLOM_RESET_16); +#ifdef KGDB +#include <machine/remote-sl.h> - DELAY(500); /* wait for the board to get its act together (500 us) */ +extern int kgdb_dev; +extern int kgdb_rate; +extern int kgdb_debug_init; +#endif - for (i = 0; i < CD1400s_PER_CYCLOM; i++) { - cy_addr base = dev->id_maddr + i * CD1400_MEMSIZE; +#ifdef CyDebug +static u_int cd_inbs; +static u_int cy_inbs; +static u_int cd_outbs; +static u_int cy_outbs; +static u_int cy_svrr_probes; +static u_int cy_timeouts; +#endif - /* wait for chip to become ready for new command */ - for (j = 0; j < 100; j += 50) { - DELAY(50); /* wait 50 us */ +static int cy_nr_cd1400s[NCY]; +#undef RxFifoThreshold +static int volatile RxFifoThreshold = (CD1400_RX_FIFO_SIZE / 2); + +static struct kern_devconf kdc_sio[NCY] = { { + 0, 0, 0, /* filled in by dev_attach */ + "cyc", 0, { MDDT_ISA, 0, "tty" }, + isa_generic_externalize, 0, 0, ISA_EXTERNALLEN, + &kdc_isa0, /* parent */ + 0, /* parentdata */ + DC_UNCONFIGURED, /* state */ + "Cyclades multiport board", + DC_CLS_MISC /* just an ordinary device */ +} }; - if (!*(base + CD1400_CCR)) - break; - } +static void +sioregisterdev(id) + struct isa_device *id; +{ + int unit; - /* clear the GFRCR register */ - *(base + CD1400_GFRCR) = 0; + unit = id->id_unit; + if (unit != 0) + kdc_sio[unit] = kdc_sio[0]; + kdc_sio[unit].kdc_unit = unit; + kdc_sio[unit].kdc_isa = id; + dev_attach(&kdc_sio[unit]); +} - /* issue a reset command */ - *(base + CD1400_CCR) = CD1400_CMD_RESET; +static int +sioprobe(dev) + struct isa_device *dev; +{ + int cyu; + u_char firmware_version; + cy_addr iobase; + int unit; - /* wait for the CD1400 to initialise itself */ - for (j = 0; j < 1000; j += 50) { - DELAY(50); /* wait 50 us */ + unit = dev->id_unit; + if ((u_int)unit >= NCY) + return (0); + cy_nr_cd1400s[unit] = 0; + sioregisterdev(dev); - /* retrieve firmware version */ - version = *(base + CD1400_GFRCR); - if (version) - break; - } + /* Cyclom-16Y hardware reset (Cyclom-8Ys don't care) */ + cy_inb((cy_addr)dev->id_maddr, CY16_RESET); /* XXX? */ - /* anything in the 40-4f range is fine */ - if ((version & 0xf0) != 0x40) { - return 0; - } - } + DELAY(500); /* wait for the board to get its act together */ - return 1; /* found */ -} + for (cyu = 0, iobase = (cy_addr)dev->id_maddr; cyu < CY_MAX_CD1400s; + ++cyu, iobase += CY_CD1400_MEMSIZE) { + int i; + /* wait for chip to become ready for new command */ + for (i = 0; i < 100; i += 50) { + DELAY(50); + if (!cd_inb(iobase, CD1400_CCR)) + break; + } -int -cyattach(struct isa_device *isdp) -{ -/* u_char unit = UNIT(isdp->id_unit); */ - int i, j, k; + /* clear the GFRCR register */ + cd_outb(iobase, CD1400_GFRCR, 0); - /* global variable used various routines */ - cyclom_base = (cy_addr)isdp->id_maddr; + /* issue a reset command */ + cd_outb(iobase, CD1400_CCR, + CD1400_CCR_CMDRESET | CD1400_CCR_FULLRESET); - for (i = 0, k = 0; i < CD1400s_PER_CYCLOM; i++) { - cy_addr base = cyclom_base + i * CD1400_MEMSIZE; + /* wait for the CD1400 to initialize itself */ + for (i = 0; i < 1000; i += 50) { + DELAY(50); - /* setup a 1ms clock tick */ - *(base + CD1400_PPR) = CD1400_CLOCK_25_1MS; + /* retrieve firmware version */ + firmware_version = cd_inb(iobase, CD1400_GFRCR); + if (firmware_version != 0) + break; + } - for (j = 0; j < CD1400_NO_OF_CHANNELS; j++, k++) { - struct cy *ip; + /* + * Anything in the 0x40-0x4F range is fine. + * If one CD1400 is bad then we don't support higher + * numbered good ones on this board. + */ + if ((firmware_version & 0xF0) != 0x40) + break; + ++cy_nr_cd1400s[unit]; + } + return (cy_nr_cd1400s[unit] == 0 ? 0 : -1); +} - /* - * grab some space. it'd be more polite to do this in cyopen(), - * but hey. - */ - info[k] = ip = malloc(sizeof(struct cy), M_DEVBUF, M_WAITOK); +static int +sioattach(isdp) + struct isa_device *isdp; +{ + int cyu; + cy_addr cy_iobase; + cy_addr iobase; + int ncyu; + int unit; - /* clear all sorts of junk */ - bzero(ip, sizeof(struct cy)); + unit = isdp->id_unit; + if ((u_int)unit >= NCY) + return (0); + ncyu = cy_nr_cd1400s[unit]; + if (ncyu == 0) + return (0); + isdp->id_ri_flags |= RI_FAST; + + cy_iobase = (cy_addr)isdp->id_maddr; + unit *= CY_MAX_PORTS; + for (cyu = 0, iobase = cy_iobase; cyu < ncyu; + ++cyu, iobase += CY_CD1400_MEMSIZE) { + int cdu; + + /* Set up a receive timeout period of than 1+ ms. */ + cd_outb(iobase, CD1400_PPR, + howmany(CY_CLOCK / CD1400_PPR_PRESCALER, 1000)); + + for (cdu = 0; cdu < CD1400_NO_OF_CHANNELS; ++cdu, ++unit) { + struct com_s *com; + int s; + + com = malloc(sizeof *com, M_DEVBUF, M_NOWAIT); + if (com == NULL) + break; + bzero(com, sizeof *com); + com->unit = unit; + com->dtr_wait = 3 * hz; + com->iptr = com->ibuf = com->ibuf1; + com->ibufend = com->ibuf1 + RS_IBUFSIZE; + com->ihighwater = com->ibuf1 + RS_IHIGHWATER; + com->obufs[0].l_head = com->obuf1; + com->obufs[1].l_head = com->obuf2; + + com->cy_iobase = cy_iobase; + com->iobase = iobase; - ip->base_addr = base; + /* + * We don't use all the flags from <sys/ttydefaults.h> since they + * are only relevant for logins. It's important to have echo off + * initially so that the line doesn't start blathering before the + * echo flag can be turned off. + */ + com->it_in.c_iflag = 0; + com->it_in.c_oflag = 0; + com->it_in.c_cflag = TTYDEF_CFLAG; + com->it_in.c_lflag = 0; + if (unit == comconsole && (COMCONSOLE || boothowto & RB_SERIAL)) { + com->it_in.c_iflag = TTYDEF_IFLAG; + com->it_in.c_oflag = TTYDEF_OFLAG; + com->it_in.c_cflag = TTYDEF_CFLAG | CLOCAL; + com->it_in.c_lflag = TTYDEF_LFLAG; + com->lt_out.c_cflag = com->lt_in.c_cflag = CLOCAL; + } + termioschars(&com->it_in); + com->it_in.c_ispeed = com->it_in.c_ospeed = comdefaultrate; + com->it_out = com->it_in; + + com->kdc = kdc_sio[0]; + com->kdc.kdc_name = "cy"; + com->kdc.kdc_unit = unit; + com->kdc.kdc_isa = isdp; + com->kdc.kdc_parent = &kdc_sio[isdp->id_unit]; + com->kdc.kdc_state = DC_IDLE; + com->kdc.kdc_description = + "Serial port: Cirrus Logic CD1400"; + com->kdc.kdc_class = DC_CLS_SERIAL; + dev_attach(&com->kdc); - /* initialise the channel, without resetting it first */ - cy_channel_init(k, 0); + s = spltty(); + com_addr(unit) = com; + splx(s); + } } - } + kdc_sio[isdp->id_unit].kdc_state = DC_BUSY; /* XXX */ - /* clear interrupts */ - *(cyclom_base + CYCLOM_CLEAR_INTR) = (u_char)0; + /* ensure an edge for the next interrupt */ + cy_outb(cy_iobase, CY_CLEAR_INTR, 0); - return 1; + return (1); } - int -cyopen(dev_t dev, int flag, int mode, struct proc *p) +sioopen(dev, flag, mode, p) + dev_t dev; + int flag; + int mode; + struct proc *p; { - u_int unit = UNIT(dev); - struct cy *infop; - cy_addr base; + struct com_s *com; + int error; + cy_addr iobase; + int mynor; + int s; struct tty *tp; - int error = 0; - u_char carrier; + int unit; - if (unit >= /* NCY * ? */ PORTS_PER_CYCLOM) + mynor = minor(dev); + unit = MINOR_TO_UNIT(mynor); + if ((u_int) unit >= NSIO || (com = com_addr(unit)) == NULL) return (ENXIO); - - infop = info[unit]; - base = infop->base_addr; -#ifdef __FreeBSD__ - infop->tty = &cy_tty[unit]; + if (mynor & CONTROL_MASK) + return (0); +#if 0 /* XXX TK2.0 */ + tp = com->tp = sio_tty[unit] = ttymalloc(sio_tty[unit]); #else - if (!cy_tty[unit]) - infop->tty = cy_tty[unit] = ttymalloc(); -#endif - tp = infop->tty; - - tp->t_oproc = cystart; - tp->t_param = cyparam; - tp->t_dev = dev; - if (!(tp->t_state & TS_ISOPEN)) { - tp->t_state |= TS_WOPEN; - ttychars(tp); - if (tp->t_ispeed == 0) { - tp->t_iflag = TTYDEF_IFLAG; - tp->t_oflag = TTYDEF_OFLAG; - tp->t_cflag = TTYDEF_CFLAG; - tp->t_lflag = TTYDEF_LFLAG; - tp->t_ispeed = tp->t_ospeed = cydefaultrate; + tp = com->tp = &sio_tty[unit]; +#endif + s = spltty(); + /* + * We jump to this label after all non-interrupted sleeps to pick + * up any changes of the device state. + */ +open_top: + while (com->state & CS_DTR_OFF) { + error = tsleep(&com->dtr_wait, TTIPRI | PCATCH, "cydtr", 0); + if (error != 0) + goto out; + } + com->kdc.kdc_state = DC_BUSY; + if (tp->t_state & TS_ISOPEN) { + /* + * The device is open, so everything has been initialized. + * Handle conflicts. + */ + if (mynor & CALLOUT_MASK) { + if (!com->active_out) { + error = EBUSY; + goto out; + } + } else { + if (com->active_out) { + if (flag & O_NONBLOCK) { + error = EBUSY; + goto out; + } + error = tsleep(&com->active_out, + TTIPRI | PCATCH, "cybi", 0); + if (error != 0) + goto out; + goto open_top; + } + } + if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) { + error = EBUSY; + goto out; } + } else { + /* + * The device isn't open, so there are no conflicts. + * Initialize it. Initialization is done twice in many + * cases: to preempt sleeping callin opens if we are + * callout, and to complete a callin open after DCD rises. + */ + tp->t_oproc = comstart; + tp->t_param = comparam; + tp->t_dev = dev; + tp->t_termios = mynor & CALLOUT_MASK + ? com->it_out : com->it_in; +#if 0 + (void)commctl(com, TIOCM_DTR | TIOCM_RTS, DMSET); + com->ftl_max = com->ftl_init; + com->poll = com->no_irq; + com->poll_output = com->loses_outints; +#endif + ++com->wopeners; + iobase = com->iobase; - (void) spltty(); - cy_channel_init(unit, 1); /* reset the hardware */ + /* reset this channel */ + cd_outb(iobase, CD1400_CAR, unit & CD1400_CAR_CHAN); + cd1400_channel_cmd(iobase, CD1400_CCR_CMDRESET); + + /* + * Resetting disables the transmitter and receiver as well as + * flushing the fifos so some of our cached state becomes + * invalid. The documentation suggests that all registers + * for the current channel are reset to defaults, but + * apparently none are. We wouldn't want DTR cleared. + */ + com->channel_control = 0; + + /* Encode per-board unit in LIVR for access in intr routines. */ + cd_outb(iobase, CD1400_LIVR, + (unit & CD1400_xIVR_CHAN) << CD1400_xIVR_CHAN_SHIFT); /* * raise dtr and generally set things up correctly. this * has the side-effect of selecting the appropriate cd1400 * channel, to help us with subsequent channel control stuff */ - cyparam(tp, &tp->t_termios); - - /* check carrier, and set t_state's TS_CARR_ON flag accordingly */ - infop->modem_sig = *(base + CD1400_MSVR); - carrier = infop->modem_sig & CD1400_MSVR_CD; - - if (carrier || (infop->carrier_mode >= 2)) - tp->t_state |= TS_CARR_ON; - else - tp->t_state &=~ TS_CARR_ON; - + error = comparam(tp, &tp->t_termios); + --com->wopeners; + if (error != 0) + goto out; /* - * enable modem & rx interrupts - relies on cyparam() - * having selected the appropriate cd1400 channel + * XXX we should goto open_top if comparam() slept. */ - infop->intr_enable = (1 << 7) | (1 << 4); - *(base + CD1400_SRER) = infop->intr_enable; - ttsetwater(tp); - } else if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) - return (EBUSY); - - if (!(flag & O_NONBLOCK)) - while (!(tp->t_cflag & CLOCAL) && - !(tp->t_state & TS_CARR_ON) && !error) - error = ttysleep(tp, (caddr_t)&tp->t_rawq, - TTIPRI|PCATCH, ttopen, 0); - (void) spl0(); - - if (!error) - error = (*linesw[(u_char)tp->t_line].l_open)(dev, tp); - return (error); -} /* end of cyopen() */ - - -void -cyclose_wakeup(void *arg) -{ - wakeup(arg); -} /* end of cyclose_wakeup() */ +#if 0 + if (com->hasfifo) { + /* + * (Re)enable and drain fifos. + * + * Certain SMC chips cause problems if the fifos + * are enabled while input is ready. Turn off the + * fifo if necessary to clear the input. We test + * the input ready bit after enabling the fifos + * since we've already enabled them in comparam() + * and to handle races between enabling and fresh + * input. + */ + while (TRUE) { + outb(iobase + com_fifo, + FIFO_RCV_RST | FIFO_XMT_RST + | FIFO_ENABLE | com->ftl); + DELAY(100); + if (!(inb(com->line_status_port) & LSR_RXRDY)) + break; + outb(iobase + com_fifo, 0); + DELAY(100); + (void) inb(com->data_port); + } + } + disable_intr(); + (void) inb(com->line_status_port); + (void) inb(com->data_port); + com->prev_modem_status = com->last_modem_status + = inb(com->modem_status_port); + outb(iobase + com_ier, IER_ERXRDY | IER_ETXRDY | IER_ERLS + | IER_EMSC); + enable_intr(); +#else /* !0 */ + /* XXX raise RTS too */ + (void)commctl(com, TIOCM_DTR | TIOCM_RTS, DMSET); + disable_intr(); + com->prev_modem_status = com->last_modem_status + = cd_inb(iobase, CD1400_MSVR2); + cd_outb(iobase, CD1400_SRER, + com->intr_enable + = CD1400_SRER_MDMCH | CD1400_SRER_RXDATA); + enable_intr(); +#endif /* 0 */ + /* + * Handle initial DCD. Callout devices get a fake initial + * DCD (trapdoor DCD). If we are callout, then any sleeping + * callin opens get woken up and resume sleeping on "cybi" + * instead of "cydcd". + */ + /* + * XXX `mynor & CALLOUT_MASK' should be + * `tp->t_cflag & (SOFT_CARRIER | TRAPDOOR_CARRIER) where + * TRAPDOOR_CARRIER is the default initial state for callout + * devices and SOFT_CARRIER is like CLOCAL except it hides + * the true carrier. + */ + if (com->prev_modem_status & MSR_DCD || mynor & CALLOUT_MASK) + (*linesw[tp->t_line].l_modem)(tp, 1); + } + /* + * Wait for DCD if necessary. + */ + if (!(tp->t_state & TS_CARR_ON) && !(mynor & CALLOUT_MASK) + && !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK)) { + ++com->wopeners; + error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "cydcd", 0); + --com->wopeners; + if (error != 0) + goto out; + goto open_top; + } + error = (*linesw[tp->t_line].l_open)(dev, tp); + disc_optim(tp, &tp->t_termios, com); + if (tp->t_state & TS_ISOPEN && mynor & CALLOUT_MASK) + com->active_out = TRUE; + siosettimeout(); +out: + splx(s); + if (!(tp->t_state & TS_ISOPEN) && com->wopeners == 0) + comhardclose(com); + return (error); +} int -cyclose(dev_t dev, int flag, int mode, struct proc *p) +sioclose(dev, flag, mode, p) + dev_t dev; + int flag; + int mode; + struct proc *p; { - u_int unit = UNIT(dev); - struct cy *infop = info[unit]; - struct tty *tp = infop->tty; - cy_addr base = infop->base_addr; + struct com_s *com; + int mynor; int s; + struct tty *tp; - (*linesw[(u_char)tp->t_line].l_close)(tp, flag); - + mynor = minor(dev); + if (mynor & CONTROL_MASK) + return (0); + com = com_addr(MINOR_TO_UNIT(mynor)); + tp = com->tp; s = spltty(); - /* select the appropriate channel on the CD1400 */ - *(base + CD1400_CAR) = (u_char)(unit & 0x03); - - /* disable this channel and lower DTR */ - infop->intr_enable = 0; - *(base + CD1400_SRER) = (u_char)0; /* no intrs */ - *(base + CD1400_DTR) = (u_char)CD1400_DTR_CLEAR; /* no DTR */ - infop->modem_sig &= ~CD1400_MSVR_DTR; - - /* disable receiver (leave transmitter enabled) */ - infop->channel_control = (1 << 4) | (1 << 3) | 1; - cd1400_channel_cmd(base, infop->channel_control); - splx(s); - + (*linesw[tp->t_line].l_close)(tp, flag); + disc_optim(tp, &tp->t_termios, com); + siostop(tp, FREAD | FWRITE); + comhardclose(com); ttyclose(tp); + siosettimeout(); + splx(s); #ifdef broken /* session holds a ref to the tty; can't deallocate */ ttyfree(tp); - infop->tty = cy_tty[unit] = (struct tty *)NULL; + com->tp = sio_tty[unit] = NULL; #endif + return (0); +} - if (infop->dtrwait) { - int error; - - timeout(cyclose_wakeup, (caddr_t)&infop->dtrwait, infop->dtrwait); - do { - error = tsleep((caddr_t)&infop->dtrwait, - TTIPRI|PCATCH, "cyclose", 0); - } while (error == ERESTART); - } +static void +comhardclose(com) + struct com_s *com; +{ + cy_addr iobase; + int s; + struct tty *tp; + int unit; - return 0; -} /* end of cyclose() */ + unit = com->unit; + iobase = com->iobase; + s = spltty(); +#if 0 + com->poll = FALSE; + com->poll_output = FALSE; +#endif + com->do_timestamp = 0; + cd_outb(iobase, CD1400_CAR, unit & CD1400_CAR_CHAN); +#if 0 + outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); +#endif +#ifdef KGDB + /* do not disable interrupts or hang up if debugging */ + if (kgdb_dev != makedev(commajor, unit)) +#endif + { +#if 0 + outb(iobase + com_ier, 0); +#else + disable_intr(); + cd_outb(iobase, CD1400_SRER, com->intr_enable = 0); + enable_intr(); +#endif + tp = com->tp; + if (tp->t_cflag & HUPCL + /* + * XXX we will miss any carrier drop between here and the + * next open. Perhaps we should watch DCD even when the + * port is closed; it is not sufficient to check it at + * the next open because it might go up and down while + * we're not watching. + */ + || !com->active_out + && !(com->prev_modem_status & MSR_DCD) + && !(com->it_in.c_cflag & CLOCAL) + || !(tp->t_state & TS_ISOPEN)) { + (void)commctl(com, TIOCM_DTR, DMBIC); + + /* Disable receiver (leave transmitter enabled). */ + com->channel_control = CD1400_CCR_CMDCHANCTL + | CD1400_CCR_XMTEN + | CD1400_CCR_RCVDIS; + cd1400_channel_cmd(iobase, com->channel_control); + + if (com->dtr_wait != 0) { + timeout(siodtrwakeup, com, com->dtr_wait); + com->state |= CS_DTR_OFF; + } + } + } + com->active_out = FALSE; + wakeup(&com->active_out); + wakeup(TSA_CARR_ON(tp)); /* restart any wopeners */ + if (!(com->state & CS_DTR_OFF) + && !(unit == comconsole && (COMCONSOLE || boothowto & RB_SERIAL))) + com->kdc.kdc_state = DC_IDLE; + splx(s); +} int -cyread(dev_t dev, struct uio *uio, int flag) +sioread(dev, uio, flag) + dev_t dev; + struct uio *uio; + int flag; { - u_int unit = UNIT(dev); - struct tty *tp = info[unit]->tty; - - return (*linesw[(u_char)tp->t_line].l_read)(tp, uio, flag); -} /* end of cyread() */ + int mynor; + struct tty *tp; + mynor = minor(dev); + if (mynor & CONTROL_MASK) + return (ENODEV); + tp = com_addr(MINOR_TO_UNIT(mynor))->tp; + return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); +} int -cywrite(dev_t dev, struct uio *uio, int flag) +siowrite(dev, uio, flag) + dev_t dev; + struct uio *uio; + int flag; { - u_int unit = UNIT(dev); - struct tty *tp = info[unit]->tty; + int mynor; + struct tty *tp; + int unit; + + mynor = minor(dev); + if (mynor & CONTROL_MASK) + return (ENODEV); + unit = MINOR_TO_UNIT(mynor); + tp = com_addr(unit)->tp; + /* + * (XXX) We disallow virtual consoles if the physical console is + * a serial port. This is in case there is a display attached that + * is not the console. In that situation we don't need/want the X + * server taking over the console. + */ + if (constty && unit == comconsole + && (COMCONSOLE || boothowto & RB_SERIAL)) + constty = NULL; #ifdef Smarts /* XXX duplicate ttwrite(), but without so much output processing on * CR & LF chars. Hardly worth the effort, given that high-throughput * sessions are raw anyhow. */ #else - return (*linesw[(u_char)tp->t_line].l_write)(tp, uio, flag); + return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); #endif -} /* end of cywrite() */ - - -#ifdef Smarts -/* standard line discipline input routine */ -int -cyinput(int c, struct tty *tp) -{ - /* XXX duplicate ttyinput(), but without the IXOFF/IXON/ISTRIP/IPARMRK - * bits, as they are done by the CD1400. Hardly worth the effort, - * given that high-throughput sessions are raw anyhow. - */ -} /* end of cyinput() */ -#endif /* Smarts */ - - -inline static void -service_upper_rx(int unit) -{ - struct cy *ip = info[unit]; - struct tty *tp = ip->tty; - struct cy_buf *buf; - int i; - u_char *ch; - - buf = ip->rx_buf; - - /* give service_rx() a new one */ - disable_intr(); /* faster than spltty() */ - ip->rx_buf = buf->next_buf; - enable_intr(); - - if (tp->t_state & TS_ISOPEN) { - ch = buf->buf; - i = buf->next_char - buf->buf; - -#ifdef FastRawInput - /* try to avoid calling the line discipline stuff if we can */ - if ((tp->t_line == 0) && - !(tp->t_iflag & (ICRNL | IMAXBEL | INLCR)) && - !(tp->t_lflag & (ECHO | ECHONL | ICANON | IEXTEN | - ISIG | PENDIN)) && - !(tp->t_state & (TS_CNTTB | TS_LNCH))) { - - i = b_to_q(ch, i, &tp->t_rawq); - if (i) { - /* - * we have no RTS flow control support on cy-8 - * boards, so this is really just tough luck - */ - - log(LOG_WARNING, "cy%d: tty input queue overflow\n", - unit); - } - - ttwakeup(tp); /* notify any readers */ - } - else -#endif /* FastRawInput */ - { - while (i--) - (*linesw[(u_char)tp->t_line].l_rint)((int)*ch++, tp); - } - } - - /* clear the buffer we've just processed */ - buf->next_char = buf->buf; - buf->free = CY_RX_BUF_SIZE; -} /* end of service_upper_rx() */ - +} -#ifdef TxBuffer static void -service_upper_tx(int unit) +siodtrwakeup(chan) + void *chan; { - struct cy *ip = info[unit]; - struct tty *tp = ip->tty; - - tp->t_state &=~ (TS_BUSY|TS_FLUSH); - - if (tp->t_outq.c_cc <= tp->t_lowat) { - if (tp->t_state&TS_ASLEEP) { - tp->t_state &= ~TS_ASLEEP; - wakeup((caddr_t)&tp->t_outq); - } - selwakeup(&tp->t_wsel); - } - - if (tp->t_outq.c_cc > 0) { - struct cy_ring *txq = &ip->tx_buf; - int free_count = CY_TX_BUF_SIZE - ip->tx_buf.used; - u_char *cp = txq->tail; - int count; - int chars_done; - - tp->t_state |= TS_BUSY; - - /* find the largest contig. copy we can do */ - count = ((txq->endish - cp) > free_count) ? - free_count : txq->endish - cp; - - count = ((cp + free_count) > txq->endish) ? - txq->endish - cp : free_count; - - /* copy the first slab */ - chars_done = q_to_b(&tp->t_outq, cp, count); - - /* check for wrap-around time */ - cp += chars_done; - if (cp == txq->endish) - cp = txq->buf; /* back to the start */ - - /* copy anything else, after we've wrapped around */ - if ((chars_done == count) && (count != free_count)) { - /* copy the second slab */ - count = q_to_b(&tp->t_outq, cp, free_count - count); - cp += count; - chars_done += count; - } - - /* - * update queue, protecting ourselves from any rampant - * lower-layers - */ - disable_intr(); - txq->tail = cp; - txq->used += chars_done; - enable_intr(); - } - - if (!tp->t_outq.c_cc) - tp->t_state &=~ TS_BUSY; -} /* end of service_upper_tx() */ -#endif /* TxBuffer */ + struct com_s *com; + com = (struct com_s *)chan; + com->state &= ~CS_DTR_OFF; + if (!(com->unit == comconsole && (COMCONSOLE || boothowto & RB_SERIAL))) + com->kdc.kdc_state = DC_IDLE; + wakeup(&com->dtr_wait); +} -inline static void -service_upper_mdm(int unit) +/* Interrupt routine for timekeeping purposes */ +void +siointrts(unit) + int unit; { - struct cy *ip = info[unit]; - - if (ip->carrier_delta) { - int carrier = ip->modem_sig & CD1400_MSVR_CD; - struct tty *tp = ip->tty; - - if (!(*linesw[(u_char)tp->t_line].l_modem)(tp, carrier)) { - cy_addr base = ip->base_addr; - - /* clear DTR */ - disable_intr(); - *(base + CD1400_CAR) = (u_char)(unit & 0x03); - *(base + CD1400_DTR) = (u_char)CD1400_DTR_CLEAR; - ip->modem_sig &= ~CD1400_MSVR_DTR; - ip->carrier_delta = 0; - enable_intr(); - } - else { - disable_intr(); - ip->carrier_delta = 0; - enable_intr(); - } - } -} /* end of service_upper_mdm() */ + /* + * XXX microtime() reenables CPU interrupts. We can't afford to + * be interrupted and don't want to slow down microtime(), so lock + * out interrupts in another way. + */ + outb(IO_ICU1 + 1, 0xff); + microtime(&intr_timestamp); + disable_intr(); + outb(IO_ICU1 + 1, imen); + siointr(unit); +} -/* upper level character processing routine */ void -cytimeout(void *ptr) -{ +siointr(unit) int unit; - - timeout_scheduled = 0; - +{ + int baseu; + cy_addr cy_iobase; + int cyu; + cy_addr iobase; + u_char status; + + baseu = unit * CY_MAX_PORTS; + cy_iobase = com_addr(baseu)->cy_iobase; + + /* check each CD1400 in turn */ + for (cyu = 0, iobase = cy_iobase; cyu < cy_nr_cd1400s[unit]; + ++cyu, iobase += CY_CD1400_MEMSIZE) { + /* poll to see if it has any work */ + status = cd_inb(iobase, CD1400_SVRR); + if (status == 0) + continue; #ifdef CyDebug - cy_timeouts++; + ++cy_svrr_probes; #endif - - /* check each port in turn */ - for (unit = 0; unit < NCY*PORTS_PER_CYCLOM; unit++) { - struct cy *ip = info[unit]; -#ifndef TxBuffer - struct tty *tp = ip->tty; + /* service requests as appropriate, giving priority to RX */ + if (status & CD1400_SVRR_RXRDY) { + struct com_s *com; + u_int count; + u_char *ioptr; + u_char line_status; + u_char recv_data; + u_char serv_type; +#ifdef PollMode + u_char save_car; + u_char save_rir; #endif - /* ignore anything that is not open */ - if (!ip->tty) - continue; +#ifdef PollMode + save_rir = cd_inb(iobase, CD1400_RIR); + save_car = cd_inb(iobase, CD1400_CAR); - /* - * any received chars to handle? (doesn't matter if intr routine - * kicks in while we're testing this) - */ - if (ip->rx_buf->free != CY_RX_BUF_SIZE) - service_upper_rx(unit); + /* enter rx service */ + cd_outb(iobase, CD1400_CAR, save_rir); -#ifdef TxBuffer - /* anything to add to the transmit buffer (low-water mark)? */ - if (ip->tx_buf.used < CY_TX_BUF_SIZE/2) - service_upper_tx(unit); + serv_type = cd_inb(iobase, CD1400_RIVR); + com = com_addr(baseu + + ((serv_type >> CD1400_xIVR_CHAN_SHIFT) + & CD1400_xIVR_CHAN)); #else - if (tp->t_outq.c_cc <= tp->t_lowat) { - if (tp->t_state&TS_ASLEEP) { - tp->t_state &= ~TS_ASLEEP; - wakeup((caddr_t)&tp->t_outq); - } - selwakeup(&tp->t_wsel); - } -#endif - - /* anything modem signals altered? */ - service_upper_mdm(unit); - - /* any overruns to log? */ -#ifdef LogOverruns - if (ip->fifo_overrun) { - /* - * turn off the alarm - not important enough to bother - * with interrupt protection. - */ - ip->fifo_overrun = 0; + /* ack receive service */ + serv_type = cy_inb(iobase, CY8_SVCACKR); - log(LOG_WARNING, "cy%d: receive fifo overrun\n", unit); - } + com = com_addr(baseu + + + ((serv_type >> CD1400_xIVR_CHAN_SHIFT) + & CD1400_xIVR_CHAN)); #endif - if (ip->rx_buf_overrun) { - /* - * turn off the alarm - not important enough to bother - * with interrupt protection. - */ - ip->rx_buf_overrun = 0; - - log(LOG_WARNING, "cy%d: receive buffer full\n", unit); - } - } -} /* cytimeout() */ - -inline static void -schedule_upper_service(void) -{ -#ifdef CyDebug - cy_timeout_req++; -#endif - - if (!timeout_scheduled) { - timeout(cytimeout, (caddr_t)0, 1); /* call next tick */ - timeout_scheduled = 1; - } -} /* end of schedule_upper_service() */ - - -/* initialise a channel on the cyclom board */ - -static void -cy_channel_init(dev_t dev, int reset) -{ - u_int unit = UNIT(dev); - int carrier_mode = CARRIER_MODE(dev); - struct cy *ip = info[unit]; - cy_addr base = ip->base_addr; - struct tty *tp = ip->tty; - struct cy_buf *buf, *next_buf; - int i; -#ifndef PollMode - u_char cd1400_unit; -#endif - - /* clear the structure and refill it */ - bzero(ip, sizeof(struct cy)); - ip->base_addr = base; - ip->tty = tp; - ip->carrier_mode = carrier_mode; - - /* select channel of the CD1400 */ - *(base + CD1400_CAR) = (u_char)(unit & 0x03); - - if (reset) - cd1400_channel_cmd(base, 0x80); /* reset the channel */ - - /* set LIVR to 0 - intr routines depend on this */ - *(base + CD1400_LIVR) = 0; - -#ifndef PollMode - /* set top four bits of {R,T,M}ICR to the cd1400 - * number, cd1400_unit - */ - cd1400_unit = unit / CD1400_NO_OF_CHANNELS; - *(base + CD1400_RICR) = (u_char)(cd1400_unit << 4); - *(base + CD1400_TICR) = (u_char)(cd1400_unit << 4); - *(base + CD1400_MICR) = (u_char)(cd1400_unit << 4); + if (com->do_timestamp) + /* XXX a little bloat here... */ + com->timestamp = intr_timestamp; + + if (serv_type & CD1400_RIVR_EXCEPTION) { + ++com->recv_exception; + line_status = cd_inb(iobase, CD1400_RDSR); + /* break/unnattached error bits or real input? */ + recv_data = cd_inb(iobase, CD1400_RDSR); +#ifndef SOFT_HOTCHAR + if (line_status & CD1400_RDSR_SPECIAL + && com->hotchar != 0) + setsofttty(); #endif - - ip->dtrwait = hz/4; /* quarter of a second */ - - /* setup low-level buffers */ - i = CY_RX_BUFS; - ip->rx_buf = next_buf = &ip->rx_buf_pool[0]; - while (i--) { - buf = &ip->rx_buf_pool[i]; - - buf->next_char = buf->buf; /* first char to use */ - buf->free = CY_RX_BUF_SIZE; /* i.e. empty */ - buf->next_buf = next_buf; /* where to go next */ - next_buf = buf; - } - -#ifdef TxBuffer - ip->tx_buf.endish = ip->tx_buf.buf + CY_TX_BUF_SIZE; - - /* clear the low-level tx buffer */ - ip->tx_buf.head = ip->tx_buf.tail = ip->tx_buf.buf; - ip->tx_buf.used = 0; -#endif - - /* clear the low-level rx buffer */ - ip->rx_buf->next_char = ip->rx_buf->buf; /* first char to use */ - ip->rx_buf->free = CY_RX_BUF_SIZE; /* completely empty */ -} /* end of cy_channel_init() */ - - -/* service a receive interrupt */ -inline static void -service_rx(int cd, caddr_t base) -{ - struct cy *infop; - unsigned count; - int ch; - u_char serv_type, channel; -#ifdef PollMode - u_char save_rir, save_car; +#if 1 /* XXX "intelligent" PFO error handling would break O error handling */ + if (line_status & (LSR_PE|LSR_FE|LSR_BI)) { + /* + Don't store PE if IGNPAR and BI if IGNBRK, + this hack allows "raw" tty optimization + works even if IGN* is set. + */ + if ( com->tp == NULL + || !(com->tp->t_state & TS_ISOPEN) + || (line_status & (LSR_PE|LSR_FE)) + && (com->tp->t_iflag & IGNPAR) + || (line_status & LSR_BI) + && (com->tp->t_iflag & IGNBRK)) + goto cont; + if ( (line_status & (LSR_PE|LSR_FE)) + && (com->tp->t_state & TS_CAN_BYPASS_L_RINT) + && ((line_status & LSR_FE) + || (line_status & LSR_PE) + && (com->tp->t_iflag & INPCK))) + recv_data = 0; + } +#endif /* 1 */ + ++com->bytes_in; +#ifdef SOFT_HOTCHAR + if (com->hotchar != 0 && recv_data == com->hotchar) + setsofttty(); #endif - - /* setup */ -#ifdef PollMode - save_rir = *(base + CD1400_RIR); - channel = cd * CD1400_NO_OF_CHANNELS + (save_rir & 0x3); - save_car = *(base + CD1400_CAR); - *(base + CD1400_CAR) = save_rir; /* enter modem service */ - serv_type = *(base + CD1400_RIVR); + ioptr = com->iptr; + if (ioptr >= com->ibufend) + CE_RECORD(com, CE_INTERRUPT_BUF_OVERFLOW); + else { + ++com_events; + ioptr[0] = recv_data; + ioptr[CE_INPUT_OFFSET] = line_status; + com->iptr = ++ioptr; + if (ioptr == com->ihighwater + && com->state & CS_RTS_IFLOW) +#if 0 + outb(com->modem_ctl_port, + com->mcr_image &= ~MCR_RTS); #else - serv_type = *(base + CD1400_SVCACKR); /* ack receive service */ - channel = ((u_char)*(base + CD1400_RICR)) >> 2; /* get cyclom channel # */ - -#ifdef CyDebug - if (channel >= PORTS_PER_CYCLOM) { - printf("cy: service_rx - channel %02x\n", channel); - panic("cy: service_rx - bad channel"); - } + cd_outb(iobase, CD1400_MSVR1, + com->mcr_image &= ~MCR_RTS); #endif + if (line_status & LSR_OE) + CE_RECORD(com, CE_OVERRUN); + } + goto cont; + } else { + int ifree; + + count = cd_inb(iobase, CD1400_RDCR); + com->bytes_in += count; + ioptr = com->iptr; + ifree = com->ibufend - ioptr; + if (count > ifree) { + count -= ifree; + com_events += ifree; + while (ifree-- != 0) { + recv_data = cd_inb(iobase, CD1400_RDSR); +#ifdef SOFT_HOTCHAR + if (com->hotchar != 0 + && recv_data == com->hotchar) + setsofttty(); #endif - - infop = info[channel]; - - /* read those chars */ - if (serv_type & CD1400_RIVR_EXCEPTION) { - /* read the exception status */ - u_char status = *(base + CD1400_RDSR); - - /* XXX is it a break? Do something if it is! */ - - /* XXX is IGNPAR not set? Store a null in the buffer. */ - -#ifdef LogOverruns - if (status & CD1400_RDSR_OVERRUN) { + ioptr[0] = recv_data; + ioptr[CE_INPUT_OFFSET] = 0; + ++ioptr; + } + com->delta_error_counts + [CE_INTERRUPT_BUF_OVERFLOW] += count; + do { + recv_data = cd_inb(iobase, CD1400_RDSR); +#ifdef SOFT_HOTCHAR + if (com->hotchar != 0 + && recv_data == com->hotchar) + setsofttty(); +#endif + } while (--count != 0); + } else { + if (ioptr <= com->ihighwater + && ioptr + count > com->ihighwater + && com->state & CS_RTS_IFLOW) #if 0 - ch |= TTY_PE; /* for SLIP */ + outb(com->modem_ctl_port, + com->mcr_image &= ~MCR_RTS); +#else + cd_outb(iobase, CD1400_MSVR1, + com->mcr_image &= ~MCR_RTS); #endif - infop->fifo_overrun++; - } + com_events += count; + do { + recv_data = cd_inb(iobase, CD1400_RDSR); +#ifdef SOFT_HOTCHAR + if (com->hotchar != 0 + && recv_data == com->hotchar) + setsofttty(); #endif - infop->recv_exception++; - } - else { - struct cy_buf *buf = infop->rx_buf; - - count = (u_char)*(base + CD1400_RDCR); /* how many to read? */ - infop->recv_normal += count; - if (buf->free < count) { - infop->rx_buf_overrun += count; - - /* read & discard everything */ - while (count--) - ch = (u_char)*(base + CD1400_RDSR); - } - else { - /* slurp it into our low-level buffer */ - buf->free -= count; - while (count--) { - ch = (u_char)*(base + CD1400_RDSR); /* read the char */ - *(buf->next_char++) = ch; + ioptr[0] = recv_data; + ioptr[CE_INPUT_OFFSET] = 0; + ++ioptr; + } while (--count != 0); } + com->iptr = ioptr; } - } +cont: + /* terminate service context */ #ifdef PollMode - *(base + CD1400_RIR) = (u_char)(save_rir & 0x3f); /* terminate service context */ + cd_outb(iobase, CD1400_RIR, + save_rir + & ~(CD1400_RIR_RDIREQ | CD1400_RIR_RBUSY)); + cd_outb(iobase, CD1400_CAR, save_car); #else - *(base + CD1400_EOSRR) = (u_char)0; /* terminate service context */ + cd_outb(iobase, CD1400_EOSRR, 0); #endif -} /* end of service_rx */ - - -/* service a transmit interrupt */ -inline static void -service_tx(int cd, caddr_t base) -{ - struct cy *ip; -#ifdef TxBuffer - struct cy_ring *txq; -#else - struct tty *tp; -#endif - u_char channel; + } + if (status & CD1400_SVRR_MDMCH) { + struct com_s *com; + u_char modem_status; #ifdef PollMode - u_char save_tir, save_car; + u_char save_car; + u_char save_mir; #else - u_char vector; + u_char vector; #endif - /* setup */ #ifdef PollMode - save_tir = *(base + CD1400_TIR); - channel = cd * CD1400_NO_OF_CHANNELS + (save_tir & 0x3); - save_car = *(base + CD1400_CAR); - *(base + CD1400_CAR) = save_tir; /* enter tx service */ -#else - vector = *(base + CD1400_SVCACKT); /* ack transmit service */ - channel = ((u_char)*(base + CD1400_TICR)) >> 2; /* get cyclom channel # */ - -#ifdef CyDebug - if (channel >= PORTS_PER_CYCLOM) { - printf("cy: service_tx - channel %02x\n", channel); - panic("cy: service_tx - bad channel"); - } -#endif -#endif - - ip = info[channel]; -#ifdef TxBuffer - txq = &ip->tx_buf; + save_mir = cd_inb(iobase, CD1400_MIR); + save_car = cd_inb(iobase, CD1400_CAR); - if (txq->used > 0) { - cy_addr base = ip->base_addr; - int count = min(CD1400_FIFOSIZE, txq->used); - int chars_done = count; - u_char *cp = txq->head; - u_char *buf_end = txq->endish; + /* enter modem service */ + cd_outb(iobase, CD1400_CAR, save_mir); - /* ip->state |= CY_BUSY; */ - while (count--) { - *(base + CD1400_TDR) = *cp++; - if (cp >= buf_end) - cp = txq->buf; - }; - txq->head = cp; - txq->used -= chars_done; /* important that this is atomic */ - ip->xmit += chars_done; - } - - /* - * disable tx intrs if no more chars to send. we re-enable - * them in cystart() - */ - if (!txq->used) { - ip->intr_enable &=~ (1 << 2); - *(base + CD1400_SRER) = ip->intr_enable; - /* ip->state &= ~CY_BUSY; */ - } + com = com_addr(baseu + cyu * CD1400_NO_OF_CHANNELS + + (save_mir & CD1400_MIR_CHAN)); #else - tp = ip->tty; - - if (!(tp->t_state & TS_TTSTOP) && (tp->t_outq.c_cc > 0)) { - cy_addr base = ip->base_addr; - int count = min(CD1400_FIFOSIZE, tp->t_outq.c_cc); + /* ack modem service */ + vector = cy_inb(iobase, CY8_SVCACKM); - ip->xmit += count; - tp->t_state |= TS_BUSY; - while (count--) - *(base + CD1400_TDR) = getc(&tp->t_outq); - } + com = com_addr(baseu + + ((vector >> CD1400_xIVR_CHAN_SHIFT) + & CD1400_xIVR_CHAN)); +#endif + ++com->mdm; + modem_status = cd_inb(iobase, CD1400_MSVR2); + if (modem_status != com->last_modem_status) { + /* + * Schedule high level to handle DCD changes. Note + * that we don't use the delta bits anywhere. Some + * UARTs mess them up, and it's easy to remember the + * previous bits and calculate the delta. + */ + com->last_modem_status = modem_status; + if (!(com->state & CS_CHECKMSR)) { + com_events += LOTS_OF_EVENTS; + com->state |= CS_CHECKMSR; + setsofttty(); + } - /* - * disable tx intrs if no more chars to send. we re-enable them - * in cystart() - */ - if (!tp->t_outq.c_cc) { - ip->intr_enable &=~ (1 << 2); - *(base + CD1400_SRER) = ip->intr_enable; - tp->t_state &= ~TS_BUSY; - } +#ifdef SOFT_CTS_OFLOW + /* handle CTS change immediately for crisp flow ctl */ + if (com->state & CS_CTS_OFLOW) { + if (modem_status & MSR_CTS) { + com->state |= CS_ODEVREADY; + if (com->state >= (CS_BUSY | CS_TTGO + | CS_ODEVREADY) + && !(com->intr_enable + & CD1400_SRER_TXRDY)) + cd_outb(iobase, CD1400_SRER, + com->intr_enable + |= CD1400_SRER_TXRDY); + } else { + com->state &= ~CS_ODEVREADY; + if (com->intr_enable & CD1400_SRER_TXRDY) + cd_outb(iobase, CD1400_SRER, + com->intr_enable + &= ~CD1400_SRER_TXRDY); + } + } #endif + } + /* terminate service context */ #ifdef PollMode - *(base + CD1400_TIR) = (u_char)(save_tir & 0x3f); /* terminate service context */ + cd_outb(iobase, CD1400_MIR, + save_mir + & ~(CD1400_MIR_RDIREQ | CD1400_MIR_RBUSY)); + cd_outb(iobase, CD1400_CAR, save_car); #else - *(base + CD1400_EOSRR) = (u_char)0; /* terminate service context */ + cd_outb(iobase, CD1400_EOSRR, 0); #endif -} /* end of service_tx */ - - -/* service a modem status interrupt */ -inline static void -service_mdm(int cd, caddr_t base) -{ - struct cy *infop; - u_char channel, deltas; + } + if (status & CD1400_SVRR_TXRDY) { + struct com_s *com; #ifdef PollMode - u_char save_mir, save_car; + u_char save_car; + u_char save_tir; #else - u_char vector; + u_char vector; #endif - /* setup */ #ifdef PollMode - save_mir = *(base + CD1400_MIR); - channel = cd * CD1400_NO_OF_CHANNELS + (save_mir & 0x3); - save_car = *(base + CD1400_CAR); - *(base + CD1400_CAR) = save_mir; /* enter modem service */ + save_tir = cd_inb(iobase, CD1400_TIR); + save_car = cd_inb(iobase, CD1400_CAR); + + /* enter tx service */ + cd_outb(iobase, CD1400_CAR, save_tir); + com = com_addr(baseu + + cyu * CD1400_NO_OF_CHANNELS + + (save_tir & CD1400_TIR_CHAN)); #else - vector = *(base + CD1400_SVCACKM); /* ack modem service */ - channel = ((u_char)*(base + CD1400_MICR)) >> 2; /* get cyclom channel # */ + /* ack transmit service */ + vector = cy_inb(iobase, CY8_SVCACKT); -#ifdef CyDebug - if (channel >= PORTS_PER_CYCLOM) { - printf("cy: service_mdm - channel %02x\n", channel); - panic("cy: service_mdm - bad channel"); - } + com = com_addr(baseu + + ((vector >> CD1400_xIVR_CHAN_SHIFT) + & CD1400_xIVR_CHAN)); #endif -#endif - - infop = info[channel]; - - /* read the siggies and see what's changed */ - infop->modem_sig = (u_char)*(base + CD1400_MSVR); - deltas = (u_char)*(base + CD1400_MISR); - if ((infop->carrier_mode <= 2) && (deltas & CD1400_MISR_CDd)) - /* something for the upper layer to deal with */ - infop->carrier_delta = 1; - - infop->mdm++; + if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) { + u_char *ioptr; + u_int ocount; + + ioptr = com->obufq.l_head; + ocount = com->obufq.l_tail - ioptr; + if (ocount > CD1400_TX_FIFO_SIZE) + ocount = CD1400_TX_FIFO_SIZE; + com->bytes_out += ocount; + do + cd_outb(iobase, CD1400_TDR, *ioptr++); + while (--ocount != 0); + com->obufq.l_head = ioptr; + if (ioptr >= com->obufq.l_tail) { + struct lbq *qp; + + qp = com->obufq.l_next; + qp->l_queued = FALSE; + qp = qp->l_next; + if (qp != NULL) { + com->obufq.l_head = qp->l_head; + com->obufq.l_tail = qp->l_tail; + com->obufq.l_next = qp; + } else { + /* output just completed */ + com->state &= ~CS_BUSY; + cd_outb(iobase, CD1400_SRER, + com->intr_enable + &= ~CD1400_SRER_TXRDY); + } + if (!(com->state & CS_ODONE)) { + com_events += LOTS_OF_EVENTS; + com->state |= CS_ODONE; + setsofttty(); /* handle at high level ASAP */ + } + } + } - /* terminate service context */ + /* terminate service context */ #ifdef PollMode - *(base + CD1400_MIR) = (u_char)(save_mir & 0x3f); + cd_outb(iobase, CD1400_TIR, + save_tir + & ~(CD1400_TIR_RDIREQ | CD1400_TIR_RBUSY)); + cd_outb(iobase, CD1400_CAR, save_car); #else - *(base + CD1400_EOSRR) = (u_char)0; + cd_outb(iobase, CD1400_EOSRR, 0); #endif -} /* end of service_mdm */ - - -int -cyintr(int unit) -{ - int cd; - u_char status; - - /* check each CD1400 in turn */ - for (cd = 0; cd < CD1400s_PER_CYCLOM; cd++) { - cy_addr base = cyclom_base + cd*CD1400_MEMSIZE; - - /* poll to see if it has any work */ - while (status = (u_char)*(base + CD1400_SVRR)) { -#ifdef CyDebug - cy_svrr_probes++; -#endif - /* service requests as appropriate, giving priority to RX */ - if (status & CD1400_SVRR_RX) - service_rx(cd, base); - if (status & CD1400_SVRR_TX) - service_tx(cd, base); - if (status & CD1400_SVRR_MDM) - service_mdm(cd, base); + } } - } - /* request upper level service to deal with whatever happened */ - schedule_upper_service(); + /* ensure an edge for the next interrupt */ + cy_outb(cy_iobase, CY_CLEAR_INTR, 0); - /* re-enable interrupts on the cyclom */ - *(cyclom_base + CYCLOM_CLEAR_INTR) = (u_char)0; - - return 1; + schedsofttty(); } +static void +siointr1(com) + struct com_s *com; +{ +} int -cyioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p) +sioioctl(dev, cmd, data, flag, p) + dev_t dev; + int cmd; + caddr_t data; + int flag; + struct proc *p; { - int unit = UNIT(dev); - struct cy *infop = info[unit]; - struct tty *tp = infop->tty; + struct com_s *com; int error; + cy_addr iobase; + int mynor; + int s; + struct tty *tp; +#if defined(COMPAT_43) || defined(COMPAT_SUNOS) + int oldcmd; + struct termios term; +#endif - error = (*linesw[(u_char)tp->t_line].l_ioctl)(tp, cmd, data, flag, p); - if (error >= 0) + mynor = minor(dev); + com = com_addr(MINOR_TO_UNIT(mynor)); + iobase = com->iobase; + if (mynor & CONTROL_MASK) { + struct termios *ct; + + switch (mynor & CONTROL_MASK) { + case CONTROL_INIT_STATE: + ct = mynor & CALLOUT_MASK ? &com->it_out : &com->it_in; + break; + case CONTROL_LOCK_STATE: + ct = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in; + break; + default: + return (ENODEV); /* /dev/nodev */ + } + switch (cmd) { + case TIOCSETA: + error = suser(p->p_ucred, &p->p_acflag); + if (error != 0) + return (error); + *ct = *(struct termios *)data; + return (0); + case TIOCGETA: + *(struct termios *)data = *ct; + return (0); + case TIOCGETD: + *(int *)data = TTYDISC; + return (0); + case TIOCGWINSZ: + bzero(data, sizeof(struct winsize)); + return (0); + default: + return (ENOTTY); + } + } + tp = com->tp; +#if defined(COMPAT_43) || defined(COMPAT_SUNOS) + term = tp->t_termios; + oldcmd = cmd; + error = ttsetcompat(tp, &cmd, data, &term); + if (error != 0) return (error); - error = ttioctl(tp, cmd, data, flag -#ifdef NetBSD - , p + if (cmd != oldcmd) + data = (caddr_t)&term; #endif - ); + if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { + int cc; + struct termios *dt = (struct termios *)data; + struct termios *lt = mynor & CALLOUT_MASK + ? &com->lt_out : &com->lt_in; + + dt->c_iflag = (tp->t_iflag & lt->c_iflag) + | (dt->c_iflag & ~lt->c_iflag); + dt->c_oflag = (tp->t_oflag & lt->c_oflag) + | (dt->c_oflag & ~lt->c_oflag); + dt->c_cflag = (tp->t_cflag & lt->c_cflag) + | (dt->c_cflag & ~lt->c_cflag); + dt->c_lflag = (tp->t_lflag & lt->c_lflag) + | (dt->c_lflag & ~lt->c_lflag); + for (cc = 0; cc < NCCS; ++cc) + if (lt->c_cc[cc] != 0) + dt->c_cc[cc] = tp->t_cc[cc]; + if (lt->c_ispeed != 0) + dt->c_ispeed = tp->t_ispeed; + if (lt->c_ospeed != 0) + dt->c_ospeed = tp->t_ospeed; + } + error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); if (error >= 0) return (error); - + s = spltty(); + error = ttioctl(tp, cmd, data, flag); + disc_optim(tp, &tp->t_termios, com); + if (error >= 0) { + splx(s); + return (error); + } + cd_outb(iobase, CD1400_CAR, MINOR_TO_UNIT(mynor) & CD1400_CAR_CHAN); switch (cmd) { -#ifdef notyet /* sigh - more junk to do XXX */ +#if 0 case TIOCSBRK: + outb(iobase + com_cfcr, com->cfcr_image |= CFCR_SBREAK); break; case TIOCCBRK: + outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); break; +#endif /* 0 */ case TIOCSDTR: + (void)commctl(com, TIOCM_DTR, DMBIS); break; case TIOCCDTR: + (void)commctl(com, TIOCM_DTR, DMBIC); break; - case TIOCMSET: + (void)commctl(com, *(int *)data, DMSET); break; case TIOCMBIS: + (void)commctl(com, *(int *)data, DMBIS); break; case TIOCMBIC: + (void)commctl(com, *(int *)data, DMBIC); break; -#endif /* notyet */ - - case TIOCMGET: { - int bits = 0; - u_char status = infop->modem_sig; - - if (status & CD1400_MSVR_DTR) bits |= TIOCM_DTR | TIOCM_RTS; - if (status & CD1400_MSVR_CD) bits |= TIOCM_CD; - if (status & CD1400_MSVR_CTS) bits |= TIOCM_CTS; - if (status & CD1400_MSVR_DSR) bits |= TIOCM_DSR; -#ifdef CYCLOM_16 - if (status & CD1400_MSVR_RI) bits |= TIOCM_RI; -#endif - if (infop->channel_control & 0x02) bits |= TIOCM_LE; - *(int *)data = bits; + case TIOCMGET: + *(int *)data = commctl(com, 0, DMGET); break; - } - -#ifdef TIOCMSBIDIR - case TIOCMSBIDIR: - return (ENOTTY); -#endif /* TIOCMSBIDIR */ - -#ifdef TIOCMGBIDIR - case TIOCMGBIDIR: - return (ENOTTY); -#endif /* TIOCMGBIDIR */ - -#ifdef TIOCMSDTRWAIT case TIOCMSDTRWAIT: - /* must be root to set dtr delay */ - if (p->p_ucred->cr_uid != 0) - return(EPERM); - - infop->dtrwait = *(u_int *)data; - break; -#endif /* TIOCMSDTRWAIT */ - -#ifdef TIOCMGDTRWAIT + /* must be root since the wait applies to following logins */ + error = suser(p->p_ucred, &p->p_acflag); + if (error != 0) { + splx(s); + return (error); + } + com->dtr_wait = *(int *)data * hz / 100; + break; case TIOCMGDTRWAIT: - *(u_int *)data = infop->dtrwait; + *(int *)data = com->dtr_wait * 100 / hz; + break; + case TIOCTIMESTAMP: + com->do_timestamp = TRUE; + *(struct timeval *)data = com->timestamp; break; -#endif /* TIOCMGDTRWAIT */ - default: + splx(s); return (ENOTTY); } + splx(s); + return (0); +} - return 0; -} /* end of cyioctl() */ - - -int -cyparam(struct tty *tp, struct termios *t) +void +siopoll() { - u_char unit = UNIT(tp->t_dev); - struct cy *infop = info[unit]; - cy_addr base = infop->base_addr; - int cflag = t->c_cflag; - int iflag = t->c_iflag; - int ispeed, ospeed; - int itimeout; - int iprescaler, oprescaler; - int s; - u_char cor_change = 0; - u_char opt; - - if (!t->c_ispeed) - t->c_ispeed = t->c_ospeed; - - s = spltty(); - - /* select the appropriate channel on the CD1400 */ - *(base + CD1400_CAR) = unit & 0x03; - - /* handle DTR drop on speed == 0 trick */ - if (t->c_ospeed == 0) { - *(base + CD1400_DTR) = CD1400_DTR_CLEAR; - infop->modem_sig &= ~CD1400_MSVR_DTR; - } - else { - *(base + CD1400_DTR) = CD1400_DTR_SET; - infop->modem_sig |= CD1400_MSVR_DTR; - } + int unit; - /* set baud rates if they've changed from last time */ - - if ((ospeed = cyspeed(t->c_ospeed, &oprescaler)) < 0) - return EINVAL; - *(base + CD1400_TBPR) = (u_char)ospeed; - *(base + CD1400_TCOR) = (u_char)oprescaler; - - if ((ispeed = cyspeed(t->c_ispeed, &iprescaler)) < 0) - return EINVAL; - *(base + CD1400_RBPR) = (u_char)ispeed; - *(base + CD1400_RCOR) = (u_char)iprescaler; +#ifdef CyDebug + ++cy_timeouts; +#endif + if (com_events == 0) + return; +repeat: + for (unit = 0; unit < NSIO; ++unit) { + u_char *buf; + struct com_s *com; + u_char *ibuf; + cy_addr iobase; + int incc; + struct tty *tp; + + com = com_addr(unit); + if (com == NULL) + continue; + tp = com->tp; + if (tp == NULL) { + /* + * XXX forget any events related to closed devices + * (actually never opened devices) so that we don't + * loop. + */ + disable_intr(); + incc = com->iptr - com->ibuf; + com->iptr = com->ibuf; + if (com->state & CS_CHECKMSR) { + incc += LOTS_OF_EVENTS; + com->state &= ~CS_CHECKMSR; + } + com_events -= incc; + enable_intr(); + if (incc != 0) + log(LOG_DEBUG, + "sio%d: %d events for device with no tp\n", + unit, incc); + continue; + } - /* - * set receive time-out period - * generate a rx interrupt if no new chars are received in - * this many ticks - * don't bother comparing old & new VMIN, VTIME and ispeed - it - * can't be much worse just to calculate and set it each time! - * certainly less hassle. :-) - */ + /* switch the role of the low-level input buffers */ + if (com->iptr == (ibuf = com->ibuf)) { + buf = NULL; /* not used, but compiler can't tell */ + incc = 0; + } else { + buf = ibuf; + disable_intr(); + incc = com->iptr - buf; + com_events -= incc; + if (ibuf == com->ibuf1) + ibuf = com->ibuf2; + else + ibuf = com->ibuf1; + com->ibufend = ibuf + RS_IBUFSIZE; + com->ihighwater = ibuf + RS_IHIGHWATER; + com->iptr = ibuf; - /* - * calculate minimum timeout period: - * 5 ms or the time it takes to receive 1 char, rounded up to the - * next ms, whichever is greater - */ - if (t->c_ispeed > 0) { - itimeout = (t->c_ispeed > 2200) ? 5 : (10000/t->c_ispeed + 1); + /* + * There is now room for another low-level buffer full + * of input, so enable RTS if it is now disabled and + * there is room in the high-level buffer. + */ + /* + * XXX this used not to look at CS_RTS_IFLOW. The + * change is to allow full control of MCR_RTS via + * ioctls after turning CS_RTS_IFLOW off. Check + * for races. We shouldn't allow the ioctls while + * CS_RTS_IFLOW is on. + */ + if ((com->state & CS_RTS_IFLOW) + && !(com->mcr_image & MCR_RTS) + && !(tp->t_state & TS_TBLOCK)) +#if 0 + outb(com->modem_ctl_port, + com->mcr_image |= MCR_RTS); +#else + iobase = com->iobase, + cd_outb(iobase, CD1400_CAR, + unit & CD1400_CAR_CHAN), + cd_outb(iobase, CD1400_MSVR1, + com->mcr_image |= MCR_RTS); +#endif + enable_intr(); + com->ibuf = ibuf; + } - /* if we're using VTIME as an inter-char timeout, and it is set to - * be longer than the minimum calculated above, go for it - */ - if (t->c_cc[VMIN] && t->c_cc[VTIME] && t->c_cc[VTIME]*10 > itimeout) - itimeout = t->c_cc[VTIME]*10; + if (com->state & CS_CHECKMSR) { + u_char delta_modem_status; - /* store it, taking care not to overflow the byte-sized register */ - *(base + CD1400_RTPR) = (u_char)((itimeout <= 255) ? itimeout : 255); + disable_intr(); + delta_modem_status = com->last_modem_status + ^ com->prev_modem_status; + com->prev_modem_status = com->last_modem_status; + com_events -= LOTS_OF_EVENTS; + com->state &= ~CS_CHECKMSR; + enable_intr(); + if (delta_modem_status & MSR_DCD) + (*linesw[tp->t_line].l_modem) + (tp, com->prev_modem_status & MSR_DCD); + } + if (com->state & CS_ODONE) { + disable_intr(); + com_events -= LOTS_OF_EVENTS; + com->state &= ~CS_ODONE; + if (!(com->state & CS_BUSY)) + com->tp->t_state &= ~TS_BUSY; + enable_intr(); + (*linesw[tp->t_line].l_start)(tp); + } + if (incc <= 0 || !(tp->t_state & TS_ISOPEN)) + continue; + /* + * XXX only do this when we bypass ttyinput. + */ + if (tp->t_rawq.c_cc + incc >= RB_I_HIGH_WATER + && (com->state & CS_RTS_IFLOW || tp->t_iflag & IXOFF) + && !(tp->t_state & TS_TBLOCK) + /* + * XXX - need flow control for all line disciplines. + * Only have it in standard one now. + */ + && linesw[tp->t_line].l_rint == ttyinput) { + int putc_status = 0; + + if ((tp->t_iflag & IXOFF + && tp->t_cc[VSTOP] != _POSIX_VDISABLE + && (putc_status = putc(tp->t_cc[VSTOP], + &tp->t_outq)) == 0) + || com->state & CS_RTS_IFLOW) { + tp->t_state |= TS_TBLOCK; + ttstart(tp); + if (putc_status != 0) + /* Try again later. */ + tp->t_state &= ~TS_TBLOCK; + } + } + /* + * Avoid the grotesquely inefficient lineswitch routine + * (ttyinput) in "raw" mode. It usually takes about 450 + * instructions (that's without canonical processing or echo!). + * slinput is reasonably fast (usually 40 instructions plus + * call overhead). + */ + if (tp->t_state & TS_CAN_BYPASS_L_RINT) { + tk_nin += incc; + tk_rawcc += incc; + tp->t_rawcc += incc; + com->delta_error_counts[CE_TTY_BUF_OVERFLOW] + += b_to_q((char *)buf, incc, &tp->t_rawq); + ttwakeup(tp); + if (tp->t_state & TS_TTSTOP + && (tp->t_iflag & IXANY + || tp->t_cc[VSTART] == tp->t_cc[VSTOP])) { + tp->t_state &= ~TS_TTSTOP; + tp->t_lflag &= ~FLUSHO; + ttstart(tp); + } + } else { + do { + u_char line_status; + int recv_data; + + line_status = (u_char) buf[CE_INPUT_OFFSET]; + recv_data = (u_char) *buf++; + if (line_status + & (LSR_BI | LSR_FE | LSR_OE | LSR_PE)) { + if (line_status & LSR_BI) + recv_data |= TTY_BI; + if (line_status & LSR_FE) + recv_data |= TTY_FE; + if (line_status & LSR_OE) + recv_data |= TTY_OE; + if (line_status & LSR_PE) + recv_data |= TTY_PE; + } + (*linesw[tp->t_line].l_rint)(recv_data, tp); + } while (--incc > 0); + } + if (com_events == 0) + break; } + if (com_events >= LOTS_OF_EVENTS) + goto repeat; +} +static int +comparam(tp, t) + struct tty *tp; + struct termios *t; +{ + int bits; + int cflag; + struct com_s *com; + u_char cor_change; + int idivisor; + int iflag; + cy_addr iobase; + int iprescaler; + int itimeout; + int odivisor; + int oprescaler; + u_char opt; + int s; + int unit; + + /* do historical conversions */ + if (t->c_ispeed == 0) + t->c_ispeed = t->c_ospeed; + + /* check requested parameters */ + idivisor = comspeed(t->c_ispeed, &iprescaler); + if (idivisor < 0) + return (EINVAL); + odivisor = comspeed(t->c_ospeed, &oprescaler); + if (odivisor < 0) + return (EINVAL); + + /* parameters are OK, convert them to the com struct and the device */ + unit = DEV_TO_UNIT(tp->t_dev); + com = com_addr(unit); + iobase = com->iobase; + s = spltty(); + cd_outb(iobase, CD1400_CAR, unit & CD1400_CAR_CHAN); + if (odivisor == 0) + (void)commctl(com, TIOCM_DTR, DMBIC); /* hang up line */ + else + (void)commctl(com, TIOCM_DTR, DMBIS); + + cd_outb(iobase, CD1400_RBPR, idivisor); + cd_outb(iobase, CD1400_RCOR, iprescaler); + cd_outb(iobase, CD1400_TBPR, odivisor); + cd_outb(iobase, CD1400_TCOR, oprescaler); /* * channel control * receiver enable * transmitter enable (always set) */ - opt = (1 << 4) | (1 << 3) | ((cflag & CREAD) ? (1 << 1) : 1); - if (opt != infop->channel_control) { - infop->channel_control = opt; - cd1400_channel_cmd(base, opt); + cflag = t->c_cflag; + opt = CD1400_CCR_CMDCHANCTL | CD1400_CCR_XMTEN + | (cflag & CREAD ? CD1400_CCR_RCVEN : CD1400_CCR_RCVDIS); + if (opt != com->channel_control) { + com->channel_control = opt; + cd1400_channel_cmd(iobase, opt); } #ifdef Smarts /* set special chars */ - if (t->c_cc[VSTOP] != _POSIX_VDISABLE && - (t->c_cc[VSTOP] != infop->spec_char[0])) { - *(base + CD1400_SCHR1) = infop->spec_char[0] = t->c_cc[VSTOP]; - } - if (t->c_cc[VSTART] != _POSIX_VDISABLE && - (t->c_cc[VSTART] != infop->spec_char[1])) { - *(base + CD1400_SCHR2) = infop->spec_char[0] = t->c_cc[VSTART]; - } - if (t->c_cc[VINTR] != _POSIX_VDISABLE && - (t->c_cc[VINTR] != infop->spec_char[2])) { - *(base + CD1400_SCHR3) = infop->spec_char[0] = t->c_cc[VINTR]; - } - if (t->c_cc[VSUSP] != _POSIX_VDISABLE && - (t->c_cc[VSUSP] != infop->spec_char[3])) { - *(base + CD1400_SCHR4) = infop->spec_char[0] = t->c_cc[VSUSP]; - } + /* XXX if one is _POSIX_VDISABLE, can't use some others */ + if (t->c_cc[VSTOP] != _POSIX_VDISABLE) + cd_outb(iobase, CD1400_SCHR1, t->c_cc[VSTOP]); + if (t->c_cc[VSTART] != _POSIX_VDISABLE) + cd_outb(iobase, CD1400_SCHR2, t->c_cc[VSTART]); + if (t->c_cc[VINTR] != _POSIX_VDISABLE) + cd_outb(iobase, CD1400_SCHR3, t->c_cc[VINTR]); + if (t->c_cc[VSUSP] != _POSIX_VDISABLE) + cd_outb(iobase, CD1400_SCHR4, t->c_cc[VSUSP]); #endif /* @@ -1294,41 +1772,84 @@ cyparam(struct tty *tp, struct termios *t) opt = 0; /* parity */ if (cflag & PARENB) { - if (cflag & PARODD) - opt |= 1 << 7; - opt |= 2 << 5; /* normal parity mode */ + if (cflag & PARODD) + opt |= CD1400_COR1_PARODD; + opt |= CD1400_COR1_PARNORMAL; } + iflag = t->c_iflag; if (!(iflag & INPCK)) - opt |= 1 << 4; /* ignore parity */ + opt |= CD1400_COR1_NOINPCK; + bits = 1 + 1; /* stop bits */ - if (cflag & CSTOPB) - opt |= 2 << 2; + if (cflag & CSTOPB) { + ++bits; + opt |= CD1400_COR1_STOP2; + } /* char length */ - opt |= (cflag & CSIZE) >> 8; /* nasty, but fast */ - if (opt != infop->cor[0]) { - cor_change |= 1 << 1; - *(base + CD1400_COR1) = opt; + switch (cflag & CSIZE) { + case CS5: + bits += 5; + opt |= CD1400_COR1_CS5; + break; + case CS6: + bits += 6; + opt |= CD1400_COR1_CS6; + break; + case CS7: + bits += 7; + opt |= CD1400_COR1_CS7; + break; + default: + bits += 8; + opt |= CD1400_COR1_CS8; + break; + } + cor_change = 0; + if (opt != com->cor[0]) { + cor_change |= CD1400_CCR_COR1; + cd_outb(iobase, CD1400_COR1, com->cor[0] = opt); } /* + * Set receive time-out period, normally to max(one char time, 5 ms). + */ + if (t->c_ispeed == 0) + itimeout = cd_inb(iobase, CD1400_RTPR); + else { + itimeout = (1000 * bits + t->c_ispeed - 1) / t->c_ispeed; +#ifdef SOFT_HOTCHAR +#define MIN_RTP 1 +#else +#define MIN_RTP 5 +#endif + if (itimeout < MIN_RTP) + itimeout = MIN_RTP; + } + if (!(t->c_lflag & ICANON) && t->c_cc[VMIN] != 0 && t->c_cc[VTIME] != 0 + && t->c_cc[VTIME] * 10 > itimeout) + itimeout = t->c_cc[VTIME] * 10; + if (itimeout > 255) + itimeout = 255; + cd_outb(iobase, CD1400_RTPR, itimeout); + + /* * set channel option register 2 - * flow control */ opt = 0; #ifdef Smarts if (iflag & IXANY) - opt |= 1 << 7; /* auto output restart on any char after XOFF */ + opt |= CD1400_COR2_IXANY; if (iflag & IXOFF) - opt |= 1 << 6; /* auto XOFF output flow-control */ + opt |= CD1400_COR2_IXOFF; #endif -#ifndef ALWAYS_RTS_CTS +#ifndef SOFT_CTS_OFLOW if (cflag & CCTS_OFLOW) + opt |= CD1400_COR2_CCTS_OFLOW; #endif - opt |= 1 << 1; /* auto CTS flow-control */ - - if (opt != infop->cor[1]) { - cor_change |= 1 << 2; - *(base + CD1400_COR2) = opt; + if (opt != com->cor[1]) { + cor_change |= CD1400_CCR_COR2; + cd_outb(iobase, CD1400_COR2, com->cor[1] = opt); } /* @@ -1336,24 +1857,22 @@ cyparam(struct tty *tp, struct termios *t) * receiver FIFO interrupt threshold * flow control */ - opt = RxFifoThreshold; /* rx fifo threshold */ + opt = RxFifoThreshold; #ifdef Smarts if (t->c_lflag & ICANON) - opt |= 1 << 6; /* detect INTR & SUSP chars */ + opt |= CD1400_COR3_SCD34; /* detect INTR & SUSP chars */ if (iflag & IXOFF) - opt |= (1 << 5) | (1 << 4); /* transparent in-band flow control */ + /* detect and transparently handle START and STOP chars */ + opt |= CD1400_COR3_FCT | CD1400_COR3_SCD12; #endif - if (opt != infop->cor[2]) { - cor_change |= 1 << 3; - *(base + CD1400_COR3) = opt; + if (opt != com->cor[2]) { + cor_change |= CD1400_CCR_COR3; + cd_outb(iobase, CD1400_COR3, com->cor[2] = opt); } - /* notify the CD1400 if COR1-3 have changed */ - if (cor_change) { - cor_change |= 1 << 6; /* COR change flag */ - cd1400_channel_cmd(base, cor_change); - } + if (cor_change) + cd1400_channel_cmd(iobase, CD1400_CCR_CMDCORCHG | cor_change); /* * set channel option register 4 - @@ -1363,7 +1882,7 @@ cyparam(struct tty *tp, struct termios *t) */ opt = 0; if (iflag & IGNCR) - opt |= 1 << 7; + opt |= CD1400_COR4_IGNCR; #ifdef Smarts /* * we need a new ttyinput() for this, as we don't want to @@ -1371,305 +1890,712 @@ cyparam(struct tty *tp, struct termios *t) * synchronisation problems */ if (iflag & ICRNL) - opt |= 1 << 6; + opt |= CD1400_COR4_ICRNL; if (iflag & INLCR) - opt |= 1 << 5; + opt |= CD1400_COR4_INLCR; #endif if (iflag & IGNBRK) - opt |= 1 << 4; + opt |= CD1400_COR4_IGNBRK; if (!(iflag & BRKINT)) - opt |= 1 << 3; + opt |= CD1400_COR4_NOBRKINT; +#if 0 + /* XXX using this "intelligence" breaks reporting of overruns. */ if (iflag & IGNPAR) -#ifdef LogOverruns - opt |= 0; /* broken chars cause receive exceptions */ -#else - opt |= 2; /* discard broken chars */ -#endif + opt |= CD1400_COR4_PFO_DISCARD; else { - if (iflag & PARMRK) - opt |= 4; /* precede broken chars with 0xff 0x0 */ - else -#ifdef LogOverruns - opt |= 0; /* broken chars cause receive exceptions */ + if (iflag & PARMRK) + opt |= CD1400_COR4_PFO_ESC; + else + opt |= CD1400_COR4_PFO_NUL; + } #else - opt |= 3; /* convert framing/parity errs to nulls */ + opt |= CD1400_COR4_PFO_EXCEPTION; #endif - } - *(base + CD1400_COR4) = opt; + cd_outb(iobase, CD1400_COR4, opt); /* * set channel option register 5 - */ opt = 0; if (iflag & ISTRIP) - opt |= 1 << 7; - if (t->c_iflag & IEXTEN) { - opt |= 1 << 6; /* enable LNEXT (e.g. ctrl-v quoting) handling */ - } + opt |= CD1400_COR5_ISTRIP; + if (t->c_iflag & IEXTEN) + /* enable LNEXT (e.g. ctrl-v quoting) handling */ + opt |= CD1400_COR5_LNEXT; #ifdef Smarts if (t->c_oflag & ONLCR) - opt |= 1 << 1; + opt |= CD1400_COR5_ONLCR; if (t->c_oflag & OCRNL) - opt |= 1; + opt |= CD1400_COR5_OCRNL; #endif - *(base + CD1400_COR5) = opt; + cd_outb(iobase, CD1400_COR5, opt); /* + * XXX we probably alway want to track carrier changes, so that + * TS_CARR_ON gives the true carrier. If we don't track them, + * then we should set TS_CARR_ON when CLOCAL drops. + */ + /* * set modem change option register 1 * generate modem interrupts on which 1 -> 0 input transitions * also controls auto-DTR output flow-control, which we don't use */ - opt = (cflag & CLOCAL) ? 0 : 1 << 4; /* CD */ - *(base + CD1400_MCOR1) = opt; + opt = cflag & CLOCAL ? 0 : CD1400_MCOR1_CDzd; +#ifdef SOFT_CTS_OFLOW + if (cflag & CCTS_OFLOW) + opt |= CD1400_MCOR1_CTSzd; +#endif + cd_outb(iobase, CD1400_MCOR1, opt); /* * set modem change option register 2 * generate modem interrupts on specific 0 -> 1 input transitions */ - opt = (cflag & CLOCAL) ? 0 : 1 << 4; /* CD */ - *(base + CD1400_MCOR2) = opt; + opt = cflag & CLOCAL ? 0 : CD1400_MCOR2_CDod; +#ifdef SOFT_CTS_OFLOW + if (cflag & CCTS_OFLOW) + opt |= CD1400_MCOR2_CTSod; +#endif + cd_outb(iobase, CD1400_MCOR2, opt); - splx(s); + /* + * XXX should have done this long ago, but there is too much state + * to change all atomically. + */ + disable_intr(); + + com->state &= ~CS_TTGO; + if (!(tp->t_state & TS_TTSTOP)) + com->state |= CS_TTGO; + if (cflag & CRTS_IFLOW) + com->state |= CS_RTS_IFLOW; /* XXX - secondary changes? */ + else + com->state &= ~CS_RTS_IFLOW; - return 0; -} /* end of cyparam */ + /* + * Set up state to handle output flow control. + * XXX - worth handling MDMBUF (DCD) flow control at the lowest level? + * Now has 10+ msec latency, while CTS flow has 50- usec latency. + */ + com->state |= CS_ODEVREADY; +#ifdef SOFT_CTS_OFLOW + com->state &= ~CS_CTS_OFLOW; + if (cflag & CCTS_OFLOW) { + com->state |= CS_CTS_OFLOW; + if (!(com->last_modem_status & MSR_CTS)) + com->state &= ~CS_ODEVREADY; + } +#endif + /* XXX shouldn't call functions while intrs are disabled. */ + disc_optim(tp, t, com); +#if 0 + /* + * Recover from fiddling with CS_TTGO. We used to call siointr1() + * unconditionally, but that defeated the careful discarding of + * stale input in sioopen(). + */ + if (com->state >= (CS_BUSY | CS_TTGO)) + siointr1(com); +#endif + if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) { + if (!(com->intr_enable & CD1400_SRER_TXRDY)) + cd_outb(iobase, CD1400_SRER, + com->intr_enable |= CD1400_SRER_TXRDY); + } else { + if (com->intr_enable & CD1400_SRER_TXRDY) + cd_outb(iobase, CD1400_SRER, + com->intr_enable &= ~CD1400_SRER_TXRDY); + } + enable_intr(); + splx(s); + return (0); +} -void -cystart(struct tty *tp) +static void +comstart(tp) + struct tty *tp; { - u_char unit = UNIT(tp->t_dev); - struct cy *infop = info[unit]; - cy_addr base = infop->base_addr; + struct com_s *com; + cy_addr iobase; int s; +#ifdef CyDebug + bool_t started; +#endif + int unit; + + unit = DEV_TO_UNIT(tp->t_dev); + com = com_addr(unit); + iobase = com->iobase; + s = spltty(); #ifdef CyDebug - infop->start_count++; + ++com->start_count; + started = FALSE; #endif - /* check the flow-control situation */ - if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) + disable_intr(); + cd_outb(iobase, CD1400_CAR, unit & CD1400_CAR_CHAN); + if (tp->t_state & TS_TTSTOP) { + com->state &= ~CS_TTGO; + if (com->intr_enable & CD1400_SRER_TXRDY) + cd_outb(iobase, CD1400_SRER, + com->intr_enable &= ~CD1400_SRER_TXRDY); + } else { + com->state |= CS_TTGO; + if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY) + && !(com->intr_enable & CD1400_SRER_TXRDY)) + cd_outb(iobase, CD1400_SRER, + com->intr_enable |= CD1400_SRER_TXRDY); + } + if (tp->t_state & TS_TBLOCK) { + if (com->mcr_image & MCR_RTS && com->state & CS_RTS_IFLOW) +#if 0 + outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); +#else + cd_outb(iobase, CD1400_MSVR1, + com->mcr_image &= ~MCR_RTS); +#endif + } else { + /* + * XXX don't raise MCR_RTS if CTS_RTS_IFLOW is off. Set it + * appropriately in comparam() if RTS-flow is being changed. + * Check for races. + */ + if (!(com->mcr_image & MCR_RTS) && com->iptr < com->ihighwater) +#if 0 + outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); +#else + cd_outb(iobase, CD1400_MSVR1, + com->mcr_image |= MCR_RTS); +#endif + } + enable_intr(); + if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) { + splx(s); return; + } + if (tp->t_outq.c_cc != 0) { + struct lbq *qp; + struct lbq *next; + if (!com->obufs[0].l_queued) { +#ifdef CyDebug + started = TRUE; +#endif + com->obufs[0].l_tail + = com->obuf1 + q_to_b(&tp->t_outq, com->obuf1, + sizeof com->obuf1); + com->obufs[0].l_next = NULL; + com->obufs[0].l_queued = TRUE; + disable_intr(); + if (com->state & CS_BUSY) { + qp = com->obufq.l_next; + while ((next = qp->l_next) != NULL) + qp = next; + qp->l_next = &com->obufs[0]; + } else { + com->obufq.l_head = com->obufs[0].l_head; + com->obufq.l_tail = com->obufs[0].l_tail; + com->obufq.l_next = &com->obufs[0]; + com->state |= CS_BUSY; + if (com->state >= (CS_BUSY | CS_TTGO + | CS_ODEVREADY)) + cd_outb(iobase, CD1400_SRER, + com->intr_enable + |= CD1400_SRER_TXRDY); + } + enable_intr(); + } + if (tp->t_outq.c_cc != 0 && !com->obufs[1].l_queued) { +#ifdef CyDebug + started = TRUE; +#endif + com->obufs[1].l_tail + = com->obuf2 + q_to_b(&tp->t_outq, com->obuf2, + sizeof com->obuf2); + com->obufs[1].l_next = NULL; + com->obufs[1].l_queued = TRUE; + disable_intr(); + if (com->state & CS_BUSY) { + qp = com->obufq.l_next; + while ((next = qp->l_next) != NULL) + qp = next; + qp->l_next = &com->obufs[1]; + } else { + com->obufq.l_head = com->obufs[1].l_head; + com->obufq.l_tail = com->obufs[1].l_tail; + com->obufq.l_next = &com->obufs[1]; + com->state |= CS_BUSY; + if (com->state >= (CS_BUSY | CS_TTGO + | CS_ODEVREADY)) + cd_outb(iobase, CD1400_SRER, + com->intr_enable + |= CD1400_SRER_TXRDY); + } + enable_intr(); + } + tp->t_state |= TS_BUSY; + } +#ifdef CyDebug + if (started) + ++com->start_real; +#endif +#if 0 + disable_intr(); + if (com->state >= (CS_BUSY | CS_TTGO)) { + siointr1(com); /* fake interrupt to start output */ + enable_intr(); +#endif + +#if 0 /* XXX TK2.0 */ + if (tp->t_state & (TS_SO_OCOMPLETE | TS_SO_OLOWAT) || tp->t_wsel) + ttwwakeup(tp); +#else if (tp->t_outq.c_cc <= tp->t_lowat) { - if (tp->t_state&TS_ASLEEP) { + if (tp->t_state & TS_ASLEEP) { tp->t_state &= ~TS_ASLEEP; - wakeup((caddr_t)&tp->t_outq); + wakeup(TSA_OLOWAT(tp)); } selwakeup(&tp->t_wsel); } - -#ifdef TxBuffer - service_upper_tx(unit); /* feed the monster */ #endif - s = spltty(); + splx(s); +} - if (!(infop->intr_enable & (1 << 2))) { - /* select the channel */ - *(base + CD1400_CAR) = unit & (u_char)3; +void +siostop(tp, rw) + struct tty *tp; + int rw; +{ + struct com_s *com; + cy_addr iobase; + int unit; + + unit = DEV_TO_UNIT(tp->t_dev); + com = com_addr(unit); + iobase = com->iobase; + disable_intr(); + if (rw & FWRITE) { + com->obufs[0].l_queued = FALSE; + com->obufs[1].l_queued = FALSE; + if (com->state & CS_ODONE) + com_events -= LOTS_OF_EVENTS; + com->state &= ~(CS_ODONE | CS_BUSY); + com->tp->t_state &= ~TS_BUSY; + } + if (rw & FREAD) { + com_events -= (com->iptr - com->ibuf); + com->iptr = com->ibuf; + } + if (tp->t_state & TS_TTSTOP) { + com->state &= ~CS_TTGO; + if (com->intr_enable & CD1400_SRER_TXRDY) { + cd_outb(iobase, CD1400_CAR, unit & CD1400_CAR_CHAN); + cd_outb(iobase, CD1400_SRER, + com->intr_enable &= ~CD1400_SRER_TXRDY); + } + } else { + com->state |= CS_TTGO; + if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY) + && !(com->intr_enable & CD1400_SRER_TXRDY)) { + cd_outb(iobase, CD1400_CAR, unit & CD1400_CAR_CHAN); + cd_outb(iobase, CD1400_SRER, + com->intr_enable |= CD1400_SRER_TXRDY); + } + } + enable_intr(); - /* (re)enable interrupts to set things in motion */ - infop->intr_enable |= (1 << 2); - *(base + CD1400_SRER) = infop->intr_enable; + /* XXX should clear h/w fifos too. */ +} + +struct tty * +siodevtotty(dev) + dev_t dev; +{ + int mynor; + int unit; - infop->start_real++; + mynor = minor(dev); + if (mynor & CONTROL_MASK) + return (NULL); + unit = MINOR_TO_UNIT(mynor); + if ((u_int) unit >= NSIO) + return (NULL); + return (&sio_tty[unit]); +} + +static int +commctl(com, bits, how) + struct com_s *com; + int bits; + int how; +{ + cy_addr iobase; + int mcr; + int msr; + + if (how == DMGET) { + if (com->channel_control & CD1400_CCR_RCVEN) + bits |= TIOCM_LE; + mcr = com->mcr_image; + if (mcr & MCR_DTR) + bits |= TIOCM_DTR; + if (mcr & MCR_RTS) + /* XXX wired on for Cyclom-8Ys */ + bits |= TIOCM_RTS; + msr = com->prev_modem_status; + if (msr & MSR_CTS) + bits |= TIOCM_CTS; + if (msr & MSR_DCD) + bits |= TIOCM_CD; + if (msr & MSR_DSR) + bits |= TIOCM_DSR; + if (msr & MSR_RI) + /* XXX not connected except for Cyclom-16Y? */ + bits |= TIOCM_RI; + return (bits); } + iobase = com->iobase; + mcr = 0; + if (bits & TIOCM_DTR) + mcr |= MCR_DTR; + if (bits & TIOCM_RTS) + mcr |= MCR_RTS; + disable_intr(); + switch (how) { + case DMSET: + com->mcr_image = mcr; + cd_outb(iobase, CD1400_MSVR1, mcr); + cd_outb(iobase, CD1400_MSVR2, mcr); + break; + case DMBIS: + com->mcr_image = mcr = com->mcr_image | mcr; + cd_outb(iobase, CD1400_MSVR1, mcr); + cd_outb(iobase, CD1400_MSVR2, mcr); + break; + case DMBIC: + com->mcr_image = mcr = com->mcr_image & ~mcr; + cd_outb(iobase, CD1400_MSVR1, mcr); + cd_outb(iobase, CD1400_MSVR2, mcr); + break; + } + enable_intr(); + return (0); +} - splx(s); -} /* end of cystart() */ +static void +siosettimeout() +{ + struct com_s *com; + bool_t someopen; + int unit; + /* + * Set our timeout period to 1 second if no polled devices are open. + * Otherwise set it to max(1/200, 1/hz). + * Enable timeouts iff some device is open. + */ + untimeout(comwakeup, (void *)NULL); + sio_timeout = hz; + someopen = FALSE; + for (unit = 0; unit < NSIO; ++unit) { + com = com_addr(unit); + if (com != NULL && com->tp != NULL + && com->tp->t_state & TS_ISOPEN) { + someopen = TRUE; +#if 0 + if (com->poll || com->poll_output) { + sio_timeout = hz > 200 ? hz / 200 : 1; + break; + } +#endif + } + } + if (someopen) { + sio_timeouts_until_log = hz / sio_timeout; + timeout(comwakeup, (void *)NULL, sio_timeout); + } else { + /* Flush error messages, if any. */ + sio_timeouts_until_log = 1; + comwakeup((void *)NULL); + untimeout(comwakeup, (void *)NULL); + } +} -int -cystop(struct tty *tp, int flag) +static void +comwakeup(chan) + void *chan; { - u_char unit = UNIT(tp->t_dev); - struct cy *ip = info[unit]; - cy_addr base = ip->base_addr; - int s; + struct com_s *com; + int unit; - s = spltty(); + timeout(comwakeup, (void *)NULL, sio_timeout); - /* select the channel */ - *(base + CD1400_CAR) = unit & 3; +#if 0 + /* + * Recover from lost output interrupts. + * Poll any lines that don't use interrupts. + */ + for (unit = 0; unit < NSIO; ++unit) { + com = com_addr(unit); + if (com != NULL + && (com->state >= (CS_BUSY | CS_TTGO) || com->poll)) { + disable_intr(); + siointr1(com); + enable_intr(); + } + } +#endif - /* halt output by disabling transmit interrupts */ - ip->intr_enable &=~ (1 << 2); - *(base + CD1400_SRER) = ip->intr_enable; + /* + * Check for and log errors, but not too often. + */ + if (--sio_timeouts_until_log > 0) + return; + sio_timeouts_until_log = hz / sio_timeout; + for (unit = 0; unit < NSIO; ++unit) { + int errnum; - splx(s); + com = com_addr(unit); + if (com == NULL) + continue; + for (errnum = 0; errnum < CE_NTYPES; ++errnum) { + u_int delta; + u_long total; - return 0; + disable_intr(); + delta = com->delta_error_counts[errnum]; + com->delta_error_counts[errnum] = 0; + enable_intr(); + if (delta == 0) + continue; + total = com->error_counts[errnum] += delta; + log(LOG_ERR, "cy%d: %u more %s%s (total %lu)\n", + unit, delta, error_desc[errnum], + delta == 1 ? "" : "s", total); +#if 0 + /* + * XXX if we resurrect this then we should move + * the dropping of the ftl to somewhere with less + * latency. + */ + if (errnum == CE_OVERRUN && com->hasfifo + && com->ftl > FIFO_TRIGGER_1) { + static u_char ftl_in_bytes[] = + { 1, 4, 8, 14, }; + + com->ftl_init = FIFO_TRIGGER_8; +#define FIFO_TRIGGER_DELTA FIFO_TRIGGER_4 + com->ftl_max = + com->ftl -= FIFO_TRIGGER_DELTA; + outb(com->iobase + com_fifo, + FIFO_ENABLE | com->ftl); + log(LOG_DEBUG, + "sio%d: reduced fifo trigger level to %d\n", + unit, + ftl_in_bytes[com->ftl + / FIFO_TRIGGER_DELTA]); + } +#endif + } + } } -struct tty * -cydevtotty(dev_t dev) +static void +disc_optim(tp, t, com) + struct tty *tp; + struct termios *t; + struct com_s *com; { - u_char unit = UNIT(dev); - - if (unit >= /* NCY * ? */ PORTS_PER_CYCLOM) - return NULL; +#ifndef SOFT_HOTCHAR + cy_addr iobase; + u_char opt; +#endif - return info[unit]->tty; + /* + * XXX can skip a lot more cases if Smarts. Maybe + * (IGNCR | ISTRIP | IXOFF | IXON) in c_iflag. But perhaps we + * shouldn't skip if (TS_CNTTB | TS_LNCH) is set in t_state. + */ + if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP + | IXOFF | IXON)) + && (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK)) + && (!(t->c_iflag & PARMRK) || + (t->c_iflag & (IGNPAR|IGNBRK)) == (IGNPAR|IGNBRK)) + && !(t->c_lflag & (ECHO | ECHONL | ICANON | IEXTEN | ISIG + | PENDIN)) + && linesw[tp->t_line].l_rint == ttyinput) + tp->t_state |= TS_CAN_BYPASS_L_RINT; + else + tp->t_state &= ~TS_CAN_BYPASS_L_RINT; + /* + * Prepare to reduce input latency for packet + * discplines with a end of packet character. + */ + if (tp->t_line == SLIPDISC) + com->hotchar = 0xc0; + else if (tp->t_line == PPPDISC) + com->hotchar = 0x7e; + else + com->hotchar = 0; +#ifndef SOFT_HOTCHAR + iobase = com->iobase; + cd_outb(iobase, CD1400_CAR, com->unit & CD1400_CAR_CHAN); + opt = com->cor[2] & ~CD1400_COR3_SCD34; + if (com->hotchar != 0) { + cd_outb(iobase, CD1400_SCHR3, com->hotchar); + cd_outb(iobase, CD1400_SCHR4, com->hotchar); + opt |= CD1400_COR3_SCD34; + } + if (opt != com->cor[2]) { + cd_outb(iobase, CD1400_COR3, com->cor[2] = opt); + cd1400_channel_cmd(com->iobase, + CD1400_CCR_CMDCORCHG | CD1400_CCR_COR3); + } +#endif } +#ifdef Smarts +/* standard line discipline input routine */ int -cyspeed(int speed, int *prescaler_io) +cyinput(c, tp) + int c; + struct tty *tp; { - int actual; - int error; - int divider; - int prescaler; - int prescaler_unit; - - if (speed == 0) - return 0; - - if (speed < 0 || speed > 150000) - return -1; - - /* determine which prescaler to use */ - for (prescaler_unit = 4, prescaler = 2048; prescaler_unit; - prescaler_unit--, prescaler >>= 2) { - if (CYCLOM_CLOCK/prescaler/speed > 63) - break; - } - - divider = (CYCLOM_CLOCK/prescaler*2/speed + 1)/2; /* round off */ - if (divider > 255) - divider = 255; - actual = CYCLOM_CLOCK/prescaler/divider; - error = ((actual-speed)*2000/speed +1)/2; /* percentage */ - - /* 3.0% max error tolerance */ - if (error < -30 || error > 30) - return -1; + /* XXX duplicate ttyinput(), but without the IXOFF/IXON/ISTRIP/IPARMRK + * bits, as they are done by the CD1400. Hardly worth the effort, + * given that high-throughput sessions are raw anyhow. + */ +} +#endif /* Smarts */ + +static int +comspeed(speed, prescaler_io) + speed_t speed; + int *prescaler_io; +{ + int actual; + int error; + int divider; + int prescaler; + int prescaler_unit; + + if (speed == 0) + return (0); + if (speed < 0 || speed > 150000) + return (-1); + + /* determine which prescaler to use */ + for (prescaler_unit = 4, prescaler = 2048; prescaler_unit; + prescaler_unit--, prescaler >>= 2) { + if (CY_CLOCK / prescaler / speed > 63) + break; + } + + divider = (CY_CLOCK / prescaler * 2 / speed + 1) / 2; /* round off */ + if (divider > 255) + divider = 255; + actual = CY_CLOCK/prescaler/divider; + error = ((actual - speed) * 2000 / speed + 1) / 2; /* percentage */ + + /* 3.0% max error tolerance */ + if (error < -30 || error > 30) + return (-1); #if 0 - printf("prescaler = %d (%d)\n", prescaler, prescaler_unit); - printf("divider = %d (%x)\n", divider, divider); - printf("actual = %d\n", actual); - printf("error = %d\n", error); + printf("prescaler = %d (%d)\n", prescaler, prescaler_unit); + printf("divider = %d (%x)\n", divider, divider); + printf("actual = %d\n", actual); + printf("error = %d\n", error); #endif - *prescaler_io = prescaler_unit; - return divider; -} /* end of cyspeed() */ - + *prescaler_io = prescaler_unit; + return (divider); +} static void -cd1400_channel_cmd(cy_addr base, u_char cmd) +cd1400_channel_cmd(iobase, cmd) + cy_addr iobase; + int cmd; { - /* XXX hsu@clinet.fi: This is always more dependent on ISA bus speed, + /* XXX hsu@clinet.fi: This is always more dependent on ISA bus speed, as the card is probed every round? Replaced delaycount with 8k. Either delaycount has to be implemented in FreeBSD or more sensible way of doing these should be implemented. DELAY isn't enough here. */ - unsigned maxwait = 5 * 8 * 1024; /* approx. 5 ms */ + u_int maxwait = 5 * 8 * 1024; /* approx. 5 ms */ /* wait for processing of previous command to complete */ - while (*(base + CD1400_CCR) && maxwait--) - ; + while (cd_inb(iobase, CD1400_CCR) && maxwait--) + ; if (!maxwait) log(LOG_ERR, "cy: channel command timeout (%d loops) - arrgh\n", 5 * 8 * 1024); - *(base + CD1400_CCR) = cmd; -} /* end of cd1400_channel_cmd() */ - - -#ifdef CyDebug -/* useful in ddb */ -void -cyclear(void) -{ - /* clear the timeout request */ - disable_intr(); - timeout_scheduled = 0; - enable_intr(); + cd_outb(iobase, CD1400_CCR, cmd); } -void -cyclearintr(void) -{ - /* clear interrupts */ - *(cyclom_base + CYCLOM_CLEAR_INTR) = (u_char)0; -} - -int -cyparam_dummy(struct tty *tp, struct termios *t) -{ - return 0; -} - -void -cyset(int unit, int active) -{ - if (unit < 0 || unit >= /* NCY *? */ PORTS_PER_CYCLOM) { - printf("bad unit number %d\n", unit); - return; - } -#ifdef __FreeBSD__ - cy_tty[unit].t_param = active ? cyparam : cyparam_dummy; -#else - cy_tty[unit]->t_param = active ? cyparam : cyparam_dummy; -#endif -} - - +#ifdef CyDebug /* useful in ddb */ void -cystatus(int unit) +cystatus(unit) + int unit; { - struct cy *infop = info[unit]; - struct tty *tp = infop->tty; - cy_addr base = infop->base_addr; + struct com_s *com; + cy_addr iobase; + u_int ocount; + struct tty *tp; + com = com_addr(unit); printf("info for channel %d\n", unit); printf("------------------\n"); - - printf("cd1400 base address:\t0x%x\n", (int)infop->base_addr); - - /* select the port */ - *(base + CD1400_CAR) = (u_char)unit; - - printf("saved channel_control:\t%02x\n", infop->channel_control); - printf("saved cor1:\t\t%02x\n", infop->cor[0]); - printf("service request enable reg:\t%02x (%02x cached)\n", - (u_char)*(base + CD1400_SRER), infop->intr_enable); - printf("service request register:\t%02x\n", - (u_char)*(base + CD1400_SVRR)); + printf("total cyclom service probes:\t%d\n", cy_svrr_probes); + printf("calls to upper layer:\t\t%d\n", cy_timeouts); + if (com == NULL) + return; + iobase = com->iobase; + printf("\n"); + printf("cd1400 base address:\\tt%p\n", iobase); + cd_outb(iobase, CD1400_CAR, unit & CD1400_CAR_CHAN); + printf("saved channel_control:\t\t0x%02x\n", com->channel_control); + printf("saved cor1-3:\t\t\t0x%02x 0x%02x 0x%02x\n", + com->cor[0], com->cor[1], com->cor[2]); + printf("service request enable reg:\t0x%02x (0x%02x cached)\n", + cd_inb(iobase, CD1400_SRER), com->intr_enable); + printf("service request register:\t0x%02x\n", + cd_inb(iobase, CD1400_SVRR)); + printf("modem status:\t\t\t0x%02x (0x%02x cached)\n", + cd_inb(iobase, CD1400_MSVR2), com->prev_modem_status); + printf("rx/tx/mdm interrupt registers:\t0x%02x 0x%02x 0x%02x\n", + cd_inb(iobase, CD1400_RIR), cd_inb(iobase, CD1400_TIR), + cd_inb(iobase, CD1400_MIR)); printf("\n"); - printf("modem status:\t\t\t%02x (%02x cached)\n", - (u_char)*(base + CD1400_MSVR), infop->modem_sig); - printf("rx/tx/mdm interrupt registers:\t%02x %02x %02x\n", - (u_char)*(base + CD1400_RIR), (u_char)*(base + CD1400_TIR), - (u_char)*(base + CD1400_MIR)); + printf("com state:\t\t\t0x%02x\n", com->state); + printf("calls to comstart():\t\t%d (%d useful)\n", + com->start_count, com->start_real); + printf("rx buffer chars free:\t\t%d\n", com->iptr - com->ibuf); + ocount = 0; + if (com->obufs[0].l_queued) + ocount += com->obufs[0].l_tail - com->obufs[0].l_head; + if (com->obufs[1].l_queued) + ocount += com->obufs[1].l_tail - com->obufs[1].l_head; + printf("tx buffer chars:\t\t%u\n", ocount); + printf("received chars:\t\t\t%d\n", com->bytes_in); + printf("received exceptions:\t\t%d\n", com->recv_exception); + printf("modem signal deltas:\t\t%d\n", com->mdm); + printf("transmitted chars:\t\t%d\n", com->bytes_out); printf("\n"); - if (tp) { - printf("tty state:\t\t\t%04x\n", tp->t_state); + tp = com->tp; + if (tp != NULL) { + printf("tty state:\t\t\t0x%08x\n", tp->t_state); printf("upper layer queue lengths:\t%d raw, %d canon, %d output\n", - tp->t_rawq.c_cc, tp->t_canq.c_cc, tp->t_outq.c_cc); - } - else + tp->t_rawq.c_cc, tp->t_canq.c_cc, tp->t_outq.c_cc); + } else printf("tty state:\t\t\tclosed\n"); - printf("\n"); +} +#endif /* CyDebug */ - printf("calls to cystart():\t\t%d (%d useful)\n", - infop->start_count, infop->start_real); - printf("\n"); - printf("total cyclom service probes:\t%d\n", cy_svrr_probes); - printf("calls to upper layer:\t\t%d\n", cy_timeouts); - printf("rx buffer chars free:\t\t%d\n", infop->rx_buf->free); -#ifdef TxBuffer - printf("tx buffer chars used:\t\t%d\n", infop->tx_buf.used); -#endif - printf("received chars:\t\t\t%d good, %d exception\n", - infop->recv_normal, infop->recv_exception); - printf("transmitted chars:\t\t%d\n", infop->xmit); - printf("modem signal deltas:\t\t%d\n", infop->mdm); - printf("\n"); -} /* end of cystatus() */ -#endif #endif /* NCY > 0 */ diff --git a/sys/dev/cy/cyreg.h b/sys/dev/cy/cyreg.h new file mode 100644 index 0000000..8df7294 --- /dev/null +++ b/sys/dev/cy/cyreg.h @@ -0,0 +1,57 @@ +/*- + * Copyright (c) 1995 Bruce Evans. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: $ + */ + +/* + * Definitions for Cyclades Cyclom-Y serial boards. + */ + +#define CY8_SVCACKR 0x100 +#define CY8_SVCACKT 0x200 +#define CY8_SVCACKM 0x300 +#define CY_CD1400_MEMSIZE 0x400 +#define CY16_RESET 0x1400 +#define CY_CLEAR_INTR 0x1800 /* intr ack address */ + +#define CY_MAX_CD1400s 4 /* for Cyclom-16Y */ + +#define CY_CLOCK 25000000 /* baud rate clock */ + +#ifdef CyDebug +#define cd_inb(iobase, reg) (++cd_inbs, *((iobase) + 2 * (reg))) +#define cy_inb(iobase, reg) (++cy_inbs, *((iobase) + (reg))) +#define cd_outb(iobase, reg, val) (++cd_outbs, (void)(*((iobase) + 2 * (reg)) = (val))) +#define cy_outb(iobase, reg, val) (++cy_outbs, (void)(*((iobase) + (reg)) = (val))) +#else +#define cd_inb(iobase, reg) (*((iobase) + 2 * (reg))) +#define cy_inb(iobase, reg) (*((iobase) + (reg))) +#define cd_outb(iobase, reg, val) ((void)(*((iobase) + 2 * (reg)) = (val))) +#define cy_outb(iobase, reg, val) ((void)(*((iobase) + (reg)) = (val))) +#endif diff --git a/sys/dev/ic/cd1400.h b/sys/dev/ic/cd1400.h index eb690ae..ffebd9a 100644 --- a/sys/dev/ic/cd1400.h +++ b/sys/dev/ic/cd1400.h @@ -1,4 +1,4 @@ -/* +/*- * cyclades cyclom-y serial driver * Andrew Herbert <andrew@werple.apana.org.au>, 17 August 1993 * @@ -26,96 +26,173 @@ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: $ */ -#define CD1400_NO_OF_CHANNELS 4 /* four serial channels per chip */ -#define CD1400_FIFOSIZE 12 /* 12 chars */ - -/* register definitions */ - -#define CD1400_CCR 2*0x05 /* channel control */ -#define CD1400_CMD_RESET 0x81 /* full reset */ - -#define CD1400_SRER 2*0x06 /* service request enable */ - -#define CD1400_GFRCR 2*0x40 /* global firmware revision code */ - -#define CD1400_LIVR 2*0x18 /* local intr vector */ -#define CD1400_MIVR 2*0x41 /* modem intr vector */ -#define CD1400_TIVR 2*0x42 /* transmit intr vector */ -#define CD1400_RIVR 2*0x43 /* receive intr vector */ -#define CD1400_RIVR_EXCEPTION (1<<2) /* receive exception bit */ - -#define CD1400_RICR 2*0x44 /* receive intr channel */ -#define CD1400_TICR 2*0x45 /* transmit intr channel */ -#define CD1400_MICR 2*0x46 /* modem intr channel */ - -#define CD1400_RDCR 2*0x0e /* rx data count */ - -#define CD1400_EOSRR 2*0x60 /* end of service request */ -#define CD1400_RDSR 2*0x62 /* rx data/status */ -#define CD1400_RDSR_OVERRUN (1<<0) /* rx overrun error */ -#define CD1400_RDSR_FRAMING (1<<1) /* rx framing error */ -#define CD1400_RDSR_PARITY (1<<2) /* rx parity error */ -#define CD1400_RDSR_BREAK (1<<3) /* rx break */ -#define CD1400_RDSR_SPECIAL (7<<4) /* rx special char */ -#define CD1400_RDSR_SPECIAL_SHIFT 4 /* rx special char shift */ -#define CD1400_RDSR_TIMEOUT (1<<7) /* rx timeout */ - -#define CD1400_TDR 2*0x63 /* tx data */ - -#define CD1400_MISR 2*0x4c /* modem intr status */ -#define CD1400_MISR_DSRd (1<<7) /* DSR delta */ -#define CD1400_MISR_CTSd (1<<6) /* CTS delta */ -#define CD1400_MISR_RId (1<<5) /* RI delta */ -#define CD1400_MISR_CDd (1<<4) /* CD delta */ - -#define CD1400_MSVR 2*0x6d /* modem signals */ -#define CD1400_MSVR_DSR (1<<7) /* !DSR line */ -#define CD1400_MSVR_CTS (1<<6) /* !CTS line */ -#define CD1400_MSVR_RI (1<<5) /* !RI line */ -#define CD1400_MSVR_CD (1<<4) /* !CD line */ -#define CD1400_MSVR_DTR (1<<1) /* DTR line */ - -#define CD1400_DTR 2*0x6d /* dtr control */ -#define CD1400_DTR_CLEAR 0 -#define CD1400_DTR_SET (1<<1) - -#define CD1400_PPR 2*0x7e -#define CD1400_CLOCK_25_1MS 0x31 - -#define CD1400_CAR 2*0x68 /* channel access */ - -#define CD1400_RIR 2*0x6B /* receive interrupt status */ -#define CD1400_TIR 2*0x6A /* transmit interrupt status */ -#define CD1400_MIR 2*0x69 /* modem interrupt status */ - -#define CD1400_RBPR 2*0x78 /* receive baud rate period */ -#define CD1400_RCOR 2*0x7C /* receive clock option */ -#define CD1400_TBPR 2*0x72 /* transmit baud rate period */ -#define CD1400_TCOR 2*0x76 /* transmit clock option */ - -#define CD1400_COR1 2*0x08 /* channel option 1 */ -#define CD1400_COR2 2*0x09 /* channel option 2 */ -#define CD1400_COR3 2*0x0A /* channel option 3 */ -#define CD1400_COR4 2*0x1E /* channel option 4 */ -#define CD1400_COR5 2*0x1F /* channel option 5 */ +/* + * Definitions for Cirrus Logic CD1400 serial/parallel chips. + */ -#define CD1400_SCHR1 2*0x1A /* special character 1 */ -#define CD1400_SCHR2 2*0x1B /* special character 2 */ -#define CD1400_SCHR3 2*0x1C /* special character 3 */ -#define CD1400_SCHR4 2*0x1D /* special character 4 */ +#define CD1400_NO_OF_CHANNELS 4 /* 4 serial channels per chip */ +#define CD1400_RX_FIFO_SIZE 12 +#define CD1400_TX_FIFO_SIZE 12 -#define CD1400_MCOR1 2*0x15 /* modem change 1 */ -#define CD1400_MCOR2 2*0x16 /* modem change 2 */ -#define CD1400_RTPR 2*0x21 /* receive timeout period */ +/* + * Global registers. + */ +#define CD1400_GFRCR 0x40 /* global firmware revision code */ +#define CD1400_CAR 0x68 /* channel access */ +#define CD1400_CAR_CHAN (3<<0) /* channel select */ +#define CD1400_GCR 0x4B /* global configuration */ +#define CD1400_GCR_PARALLEL (1<<7) /* channel 0 is parallel */ +#define CD1400_SVRR 0x67 /* service request */ +#define CD1400_SVRR_MDMCH (1<<2) +#define CD1400_SVRR_TXRDY (1<<1) +#define CD1400_SVRR_RXRDY (1<<0) +#define CD1400_RICR 0x44 /* receive interrupting channel */ +#define CD1400_TICR 0x45 /* transmit interrupting channel */ +#define CD1400_MICR 0x46 /* modem interrupting channel */ +#define CD1400_RIR 0x6B /* receive interrupt status */ +#define CD1400_RIR_RDIREQ (1<<7) /* rx service required */ +#define CD1400_RIR_RBUSY (1<<6) /* rx service in progress */ +#define CD1400_RIR_CHAN (3<<0) /* channel select */ +#define CD1400_TIR 0x6A /* transmit interrupt status */ +#define CD1400_TIR_RDIREQ (1<<7) /* tx service required */ +#define CD1400_TIR_RBUSY (1<<6) /* tx service in progress */ +#define CD1400_TIR_CHAN (3<<0) /* channel select */ +#define CD1400_MIR 0x69 /* modem interrupt status */ +#define CD1400_MIR_RDIREQ (1<<7) /* modem service required */ +#define CD1400_MIR_RBUSY (1<<6) /* modem service in progress */ +#define CD1400_MIR_CHAN (3<<0) /* channel select */ +#define CD1400_PPR 0x7E /* prescaler period */ +#define CD1400_PPR_PRESCALER 512 -#define CD1400_SVRR 2*0x67 /* service request */ -#define CD1400_SVRR_RX (1<<0) -#define CD1400_SVRR_TX (1<<1) -#define CD1400_SVRR_MDM (1<<2) +/* + * Virtual registers. + */ +#define CD1400_RIVR 0x43 /* receive interrupt vector */ +#define CD1400_RIVR_EXCEPTION (1<<2) /* receive exception bit */ +#define CD1400_TIVR 0x42 /* transmit interrupt vector */ +#define CD1400_MIVR 0x41 /* modem interrupt vector */ +#define CD1400_TDR 0x63 /* transmit data */ +#define CD1400_RDSR 0x62 /* receive data/status */ +#define CD1400_RDSR_TIMEOUT (1<<7) /* rx timeout */ +#define CD1400_RDSR_SPECIAL_SHIFT 4 /* rx special char shift */ +#define CD1400_RDSR_SPECIAL (7<<4) /* rx special char */ +#define CD1400_RDSR_BREAK (1<<3) /* rx break */ +#define CD1400_RDSR_PE (1<<2) /* rx parity error */ +#define CD1400_RDSR_FE (1<<1) /* rx framing error */ +#define CD1400_RDSR_OE (1<<0) /* rx overrun error */ +#define CD1400_MISR 0x4C /* modem interrupt status */ +#define CD1400_MISR_DSRd (1<<7) /* DSR delta */ +#define CD1400_MISR_CTSd (1<<6) /* CTS delta */ +#define CD1400_MISR_RId (1<<5) /* RI delta */ +#define CD1400_MISR_CDd (1<<4) /* CD delta */ +#define CD1400_EOSRR 0x60 /* end of service request */ -/* hardware SVCACK addresses, for use in interrupt handlers */ -#define CD1400_SVCACKR 0x100 -#define CD1400_SVCACKT 0x200 -#define CD1400_SVCACKM 0x300 +/* + * Channel registers. + */ +#define CD1400_LIVR 0x18 /* local interrupt vector */ +#define CD1400_CCR 0x05 /* channel control */ +#define CD1400_CCR_CMDRESET (1<<7) /* enables following: */ +#define CD1400_CCR_FTF (1<<1) /* flush tx fifo */ +#define CD1400_CCR_FULLRESET (1<<0) /* full reset */ +#define CD1400_CCR_CMDCORCHG (1<<6) /* enables following: */ +#define CD1400_CCR_COR3 (1<<3) /* COR3 changed */ +#define CD1400_CCR_COR2 (1<<2) /* COR2 changed */ +#define CD1400_CCR_COR1 (1<<1) /* COR1 changed */ +#define CD1400_CCR_CMDSENDSC (1<<5) /* enables following: */ +#define CD1400_CCR_SC (7<<0) /* special char 1-4 */ +#define CD1400_CCR_CMDCHANCTL (1<<4) /* enables following: */ +#define CD1400_CCR_XMTEN (1<<3) /* tx enable */ +#define CD1400_CCR_XMTDIS (1<<2) /* tx disable */ +#define CD1400_CCR_RCVEN (1<<1) /* rx enable */ +#define CD1400_CCR_RCVDIS (1<<0) /* rx disable */ +#define CD1400_SRER 0x06 /* service request enable */ +#define CD1400_SRER_MDMCH (1<<7) /* modem change */ +#define CD1400_SRER_RXDATA (1<<4) /* rx data */ +#define CD1400_SRER_TXRDY (1<<2) /* tx fifo empty */ +#define CD1400_SRER_TXMPTY (1<<1) /* tx shift reg empty */ +#define CD1400_SRER_NNDT (1<<0) /* no new data */ +#define CD1400_COR1 0x08 /* channel option 1 */ +#define CD1400_COR1_PARODD (1<<7) +#define CD1400_COR1_PARNORMAL (2<<5) +#define CD1400_COR1_PARFORCE (1<<5) /* odd/even = force 1/0 */ +#define CD1400_COR1_PARNONE (0<<5) +#define CD1400_COR1_NOINPCK (1<<4) +#define CD1400_COR1_STOP2 (2<<2) +#define CD1400_COR1_STOP15 (1<<2) /* 1.5 stop bits */ +#define CD1400_COR1_STOP1 (0<<2) +#define CD1400_COR1_CS8 (3<<0) +#define CD1400_COR1_CS7 (2<<0) +#define CD1400_COR1_CS6 (1<<0) +#define CD1400_COR1_CS5 (0<<0) +#define CD1400_COR2 0x09 /* channel option 2 */ +#define CD1400_COR2_IXANY (1<<7) /* implied XON mode */ +#define CD1400_COR2_IXOFF (1<<6) /* in-band tx flow control */ +#define CD1400_COR2_ETC (1<<5) /* embedded tx command */ +#define CD1400_COR2_LLM (1<<4) /* local loopback mode */ +#define CD1400_COR2_RLM (1<<3) /* remote loopback mode */ +#define CD1400_COR2_RTSAO (1<<2) /* RTS auto output */ +#define CD1400_COR2_CCTS_OFLOW (1<<1) /* CTS auto enable */ +#define CD1400_COR2_CDSR_OFLOW (1<<0) /* DSR auto enable */ +#define CD1400_COR3 0x0A /* channel option 3 */ +#define CD1400_COR3_SCDRNG (1<<7) /* special char detect range */ +#define CD1400_COR3_SCD34 (1<<6) /* special char detect 3-4 */ +#define CD1400_COR3_FTC (1<<5) /* flow control transparency */ +#define CD1400_COR3_SCD12 (1<<4) /* special char detect 1-2 */ +#define CD1400_COR3_RXTH (15<<0) /* rx fifo threshold */ +#define CD1400_COR4 0x1E /* channel option 4 */ +#define CD1400_COR4_IGNCR (1<<7) +#define CD1400_COR4_ICRNL (1<<6) +#define CD1400_COR4_INLCR (1<<5) +#define CD1400_COR4_IGNBRK (1<<4) +#define CD1400_COR4_NOBRKINT (1<<3) +#define CD1400_COR4_PFO_ESC (4<<0) /* parity/framing/overrun... */ +#define CD1400_COR4_PFO_NUL (3<<0) +#define CD1400_COR4_PFO_DISCARD (2<<0) +#define CD1400_COR4_PFO_GOOD (1<<0) +#define CD1400_COR4_PFO_EXCEPTION (0<<0) +#define CD1400_COR5 0x1F /* channel option 5 */ +#define CD1400_COR5_ISTRIP (1<<7) +#define CD1400_COR5_LNEXT (1<<6) +#define CD1400_COR5_CMOE (1<<5) /* char matching on error */ +#define CD1400_COR5_EBD (1<<2) /* end of break detected */ +#define CD1400_COR5_ONLCR (1<<1) +#define CD1400_COR5_OCRNL (1<<0) +#define CD1400_CCSR 0x0B /* channel control status */ +#define CD1400_RDCR 0x0E /* received data count */ +#define CD1400_SCHR1 0x1A /* special character 1 */ +#define CD1400_SCHR2 0x1B /* special character 2 */ +#define CD1400_SCHR3 0x1C /* special character 3 */ +#define CD1400_SCHR4 0x1D /* special character 4 */ +#define CD1400_SCRL 0x22 /* special character range, low */ +#define CD1400_SCRH 0x23 /* special character range, high */ +#define CD1400_LNC 0x24 /* lnext character */ +#define CD1400_MCOR1 0x15 /* modem change option 1 */ +#define CD1400_MCOR1_DSRzd (1<<7) /* DSR one-to-zero delta */ +#define CD1400_MCOR1_CTSzd (1<<6) +#define CD1400_MCOR1_RIzd (1<<5) +#define CD1400_MCOR1_CDzd (1<<4) +#define CD1400_MCOR1_DTRth (15<<0) /* dtrflow threshold */ +#define CD1400_MCOR2 0x16 /* modem change option 2 */ +#define CD1400_MCOR2_DSRod (1<<7) /* DSR zero-to-one delta */ +#define CD1400_MCOR2_CTSod (1<<6) +#define CD1400_MCOR2_RIod (1<<5) +#define CD1400_MCOR2_CDod (1<<4) +#define CD1400_RTPR 0x21 /* receive timeout period */ +#define CD1400_MSVR1 0x6C /* modem signal value 1 */ +#define CD1400_MSVR1_RTS (1<<0) /* RTS line (r/w) */ +#define CD1400_MSVR2 0x6D /* modem signal value 2 */ +#define CD1400_MSVR2_DSR (1<<7) /* !DSR line (r) */ +#define CD1400_MSVR2_CTS (1<<6) /* !CTS line (r) */ +#define CD1400_MSVR2_RI (1<<5) /* !RI line (r) */ +#define CD1400_MSVR2_CD (1<<4) /* !CD line (r) */ +#define CD1400_MSVR2_DTR (1<<1) /* DTR line (r/w) */ +#define CD1400_PSVR 0x6F /* printer signal value */ +#define CD1400_RBPR 0x78 /* receive baud rate period */ +#define CD1400_RCOR 0x7C /* receive clock option */ +#define CD1400_TBPR 0x72 /* transmit baud rate period */ +#define CD1400_TCOR 0x76 /* transmit clock option */ diff --git a/sys/i386/isa/cy.c b/sys/i386/isa/cy.c index fd898f2..81f7022 100644 --- a/sys/i386/isa/cy.c +++ b/sys/i386/isa/cy.c @@ -1,4 +1,4 @@ -/* +/*- * cyclades cyclom-y serial driver * Andrew Herbert <andrew@werple.apana.org.au>, 17 August 1993 * @@ -27,1262 +27,1740 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $Id: cy.c,v 1.6 1995/03/28 12:29:11 bde Exp $ + * $Id: cy.c,v 1.7 1995/05/30 08:01:34 rgrimes Exp $ */ +#include "cy.h" +#if NCY > 0 /* - * Device minor number encoding: - * - * c c x x u u u u - bits in the minor device number - * - * bits meaning - * ---- ------- - * uuuu physical serial line (i.e. unit) to use - * 0-7 on a cyclom-8Y, 0-15 on a cyclom-16Y - * xx unused - * cc carrier control mode - * 00 complete hardware carrier control of the tty. - * DCD must be high for the open(2) to complete. - * 01 dialin pseudo-device (not yet implemented) - * 10 carrier ignored until a high->low transition - * 11 carrier completed ignored + * TODO: + * Check that cy16's work. + * Implement BREAK. + * Fix overflows when closing line. + * Atomic COR change. + * Don't report individual ports in devconf; busy flag for board should be + * union of the current individual busy flags. + * Consoles. */ /* - * Known deficiencies: - * - * * no BREAK handling - breaks are ignored, and can't be sent either - * * no support for bad-char reporting, except via PARMRK - * * no support for dialin + dialout devices + * Temporary compile-time configuration options. */ +#define RxFifoThreshold (CD1400_RX_FIFO_SIZE / 2) + /* Number of chars in the receiver FIFO before an + * an interrupt is generated. Should depend on + * line speed. Needs to be about 6 on a 486DX33 + * for 4 active ports at 115200 bps. Why doesn't + * 10 work? + */ +#define PollMode /* Use polling-based irq service routine, not the + * hardware svcack lines. Must be defined for + * Cyclom-16Y boards. Less efficient for Cyclom-8Ys, + * and stops 4 * 115200 bps from working. + */ +#undef Smarts /* Enable slightly more CD1400 intelligence. Mainly + * the output CR/LF processing, plus we can avoid a + * few checks usually done in ttyinput(). + * + * XXX not fully implemented, and not particularly + * worthwhile. + */ +#undef CyDebug /* Include debugging code (not very expensive). */ -#include "cy.h" -#if NCY > 0 - -/* This disgusing hack because we actually have 16 units on one controller */ -#if NCY < 2 -#undef NCY -#define NCY (16) -#endif +/* These will go away. */ +#undef SOFT_CTS_OFLOW +#define SOFT_HOTCHAR #include <sys/param.h> #include <sys/systm.h> -#include <sys/kernel.h> -#include <sys/malloc.h> +#include <sys/reboot.h> #include <sys/ioctl.h> +#define TTYDEFCHARS /* XXX TK2.0 */ #include <sys/tty.h> +#undef TTYDEFCHARS #include <sys/proc.h> #include <sys/user.h> #include <sys/conf.h> +#include <sys/dkstat.h> #include <sys/file.h> #include <sys/uio.h> #include <sys/kernel.h> +#include <sys/malloc.h> #include <sys/syslog.h> +#include <sys/devconf.h> -#include <machine/cpu.h> -#ifdef NetBSD -#include <machine/pio.h> -#include <machine/cpufunc.h> -#else #include <machine/clock.h> -#endif +#include <i386/isa/icu.h> /* XXX just to get at `imen' */ +#include <i386/isa/isa.h> #include <i386/isa/isa_device.h> +#include <i386/isa/cyreg.h> #include <i386/isa/ic/cd1400.h> -#define RxFifoThreshold 3 /* 3 characters (out of 12) in the receive - * FIFO before an interrupt is generated - */ -#define FastRawInput /* bypass the regular char-by-char canonical input - * processing whenever possible - */ -#define PollMode /* use polling-based irq service routine, not the - * hardware svcack lines. Must be defined for - * cyclom-16y boards. - * - * XXX cyclom-8y doesn't work without this defined - * either (!) - */ -#define LogOverruns /* log receive fifo overruns */ -#undef TxBuffer /* buffer driver output, to be slightly more - * efficient - * - * XXX presently buggy - */ -#undef Smarts /* enable slightly more CD1400 intelligence. Mainly - * the output CR/LF processing, plus we can avoid a - * few checks usually done in ttyinput(). - * - * XXX not yet implemented, and not particularly - * worthwhile either. - */ -#define CyDebug /* include debugging code (minimal effect on - * performance) - */ +/* + * Dictionary so that I can name everything *sio* or *com* to compare with + * sio.c. There is also lots of ugly formatting and unnecessary ifdefs to + * simplify the comparision. These will go away. + */ +#define LSR_BI CD1400_RDSR_BREAK +#define LSR_FE CD1400_RDSR_FE +#define LSR_OE CD1400_RDSR_OE +#define LSR_PE CD1400_RDSR_PE +#define MCR_DTR CD1400_MSVR2_DTR +#define MCR_RTS CD1400_MSVR1_RTS +#define MSR_CTS CD1400_MSVR2_CTS +#define MSR_DCD CD1400_MSVR2_CD +#define MSR_DSR CD1400_MSVR2_DSR +#define MSR_RI CD1400_MSVR2_RI +#define NSIO (NCY * CY_MAX_PORTS) +#define comconsole cyconsole +#define comdefaultrate cydefaultrate +#define com_events cy_events +#define comhardclose cyhardclose +#define commajor cymajor +#define commctl cymctl +#define comparam cyparam +#define comspeed cyspeed +#define comstart cystart +#define comwakeup cywakeup +#define kdc_sio kdc_cy +#define nsio_tty ncy_tty +#define p_com_addr p_cy_addr +#define sioattach cyattach +#define sioclose cyclose +#define siodevtotty cydevtotty +#define siodriver cydriver +#define siodtrwakeup cydtrwakeup +#define sioioctl cyioctl +#define siointr cyintr +#define siointr1 cyintr1 +#define siointrts cyintrts +#define sioopen cyopen +#define siopoll cypoll +#define sioprobe cyprobe +#define sioread cyread +#define sioregisterdev cyregisterdev +#define siosettimeout cysettimeout +#define siostop cystop +#define siowrite cywrite +#define sio_timeout cy_timeout +#define sio_timeouts_until_log cy_timeouts_until_log +#define sio_tty cy_tty + +#define CY_MAX_PORTS (CD1400_NO_OF_CHANNELS * CY_MAX_CD1400s) + +/* We encode the cyclom unit number (cyu) in spare bits in the IVR's. */ +#define CD1400_xIVR_CHAN_SHIFT 3 +#define CD1400_xIVR_CHAN 0x0F /* XXX reduce to pack Cyclom-8Ys */ -#define CY_RX_BUFS 2 /* two receive buffers per port */ -#define CY_RX_BUF_SIZE 256 /* bytes per receive buffer */ -#define CY_TX_BUF_SIZE 512 /* bytes per transmit buffer */ +/* + * XXX temporary kludges for 2.0 (XXX TK2.0). + */ +#define TSA_CARR_ON(tp) ((void *)&(tp)->t_rawq) +#define TSA_OCOMPLETE(tp) ((void *)&(tp)->t_outq) +#define TSA_OLOWAT(tp) ((void *)&(tp)->t_outq) +static void termioschars __P((struct termios *t)); +static void +termioschars(t) + struct termios *t; +{ -/* #define CD1400s_PER_CYCLOM 1 */ /* cyclom-4y */ -/* #define CD1400s_PER_CYCLOM 2 */ /* cyclom-8y */ -#define CD1400s_PER_CYCLOM 4 /* cyclom-16y */ + bcopy(ttydefchars, t->c_cc, sizeof t->c_cc); +} -/* FreeBSD's getty doesn't know option for setting RTS/CTS handshake. Its - getty, like a lot of other old cruft, should be replaced with something - which used POSIX tty interfaces which at least allow enabling it. In the - meantime, use the force. */ -#define ALWAYS_RTS_CTS 1 +#define LOTS_OF_EVENTS 64 /* helps separate urgent events from input */ +#define RB_I_HIGH_WATER (TTYHOG - 2 * RS_IBUFSIZE) +#define RS_IBUFSIZE 256 -#if CD1400s_PER_CYCLOM < 4 -#define CD1400_MEMSIZE 0x400 /* 4*256 bytes per chip: cyclom-[48]y */ -#else -#define CD1400_MEMSIZE 0x100 /* 256 bytes per chip: cyclom-16y */ - /* XXX or is it 0x400 like the rest? */ -#define CYCLOM_16 1 /* This is a cyclom-16Y */ -#endif +#define CALLOUT_MASK 0x80 +#define CONTROL_MASK 0x60 +#define CONTROL_INIT_STATE 0x20 +#define CONTROL_LOCK_STATE 0x40 +#define DEV_TO_UNIT(dev) (MINOR_TO_UNIT(minor(dev))) +#define MINOR_MAGIC_MASK (CALLOUT_MASK | CONTROL_MASK) +#define MINOR_TO_UNIT(mynor) ((mynor) & ~MINOR_MAGIC_MASK) -#define PORTS_PER_CYCLOM (CD1400_NO_OF_CHANNELS * CD1400s_PER_CYCLOM) -#define CYCLOM_RESET_16 0x1400 /* cyclom-16y reset */ -#define CYCLOM_CLEAR_INTR 0x1800 /* intr ack address */ -#define CYCLOM_CLOCK 25000000 /* baud rate clock */ +/* + * Input buffer watermarks. + * The external device is asked to stop sending when the buffer exactly reaches + * high water, or when the high level requests it. + * The high level is notified immediately (rather than at a later clock tick) + * when this watermark is reached. + * The buffer size is chosen so the watermark should almost never be reached. + * The low watermark is invisibly 0 since the buffer is always emptied all at + * once. + */ +#define RS_IHIGHWATER (3 * RS_IBUFSIZE / 4) -#define CY_UNITMASK 0x0f -#define CY_CARRIERMASK 0xC0 -#define CY_CARRIERSHIFT 6 +/* + * com state bits. + * (CS_BUSY | CS_TTGO) and (CS_BUSY | CS_TTGO | CS_ODEVREADY) must be higher + * than the other bits so that they can be tested as a group without masking + * off the low bits. + * + * The following com and tty flags correspond closely: + * CS_BUSY = TS_BUSY (maintained by comstart(), siopoll() and + * siostop()) + * CS_TTGO = ~TS_TTSTOP (maintained by comstart() and siostop()) + * CS_CTS_OFLOW = CCTS_OFLOW (maintained by comparam()) + * CS_RTS_IFLOW = CRTS_IFLOW (maintained by comparam()) + * TS_FLUSH is not used. + * XXX I think TIOCSETA doesn't clear TS_TTSTOP when it clears IXON. + * XXX CS_*FLOW should be CF_*FLOW in com->flags (control flags not state). + */ +#define CS_BUSY 0x80 /* output in progress */ +#define CS_TTGO 0x40 /* output not stopped by XOFF */ +#define CS_ODEVREADY 0x20 /* external device h/w ready (CTS) */ +#define CS_CHECKMSR 1 /* check of MSR scheduled */ +#define CS_CTS_OFLOW 2 /* use CTS output flow control */ +#define CS_DTR_OFF 0x10 /* DTR held off */ +#define CS_ODONE 4 /* output completed */ +#define CS_RTS_IFLOW 8 /* use RTS input flow control */ + +static char const * const error_desc[] = { +#define CE_OVERRUN 0 + "silo overflow", +#define CE_INTERRUPT_BUF_OVERFLOW 1 + "interrupt-level buffer overflow", +#define CE_TTY_BUF_OVERFLOW 2 + "tty-level buffer overflow", +}; -#define UNIT(x) (minor(x) & CY_UNITMASK) -#define CARRIER_MODE(x) ((minor(x) & CY_CARRIERMASK) >> CY_CARRIERSHIFT) +#define CE_NTYPES 3 +#define CE_RECORD(com, errnum) (++(com)->delta_error_counts[errnum]) -typedef u_char * volatile cy_addr; +/* types. XXX - should be elsewhere */ +typedef u_char bool_t; /* boolean */ +typedef u_char volatile *cy_addr; -int cyprobe(struct isa_device *dev); -int cyattach(struct isa_device *isdp); -void cystart(struct tty *tp); -int cyparam(struct tty *tp, struct termios *t); -int cyspeed(int speed, int *prescaler_io); -static void cy_channel_init(dev_t dev, int reset); -static void cd1400_channel_cmd(cy_addr base, u_char cmd); +/* queue of linear buffers */ +struct lbq { + u_char *l_head; /* next char to process */ + u_char *l_tail; /* one past the last char to process */ + struct lbq *l_next; /* next in queue */ + bool_t l_queued; /* nonzero if queued */ +}; -/* hsu@clinet.fi: sigh */ -#ifdef __NetBSD__ -#define DELAY(foo) delay(foo) -void delay(int delay); +/* com device structure */ +struct com_s { + u_char state; /* miscellaneous flag bits */ + bool_t active_out; /* nonzero if the callout device is open */ +#if 0 + u_char cfcr_image; /* copy of value written to CFCR */ + u_char ftl; /* current rx fifo trigger level */ + u_char ftl_init; /* ftl_max for next open() */ + u_char ftl_max; /* maximum ftl for curent open() */ + bool_t hasfifo; /* nonzero for 16550 UARTs */ + bool_t loses_outints; /* nonzero if device loses output interrupts */ #endif - -/* Better get rid of this until the core people agree on kernel interfaces. - At least it will then compile on both WhichBSDs. - */ + u_char mcr_image; /* copy of value written to MCR */ #if 0 -extern unsigned int delaycount; /* calibrated 1 ms cpu-spin delay */ +#ifdef COM_MULTIPORT + bool_t multiport; /* is this unit part of a multiport device? */ +#endif /* COM_MULTIPORT */ + bool_t no_irq; /* nonzero if irq is not attached */ + bool_t poll; /* nonzero if polling is required */ + bool_t poll_output; /* nonzero if polling for output is required */ #endif + int unit; /* unit number */ + int dtr_wait; /* time to hold DTR down on close (* 1/hz) */ +#if 0 + u_int tx_fifo_size; +#endif + u_int wopeners; /* # processes waiting for DCD in open() */ -struct isa_driver cydriver = { - cyprobe, cyattach, "cy" -}; + /* + * The high level of the driver never reads status registers directly + * because there would be too many side effects to handle conveniently. + * Instead, it reads copies of the registers stored here by the + * interrupt handler. + */ + u_char last_modem_status; /* last MSR read by intr handler */ + u_char prev_modem_status; /* last MSR handled by high level */ -/* low-level ping-pong buffer structure */ + u_char hotchar; /* ldisc-specific char to be handled ASAP */ + u_char *ibuf; /* start of input buffer */ + u_char *ibufend; /* end of input buffer */ + u_char *ihighwater; /* threshold in input buffer */ + u_char *iptr; /* next free spot in input buffer */ -struct cy_buf { - u_char *next_char; /* location of next char to write */ - u_int free; /* free chars remaining in buffer */ - struct cy_buf *next_buf; /* circular, you know */ - u_char buf[CY_RX_BUF_SIZE]; /* start of the buffer */ -}; + struct lbq obufq; /* head of queue of output buffers */ + struct lbq obufs[2]; /* output buffers */ -/* low-level ring buffer */ + cy_addr cy_iobase; /* base address of this port's cyclom */ + cy_addr iobase; /* base address of this port's cd1400 */ -#ifdef TxBuffer -struct cy_ring { - u_char buf[CY_TX_BUF_SIZE]; - u_char *head; - u_char *tail; /* next pos. to insert char */ - u_char *endish; /* physical end of buf */ - u_int used; /* no. of chars in queue */ -}; + struct tty *tp; /* cross reference */ + + /* Initial state. */ + struct termios it_in; /* should be in struct tty */ + struct termios it_out; + + /* Lock state. */ + struct termios lt_in; /* should be in struct tty */ + struct termios lt_out; + + bool_t do_timestamp; + struct timeval timestamp; + + u_long bytes_in; /* statistics */ + u_long bytes_out; + u_int delta_error_counts[CE_NTYPES]; + u_long error_counts[CE_NTYPES]; + + u_int recv_exception; /* exception chars received */ + u_int mdm; /* modem signal changes */ +#ifdef CyDebug + u_int start_count; /* no. of calls to comstart() */ + u_int start_real; /* no. of calls that did something */ #endif + u_char channel_control;/* CD1400 CCR control command shadow */ + u_char cor[3]; /* CD1400 COR1-3 shadows */ + u_char intr_enable; /* CD1400 SRER shadow */ + /* + * Ping-pong input buffers. The extra factor of 2 in the sizes is + * to allow for an error byte for each input byte. + */ +#define CE_INPUT_OFFSET RS_IBUFSIZE + u_char ibuf1[2 * RS_IBUFSIZE]; + u_char ibuf2[2 * RS_IBUFSIZE]; + + /* + * Data area for output buffers. Someday we should build the output + * buffer queue without copying data. + */ + u_char obuf1[256]; + u_char obuf2[256]; + + struct kern_devconf kdc; +}; /* - * define a structure to keep track of each serial line + * XXX public functions in drivers should be declared in headers produced + * by `config', not here. */ -struct cy { - cy_addr base_addr; /* base address of this port's cd1400 */ - struct tty *tty; - u_int dtrwait; /* time (in ticks) to hold dtr low after close */ - u_int recv_exception; /* exception chars received */ - u_int recv_normal; /* normal chars received */ - u_int xmit; /* chars transmitted */ - u_int mdm; /* modem signal changes */ +/* Interrupt handling entry points. */ +void siointr __P((int unit)); +void siointrts __P((int unit)); +void siopoll __P((void)); + +/* Device switch entry points. */ +int sioopen __P((dev_t dev, int oflags, int devtype, + struct proc *p)); +int sioclose __P((dev_t dev, int fflag, int devtype, + struct proc *p)); +int sioread __P((dev_t dev, struct uio *uio, int ioflag)); +int siowrite __P((dev_t dev, struct uio *uio, int ioflag)); +int sioioctl __P((dev_t dev, int cmd, caddr_t data, + int fflag, struct proc *p)); +void siostop __P((struct tty *tp, int rw)); +#define sioreset noreset +struct tty *siodevtotty __P((dev_t dev)); +#define siommap nommap +#define siostrategy nostrategy + +static int sioattach __P((struct isa_device *dev)); +static void cd1400_channel_cmd __P((cy_addr iobase, int cmd)); +static timeout_t siodtrwakeup; +static void comhardclose __P((struct com_s *com)); +static void siointr1 __P((struct com_s *com)); +static int commctl __P((struct com_s *com, int bits, int how)); +static int comparam __P((struct tty *tp, struct termios *t)); +static int sioprobe __P((struct isa_device *dev)); +static void sioregisterdev __P((struct isa_device *id)); +static void siosettimeout __P((void)); +static int comspeed __P((speed_t speed, int *prescaler_io)); +static void comstart __P((struct tty *tp)); +static timeout_t comwakeup; +static void disc_optim __P((struct tty *tp, struct termios *t, + struct com_s *com)); + #ifdef CyDebug - u_int start_count; /* no. of calls to cystart() */ - u_int start_real; /* no. of calls that did something */ -#endif - u_char carrier_mode; /* hardware carrier handling mode */ - /* - * 0 = always use - * 1 = always use (dialin port) - * 2 = ignore during open, then use it - * 3 = ignore completely - */ - u_char carrier_delta; /* true if carrier has changed state */ - u_char fifo_overrun; /* true if cd1400 receive fifo has... */ - u_char rx_buf_overrun; /* true if low-level buf overflow */ - u_char intr_enable; /* CD1400 SRER shadow */ - u_char modem_sig; /* CD1400 modem signal shadow */ - u_char channel_control;/* CD1400 CCR control command shadow */ - u_char cor[3]; /* CD1400 COR1-3 shadows */ -#ifdef Smarts - u_char spec_char[4]; /* CD1400 SCHR1-4 shadows */ -#endif - struct cy_buf *rx_buf; /* current receive buffer */ - struct cy_buf rx_buf_pool[CY_RX_BUFS];/* receive ping-pong buffers */ -#ifdef TxBuffer - struct cy_ring tx_buf; /* transmit buffer */ +void cystatus __P((int unit)); #endif + +/* table and macro for fast conversion from a unit number to its com struct */ +static struct com_s *p_com_addr[NSIO]; +#define com_addr(unit) (p_com_addr[unit]) + +static struct timeval intr_timestamp; + +struct isa_driver siodriver = { + sioprobe, sioattach, "cy" }; -int cydefaultrate = TTYDEF_SPEED; -cy_addr cyclom_base; /* base address of the card */ -static struct cy *info[NCY*PORTS_PER_CYCLOM]; -#ifdef __FreeBSD__ /* XXX actually only temporarily for 2.1-Development */ -struct tty cy_tty[NCY*PORTS_PER_CYCLOM]; +#ifdef COMCONSOLE +#undef COMCONSOLE +#define COMCONSOLE 1 #else -struct tty *cy_tty[NCY*PORTS_PER_CYCLOM]; +#define COMCONSOLE 0 #endif -static volatile u_char timeout_scheduled = 0; /* true if a timeout has been scheduled */ -#ifdef CyDebug -u_int cy_svrr_probes = 0; /* debugging */ -u_int cy_timeouts = 0; -u_int cy_timeout_req = 0; +#ifndef CONUNIT +#define CONUNIT (0) #endif -/**********************************************************************/ - -int -cyprobe(struct isa_device *dev) -{ - int i, j; - u_char version = 0; /* firmware version */ +static int comconsole = CONUNIT; +static speed_t comdefaultrate = TTYDEF_SPEED; +static u_int com_events; /* input chars + weighted output completions */ +static int commajor; +static int sio_timeout; +static int sio_timeouts_until_log; +#if 0 /* XXX TK2.0 */ +static struct tty *sio_tty[NSIO]; +#else +static struct tty sio_tty[NSIO]; +static int nsio_tty = NSIO; +#endif - /* Cyclom-16Y hardware reset (Cyclom-8Ys don't care) */ - i = *(cy_addr)(dev->id_maddr + CYCLOM_RESET_16); +#ifdef KGDB +#include <machine/remote-sl.h> - DELAY(500); /* wait for the board to get its act together (500 us) */ +extern int kgdb_dev; +extern int kgdb_rate; +extern int kgdb_debug_init; +#endif - for (i = 0; i < CD1400s_PER_CYCLOM; i++) { - cy_addr base = dev->id_maddr + i * CD1400_MEMSIZE; +#ifdef CyDebug +static u_int cd_inbs; +static u_int cy_inbs; +static u_int cd_outbs; +static u_int cy_outbs; +static u_int cy_svrr_probes; +static u_int cy_timeouts; +#endif - /* wait for chip to become ready for new command */ - for (j = 0; j < 100; j += 50) { - DELAY(50); /* wait 50 us */ +static int cy_nr_cd1400s[NCY]; +#undef RxFifoThreshold +static int volatile RxFifoThreshold = (CD1400_RX_FIFO_SIZE / 2); + +static struct kern_devconf kdc_sio[NCY] = { { + 0, 0, 0, /* filled in by dev_attach */ + "cyc", 0, { MDDT_ISA, 0, "tty" }, + isa_generic_externalize, 0, 0, ISA_EXTERNALLEN, + &kdc_isa0, /* parent */ + 0, /* parentdata */ + DC_UNCONFIGURED, /* state */ + "Cyclades multiport board", + DC_CLS_MISC /* just an ordinary device */ +} }; - if (!*(base + CD1400_CCR)) - break; - } +static void +sioregisterdev(id) + struct isa_device *id; +{ + int unit; - /* clear the GFRCR register */ - *(base + CD1400_GFRCR) = 0; + unit = id->id_unit; + if (unit != 0) + kdc_sio[unit] = kdc_sio[0]; + kdc_sio[unit].kdc_unit = unit; + kdc_sio[unit].kdc_isa = id; + dev_attach(&kdc_sio[unit]); +} - /* issue a reset command */ - *(base + CD1400_CCR) = CD1400_CMD_RESET; +static int +sioprobe(dev) + struct isa_device *dev; +{ + int cyu; + u_char firmware_version; + cy_addr iobase; + int unit; - /* wait for the CD1400 to initialise itself */ - for (j = 0; j < 1000; j += 50) { - DELAY(50); /* wait 50 us */ + unit = dev->id_unit; + if ((u_int)unit >= NCY) + return (0); + cy_nr_cd1400s[unit] = 0; + sioregisterdev(dev); - /* retrieve firmware version */ - version = *(base + CD1400_GFRCR); - if (version) - break; - } + /* Cyclom-16Y hardware reset (Cyclom-8Ys don't care) */ + cy_inb((cy_addr)dev->id_maddr, CY16_RESET); /* XXX? */ - /* anything in the 40-4f range is fine */ - if ((version & 0xf0) != 0x40) { - return 0; - } - } + DELAY(500); /* wait for the board to get its act together */ - return 1; /* found */ -} + for (cyu = 0, iobase = (cy_addr)dev->id_maddr; cyu < CY_MAX_CD1400s; + ++cyu, iobase += CY_CD1400_MEMSIZE) { + int i; + /* wait for chip to become ready for new command */ + for (i = 0; i < 100; i += 50) { + DELAY(50); + if (!cd_inb(iobase, CD1400_CCR)) + break; + } -int -cyattach(struct isa_device *isdp) -{ -/* u_char unit = UNIT(isdp->id_unit); */ - int i, j, k; + /* clear the GFRCR register */ + cd_outb(iobase, CD1400_GFRCR, 0); - /* global variable used various routines */ - cyclom_base = (cy_addr)isdp->id_maddr; + /* issue a reset command */ + cd_outb(iobase, CD1400_CCR, + CD1400_CCR_CMDRESET | CD1400_CCR_FULLRESET); - for (i = 0, k = 0; i < CD1400s_PER_CYCLOM; i++) { - cy_addr base = cyclom_base + i * CD1400_MEMSIZE; + /* wait for the CD1400 to initialize itself */ + for (i = 0; i < 1000; i += 50) { + DELAY(50); - /* setup a 1ms clock tick */ - *(base + CD1400_PPR) = CD1400_CLOCK_25_1MS; + /* retrieve firmware version */ + firmware_version = cd_inb(iobase, CD1400_GFRCR); + if (firmware_version != 0) + break; + } - for (j = 0; j < CD1400_NO_OF_CHANNELS; j++, k++) { - struct cy *ip; + /* + * Anything in the 0x40-0x4F range is fine. + * If one CD1400 is bad then we don't support higher + * numbered good ones on this board. + */ + if ((firmware_version & 0xF0) != 0x40) + break; + ++cy_nr_cd1400s[unit]; + } + return (cy_nr_cd1400s[unit] == 0 ? 0 : -1); +} - /* - * grab some space. it'd be more polite to do this in cyopen(), - * but hey. - */ - info[k] = ip = malloc(sizeof(struct cy), M_DEVBUF, M_WAITOK); +static int +sioattach(isdp) + struct isa_device *isdp; +{ + int cyu; + cy_addr cy_iobase; + cy_addr iobase; + int ncyu; + int unit; - /* clear all sorts of junk */ - bzero(ip, sizeof(struct cy)); + unit = isdp->id_unit; + if ((u_int)unit >= NCY) + return (0); + ncyu = cy_nr_cd1400s[unit]; + if (ncyu == 0) + return (0); + isdp->id_ri_flags |= RI_FAST; + + cy_iobase = (cy_addr)isdp->id_maddr; + unit *= CY_MAX_PORTS; + for (cyu = 0, iobase = cy_iobase; cyu < ncyu; + ++cyu, iobase += CY_CD1400_MEMSIZE) { + int cdu; + + /* Set up a receive timeout period of than 1+ ms. */ + cd_outb(iobase, CD1400_PPR, + howmany(CY_CLOCK / CD1400_PPR_PRESCALER, 1000)); + + for (cdu = 0; cdu < CD1400_NO_OF_CHANNELS; ++cdu, ++unit) { + struct com_s *com; + int s; + + com = malloc(sizeof *com, M_DEVBUF, M_NOWAIT); + if (com == NULL) + break; + bzero(com, sizeof *com); + com->unit = unit; + com->dtr_wait = 3 * hz; + com->iptr = com->ibuf = com->ibuf1; + com->ibufend = com->ibuf1 + RS_IBUFSIZE; + com->ihighwater = com->ibuf1 + RS_IHIGHWATER; + com->obufs[0].l_head = com->obuf1; + com->obufs[1].l_head = com->obuf2; + + com->cy_iobase = cy_iobase; + com->iobase = iobase; - ip->base_addr = base; + /* + * We don't use all the flags from <sys/ttydefaults.h> since they + * are only relevant for logins. It's important to have echo off + * initially so that the line doesn't start blathering before the + * echo flag can be turned off. + */ + com->it_in.c_iflag = 0; + com->it_in.c_oflag = 0; + com->it_in.c_cflag = TTYDEF_CFLAG; + com->it_in.c_lflag = 0; + if (unit == comconsole && (COMCONSOLE || boothowto & RB_SERIAL)) { + com->it_in.c_iflag = TTYDEF_IFLAG; + com->it_in.c_oflag = TTYDEF_OFLAG; + com->it_in.c_cflag = TTYDEF_CFLAG | CLOCAL; + com->it_in.c_lflag = TTYDEF_LFLAG; + com->lt_out.c_cflag = com->lt_in.c_cflag = CLOCAL; + } + termioschars(&com->it_in); + com->it_in.c_ispeed = com->it_in.c_ospeed = comdefaultrate; + com->it_out = com->it_in; + + com->kdc = kdc_sio[0]; + com->kdc.kdc_name = "cy"; + com->kdc.kdc_unit = unit; + com->kdc.kdc_isa = isdp; + com->kdc.kdc_parent = &kdc_sio[isdp->id_unit]; + com->kdc.kdc_state = DC_IDLE; + com->kdc.kdc_description = + "Serial port: Cirrus Logic CD1400"; + com->kdc.kdc_class = DC_CLS_SERIAL; + dev_attach(&com->kdc); - /* initialise the channel, without resetting it first */ - cy_channel_init(k, 0); + s = spltty(); + com_addr(unit) = com; + splx(s); + } } - } + kdc_sio[isdp->id_unit].kdc_state = DC_BUSY; /* XXX */ - /* clear interrupts */ - *(cyclom_base + CYCLOM_CLEAR_INTR) = (u_char)0; + /* ensure an edge for the next interrupt */ + cy_outb(cy_iobase, CY_CLEAR_INTR, 0); - return 1; + return (1); } - int -cyopen(dev_t dev, int flag, int mode, struct proc *p) +sioopen(dev, flag, mode, p) + dev_t dev; + int flag; + int mode; + struct proc *p; { - u_int unit = UNIT(dev); - struct cy *infop; - cy_addr base; + struct com_s *com; + int error; + cy_addr iobase; + int mynor; + int s; struct tty *tp; - int error = 0; - u_char carrier; + int unit; - if (unit >= /* NCY * ? */ PORTS_PER_CYCLOM) + mynor = minor(dev); + unit = MINOR_TO_UNIT(mynor); + if ((u_int) unit >= NSIO || (com = com_addr(unit)) == NULL) return (ENXIO); - - infop = info[unit]; - base = infop->base_addr; -#ifdef __FreeBSD__ - infop->tty = &cy_tty[unit]; + if (mynor & CONTROL_MASK) + return (0); +#if 0 /* XXX TK2.0 */ + tp = com->tp = sio_tty[unit] = ttymalloc(sio_tty[unit]); #else - if (!cy_tty[unit]) - infop->tty = cy_tty[unit] = ttymalloc(); -#endif - tp = infop->tty; - - tp->t_oproc = cystart; - tp->t_param = cyparam; - tp->t_dev = dev; - if (!(tp->t_state & TS_ISOPEN)) { - tp->t_state |= TS_WOPEN; - ttychars(tp); - if (tp->t_ispeed == 0) { - tp->t_iflag = TTYDEF_IFLAG; - tp->t_oflag = TTYDEF_OFLAG; - tp->t_cflag = TTYDEF_CFLAG; - tp->t_lflag = TTYDEF_LFLAG; - tp->t_ispeed = tp->t_ospeed = cydefaultrate; + tp = com->tp = &sio_tty[unit]; +#endif + s = spltty(); + /* + * We jump to this label after all non-interrupted sleeps to pick + * up any changes of the device state. + */ +open_top: + while (com->state & CS_DTR_OFF) { + error = tsleep(&com->dtr_wait, TTIPRI | PCATCH, "cydtr", 0); + if (error != 0) + goto out; + } + com->kdc.kdc_state = DC_BUSY; + if (tp->t_state & TS_ISOPEN) { + /* + * The device is open, so everything has been initialized. + * Handle conflicts. + */ + if (mynor & CALLOUT_MASK) { + if (!com->active_out) { + error = EBUSY; + goto out; + } + } else { + if (com->active_out) { + if (flag & O_NONBLOCK) { + error = EBUSY; + goto out; + } + error = tsleep(&com->active_out, + TTIPRI | PCATCH, "cybi", 0); + if (error != 0) + goto out; + goto open_top; + } + } + if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) { + error = EBUSY; + goto out; } + } else { + /* + * The device isn't open, so there are no conflicts. + * Initialize it. Initialization is done twice in many + * cases: to preempt sleeping callin opens if we are + * callout, and to complete a callin open after DCD rises. + */ + tp->t_oproc = comstart; + tp->t_param = comparam; + tp->t_dev = dev; + tp->t_termios = mynor & CALLOUT_MASK + ? com->it_out : com->it_in; +#if 0 + (void)commctl(com, TIOCM_DTR | TIOCM_RTS, DMSET); + com->ftl_max = com->ftl_init; + com->poll = com->no_irq; + com->poll_output = com->loses_outints; +#endif + ++com->wopeners; + iobase = com->iobase; - (void) spltty(); - cy_channel_init(unit, 1); /* reset the hardware */ + /* reset this channel */ + cd_outb(iobase, CD1400_CAR, unit & CD1400_CAR_CHAN); + cd1400_channel_cmd(iobase, CD1400_CCR_CMDRESET); + + /* + * Resetting disables the transmitter and receiver as well as + * flushing the fifos so some of our cached state becomes + * invalid. The documentation suggests that all registers + * for the current channel are reset to defaults, but + * apparently none are. We wouldn't want DTR cleared. + */ + com->channel_control = 0; + + /* Encode per-board unit in LIVR for access in intr routines. */ + cd_outb(iobase, CD1400_LIVR, + (unit & CD1400_xIVR_CHAN) << CD1400_xIVR_CHAN_SHIFT); /* * raise dtr and generally set things up correctly. this * has the side-effect of selecting the appropriate cd1400 * channel, to help us with subsequent channel control stuff */ - cyparam(tp, &tp->t_termios); - - /* check carrier, and set t_state's TS_CARR_ON flag accordingly */ - infop->modem_sig = *(base + CD1400_MSVR); - carrier = infop->modem_sig & CD1400_MSVR_CD; - - if (carrier || (infop->carrier_mode >= 2)) - tp->t_state |= TS_CARR_ON; - else - tp->t_state &=~ TS_CARR_ON; - + error = comparam(tp, &tp->t_termios); + --com->wopeners; + if (error != 0) + goto out; /* - * enable modem & rx interrupts - relies on cyparam() - * having selected the appropriate cd1400 channel + * XXX we should goto open_top if comparam() slept. */ - infop->intr_enable = (1 << 7) | (1 << 4); - *(base + CD1400_SRER) = infop->intr_enable; - ttsetwater(tp); - } else if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) - return (EBUSY); - - if (!(flag & O_NONBLOCK)) - while (!(tp->t_cflag & CLOCAL) && - !(tp->t_state & TS_CARR_ON) && !error) - error = ttysleep(tp, (caddr_t)&tp->t_rawq, - TTIPRI|PCATCH, ttopen, 0); - (void) spl0(); - - if (!error) - error = (*linesw[(u_char)tp->t_line].l_open)(dev, tp); - return (error); -} /* end of cyopen() */ - - -void -cyclose_wakeup(void *arg) -{ - wakeup(arg); -} /* end of cyclose_wakeup() */ +#if 0 + if (com->hasfifo) { + /* + * (Re)enable and drain fifos. + * + * Certain SMC chips cause problems if the fifos + * are enabled while input is ready. Turn off the + * fifo if necessary to clear the input. We test + * the input ready bit after enabling the fifos + * since we've already enabled them in comparam() + * and to handle races between enabling and fresh + * input. + */ + while (TRUE) { + outb(iobase + com_fifo, + FIFO_RCV_RST | FIFO_XMT_RST + | FIFO_ENABLE | com->ftl); + DELAY(100); + if (!(inb(com->line_status_port) & LSR_RXRDY)) + break; + outb(iobase + com_fifo, 0); + DELAY(100); + (void) inb(com->data_port); + } + } + disable_intr(); + (void) inb(com->line_status_port); + (void) inb(com->data_port); + com->prev_modem_status = com->last_modem_status + = inb(com->modem_status_port); + outb(iobase + com_ier, IER_ERXRDY | IER_ETXRDY | IER_ERLS + | IER_EMSC); + enable_intr(); +#else /* !0 */ + /* XXX raise RTS too */ + (void)commctl(com, TIOCM_DTR | TIOCM_RTS, DMSET); + disable_intr(); + com->prev_modem_status = com->last_modem_status + = cd_inb(iobase, CD1400_MSVR2); + cd_outb(iobase, CD1400_SRER, + com->intr_enable + = CD1400_SRER_MDMCH | CD1400_SRER_RXDATA); + enable_intr(); +#endif /* 0 */ + /* + * Handle initial DCD. Callout devices get a fake initial + * DCD (trapdoor DCD). If we are callout, then any sleeping + * callin opens get woken up and resume sleeping on "cybi" + * instead of "cydcd". + */ + /* + * XXX `mynor & CALLOUT_MASK' should be + * `tp->t_cflag & (SOFT_CARRIER | TRAPDOOR_CARRIER) where + * TRAPDOOR_CARRIER is the default initial state for callout + * devices and SOFT_CARRIER is like CLOCAL except it hides + * the true carrier. + */ + if (com->prev_modem_status & MSR_DCD || mynor & CALLOUT_MASK) + (*linesw[tp->t_line].l_modem)(tp, 1); + } + /* + * Wait for DCD if necessary. + */ + if (!(tp->t_state & TS_CARR_ON) && !(mynor & CALLOUT_MASK) + && !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK)) { + ++com->wopeners; + error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "cydcd", 0); + --com->wopeners; + if (error != 0) + goto out; + goto open_top; + } + error = (*linesw[tp->t_line].l_open)(dev, tp); + disc_optim(tp, &tp->t_termios, com); + if (tp->t_state & TS_ISOPEN && mynor & CALLOUT_MASK) + com->active_out = TRUE; + siosettimeout(); +out: + splx(s); + if (!(tp->t_state & TS_ISOPEN) && com->wopeners == 0) + comhardclose(com); + return (error); +} int -cyclose(dev_t dev, int flag, int mode, struct proc *p) +sioclose(dev, flag, mode, p) + dev_t dev; + int flag; + int mode; + struct proc *p; { - u_int unit = UNIT(dev); - struct cy *infop = info[unit]; - struct tty *tp = infop->tty; - cy_addr base = infop->base_addr; + struct com_s *com; + int mynor; int s; + struct tty *tp; - (*linesw[(u_char)tp->t_line].l_close)(tp, flag); - + mynor = minor(dev); + if (mynor & CONTROL_MASK) + return (0); + com = com_addr(MINOR_TO_UNIT(mynor)); + tp = com->tp; s = spltty(); - /* select the appropriate channel on the CD1400 */ - *(base + CD1400_CAR) = (u_char)(unit & 0x03); - - /* disable this channel and lower DTR */ - infop->intr_enable = 0; - *(base + CD1400_SRER) = (u_char)0; /* no intrs */ - *(base + CD1400_DTR) = (u_char)CD1400_DTR_CLEAR; /* no DTR */ - infop->modem_sig &= ~CD1400_MSVR_DTR; - - /* disable receiver (leave transmitter enabled) */ - infop->channel_control = (1 << 4) | (1 << 3) | 1; - cd1400_channel_cmd(base, infop->channel_control); - splx(s); - + (*linesw[tp->t_line].l_close)(tp, flag); + disc_optim(tp, &tp->t_termios, com); + siostop(tp, FREAD | FWRITE); + comhardclose(com); ttyclose(tp); + siosettimeout(); + splx(s); #ifdef broken /* session holds a ref to the tty; can't deallocate */ ttyfree(tp); - infop->tty = cy_tty[unit] = (struct tty *)NULL; + com->tp = sio_tty[unit] = NULL; #endif + return (0); +} - if (infop->dtrwait) { - int error; - - timeout(cyclose_wakeup, (caddr_t)&infop->dtrwait, infop->dtrwait); - do { - error = tsleep((caddr_t)&infop->dtrwait, - TTIPRI|PCATCH, "cyclose", 0); - } while (error == ERESTART); - } +static void +comhardclose(com) + struct com_s *com; +{ + cy_addr iobase; + int s; + struct tty *tp; + int unit; - return 0; -} /* end of cyclose() */ + unit = com->unit; + iobase = com->iobase; + s = spltty(); +#if 0 + com->poll = FALSE; + com->poll_output = FALSE; +#endif + com->do_timestamp = 0; + cd_outb(iobase, CD1400_CAR, unit & CD1400_CAR_CHAN); +#if 0 + outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); +#endif +#ifdef KGDB + /* do not disable interrupts or hang up if debugging */ + if (kgdb_dev != makedev(commajor, unit)) +#endif + { +#if 0 + outb(iobase + com_ier, 0); +#else + disable_intr(); + cd_outb(iobase, CD1400_SRER, com->intr_enable = 0); + enable_intr(); +#endif + tp = com->tp; + if (tp->t_cflag & HUPCL + /* + * XXX we will miss any carrier drop between here and the + * next open. Perhaps we should watch DCD even when the + * port is closed; it is not sufficient to check it at + * the next open because it might go up and down while + * we're not watching. + */ + || !com->active_out + && !(com->prev_modem_status & MSR_DCD) + && !(com->it_in.c_cflag & CLOCAL) + || !(tp->t_state & TS_ISOPEN)) { + (void)commctl(com, TIOCM_DTR, DMBIC); + + /* Disable receiver (leave transmitter enabled). */ + com->channel_control = CD1400_CCR_CMDCHANCTL + | CD1400_CCR_XMTEN + | CD1400_CCR_RCVDIS; + cd1400_channel_cmd(iobase, com->channel_control); + + if (com->dtr_wait != 0) { + timeout(siodtrwakeup, com, com->dtr_wait); + com->state |= CS_DTR_OFF; + } + } + } + com->active_out = FALSE; + wakeup(&com->active_out); + wakeup(TSA_CARR_ON(tp)); /* restart any wopeners */ + if (!(com->state & CS_DTR_OFF) + && !(unit == comconsole && (COMCONSOLE || boothowto & RB_SERIAL))) + com->kdc.kdc_state = DC_IDLE; + splx(s); +} int -cyread(dev_t dev, struct uio *uio, int flag) +sioread(dev, uio, flag) + dev_t dev; + struct uio *uio; + int flag; { - u_int unit = UNIT(dev); - struct tty *tp = info[unit]->tty; - - return (*linesw[(u_char)tp->t_line].l_read)(tp, uio, flag); -} /* end of cyread() */ + int mynor; + struct tty *tp; + mynor = minor(dev); + if (mynor & CONTROL_MASK) + return (ENODEV); + tp = com_addr(MINOR_TO_UNIT(mynor))->tp; + return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); +} int -cywrite(dev_t dev, struct uio *uio, int flag) +siowrite(dev, uio, flag) + dev_t dev; + struct uio *uio; + int flag; { - u_int unit = UNIT(dev); - struct tty *tp = info[unit]->tty; + int mynor; + struct tty *tp; + int unit; + + mynor = minor(dev); + if (mynor & CONTROL_MASK) + return (ENODEV); + unit = MINOR_TO_UNIT(mynor); + tp = com_addr(unit)->tp; + /* + * (XXX) We disallow virtual consoles if the physical console is + * a serial port. This is in case there is a display attached that + * is not the console. In that situation we don't need/want the X + * server taking over the console. + */ + if (constty && unit == comconsole + && (COMCONSOLE || boothowto & RB_SERIAL)) + constty = NULL; #ifdef Smarts /* XXX duplicate ttwrite(), but without so much output processing on * CR & LF chars. Hardly worth the effort, given that high-throughput * sessions are raw anyhow. */ #else - return (*linesw[(u_char)tp->t_line].l_write)(tp, uio, flag); + return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); #endif -} /* end of cywrite() */ - - -#ifdef Smarts -/* standard line discipline input routine */ -int -cyinput(int c, struct tty *tp) -{ - /* XXX duplicate ttyinput(), but without the IXOFF/IXON/ISTRIP/IPARMRK - * bits, as they are done by the CD1400. Hardly worth the effort, - * given that high-throughput sessions are raw anyhow. - */ -} /* end of cyinput() */ -#endif /* Smarts */ - - -inline static void -service_upper_rx(int unit) -{ - struct cy *ip = info[unit]; - struct tty *tp = ip->tty; - struct cy_buf *buf; - int i; - u_char *ch; - - buf = ip->rx_buf; - - /* give service_rx() a new one */ - disable_intr(); /* faster than spltty() */ - ip->rx_buf = buf->next_buf; - enable_intr(); - - if (tp->t_state & TS_ISOPEN) { - ch = buf->buf; - i = buf->next_char - buf->buf; - -#ifdef FastRawInput - /* try to avoid calling the line discipline stuff if we can */ - if ((tp->t_line == 0) && - !(tp->t_iflag & (ICRNL | IMAXBEL | INLCR)) && - !(tp->t_lflag & (ECHO | ECHONL | ICANON | IEXTEN | - ISIG | PENDIN)) && - !(tp->t_state & (TS_CNTTB | TS_LNCH))) { - - i = b_to_q(ch, i, &tp->t_rawq); - if (i) { - /* - * we have no RTS flow control support on cy-8 - * boards, so this is really just tough luck - */ - - log(LOG_WARNING, "cy%d: tty input queue overflow\n", - unit); - } - - ttwakeup(tp); /* notify any readers */ - } - else -#endif /* FastRawInput */ - { - while (i--) - (*linesw[(u_char)tp->t_line].l_rint)((int)*ch++, tp); - } - } - - /* clear the buffer we've just processed */ - buf->next_char = buf->buf; - buf->free = CY_RX_BUF_SIZE; -} /* end of service_upper_rx() */ - +} -#ifdef TxBuffer static void -service_upper_tx(int unit) +siodtrwakeup(chan) + void *chan; { - struct cy *ip = info[unit]; - struct tty *tp = ip->tty; - - tp->t_state &=~ (TS_BUSY|TS_FLUSH); - - if (tp->t_outq.c_cc <= tp->t_lowat) { - if (tp->t_state&TS_ASLEEP) { - tp->t_state &= ~TS_ASLEEP; - wakeup((caddr_t)&tp->t_outq); - } - selwakeup(&tp->t_wsel); - } - - if (tp->t_outq.c_cc > 0) { - struct cy_ring *txq = &ip->tx_buf; - int free_count = CY_TX_BUF_SIZE - ip->tx_buf.used; - u_char *cp = txq->tail; - int count; - int chars_done; - - tp->t_state |= TS_BUSY; - - /* find the largest contig. copy we can do */ - count = ((txq->endish - cp) > free_count) ? - free_count : txq->endish - cp; - - count = ((cp + free_count) > txq->endish) ? - txq->endish - cp : free_count; - - /* copy the first slab */ - chars_done = q_to_b(&tp->t_outq, cp, count); - - /* check for wrap-around time */ - cp += chars_done; - if (cp == txq->endish) - cp = txq->buf; /* back to the start */ - - /* copy anything else, after we've wrapped around */ - if ((chars_done == count) && (count != free_count)) { - /* copy the second slab */ - count = q_to_b(&tp->t_outq, cp, free_count - count); - cp += count; - chars_done += count; - } - - /* - * update queue, protecting ourselves from any rampant - * lower-layers - */ - disable_intr(); - txq->tail = cp; - txq->used += chars_done; - enable_intr(); - } - - if (!tp->t_outq.c_cc) - tp->t_state &=~ TS_BUSY; -} /* end of service_upper_tx() */ -#endif /* TxBuffer */ + struct com_s *com; + com = (struct com_s *)chan; + com->state &= ~CS_DTR_OFF; + if (!(com->unit == comconsole && (COMCONSOLE || boothowto & RB_SERIAL))) + com->kdc.kdc_state = DC_IDLE; + wakeup(&com->dtr_wait); +} -inline static void -service_upper_mdm(int unit) +/* Interrupt routine for timekeeping purposes */ +void +siointrts(unit) + int unit; { - struct cy *ip = info[unit]; - - if (ip->carrier_delta) { - int carrier = ip->modem_sig & CD1400_MSVR_CD; - struct tty *tp = ip->tty; - - if (!(*linesw[(u_char)tp->t_line].l_modem)(tp, carrier)) { - cy_addr base = ip->base_addr; - - /* clear DTR */ - disable_intr(); - *(base + CD1400_CAR) = (u_char)(unit & 0x03); - *(base + CD1400_DTR) = (u_char)CD1400_DTR_CLEAR; - ip->modem_sig &= ~CD1400_MSVR_DTR; - ip->carrier_delta = 0; - enable_intr(); - } - else { - disable_intr(); - ip->carrier_delta = 0; - enable_intr(); - } - } -} /* end of service_upper_mdm() */ + /* + * XXX microtime() reenables CPU interrupts. We can't afford to + * be interrupted and don't want to slow down microtime(), so lock + * out interrupts in another way. + */ + outb(IO_ICU1 + 1, 0xff); + microtime(&intr_timestamp); + disable_intr(); + outb(IO_ICU1 + 1, imen); + siointr(unit); +} -/* upper level character processing routine */ void -cytimeout(void *ptr) -{ +siointr(unit) int unit; - - timeout_scheduled = 0; - +{ + int baseu; + cy_addr cy_iobase; + int cyu; + cy_addr iobase; + u_char status; + + baseu = unit * CY_MAX_PORTS; + cy_iobase = com_addr(baseu)->cy_iobase; + + /* check each CD1400 in turn */ + for (cyu = 0, iobase = cy_iobase; cyu < cy_nr_cd1400s[unit]; + ++cyu, iobase += CY_CD1400_MEMSIZE) { + /* poll to see if it has any work */ + status = cd_inb(iobase, CD1400_SVRR); + if (status == 0) + continue; #ifdef CyDebug - cy_timeouts++; + ++cy_svrr_probes; #endif - - /* check each port in turn */ - for (unit = 0; unit < NCY*PORTS_PER_CYCLOM; unit++) { - struct cy *ip = info[unit]; -#ifndef TxBuffer - struct tty *tp = ip->tty; + /* service requests as appropriate, giving priority to RX */ + if (status & CD1400_SVRR_RXRDY) { + struct com_s *com; + u_int count; + u_char *ioptr; + u_char line_status; + u_char recv_data; + u_char serv_type; +#ifdef PollMode + u_char save_car; + u_char save_rir; #endif - /* ignore anything that is not open */ - if (!ip->tty) - continue; +#ifdef PollMode + save_rir = cd_inb(iobase, CD1400_RIR); + save_car = cd_inb(iobase, CD1400_CAR); - /* - * any received chars to handle? (doesn't matter if intr routine - * kicks in while we're testing this) - */ - if (ip->rx_buf->free != CY_RX_BUF_SIZE) - service_upper_rx(unit); + /* enter rx service */ + cd_outb(iobase, CD1400_CAR, save_rir); -#ifdef TxBuffer - /* anything to add to the transmit buffer (low-water mark)? */ - if (ip->tx_buf.used < CY_TX_BUF_SIZE/2) - service_upper_tx(unit); + serv_type = cd_inb(iobase, CD1400_RIVR); + com = com_addr(baseu + + ((serv_type >> CD1400_xIVR_CHAN_SHIFT) + & CD1400_xIVR_CHAN)); #else - if (tp->t_outq.c_cc <= tp->t_lowat) { - if (tp->t_state&TS_ASLEEP) { - tp->t_state &= ~TS_ASLEEP; - wakeup((caddr_t)&tp->t_outq); - } - selwakeup(&tp->t_wsel); - } -#endif - - /* anything modem signals altered? */ - service_upper_mdm(unit); - - /* any overruns to log? */ -#ifdef LogOverruns - if (ip->fifo_overrun) { - /* - * turn off the alarm - not important enough to bother - * with interrupt protection. - */ - ip->fifo_overrun = 0; + /* ack receive service */ + serv_type = cy_inb(iobase, CY8_SVCACKR); - log(LOG_WARNING, "cy%d: receive fifo overrun\n", unit); - } + com = com_addr(baseu + + + ((serv_type >> CD1400_xIVR_CHAN_SHIFT) + & CD1400_xIVR_CHAN)); #endif - if (ip->rx_buf_overrun) { - /* - * turn off the alarm - not important enough to bother - * with interrupt protection. - */ - ip->rx_buf_overrun = 0; - - log(LOG_WARNING, "cy%d: receive buffer full\n", unit); - } - } -} /* cytimeout() */ - -inline static void -schedule_upper_service(void) -{ -#ifdef CyDebug - cy_timeout_req++; -#endif - - if (!timeout_scheduled) { - timeout(cytimeout, (caddr_t)0, 1); /* call next tick */ - timeout_scheduled = 1; - } -} /* end of schedule_upper_service() */ - - -/* initialise a channel on the cyclom board */ - -static void -cy_channel_init(dev_t dev, int reset) -{ - u_int unit = UNIT(dev); - int carrier_mode = CARRIER_MODE(dev); - struct cy *ip = info[unit]; - cy_addr base = ip->base_addr; - struct tty *tp = ip->tty; - struct cy_buf *buf, *next_buf; - int i; -#ifndef PollMode - u_char cd1400_unit; -#endif - - /* clear the structure and refill it */ - bzero(ip, sizeof(struct cy)); - ip->base_addr = base; - ip->tty = tp; - ip->carrier_mode = carrier_mode; - - /* select channel of the CD1400 */ - *(base + CD1400_CAR) = (u_char)(unit & 0x03); - - if (reset) - cd1400_channel_cmd(base, 0x80); /* reset the channel */ - - /* set LIVR to 0 - intr routines depend on this */ - *(base + CD1400_LIVR) = 0; - -#ifndef PollMode - /* set top four bits of {R,T,M}ICR to the cd1400 - * number, cd1400_unit - */ - cd1400_unit = unit / CD1400_NO_OF_CHANNELS; - *(base + CD1400_RICR) = (u_char)(cd1400_unit << 4); - *(base + CD1400_TICR) = (u_char)(cd1400_unit << 4); - *(base + CD1400_MICR) = (u_char)(cd1400_unit << 4); + if (com->do_timestamp) + /* XXX a little bloat here... */ + com->timestamp = intr_timestamp; + + if (serv_type & CD1400_RIVR_EXCEPTION) { + ++com->recv_exception; + line_status = cd_inb(iobase, CD1400_RDSR); + /* break/unnattached error bits or real input? */ + recv_data = cd_inb(iobase, CD1400_RDSR); +#ifndef SOFT_HOTCHAR + if (line_status & CD1400_RDSR_SPECIAL + && com->hotchar != 0) + setsofttty(); #endif - - ip->dtrwait = hz/4; /* quarter of a second */ - - /* setup low-level buffers */ - i = CY_RX_BUFS; - ip->rx_buf = next_buf = &ip->rx_buf_pool[0]; - while (i--) { - buf = &ip->rx_buf_pool[i]; - - buf->next_char = buf->buf; /* first char to use */ - buf->free = CY_RX_BUF_SIZE; /* i.e. empty */ - buf->next_buf = next_buf; /* where to go next */ - next_buf = buf; - } - -#ifdef TxBuffer - ip->tx_buf.endish = ip->tx_buf.buf + CY_TX_BUF_SIZE; - - /* clear the low-level tx buffer */ - ip->tx_buf.head = ip->tx_buf.tail = ip->tx_buf.buf; - ip->tx_buf.used = 0; -#endif - - /* clear the low-level rx buffer */ - ip->rx_buf->next_char = ip->rx_buf->buf; /* first char to use */ - ip->rx_buf->free = CY_RX_BUF_SIZE; /* completely empty */ -} /* end of cy_channel_init() */ - - -/* service a receive interrupt */ -inline static void -service_rx(int cd, caddr_t base) -{ - struct cy *infop; - unsigned count; - int ch; - u_char serv_type, channel; -#ifdef PollMode - u_char save_rir, save_car; +#if 1 /* XXX "intelligent" PFO error handling would break O error handling */ + if (line_status & (LSR_PE|LSR_FE|LSR_BI)) { + /* + Don't store PE if IGNPAR and BI if IGNBRK, + this hack allows "raw" tty optimization + works even if IGN* is set. + */ + if ( com->tp == NULL + || !(com->tp->t_state & TS_ISOPEN) + || (line_status & (LSR_PE|LSR_FE)) + && (com->tp->t_iflag & IGNPAR) + || (line_status & LSR_BI) + && (com->tp->t_iflag & IGNBRK)) + goto cont; + if ( (line_status & (LSR_PE|LSR_FE)) + && (com->tp->t_state & TS_CAN_BYPASS_L_RINT) + && ((line_status & LSR_FE) + || (line_status & LSR_PE) + && (com->tp->t_iflag & INPCK))) + recv_data = 0; + } +#endif /* 1 */ + ++com->bytes_in; +#ifdef SOFT_HOTCHAR + if (com->hotchar != 0 && recv_data == com->hotchar) + setsofttty(); #endif - - /* setup */ -#ifdef PollMode - save_rir = *(base + CD1400_RIR); - channel = cd * CD1400_NO_OF_CHANNELS + (save_rir & 0x3); - save_car = *(base + CD1400_CAR); - *(base + CD1400_CAR) = save_rir; /* enter modem service */ - serv_type = *(base + CD1400_RIVR); + ioptr = com->iptr; + if (ioptr >= com->ibufend) + CE_RECORD(com, CE_INTERRUPT_BUF_OVERFLOW); + else { + ++com_events; + ioptr[0] = recv_data; + ioptr[CE_INPUT_OFFSET] = line_status; + com->iptr = ++ioptr; + if (ioptr == com->ihighwater + && com->state & CS_RTS_IFLOW) +#if 0 + outb(com->modem_ctl_port, + com->mcr_image &= ~MCR_RTS); #else - serv_type = *(base + CD1400_SVCACKR); /* ack receive service */ - channel = ((u_char)*(base + CD1400_RICR)) >> 2; /* get cyclom channel # */ - -#ifdef CyDebug - if (channel >= PORTS_PER_CYCLOM) { - printf("cy: service_rx - channel %02x\n", channel); - panic("cy: service_rx - bad channel"); - } + cd_outb(iobase, CD1400_MSVR1, + com->mcr_image &= ~MCR_RTS); #endif + if (line_status & LSR_OE) + CE_RECORD(com, CE_OVERRUN); + } + goto cont; + } else { + int ifree; + + count = cd_inb(iobase, CD1400_RDCR); + com->bytes_in += count; + ioptr = com->iptr; + ifree = com->ibufend - ioptr; + if (count > ifree) { + count -= ifree; + com_events += ifree; + while (ifree-- != 0) { + recv_data = cd_inb(iobase, CD1400_RDSR); +#ifdef SOFT_HOTCHAR + if (com->hotchar != 0 + && recv_data == com->hotchar) + setsofttty(); #endif - - infop = info[channel]; - - /* read those chars */ - if (serv_type & CD1400_RIVR_EXCEPTION) { - /* read the exception status */ - u_char status = *(base + CD1400_RDSR); - - /* XXX is it a break? Do something if it is! */ - - /* XXX is IGNPAR not set? Store a null in the buffer. */ - -#ifdef LogOverruns - if (status & CD1400_RDSR_OVERRUN) { + ioptr[0] = recv_data; + ioptr[CE_INPUT_OFFSET] = 0; + ++ioptr; + } + com->delta_error_counts + [CE_INTERRUPT_BUF_OVERFLOW] += count; + do { + recv_data = cd_inb(iobase, CD1400_RDSR); +#ifdef SOFT_HOTCHAR + if (com->hotchar != 0 + && recv_data == com->hotchar) + setsofttty(); +#endif + } while (--count != 0); + } else { + if (ioptr <= com->ihighwater + && ioptr + count > com->ihighwater + && com->state & CS_RTS_IFLOW) #if 0 - ch |= TTY_PE; /* for SLIP */ + outb(com->modem_ctl_port, + com->mcr_image &= ~MCR_RTS); +#else + cd_outb(iobase, CD1400_MSVR1, + com->mcr_image &= ~MCR_RTS); #endif - infop->fifo_overrun++; - } + com_events += count; + do { + recv_data = cd_inb(iobase, CD1400_RDSR); +#ifdef SOFT_HOTCHAR + if (com->hotchar != 0 + && recv_data == com->hotchar) + setsofttty(); #endif - infop->recv_exception++; - } - else { - struct cy_buf *buf = infop->rx_buf; - - count = (u_char)*(base + CD1400_RDCR); /* how many to read? */ - infop->recv_normal += count; - if (buf->free < count) { - infop->rx_buf_overrun += count; - - /* read & discard everything */ - while (count--) - ch = (u_char)*(base + CD1400_RDSR); - } - else { - /* slurp it into our low-level buffer */ - buf->free -= count; - while (count--) { - ch = (u_char)*(base + CD1400_RDSR); /* read the char */ - *(buf->next_char++) = ch; + ioptr[0] = recv_data; + ioptr[CE_INPUT_OFFSET] = 0; + ++ioptr; + } while (--count != 0); } + com->iptr = ioptr; } - } +cont: + /* terminate service context */ #ifdef PollMode - *(base + CD1400_RIR) = (u_char)(save_rir & 0x3f); /* terminate service context */ + cd_outb(iobase, CD1400_RIR, + save_rir + & ~(CD1400_RIR_RDIREQ | CD1400_RIR_RBUSY)); + cd_outb(iobase, CD1400_CAR, save_car); #else - *(base + CD1400_EOSRR) = (u_char)0; /* terminate service context */ + cd_outb(iobase, CD1400_EOSRR, 0); #endif -} /* end of service_rx */ - - -/* service a transmit interrupt */ -inline static void -service_tx(int cd, caddr_t base) -{ - struct cy *ip; -#ifdef TxBuffer - struct cy_ring *txq; -#else - struct tty *tp; -#endif - u_char channel; + } + if (status & CD1400_SVRR_MDMCH) { + struct com_s *com; + u_char modem_status; #ifdef PollMode - u_char save_tir, save_car; + u_char save_car; + u_char save_mir; #else - u_char vector; + u_char vector; #endif - /* setup */ #ifdef PollMode - save_tir = *(base + CD1400_TIR); - channel = cd * CD1400_NO_OF_CHANNELS + (save_tir & 0x3); - save_car = *(base + CD1400_CAR); - *(base + CD1400_CAR) = save_tir; /* enter tx service */ -#else - vector = *(base + CD1400_SVCACKT); /* ack transmit service */ - channel = ((u_char)*(base + CD1400_TICR)) >> 2; /* get cyclom channel # */ - -#ifdef CyDebug - if (channel >= PORTS_PER_CYCLOM) { - printf("cy: service_tx - channel %02x\n", channel); - panic("cy: service_tx - bad channel"); - } -#endif -#endif - - ip = info[channel]; -#ifdef TxBuffer - txq = &ip->tx_buf; + save_mir = cd_inb(iobase, CD1400_MIR); + save_car = cd_inb(iobase, CD1400_CAR); - if (txq->used > 0) { - cy_addr base = ip->base_addr; - int count = min(CD1400_FIFOSIZE, txq->used); - int chars_done = count; - u_char *cp = txq->head; - u_char *buf_end = txq->endish; + /* enter modem service */ + cd_outb(iobase, CD1400_CAR, save_mir); - /* ip->state |= CY_BUSY; */ - while (count--) { - *(base + CD1400_TDR) = *cp++; - if (cp >= buf_end) - cp = txq->buf; - }; - txq->head = cp; - txq->used -= chars_done; /* important that this is atomic */ - ip->xmit += chars_done; - } - - /* - * disable tx intrs if no more chars to send. we re-enable - * them in cystart() - */ - if (!txq->used) { - ip->intr_enable &=~ (1 << 2); - *(base + CD1400_SRER) = ip->intr_enable; - /* ip->state &= ~CY_BUSY; */ - } + com = com_addr(baseu + cyu * CD1400_NO_OF_CHANNELS + + (save_mir & CD1400_MIR_CHAN)); #else - tp = ip->tty; - - if (!(tp->t_state & TS_TTSTOP) && (tp->t_outq.c_cc > 0)) { - cy_addr base = ip->base_addr; - int count = min(CD1400_FIFOSIZE, tp->t_outq.c_cc); + /* ack modem service */ + vector = cy_inb(iobase, CY8_SVCACKM); - ip->xmit += count; - tp->t_state |= TS_BUSY; - while (count--) - *(base + CD1400_TDR) = getc(&tp->t_outq); - } + com = com_addr(baseu + + ((vector >> CD1400_xIVR_CHAN_SHIFT) + & CD1400_xIVR_CHAN)); +#endif + ++com->mdm; + modem_status = cd_inb(iobase, CD1400_MSVR2); + if (modem_status != com->last_modem_status) { + /* + * Schedule high level to handle DCD changes. Note + * that we don't use the delta bits anywhere. Some + * UARTs mess them up, and it's easy to remember the + * previous bits and calculate the delta. + */ + com->last_modem_status = modem_status; + if (!(com->state & CS_CHECKMSR)) { + com_events += LOTS_OF_EVENTS; + com->state |= CS_CHECKMSR; + setsofttty(); + } - /* - * disable tx intrs if no more chars to send. we re-enable them - * in cystart() - */ - if (!tp->t_outq.c_cc) { - ip->intr_enable &=~ (1 << 2); - *(base + CD1400_SRER) = ip->intr_enable; - tp->t_state &= ~TS_BUSY; - } +#ifdef SOFT_CTS_OFLOW + /* handle CTS change immediately for crisp flow ctl */ + if (com->state & CS_CTS_OFLOW) { + if (modem_status & MSR_CTS) { + com->state |= CS_ODEVREADY; + if (com->state >= (CS_BUSY | CS_TTGO + | CS_ODEVREADY) + && !(com->intr_enable + & CD1400_SRER_TXRDY)) + cd_outb(iobase, CD1400_SRER, + com->intr_enable + |= CD1400_SRER_TXRDY); + } else { + com->state &= ~CS_ODEVREADY; + if (com->intr_enable & CD1400_SRER_TXRDY) + cd_outb(iobase, CD1400_SRER, + com->intr_enable + &= ~CD1400_SRER_TXRDY); + } + } #endif + } + /* terminate service context */ #ifdef PollMode - *(base + CD1400_TIR) = (u_char)(save_tir & 0x3f); /* terminate service context */ + cd_outb(iobase, CD1400_MIR, + save_mir + & ~(CD1400_MIR_RDIREQ | CD1400_MIR_RBUSY)); + cd_outb(iobase, CD1400_CAR, save_car); #else - *(base + CD1400_EOSRR) = (u_char)0; /* terminate service context */ + cd_outb(iobase, CD1400_EOSRR, 0); #endif -} /* end of service_tx */ - - -/* service a modem status interrupt */ -inline static void -service_mdm(int cd, caddr_t base) -{ - struct cy *infop; - u_char channel, deltas; + } + if (status & CD1400_SVRR_TXRDY) { + struct com_s *com; #ifdef PollMode - u_char save_mir, save_car; + u_char save_car; + u_char save_tir; #else - u_char vector; + u_char vector; #endif - /* setup */ #ifdef PollMode - save_mir = *(base + CD1400_MIR); - channel = cd * CD1400_NO_OF_CHANNELS + (save_mir & 0x3); - save_car = *(base + CD1400_CAR); - *(base + CD1400_CAR) = save_mir; /* enter modem service */ + save_tir = cd_inb(iobase, CD1400_TIR); + save_car = cd_inb(iobase, CD1400_CAR); + + /* enter tx service */ + cd_outb(iobase, CD1400_CAR, save_tir); + com = com_addr(baseu + + cyu * CD1400_NO_OF_CHANNELS + + (save_tir & CD1400_TIR_CHAN)); #else - vector = *(base + CD1400_SVCACKM); /* ack modem service */ - channel = ((u_char)*(base + CD1400_MICR)) >> 2; /* get cyclom channel # */ + /* ack transmit service */ + vector = cy_inb(iobase, CY8_SVCACKT); -#ifdef CyDebug - if (channel >= PORTS_PER_CYCLOM) { - printf("cy: service_mdm - channel %02x\n", channel); - panic("cy: service_mdm - bad channel"); - } + com = com_addr(baseu + + ((vector >> CD1400_xIVR_CHAN_SHIFT) + & CD1400_xIVR_CHAN)); #endif -#endif - - infop = info[channel]; - - /* read the siggies and see what's changed */ - infop->modem_sig = (u_char)*(base + CD1400_MSVR); - deltas = (u_char)*(base + CD1400_MISR); - if ((infop->carrier_mode <= 2) && (deltas & CD1400_MISR_CDd)) - /* something for the upper layer to deal with */ - infop->carrier_delta = 1; - - infop->mdm++; + if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) { + u_char *ioptr; + u_int ocount; + + ioptr = com->obufq.l_head; + ocount = com->obufq.l_tail - ioptr; + if (ocount > CD1400_TX_FIFO_SIZE) + ocount = CD1400_TX_FIFO_SIZE; + com->bytes_out += ocount; + do + cd_outb(iobase, CD1400_TDR, *ioptr++); + while (--ocount != 0); + com->obufq.l_head = ioptr; + if (ioptr >= com->obufq.l_tail) { + struct lbq *qp; + + qp = com->obufq.l_next; + qp->l_queued = FALSE; + qp = qp->l_next; + if (qp != NULL) { + com->obufq.l_head = qp->l_head; + com->obufq.l_tail = qp->l_tail; + com->obufq.l_next = qp; + } else { + /* output just completed */ + com->state &= ~CS_BUSY; + cd_outb(iobase, CD1400_SRER, + com->intr_enable + &= ~CD1400_SRER_TXRDY); + } + if (!(com->state & CS_ODONE)) { + com_events += LOTS_OF_EVENTS; + com->state |= CS_ODONE; + setsofttty(); /* handle at high level ASAP */ + } + } + } - /* terminate service context */ + /* terminate service context */ #ifdef PollMode - *(base + CD1400_MIR) = (u_char)(save_mir & 0x3f); + cd_outb(iobase, CD1400_TIR, + save_tir + & ~(CD1400_TIR_RDIREQ | CD1400_TIR_RBUSY)); + cd_outb(iobase, CD1400_CAR, save_car); #else - *(base + CD1400_EOSRR) = (u_char)0; + cd_outb(iobase, CD1400_EOSRR, 0); #endif -} /* end of service_mdm */ - - -int -cyintr(int unit) -{ - int cd; - u_char status; - - /* check each CD1400 in turn */ - for (cd = 0; cd < CD1400s_PER_CYCLOM; cd++) { - cy_addr base = cyclom_base + cd*CD1400_MEMSIZE; - - /* poll to see if it has any work */ - while (status = (u_char)*(base + CD1400_SVRR)) { -#ifdef CyDebug - cy_svrr_probes++; -#endif - /* service requests as appropriate, giving priority to RX */ - if (status & CD1400_SVRR_RX) - service_rx(cd, base); - if (status & CD1400_SVRR_TX) - service_tx(cd, base); - if (status & CD1400_SVRR_MDM) - service_mdm(cd, base); + } } - } - /* request upper level service to deal with whatever happened */ - schedule_upper_service(); + /* ensure an edge for the next interrupt */ + cy_outb(cy_iobase, CY_CLEAR_INTR, 0); - /* re-enable interrupts on the cyclom */ - *(cyclom_base + CYCLOM_CLEAR_INTR) = (u_char)0; - - return 1; + schedsofttty(); } +static void +siointr1(com) + struct com_s *com; +{ +} int -cyioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p) +sioioctl(dev, cmd, data, flag, p) + dev_t dev; + int cmd; + caddr_t data; + int flag; + struct proc *p; { - int unit = UNIT(dev); - struct cy *infop = info[unit]; - struct tty *tp = infop->tty; + struct com_s *com; int error; + cy_addr iobase; + int mynor; + int s; + struct tty *tp; +#if defined(COMPAT_43) || defined(COMPAT_SUNOS) + int oldcmd; + struct termios term; +#endif - error = (*linesw[(u_char)tp->t_line].l_ioctl)(tp, cmd, data, flag, p); - if (error >= 0) + mynor = minor(dev); + com = com_addr(MINOR_TO_UNIT(mynor)); + iobase = com->iobase; + if (mynor & CONTROL_MASK) { + struct termios *ct; + + switch (mynor & CONTROL_MASK) { + case CONTROL_INIT_STATE: + ct = mynor & CALLOUT_MASK ? &com->it_out : &com->it_in; + break; + case CONTROL_LOCK_STATE: + ct = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in; + break; + default: + return (ENODEV); /* /dev/nodev */ + } + switch (cmd) { + case TIOCSETA: + error = suser(p->p_ucred, &p->p_acflag); + if (error != 0) + return (error); + *ct = *(struct termios *)data; + return (0); + case TIOCGETA: + *(struct termios *)data = *ct; + return (0); + case TIOCGETD: + *(int *)data = TTYDISC; + return (0); + case TIOCGWINSZ: + bzero(data, sizeof(struct winsize)); + return (0); + default: + return (ENOTTY); + } + } + tp = com->tp; +#if defined(COMPAT_43) || defined(COMPAT_SUNOS) + term = tp->t_termios; + oldcmd = cmd; + error = ttsetcompat(tp, &cmd, data, &term); + if (error != 0) return (error); - error = ttioctl(tp, cmd, data, flag -#ifdef NetBSD - , p + if (cmd != oldcmd) + data = (caddr_t)&term; #endif - ); + if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { + int cc; + struct termios *dt = (struct termios *)data; + struct termios *lt = mynor & CALLOUT_MASK + ? &com->lt_out : &com->lt_in; + + dt->c_iflag = (tp->t_iflag & lt->c_iflag) + | (dt->c_iflag & ~lt->c_iflag); + dt->c_oflag = (tp->t_oflag & lt->c_oflag) + | (dt->c_oflag & ~lt->c_oflag); + dt->c_cflag = (tp->t_cflag & lt->c_cflag) + | (dt->c_cflag & ~lt->c_cflag); + dt->c_lflag = (tp->t_lflag & lt->c_lflag) + | (dt->c_lflag & ~lt->c_lflag); + for (cc = 0; cc < NCCS; ++cc) + if (lt->c_cc[cc] != 0) + dt->c_cc[cc] = tp->t_cc[cc]; + if (lt->c_ispeed != 0) + dt->c_ispeed = tp->t_ispeed; + if (lt->c_ospeed != 0) + dt->c_ospeed = tp->t_ospeed; + } + error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); if (error >= 0) return (error); - + s = spltty(); + error = ttioctl(tp, cmd, data, flag); + disc_optim(tp, &tp->t_termios, com); + if (error >= 0) { + splx(s); + return (error); + } + cd_outb(iobase, CD1400_CAR, MINOR_TO_UNIT(mynor) & CD1400_CAR_CHAN); switch (cmd) { -#ifdef notyet /* sigh - more junk to do XXX */ +#if 0 case TIOCSBRK: + outb(iobase + com_cfcr, com->cfcr_image |= CFCR_SBREAK); break; case TIOCCBRK: + outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); break; +#endif /* 0 */ case TIOCSDTR: + (void)commctl(com, TIOCM_DTR, DMBIS); break; case TIOCCDTR: + (void)commctl(com, TIOCM_DTR, DMBIC); break; - case TIOCMSET: + (void)commctl(com, *(int *)data, DMSET); break; case TIOCMBIS: + (void)commctl(com, *(int *)data, DMBIS); break; case TIOCMBIC: + (void)commctl(com, *(int *)data, DMBIC); break; -#endif /* notyet */ - - case TIOCMGET: { - int bits = 0; - u_char status = infop->modem_sig; - - if (status & CD1400_MSVR_DTR) bits |= TIOCM_DTR | TIOCM_RTS; - if (status & CD1400_MSVR_CD) bits |= TIOCM_CD; - if (status & CD1400_MSVR_CTS) bits |= TIOCM_CTS; - if (status & CD1400_MSVR_DSR) bits |= TIOCM_DSR; -#ifdef CYCLOM_16 - if (status & CD1400_MSVR_RI) bits |= TIOCM_RI; -#endif - if (infop->channel_control & 0x02) bits |= TIOCM_LE; - *(int *)data = bits; + case TIOCMGET: + *(int *)data = commctl(com, 0, DMGET); break; - } - -#ifdef TIOCMSBIDIR - case TIOCMSBIDIR: - return (ENOTTY); -#endif /* TIOCMSBIDIR */ - -#ifdef TIOCMGBIDIR - case TIOCMGBIDIR: - return (ENOTTY); -#endif /* TIOCMGBIDIR */ - -#ifdef TIOCMSDTRWAIT case TIOCMSDTRWAIT: - /* must be root to set dtr delay */ - if (p->p_ucred->cr_uid != 0) - return(EPERM); - - infop->dtrwait = *(u_int *)data; - break; -#endif /* TIOCMSDTRWAIT */ - -#ifdef TIOCMGDTRWAIT + /* must be root since the wait applies to following logins */ + error = suser(p->p_ucred, &p->p_acflag); + if (error != 0) { + splx(s); + return (error); + } + com->dtr_wait = *(int *)data * hz / 100; + break; case TIOCMGDTRWAIT: - *(u_int *)data = infop->dtrwait; + *(int *)data = com->dtr_wait * 100 / hz; + break; + case TIOCTIMESTAMP: + com->do_timestamp = TRUE; + *(struct timeval *)data = com->timestamp; break; -#endif /* TIOCMGDTRWAIT */ - default: + splx(s); return (ENOTTY); } + splx(s); + return (0); +} - return 0; -} /* end of cyioctl() */ - - -int -cyparam(struct tty *tp, struct termios *t) +void +siopoll() { - u_char unit = UNIT(tp->t_dev); - struct cy *infop = info[unit]; - cy_addr base = infop->base_addr; - int cflag = t->c_cflag; - int iflag = t->c_iflag; - int ispeed, ospeed; - int itimeout; - int iprescaler, oprescaler; - int s; - u_char cor_change = 0; - u_char opt; - - if (!t->c_ispeed) - t->c_ispeed = t->c_ospeed; - - s = spltty(); - - /* select the appropriate channel on the CD1400 */ - *(base + CD1400_CAR) = unit & 0x03; - - /* handle DTR drop on speed == 0 trick */ - if (t->c_ospeed == 0) { - *(base + CD1400_DTR) = CD1400_DTR_CLEAR; - infop->modem_sig &= ~CD1400_MSVR_DTR; - } - else { - *(base + CD1400_DTR) = CD1400_DTR_SET; - infop->modem_sig |= CD1400_MSVR_DTR; - } + int unit; - /* set baud rates if they've changed from last time */ - - if ((ospeed = cyspeed(t->c_ospeed, &oprescaler)) < 0) - return EINVAL; - *(base + CD1400_TBPR) = (u_char)ospeed; - *(base + CD1400_TCOR) = (u_char)oprescaler; - - if ((ispeed = cyspeed(t->c_ispeed, &iprescaler)) < 0) - return EINVAL; - *(base + CD1400_RBPR) = (u_char)ispeed; - *(base + CD1400_RCOR) = (u_char)iprescaler; +#ifdef CyDebug + ++cy_timeouts; +#endif + if (com_events == 0) + return; +repeat: + for (unit = 0; unit < NSIO; ++unit) { + u_char *buf; + struct com_s *com; + u_char *ibuf; + cy_addr iobase; + int incc; + struct tty *tp; + + com = com_addr(unit); + if (com == NULL) + continue; + tp = com->tp; + if (tp == NULL) { + /* + * XXX forget any events related to closed devices + * (actually never opened devices) so that we don't + * loop. + */ + disable_intr(); + incc = com->iptr - com->ibuf; + com->iptr = com->ibuf; + if (com->state & CS_CHECKMSR) { + incc += LOTS_OF_EVENTS; + com->state &= ~CS_CHECKMSR; + } + com_events -= incc; + enable_intr(); + if (incc != 0) + log(LOG_DEBUG, + "sio%d: %d events for device with no tp\n", + unit, incc); + continue; + } - /* - * set receive time-out period - * generate a rx interrupt if no new chars are received in - * this many ticks - * don't bother comparing old & new VMIN, VTIME and ispeed - it - * can't be much worse just to calculate and set it each time! - * certainly less hassle. :-) - */ + /* switch the role of the low-level input buffers */ + if (com->iptr == (ibuf = com->ibuf)) { + buf = NULL; /* not used, but compiler can't tell */ + incc = 0; + } else { + buf = ibuf; + disable_intr(); + incc = com->iptr - buf; + com_events -= incc; + if (ibuf == com->ibuf1) + ibuf = com->ibuf2; + else + ibuf = com->ibuf1; + com->ibufend = ibuf + RS_IBUFSIZE; + com->ihighwater = ibuf + RS_IHIGHWATER; + com->iptr = ibuf; - /* - * calculate minimum timeout period: - * 5 ms or the time it takes to receive 1 char, rounded up to the - * next ms, whichever is greater - */ - if (t->c_ispeed > 0) { - itimeout = (t->c_ispeed > 2200) ? 5 : (10000/t->c_ispeed + 1); + /* + * There is now room for another low-level buffer full + * of input, so enable RTS if it is now disabled and + * there is room in the high-level buffer. + */ + /* + * XXX this used not to look at CS_RTS_IFLOW. The + * change is to allow full control of MCR_RTS via + * ioctls after turning CS_RTS_IFLOW off. Check + * for races. We shouldn't allow the ioctls while + * CS_RTS_IFLOW is on. + */ + if ((com->state & CS_RTS_IFLOW) + && !(com->mcr_image & MCR_RTS) + && !(tp->t_state & TS_TBLOCK)) +#if 0 + outb(com->modem_ctl_port, + com->mcr_image |= MCR_RTS); +#else + iobase = com->iobase, + cd_outb(iobase, CD1400_CAR, + unit & CD1400_CAR_CHAN), + cd_outb(iobase, CD1400_MSVR1, + com->mcr_image |= MCR_RTS); +#endif + enable_intr(); + com->ibuf = ibuf; + } - /* if we're using VTIME as an inter-char timeout, and it is set to - * be longer than the minimum calculated above, go for it - */ - if (t->c_cc[VMIN] && t->c_cc[VTIME] && t->c_cc[VTIME]*10 > itimeout) - itimeout = t->c_cc[VTIME]*10; + if (com->state & CS_CHECKMSR) { + u_char delta_modem_status; - /* store it, taking care not to overflow the byte-sized register */ - *(base + CD1400_RTPR) = (u_char)((itimeout <= 255) ? itimeout : 255); + disable_intr(); + delta_modem_status = com->last_modem_status + ^ com->prev_modem_status; + com->prev_modem_status = com->last_modem_status; + com_events -= LOTS_OF_EVENTS; + com->state &= ~CS_CHECKMSR; + enable_intr(); + if (delta_modem_status & MSR_DCD) + (*linesw[tp->t_line].l_modem) + (tp, com->prev_modem_status & MSR_DCD); + } + if (com->state & CS_ODONE) { + disable_intr(); + com_events -= LOTS_OF_EVENTS; + com->state &= ~CS_ODONE; + if (!(com->state & CS_BUSY)) + com->tp->t_state &= ~TS_BUSY; + enable_intr(); + (*linesw[tp->t_line].l_start)(tp); + } + if (incc <= 0 || !(tp->t_state & TS_ISOPEN)) + continue; + /* + * XXX only do this when we bypass ttyinput. + */ + if (tp->t_rawq.c_cc + incc >= RB_I_HIGH_WATER + && (com->state & CS_RTS_IFLOW || tp->t_iflag & IXOFF) + && !(tp->t_state & TS_TBLOCK) + /* + * XXX - need flow control for all line disciplines. + * Only have it in standard one now. + */ + && linesw[tp->t_line].l_rint == ttyinput) { + int putc_status = 0; + + if ((tp->t_iflag & IXOFF + && tp->t_cc[VSTOP] != _POSIX_VDISABLE + && (putc_status = putc(tp->t_cc[VSTOP], + &tp->t_outq)) == 0) + || com->state & CS_RTS_IFLOW) { + tp->t_state |= TS_TBLOCK; + ttstart(tp); + if (putc_status != 0) + /* Try again later. */ + tp->t_state &= ~TS_TBLOCK; + } + } + /* + * Avoid the grotesquely inefficient lineswitch routine + * (ttyinput) in "raw" mode. It usually takes about 450 + * instructions (that's without canonical processing or echo!). + * slinput is reasonably fast (usually 40 instructions plus + * call overhead). + */ + if (tp->t_state & TS_CAN_BYPASS_L_RINT) { + tk_nin += incc; + tk_rawcc += incc; + tp->t_rawcc += incc; + com->delta_error_counts[CE_TTY_BUF_OVERFLOW] + += b_to_q((char *)buf, incc, &tp->t_rawq); + ttwakeup(tp); + if (tp->t_state & TS_TTSTOP + && (tp->t_iflag & IXANY + || tp->t_cc[VSTART] == tp->t_cc[VSTOP])) { + tp->t_state &= ~TS_TTSTOP; + tp->t_lflag &= ~FLUSHO; + ttstart(tp); + } + } else { + do { + u_char line_status; + int recv_data; + + line_status = (u_char) buf[CE_INPUT_OFFSET]; + recv_data = (u_char) *buf++; + if (line_status + & (LSR_BI | LSR_FE | LSR_OE | LSR_PE)) { + if (line_status & LSR_BI) + recv_data |= TTY_BI; + if (line_status & LSR_FE) + recv_data |= TTY_FE; + if (line_status & LSR_OE) + recv_data |= TTY_OE; + if (line_status & LSR_PE) + recv_data |= TTY_PE; + } + (*linesw[tp->t_line].l_rint)(recv_data, tp); + } while (--incc > 0); + } + if (com_events == 0) + break; } + if (com_events >= LOTS_OF_EVENTS) + goto repeat; +} +static int +comparam(tp, t) + struct tty *tp; + struct termios *t; +{ + int bits; + int cflag; + struct com_s *com; + u_char cor_change; + int idivisor; + int iflag; + cy_addr iobase; + int iprescaler; + int itimeout; + int odivisor; + int oprescaler; + u_char opt; + int s; + int unit; + + /* do historical conversions */ + if (t->c_ispeed == 0) + t->c_ispeed = t->c_ospeed; + + /* check requested parameters */ + idivisor = comspeed(t->c_ispeed, &iprescaler); + if (idivisor < 0) + return (EINVAL); + odivisor = comspeed(t->c_ospeed, &oprescaler); + if (odivisor < 0) + return (EINVAL); + + /* parameters are OK, convert them to the com struct and the device */ + unit = DEV_TO_UNIT(tp->t_dev); + com = com_addr(unit); + iobase = com->iobase; + s = spltty(); + cd_outb(iobase, CD1400_CAR, unit & CD1400_CAR_CHAN); + if (odivisor == 0) + (void)commctl(com, TIOCM_DTR, DMBIC); /* hang up line */ + else + (void)commctl(com, TIOCM_DTR, DMBIS); + + cd_outb(iobase, CD1400_RBPR, idivisor); + cd_outb(iobase, CD1400_RCOR, iprescaler); + cd_outb(iobase, CD1400_TBPR, odivisor); + cd_outb(iobase, CD1400_TCOR, oprescaler); /* * channel control * receiver enable * transmitter enable (always set) */ - opt = (1 << 4) | (1 << 3) | ((cflag & CREAD) ? (1 << 1) : 1); - if (opt != infop->channel_control) { - infop->channel_control = opt; - cd1400_channel_cmd(base, opt); + cflag = t->c_cflag; + opt = CD1400_CCR_CMDCHANCTL | CD1400_CCR_XMTEN + | (cflag & CREAD ? CD1400_CCR_RCVEN : CD1400_CCR_RCVDIS); + if (opt != com->channel_control) { + com->channel_control = opt; + cd1400_channel_cmd(iobase, opt); } #ifdef Smarts /* set special chars */ - if (t->c_cc[VSTOP] != _POSIX_VDISABLE && - (t->c_cc[VSTOP] != infop->spec_char[0])) { - *(base + CD1400_SCHR1) = infop->spec_char[0] = t->c_cc[VSTOP]; - } - if (t->c_cc[VSTART] != _POSIX_VDISABLE && - (t->c_cc[VSTART] != infop->spec_char[1])) { - *(base + CD1400_SCHR2) = infop->spec_char[0] = t->c_cc[VSTART]; - } - if (t->c_cc[VINTR] != _POSIX_VDISABLE && - (t->c_cc[VINTR] != infop->spec_char[2])) { - *(base + CD1400_SCHR3) = infop->spec_char[0] = t->c_cc[VINTR]; - } - if (t->c_cc[VSUSP] != _POSIX_VDISABLE && - (t->c_cc[VSUSP] != infop->spec_char[3])) { - *(base + CD1400_SCHR4) = infop->spec_char[0] = t->c_cc[VSUSP]; - } + /* XXX if one is _POSIX_VDISABLE, can't use some others */ + if (t->c_cc[VSTOP] != _POSIX_VDISABLE) + cd_outb(iobase, CD1400_SCHR1, t->c_cc[VSTOP]); + if (t->c_cc[VSTART] != _POSIX_VDISABLE) + cd_outb(iobase, CD1400_SCHR2, t->c_cc[VSTART]); + if (t->c_cc[VINTR] != _POSIX_VDISABLE) + cd_outb(iobase, CD1400_SCHR3, t->c_cc[VINTR]); + if (t->c_cc[VSUSP] != _POSIX_VDISABLE) + cd_outb(iobase, CD1400_SCHR4, t->c_cc[VSUSP]); #endif /* @@ -1294,41 +1772,84 @@ cyparam(struct tty *tp, struct termios *t) opt = 0; /* parity */ if (cflag & PARENB) { - if (cflag & PARODD) - opt |= 1 << 7; - opt |= 2 << 5; /* normal parity mode */ + if (cflag & PARODD) + opt |= CD1400_COR1_PARODD; + opt |= CD1400_COR1_PARNORMAL; } + iflag = t->c_iflag; if (!(iflag & INPCK)) - opt |= 1 << 4; /* ignore parity */ + opt |= CD1400_COR1_NOINPCK; + bits = 1 + 1; /* stop bits */ - if (cflag & CSTOPB) - opt |= 2 << 2; + if (cflag & CSTOPB) { + ++bits; + opt |= CD1400_COR1_STOP2; + } /* char length */ - opt |= (cflag & CSIZE) >> 8; /* nasty, but fast */ - if (opt != infop->cor[0]) { - cor_change |= 1 << 1; - *(base + CD1400_COR1) = opt; + switch (cflag & CSIZE) { + case CS5: + bits += 5; + opt |= CD1400_COR1_CS5; + break; + case CS6: + bits += 6; + opt |= CD1400_COR1_CS6; + break; + case CS7: + bits += 7; + opt |= CD1400_COR1_CS7; + break; + default: + bits += 8; + opt |= CD1400_COR1_CS8; + break; + } + cor_change = 0; + if (opt != com->cor[0]) { + cor_change |= CD1400_CCR_COR1; + cd_outb(iobase, CD1400_COR1, com->cor[0] = opt); } /* + * Set receive time-out period, normally to max(one char time, 5 ms). + */ + if (t->c_ispeed == 0) + itimeout = cd_inb(iobase, CD1400_RTPR); + else { + itimeout = (1000 * bits + t->c_ispeed - 1) / t->c_ispeed; +#ifdef SOFT_HOTCHAR +#define MIN_RTP 1 +#else +#define MIN_RTP 5 +#endif + if (itimeout < MIN_RTP) + itimeout = MIN_RTP; + } + if (!(t->c_lflag & ICANON) && t->c_cc[VMIN] != 0 && t->c_cc[VTIME] != 0 + && t->c_cc[VTIME] * 10 > itimeout) + itimeout = t->c_cc[VTIME] * 10; + if (itimeout > 255) + itimeout = 255; + cd_outb(iobase, CD1400_RTPR, itimeout); + + /* * set channel option register 2 - * flow control */ opt = 0; #ifdef Smarts if (iflag & IXANY) - opt |= 1 << 7; /* auto output restart on any char after XOFF */ + opt |= CD1400_COR2_IXANY; if (iflag & IXOFF) - opt |= 1 << 6; /* auto XOFF output flow-control */ + opt |= CD1400_COR2_IXOFF; #endif -#ifndef ALWAYS_RTS_CTS +#ifndef SOFT_CTS_OFLOW if (cflag & CCTS_OFLOW) + opt |= CD1400_COR2_CCTS_OFLOW; #endif - opt |= 1 << 1; /* auto CTS flow-control */ - - if (opt != infop->cor[1]) { - cor_change |= 1 << 2; - *(base + CD1400_COR2) = opt; + if (opt != com->cor[1]) { + cor_change |= CD1400_CCR_COR2; + cd_outb(iobase, CD1400_COR2, com->cor[1] = opt); } /* @@ -1336,24 +1857,22 @@ cyparam(struct tty *tp, struct termios *t) * receiver FIFO interrupt threshold * flow control */ - opt = RxFifoThreshold; /* rx fifo threshold */ + opt = RxFifoThreshold; #ifdef Smarts if (t->c_lflag & ICANON) - opt |= 1 << 6; /* detect INTR & SUSP chars */ + opt |= CD1400_COR3_SCD34; /* detect INTR & SUSP chars */ if (iflag & IXOFF) - opt |= (1 << 5) | (1 << 4); /* transparent in-band flow control */ + /* detect and transparently handle START and STOP chars */ + opt |= CD1400_COR3_FCT | CD1400_COR3_SCD12; #endif - if (opt != infop->cor[2]) { - cor_change |= 1 << 3; - *(base + CD1400_COR3) = opt; + if (opt != com->cor[2]) { + cor_change |= CD1400_CCR_COR3; + cd_outb(iobase, CD1400_COR3, com->cor[2] = opt); } - /* notify the CD1400 if COR1-3 have changed */ - if (cor_change) { - cor_change |= 1 << 6; /* COR change flag */ - cd1400_channel_cmd(base, cor_change); - } + if (cor_change) + cd1400_channel_cmd(iobase, CD1400_CCR_CMDCORCHG | cor_change); /* * set channel option register 4 - @@ -1363,7 +1882,7 @@ cyparam(struct tty *tp, struct termios *t) */ opt = 0; if (iflag & IGNCR) - opt |= 1 << 7; + opt |= CD1400_COR4_IGNCR; #ifdef Smarts /* * we need a new ttyinput() for this, as we don't want to @@ -1371,305 +1890,712 @@ cyparam(struct tty *tp, struct termios *t) * synchronisation problems */ if (iflag & ICRNL) - opt |= 1 << 6; + opt |= CD1400_COR4_ICRNL; if (iflag & INLCR) - opt |= 1 << 5; + opt |= CD1400_COR4_INLCR; #endif if (iflag & IGNBRK) - opt |= 1 << 4; + opt |= CD1400_COR4_IGNBRK; if (!(iflag & BRKINT)) - opt |= 1 << 3; + opt |= CD1400_COR4_NOBRKINT; +#if 0 + /* XXX using this "intelligence" breaks reporting of overruns. */ if (iflag & IGNPAR) -#ifdef LogOverruns - opt |= 0; /* broken chars cause receive exceptions */ -#else - opt |= 2; /* discard broken chars */ -#endif + opt |= CD1400_COR4_PFO_DISCARD; else { - if (iflag & PARMRK) - opt |= 4; /* precede broken chars with 0xff 0x0 */ - else -#ifdef LogOverruns - opt |= 0; /* broken chars cause receive exceptions */ + if (iflag & PARMRK) + opt |= CD1400_COR4_PFO_ESC; + else + opt |= CD1400_COR4_PFO_NUL; + } #else - opt |= 3; /* convert framing/parity errs to nulls */ + opt |= CD1400_COR4_PFO_EXCEPTION; #endif - } - *(base + CD1400_COR4) = opt; + cd_outb(iobase, CD1400_COR4, opt); /* * set channel option register 5 - */ opt = 0; if (iflag & ISTRIP) - opt |= 1 << 7; - if (t->c_iflag & IEXTEN) { - opt |= 1 << 6; /* enable LNEXT (e.g. ctrl-v quoting) handling */ - } + opt |= CD1400_COR5_ISTRIP; + if (t->c_iflag & IEXTEN) + /* enable LNEXT (e.g. ctrl-v quoting) handling */ + opt |= CD1400_COR5_LNEXT; #ifdef Smarts if (t->c_oflag & ONLCR) - opt |= 1 << 1; + opt |= CD1400_COR5_ONLCR; if (t->c_oflag & OCRNL) - opt |= 1; + opt |= CD1400_COR5_OCRNL; #endif - *(base + CD1400_COR5) = opt; + cd_outb(iobase, CD1400_COR5, opt); /* + * XXX we probably alway want to track carrier changes, so that + * TS_CARR_ON gives the true carrier. If we don't track them, + * then we should set TS_CARR_ON when CLOCAL drops. + */ + /* * set modem change option register 1 * generate modem interrupts on which 1 -> 0 input transitions * also controls auto-DTR output flow-control, which we don't use */ - opt = (cflag & CLOCAL) ? 0 : 1 << 4; /* CD */ - *(base + CD1400_MCOR1) = opt; + opt = cflag & CLOCAL ? 0 : CD1400_MCOR1_CDzd; +#ifdef SOFT_CTS_OFLOW + if (cflag & CCTS_OFLOW) + opt |= CD1400_MCOR1_CTSzd; +#endif + cd_outb(iobase, CD1400_MCOR1, opt); /* * set modem change option register 2 * generate modem interrupts on specific 0 -> 1 input transitions */ - opt = (cflag & CLOCAL) ? 0 : 1 << 4; /* CD */ - *(base + CD1400_MCOR2) = opt; + opt = cflag & CLOCAL ? 0 : CD1400_MCOR2_CDod; +#ifdef SOFT_CTS_OFLOW + if (cflag & CCTS_OFLOW) + opt |= CD1400_MCOR2_CTSod; +#endif + cd_outb(iobase, CD1400_MCOR2, opt); - splx(s); + /* + * XXX should have done this long ago, but there is too much state + * to change all atomically. + */ + disable_intr(); + + com->state &= ~CS_TTGO; + if (!(tp->t_state & TS_TTSTOP)) + com->state |= CS_TTGO; + if (cflag & CRTS_IFLOW) + com->state |= CS_RTS_IFLOW; /* XXX - secondary changes? */ + else + com->state &= ~CS_RTS_IFLOW; - return 0; -} /* end of cyparam */ + /* + * Set up state to handle output flow control. + * XXX - worth handling MDMBUF (DCD) flow control at the lowest level? + * Now has 10+ msec latency, while CTS flow has 50- usec latency. + */ + com->state |= CS_ODEVREADY; +#ifdef SOFT_CTS_OFLOW + com->state &= ~CS_CTS_OFLOW; + if (cflag & CCTS_OFLOW) { + com->state |= CS_CTS_OFLOW; + if (!(com->last_modem_status & MSR_CTS)) + com->state &= ~CS_ODEVREADY; + } +#endif + /* XXX shouldn't call functions while intrs are disabled. */ + disc_optim(tp, t, com); +#if 0 + /* + * Recover from fiddling with CS_TTGO. We used to call siointr1() + * unconditionally, but that defeated the careful discarding of + * stale input in sioopen(). + */ + if (com->state >= (CS_BUSY | CS_TTGO)) + siointr1(com); +#endif + if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) { + if (!(com->intr_enable & CD1400_SRER_TXRDY)) + cd_outb(iobase, CD1400_SRER, + com->intr_enable |= CD1400_SRER_TXRDY); + } else { + if (com->intr_enable & CD1400_SRER_TXRDY) + cd_outb(iobase, CD1400_SRER, + com->intr_enable &= ~CD1400_SRER_TXRDY); + } + enable_intr(); + splx(s); + return (0); +} -void -cystart(struct tty *tp) +static void +comstart(tp) + struct tty *tp; { - u_char unit = UNIT(tp->t_dev); - struct cy *infop = info[unit]; - cy_addr base = infop->base_addr; + struct com_s *com; + cy_addr iobase; int s; +#ifdef CyDebug + bool_t started; +#endif + int unit; + + unit = DEV_TO_UNIT(tp->t_dev); + com = com_addr(unit); + iobase = com->iobase; + s = spltty(); #ifdef CyDebug - infop->start_count++; + ++com->start_count; + started = FALSE; #endif - /* check the flow-control situation */ - if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) + disable_intr(); + cd_outb(iobase, CD1400_CAR, unit & CD1400_CAR_CHAN); + if (tp->t_state & TS_TTSTOP) { + com->state &= ~CS_TTGO; + if (com->intr_enable & CD1400_SRER_TXRDY) + cd_outb(iobase, CD1400_SRER, + com->intr_enable &= ~CD1400_SRER_TXRDY); + } else { + com->state |= CS_TTGO; + if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY) + && !(com->intr_enable & CD1400_SRER_TXRDY)) + cd_outb(iobase, CD1400_SRER, + com->intr_enable |= CD1400_SRER_TXRDY); + } + if (tp->t_state & TS_TBLOCK) { + if (com->mcr_image & MCR_RTS && com->state & CS_RTS_IFLOW) +#if 0 + outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); +#else + cd_outb(iobase, CD1400_MSVR1, + com->mcr_image &= ~MCR_RTS); +#endif + } else { + /* + * XXX don't raise MCR_RTS if CTS_RTS_IFLOW is off. Set it + * appropriately in comparam() if RTS-flow is being changed. + * Check for races. + */ + if (!(com->mcr_image & MCR_RTS) && com->iptr < com->ihighwater) +#if 0 + outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); +#else + cd_outb(iobase, CD1400_MSVR1, + com->mcr_image |= MCR_RTS); +#endif + } + enable_intr(); + if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) { + splx(s); return; + } + if (tp->t_outq.c_cc != 0) { + struct lbq *qp; + struct lbq *next; + if (!com->obufs[0].l_queued) { +#ifdef CyDebug + started = TRUE; +#endif + com->obufs[0].l_tail + = com->obuf1 + q_to_b(&tp->t_outq, com->obuf1, + sizeof com->obuf1); + com->obufs[0].l_next = NULL; + com->obufs[0].l_queued = TRUE; + disable_intr(); + if (com->state & CS_BUSY) { + qp = com->obufq.l_next; + while ((next = qp->l_next) != NULL) + qp = next; + qp->l_next = &com->obufs[0]; + } else { + com->obufq.l_head = com->obufs[0].l_head; + com->obufq.l_tail = com->obufs[0].l_tail; + com->obufq.l_next = &com->obufs[0]; + com->state |= CS_BUSY; + if (com->state >= (CS_BUSY | CS_TTGO + | CS_ODEVREADY)) + cd_outb(iobase, CD1400_SRER, + com->intr_enable + |= CD1400_SRER_TXRDY); + } + enable_intr(); + } + if (tp->t_outq.c_cc != 0 && !com->obufs[1].l_queued) { +#ifdef CyDebug + started = TRUE; +#endif + com->obufs[1].l_tail + = com->obuf2 + q_to_b(&tp->t_outq, com->obuf2, + sizeof com->obuf2); + com->obufs[1].l_next = NULL; + com->obufs[1].l_queued = TRUE; + disable_intr(); + if (com->state & CS_BUSY) { + qp = com->obufq.l_next; + while ((next = qp->l_next) != NULL) + qp = next; + qp->l_next = &com->obufs[1]; + } else { + com->obufq.l_head = com->obufs[1].l_head; + com->obufq.l_tail = com->obufs[1].l_tail; + com->obufq.l_next = &com->obufs[1]; + com->state |= CS_BUSY; + if (com->state >= (CS_BUSY | CS_TTGO + | CS_ODEVREADY)) + cd_outb(iobase, CD1400_SRER, + com->intr_enable + |= CD1400_SRER_TXRDY); + } + enable_intr(); + } + tp->t_state |= TS_BUSY; + } +#ifdef CyDebug + if (started) + ++com->start_real; +#endif +#if 0 + disable_intr(); + if (com->state >= (CS_BUSY | CS_TTGO)) { + siointr1(com); /* fake interrupt to start output */ + enable_intr(); +#endif + +#if 0 /* XXX TK2.0 */ + if (tp->t_state & (TS_SO_OCOMPLETE | TS_SO_OLOWAT) || tp->t_wsel) + ttwwakeup(tp); +#else if (tp->t_outq.c_cc <= tp->t_lowat) { - if (tp->t_state&TS_ASLEEP) { + if (tp->t_state & TS_ASLEEP) { tp->t_state &= ~TS_ASLEEP; - wakeup((caddr_t)&tp->t_outq); + wakeup(TSA_OLOWAT(tp)); } selwakeup(&tp->t_wsel); } - -#ifdef TxBuffer - service_upper_tx(unit); /* feed the monster */ #endif - s = spltty(); + splx(s); +} - if (!(infop->intr_enable & (1 << 2))) { - /* select the channel */ - *(base + CD1400_CAR) = unit & (u_char)3; +void +siostop(tp, rw) + struct tty *tp; + int rw; +{ + struct com_s *com; + cy_addr iobase; + int unit; + + unit = DEV_TO_UNIT(tp->t_dev); + com = com_addr(unit); + iobase = com->iobase; + disable_intr(); + if (rw & FWRITE) { + com->obufs[0].l_queued = FALSE; + com->obufs[1].l_queued = FALSE; + if (com->state & CS_ODONE) + com_events -= LOTS_OF_EVENTS; + com->state &= ~(CS_ODONE | CS_BUSY); + com->tp->t_state &= ~TS_BUSY; + } + if (rw & FREAD) { + com_events -= (com->iptr - com->ibuf); + com->iptr = com->ibuf; + } + if (tp->t_state & TS_TTSTOP) { + com->state &= ~CS_TTGO; + if (com->intr_enable & CD1400_SRER_TXRDY) { + cd_outb(iobase, CD1400_CAR, unit & CD1400_CAR_CHAN); + cd_outb(iobase, CD1400_SRER, + com->intr_enable &= ~CD1400_SRER_TXRDY); + } + } else { + com->state |= CS_TTGO; + if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY) + && !(com->intr_enable & CD1400_SRER_TXRDY)) { + cd_outb(iobase, CD1400_CAR, unit & CD1400_CAR_CHAN); + cd_outb(iobase, CD1400_SRER, + com->intr_enable |= CD1400_SRER_TXRDY); + } + } + enable_intr(); - /* (re)enable interrupts to set things in motion */ - infop->intr_enable |= (1 << 2); - *(base + CD1400_SRER) = infop->intr_enable; + /* XXX should clear h/w fifos too. */ +} + +struct tty * +siodevtotty(dev) + dev_t dev; +{ + int mynor; + int unit; - infop->start_real++; + mynor = minor(dev); + if (mynor & CONTROL_MASK) + return (NULL); + unit = MINOR_TO_UNIT(mynor); + if ((u_int) unit >= NSIO) + return (NULL); + return (&sio_tty[unit]); +} + +static int +commctl(com, bits, how) + struct com_s *com; + int bits; + int how; +{ + cy_addr iobase; + int mcr; + int msr; + + if (how == DMGET) { + if (com->channel_control & CD1400_CCR_RCVEN) + bits |= TIOCM_LE; + mcr = com->mcr_image; + if (mcr & MCR_DTR) + bits |= TIOCM_DTR; + if (mcr & MCR_RTS) + /* XXX wired on for Cyclom-8Ys */ + bits |= TIOCM_RTS; + msr = com->prev_modem_status; + if (msr & MSR_CTS) + bits |= TIOCM_CTS; + if (msr & MSR_DCD) + bits |= TIOCM_CD; + if (msr & MSR_DSR) + bits |= TIOCM_DSR; + if (msr & MSR_RI) + /* XXX not connected except for Cyclom-16Y? */ + bits |= TIOCM_RI; + return (bits); } + iobase = com->iobase; + mcr = 0; + if (bits & TIOCM_DTR) + mcr |= MCR_DTR; + if (bits & TIOCM_RTS) + mcr |= MCR_RTS; + disable_intr(); + switch (how) { + case DMSET: + com->mcr_image = mcr; + cd_outb(iobase, CD1400_MSVR1, mcr); + cd_outb(iobase, CD1400_MSVR2, mcr); + break; + case DMBIS: + com->mcr_image = mcr = com->mcr_image | mcr; + cd_outb(iobase, CD1400_MSVR1, mcr); + cd_outb(iobase, CD1400_MSVR2, mcr); + break; + case DMBIC: + com->mcr_image = mcr = com->mcr_image & ~mcr; + cd_outb(iobase, CD1400_MSVR1, mcr); + cd_outb(iobase, CD1400_MSVR2, mcr); + break; + } + enable_intr(); + return (0); +} - splx(s); -} /* end of cystart() */ +static void +siosettimeout() +{ + struct com_s *com; + bool_t someopen; + int unit; + /* + * Set our timeout period to 1 second if no polled devices are open. + * Otherwise set it to max(1/200, 1/hz). + * Enable timeouts iff some device is open. + */ + untimeout(comwakeup, (void *)NULL); + sio_timeout = hz; + someopen = FALSE; + for (unit = 0; unit < NSIO; ++unit) { + com = com_addr(unit); + if (com != NULL && com->tp != NULL + && com->tp->t_state & TS_ISOPEN) { + someopen = TRUE; +#if 0 + if (com->poll || com->poll_output) { + sio_timeout = hz > 200 ? hz / 200 : 1; + break; + } +#endif + } + } + if (someopen) { + sio_timeouts_until_log = hz / sio_timeout; + timeout(comwakeup, (void *)NULL, sio_timeout); + } else { + /* Flush error messages, if any. */ + sio_timeouts_until_log = 1; + comwakeup((void *)NULL); + untimeout(comwakeup, (void *)NULL); + } +} -int -cystop(struct tty *tp, int flag) +static void +comwakeup(chan) + void *chan; { - u_char unit = UNIT(tp->t_dev); - struct cy *ip = info[unit]; - cy_addr base = ip->base_addr; - int s; + struct com_s *com; + int unit; - s = spltty(); + timeout(comwakeup, (void *)NULL, sio_timeout); - /* select the channel */ - *(base + CD1400_CAR) = unit & 3; +#if 0 + /* + * Recover from lost output interrupts. + * Poll any lines that don't use interrupts. + */ + for (unit = 0; unit < NSIO; ++unit) { + com = com_addr(unit); + if (com != NULL + && (com->state >= (CS_BUSY | CS_TTGO) || com->poll)) { + disable_intr(); + siointr1(com); + enable_intr(); + } + } +#endif - /* halt output by disabling transmit interrupts */ - ip->intr_enable &=~ (1 << 2); - *(base + CD1400_SRER) = ip->intr_enable; + /* + * Check for and log errors, but not too often. + */ + if (--sio_timeouts_until_log > 0) + return; + sio_timeouts_until_log = hz / sio_timeout; + for (unit = 0; unit < NSIO; ++unit) { + int errnum; - splx(s); + com = com_addr(unit); + if (com == NULL) + continue; + for (errnum = 0; errnum < CE_NTYPES; ++errnum) { + u_int delta; + u_long total; - return 0; + disable_intr(); + delta = com->delta_error_counts[errnum]; + com->delta_error_counts[errnum] = 0; + enable_intr(); + if (delta == 0) + continue; + total = com->error_counts[errnum] += delta; + log(LOG_ERR, "cy%d: %u more %s%s (total %lu)\n", + unit, delta, error_desc[errnum], + delta == 1 ? "" : "s", total); +#if 0 + /* + * XXX if we resurrect this then we should move + * the dropping of the ftl to somewhere with less + * latency. + */ + if (errnum == CE_OVERRUN && com->hasfifo + && com->ftl > FIFO_TRIGGER_1) { + static u_char ftl_in_bytes[] = + { 1, 4, 8, 14, }; + + com->ftl_init = FIFO_TRIGGER_8; +#define FIFO_TRIGGER_DELTA FIFO_TRIGGER_4 + com->ftl_max = + com->ftl -= FIFO_TRIGGER_DELTA; + outb(com->iobase + com_fifo, + FIFO_ENABLE | com->ftl); + log(LOG_DEBUG, + "sio%d: reduced fifo trigger level to %d\n", + unit, + ftl_in_bytes[com->ftl + / FIFO_TRIGGER_DELTA]); + } +#endif + } + } } -struct tty * -cydevtotty(dev_t dev) +static void +disc_optim(tp, t, com) + struct tty *tp; + struct termios *t; + struct com_s *com; { - u_char unit = UNIT(dev); - - if (unit >= /* NCY * ? */ PORTS_PER_CYCLOM) - return NULL; +#ifndef SOFT_HOTCHAR + cy_addr iobase; + u_char opt; +#endif - return info[unit]->tty; + /* + * XXX can skip a lot more cases if Smarts. Maybe + * (IGNCR | ISTRIP | IXOFF | IXON) in c_iflag. But perhaps we + * shouldn't skip if (TS_CNTTB | TS_LNCH) is set in t_state. + */ + if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP + | IXOFF | IXON)) + && (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK)) + && (!(t->c_iflag & PARMRK) || + (t->c_iflag & (IGNPAR|IGNBRK)) == (IGNPAR|IGNBRK)) + && !(t->c_lflag & (ECHO | ECHONL | ICANON | IEXTEN | ISIG + | PENDIN)) + && linesw[tp->t_line].l_rint == ttyinput) + tp->t_state |= TS_CAN_BYPASS_L_RINT; + else + tp->t_state &= ~TS_CAN_BYPASS_L_RINT; + /* + * Prepare to reduce input latency for packet + * discplines with a end of packet character. + */ + if (tp->t_line == SLIPDISC) + com->hotchar = 0xc0; + else if (tp->t_line == PPPDISC) + com->hotchar = 0x7e; + else + com->hotchar = 0; +#ifndef SOFT_HOTCHAR + iobase = com->iobase; + cd_outb(iobase, CD1400_CAR, com->unit & CD1400_CAR_CHAN); + opt = com->cor[2] & ~CD1400_COR3_SCD34; + if (com->hotchar != 0) { + cd_outb(iobase, CD1400_SCHR3, com->hotchar); + cd_outb(iobase, CD1400_SCHR4, com->hotchar); + opt |= CD1400_COR3_SCD34; + } + if (opt != com->cor[2]) { + cd_outb(iobase, CD1400_COR3, com->cor[2] = opt); + cd1400_channel_cmd(com->iobase, + CD1400_CCR_CMDCORCHG | CD1400_CCR_COR3); + } +#endif } +#ifdef Smarts +/* standard line discipline input routine */ int -cyspeed(int speed, int *prescaler_io) +cyinput(c, tp) + int c; + struct tty *tp; { - int actual; - int error; - int divider; - int prescaler; - int prescaler_unit; - - if (speed == 0) - return 0; - - if (speed < 0 || speed > 150000) - return -1; - - /* determine which prescaler to use */ - for (prescaler_unit = 4, prescaler = 2048; prescaler_unit; - prescaler_unit--, prescaler >>= 2) { - if (CYCLOM_CLOCK/prescaler/speed > 63) - break; - } - - divider = (CYCLOM_CLOCK/prescaler*2/speed + 1)/2; /* round off */ - if (divider > 255) - divider = 255; - actual = CYCLOM_CLOCK/prescaler/divider; - error = ((actual-speed)*2000/speed +1)/2; /* percentage */ - - /* 3.0% max error tolerance */ - if (error < -30 || error > 30) - return -1; + /* XXX duplicate ttyinput(), but without the IXOFF/IXON/ISTRIP/IPARMRK + * bits, as they are done by the CD1400. Hardly worth the effort, + * given that high-throughput sessions are raw anyhow. + */ +} +#endif /* Smarts */ + +static int +comspeed(speed, prescaler_io) + speed_t speed; + int *prescaler_io; +{ + int actual; + int error; + int divider; + int prescaler; + int prescaler_unit; + + if (speed == 0) + return (0); + if (speed < 0 || speed > 150000) + return (-1); + + /* determine which prescaler to use */ + for (prescaler_unit = 4, prescaler = 2048; prescaler_unit; + prescaler_unit--, prescaler >>= 2) { + if (CY_CLOCK / prescaler / speed > 63) + break; + } + + divider = (CY_CLOCK / prescaler * 2 / speed + 1) / 2; /* round off */ + if (divider > 255) + divider = 255; + actual = CY_CLOCK/prescaler/divider; + error = ((actual - speed) * 2000 / speed + 1) / 2; /* percentage */ + + /* 3.0% max error tolerance */ + if (error < -30 || error > 30) + return (-1); #if 0 - printf("prescaler = %d (%d)\n", prescaler, prescaler_unit); - printf("divider = %d (%x)\n", divider, divider); - printf("actual = %d\n", actual); - printf("error = %d\n", error); + printf("prescaler = %d (%d)\n", prescaler, prescaler_unit); + printf("divider = %d (%x)\n", divider, divider); + printf("actual = %d\n", actual); + printf("error = %d\n", error); #endif - *prescaler_io = prescaler_unit; - return divider; -} /* end of cyspeed() */ - + *prescaler_io = prescaler_unit; + return (divider); +} static void -cd1400_channel_cmd(cy_addr base, u_char cmd) +cd1400_channel_cmd(iobase, cmd) + cy_addr iobase; + int cmd; { - /* XXX hsu@clinet.fi: This is always more dependent on ISA bus speed, + /* XXX hsu@clinet.fi: This is always more dependent on ISA bus speed, as the card is probed every round? Replaced delaycount with 8k. Either delaycount has to be implemented in FreeBSD or more sensible way of doing these should be implemented. DELAY isn't enough here. */ - unsigned maxwait = 5 * 8 * 1024; /* approx. 5 ms */ + u_int maxwait = 5 * 8 * 1024; /* approx. 5 ms */ /* wait for processing of previous command to complete */ - while (*(base + CD1400_CCR) && maxwait--) - ; + while (cd_inb(iobase, CD1400_CCR) && maxwait--) + ; if (!maxwait) log(LOG_ERR, "cy: channel command timeout (%d loops) - arrgh\n", 5 * 8 * 1024); - *(base + CD1400_CCR) = cmd; -} /* end of cd1400_channel_cmd() */ - - -#ifdef CyDebug -/* useful in ddb */ -void -cyclear(void) -{ - /* clear the timeout request */ - disable_intr(); - timeout_scheduled = 0; - enable_intr(); + cd_outb(iobase, CD1400_CCR, cmd); } -void -cyclearintr(void) -{ - /* clear interrupts */ - *(cyclom_base + CYCLOM_CLEAR_INTR) = (u_char)0; -} - -int -cyparam_dummy(struct tty *tp, struct termios *t) -{ - return 0; -} - -void -cyset(int unit, int active) -{ - if (unit < 0 || unit >= /* NCY *? */ PORTS_PER_CYCLOM) { - printf("bad unit number %d\n", unit); - return; - } -#ifdef __FreeBSD__ - cy_tty[unit].t_param = active ? cyparam : cyparam_dummy; -#else - cy_tty[unit]->t_param = active ? cyparam : cyparam_dummy; -#endif -} - - +#ifdef CyDebug /* useful in ddb */ void -cystatus(int unit) +cystatus(unit) + int unit; { - struct cy *infop = info[unit]; - struct tty *tp = infop->tty; - cy_addr base = infop->base_addr; + struct com_s *com; + cy_addr iobase; + u_int ocount; + struct tty *tp; + com = com_addr(unit); printf("info for channel %d\n", unit); printf("------------------\n"); - - printf("cd1400 base address:\t0x%x\n", (int)infop->base_addr); - - /* select the port */ - *(base + CD1400_CAR) = (u_char)unit; - - printf("saved channel_control:\t%02x\n", infop->channel_control); - printf("saved cor1:\t\t%02x\n", infop->cor[0]); - printf("service request enable reg:\t%02x (%02x cached)\n", - (u_char)*(base + CD1400_SRER), infop->intr_enable); - printf("service request register:\t%02x\n", - (u_char)*(base + CD1400_SVRR)); + printf("total cyclom service probes:\t%d\n", cy_svrr_probes); + printf("calls to upper layer:\t\t%d\n", cy_timeouts); + if (com == NULL) + return; + iobase = com->iobase; + printf("\n"); + printf("cd1400 base address:\\tt%p\n", iobase); + cd_outb(iobase, CD1400_CAR, unit & CD1400_CAR_CHAN); + printf("saved channel_control:\t\t0x%02x\n", com->channel_control); + printf("saved cor1-3:\t\t\t0x%02x 0x%02x 0x%02x\n", + com->cor[0], com->cor[1], com->cor[2]); + printf("service request enable reg:\t0x%02x (0x%02x cached)\n", + cd_inb(iobase, CD1400_SRER), com->intr_enable); + printf("service request register:\t0x%02x\n", + cd_inb(iobase, CD1400_SVRR)); + printf("modem status:\t\t\t0x%02x (0x%02x cached)\n", + cd_inb(iobase, CD1400_MSVR2), com->prev_modem_status); + printf("rx/tx/mdm interrupt registers:\t0x%02x 0x%02x 0x%02x\n", + cd_inb(iobase, CD1400_RIR), cd_inb(iobase, CD1400_TIR), + cd_inb(iobase, CD1400_MIR)); printf("\n"); - printf("modem status:\t\t\t%02x (%02x cached)\n", - (u_char)*(base + CD1400_MSVR), infop->modem_sig); - printf("rx/tx/mdm interrupt registers:\t%02x %02x %02x\n", - (u_char)*(base + CD1400_RIR), (u_char)*(base + CD1400_TIR), - (u_char)*(base + CD1400_MIR)); + printf("com state:\t\t\t0x%02x\n", com->state); + printf("calls to comstart():\t\t%d (%d useful)\n", + com->start_count, com->start_real); + printf("rx buffer chars free:\t\t%d\n", com->iptr - com->ibuf); + ocount = 0; + if (com->obufs[0].l_queued) + ocount += com->obufs[0].l_tail - com->obufs[0].l_head; + if (com->obufs[1].l_queued) + ocount += com->obufs[1].l_tail - com->obufs[1].l_head; + printf("tx buffer chars:\t\t%u\n", ocount); + printf("received chars:\t\t\t%d\n", com->bytes_in); + printf("received exceptions:\t\t%d\n", com->recv_exception); + printf("modem signal deltas:\t\t%d\n", com->mdm); + printf("transmitted chars:\t\t%d\n", com->bytes_out); printf("\n"); - if (tp) { - printf("tty state:\t\t\t%04x\n", tp->t_state); + tp = com->tp; + if (tp != NULL) { + printf("tty state:\t\t\t0x%08x\n", tp->t_state); printf("upper layer queue lengths:\t%d raw, %d canon, %d output\n", - tp->t_rawq.c_cc, tp->t_canq.c_cc, tp->t_outq.c_cc); - } - else + tp->t_rawq.c_cc, tp->t_canq.c_cc, tp->t_outq.c_cc); + } else printf("tty state:\t\t\tclosed\n"); - printf("\n"); +} +#endif /* CyDebug */ - printf("calls to cystart():\t\t%d (%d useful)\n", - infop->start_count, infop->start_real); - printf("\n"); - printf("total cyclom service probes:\t%d\n", cy_svrr_probes); - printf("calls to upper layer:\t\t%d\n", cy_timeouts); - printf("rx buffer chars free:\t\t%d\n", infop->rx_buf->free); -#ifdef TxBuffer - printf("tx buffer chars used:\t\t%d\n", infop->tx_buf.used); -#endif - printf("received chars:\t\t\t%d good, %d exception\n", - infop->recv_normal, infop->recv_exception); - printf("transmitted chars:\t\t%d\n", infop->xmit); - printf("modem signal deltas:\t\t%d\n", infop->mdm); - printf("\n"); -} /* end of cystatus() */ -#endif #endif /* NCY > 0 */ diff --git a/sys/i386/isa/cyreg.h b/sys/i386/isa/cyreg.h new file mode 100644 index 0000000..8df7294 --- /dev/null +++ b/sys/i386/isa/cyreg.h @@ -0,0 +1,57 @@ +/*- + * Copyright (c) 1995 Bruce Evans. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: $ + */ + +/* + * Definitions for Cyclades Cyclom-Y serial boards. + */ + +#define CY8_SVCACKR 0x100 +#define CY8_SVCACKT 0x200 +#define CY8_SVCACKM 0x300 +#define CY_CD1400_MEMSIZE 0x400 +#define CY16_RESET 0x1400 +#define CY_CLEAR_INTR 0x1800 /* intr ack address */ + +#define CY_MAX_CD1400s 4 /* for Cyclom-16Y */ + +#define CY_CLOCK 25000000 /* baud rate clock */ + +#ifdef CyDebug +#define cd_inb(iobase, reg) (++cd_inbs, *((iobase) + 2 * (reg))) +#define cy_inb(iobase, reg) (++cy_inbs, *((iobase) + (reg))) +#define cd_outb(iobase, reg, val) (++cd_outbs, (void)(*((iobase) + 2 * (reg)) = (val))) +#define cy_outb(iobase, reg, val) (++cy_outbs, (void)(*((iobase) + (reg)) = (val))) +#else +#define cd_inb(iobase, reg) (*((iobase) + 2 * (reg))) +#define cy_inb(iobase, reg) (*((iobase) + (reg))) +#define cd_outb(iobase, reg, val) ((void)(*((iobase) + 2 * (reg)) = (val))) +#define cy_outb(iobase, reg, val) ((void)(*((iobase) + (reg)) = (val))) +#endif diff --git a/sys/i386/isa/ic/cd1400.h b/sys/i386/isa/ic/cd1400.h index eb690ae..ffebd9a 100644 --- a/sys/i386/isa/ic/cd1400.h +++ b/sys/i386/isa/ic/cd1400.h @@ -1,4 +1,4 @@ -/* +/*- * cyclades cyclom-y serial driver * Andrew Herbert <andrew@werple.apana.org.au>, 17 August 1993 * @@ -26,96 +26,173 @@ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: $ */ -#define CD1400_NO_OF_CHANNELS 4 /* four serial channels per chip */ -#define CD1400_FIFOSIZE 12 /* 12 chars */ - -/* register definitions */ - -#define CD1400_CCR 2*0x05 /* channel control */ -#define CD1400_CMD_RESET 0x81 /* full reset */ - -#define CD1400_SRER 2*0x06 /* service request enable */ - -#define CD1400_GFRCR 2*0x40 /* global firmware revision code */ - -#define CD1400_LIVR 2*0x18 /* local intr vector */ -#define CD1400_MIVR 2*0x41 /* modem intr vector */ -#define CD1400_TIVR 2*0x42 /* transmit intr vector */ -#define CD1400_RIVR 2*0x43 /* receive intr vector */ -#define CD1400_RIVR_EXCEPTION (1<<2) /* receive exception bit */ - -#define CD1400_RICR 2*0x44 /* receive intr channel */ -#define CD1400_TICR 2*0x45 /* transmit intr channel */ -#define CD1400_MICR 2*0x46 /* modem intr channel */ - -#define CD1400_RDCR 2*0x0e /* rx data count */ - -#define CD1400_EOSRR 2*0x60 /* end of service request */ -#define CD1400_RDSR 2*0x62 /* rx data/status */ -#define CD1400_RDSR_OVERRUN (1<<0) /* rx overrun error */ -#define CD1400_RDSR_FRAMING (1<<1) /* rx framing error */ -#define CD1400_RDSR_PARITY (1<<2) /* rx parity error */ -#define CD1400_RDSR_BREAK (1<<3) /* rx break */ -#define CD1400_RDSR_SPECIAL (7<<4) /* rx special char */ -#define CD1400_RDSR_SPECIAL_SHIFT 4 /* rx special char shift */ -#define CD1400_RDSR_TIMEOUT (1<<7) /* rx timeout */ - -#define CD1400_TDR 2*0x63 /* tx data */ - -#define CD1400_MISR 2*0x4c /* modem intr status */ -#define CD1400_MISR_DSRd (1<<7) /* DSR delta */ -#define CD1400_MISR_CTSd (1<<6) /* CTS delta */ -#define CD1400_MISR_RId (1<<5) /* RI delta */ -#define CD1400_MISR_CDd (1<<4) /* CD delta */ - -#define CD1400_MSVR 2*0x6d /* modem signals */ -#define CD1400_MSVR_DSR (1<<7) /* !DSR line */ -#define CD1400_MSVR_CTS (1<<6) /* !CTS line */ -#define CD1400_MSVR_RI (1<<5) /* !RI line */ -#define CD1400_MSVR_CD (1<<4) /* !CD line */ -#define CD1400_MSVR_DTR (1<<1) /* DTR line */ - -#define CD1400_DTR 2*0x6d /* dtr control */ -#define CD1400_DTR_CLEAR 0 -#define CD1400_DTR_SET (1<<1) - -#define CD1400_PPR 2*0x7e -#define CD1400_CLOCK_25_1MS 0x31 - -#define CD1400_CAR 2*0x68 /* channel access */ - -#define CD1400_RIR 2*0x6B /* receive interrupt status */ -#define CD1400_TIR 2*0x6A /* transmit interrupt status */ -#define CD1400_MIR 2*0x69 /* modem interrupt status */ - -#define CD1400_RBPR 2*0x78 /* receive baud rate period */ -#define CD1400_RCOR 2*0x7C /* receive clock option */ -#define CD1400_TBPR 2*0x72 /* transmit baud rate period */ -#define CD1400_TCOR 2*0x76 /* transmit clock option */ - -#define CD1400_COR1 2*0x08 /* channel option 1 */ -#define CD1400_COR2 2*0x09 /* channel option 2 */ -#define CD1400_COR3 2*0x0A /* channel option 3 */ -#define CD1400_COR4 2*0x1E /* channel option 4 */ -#define CD1400_COR5 2*0x1F /* channel option 5 */ +/* + * Definitions for Cirrus Logic CD1400 serial/parallel chips. + */ -#define CD1400_SCHR1 2*0x1A /* special character 1 */ -#define CD1400_SCHR2 2*0x1B /* special character 2 */ -#define CD1400_SCHR3 2*0x1C /* special character 3 */ -#define CD1400_SCHR4 2*0x1D /* special character 4 */ +#define CD1400_NO_OF_CHANNELS 4 /* 4 serial channels per chip */ +#define CD1400_RX_FIFO_SIZE 12 +#define CD1400_TX_FIFO_SIZE 12 -#define CD1400_MCOR1 2*0x15 /* modem change 1 */ -#define CD1400_MCOR2 2*0x16 /* modem change 2 */ -#define CD1400_RTPR 2*0x21 /* receive timeout period */ +/* + * Global registers. + */ +#define CD1400_GFRCR 0x40 /* global firmware revision code */ +#define CD1400_CAR 0x68 /* channel access */ +#define CD1400_CAR_CHAN (3<<0) /* channel select */ +#define CD1400_GCR 0x4B /* global configuration */ +#define CD1400_GCR_PARALLEL (1<<7) /* channel 0 is parallel */ +#define CD1400_SVRR 0x67 /* service request */ +#define CD1400_SVRR_MDMCH (1<<2) +#define CD1400_SVRR_TXRDY (1<<1) +#define CD1400_SVRR_RXRDY (1<<0) +#define CD1400_RICR 0x44 /* receive interrupting channel */ +#define CD1400_TICR 0x45 /* transmit interrupting channel */ +#define CD1400_MICR 0x46 /* modem interrupting channel */ +#define CD1400_RIR 0x6B /* receive interrupt status */ +#define CD1400_RIR_RDIREQ (1<<7) /* rx service required */ +#define CD1400_RIR_RBUSY (1<<6) /* rx service in progress */ +#define CD1400_RIR_CHAN (3<<0) /* channel select */ +#define CD1400_TIR 0x6A /* transmit interrupt status */ +#define CD1400_TIR_RDIREQ (1<<7) /* tx service required */ +#define CD1400_TIR_RBUSY (1<<6) /* tx service in progress */ +#define CD1400_TIR_CHAN (3<<0) /* channel select */ +#define CD1400_MIR 0x69 /* modem interrupt status */ +#define CD1400_MIR_RDIREQ (1<<7) /* modem service required */ +#define CD1400_MIR_RBUSY (1<<6) /* modem service in progress */ +#define CD1400_MIR_CHAN (3<<0) /* channel select */ +#define CD1400_PPR 0x7E /* prescaler period */ +#define CD1400_PPR_PRESCALER 512 -#define CD1400_SVRR 2*0x67 /* service request */ -#define CD1400_SVRR_RX (1<<0) -#define CD1400_SVRR_TX (1<<1) -#define CD1400_SVRR_MDM (1<<2) +/* + * Virtual registers. + */ +#define CD1400_RIVR 0x43 /* receive interrupt vector */ +#define CD1400_RIVR_EXCEPTION (1<<2) /* receive exception bit */ +#define CD1400_TIVR 0x42 /* transmit interrupt vector */ +#define CD1400_MIVR 0x41 /* modem interrupt vector */ +#define CD1400_TDR 0x63 /* transmit data */ +#define CD1400_RDSR 0x62 /* receive data/status */ +#define CD1400_RDSR_TIMEOUT (1<<7) /* rx timeout */ +#define CD1400_RDSR_SPECIAL_SHIFT 4 /* rx special char shift */ +#define CD1400_RDSR_SPECIAL (7<<4) /* rx special char */ +#define CD1400_RDSR_BREAK (1<<3) /* rx break */ +#define CD1400_RDSR_PE (1<<2) /* rx parity error */ +#define CD1400_RDSR_FE (1<<1) /* rx framing error */ +#define CD1400_RDSR_OE (1<<0) /* rx overrun error */ +#define CD1400_MISR 0x4C /* modem interrupt status */ +#define CD1400_MISR_DSRd (1<<7) /* DSR delta */ +#define CD1400_MISR_CTSd (1<<6) /* CTS delta */ +#define CD1400_MISR_RId (1<<5) /* RI delta */ +#define CD1400_MISR_CDd (1<<4) /* CD delta */ +#define CD1400_EOSRR 0x60 /* end of service request */ -/* hardware SVCACK addresses, for use in interrupt handlers */ -#define CD1400_SVCACKR 0x100 -#define CD1400_SVCACKT 0x200 -#define CD1400_SVCACKM 0x300 +/* + * Channel registers. + */ +#define CD1400_LIVR 0x18 /* local interrupt vector */ +#define CD1400_CCR 0x05 /* channel control */ +#define CD1400_CCR_CMDRESET (1<<7) /* enables following: */ +#define CD1400_CCR_FTF (1<<1) /* flush tx fifo */ +#define CD1400_CCR_FULLRESET (1<<0) /* full reset */ +#define CD1400_CCR_CMDCORCHG (1<<6) /* enables following: */ +#define CD1400_CCR_COR3 (1<<3) /* COR3 changed */ +#define CD1400_CCR_COR2 (1<<2) /* COR2 changed */ +#define CD1400_CCR_COR1 (1<<1) /* COR1 changed */ +#define CD1400_CCR_CMDSENDSC (1<<5) /* enables following: */ +#define CD1400_CCR_SC (7<<0) /* special char 1-4 */ +#define CD1400_CCR_CMDCHANCTL (1<<4) /* enables following: */ +#define CD1400_CCR_XMTEN (1<<3) /* tx enable */ +#define CD1400_CCR_XMTDIS (1<<2) /* tx disable */ +#define CD1400_CCR_RCVEN (1<<1) /* rx enable */ +#define CD1400_CCR_RCVDIS (1<<0) /* rx disable */ +#define CD1400_SRER 0x06 /* service request enable */ +#define CD1400_SRER_MDMCH (1<<7) /* modem change */ +#define CD1400_SRER_RXDATA (1<<4) /* rx data */ +#define CD1400_SRER_TXRDY (1<<2) /* tx fifo empty */ +#define CD1400_SRER_TXMPTY (1<<1) /* tx shift reg empty */ +#define CD1400_SRER_NNDT (1<<0) /* no new data */ +#define CD1400_COR1 0x08 /* channel option 1 */ +#define CD1400_COR1_PARODD (1<<7) +#define CD1400_COR1_PARNORMAL (2<<5) +#define CD1400_COR1_PARFORCE (1<<5) /* odd/even = force 1/0 */ +#define CD1400_COR1_PARNONE (0<<5) +#define CD1400_COR1_NOINPCK (1<<4) +#define CD1400_COR1_STOP2 (2<<2) +#define CD1400_COR1_STOP15 (1<<2) /* 1.5 stop bits */ +#define CD1400_COR1_STOP1 (0<<2) +#define CD1400_COR1_CS8 (3<<0) +#define CD1400_COR1_CS7 (2<<0) +#define CD1400_COR1_CS6 (1<<0) +#define CD1400_COR1_CS5 (0<<0) +#define CD1400_COR2 0x09 /* channel option 2 */ +#define CD1400_COR2_IXANY (1<<7) /* implied XON mode */ +#define CD1400_COR2_IXOFF (1<<6) /* in-band tx flow control */ +#define CD1400_COR2_ETC (1<<5) /* embedded tx command */ +#define CD1400_COR2_LLM (1<<4) /* local loopback mode */ +#define CD1400_COR2_RLM (1<<3) /* remote loopback mode */ +#define CD1400_COR2_RTSAO (1<<2) /* RTS auto output */ +#define CD1400_COR2_CCTS_OFLOW (1<<1) /* CTS auto enable */ +#define CD1400_COR2_CDSR_OFLOW (1<<0) /* DSR auto enable */ +#define CD1400_COR3 0x0A /* channel option 3 */ +#define CD1400_COR3_SCDRNG (1<<7) /* special char detect range */ +#define CD1400_COR3_SCD34 (1<<6) /* special char detect 3-4 */ +#define CD1400_COR3_FTC (1<<5) /* flow control transparency */ +#define CD1400_COR3_SCD12 (1<<4) /* special char detect 1-2 */ +#define CD1400_COR3_RXTH (15<<0) /* rx fifo threshold */ +#define CD1400_COR4 0x1E /* channel option 4 */ +#define CD1400_COR4_IGNCR (1<<7) +#define CD1400_COR4_ICRNL (1<<6) +#define CD1400_COR4_INLCR (1<<5) +#define CD1400_COR4_IGNBRK (1<<4) +#define CD1400_COR4_NOBRKINT (1<<3) +#define CD1400_COR4_PFO_ESC (4<<0) /* parity/framing/overrun... */ +#define CD1400_COR4_PFO_NUL (3<<0) +#define CD1400_COR4_PFO_DISCARD (2<<0) +#define CD1400_COR4_PFO_GOOD (1<<0) +#define CD1400_COR4_PFO_EXCEPTION (0<<0) +#define CD1400_COR5 0x1F /* channel option 5 */ +#define CD1400_COR5_ISTRIP (1<<7) +#define CD1400_COR5_LNEXT (1<<6) +#define CD1400_COR5_CMOE (1<<5) /* char matching on error */ +#define CD1400_COR5_EBD (1<<2) /* end of break detected */ +#define CD1400_COR5_ONLCR (1<<1) +#define CD1400_COR5_OCRNL (1<<0) +#define CD1400_CCSR 0x0B /* channel control status */ +#define CD1400_RDCR 0x0E /* received data count */ +#define CD1400_SCHR1 0x1A /* special character 1 */ +#define CD1400_SCHR2 0x1B /* special character 2 */ +#define CD1400_SCHR3 0x1C /* special character 3 */ +#define CD1400_SCHR4 0x1D /* special character 4 */ +#define CD1400_SCRL 0x22 /* special character range, low */ +#define CD1400_SCRH 0x23 /* special character range, high */ +#define CD1400_LNC 0x24 /* lnext character */ +#define CD1400_MCOR1 0x15 /* modem change option 1 */ +#define CD1400_MCOR1_DSRzd (1<<7) /* DSR one-to-zero delta */ +#define CD1400_MCOR1_CTSzd (1<<6) +#define CD1400_MCOR1_RIzd (1<<5) +#define CD1400_MCOR1_CDzd (1<<4) +#define CD1400_MCOR1_DTRth (15<<0) /* dtrflow threshold */ +#define CD1400_MCOR2 0x16 /* modem change option 2 */ +#define CD1400_MCOR2_DSRod (1<<7) /* DSR zero-to-one delta */ +#define CD1400_MCOR2_CTSod (1<<6) +#define CD1400_MCOR2_RIod (1<<5) +#define CD1400_MCOR2_CDod (1<<4) +#define CD1400_RTPR 0x21 /* receive timeout period */ +#define CD1400_MSVR1 0x6C /* modem signal value 1 */ +#define CD1400_MSVR1_RTS (1<<0) /* RTS line (r/w) */ +#define CD1400_MSVR2 0x6D /* modem signal value 2 */ +#define CD1400_MSVR2_DSR (1<<7) /* !DSR line (r) */ +#define CD1400_MSVR2_CTS (1<<6) /* !CTS line (r) */ +#define CD1400_MSVR2_RI (1<<5) /* !RI line (r) */ +#define CD1400_MSVR2_CD (1<<4) /* !CD line (r) */ +#define CD1400_MSVR2_DTR (1<<1) /* DTR line (r/w) */ +#define CD1400_PSVR 0x6F /* printer signal value */ +#define CD1400_RBPR 0x78 /* receive baud rate period */ +#define CD1400_RCOR 0x7C /* receive clock option */ +#define CD1400_TBPR 0x72 /* transmit baud rate period */ +#define CD1400_TCOR 0x76 /* transmit clock option */ |