cy_isa.c revision 9406
19406Sbde/*- 26261Sjkh * cyclades cyclom-y serial driver 36261Sjkh * Andrew Herbert <andrew@werple.apana.org.au>, 17 August 1993 46261Sjkh * 56261Sjkh * Copyright (c) 1993 Andrew Herbert. 66261Sjkh * All rights reserved. 76261Sjkh * 86261Sjkh * Redistribution and use in source and binary forms, with or without 96261Sjkh * modification, are permitted provided that the following conditions 106261Sjkh * are met: 116261Sjkh * 1. Redistributions of source code must retain the above copyright 126261Sjkh * notice, this list of conditions and the following disclaimer. 136261Sjkh * 2. Redistributions in binary form must reproduce the above copyright 146261Sjkh * notice, this list of conditions and the following disclaimer in the 156261Sjkh * documentation and/or other materials provided with the distribution. 166261Sjkh * 3. The name Andrew Herbert may not be used to endorse or promote products 176261Sjkh * derived from this software without specific prior written permission. 186261Sjkh * 196261Sjkh * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY EXPRESS OR IMPLIED 206261Sjkh * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 216261Sjkh * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN 226261Sjkh * NO EVENT SHALL I BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 236261Sjkh * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 246261Sjkh * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 256261Sjkh * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 266261Sjkh * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 276261Sjkh * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 286261Sjkh * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 296261Sjkh * 309406Sbde * $Id: cy.c,v 1.7 1995/05/30 08:01:34 rgrimes Exp $ 316261Sjkh */ 326261Sjkh 339406Sbde#include "cy.h" 349406Sbde#if NCY > 0 356261Sjkh/* 369406Sbde * TODO: 379406Sbde * Check that cy16's work. 389406Sbde * Implement BREAK. 399406Sbde * Fix overflows when closing line. 409406Sbde * Atomic COR change. 419406Sbde * Don't report individual ports in devconf; busy flag for board should be 429406Sbde * union of the current individual busy flags. 439406Sbde * Consoles. 446261Sjkh */ 456261Sjkh 466261Sjkh/* 479406Sbde * Temporary compile-time configuration options. 486261Sjkh */ 499406Sbde#define RxFifoThreshold (CD1400_RX_FIFO_SIZE / 2) 509406Sbde /* Number of chars in the receiver FIFO before an 519406Sbde * an interrupt is generated. Should depend on 529406Sbde * line speed. Needs to be about 6 on a 486DX33 539406Sbde * for 4 active ports at 115200 bps. Why doesn't 549406Sbde * 10 work? 559406Sbde */ 569406Sbde#define PollMode /* Use polling-based irq service routine, not the 579406Sbde * hardware svcack lines. Must be defined for 589406Sbde * Cyclom-16Y boards. Less efficient for Cyclom-8Ys, 599406Sbde * and stops 4 * 115200 bps from working. 609406Sbde */ 619406Sbde#undef Smarts /* Enable slightly more CD1400 intelligence. Mainly 629406Sbde * the output CR/LF processing, plus we can avoid a 639406Sbde * few checks usually done in ttyinput(). 649406Sbde * 659406Sbde * XXX not fully implemented, and not particularly 669406Sbde * worthwhile. 679406Sbde */ 689406Sbde#undef CyDebug /* Include debugging code (not very expensive). */ 696261Sjkh 709406Sbde/* These will go away. */ 719406Sbde#undef SOFT_CTS_OFLOW 729406Sbde#define SOFT_HOTCHAR 736261Sjkh 746261Sjkh#include <sys/param.h> 756261Sjkh#include <sys/systm.h> 769406Sbde#include <sys/reboot.h> 776261Sjkh#include <sys/ioctl.h> 789406Sbde#define TTYDEFCHARS /* XXX TK2.0 */ 796261Sjkh#include <sys/tty.h> 809406Sbde#undef TTYDEFCHARS 816261Sjkh#include <sys/proc.h> 826261Sjkh#include <sys/user.h> 836261Sjkh#include <sys/conf.h> 849406Sbde#include <sys/dkstat.h> 856261Sjkh#include <sys/file.h> 866261Sjkh#include <sys/uio.h> 876261Sjkh#include <sys/kernel.h> 889406Sbde#include <sys/malloc.h> 896261Sjkh#include <sys/syslog.h> 909406Sbde#include <sys/devconf.h> 916261Sjkh 927442Sbde#include <machine/clock.h> 936261Sjkh 949406Sbde#include <i386/isa/icu.h> /* XXX just to get at `imen' */ 959406Sbde#include <i386/isa/isa.h> 966261Sjkh#include <i386/isa/isa_device.h> 979406Sbde#include <i386/isa/cyreg.h> 986261Sjkh#include <i386/isa/ic/cd1400.h> 996261Sjkh 1009406Sbde/* 1019406Sbde * Dictionary so that I can name everything *sio* or *com* to compare with 1029406Sbde * sio.c. There is also lots of ugly formatting and unnecessary ifdefs to 1039406Sbde * simplify the comparision. These will go away. 1049406Sbde */ 1059406Sbde#define LSR_BI CD1400_RDSR_BREAK 1069406Sbde#define LSR_FE CD1400_RDSR_FE 1079406Sbde#define LSR_OE CD1400_RDSR_OE 1089406Sbde#define LSR_PE CD1400_RDSR_PE 1099406Sbde#define MCR_DTR CD1400_MSVR2_DTR 1109406Sbde#define MCR_RTS CD1400_MSVR1_RTS 1119406Sbde#define MSR_CTS CD1400_MSVR2_CTS 1129406Sbde#define MSR_DCD CD1400_MSVR2_CD 1139406Sbde#define MSR_DSR CD1400_MSVR2_DSR 1149406Sbde#define MSR_RI CD1400_MSVR2_RI 1159406Sbde#define NSIO (NCY * CY_MAX_PORTS) 1169406Sbde#define comconsole cyconsole 1179406Sbde#define comdefaultrate cydefaultrate 1189406Sbde#define com_events cy_events 1199406Sbde#define comhardclose cyhardclose 1209406Sbde#define commajor cymajor 1219406Sbde#define commctl cymctl 1229406Sbde#define comparam cyparam 1239406Sbde#define comspeed cyspeed 1249406Sbde#define comstart cystart 1259406Sbde#define comwakeup cywakeup 1269406Sbde#define kdc_sio kdc_cy 1279406Sbde#define nsio_tty ncy_tty 1289406Sbde#define p_com_addr p_cy_addr 1299406Sbde#define sioattach cyattach 1309406Sbde#define sioclose cyclose 1319406Sbde#define siodevtotty cydevtotty 1329406Sbde#define siodriver cydriver 1339406Sbde#define siodtrwakeup cydtrwakeup 1349406Sbde#define sioioctl cyioctl 1359406Sbde#define siointr cyintr 1369406Sbde#define siointr1 cyintr1 1379406Sbde#define siointrts cyintrts 1389406Sbde#define sioopen cyopen 1399406Sbde#define siopoll cypoll 1409406Sbde#define sioprobe cyprobe 1419406Sbde#define sioread cyread 1429406Sbde#define sioregisterdev cyregisterdev 1439406Sbde#define siosettimeout cysettimeout 1449406Sbde#define siostop cystop 1459406Sbde#define siowrite cywrite 1469406Sbde#define sio_timeout cy_timeout 1479406Sbde#define sio_timeouts_until_log cy_timeouts_until_log 1489406Sbde#define sio_tty cy_tty 1496261Sjkh 1509406Sbde#define CY_MAX_PORTS (CD1400_NO_OF_CHANNELS * CY_MAX_CD1400s) 1516261Sjkh 1529406Sbde/* We encode the cyclom unit number (cyu) in spare bits in the IVR's. */ 1539406Sbde#define CD1400_xIVR_CHAN_SHIFT 3 1549406Sbde#define CD1400_xIVR_CHAN 0x0F /* XXX reduce to pack Cyclom-8Ys */ 1556261Sjkh 1569406Sbde/* 1579406Sbde * XXX temporary kludges for 2.0 (XXX TK2.0). 1589406Sbde */ 1599406Sbde#define TSA_CARR_ON(tp) ((void *)&(tp)->t_rawq) 1609406Sbde#define TSA_OCOMPLETE(tp) ((void *)&(tp)->t_outq) 1619406Sbde#define TSA_OLOWAT(tp) ((void *)&(tp)->t_outq) 1629406Sbdestatic void termioschars __P((struct termios *t)); 1639406Sbdestatic void 1649406Sbdetermioschars(t) 1659406Sbde struct termios *t; 1669406Sbde{ 1676261Sjkh 1689406Sbde bcopy(ttydefchars, t->c_cc, sizeof t->c_cc); 1699406Sbde} 1706261Sjkh 1719406Sbde#define LOTS_OF_EVENTS 64 /* helps separate urgent events from input */ 1729406Sbde#define RB_I_HIGH_WATER (TTYHOG - 2 * RS_IBUFSIZE) 1739406Sbde#define RS_IBUFSIZE 256 1746261Sjkh 1759406Sbde#define CALLOUT_MASK 0x80 1769406Sbde#define CONTROL_MASK 0x60 1779406Sbde#define CONTROL_INIT_STATE 0x20 1789406Sbde#define CONTROL_LOCK_STATE 0x40 1799406Sbde#define DEV_TO_UNIT(dev) (MINOR_TO_UNIT(minor(dev))) 1809406Sbde#define MINOR_MAGIC_MASK (CALLOUT_MASK | CONTROL_MASK) 1819406Sbde#define MINOR_TO_UNIT(mynor) ((mynor) & ~MINOR_MAGIC_MASK) 1826261Sjkh 1839406Sbde/* 1849406Sbde * Input buffer watermarks. 1859406Sbde * The external device is asked to stop sending when the buffer exactly reaches 1869406Sbde * high water, or when the high level requests it. 1879406Sbde * The high level is notified immediately (rather than at a later clock tick) 1889406Sbde * when this watermark is reached. 1899406Sbde * The buffer size is chosen so the watermark should almost never be reached. 1909406Sbde * The low watermark is invisibly 0 since the buffer is always emptied all at 1919406Sbde * once. 1929406Sbde */ 1939406Sbde#define RS_IHIGHWATER (3 * RS_IBUFSIZE / 4) 1946261Sjkh 1959406Sbde/* 1969406Sbde * com state bits. 1979406Sbde * (CS_BUSY | CS_TTGO) and (CS_BUSY | CS_TTGO | CS_ODEVREADY) must be higher 1989406Sbde * than the other bits so that they can be tested as a group without masking 1999406Sbde * off the low bits. 2009406Sbde * 2019406Sbde * The following com and tty flags correspond closely: 2029406Sbde * CS_BUSY = TS_BUSY (maintained by comstart(), siopoll() and 2039406Sbde * siostop()) 2049406Sbde * CS_TTGO = ~TS_TTSTOP (maintained by comstart() and siostop()) 2059406Sbde * CS_CTS_OFLOW = CCTS_OFLOW (maintained by comparam()) 2069406Sbde * CS_RTS_IFLOW = CRTS_IFLOW (maintained by comparam()) 2079406Sbde * TS_FLUSH is not used. 2089406Sbde * XXX I think TIOCSETA doesn't clear TS_TTSTOP when it clears IXON. 2099406Sbde * XXX CS_*FLOW should be CF_*FLOW in com->flags (control flags not state). 2109406Sbde */ 2119406Sbde#define CS_BUSY 0x80 /* output in progress */ 2129406Sbde#define CS_TTGO 0x40 /* output not stopped by XOFF */ 2139406Sbde#define CS_ODEVREADY 0x20 /* external device h/w ready (CTS) */ 2149406Sbde#define CS_CHECKMSR 1 /* check of MSR scheduled */ 2159406Sbde#define CS_CTS_OFLOW 2 /* use CTS output flow control */ 2169406Sbde#define CS_DTR_OFF 0x10 /* DTR held off */ 2179406Sbde#define CS_ODONE 4 /* output completed */ 2189406Sbde#define CS_RTS_IFLOW 8 /* use RTS input flow control */ 2196261Sjkh 2209406Sbdestatic char const * const error_desc[] = { 2219406Sbde#define CE_OVERRUN 0 2229406Sbde "silo overflow", 2239406Sbde#define CE_INTERRUPT_BUF_OVERFLOW 1 2249406Sbde "interrupt-level buffer overflow", 2259406Sbde#define CE_TTY_BUF_OVERFLOW 2 2269406Sbde "tty-level buffer overflow", 2279406Sbde}; 2286261Sjkh 2299406Sbde#define CE_NTYPES 3 2309406Sbde#define CE_RECORD(com, errnum) (++(com)->delta_error_counts[errnum]) 2316261Sjkh 2329406Sbde/* types. XXX - should be elsewhere */ 2339406Sbdetypedef u_char bool_t; /* boolean */ 2349406Sbdetypedef u_char volatile *cy_addr; 2359406Sbde 2369406Sbde/* queue of linear buffers */ 2379406Sbdestruct lbq { 2389406Sbde u_char *l_head; /* next char to process */ 2399406Sbde u_char *l_tail; /* one past the last char to process */ 2409406Sbde struct lbq *l_next; /* next in queue */ 2419406Sbde bool_t l_queued; /* nonzero if queued */ 2429406Sbde}; 2439406Sbde 2449406Sbde/* com device structure */ 2459406Sbdestruct com_s { 2469406Sbde u_char state; /* miscellaneous flag bits */ 2479406Sbde bool_t active_out; /* nonzero if the callout device is open */ 2486261Sjkh#if 0 2499406Sbde u_char cfcr_image; /* copy of value written to CFCR */ 2509406Sbde u_char ftl; /* current rx fifo trigger level */ 2519406Sbde u_char ftl_init; /* ftl_max for next open() */ 2529406Sbde u_char ftl_max; /* maximum ftl for curent open() */ 2539406Sbde bool_t hasfifo; /* nonzero for 16550 UARTs */ 2549406Sbde bool_t loses_outints; /* nonzero if device loses output interrupts */ 2556261Sjkh#endif 2569406Sbde u_char mcr_image; /* copy of value written to MCR */ 2579406Sbde#if 0 2589406Sbde#ifdef COM_MULTIPORT 2599406Sbde bool_t multiport; /* is this unit part of a multiport device? */ 2609406Sbde#endif /* COM_MULTIPORT */ 2619406Sbde bool_t no_irq; /* nonzero if irq is not attached */ 2629406Sbde bool_t poll; /* nonzero if polling is required */ 2639406Sbde bool_t poll_output; /* nonzero if polling for output is required */ 2649406Sbde#endif 2659406Sbde int unit; /* unit number */ 2669406Sbde int dtr_wait; /* time to hold DTR down on close (* 1/hz) */ 2679406Sbde#if 0 2689406Sbde u_int tx_fifo_size; 2699406Sbde#endif 2709406Sbde u_int wopeners; /* # processes waiting for DCD in open() */ 2716261Sjkh 2729406Sbde /* 2739406Sbde * The high level of the driver never reads status registers directly 2749406Sbde * because there would be too many side effects to handle conveniently. 2759406Sbde * Instead, it reads copies of the registers stored here by the 2769406Sbde * interrupt handler. 2779406Sbde */ 2789406Sbde u_char last_modem_status; /* last MSR read by intr handler */ 2799406Sbde u_char prev_modem_status; /* last MSR handled by high level */ 2806261Sjkh 2819406Sbde u_char hotchar; /* ldisc-specific char to be handled ASAP */ 2829406Sbde u_char *ibuf; /* start of input buffer */ 2839406Sbde u_char *ibufend; /* end of input buffer */ 2849406Sbde u_char *ihighwater; /* threshold in input buffer */ 2859406Sbde u_char *iptr; /* next free spot in input buffer */ 2866261Sjkh 2879406Sbde struct lbq obufq; /* head of queue of output buffers */ 2889406Sbde struct lbq obufs[2]; /* output buffers */ 2896261Sjkh 2909406Sbde cy_addr cy_iobase; /* base address of this port's cyclom */ 2919406Sbde cy_addr iobase; /* base address of this port's cd1400 */ 2926261Sjkh 2939406Sbde struct tty *tp; /* cross reference */ 2949406Sbde 2959406Sbde /* Initial state. */ 2969406Sbde struct termios it_in; /* should be in struct tty */ 2979406Sbde struct termios it_out; 2989406Sbde 2999406Sbde /* Lock state. */ 3009406Sbde struct termios lt_in; /* should be in struct tty */ 3019406Sbde struct termios lt_out; 3029406Sbde 3039406Sbde bool_t do_timestamp; 3049406Sbde struct timeval timestamp; 3059406Sbde 3069406Sbde u_long bytes_in; /* statistics */ 3079406Sbde u_long bytes_out; 3089406Sbde u_int delta_error_counts[CE_NTYPES]; 3099406Sbde u_long error_counts[CE_NTYPES]; 3109406Sbde 3119406Sbde u_int recv_exception; /* exception chars received */ 3129406Sbde u_int mdm; /* modem signal changes */ 3139406Sbde#ifdef CyDebug 3149406Sbde u_int start_count; /* no. of calls to comstart() */ 3159406Sbde u_int start_real; /* no. of calls that did something */ 3166261Sjkh#endif 3179406Sbde u_char channel_control;/* CD1400 CCR control command shadow */ 3189406Sbde u_char cor[3]; /* CD1400 COR1-3 shadows */ 3199406Sbde u_char intr_enable; /* CD1400 SRER shadow */ 3206261Sjkh 3219406Sbde /* 3229406Sbde * Ping-pong input buffers. The extra factor of 2 in the sizes is 3239406Sbde * to allow for an error byte for each input byte. 3249406Sbde */ 3259406Sbde#define CE_INPUT_OFFSET RS_IBUFSIZE 3269406Sbde u_char ibuf1[2 * RS_IBUFSIZE]; 3279406Sbde u_char ibuf2[2 * RS_IBUFSIZE]; 3286261Sjkh 3299406Sbde /* 3309406Sbde * Data area for output buffers. Someday we should build the output 3319406Sbde * buffer queue without copying data. 3329406Sbde */ 3339406Sbde u_char obuf1[256]; 3349406Sbde u_char obuf2[256]; 3359406Sbde 3369406Sbde struct kern_devconf kdc; 3379406Sbde}; 3389406Sbde 3396261Sjkh/* 3409406Sbde * XXX public functions in drivers should be declared in headers produced 3419406Sbde * by `config', not here. 3426261Sjkh */ 3436261Sjkh 3449406Sbde/* Interrupt handling entry points. */ 3459406Sbdevoid siointr __P((int unit)); 3469406Sbdevoid siointrts __P((int unit)); 3479406Sbdevoid siopoll __P((void)); 3489406Sbde 3499406Sbde/* Device switch entry points. */ 3509406Sbdeint sioopen __P((dev_t dev, int oflags, int devtype, 3519406Sbde struct proc *p)); 3529406Sbdeint sioclose __P((dev_t dev, int fflag, int devtype, 3539406Sbde struct proc *p)); 3549406Sbdeint sioread __P((dev_t dev, struct uio *uio, int ioflag)); 3559406Sbdeint siowrite __P((dev_t dev, struct uio *uio, int ioflag)); 3569406Sbdeint sioioctl __P((dev_t dev, int cmd, caddr_t data, 3579406Sbde int fflag, struct proc *p)); 3589406Sbdevoid siostop __P((struct tty *tp, int rw)); 3599406Sbde#define sioreset noreset 3609406Sbdestruct tty *siodevtotty __P((dev_t dev)); 3619406Sbde#define siommap nommap 3629406Sbde#define siostrategy nostrategy 3639406Sbde 3649406Sbdestatic int sioattach __P((struct isa_device *dev)); 3659406Sbdestatic void cd1400_channel_cmd __P((cy_addr iobase, int cmd)); 3669406Sbdestatic timeout_t siodtrwakeup; 3679406Sbdestatic void comhardclose __P((struct com_s *com)); 3689406Sbdestatic void siointr1 __P((struct com_s *com)); 3699406Sbdestatic int commctl __P((struct com_s *com, int bits, int how)); 3709406Sbdestatic int comparam __P((struct tty *tp, struct termios *t)); 3719406Sbdestatic int sioprobe __P((struct isa_device *dev)); 3729406Sbdestatic void sioregisterdev __P((struct isa_device *id)); 3739406Sbdestatic void siosettimeout __P((void)); 3749406Sbdestatic int comspeed __P((speed_t speed, int *prescaler_io)); 3759406Sbdestatic void comstart __P((struct tty *tp)); 3769406Sbdestatic timeout_t comwakeup; 3779406Sbdestatic void disc_optim __P((struct tty *tp, struct termios *t, 3789406Sbde struct com_s *com)); 3799406Sbde 3806261Sjkh#ifdef CyDebug 3819406Sbdevoid cystatus __P((int unit)); 3826261Sjkh#endif 3839406Sbde 3849406Sbde/* table and macro for fast conversion from a unit number to its com struct */ 3859406Sbdestatic struct com_s *p_com_addr[NSIO]; 3869406Sbde#define com_addr(unit) (p_com_addr[unit]) 3879406Sbde 3889406Sbdestatic struct timeval intr_timestamp; 3899406Sbde 3909406Sbdestruct isa_driver siodriver = { 3919406Sbde sioprobe, sioattach, "cy" 3929406Sbde}; 3939406Sbde 3949406Sbde#ifdef COMCONSOLE 3959406Sbde#undef COMCONSOLE 3969406Sbde#define COMCONSOLE 1 3979406Sbde#else 3989406Sbde#define COMCONSOLE 0 3996261Sjkh#endif 4009406Sbde 4019406Sbde#ifndef CONUNIT 4029406Sbde#define CONUNIT (0) 4036261Sjkh#endif 4046261Sjkh 4059406Sbdestatic int comconsole = CONUNIT; 4069406Sbdestatic speed_t comdefaultrate = TTYDEF_SPEED; 4079406Sbdestatic u_int com_events; /* input chars + weighted output completions */ 4089406Sbdestatic int commajor; 4099406Sbdestatic int sio_timeout; 4109406Sbdestatic int sio_timeouts_until_log; 4119406Sbde#if 0 /* XXX TK2.0 */ 4129406Sbdestatic struct tty *sio_tty[NSIO]; 4136454Sbde#else 4149406Sbdestatic struct tty sio_tty[NSIO]; 4159406Sbdestatic int nsio_tty = NSIO; 4166454Sbde#endif 4176261Sjkh 4189406Sbde#ifdef KGDB 4199406Sbde#include <machine/remote-sl.h> 4209406Sbde 4219406Sbdeextern int kgdb_dev; 4229406Sbdeextern int kgdb_rate; 4239406Sbdeextern int kgdb_debug_init; 4249406Sbde#endif 4259406Sbde 4266261Sjkh#ifdef CyDebug 4279406Sbdestatic u_int cd_inbs; 4289406Sbdestatic u_int cy_inbs; 4299406Sbdestatic u_int cd_outbs; 4309406Sbdestatic u_int cy_outbs; 4319406Sbdestatic u_int cy_svrr_probes; 4329406Sbdestatic u_int cy_timeouts; 4336261Sjkh#endif 4346261Sjkh 4359406Sbdestatic int cy_nr_cd1400s[NCY]; 4369406Sbde#undef RxFifoThreshold 4379406Sbdestatic int volatile RxFifoThreshold = (CD1400_RX_FIFO_SIZE / 2); 4386261Sjkh 4399406Sbdestatic struct kern_devconf kdc_sio[NCY] = { { 4409406Sbde 0, 0, 0, /* filled in by dev_attach */ 4419406Sbde "cyc", 0, { MDDT_ISA, 0, "tty" }, 4429406Sbde isa_generic_externalize, 0, 0, ISA_EXTERNALLEN, 4439406Sbde &kdc_isa0, /* parent */ 4449406Sbde 0, /* parentdata */ 4459406Sbde DC_UNCONFIGURED, /* state */ 4469406Sbde "Cyclades multiport board", 4479406Sbde DC_CLS_MISC /* just an ordinary device */ 4489406Sbde} }; 4499406Sbde 4509406Sbdestatic void 4519406Sbdesioregisterdev(id) 4529406Sbde struct isa_device *id; 4536261Sjkh{ 4549406Sbde int unit; 4556261Sjkh 4569406Sbde unit = id->id_unit; 4579406Sbde if (unit != 0) 4589406Sbde kdc_sio[unit] = kdc_sio[0]; 4599406Sbde kdc_sio[unit].kdc_unit = unit; 4609406Sbde kdc_sio[unit].kdc_isa = id; 4619406Sbde dev_attach(&kdc_sio[unit]); 4629406Sbde} 4636261Sjkh 4649406Sbdestatic int 4659406Sbdesioprobe(dev) 4669406Sbde struct isa_device *dev; 4679406Sbde{ 4689406Sbde int cyu; 4699406Sbde u_char firmware_version; 4709406Sbde cy_addr iobase; 4719406Sbde int unit; 4726261Sjkh 4739406Sbde unit = dev->id_unit; 4749406Sbde if ((u_int)unit >= NCY) 4759406Sbde return (0); 4769406Sbde cy_nr_cd1400s[unit] = 0; 4779406Sbde sioregisterdev(dev); 4786261Sjkh 4799406Sbde /* Cyclom-16Y hardware reset (Cyclom-8Ys don't care) */ 4809406Sbde cy_inb((cy_addr)dev->id_maddr, CY16_RESET); /* XXX? */ 4816261Sjkh 4829406Sbde DELAY(500); /* wait for the board to get its act together */ 4836261Sjkh 4849406Sbde for (cyu = 0, iobase = (cy_addr)dev->id_maddr; cyu < CY_MAX_CD1400s; 4859406Sbde ++cyu, iobase += CY_CD1400_MEMSIZE) { 4869406Sbde int i; 4876261Sjkh 4889406Sbde /* wait for chip to become ready for new command */ 4899406Sbde for (i = 0; i < 100; i += 50) { 4909406Sbde DELAY(50); 4919406Sbde if (!cd_inb(iobase, CD1400_CCR)) 4929406Sbde break; 4939406Sbde } 4946261Sjkh 4959406Sbde /* clear the GFRCR register */ 4969406Sbde cd_outb(iobase, CD1400_GFRCR, 0); 4976261Sjkh 4989406Sbde /* issue a reset command */ 4999406Sbde cd_outb(iobase, CD1400_CCR, 5009406Sbde CD1400_CCR_CMDRESET | CD1400_CCR_FULLRESET); 5016261Sjkh 5029406Sbde /* wait for the CD1400 to initialize itself */ 5039406Sbde for (i = 0; i < 1000; i += 50) { 5049406Sbde DELAY(50); 5059406Sbde 5069406Sbde /* retrieve firmware version */ 5079406Sbde firmware_version = cd_inb(iobase, CD1400_GFRCR); 5089406Sbde if (firmware_version != 0) 5099406Sbde break; 5109406Sbde } 5119406Sbde 5129406Sbde /* 5139406Sbde * Anything in the 0x40-0x4F range is fine. 5149406Sbde * If one CD1400 is bad then we don't support higher 5159406Sbde * numbered good ones on this board. 5169406Sbde */ 5179406Sbde if ((firmware_version & 0xF0) != 0x40) 5189406Sbde break; 5199406Sbde ++cy_nr_cd1400s[unit]; 5206261Sjkh } 5219406Sbde return (cy_nr_cd1400s[unit] == 0 ? 0 : -1); 5226261Sjkh} 5236261Sjkh 5249406Sbdestatic int 5259406Sbdesioattach(isdp) 5269406Sbde struct isa_device *isdp; 5276261Sjkh{ 5289406Sbde int cyu; 5299406Sbde cy_addr cy_iobase; 5309406Sbde cy_addr iobase; 5319406Sbde int ncyu; 5329406Sbde int unit; 5336261Sjkh 5349406Sbde unit = isdp->id_unit; 5359406Sbde if ((u_int)unit >= NCY) 5369406Sbde return (0); 5379406Sbde ncyu = cy_nr_cd1400s[unit]; 5389406Sbde if (ncyu == 0) 5399406Sbde return (0); 5409406Sbde isdp->id_ri_flags |= RI_FAST; 5416261Sjkh 5429406Sbde cy_iobase = (cy_addr)isdp->id_maddr; 5439406Sbde unit *= CY_MAX_PORTS; 5449406Sbde for (cyu = 0, iobase = cy_iobase; cyu < ncyu; 5459406Sbde ++cyu, iobase += CY_CD1400_MEMSIZE) { 5469406Sbde int cdu; 5476261Sjkh 5489406Sbde /* Set up a receive timeout period of than 1+ ms. */ 5499406Sbde cd_outb(iobase, CD1400_PPR, 5509406Sbde howmany(CY_CLOCK / CD1400_PPR_PRESCALER, 1000)); 5516261Sjkh 5529406Sbde for (cdu = 0; cdu < CD1400_NO_OF_CHANNELS; ++cdu, ++unit) { 5539406Sbde struct com_s *com; 5549406Sbde int s; 5556261Sjkh 5569406Sbde com = malloc(sizeof *com, M_DEVBUF, M_NOWAIT); 5579406Sbde if (com == NULL) 5589406Sbde break; 5599406Sbde bzero(com, sizeof *com); 5609406Sbde com->unit = unit; 5619406Sbde com->dtr_wait = 3 * hz; 5629406Sbde com->iptr = com->ibuf = com->ibuf1; 5639406Sbde com->ibufend = com->ibuf1 + RS_IBUFSIZE; 5649406Sbde com->ihighwater = com->ibuf1 + RS_IHIGHWATER; 5659406Sbde com->obufs[0].l_head = com->obuf1; 5669406Sbde com->obufs[1].l_head = com->obuf2; 5676261Sjkh 5689406Sbde com->cy_iobase = cy_iobase; 5699406Sbde com->iobase = iobase; 5706261Sjkh 5719406Sbde /* 5729406Sbde * We don't use all the flags from <sys/ttydefaults.h> since they 5739406Sbde * are only relevant for logins. It's important to have echo off 5749406Sbde * initially so that the line doesn't start blathering before the 5759406Sbde * echo flag can be turned off. 5769406Sbde */ 5779406Sbde com->it_in.c_iflag = 0; 5789406Sbde com->it_in.c_oflag = 0; 5799406Sbde com->it_in.c_cflag = TTYDEF_CFLAG; 5809406Sbde com->it_in.c_lflag = 0; 5819406Sbde if (unit == comconsole && (COMCONSOLE || boothowto & RB_SERIAL)) { 5829406Sbde com->it_in.c_iflag = TTYDEF_IFLAG; 5839406Sbde com->it_in.c_oflag = TTYDEF_OFLAG; 5849406Sbde com->it_in.c_cflag = TTYDEF_CFLAG | CLOCAL; 5859406Sbde com->it_in.c_lflag = TTYDEF_LFLAG; 5869406Sbde com->lt_out.c_cflag = com->lt_in.c_cflag = CLOCAL; 5879406Sbde } 5889406Sbde termioschars(&com->it_in); 5899406Sbde com->it_in.c_ispeed = com->it_in.c_ospeed = comdefaultrate; 5909406Sbde com->it_out = com->it_in; 5916261Sjkh 5929406Sbde com->kdc = kdc_sio[0]; 5939406Sbde com->kdc.kdc_name = "cy"; 5949406Sbde com->kdc.kdc_unit = unit; 5959406Sbde com->kdc.kdc_isa = isdp; 5969406Sbde com->kdc.kdc_parent = &kdc_sio[isdp->id_unit]; 5979406Sbde com->kdc.kdc_state = DC_IDLE; 5989406Sbde com->kdc.kdc_description = 5999406Sbde "Serial port: Cirrus Logic CD1400"; 6009406Sbde com->kdc.kdc_class = DC_CLS_SERIAL; 6019406Sbde dev_attach(&com->kdc); 6029406Sbde 6039406Sbde s = spltty(); 6049406Sbde com_addr(unit) = com; 6059406Sbde splx(s); 6069406Sbde } 6076261Sjkh } 6089406Sbde kdc_sio[isdp->id_unit].kdc_state = DC_BUSY; /* XXX */ 6096261Sjkh 6109406Sbde /* ensure an edge for the next interrupt */ 6119406Sbde cy_outb(cy_iobase, CY_CLEAR_INTR, 0); 6126261Sjkh 6139406Sbde return (1); 6146261Sjkh} 6156261Sjkh 6166261Sjkhint 6179406Sbdesioopen(dev, flag, mode, p) 6189406Sbde dev_t dev; 6199406Sbde int flag; 6209406Sbde int mode; 6219406Sbde struct proc *p; 6226261Sjkh{ 6239406Sbde struct com_s *com; 6249406Sbde int error; 6259406Sbde cy_addr iobase; 6269406Sbde int mynor; 6279406Sbde int s; 6286261Sjkh struct tty *tp; 6299406Sbde int unit; 6306261Sjkh 6319406Sbde mynor = minor(dev); 6329406Sbde unit = MINOR_TO_UNIT(mynor); 6339406Sbde if ((u_int) unit >= NSIO || (com = com_addr(unit)) == NULL) 6346261Sjkh return (ENXIO); 6359406Sbde if (mynor & CONTROL_MASK) 6369406Sbde return (0); 6379406Sbde#if 0 /* XXX TK2.0 */ 6389406Sbde tp = com->tp = sio_tty[unit] = ttymalloc(sio_tty[unit]); 6396454Sbde#else 6409406Sbde tp = com->tp = &sio_tty[unit]; 6416454Sbde#endif 6429406Sbde s = spltty(); 6439406Sbde /* 6449406Sbde * We jump to this label after all non-interrupted sleeps to pick 6459406Sbde * up any changes of the device state. 6469406Sbde */ 6479406Sbdeopen_top: 6489406Sbde while (com->state & CS_DTR_OFF) { 6499406Sbde error = tsleep(&com->dtr_wait, TTIPRI | PCATCH, "cydtr", 0); 6509406Sbde if (error != 0) 6519406Sbde goto out; 6529406Sbde } 6539406Sbde com->kdc.kdc_state = DC_BUSY; 6549406Sbde if (tp->t_state & TS_ISOPEN) { 6559406Sbde /* 6569406Sbde * The device is open, so everything has been initialized. 6579406Sbde * Handle conflicts. 6589406Sbde */ 6599406Sbde if (mynor & CALLOUT_MASK) { 6609406Sbde if (!com->active_out) { 6619406Sbde error = EBUSY; 6629406Sbde goto out; 6639406Sbde } 6649406Sbde } else { 6659406Sbde if (com->active_out) { 6669406Sbde if (flag & O_NONBLOCK) { 6679406Sbde error = EBUSY; 6689406Sbde goto out; 6699406Sbde } 6709406Sbde error = tsleep(&com->active_out, 6719406Sbde TTIPRI | PCATCH, "cybi", 0); 6729406Sbde if (error != 0) 6739406Sbde goto out; 6749406Sbde goto open_top; 6759406Sbde } 6766261Sjkh } 6779406Sbde if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) { 6789406Sbde error = EBUSY; 6799406Sbde goto out; 6809406Sbde } 6819406Sbde } else { 6829406Sbde /* 6839406Sbde * The device isn't open, so there are no conflicts. 6849406Sbde * Initialize it. Initialization is done twice in many 6859406Sbde * cases: to preempt sleeping callin opens if we are 6869406Sbde * callout, and to complete a callin open after DCD rises. 6879406Sbde */ 6889406Sbde tp->t_oproc = comstart; 6899406Sbde tp->t_param = comparam; 6909406Sbde tp->t_dev = dev; 6919406Sbde tp->t_termios = mynor & CALLOUT_MASK 6929406Sbde ? com->it_out : com->it_in; 6939406Sbde#if 0 6949406Sbde (void)commctl(com, TIOCM_DTR | TIOCM_RTS, DMSET); 6959406Sbde com->ftl_max = com->ftl_init; 6969406Sbde com->poll = com->no_irq; 6979406Sbde com->poll_output = com->loses_outints; 6989406Sbde#endif 6999406Sbde ++com->wopeners; 7009406Sbde iobase = com->iobase; 7016261Sjkh 7029406Sbde /* reset this channel */ 7039406Sbde cd_outb(iobase, CD1400_CAR, unit & CD1400_CAR_CHAN); 7049406Sbde cd1400_channel_cmd(iobase, CD1400_CCR_CMDRESET); 7056261Sjkh 7066261Sjkh /* 7079406Sbde * Resetting disables the transmitter and receiver as well as 7089406Sbde * flushing the fifos so some of our cached state becomes 7099406Sbde * invalid. The documentation suggests that all registers 7109406Sbde * for the current channel are reset to defaults, but 7119406Sbde * apparently none are. We wouldn't want DTR cleared. 7129406Sbde */ 7139406Sbde com->channel_control = 0; 7149406Sbde 7159406Sbde /* Encode per-board unit in LIVR for access in intr routines. */ 7169406Sbde cd_outb(iobase, CD1400_LIVR, 7179406Sbde (unit & CD1400_xIVR_CHAN) << CD1400_xIVR_CHAN_SHIFT); 7189406Sbde 7199406Sbde /* 7206261Sjkh * raise dtr and generally set things up correctly. this 7216261Sjkh * has the side-effect of selecting the appropriate cd1400 7226261Sjkh * channel, to help us with subsequent channel control stuff 7236261Sjkh */ 7249406Sbde error = comparam(tp, &tp->t_termios); 7259406Sbde --com->wopeners; 7269406Sbde if (error != 0) 7279406Sbde goto out; 7286261Sjkh /* 7299406Sbde * XXX we should goto open_top if comparam() slept. 7306261Sjkh */ 7316261Sjkh ttsetwater(tp); 7329406Sbde#if 0 7339406Sbde if (com->hasfifo) { 7349406Sbde /* 7359406Sbde * (Re)enable and drain fifos. 7369406Sbde * 7379406Sbde * Certain SMC chips cause problems if the fifos 7389406Sbde * are enabled while input is ready. Turn off the 7399406Sbde * fifo if necessary to clear the input. We test 7409406Sbde * the input ready bit after enabling the fifos 7419406Sbde * since we've already enabled them in comparam() 7429406Sbde * and to handle races between enabling and fresh 7439406Sbde * input. 7449406Sbde */ 7459406Sbde while (TRUE) { 7469406Sbde outb(iobase + com_fifo, 7479406Sbde FIFO_RCV_RST | FIFO_XMT_RST 7489406Sbde | FIFO_ENABLE | com->ftl); 7499406Sbde DELAY(100); 7509406Sbde if (!(inb(com->line_status_port) & LSR_RXRDY)) 7519406Sbde break; 7529406Sbde outb(iobase + com_fifo, 0); 7539406Sbde DELAY(100); 7549406Sbde (void) inb(com->data_port); 7559406Sbde } 7569406Sbde } 7576261Sjkh 7589406Sbde disable_intr(); 7599406Sbde (void) inb(com->line_status_port); 7609406Sbde (void) inb(com->data_port); 7619406Sbde com->prev_modem_status = com->last_modem_status 7629406Sbde = inb(com->modem_status_port); 7639406Sbde outb(iobase + com_ier, IER_ERXRDY | IER_ETXRDY | IER_ERLS 7649406Sbde | IER_EMSC); 7659406Sbde enable_intr(); 7669406Sbde#else /* !0 */ 7679406Sbde /* XXX raise RTS too */ 7689406Sbde (void)commctl(com, TIOCM_DTR | TIOCM_RTS, DMSET); 7699406Sbde disable_intr(); 7709406Sbde com->prev_modem_status = com->last_modem_status 7719406Sbde = cd_inb(iobase, CD1400_MSVR2); 7729406Sbde cd_outb(iobase, CD1400_SRER, 7739406Sbde com->intr_enable 7749406Sbde = CD1400_SRER_MDMCH | CD1400_SRER_RXDATA); 7759406Sbde enable_intr(); 7769406Sbde#endif /* 0 */ 7779406Sbde /* 7789406Sbde * Handle initial DCD. Callout devices get a fake initial 7799406Sbde * DCD (trapdoor DCD). If we are callout, then any sleeping 7809406Sbde * callin opens get woken up and resume sleeping on "cybi" 7819406Sbde * instead of "cydcd". 7829406Sbde */ 7839406Sbde /* 7849406Sbde * XXX `mynor & CALLOUT_MASK' should be 7859406Sbde * `tp->t_cflag & (SOFT_CARRIER | TRAPDOOR_CARRIER) where 7869406Sbde * TRAPDOOR_CARRIER is the default initial state for callout 7879406Sbde * devices and SOFT_CARRIER is like CLOCAL except it hides 7889406Sbde * the true carrier. 7899406Sbde */ 7909406Sbde if (com->prev_modem_status & MSR_DCD || mynor & CALLOUT_MASK) 7919406Sbde (*linesw[tp->t_line].l_modem)(tp, 1); 7929406Sbde } 7939406Sbde /* 7949406Sbde * Wait for DCD if necessary. 7959406Sbde */ 7969406Sbde if (!(tp->t_state & TS_CARR_ON) && !(mynor & CALLOUT_MASK) 7979406Sbde && !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK)) { 7989406Sbde ++com->wopeners; 7999406Sbde error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "cydcd", 0); 8009406Sbde --com->wopeners; 8019406Sbde if (error != 0) 8029406Sbde goto out; 8039406Sbde goto open_top; 8049406Sbde } 8059406Sbde error = (*linesw[tp->t_line].l_open)(dev, tp); 8069406Sbde disc_optim(tp, &tp->t_termios, com); 8079406Sbde if (tp->t_state & TS_ISOPEN && mynor & CALLOUT_MASK) 8089406Sbde com->active_out = TRUE; 8099406Sbde siosettimeout(); 8109406Sbdeout: 8119406Sbde splx(s); 8129406Sbde if (!(tp->t_state & TS_ISOPEN) && com->wopeners == 0) 8139406Sbde comhardclose(com); 8146261Sjkh return (error); 8159406Sbde} 8166261Sjkh 8176261Sjkhint 8189406Sbdesioclose(dev, flag, mode, p) 8199406Sbde dev_t dev; 8209406Sbde int flag; 8219406Sbde int mode; 8229406Sbde struct proc *p; 8236261Sjkh{ 8249406Sbde struct com_s *com; 8259406Sbde int mynor; 8266261Sjkh int s; 8279406Sbde struct tty *tp; 8286261Sjkh 8299406Sbde mynor = minor(dev); 8309406Sbde if (mynor & CONTROL_MASK) 8319406Sbde return (0); 8329406Sbde com = com_addr(MINOR_TO_UNIT(mynor)); 8339406Sbde tp = com->tp; 8346261Sjkh s = spltty(); 8359406Sbde (*linesw[tp->t_line].l_close)(tp, flag); 8369406Sbde disc_optim(tp, &tp->t_termios, com); 8379406Sbde siostop(tp, FREAD | FWRITE); 8389406Sbde comhardclose(com); 8399406Sbde ttyclose(tp); 8409406Sbde siosettimeout(); 8416261Sjkh splx(s); 8426261Sjkh#ifdef broken /* session holds a ref to the tty; can't deallocate */ 8436261Sjkh ttyfree(tp); 8449406Sbde com->tp = sio_tty[unit] = NULL; 8456261Sjkh#endif 8469406Sbde return (0); 8479406Sbde} 8486261Sjkh 8499406Sbdestatic void 8509406Sbdecomhardclose(com) 8519406Sbde struct com_s *com; 8529406Sbde{ 8539406Sbde cy_addr iobase; 8549406Sbde int s; 8559406Sbde struct tty *tp; 8569406Sbde int unit; 8576261Sjkh 8589406Sbde unit = com->unit; 8599406Sbde iobase = com->iobase; 8609406Sbde s = spltty(); 8619406Sbde#if 0 8629406Sbde com->poll = FALSE; 8639406Sbde com->poll_output = FALSE; 8649406Sbde#endif 8659406Sbde com->do_timestamp = 0; 8669406Sbde cd_outb(iobase, CD1400_CAR, unit & CD1400_CAR_CHAN); 8679406Sbde#if 0 8689406Sbde outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); 8699406Sbde#endif 8706261Sjkh 8719406Sbde#ifdef KGDB 8729406Sbde /* do not disable interrupts or hang up if debugging */ 8739406Sbde if (kgdb_dev != makedev(commajor, unit)) 8749406Sbde#endif 8759406Sbde { 8769406Sbde#if 0 8779406Sbde outb(iobase + com_ier, 0); 8789406Sbde#else 8799406Sbde disable_intr(); 8809406Sbde cd_outb(iobase, CD1400_SRER, com->intr_enable = 0); 8819406Sbde enable_intr(); 8829406Sbde#endif 8839406Sbde tp = com->tp; 8849406Sbde if (tp->t_cflag & HUPCL 8859406Sbde /* 8869406Sbde * XXX we will miss any carrier drop between here and the 8879406Sbde * next open. Perhaps we should watch DCD even when the 8889406Sbde * port is closed; it is not sufficient to check it at 8899406Sbde * the next open because it might go up and down while 8909406Sbde * we're not watching. 8919406Sbde */ 8929406Sbde || !com->active_out 8939406Sbde && !(com->prev_modem_status & MSR_DCD) 8949406Sbde && !(com->it_in.c_cflag & CLOCAL) 8959406Sbde || !(tp->t_state & TS_ISOPEN)) { 8969406Sbde (void)commctl(com, TIOCM_DTR, DMBIC); 8976261Sjkh 8989406Sbde /* Disable receiver (leave transmitter enabled). */ 8999406Sbde com->channel_control = CD1400_CCR_CMDCHANCTL 9009406Sbde | CD1400_CCR_XMTEN 9019406Sbde | CD1400_CCR_RCVDIS; 9029406Sbde cd1400_channel_cmd(iobase, com->channel_control); 9036261Sjkh 9049406Sbde if (com->dtr_wait != 0) { 9059406Sbde timeout(siodtrwakeup, com, com->dtr_wait); 9069406Sbde com->state |= CS_DTR_OFF; 9079406Sbde } 9089406Sbde } 9099406Sbde } 9109406Sbde com->active_out = FALSE; 9119406Sbde wakeup(&com->active_out); 9129406Sbde wakeup(TSA_CARR_ON(tp)); /* restart any wopeners */ 9139406Sbde if (!(com->state & CS_DTR_OFF) 9149406Sbde && !(unit == comconsole && (COMCONSOLE || boothowto & RB_SERIAL))) 9159406Sbde com->kdc.kdc_state = DC_IDLE; 9169406Sbde splx(s); 9179406Sbde} 9189406Sbde 9196261Sjkhint 9209406Sbdesioread(dev, uio, flag) 9219406Sbde dev_t dev; 9229406Sbde struct uio *uio; 9239406Sbde int flag; 9246261Sjkh{ 9259406Sbde int mynor; 9269406Sbde struct tty *tp; 9276261Sjkh 9289406Sbde mynor = minor(dev); 9299406Sbde if (mynor & CONTROL_MASK) 9309406Sbde return (ENODEV); 9319406Sbde tp = com_addr(MINOR_TO_UNIT(mynor))->tp; 9329406Sbde return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); 9339406Sbde} 9346261Sjkh 9356261Sjkhint 9369406Sbdesiowrite(dev, uio, flag) 9379406Sbde dev_t dev; 9389406Sbde struct uio *uio; 9399406Sbde int flag; 9406261Sjkh{ 9419406Sbde int mynor; 9429406Sbde struct tty *tp; 9439406Sbde int unit; 9446261Sjkh 9459406Sbde mynor = minor(dev); 9469406Sbde if (mynor & CONTROL_MASK) 9479406Sbde return (ENODEV); 9489406Sbde 9499406Sbde unit = MINOR_TO_UNIT(mynor); 9509406Sbde tp = com_addr(unit)->tp; 9519406Sbde /* 9529406Sbde * (XXX) We disallow virtual consoles if the physical console is 9539406Sbde * a serial port. This is in case there is a display attached that 9549406Sbde * is not the console. In that situation we don't need/want the X 9559406Sbde * server taking over the console. 9569406Sbde */ 9579406Sbde if (constty && unit == comconsole 9589406Sbde && (COMCONSOLE || boothowto & RB_SERIAL)) 9599406Sbde constty = NULL; 9606261Sjkh#ifdef Smarts 9616261Sjkh /* XXX duplicate ttwrite(), but without so much output processing on 9626261Sjkh * CR & LF chars. Hardly worth the effort, given that high-throughput 9636261Sjkh * sessions are raw anyhow. 9646261Sjkh */ 9656261Sjkh#else 9669406Sbde return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); 9676261Sjkh#endif 9689406Sbde} 9696261Sjkh 9706261Sjkhstatic void 9719406Sbdesiodtrwakeup(chan) 9729406Sbde void *chan; 9736261Sjkh{ 9749406Sbde struct com_s *com; 9756261Sjkh 9769406Sbde com = (struct com_s *)chan; 9779406Sbde com->state &= ~CS_DTR_OFF; 9789406Sbde if (!(com->unit == comconsole && (COMCONSOLE || boothowto & RB_SERIAL))) 9799406Sbde com->kdc.kdc_state = DC_IDLE; 9809406Sbde wakeup(&com->dtr_wait); 9819406Sbde} 9826261Sjkh 9839406Sbde/* Interrupt routine for timekeeping purposes */ 9849406Sbdevoid 9859406Sbdesiointrts(unit) 9869406Sbde int unit; 9876261Sjkh{ 9889406Sbde /* 9899406Sbde * XXX microtime() reenables CPU interrupts. We can't afford to 9909406Sbde * be interrupted and don't want to slow down microtime(), so lock 9919406Sbde * out interrupts in another way. 9929406Sbde */ 9939406Sbde outb(IO_ICU1 + 1, 0xff); 9949406Sbde microtime(&intr_timestamp); 9959406Sbde disable_intr(); 9969406Sbde outb(IO_ICU1 + 1, imen); 9976261Sjkh 9989406Sbde siointr(unit); 9999406Sbde} 10006261Sjkh 10016261Sjkhvoid 10029406Sbdesiointr(unit) 10039406Sbde int unit; 10046261Sjkh{ 10059406Sbde int baseu; 10069406Sbde cy_addr cy_iobase; 10079406Sbde int cyu; 10089406Sbde cy_addr iobase; 10099406Sbde u_char status; 10106261Sjkh 10119406Sbde baseu = unit * CY_MAX_PORTS; 10129406Sbde cy_iobase = com_addr(baseu)->cy_iobase; 10136261Sjkh 10149406Sbde /* check each CD1400 in turn */ 10159406Sbde for (cyu = 0, iobase = cy_iobase; cyu < cy_nr_cd1400s[unit]; 10169406Sbde ++cyu, iobase += CY_CD1400_MEMSIZE) { 10179406Sbde /* poll to see if it has any work */ 10189406Sbde status = cd_inb(iobase, CD1400_SVRR); 10199406Sbde if (status == 0) 10209406Sbde continue; 10216261Sjkh#ifdef CyDebug 10229406Sbde ++cy_svrr_probes; 10236261Sjkh#endif 10249406Sbde /* service requests as appropriate, giving priority to RX */ 10259406Sbde if (status & CD1400_SVRR_RXRDY) { 10269406Sbde struct com_s *com; 10279406Sbde u_int count; 10289406Sbde u_char *ioptr; 10299406Sbde u_char line_status; 10309406Sbde u_char recv_data; 10319406Sbde u_char serv_type; 10329406Sbde#ifdef PollMode 10339406Sbde u_char save_car; 10349406Sbde u_char save_rir; 10356261Sjkh#endif 10366261Sjkh 10379406Sbde#ifdef PollMode 10389406Sbde save_rir = cd_inb(iobase, CD1400_RIR); 10399406Sbde save_car = cd_inb(iobase, CD1400_CAR); 10406261Sjkh 10419406Sbde /* enter rx service */ 10429406Sbde cd_outb(iobase, CD1400_CAR, save_rir); 10436261Sjkh 10449406Sbde serv_type = cd_inb(iobase, CD1400_RIVR); 10459406Sbde com = com_addr(baseu 10469406Sbde + ((serv_type >> CD1400_xIVR_CHAN_SHIFT) 10479406Sbde & CD1400_xIVR_CHAN)); 10486261Sjkh#else 10499406Sbde /* ack receive service */ 10509406Sbde serv_type = cy_inb(iobase, CY8_SVCACKR); 10516261Sjkh 10529406Sbde com = com_addr(baseu + 10539406Sbde + ((serv_type >> CD1400_xIVR_CHAN_SHIFT) 10549406Sbde & CD1400_xIVR_CHAN)); 10556261Sjkh#endif 10566261Sjkh 10579406Sbde if (com->do_timestamp) 10589406Sbde /* XXX a little bloat here... */ 10599406Sbde com->timestamp = intr_timestamp; 10606261Sjkh 10619406Sbde if (serv_type & CD1400_RIVR_EXCEPTION) { 10629406Sbde ++com->recv_exception; 10639406Sbde line_status = cd_inb(iobase, CD1400_RDSR); 10649406Sbde /* break/unnattached error bits or real input? */ 10659406Sbde recv_data = cd_inb(iobase, CD1400_RDSR); 10669406Sbde#ifndef SOFT_HOTCHAR 10679406Sbde if (line_status & CD1400_RDSR_SPECIAL 10689406Sbde && com->hotchar != 0) 10699406Sbde setsofttty(); 10706261Sjkh#endif 10719406Sbde#if 1 /* XXX "intelligent" PFO error handling would break O error handling */ 10729406Sbde if (line_status & (LSR_PE|LSR_FE|LSR_BI)) { 10739406Sbde /* 10749406Sbde Don't store PE if IGNPAR and BI if IGNBRK, 10759406Sbde this hack allows "raw" tty optimization 10769406Sbde works even if IGN* is set. 10779406Sbde */ 10789406Sbde if ( com->tp == NULL 10799406Sbde || !(com->tp->t_state & TS_ISOPEN) 10809406Sbde || (line_status & (LSR_PE|LSR_FE)) 10819406Sbde && (com->tp->t_iflag & IGNPAR) 10829406Sbde || (line_status & LSR_BI) 10839406Sbde && (com->tp->t_iflag & IGNBRK)) 10849406Sbde goto cont; 10859406Sbde if ( (line_status & (LSR_PE|LSR_FE)) 10869406Sbde && (com->tp->t_state & TS_CAN_BYPASS_L_RINT) 10879406Sbde && ((line_status & LSR_FE) 10889406Sbde || (line_status & LSR_PE) 10899406Sbde && (com->tp->t_iflag & INPCK))) 10909406Sbde recv_data = 0; 10919406Sbde } 10929406Sbde#endif /* 1 */ 10939406Sbde ++com->bytes_in; 10949406Sbde#ifdef SOFT_HOTCHAR 10959406Sbde if (com->hotchar != 0 && recv_data == com->hotchar) 10969406Sbde setsofttty(); 10976261Sjkh#endif 10989406Sbde ioptr = com->iptr; 10999406Sbde if (ioptr >= com->ibufend) 11009406Sbde CE_RECORD(com, CE_INTERRUPT_BUF_OVERFLOW); 11019406Sbde else { 11029406Sbde ++com_events; 11039406Sbde ioptr[0] = recv_data; 11049406Sbde ioptr[CE_INPUT_OFFSET] = line_status; 11059406Sbde com->iptr = ++ioptr; 11069406Sbde if (ioptr == com->ihighwater 11079406Sbde && com->state & CS_RTS_IFLOW) 11089406Sbde#if 0 11099406Sbde outb(com->modem_ctl_port, 11109406Sbde com->mcr_image &= ~MCR_RTS); 11119406Sbde#else 11129406Sbde cd_outb(iobase, CD1400_MSVR1, 11139406Sbde com->mcr_image &= ~MCR_RTS); 11146261Sjkh#endif 11159406Sbde if (line_status & LSR_OE) 11169406Sbde CE_RECORD(com, CE_OVERRUN); 11179406Sbde } 11189406Sbde goto cont; 11199406Sbde } else { 11209406Sbde int ifree; 11216261Sjkh 11229406Sbde count = cd_inb(iobase, CD1400_RDCR); 11239406Sbde com->bytes_in += count; 11249406Sbde ioptr = com->iptr; 11259406Sbde ifree = com->ibufend - ioptr; 11269406Sbde if (count > ifree) { 11279406Sbde count -= ifree; 11289406Sbde com_events += ifree; 11299406Sbde while (ifree-- != 0) { 11309406Sbde recv_data = cd_inb(iobase, CD1400_RDSR); 11319406Sbde#ifdef SOFT_HOTCHAR 11329406Sbde if (com->hotchar != 0 11339406Sbde && recv_data == com->hotchar) 11349406Sbde setsofttty(); 11356261Sjkh#endif 11369406Sbde ioptr[0] = recv_data; 11379406Sbde ioptr[CE_INPUT_OFFSET] = 0; 11389406Sbde ++ioptr; 11399406Sbde } 11409406Sbde com->delta_error_counts 11419406Sbde [CE_INTERRUPT_BUF_OVERFLOW] += count; 11429406Sbde do { 11439406Sbde recv_data = cd_inb(iobase, CD1400_RDSR); 11449406Sbde#ifdef SOFT_HOTCHAR 11459406Sbde if (com->hotchar != 0 11469406Sbde && recv_data == com->hotchar) 11479406Sbde setsofttty(); 11486261Sjkh#endif 11499406Sbde } while (--count != 0); 11509406Sbde } else { 11519406Sbde if (ioptr <= com->ihighwater 11529406Sbde && ioptr + count > com->ihighwater 11539406Sbde && com->state & CS_RTS_IFLOW) 11549406Sbde#if 0 11559406Sbde outb(com->modem_ctl_port, 11569406Sbde com->mcr_image &= ~MCR_RTS); 11576261Sjkh#else 11589406Sbde cd_outb(iobase, CD1400_MSVR1, 11599406Sbde com->mcr_image &= ~MCR_RTS); 11606261Sjkh#endif 11619406Sbde com_events += count; 11629406Sbde do { 11639406Sbde recv_data = cd_inb(iobase, CD1400_RDSR); 11649406Sbde#ifdef SOFT_HOTCHAR 11659406Sbde if (com->hotchar != 0 11669406Sbde && recv_data == com->hotchar) 11679406Sbde setsofttty(); 11686261Sjkh#endif 11699406Sbde ioptr[0] = recv_data; 11709406Sbde ioptr[CE_INPUT_OFFSET] = 0; 11719406Sbde ++ioptr; 11729406Sbde } while (--count != 0); 11736261Sjkh } 11749406Sbde com->iptr = ioptr; 11756261Sjkh } 11769406Sbdecont: 11776261Sjkh 11789406Sbde /* terminate service context */ 11796261Sjkh#ifdef PollMode 11809406Sbde cd_outb(iobase, CD1400_RIR, 11819406Sbde save_rir 11829406Sbde & ~(CD1400_RIR_RDIREQ | CD1400_RIR_RBUSY)); 11839406Sbde cd_outb(iobase, CD1400_CAR, save_car); 11846261Sjkh#else 11859406Sbde cd_outb(iobase, CD1400_EOSRR, 0); 11866261Sjkh#endif 11879406Sbde } 11889406Sbde if (status & CD1400_SVRR_MDMCH) { 11899406Sbde struct com_s *com; 11909406Sbde u_char modem_status; 11916261Sjkh#ifdef PollMode 11929406Sbde u_char save_car; 11939406Sbde u_char save_mir; 11946261Sjkh#else 11959406Sbde u_char vector; 11966261Sjkh#endif 11976261Sjkh 11986261Sjkh#ifdef PollMode 11999406Sbde save_mir = cd_inb(iobase, CD1400_MIR); 12009406Sbde save_car = cd_inb(iobase, CD1400_CAR); 12016261Sjkh 12029406Sbde /* enter modem service */ 12039406Sbde cd_outb(iobase, CD1400_CAR, save_mir); 12046261Sjkh 12059406Sbde com = com_addr(baseu + cyu * CD1400_NO_OF_CHANNELS 12069406Sbde + (save_mir & CD1400_MIR_CHAN)); 12076261Sjkh#else 12089406Sbde /* ack modem service */ 12099406Sbde vector = cy_inb(iobase, CY8_SVCACKM); 12106261Sjkh 12119406Sbde com = com_addr(baseu 12129406Sbde + ((vector >> CD1400_xIVR_CHAN_SHIFT) 12139406Sbde & CD1400_xIVR_CHAN)); 12149406Sbde#endif 12159406Sbde ++com->mdm; 12169406Sbde modem_status = cd_inb(iobase, CD1400_MSVR2); 12179406Sbde if (modem_status != com->last_modem_status) { 12189406Sbde /* 12199406Sbde * Schedule high level to handle DCD changes. Note 12209406Sbde * that we don't use the delta bits anywhere. Some 12219406Sbde * UARTs mess them up, and it's easy to remember the 12229406Sbde * previous bits and calculate the delta. 12239406Sbde */ 12249406Sbde com->last_modem_status = modem_status; 12259406Sbde if (!(com->state & CS_CHECKMSR)) { 12269406Sbde com_events += LOTS_OF_EVENTS; 12279406Sbde com->state |= CS_CHECKMSR; 12289406Sbde setsofttty(); 12299406Sbde } 12306261Sjkh 12319406Sbde#ifdef SOFT_CTS_OFLOW 12329406Sbde /* handle CTS change immediately for crisp flow ctl */ 12339406Sbde if (com->state & CS_CTS_OFLOW) { 12349406Sbde if (modem_status & MSR_CTS) { 12359406Sbde com->state |= CS_ODEVREADY; 12369406Sbde if (com->state >= (CS_BUSY | CS_TTGO 12379406Sbde | CS_ODEVREADY) 12389406Sbde && !(com->intr_enable 12399406Sbde & CD1400_SRER_TXRDY)) 12409406Sbde cd_outb(iobase, CD1400_SRER, 12419406Sbde com->intr_enable 12429406Sbde |= CD1400_SRER_TXRDY); 12439406Sbde } else { 12449406Sbde com->state &= ~CS_ODEVREADY; 12459406Sbde if (com->intr_enable & CD1400_SRER_TXRDY) 12469406Sbde cd_outb(iobase, CD1400_SRER, 12479406Sbde com->intr_enable 12489406Sbde &= ~CD1400_SRER_TXRDY); 12499406Sbde } 12509406Sbde } 12516261Sjkh#endif 12529406Sbde } 12536261Sjkh 12549406Sbde /* terminate service context */ 12556261Sjkh#ifdef PollMode 12569406Sbde cd_outb(iobase, CD1400_MIR, 12579406Sbde save_mir 12589406Sbde & ~(CD1400_MIR_RDIREQ | CD1400_MIR_RBUSY)); 12599406Sbde cd_outb(iobase, CD1400_CAR, save_car); 12606261Sjkh#else 12619406Sbde cd_outb(iobase, CD1400_EOSRR, 0); 12626261Sjkh#endif 12639406Sbde } 12649406Sbde if (status & CD1400_SVRR_TXRDY) { 12659406Sbde struct com_s *com; 12666261Sjkh#ifdef PollMode 12679406Sbde u_char save_car; 12689406Sbde u_char save_tir; 12696261Sjkh#else 12709406Sbde u_char vector; 12716261Sjkh#endif 12726261Sjkh 12736261Sjkh#ifdef PollMode 12749406Sbde save_tir = cd_inb(iobase, CD1400_TIR); 12759406Sbde save_car = cd_inb(iobase, CD1400_CAR); 12769406Sbde 12779406Sbde /* enter tx service */ 12789406Sbde cd_outb(iobase, CD1400_CAR, save_tir); 12799406Sbde com = com_addr(baseu 12809406Sbde + cyu * CD1400_NO_OF_CHANNELS 12819406Sbde + (save_tir & CD1400_TIR_CHAN)); 12826261Sjkh#else 12839406Sbde /* ack transmit service */ 12849406Sbde vector = cy_inb(iobase, CY8_SVCACKT); 12856261Sjkh 12869406Sbde com = com_addr(baseu 12879406Sbde + ((vector >> CD1400_xIVR_CHAN_SHIFT) 12889406Sbde & CD1400_xIVR_CHAN)); 12896261Sjkh#endif 12906261Sjkh 12919406Sbde if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) { 12929406Sbde u_char *ioptr; 12939406Sbde u_int ocount; 12946261Sjkh 12959406Sbde ioptr = com->obufq.l_head; 12969406Sbde ocount = com->obufq.l_tail - ioptr; 12979406Sbde if (ocount > CD1400_TX_FIFO_SIZE) 12989406Sbde ocount = CD1400_TX_FIFO_SIZE; 12999406Sbde com->bytes_out += ocount; 13009406Sbde do 13019406Sbde cd_outb(iobase, CD1400_TDR, *ioptr++); 13029406Sbde while (--ocount != 0); 13039406Sbde com->obufq.l_head = ioptr; 13049406Sbde if (ioptr >= com->obufq.l_tail) { 13059406Sbde struct lbq *qp; 13066261Sjkh 13079406Sbde qp = com->obufq.l_next; 13089406Sbde qp->l_queued = FALSE; 13099406Sbde qp = qp->l_next; 13109406Sbde if (qp != NULL) { 13119406Sbde com->obufq.l_head = qp->l_head; 13129406Sbde com->obufq.l_tail = qp->l_tail; 13139406Sbde com->obufq.l_next = qp; 13149406Sbde } else { 13159406Sbde /* output just completed */ 13169406Sbde com->state &= ~CS_BUSY; 13179406Sbde cd_outb(iobase, CD1400_SRER, 13189406Sbde com->intr_enable 13199406Sbde &= ~CD1400_SRER_TXRDY); 13209406Sbde } 13219406Sbde if (!(com->state & CS_ODONE)) { 13229406Sbde com_events += LOTS_OF_EVENTS; 13239406Sbde com->state |= CS_ODONE; 13249406Sbde setsofttty(); /* handle at high level ASAP */ 13259406Sbde } 13269406Sbde } 13279406Sbde } 13286261Sjkh 13299406Sbde /* terminate service context */ 13306261Sjkh#ifdef PollMode 13319406Sbde cd_outb(iobase, CD1400_TIR, 13329406Sbde save_tir 13339406Sbde & ~(CD1400_TIR_RDIREQ | CD1400_TIR_RBUSY)); 13349406Sbde cd_outb(iobase, CD1400_CAR, save_car); 13356261Sjkh#else 13369406Sbde cd_outb(iobase, CD1400_EOSRR, 0); 13376261Sjkh#endif 13389406Sbde } 13396261Sjkh } 13406261Sjkh 13419406Sbde /* ensure an edge for the next interrupt */ 13429406Sbde cy_outb(cy_iobase, CY_CLEAR_INTR, 0); 13436261Sjkh 13449406Sbde schedsofttty(); 13459406Sbde} 13466261Sjkh 13479406Sbdestatic void 13489406Sbdesiointr1(com) 13499406Sbde struct com_s *com; 13509406Sbde{ 13516261Sjkh} 13526261Sjkh 13536261Sjkhint 13549406Sbdesioioctl(dev, cmd, data, flag, p) 13559406Sbde dev_t dev; 13569406Sbde int cmd; 13579406Sbde caddr_t data; 13589406Sbde int flag; 13599406Sbde struct proc *p; 13606261Sjkh{ 13619406Sbde struct com_s *com; 13626261Sjkh int error; 13639406Sbde cy_addr iobase; 13649406Sbde int mynor; 13659406Sbde int s; 13669406Sbde struct tty *tp; 13679406Sbde#if defined(COMPAT_43) || defined(COMPAT_SUNOS) 13689406Sbde int oldcmd; 13699406Sbde struct termios term; 13709406Sbde#endif 13716261Sjkh 13729406Sbde mynor = minor(dev); 13739406Sbde com = com_addr(MINOR_TO_UNIT(mynor)); 13749406Sbde iobase = com->iobase; 13759406Sbde if (mynor & CONTROL_MASK) { 13769406Sbde struct termios *ct; 13779406Sbde 13789406Sbde switch (mynor & CONTROL_MASK) { 13799406Sbde case CONTROL_INIT_STATE: 13809406Sbde ct = mynor & CALLOUT_MASK ? &com->it_out : &com->it_in; 13819406Sbde break; 13829406Sbde case CONTROL_LOCK_STATE: 13839406Sbde ct = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in; 13849406Sbde break; 13859406Sbde default: 13869406Sbde return (ENODEV); /* /dev/nodev */ 13879406Sbde } 13889406Sbde switch (cmd) { 13899406Sbde case TIOCSETA: 13909406Sbde error = suser(p->p_ucred, &p->p_acflag); 13919406Sbde if (error != 0) 13929406Sbde return (error); 13939406Sbde *ct = *(struct termios *)data; 13949406Sbde return (0); 13959406Sbde case TIOCGETA: 13969406Sbde *(struct termios *)data = *ct; 13979406Sbde return (0); 13989406Sbde case TIOCGETD: 13999406Sbde *(int *)data = TTYDISC; 14009406Sbde return (0); 14019406Sbde case TIOCGWINSZ: 14029406Sbde bzero(data, sizeof(struct winsize)); 14039406Sbde return (0); 14049406Sbde default: 14059406Sbde return (ENOTTY); 14069406Sbde } 14079406Sbde } 14089406Sbde tp = com->tp; 14099406Sbde#if defined(COMPAT_43) || defined(COMPAT_SUNOS) 14109406Sbde term = tp->t_termios; 14119406Sbde oldcmd = cmd; 14129406Sbde error = ttsetcompat(tp, &cmd, data, &term); 14139406Sbde if (error != 0) 14146261Sjkh return (error); 14159406Sbde if (cmd != oldcmd) 14169406Sbde data = (caddr_t)&term; 14176261Sjkh#endif 14189406Sbde if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { 14199406Sbde int cc; 14209406Sbde struct termios *dt = (struct termios *)data; 14219406Sbde struct termios *lt = mynor & CALLOUT_MASK 14229406Sbde ? &com->lt_out : &com->lt_in; 14239406Sbde 14249406Sbde dt->c_iflag = (tp->t_iflag & lt->c_iflag) 14259406Sbde | (dt->c_iflag & ~lt->c_iflag); 14269406Sbde dt->c_oflag = (tp->t_oflag & lt->c_oflag) 14279406Sbde | (dt->c_oflag & ~lt->c_oflag); 14289406Sbde dt->c_cflag = (tp->t_cflag & lt->c_cflag) 14299406Sbde | (dt->c_cflag & ~lt->c_cflag); 14309406Sbde dt->c_lflag = (tp->t_lflag & lt->c_lflag) 14319406Sbde | (dt->c_lflag & ~lt->c_lflag); 14329406Sbde for (cc = 0; cc < NCCS; ++cc) 14339406Sbde if (lt->c_cc[cc] != 0) 14349406Sbde dt->c_cc[cc] = tp->t_cc[cc]; 14359406Sbde if (lt->c_ispeed != 0) 14369406Sbde dt->c_ispeed = tp->t_ispeed; 14379406Sbde if (lt->c_ospeed != 0) 14389406Sbde dt->c_ospeed = tp->t_ospeed; 14399406Sbde } 14409406Sbde error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); 14416261Sjkh if (error >= 0) 14426261Sjkh return (error); 14439406Sbde s = spltty(); 14449406Sbde error = ttioctl(tp, cmd, data, flag); 14459406Sbde disc_optim(tp, &tp->t_termios, com); 14469406Sbde if (error >= 0) { 14479406Sbde splx(s); 14489406Sbde return (error); 14499406Sbde } 14509406Sbde cd_outb(iobase, CD1400_CAR, MINOR_TO_UNIT(mynor) & CD1400_CAR_CHAN); 14516261Sjkh switch (cmd) { 14529406Sbde#if 0 14536261Sjkh case TIOCSBRK: 14549406Sbde outb(iobase + com_cfcr, com->cfcr_image |= CFCR_SBREAK); 14556261Sjkh break; 14566261Sjkh case TIOCCBRK: 14579406Sbde outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); 14586261Sjkh break; 14599406Sbde#endif /* 0 */ 14606261Sjkh case TIOCSDTR: 14619406Sbde (void)commctl(com, TIOCM_DTR, DMBIS); 14626261Sjkh break; 14636261Sjkh case TIOCCDTR: 14649406Sbde (void)commctl(com, TIOCM_DTR, DMBIC); 14656261Sjkh break; 14666261Sjkh case TIOCMSET: 14679406Sbde (void)commctl(com, *(int *)data, DMSET); 14686261Sjkh break; 14696261Sjkh case TIOCMBIS: 14709406Sbde (void)commctl(com, *(int *)data, DMBIS); 14716261Sjkh break; 14726261Sjkh case TIOCMBIC: 14739406Sbde (void)commctl(com, *(int *)data, DMBIC); 14746261Sjkh break; 14759406Sbde case TIOCMGET: 14769406Sbde *(int *)data = commctl(com, 0, DMGET); 14776261Sjkh break; 14786261Sjkh case TIOCMSDTRWAIT: 14799406Sbde /* must be root since the wait applies to following logins */ 14809406Sbde error = suser(p->p_ucred, &p->p_acflag); 14819406Sbde if (error != 0) { 14829406Sbde splx(s); 14839406Sbde return (error); 14849406Sbde } 14859406Sbde com->dtr_wait = *(int *)data * hz / 100; 14869406Sbde break; 14876261Sjkh case TIOCMGDTRWAIT: 14889406Sbde *(int *)data = com->dtr_wait * 100 / hz; 14896261Sjkh break; 14909406Sbde case TIOCTIMESTAMP: 14919406Sbde com->do_timestamp = TRUE; 14929406Sbde *(struct timeval *)data = com->timestamp; 14939406Sbde break; 14946261Sjkh default: 14959406Sbde splx(s); 14966261Sjkh return (ENOTTY); 14976261Sjkh } 14989406Sbde splx(s); 14999406Sbde return (0); 15009406Sbde} 15016261Sjkh 15029406Sbdevoid 15039406Sbdesiopoll() 15049406Sbde{ 15059406Sbde int unit; 15066261Sjkh 15079406Sbde#ifdef CyDebug 15089406Sbde ++cy_timeouts; 15099406Sbde#endif 15109406Sbde if (com_events == 0) 15119406Sbde return; 15129406Sbderepeat: 15139406Sbde for (unit = 0; unit < NSIO; ++unit) { 15149406Sbde u_char *buf; 15159406Sbde struct com_s *com; 15169406Sbde u_char *ibuf; 15179406Sbde cy_addr iobase; 15189406Sbde int incc; 15199406Sbde struct tty *tp; 15206261Sjkh 15219406Sbde com = com_addr(unit); 15229406Sbde if (com == NULL) 15239406Sbde continue; 15249406Sbde tp = com->tp; 15259406Sbde if (tp == NULL) { 15269406Sbde /* 15279406Sbde * XXX forget any events related to closed devices 15289406Sbde * (actually never opened devices) so that we don't 15299406Sbde * loop. 15309406Sbde */ 15319406Sbde disable_intr(); 15329406Sbde incc = com->iptr - com->ibuf; 15339406Sbde com->iptr = com->ibuf; 15349406Sbde if (com->state & CS_CHECKMSR) { 15359406Sbde incc += LOTS_OF_EVENTS; 15369406Sbde com->state &= ~CS_CHECKMSR; 15379406Sbde } 15389406Sbde com_events -= incc; 15399406Sbde enable_intr(); 15409406Sbde if (incc != 0) 15419406Sbde log(LOG_DEBUG, 15429406Sbde "sio%d: %d events for device with no tp\n", 15439406Sbde unit, incc); 15449406Sbde continue; 15459406Sbde } 15466261Sjkh 15479406Sbde /* switch the role of the low-level input buffers */ 15489406Sbde if (com->iptr == (ibuf = com->ibuf)) { 15499406Sbde buf = NULL; /* not used, but compiler can't tell */ 15509406Sbde incc = 0; 15519406Sbde } else { 15529406Sbde buf = ibuf; 15539406Sbde disable_intr(); 15549406Sbde incc = com->iptr - buf; 15559406Sbde com_events -= incc; 15569406Sbde if (ibuf == com->ibuf1) 15579406Sbde ibuf = com->ibuf2; 15589406Sbde else 15599406Sbde ibuf = com->ibuf1; 15609406Sbde com->ibufend = ibuf + RS_IBUFSIZE; 15619406Sbde com->ihighwater = ibuf + RS_IHIGHWATER; 15629406Sbde com->iptr = ibuf; 15636261Sjkh 15649406Sbde /* 15659406Sbde * There is now room for another low-level buffer full 15669406Sbde * of input, so enable RTS if it is now disabled and 15679406Sbde * there is room in the high-level buffer. 15689406Sbde */ 15699406Sbde /* 15709406Sbde * XXX this used not to look at CS_RTS_IFLOW. The 15719406Sbde * change is to allow full control of MCR_RTS via 15729406Sbde * ioctls after turning CS_RTS_IFLOW off. Check 15739406Sbde * for races. We shouldn't allow the ioctls while 15749406Sbde * CS_RTS_IFLOW is on. 15759406Sbde */ 15769406Sbde if ((com->state & CS_RTS_IFLOW) 15779406Sbde && !(com->mcr_image & MCR_RTS) 15789406Sbde && !(tp->t_state & TS_TBLOCK)) 15799406Sbde#if 0 15809406Sbde outb(com->modem_ctl_port, 15819406Sbde com->mcr_image |= MCR_RTS); 15829406Sbde#else 15839406Sbde iobase = com->iobase, 15849406Sbde cd_outb(iobase, CD1400_CAR, 15859406Sbde unit & CD1400_CAR_CHAN), 15869406Sbde cd_outb(iobase, CD1400_MSVR1, 15879406Sbde com->mcr_image |= MCR_RTS); 15889406Sbde#endif 15899406Sbde enable_intr(); 15909406Sbde com->ibuf = ibuf; 15919406Sbde } 15926261Sjkh 15939406Sbde if (com->state & CS_CHECKMSR) { 15949406Sbde u_char delta_modem_status; 15956261Sjkh 15969406Sbde disable_intr(); 15979406Sbde delta_modem_status = com->last_modem_status 15989406Sbde ^ com->prev_modem_status; 15999406Sbde com->prev_modem_status = com->last_modem_status; 16009406Sbde com_events -= LOTS_OF_EVENTS; 16019406Sbde com->state &= ~CS_CHECKMSR; 16029406Sbde enable_intr(); 16039406Sbde if (delta_modem_status & MSR_DCD) 16049406Sbde (*linesw[tp->t_line].l_modem) 16059406Sbde (tp, com->prev_modem_status & MSR_DCD); 16069406Sbde } 16079406Sbde if (com->state & CS_ODONE) { 16089406Sbde disable_intr(); 16099406Sbde com_events -= LOTS_OF_EVENTS; 16109406Sbde com->state &= ~CS_ODONE; 16119406Sbde if (!(com->state & CS_BUSY)) 16129406Sbde com->tp->t_state &= ~TS_BUSY; 16139406Sbde enable_intr(); 16149406Sbde (*linesw[tp->t_line].l_start)(tp); 16159406Sbde } 16169406Sbde if (incc <= 0 || !(tp->t_state & TS_ISOPEN)) 16179406Sbde continue; 16189406Sbde /* 16199406Sbde * XXX only do this when we bypass ttyinput. 16209406Sbde */ 16219406Sbde if (tp->t_rawq.c_cc + incc >= RB_I_HIGH_WATER 16229406Sbde && (com->state & CS_RTS_IFLOW || tp->t_iflag & IXOFF) 16239406Sbde && !(tp->t_state & TS_TBLOCK) 16249406Sbde /* 16259406Sbde * XXX - need flow control for all line disciplines. 16269406Sbde * Only have it in standard one now. 16279406Sbde */ 16289406Sbde && linesw[tp->t_line].l_rint == ttyinput) { 16299406Sbde int putc_status = 0; 16306261Sjkh 16319406Sbde if ((tp->t_iflag & IXOFF 16329406Sbde && tp->t_cc[VSTOP] != _POSIX_VDISABLE 16339406Sbde && (putc_status = putc(tp->t_cc[VSTOP], 16349406Sbde &tp->t_outq)) == 0) 16359406Sbde || com->state & CS_RTS_IFLOW) { 16369406Sbde tp->t_state |= TS_TBLOCK; 16379406Sbde ttstart(tp); 16389406Sbde if (putc_status != 0) 16399406Sbde /* Try again later. */ 16409406Sbde tp->t_state &= ~TS_TBLOCK; 16419406Sbde } 16429406Sbde } 16439406Sbde /* 16449406Sbde * Avoid the grotesquely inefficient lineswitch routine 16459406Sbde * (ttyinput) in "raw" mode. It usually takes about 450 16469406Sbde * instructions (that's without canonical processing or echo!). 16479406Sbde * slinput is reasonably fast (usually 40 instructions plus 16489406Sbde * call overhead). 16499406Sbde */ 16509406Sbde if (tp->t_state & TS_CAN_BYPASS_L_RINT) { 16519406Sbde tk_nin += incc; 16529406Sbde tk_rawcc += incc; 16539406Sbde tp->t_rawcc += incc; 16549406Sbde com->delta_error_counts[CE_TTY_BUF_OVERFLOW] 16559406Sbde += b_to_q((char *)buf, incc, &tp->t_rawq); 16569406Sbde ttwakeup(tp); 16579406Sbde if (tp->t_state & TS_TTSTOP 16589406Sbde && (tp->t_iflag & IXANY 16599406Sbde || tp->t_cc[VSTART] == tp->t_cc[VSTOP])) { 16609406Sbde tp->t_state &= ~TS_TTSTOP; 16619406Sbde tp->t_lflag &= ~FLUSHO; 16629406Sbde ttstart(tp); 16639406Sbde } 16649406Sbde } else { 16659406Sbde do { 16669406Sbde u_char line_status; 16679406Sbde int recv_data; 16686261Sjkh 16699406Sbde line_status = (u_char) buf[CE_INPUT_OFFSET]; 16709406Sbde recv_data = (u_char) *buf++; 16719406Sbde if (line_status 16729406Sbde & (LSR_BI | LSR_FE | LSR_OE | LSR_PE)) { 16739406Sbde if (line_status & LSR_BI) 16749406Sbde recv_data |= TTY_BI; 16759406Sbde if (line_status & LSR_FE) 16769406Sbde recv_data |= TTY_FE; 16779406Sbde if (line_status & LSR_OE) 16789406Sbde recv_data |= TTY_OE; 16799406Sbde if (line_status & LSR_PE) 16809406Sbde recv_data |= TTY_PE; 16819406Sbde } 16829406Sbde (*linesw[tp->t_line].l_rint)(recv_data, tp); 16839406Sbde } while (--incc > 0); 16849406Sbde } 16859406Sbde if (com_events == 0) 16869406Sbde break; 16879406Sbde } 16889406Sbde if (com_events >= LOTS_OF_EVENTS) 16899406Sbde goto repeat; 16909406Sbde} 16916261Sjkh 16929406Sbdestatic int 16939406Sbdecomparam(tp, t) 16949406Sbde struct tty *tp; 16959406Sbde struct termios *t; 16969406Sbde{ 16979406Sbde int bits; 16989406Sbde int cflag; 16999406Sbde struct com_s *com; 17009406Sbde u_char cor_change; 17019406Sbde int idivisor; 17029406Sbde int iflag; 17039406Sbde cy_addr iobase; 17049406Sbde int iprescaler; 17059406Sbde int itimeout; 17069406Sbde int odivisor; 17079406Sbde int oprescaler; 17089406Sbde u_char opt; 17099406Sbde int s; 17109406Sbde int unit; 17116261Sjkh 17129406Sbde /* do historical conversions */ 17139406Sbde if (t->c_ispeed == 0) 17149406Sbde t->c_ispeed = t->c_ospeed; 17156261Sjkh 17169406Sbde /* check requested parameters */ 17179406Sbde idivisor = comspeed(t->c_ispeed, &iprescaler); 17189406Sbde if (idivisor < 0) 17199406Sbde return (EINVAL); 17209406Sbde odivisor = comspeed(t->c_ospeed, &oprescaler); 17219406Sbde if (odivisor < 0) 17229406Sbde return (EINVAL); 17236261Sjkh 17249406Sbde /* parameters are OK, convert them to the com struct and the device */ 17259406Sbde unit = DEV_TO_UNIT(tp->t_dev); 17269406Sbde com = com_addr(unit); 17279406Sbde iobase = com->iobase; 17289406Sbde s = spltty(); 17299406Sbde cd_outb(iobase, CD1400_CAR, unit & CD1400_CAR_CHAN); 17309406Sbde if (odivisor == 0) 17319406Sbde (void)commctl(com, TIOCM_DTR, DMBIC); /* hang up line */ 17329406Sbde else 17339406Sbde (void)commctl(com, TIOCM_DTR, DMBIS); 17346261Sjkh 17359406Sbde cd_outb(iobase, CD1400_RBPR, idivisor); 17369406Sbde cd_outb(iobase, CD1400_RCOR, iprescaler); 17379406Sbde cd_outb(iobase, CD1400_TBPR, odivisor); 17389406Sbde cd_outb(iobase, CD1400_TCOR, oprescaler); 17396261Sjkh 17406261Sjkh /* 17416261Sjkh * channel control 17426261Sjkh * receiver enable 17436261Sjkh * transmitter enable (always set) 17446261Sjkh */ 17459406Sbde cflag = t->c_cflag; 17469406Sbde opt = CD1400_CCR_CMDCHANCTL | CD1400_CCR_XMTEN 17479406Sbde | (cflag & CREAD ? CD1400_CCR_RCVEN : CD1400_CCR_RCVDIS); 17489406Sbde if (opt != com->channel_control) { 17499406Sbde com->channel_control = opt; 17509406Sbde cd1400_channel_cmd(iobase, opt); 17516261Sjkh } 17526261Sjkh 17536261Sjkh#ifdef Smarts 17546261Sjkh /* set special chars */ 17559406Sbde /* XXX if one is _POSIX_VDISABLE, can't use some others */ 17569406Sbde if (t->c_cc[VSTOP] != _POSIX_VDISABLE) 17579406Sbde cd_outb(iobase, CD1400_SCHR1, t->c_cc[VSTOP]); 17589406Sbde if (t->c_cc[VSTART] != _POSIX_VDISABLE) 17599406Sbde cd_outb(iobase, CD1400_SCHR2, t->c_cc[VSTART]); 17609406Sbde if (t->c_cc[VINTR] != _POSIX_VDISABLE) 17619406Sbde cd_outb(iobase, CD1400_SCHR3, t->c_cc[VINTR]); 17629406Sbde if (t->c_cc[VSUSP] != _POSIX_VDISABLE) 17639406Sbde cd_outb(iobase, CD1400_SCHR4, t->c_cc[VSUSP]); 17646261Sjkh#endif 17656261Sjkh 17666261Sjkh /* 17676261Sjkh * set channel option register 1 - 17686261Sjkh * parity mode 17696261Sjkh * stop bits 17706261Sjkh * char length 17716261Sjkh */ 17726261Sjkh opt = 0; 17736261Sjkh /* parity */ 17746261Sjkh if (cflag & PARENB) { 17759406Sbde if (cflag & PARODD) 17769406Sbde opt |= CD1400_COR1_PARODD; 17779406Sbde opt |= CD1400_COR1_PARNORMAL; 17786261Sjkh } 17799406Sbde iflag = t->c_iflag; 17806261Sjkh if (!(iflag & INPCK)) 17819406Sbde opt |= CD1400_COR1_NOINPCK; 17829406Sbde bits = 1 + 1; 17836261Sjkh /* stop bits */ 17849406Sbde if (cflag & CSTOPB) { 17859406Sbde ++bits; 17869406Sbde opt |= CD1400_COR1_STOP2; 17879406Sbde } 17886261Sjkh /* char length */ 17899406Sbde switch (cflag & CSIZE) { 17909406Sbde case CS5: 17919406Sbde bits += 5; 17929406Sbde opt |= CD1400_COR1_CS5; 17939406Sbde break; 17949406Sbde case CS6: 17959406Sbde bits += 6; 17969406Sbde opt |= CD1400_COR1_CS6; 17979406Sbde break; 17989406Sbde case CS7: 17999406Sbde bits += 7; 18009406Sbde opt |= CD1400_COR1_CS7; 18019406Sbde break; 18029406Sbde default: 18039406Sbde bits += 8; 18049406Sbde opt |= CD1400_COR1_CS8; 18059406Sbde break; 18066261Sjkh } 18079406Sbde cor_change = 0; 18089406Sbde if (opt != com->cor[0]) { 18099406Sbde cor_change |= CD1400_CCR_COR1; 18109406Sbde cd_outb(iobase, CD1400_COR1, com->cor[0] = opt); 18119406Sbde } 18126261Sjkh 18136261Sjkh /* 18149406Sbde * Set receive time-out period, normally to max(one char time, 5 ms). 18159406Sbde */ 18169406Sbde if (t->c_ispeed == 0) 18179406Sbde itimeout = cd_inb(iobase, CD1400_RTPR); 18189406Sbde else { 18199406Sbde itimeout = (1000 * bits + t->c_ispeed - 1) / t->c_ispeed; 18209406Sbde#ifdef SOFT_HOTCHAR 18219406Sbde#define MIN_RTP 1 18229406Sbde#else 18239406Sbde#define MIN_RTP 5 18249406Sbde#endif 18259406Sbde if (itimeout < MIN_RTP) 18269406Sbde itimeout = MIN_RTP; 18279406Sbde } 18289406Sbde if (!(t->c_lflag & ICANON) && t->c_cc[VMIN] != 0 && t->c_cc[VTIME] != 0 18299406Sbde && t->c_cc[VTIME] * 10 > itimeout) 18309406Sbde itimeout = t->c_cc[VTIME] * 10; 18319406Sbde if (itimeout > 255) 18329406Sbde itimeout = 255; 18339406Sbde cd_outb(iobase, CD1400_RTPR, itimeout); 18349406Sbde 18359406Sbde /* 18366261Sjkh * set channel option register 2 - 18376261Sjkh * flow control 18386261Sjkh */ 18396261Sjkh opt = 0; 18406261Sjkh#ifdef Smarts 18416261Sjkh if (iflag & IXANY) 18429406Sbde opt |= CD1400_COR2_IXANY; 18436261Sjkh if (iflag & IXOFF) 18449406Sbde opt |= CD1400_COR2_IXOFF; 18456261Sjkh#endif 18469406Sbde#ifndef SOFT_CTS_OFLOW 18476261Sjkh if (cflag & CCTS_OFLOW) 18489406Sbde opt |= CD1400_COR2_CCTS_OFLOW; 18496261Sjkh#endif 18509406Sbde if (opt != com->cor[1]) { 18519406Sbde cor_change |= CD1400_CCR_COR2; 18529406Sbde cd_outb(iobase, CD1400_COR2, com->cor[1] = opt); 18536261Sjkh } 18546261Sjkh 18556261Sjkh /* 18566261Sjkh * set channel option register 3 - 18576261Sjkh * receiver FIFO interrupt threshold 18586261Sjkh * flow control 18596261Sjkh */ 18609406Sbde opt = RxFifoThreshold; 18616261Sjkh#ifdef Smarts 18626261Sjkh if (t->c_lflag & ICANON) 18639406Sbde opt |= CD1400_COR3_SCD34; /* detect INTR & SUSP chars */ 18646261Sjkh if (iflag & IXOFF) 18659406Sbde /* detect and transparently handle START and STOP chars */ 18669406Sbde opt |= CD1400_COR3_FCT | CD1400_COR3_SCD12; 18676261Sjkh#endif 18689406Sbde if (opt != com->cor[2]) { 18699406Sbde cor_change |= CD1400_CCR_COR3; 18709406Sbde cd_outb(iobase, CD1400_COR3, com->cor[2] = opt); 18716261Sjkh } 18726261Sjkh 18736261Sjkh /* notify the CD1400 if COR1-3 have changed */ 18749406Sbde if (cor_change) 18759406Sbde cd1400_channel_cmd(iobase, CD1400_CCR_CMDCORCHG | cor_change); 18766261Sjkh 18776261Sjkh /* 18786261Sjkh * set channel option register 4 - 18796261Sjkh * CR/NL processing 18806261Sjkh * break processing 18816261Sjkh * received exception processing 18826261Sjkh */ 18836261Sjkh opt = 0; 18846261Sjkh if (iflag & IGNCR) 18859406Sbde opt |= CD1400_COR4_IGNCR; 18866261Sjkh#ifdef Smarts 18876261Sjkh /* 18886261Sjkh * we need a new ttyinput() for this, as we don't want to 18896261Sjkh * have ICRNL && INLCR being done in both layers, or to have 18906261Sjkh * synchronisation problems 18916261Sjkh */ 18926261Sjkh if (iflag & ICRNL) 18939406Sbde opt |= CD1400_COR4_ICRNL; 18946261Sjkh if (iflag & INLCR) 18959406Sbde opt |= CD1400_COR4_INLCR; 18966261Sjkh#endif 18976261Sjkh if (iflag & IGNBRK) 18989406Sbde opt |= CD1400_COR4_IGNBRK; 18996261Sjkh if (!(iflag & BRKINT)) 19009406Sbde opt |= CD1400_COR4_NOBRKINT; 19019406Sbde#if 0 19029406Sbde /* XXX using this "intelligence" breaks reporting of overruns. */ 19036261Sjkh if (iflag & IGNPAR) 19049406Sbde opt |= CD1400_COR4_PFO_DISCARD; 19056261Sjkh else { 19069406Sbde if (iflag & PARMRK) 19079406Sbde opt |= CD1400_COR4_PFO_ESC; 19089406Sbde else 19099406Sbde opt |= CD1400_COR4_PFO_NUL; 19109406Sbde } 19116261Sjkh#else 19129406Sbde opt |= CD1400_COR4_PFO_EXCEPTION; 19136261Sjkh#endif 19149406Sbde cd_outb(iobase, CD1400_COR4, opt); 19156261Sjkh 19166261Sjkh /* 19176261Sjkh * set channel option register 5 - 19186261Sjkh */ 19196261Sjkh opt = 0; 19206261Sjkh if (iflag & ISTRIP) 19219406Sbde opt |= CD1400_COR5_ISTRIP; 19229406Sbde if (t->c_iflag & IEXTEN) 19239406Sbde /* enable LNEXT (e.g. ctrl-v quoting) handling */ 19249406Sbde opt |= CD1400_COR5_LNEXT; 19256261Sjkh#ifdef Smarts 19266261Sjkh if (t->c_oflag & ONLCR) 19279406Sbde opt |= CD1400_COR5_ONLCR; 19286261Sjkh if (t->c_oflag & OCRNL) 19299406Sbde opt |= CD1400_COR5_OCRNL; 19306261Sjkh#endif 19319406Sbde cd_outb(iobase, CD1400_COR5, opt); 19326261Sjkh 19336261Sjkh /* 19349406Sbde * XXX we probably alway want to track carrier changes, so that 19359406Sbde * TS_CARR_ON gives the true carrier. If we don't track them, 19369406Sbde * then we should set TS_CARR_ON when CLOCAL drops. 19379406Sbde */ 19389406Sbde /* 19396261Sjkh * set modem change option register 1 19406261Sjkh * generate modem interrupts on which 1 -> 0 input transitions 19416261Sjkh * also controls auto-DTR output flow-control, which we don't use 19426261Sjkh */ 19439406Sbde opt = cflag & CLOCAL ? 0 : CD1400_MCOR1_CDzd; 19449406Sbde#ifdef SOFT_CTS_OFLOW 19459406Sbde if (cflag & CCTS_OFLOW) 19469406Sbde opt |= CD1400_MCOR1_CTSzd; 19479406Sbde#endif 19489406Sbde cd_outb(iobase, CD1400_MCOR1, opt); 19496261Sjkh 19506261Sjkh /* 19516261Sjkh * set modem change option register 2 19526261Sjkh * generate modem interrupts on specific 0 -> 1 input transitions 19536261Sjkh */ 19549406Sbde opt = cflag & CLOCAL ? 0 : CD1400_MCOR2_CDod; 19559406Sbde#ifdef SOFT_CTS_OFLOW 19569406Sbde if (cflag & CCTS_OFLOW) 19579406Sbde opt |= CD1400_MCOR2_CTSod; 19589406Sbde#endif 19599406Sbde cd_outb(iobase, CD1400_MCOR2, opt); 19606261Sjkh 19619406Sbde /* 19629406Sbde * XXX should have done this long ago, but there is too much state 19639406Sbde * to change all atomically. 19649406Sbde */ 19659406Sbde disable_intr(); 19666261Sjkh 19679406Sbde com->state &= ~CS_TTGO; 19689406Sbde if (!(tp->t_state & TS_TTSTOP)) 19699406Sbde com->state |= CS_TTGO; 19709406Sbde if (cflag & CRTS_IFLOW) 19719406Sbde com->state |= CS_RTS_IFLOW; /* XXX - secondary changes? */ 19729406Sbde else 19739406Sbde com->state &= ~CS_RTS_IFLOW; 19746261Sjkh 19759406Sbde /* 19769406Sbde * Set up state to handle output flow control. 19779406Sbde * XXX - worth handling MDMBUF (DCD) flow control at the lowest level? 19789406Sbde * Now has 10+ msec latency, while CTS flow has 50- usec latency. 19799406Sbde */ 19809406Sbde com->state |= CS_ODEVREADY; 19819406Sbde#ifdef SOFT_CTS_OFLOW 19829406Sbde com->state &= ~CS_CTS_OFLOW; 19839406Sbde if (cflag & CCTS_OFLOW) { 19849406Sbde com->state |= CS_CTS_OFLOW; 19859406Sbde if (!(com->last_modem_status & MSR_CTS)) 19869406Sbde com->state &= ~CS_ODEVREADY; 19879406Sbde } 19889406Sbde#endif 19899406Sbde /* XXX shouldn't call functions while intrs are disabled. */ 19909406Sbde disc_optim(tp, t, com); 19919406Sbde#if 0 19929406Sbde /* 19939406Sbde * Recover from fiddling with CS_TTGO. We used to call siointr1() 19949406Sbde * unconditionally, but that defeated the careful discarding of 19959406Sbde * stale input in sioopen(). 19969406Sbde */ 19979406Sbde if (com->state >= (CS_BUSY | CS_TTGO)) 19989406Sbde siointr1(com); 19999406Sbde#endif 20009406Sbde if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) { 20019406Sbde if (!(com->intr_enable & CD1400_SRER_TXRDY)) 20029406Sbde cd_outb(iobase, CD1400_SRER, 20039406Sbde com->intr_enable |= CD1400_SRER_TXRDY); 20049406Sbde } else { 20059406Sbde if (com->intr_enable & CD1400_SRER_TXRDY) 20069406Sbde cd_outb(iobase, CD1400_SRER, 20079406Sbde com->intr_enable &= ~CD1400_SRER_TXRDY); 20089406Sbde } 20096261Sjkh 20109406Sbde enable_intr(); 20119406Sbde splx(s); 20129406Sbde return (0); 20139406Sbde} 20149406Sbde 20159406Sbdestatic void 20169406Sbdecomstart(tp) 20179406Sbde struct tty *tp; 20186261Sjkh{ 20199406Sbde struct com_s *com; 20209406Sbde cy_addr iobase; 20216261Sjkh int s; 20229406Sbde#ifdef CyDebug 20239406Sbde bool_t started; 20249406Sbde#endif 20259406Sbde int unit; 20266261Sjkh 20279406Sbde unit = DEV_TO_UNIT(tp->t_dev); 20289406Sbde com = com_addr(unit); 20299406Sbde iobase = com->iobase; 20309406Sbde s = spltty(); 20319406Sbde 20326261Sjkh#ifdef CyDebug 20339406Sbde ++com->start_count; 20349406Sbde started = FALSE; 20356261Sjkh#endif 20366261Sjkh 20379406Sbde disable_intr(); 20389406Sbde cd_outb(iobase, CD1400_CAR, unit & CD1400_CAR_CHAN); 20399406Sbde if (tp->t_state & TS_TTSTOP) { 20409406Sbde com->state &= ~CS_TTGO; 20419406Sbde if (com->intr_enable & CD1400_SRER_TXRDY) 20429406Sbde cd_outb(iobase, CD1400_SRER, 20439406Sbde com->intr_enable &= ~CD1400_SRER_TXRDY); 20449406Sbde } else { 20459406Sbde com->state |= CS_TTGO; 20469406Sbde if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY) 20479406Sbde && !(com->intr_enable & CD1400_SRER_TXRDY)) 20489406Sbde cd_outb(iobase, CD1400_SRER, 20499406Sbde com->intr_enable |= CD1400_SRER_TXRDY); 20509406Sbde } 20519406Sbde if (tp->t_state & TS_TBLOCK) { 20529406Sbde if (com->mcr_image & MCR_RTS && com->state & CS_RTS_IFLOW) 20539406Sbde#if 0 20549406Sbde outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); 20559406Sbde#else 20569406Sbde cd_outb(iobase, CD1400_MSVR1, 20579406Sbde com->mcr_image &= ~MCR_RTS); 20589406Sbde#endif 20599406Sbde } else { 20609406Sbde /* 20619406Sbde * XXX don't raise MCR_RTS if CTS_RTS_IFLOW is off. Set it 20629406Sbde * appropriately in comparam() if RTS-flow is being changed. 20639406Sbde * Check for races. 20649406Sbde */ 20659406Sbde if (!(com->mcr_image & MCR_RTS) && com->iptr < com->ihighwater) 20669406Sbde#if 0 20679406Sbde outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); 20689406Sbde#else 20699406Sbde cd_outb(iobase, CD1400_MSVR1, 20709406Sbde com->mcr_image |= MCR_RTS); 20719406Sbde#endif 20729406Sbde } 20739406Sbde enable_intr(); 20749406Sbde if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) { 20759406Sbde splx(s); 20766261Sjkh return; 20779406Sbde } 20789406Sbde if (tp->t_outq.c_cc != 0) { 20799406Sbde struct lbq *qp; 20809406Sbde struct lbq *next; 20816261Sjkh 20829406Sbde if (!com->obufs[0].l_queued) { 20839406Sbde#ifdef CyDebug 20849406Sbde started = TRUE; 20859406Sbde#endif 20869406Sbde com->obufs[0].l_tail 20879406Sbde = com->obuf1 + q_to_b(&tp->t_outq, com->obuf1, 20889406Sbde sizeof com->obuf1); 20899406Sbde com->obufs[0].l_next = NULL; 20909406Sbde com->obufs[0].l_queued = TRUE; 20919406Sbde disable_intr(); 20929406Sbde if (com->state & CS_BUSY) { 20939406Sbde qp = com->obufq.l_next; 20949406Sbde while ((next = qp->l_next) != NULL) 20959406Sbde qp = next; 20969406Sbde qp->l_next = &com->obufs[0]; 20979406Sbde } else { 20989406Sbde com->obufq.l_head = com->obufs[0].l_head; 20999406Sbde com->obufq.l_tail = com->obufs[0].l_tail; 21009406Sbde com->obufq.l_next = &com->obufs[0]; 21019406Sbde com->state |= CS_BUSY; 21029406Sbde if (com->state >= (CS_BUSY | CS_TTGO 21039406Sbde | CS_ODEVREADY)) 21049406Sbde cd_outb(iobase, CD1400_SRER, 21059406Sbde com->intr_enable 21069406Sbde |= CD1400_SRER_TXRDY); 21079406Sbde } 21089406Sbde enable_intr(); 21099406Sbde } 21109406Sbde if (tp->t_outq.c_cc != 0 && !com->obufs[1].l_queued) { 21119406Sbde#ifdef CyDebug 21129406Sbde started = TRUE; 21139406Sbde#endif 21149406Sbde com->obufs[1].l_tail 21159406Sbde = com->obuf2 + q_to_b(&tp->t_outq, com->obuf2, 21169406Sbde sizeof com->obuf2); 21179406Sbde com->obufs[1].l_next = NULL; 21189406Sbde com->obufs[1].l_queued = TRUE; 21199406Sbde disable_intr(); 21209406Sbde if (com->state & CS_BUSY) { 21219406Sbde qp = com->obufq.l_next; 21229406Sbde while ((next = qp->l_next) != NULL) 21239406Sbde qp = next; 21249406Sbde qp->l_next = &com->obufs[1]; 21259406Sbde } else { 21269406Sbde com->obufq.l_head = com->obufs[1].l_head; 21279406Sbde com->obufq.l_tail = com->obufs[1].l_tail; 21289406Sbde com->obufq.l_next = &com->obufs[1]; 21299406Sbde com->state |= CS_BUSY; 21309406Sbde if (com->state >= (CS_BUSY | CS_TTGO 21319406Sbde | CS_ODEVREADY)) 21329406Sbde cd_outb(iobase, CD1400_SRER, 21339406Sbde com->intr_enable 21349406Sbde |= CD1400_SRER_TXRDY); 21359406Sbde } 21369406Sbde enable_intr(); 21379406Sbde } 21389406Sbde tp->t_state |= TS_BUSY; 21399406Sbde } 21409406Sbde#ifdef CyDebug 21419406Sbde if (started) 21429406Sbde ++com->start_real; 21439406Sbde#endif 21449406Sbde#if 0 21459406Sbde disable_intr(); 21469406Sbde if (com->state >= (CS_BUSY | CS_TTGO)) { 21479406Sbde siointr1(com); /* fake interrupt to start output */ 21489406Sbde enable_intr(); 21499406Sbde#endif 21509406Sbde 21519406Sbde#if 0 /* XXX TK2.0 */ 21529406Sbde if (tp->t_state & (TS_SO_OCOMPLETE | TS_SO_OLOWAT) || tp->t_wsel) 21539406Sbde ttwwakeup(tp); 21549406Sbde#else 21556261Sjkh if (tp->t_outq.c_cc <= tp->t_lowat) { 21569406Sbde if (tp->t_state & TS_ASLEEP) { 21576261Sjkh tp->t_state &= ~TS_ASLEEP; 21589406Sbde wakeup(TSA_OLOWAT(tp)); 21596261Sjkh } 21606261Sjkh selwakeup(&tp->t_wsel); 21616261Sjkh } 21626261Sjkh#endif 21636261Sjkh 21649406Sbde splx(s); 21659406Sbde} 21666261Sjkh 21679406Sbdevoid 21689406Sbdesiostop(tp, rw) 21699406Sbde struct tty *tp; 21709406Sbde int rw; 21719406Sbde{ 21729406Sbde struct com_s *com; 21739406Sbde cy_addr iobase; 21749406Sbde int unit; 21756261Sjkh 21769406Sbde unit = DEV_TO_UNIT(tp->t_dev); 21779406Sbde com = com_addr(unit); 21789406Sbde iobase = com->iobase; 21799406Sbde disable_intr(); 21809406Sbde if (rw & FWRITE) { 21819406Sbde com->obufs[0].l_queued = FALSE; 21829406Sbde com->obufs[1].l_queued = FALSE; 21839406Sbde if (com->state & CS_ODONE) 21849406Sbde com_events -= LOTS_OF_EVENTS; 21859406Sbde com->state &= ~(CS_ODONE | CS_BUSY); 21869406Sbde com->tp->t_state &= ~TS_BUSY; 21879406Sbde } 21889406Sbde if (rw & FREAD) { 21899406Sbde com_events -= (com->iptr - com->ibuf); 21909406Sbde com->iptr = com->ibuf; 21919406Sbde } 21929406Sbde if (tp->t_state & TS_TTSTOP) { 21939406Sbde com->state &= ~CS_TTGO; 21949406Sbde if (com->intr_enable & CD1400_SRER_TXRDY) { 21959406Sbde cd_outb(iobase, CD1400_CAR, unit & CD1400_CAR_CHAN); 21969406Sbde cd_outb(iobase, CD1400_SRER, 21979406Sbde com->intr_enable &= ~CD1400_SRER_TXRDY); 21989406Sbde } 21999406Sbde } else { 22009406Sbde com->state |= CS_TTGO; 22019406Sbde if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY) 22029406Sbde && !(com->intr_enable & CD1400_SRER_TXRDY)) { 22039406Sbde cd_outb(iobase, CD1400_CAR, unit & CD1400_CAR_CHAN); 22049406Sbde cd_outb(iobase, CD1400_SRER, 22059406Sbde com->intr_enable |= CD1400_SRER_TXRDY); 22069406Sbde } 22079406Sbde } 22089406Sbde enable_intr(); 22096261Sjkh 22109406Sbde /* XXX should clear h/w fifos too. */ 22119406Sbde} 22129406Sbde 22139406Sbdestruct tty * 22149406Sbdesiodevtotty(dev) 22159406Sbde dev_t dev; 22169406Sbde{ 22179406Sbde int mynor; 22189406Sbde int unit; 22199406Sbde 22209406Sbde mynor = minor(dev); 22219406Sbde if (mynor & CONTROL_MASK) 22229406Sbde return (NULL); 22239406Sbde unit = MINOR_TO_UNIT(mynor); 22249406Sbde if ((u_int) unit >= NSIO) 22259406Sbde return (NULL); 22269406Sbde return (&sio_tty[unit]); 22279406Sbde} 22289406Sbde 22299406Sbdestatic int 22309406Sbdecommctl(com, bits, how) 22319406Sbde struct com_s *com; 22329406Sbde int bits; 22339406Sbde int how; 22349406Sbde{ 22359406Sbde cy_addr iobase; 22369406Sbde int mcr; 22379406Sbde int msr; 22389406Sbde 22399406Sbde if (how == DMGET) { 22409406Sbde if (com->channel_control & CD1400_CCR_RCVEN) 22419406Sbde bits |= TIOCM_LE; 22429406Sbde mcr = com->mcr_image; 22439406Sbde if (mcr & MCR_DTR) 22449406Sbde bits |= TIOCM_DTR; 22459406Sbde if (mcr & MCR_RTS) 22469406Sbde /* XXX wired on for Cyclom-8Ys */ 22479406Sbde bits |= TIOCM_RTS; 22489406Sbde msr = com->prev_modem_status; 22499406Sbde if (msr & MSR_CTS) 22509406Sbde bits |= TIOCM_CTS; 22519406Sbde if (msr & MSR_DCD) 22529406Sbde bits |= TIOCM_CD; 22539406Sbde if (msr & MSR_DSR) 22549406Sbde bits |= TIOCM_DSR; 22559406Sbde if (msr & MSR_RI) 22569406Sbde /* XXX not connected except for Cyclom-16Y? */ 22579406Sbde bits |= TIOCM_RI; 22589406Sbde return (bits); 22596261Sjkh } 22609406Sbde iobase = com->iobase; 22619406Sbde mcr = 0; 22629406Sbde if (bits & TIOCM_DTR) 22639406Sbde mcr |= MCR_DTR; 22649406Sbde if (bits & TIOCM_RTS) 22659406Sbde mcr |= MCR_RTS; 22669406Sbde disable_intr(); 22679406Sbde switch (how) { 22689406Sbde case DMSET: 22699406Sbde com->mcr_image = mcr; 22709406Sbde cd_outb(iobase, CD1400_MSVR1, mcr); 22719406Sbde cd_outb(iobase, CD1400_MSVR2, mcr); 22729406Sbde break; 22739406Sbde case DMBIS: 22749406Sbde com->mcr_image = mcr = com->mcr_image | mcr; 22759406Sbde cd_outb(iobase, CD1400_MSVR1, mcr); 22769406Sbde cd_outb(iobase, CD1400_MSVR2, mcr); 22779406Sbde break; 22789406Sbde case DMBIC: 22799406Sbde com->mcr_image = mcr = com->mcr_image & ~mcr; 22809406Sbde cd_outb(iobase, CD1400_MSVR1, mcr); 22819406Sbde cd_outb(iobase, CD1400_MSVR2, mcr); 22829406Sbde break; 22839406Sbde } 22849406Sbde enable_intr(); 22859406Sbde return (0); 22869406Sbde} 22876261Sjkh 22889406Sbdestatic void 22899406Sbdesiosettimeout() 22909406Sbde{ 22919406Sbde struct com_s *com; 22929406Sbde bool_t someopen; 22939406Sbde int unit; 22946261Sjkh 22959406Sbde /* 22969406Sbde * Set our timeout period to 1 second if no polled devices are open. 22979406Sbde * Otherwise set it to max(1/200, 1/hz). 22989406Sbde * Enable timeouts iff some device is open. 22999406Sbde */ 23009406Sbde untimeout(comwakeup, (void *)NULL); 23019406Sbde sio_timeout = hz; 23029406Sbde someopen = FALSE; 23039406Sbde for (unit = 0; unit < NSIO; ++unit) { 23049406Sbde com = com_addr(unit); 23059406Sbde if (com != NULL && com->tp != NULL 23069406Sbde && com->tp->t_state & TS_ISOPEN) { 23079406Sbde someopen = TRUE; 23089406Sbde#if 0 23099406Sbde if (com->poll || com->poll_output) { 23109406Sbde sio_timeout = hz > 200 ? hz / 200 : 1; 23119406Sbde break; 23129406Sbde } 23139406Sbde#endif 23149406Sbde } 23159406Sbde } 23169406Sbde if (someopen) { 23179406Sbde sio_timeouts_until_log = hz / sio_timeout; 23189406Sbde timeout(comwakeup, (void *)NULL, sio_timeout); 23199406Sbde } else { 23209406Sbde /* Flush error messages, if any. */ 23219406Sbde sio_timeouts_until_log = 1; 23229406Sbde comwakeup((void *)NULL); 23239406Sbde untimeout(comwakeup, (void *)NULL); 23249406Sbde } 23259406Sbde} 23266261Sjkh 23279406Sbdestatic void 23289406Sbdecomwakeup(chan) 23299406Sbde void *chan; 23306261Sjkh{ 23319406Sbde struct com_s *com; 23329406Sbde int unit; 23336261Sjkh 23349406Sbde timeout(comwakeup, (void *)NULL, sio_timeout); 23356261Sjkh 23369406Sbde#if 0 23379406Sbde /* 23389406Sbde * Recover from lost output interrupts. 23399406Sbde * Poll any lines that don't use interrupts. 23409406Sbde */ 23419406Sbde for (unit = 0; unit < NSIO; ++unit) { 23429406Sbde com = com_addr(unit); 23439406Sbde if (com != NULL 23449406Sbde && (com->state >= (CS_BUSY | CS_TTGO) || com->poll)) { 23459406Sbde disable_intr(); 23469406Sbde siointr1(com); 23479406Sbde enable_intr(); 23489406Sbde } 23499406Sbde } 23509406Sbde#endif 23516261Sjkh 23529406Sbde /* 23539406Sbde * Check for and log errors, but not too often. 23549406Sbde */ 23559406Sbde if (--sio_timeouts_until_log > 0) 23569406Sbde return; 23579406Sbde sio_timeouts_until_log = hz / sio_timeout; 23589406Sbde for (unit = 0; unit < NSIO; ++unit) { 23599406Sbde int errnum; 23606261Sjkh 23619406Sbde com = com_addr(unit); 23629406Sbde if (com == NULL) 23639406Sbde continue; 23649406Sbde for (errnum = 0; errnum < CE_NTYPES; ++errnum) { 23659406Sbde u_int delta; 23669406Sbde u_long total; 23676261Sjkh 23689406Sbde disable_intr(); 23699406Sbde delta = com->delta_error_counts[errnum]; 23709406Sbde com->delta_error_counts[errnum] = 0; 23719406Sbde enable_intr(); 23729406Sbde if (delta == 0) 23739406Sbde continue; 23749406Sbde total = com->error_counts[errnum] += delta; 23759406Sbde log(LOG_ERR, "cy%d: %u more %s%s (total %lu)\n", 23769406Sbde unit, delta, error_desc[errnum], 23779406Sbde delta == 1 ? "" : "s", total); 23789406Sbde#if 0 23799406Sbde /* 23809406Sbde * XXX if we resurrect this then we should move 23819406Sbde * the dropping of the ftl to somewhere with less 23829406Sbde * latency. 23839406Sbde */ 23849406Sbde if (errnum == CE_OVERRUN && com->hasfifo 23859406Sbde && com->ftl > FIFO_TRIGGER_1) { 23869406Sbde static u_char ftl_in_bytes[] = 23879406Sbde { 1, 4, 8, 14, }; 23889406Sbde 23899406Sbde com->ftl_init = FIFO_TRIGGER_8; 23909406Sbde#define FIFO_TRIGGER_DELTA FIFO_TRIGGER_4 23919406Sbde com->ftl_max = 23929406Sbde com->ftl -= FIFO_TRIGGER_DELTA; 23939406Sbde outb(com->iobase + com_fifo, 23949406Sbde FIFO_ENABLE | com->ftl); 23959406Sbde log(LOG_DEBUG, 23969406Sbde "sio%d: reduced fifo trigger level to %d\n", 23979406Sbde unit, 23989406Sbde ftl_in_bytes[com->ftl 23999406Sbde / FIFO_TRIGGER_DELTA]); 24009406Sbde } 24019406Sbde#endif 24029406Sbde } 24039406Sbde } 24046261Sjkh} 24056261Sjkh 24069406Sbdestatic void 24079406Sbdedisc_optim(tp, t, com) 24089406Sbde struct tty *tp; 24099406Sbde struct termios *t; 24109406Sbde struct com_s *com; 24116712Spst{ 24129406Sbde#ifndef SOFT_HOTCHAR 24139406Sbde cy_addr iobase; 24149406Sbde u_char opt; 24159406Sbde#endif 24166261Sjkh 24179406Sbde /* 24189406Sbde * XXX can skip a lot more cases if Smarts. Maybe 24199406Sbde * (IGNCR | ISTRIP | IXOFF | IXON) in c_iflag. But perhaps we 24209406Sbde * shouldn't skip if (TS_CNTTB | TS_LNCH) is set in t_state. 24219406Sbde */ 24229406Sbde if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP 24239406Sbde | IXOFF | IXON)) 24249406Sbde && (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK)) 24259406Sbde && (!(t->c_iflag & PARMRK) || 24269406Sbde (t->c_iflag & (IGNPAR|IGNBRK)) == (IGNPAR|IGNBRK)) 24279406Sbde && !(t->c_lflag & (ECHO | ECHONL | ICANON | IEXTEN | ISIG 24289406Sbde | PENDIN)) 24299406Sbde && linesw[tp->t_line].l_rint == ttyinput) 24309406Sbde tp->t_state |= TS_CAN_BYPASS_L_RINT; 24319406Sbde else 24329406Sbde tp->t_state &= ~TS_CAN_BYPASS_L_RINT; 24339406Sbde /* 24349406Sbde * Prepare to reduce input latency for packet 24359406Sbde * discplines with a end of packet character. 24369406Sbde */ 24379406Sbde if (tp->t_line == SLIPDISC) 24389406Sbde com->hotchar = 0xc0; 24399406Sbde else if (tp->t_line == PPPDISC) 24409406Sbde com->hotchar = 0x7e; 24419406Sbde else 24429406Sbde com->hotchar = 0; 24439406Sbde#ifndef SOFT_HOTCHAR 24449406Sbde iobase = com->iobase; 24459406Sbde cd_outb(iobase, CD1400_CAR, com->unit & CD1400_CAR_CHAN); 24469406Sbde opt = com->cor[2] & ~CD1400_COR3_SCD34; 24479406Sbde if (com->hotchar != 0) { 24489406Sbde cd_outb(iobase, CD1400_SCHR3, com->hotchar); 24499406Sbde cd_outb(iobase, CD1400_SCHR4, com->hotchar); 24509406Sbde opt |= CD1400_COR3_SCD34; 24519406Sbde } 24529406Sbde if (opt != com->cor[2]) { 24539406Sbde cd_outb(iobase, CD1400_COR3, com->cor[2] = opt); 24549406Sbde cd1400_channel_cmd(com->iobase, 24559406Sbde CD1400_CCR_CMDCORCHG | CD1400_CCR_COR3); 24569406Sbde } 24579406Sbde#endif 24586712Spst} 24596712Spst 24609406Sbde#ifdef Smarts 24619406Sbde/* standard line discipline input routine */ 24626261Sjkhint 24639406Sbdecyinput(c, tp) 24649406Sbde int c; 24659406Sbde struct tty *tp; 24666261Sjkh{ 24679406Sbde /* XXX duplicate ttyinput(), but without the IXOFF/IXON/ISTRIP/IPARMRK 24689406Sbde * bits, as they are done by the CD1400. Hardly worth the effort, 24699406Sbde * given that high-throughput sessions are raw anyhow. 24709406Sbde */ 24719406Sbde} 24729406Sbde#endif /* Smarts */ 24736261Sjkh 24749406Sbdestatic int 24759406Sbdecomspeed(speed, prescaler_io) 24769406Sbde speed_t speed; 24779406Sbde int *prescaler_io; 24789406Sbde{ 24799406Sbde int actual; 24809406Sbde int error; 24819406Sbde int divider; 24829406Sbde int prescaler; 24839406Sbde int prescaler_unit; 24846261Sjkh 24859406Sbde if (speed == 0) 24869406Sbde return (0); 24879406Sbde if (speed < 0 || speed > 150000) 24889406Sbde return (-1); 24896261Sjkh 24909406Sbde /* determine which prescaler to use */ 24919406Sbde for (prescaler_unit = 4, prescaler = 2048; prescaler_unit; 24929406Sbde prescaler_unit--, prescaler >>= 2) { 24939406Sbde if (CY_CLOCK / prescaler / speed > 63) 24949406Sbde break; 24959406Sbde } 24966261Sjkh 24979406Sbde divider = (CY_CLOCK / prescaler * 2 / speed + 1) / 2; /* round off */ 24989406Sbde if (divider > 255) 24999406Sbde divider = 255; 25009406Sbde actual = CY_CLOCK/prescaler/divider; 25019406Sbde error = ((actual - speed) * 2000 / speed + 1) / 2; /* percentage */ 25026261Sjkh 25039406Sbde /* 3.0% max error tolerance */ 25049406Sbde if (error < -30 || error > 30) 25059406Sbde return (-1); 25066261Sjkh 25076261Sjkh#if 0 25089406Sbde printf("prescaler = %d (%d)\n", prescaler, prescaler_unit); 25099406Sbde printf("divider = %d (%x)\n", divider, divider); 25109406Sbde printf("actual = %d\n", actual); 25119406Sbde printf("error = %d\n", error); 25126261Sjkh#endif 25136261Sjkh 25149406Sbde *prescaler_io = prescaler_unit; 25159406Sbde return (divider); 25169406Sbde} 25176261Sjkh 25186261Sjkhstatic void 25199406Sbdecd1400_channel_cmd(iobase, cmd) 25209406Sbde cy_addr iobase; 25219406Sbde int cmd; 25226261Sjkh{ 25239406Sbde /* XXX hsu@clinet.fi: This is always more dependent on ISA bus speed, 25246261Sjkh as the card is probed every round? Replaced delaycount with 8k. 25256261Sjkh Either delaycount has to be implemented in FreeBSD or more sensible 25266261Sjkh way of doing these should be implemented. DELAY isn't enough here. 25276261Sjkh */ 25289406Sbde u_int maxwait = 5 * 8 * 1024; /* approx. 5 ms */ 25296261Sjkh 25306261Sjkh /* wait for processing of previous command to complete */ 25319406Sbde while (cd_inb(iobase, CD1400_CCR) && maxwait--) 25329406Sbde ; 25338876Srgrimes 25346261Sjkh if (!maxwait) 25356261Sjkh log(LOG_ERR, "cy: channel command timeout (%d loops) - arrgh\n", 25366261Sjkh 5 * 8 * 1024); 25376261Sjkh 25389406Sbde cd_outb(iobase, CD1400_CCR, cmd); 25399406Sbde} 25406261Sjkh 25416261Sjkh#ifdef CyDebug 25426261Sjkh/* useful in ddb */ 25436261Sjkhvoid 25449406Sbdecystatus(unit) 25459406Sbde int unit; 25466261Sjkh{ 25479406Sbde struct com_s *com; 25489406Sbde cy_addr iobase; 25499406Sbde u_int ocount; 25509406Sbde struct tty *tp; 25516261Sjkh 25529406Sbde com = com_addr(unit); 25536261Sjkh printf("info for channel %d\n", unit); 25546261Sjkh printf("------------------\n"); 25559406Sbde printf("total cyclom service probes:\t%d\n", cy_svrr_probes); 25569406Sbde printf("calls to upper layer:\t\t%d\n", cy_timeouts); 25579406Sbde if (com == NULL) 25589406Sbde return; 25599406Sbde iobase = com->iobase; 25606261Sjkh printf("\n"); 25619406Sbde printf("cd1400 base address:\\tt%p\n", iobase); 25629406Sbde cd_outb(iobase, CD1400_CAR, unit & CD1400_CAR_CHAN); 25639406Sbde printf("saved channel_control:\t\t0x%02x\n", com->channel_control); 25649406Sbde printf("saved cor1-3:\t\t\t0x%02x 0x%02x 0x%02x\n", 25659406Sbde com->cor[0], com->cor[1], com->cor[2]); 25669406Sbde printf("service request enable reg:\t0x%02x (0x%02x cached)\n", 25679406Sbde cd_inb(iobase, CD1400_SRER), com->intr_enable); 25689406Sbde printf("service request register:\t0x%02x\n", 25699406Sbde cd_inb(iobase, CD1400_SVRR)); 25709406Sbde printf("modem status:\t\t\t0x%02x (0x%02x cached)\n", 25719406Sbde cd_inb(iobase, CD1400_MSVR2), com->prev_modem_status); 25729406Sbde printf("rx/tx/mdm interrupt registers:\t0x%02x 0x%02x 0x%02x\n", 25739406Sbde cd_inb(iobase, CD1400_RIR), cd_inb(iobase, CD1400_TIR), 25749406Sbde cd_inb(iobase, CD1400_MIR)); 25756261Sjkh printf("\n"); 25769406Sbde printf("com state:\t\t\t0x%02x\n", com->state); 25779406Sbde printf("calls to comstart():\t\t%d (%d useful)\n", 25789406Sbde com->start_count, com->start_real); 25799406Sbde printf("rx buffer chars free:\t\t%d\n", com->iptr - com->ibuf); 25809406Sbde ocount = 0; 25819406Sbde if (com->obufs[0].l_queued) 25829406Sbde ocount += com->obufs[0].l_tail - com->obufs[0].l_head; 25839406Sbde if (com->obufs[1].l_queued) 25849406Sbde ocount += com->obufs[1].l_tail - com->obufs[1].l_head; 25859406Sbde printf("tx buffer chars:\t\t%u\n", ocount); 25869406Sbde printf("received chars:\t\t\t%d\n", com->bytes_in); 25879406Sbde printf("received exceptions:\t\t%d\n", com->recv_exception); 25889406Sbde printf("modem signal deltas:\t\t%d\n", com->mdm); 25899406Sbde printf("transmitted chars:\t\t%d\n", com->bytes_out); 25909406Sbde printf("\n"); 25919406Sbde tp = com->tp; 25929406Sbde if (tp != NULL) { 25939406Sbde printf("tty state:\t\t\t0x%08x\n", tp->t_state); 25946261Sjkh printf("upper layer queue lengths:\t%d raw, %d canon, %d output\n", 25959406Sbde tp->t_rawq.c_cc, tp->t_canq.c_cc, tp->t_outq.c_cc); 25969406Sbde } else 25976261Sjkh printf("tty state:\t\t\tclosed\n"); 25989406Sbde} 25999406Sbde#endif /* CyDebug */ 26006261Sjkh 26016261Sjkh#endif /* NCY > 0 */ 2602