cy_isa.c revision 123104
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 */ 306261Sjkh 31115703Sobrien#include <sys/cdefs.h> 32115703Sobrien__FBSDID("$FreeBSD: head/sys/dev/cy/cy_isa.c 123104 2003-12-02 12:36:00Z bde $"); 33115703Sobrien 3431778Seivind#include "opt_compat.h" 359406Sbde#include "cy.h" 3629047Sbde 376261Sjkh/* 389406Sbde * TODO: 399406Sbde * Atomic COR change. 409406Sbde * Consoles. 416261Sjkh */ 426261Sjkh 436261Sjkh/* 449406Sbde * Temporary compile-time configuration options. 456261Sjkh */ 469406Sbde#define RxFifoThreshold (CD1400_RX_FIFO_SIZE / 2) 479406Sbde /* Number of chars in the receiver FIFO before an 489406Sbde * an interrupt is generated. Should depend on 499406Sbde * line speed. Needs to be about 6 on a 486DX33 509406Sbde * for 4 active ports at 115200 bps. Why doesn't 519406Sbde * 10 work? 529406Sbde */ 539406Sbde#define PollMode /* Use polling-based irq service routine, not the 549406Sbde * hardware svcack lines. Must be defined for 559406Sbde * Cyclom-16Y boards. Less efficient for Cyclom-8Ys, 569406Sbde * and stops 4 * 115200 bps from working. 579406Sbde */ 589406Sbde#undef Smarts /* Enable slightly more CD1400 intelligence. Mainly 599406Sbde * the output CR/LF processing, plus we can avoid a 609406Sbde * few checks usually done in ttyinput(). 619406Sbde * 629406Sbde * XXX not fully implemented, and not particularly 639406Sbde * worthwhile. 649406Sbde */ 659406Sbde#undef CyDebug /* Include debugging code (not very expensive). */ 666261Sjkh 679406Sbde/* These will go away. */ 689406Sbde#undef SOFT_CTS_OFLOW 699406Sbde#define SOFT_HOTCHAR 706261Sjkh 716261Sjkh#include <sys/param.h> 726261Sjkh#include <sys/systm.h> 7376166Smarkm#include <sys/bus.h> 746261Sjkh#include <sys/conf.h> 7524131Sbde#include <sys/fcntl.h> 7638246Sbde#include <sys/interrupt.h> 776261Sjkh#include <sys/kernel.h> 7876166Smarkm#include <sys/lock.h> 799406Sbde#include <sys/malloc.h> 8071846Sbde#include <sys/mutex.h> 816261Sjkh#include <sys/syslog.h> 8276166Smarkm#include <sys/tty.h> 8376166Smarkm 8447585Sbde#include <machine/psl.h> 856261Sjkh 866261Sjkh#include <i386/isa/isa_device.h> 879406Sbde#include <i386/isa/cyreg.h> 886261Sjkh#include <i386/isa/ic/cd1400.h> 896261Sjkh 9061011Speter#ifndef COMPAT_OLDISA 9161011Speter#error "The cy device requires the old isa compatibility shims" 9261011Speter#endif 9361011Speter 949406Sbde/* 959406Sbde * Dictionary so that I can name everything *sio* or *com* to compare with 969406Sbde * sio.c. There is also lots of ugly formatting and unnecessary ifdefs to 979406Sbde * simplify the comparision. These will go away. 989406Sbde */ 999406Sbde#define LSR_BI CD1400_RDSR_BREAK 1009406Sbde#define LSR_FE CD1400_RDSR_FE 1019406Sbde#define LSR_OE CD1400_RDSR_OE 1029406Sbde#define LSR_PE CD1400_RDSR_PE 1039406Sbde#define MCR_DTR CD1400_MSVR2_DTR 1049406Sbde#define MCR_RTS CD1400_MSVR1_RTS 1059406Sbde#define MSR_CTS CD1400_MSVR2_CTS 1069406Sbde#define MSR_DCD CD1400_MSVR2_CD 1079406Sbde#define MSR_DSR CD1400_MSVR2_DSR 1089406Sbde#define MSR_RI CD1400_MSVR2_RI 1099406Sbde#define NSIO (NCY * CY_MAX_PORTS) 1109406Sbde#define comconsole cyconsole 1119406Sbde#define comdefaultrate cydefaultrate 1129406Sbde#define com_events cy_events 1139406Sbde#define comhardclose cyhardclose 1149406Sbde#define commctl cymctl 1159406Sbde#define comparam cyparam 1169406Sbde#define comspeed cyspeed 1179406Sbde#define comstart cystart 1189406Sbde#define comwakeup cywakeup 1199406Sbde#define p_com_addr p_cy_addr 1209406Sbde#define sioattach cyattach 1219406Sbde#define sioclose cyclose 1229406Sbde#define siodriver cydriver 1239406Sbde#define siodtrwakeup cydtrwakeup 12443611Sbde#define sioinput cyinput 125123104Sbde#define siointr1 cyintr 12638445Sbde#define sioioctl cyioctl 1279406Sbde#define sioopen cyopen 1289406Sbde#define siopoll cypoll 1299406Sbde#define sioprobe cyprobe 1309406Sbde#define siosettimeout cysettimeout 13143611Sbde#define siosetwater cysetwater 13251654Sphk#define comstop cystop 1339406Sbde#define siowrite cywrite 134122799Sbde#define sio_fast_ih cy_fast_ih 135122771Sbde#define sio_inited cy_inited 13667551Sjhb#define sio_irec cy_irec 137122771Sbde#define sio_lock cy_lock 138122799Sbde#define sio_slow_ih cy_slow_ih 1399406Sbde#define sio_timeout cy_timeout 14038445Sbde#define sio_timeout_handle cy_timeout_handle 1419406Sbde#define sio_timeouts_until_log cy_timeouts_until_log 1426261Sjkh 1439406Sbde#define CY_MAX_PORTS (CD1400_NO_OF_CHANNELS * CY_MAX_CD1400s) 1446261Sjkh 1459406Sbde/* We encode the cyclom unit number (cyu) in spare bits in the IVR's. */ 1469406Sbde#define CD1400_xIVR_CHAN_SHIFT 3 14729047Sbde#define CD1400_xIVR_CHAN 0x1F 1486261Sjkh 14941903Sbde/* 15041903Sbde * ETC states. com->etc may also contain a hardware ETC command value, 15141903Sbde * meaning that execution of that command is pending. 15241903Sbde */ 15341903Sbde#define ETC_NONE 0 /* we depend on bzero() setting this */ 15441903Sbde#define ETC_BREAK_STARTING 1 15541903Sbde#define ETC_BREAK_STARTED 2 15641903Sbde#define ETC_BREAK_ENDING 3 15741903Sbde#define ETC_BREAK_ENDED 4 15841903Sbde 1599406Sbde#define LOTS_OF_EVENTS 64 /* helps separate urgent events from input */ 1606261Sjkh 1619406Sbde#define CALLOUT_MASK 0x80 1629406Sbde#define CONTROL_MASK 0x60 1639406Sbde#define CONTROL_INIT_STATE 0x20 1649406Sbde#define CONTROL_LOCK_STATE 0x40 1659406Sbde#define DEV_TO_UNIT(dev) (MINOR_TO_UNIT(minor(dev))) 1669406Sbde#define MINOR_MAGIC_MASK (CALLOUT_MASK | CONTROL_MASK) 16781580Sbde/* 16881580Sbde * Not all of the magic is parametrized in the following macros. 16 and 16981580Sbde * 0xff are related to the bitfields in a udev_t. CY_MAX_PORTS must be 17081580Sbde * ((0xff & ~MINOR_MAGIC_MASK) + 1) for things to work. 17181580Sbde */ 17229047Sbde#define MINOR_TO_UNIT(mynor) (((mynor) >> 16) * CY_MAX_PORTS \ 17329047Sbde | (((mynor) & 0xff) & ~MINOR_MAGIC_MASK)) 17481580Sbde#define UNIT_TO_MINOR(unit) (((unit) / CY_MAX_PORTS) << 16 \ 17581580Sbde | (((unit) & 0xff) & ~MINOR_MAGIC_MASK)) 1766261Sjkh 1779406Sbde/* 1789406Sbde * com state bits. 1799406Sbde * (CS_BUSY | CS_TTGO) and (CS_BUSY | CS_TTGO | CS_ODEVREADY) must be higher 1809406Sbde * than the other bits so that they can be tested as a group without masking 1819406Sbde * off the low bits. 1829406Sbde * 1839406Sbde * The following com and tty flags correspond closely: 1849406Sbde * CS_BUSY = TS_BUSY (maintained by comstart(), siopoll() and 18551654Sphk * comstop()) 1869754Sbde * CS_TTGO = ~TS_TTSTOP (maintained by comparam() and comstart()) 1879406Sbde * CS_CTS_OFLOW = CCTS_OFLOW (maintained by comparam()) 1889406Sbde * CS_RTS_IFLOW = CRTS_IFLOW (maintained by comparam()) 1899406Sbde * TS_FLUSH is not used. 1909406Sbde * XXX I think TIOCSETA doesn't clear TS_TTSTOP when it clears IXON. 1919406Sbde * XXX CS_*FLOW should be CF_*FLOW in com->flags (control flags not state). 1929406Sbde */ 1939406Sbde#define CS_BUSY 0x80 /* output in progress */ 1949406Sbde#define CS_TTGO 0x40 /* output not stopped by XOFF */ 1959406Sbde#define CS_ODEVREADY 0x20 /* external device h/w ready (CTS) */ 1969406Sbde#define CS_CHECKMSR 1 /* check of MSR scheduled */ 1979406Sbde#define CS_CTS_OFLOW 2 /* use CTS output flow control */ 1989406Sbde#define CS_DTR_OFF 0x10 /* DTR held off */ 1999406Sbde#define CS_ODONE 4 /* output completed */ 2009406Sbde#define CS_RTS_IFLOW 8 /* use RTS input flow control */ 20141388Sbde#define CSE_ODONE 1 /* output transmitted */ 2026261Sjkh 2039406Sbdestatic char const * const error_desc[] = { 2049406Sbde#define CE_OVERRUN 0 2059406Sbde "silo overflow", 2069406Sbde#define CE_INTERRUPT_BUF_OVERFLOW 1 2079406Sbde "interrupt-level buffer overflow", 2089406Sbde#define CE_TTY_BUF_OVERFLOW 2 2099406Sbde "tty-level buffer overflow", 2109406Sbde}; 2116261Sjkh 2129406Sbde#define CE_NTYPES 3 2139406Sbde#define CE_RECORD(com, errnum) (++(com)->delta_error_counts[errnum]) 2146261Sjkh 215122771Sbde#ifdef SMP 216122771Sbde#define COM_LOCK() mtx_lock_spin(&sio_lock) 217122771Sbde#define COM_UNLOCK() mtx_unlock_spin(&sio_lock) 218122771Sbde#else 219122771Sbde#define COM_LOCK() 220122771Sbde#define COM_UNLOCK() 221122771Sbde#endif 222122771Sbde 2239406Sbde/* types. XXX - should be elsewhere */ 2249406Sbdetypedef u_char bool_t; /* boolean */ 2259406Sbdetypedef u_char volatile *cy_addr; 2269406Sbde 2279406Sbde/* queue of linear buffers */ 2289406Sbdestruct lbq { 2299406Sbde u_char *l_head; /* next char to process */ 2309406Sbde u_char *l_tail; /* one past the last char to process */ 2319406Sbde struct lbq *l_next; /* next in queue */ 2329406Sbde bool_t l_queued; /* nonzero if queued */ 2339406Sbde}; 2349406Sbde 2359406Sbde/* com device structure */ 2369406Sbdestruct com_s { 2379406Sbde u_char state; /* miscellaneous flag bits */ 2389406Sbde bool_t active_out; /* nonzero if the callout device is open */ 2396261Sjkh#if 0 2409406Sbde u_char cfcr_image; /* copy of value written to CFCR */ 24141388Sbde#endif 24241903Sbde u_char etc; /* pending Embedded Transmit Command */ 24341388Sbde u_char extra_state; /* more flag bits, separate for order trick */ 24441388Sbde#if 0 24512962Sbde u_char fifo_image; /* copy of value written to FIFO */ 24638303Sbde#endif 24738303Sbde u_char gfrcr_image; /* copy of value read from GFRCR */ 24838303Sbde#if 0 2499406Sbde bool_t hasfifo; /* nonzero for 16550 UARTs */ 2509406Sbde bool_t loses_outints; /* nonzero if device loses output interrupts */ 2516261Sjkh#endif 25238303Sbde u_char mcr_dtr; /* MCR bit that is wired to DTR */ 2539406Sbde u_char mcr_image; /* copy of value written to MCR */ 25438303Sbde u_char mcr_rts; /* MCR bit that is wired to RTS */ 2559406Sbde#if 0 2569406Sbde#ifdef COM_MULTIPORT 2579406Sbde bool_t multiport; /* is this unit part of a multiport device? */ 2589406Sbde#endif /* COM_MULTIPORT */ 2599406Sbde bool_t no_irq; /* nonzero if irq is not attached */ 2609406Sbde bool_t poll; /* nonzero if polling is required */ 2619406Sbde bool_t poll_output; /* nonzero if polling for output is required */ 2629406Sbde#endif 2639406Sbde int unit; /* unit number */ 2649406Sbde int dtr_wait; /* time to hold DTR down on close (* 1/hz) */ 2659406Sbde#if 0 2669406Sbde u_int tx_fifo_size; 2679406Sbde#endif 2689406Sbde u_int wopeners; /* # processes waiting for DCD in open() */ 2696261Sjkh 2709406Sbde /* 2719406Sbde * The high level of the driver never reads status registers directly 2729406Sbde * because there would be too many side effects to handle conveniently. 2739406Sbde * Instead, it reads copies of the registers stored here by the 2749406Sbde * interrupt handler. 2759406Sbde */ 2769406Sbde u_char last_modem_status; /* last MSR read by intr handler */ 2779406Sbde u_char prev_modem_status; /* last MSR handled by high level */ 2786261Sjkh 2799406Sbde u_char hotchar; /* ldisc-specific char to be handled ASAP */ 2809406Sbde u_char *ibuf; /* start of input buffer */ 2819406Sbde u_char *ibufend; /* end of input buffer */ 28243611Sbde u_char *ibufold; /* old input buffer, to be freed */ 2839406Sbde u_char *ihighwater; /* threshold in input buffer */ 2849406Sbde u_char *iptr; /* next free spot in input buffer */ 28543611Sbde int ibufsize; /* size of ibuf (not include error bytes) */ 28643611Sbde int ierroff; /* offset of error bytes in ibuf */ 2876261Sjkh 2889406Sbde struct lbq obufq; /* head of queue of output buffers */ 2899406Sbde struct lbq obufs[2]; /* output buffers */ 2906261Sjkh 29129047Sbde int cy_align; /* index for register alignment */ 2929406Sbde cy_addr cy_iobase; /* base address of this port's cyclom */ 2939406Sbde cy_addr iobase; /* base address of this port's cd1400 */ 29438303Sbde int mcr_rts_reg; /* cd1400 reg number of reg holding mcr_rts */ 2956261Sjkh 2969406Sbde struct tty *tp; /* cross reference */ 2979406Sbde 2989406Sbde /* Initial state. */ 2999406Sbde struct termios it_in; /* should be in struct tty */ 3009406Sbde struct termios it_out; 3019406Sbde 3029406Sbde /* Lock state. */ 3039406Sbde struct termios lt_in; /* should be in struct tty */ 3049406Sbde struct termios lt_out; 3059406Sbde 3069406Sbde bool_t do_timestamp; 30717354Sbde bool_t do_dcd_timestamp; 3089406Sbde struct timeval timestamp; 30917354Sbde struct timeval dcd_timestamp; 3109406Sbde 3119406Sbde u_long bytes_in; /* statistics */ 3129406Sbde u_long bytes_out; 3139406Sbde u_int delta_error_counts[CE_NTYPES]; 3149406Sbde u_long error_counts[CE_NTYPES]; 3159406Sbde 3169406Sbde u_int recv_exception; /* exception chars received */ 3179406Sbde u_int mdm; /* modem signal changes */ 3189406Sbde#ifdef CyDebug 3199406Sbde u_int start_count; /* no. of calls to comstart() */ 3209406Sbde u_int start_real; /* no. of calls that did something */ 3216261Sjkh#endif 32241293Sbde u_char car; /* CD1400 CAR shadow (if first unit in cd) */ 3239406Sbde u_char channel_control;/* CD1400 CCR control command shadow */ 3249406Sbde u_char cor[3]; /* CD1400 COR1-3 shadows */ 3259406Sbde u_char intr_enable; /* CD1400 SRER shadow */ 3266261Sjkh 3279406Sbde /* 3289406Sbde * Data area for output buffers. Someday we should build the output 3299406Sbde * buffer queue without copying data. 3309406Sbde */ 3319406Sbde u_char obuf1[256]; 3329406Sbde u_char obuf2[256]; 3339406Sbde}; 3349406Sbde 335123104Sbde/* PCI driver entry points. */ 336123104Sbdevoid *cyattach_common(cy_addr cy_iobase, int cy_align); 337123104Sbdedriver_intr_t siointr1; 33829047Sbde 339123104Sbdestatic ointhand2_t cyointr; 34093024Sbdestatic int cy_units(cy_addr cy_iobase, int cy_align); 34193024Sbdestatic int sioattach(struct isa_device *dev); 34292765Salfredstatic void cd1400_channel_cmd(struct com_s *com, int cmd); 34392765Salfredstatic void cd1400_channel_cmd_wait(struct com_s *com); 34493024Sbdestatic void cd_etc(struct com_s *com, int etc); 34593024Sbdestatic int cd_getreg(struct com_s *com, int reg); 34693024Sbdestatic void cd_setreg(struct com_s *com, int reg, int val); 3479406Sbdestatic timeout_t siodtrwakeup; 34893024Sbdestatic void comhardclose(struct com_s *com); 34993024Sbdestatic void sioinput(struct com_s *com); 35093024Sbdestatic int commctl(struct com_s *com, int bits, int how); 35193024Sbdestatic int comparam(struct tty *tp, struct termios *t); 35293024Sbdestatic void siopoll(void *arg); 35393024Sbdestatic int sioprobe(struct isa_device *dev); 35493024Sbdestatic void siosettimeout(void); 35593024Sbdestatic int siosetwater(struct com_s *com, speed_t speed); 35693024Sbdestatic int comspeed(speed_t speed, u_long cy_clock, int *prescaler_io); 35793024Sbdestatic void comstart(struct tty *tp); 35893024Sbdestatic void comstop(struct tty *tp, int rw); 3599406Sbdestatic timeout_t comwakeup; 36093024Sbdestatic void disc_optim(struct tty *tp, struct termios *t, 36193024Sbde struct com_s *com); 3629406Sbde 3636261Sjkh#ifdef CyDebug 36493024Sbdevoid cystatus(int unit); 3656261Sjkh#endif 3669406Sbde 36772262Sjhbstatic char driver_name[] = "cy"; 368122771Sbdestatic struct mtx sio_lock; 369122771Sbdestatic int sio_inited; 37012962Sbde 3719406Sbde/* table and macro for fast conversion from a unit number to its com struct */ 3729406Sbdestatic struct com_s *p_com_addr[NSIO]; 3739406Sbde#define com_addr(unit) (p_com_addr[unit]) 3749406Sbde 37512962Sbdestruct isa_driver siodriver = { 37665557Sjasone INTR_TYPE_TTY | INTR_FAST, 37761011Speter sioprobe, 37861011Speter sioattach, 37961011Speter driver_name 38012962Sbde}; 38161011SpeterCOMPAT_ISA_DRIVER(cy, cydriver); /* XXX */ 38212675Sjulian 38312962Sbdestatic d_open_t sioopen; 38412962Sbdestatic d_close_t sioclose; 38512962Sbdestatic d_write_t siowrite; 38612962Sbdestatic d_ioctl_t sioioctl; 38712962Sbde 38838485Sbde#define CDEV_MAJOR 48 38947625Sphkstatic struct cdevsw sio_cdevsw = { 390111815Sphk .d_open = sioopen, 391111815Sphk .d_close = sioclose, 392111815Sphk .d_read = ttyread, 393111815Sphk .d_write = siowrite, 394111815Sphk .d_ioctl = sioioctl, 395111815Sphk .d_poll = ttypoll, 396111815Sphk .d_name = driver_name, 397111815Sphk .d_maj = CDEV_MAJOR, 398111821Sphk .d_flags = D_TTY, 399111815Sphk .d_kqfilter = ttykqfilter, 4009406Sbde}; 4019406Sbde 40211671Sbdestatic int comconsole = -1; 4039406Sbdestatic speed_t comdefaultrate = TTYDEF_SPEED; 4049406Sbdestatic u_int com_events; /* input chars + weighted output completions */ 405122799Sbdestatic void *sio_fast_ih; 406122799Sbdestatic void *sio_slow_ih; 4079406Sbdestatic int sio_timeout; 4089406Sbdestatic int sio_timeouts_until_log; 40929677Sgibbsstatic struct callout_handle sio_timeout_handle 41029677Sgibbs = CALLOUT_HANDLE_INITIALIZER(&sio_timeout_handle); 4116261Sjkh 4126261Sjkh#ifdef CyDebug 4139406Sbdestatic u_int cd_inbs; 4149406Sbdestatic u_int cy_inbs; 4159406Sbdestatic u_int cd_outbs; 4169406Sbdestatic u_int cy_outbs; 4179406Sbdestatic u_int cy_svrr_probes; 4189406Sbdestatic u_int cy_timeouts; 4196261Sjkh#endif 4206261Sjkh 42129047Sbdestatic int cy_chip_offset[] = { 42231104Sbde 0x0000, 0x0400, 0x0800, 0x0c00, 0x0200, 0x0600, 0x0a00, 0x0e00, 42329047Sbde}; 4249406Sbdestatic int cy_nr_cd1400s[NCY]; 42518901Sdgstatic int cy_total_devices; 4269406Sbde#undef RxFifoThreshold 4279406Sbdestatic int volatile RxFifoThreshold = (CD1400_RX_FIFO_SIZE / 2); 4286261Sjkh 4299406Sbdestatic int 4309406Sbdesioprobe(dev) 4319406Sbde struct isa_device *dev; 4329406Sbde{ 4339406Sbde cy_addr iobase; 4346261Sjkh 435122771Sbde while (sio_inited != 2) 436122771Sbde if (atomic_cmpset_int(&sio_inited, 0, 1)) { 437122771Sbde mtx_init(&sio_lock, driver_name, NULL, MTX_SPIN); 438122771Sbde atomic_store_rel_int(&sio_inited, 2); 439122771Sbde } 440122771Sbde 44111424Sdg iobase = (cy_addr)dev->id_maddr; 4426261Sjkh 4439406Sbde /* Cyclom-16Y hardware reset (Cyclom-8Ys don't care) */ 44441309Sbde cy_inb(iobase, CY16_RESET, 0); /* XXX? */ 4459406Sbde DELAY(500); /* wait for the board to get its act together */ 4466261Sjkh 44711424Sdg /* this is needed to get the board out of reset */ 44841309Sbde cy_outb(iobase, CY_CLEAR_INTR, 0, 0); 44911424Sdg DELAY(500); 45011424Sdg 45118901Sdg return (cy_units(iobase, 0) == 0 ? 0 : -1); 45218901Sdg} 45318901Sdg 45418901Sdgstatic int 45518901Sdgcy_units(cy_iobase, cy_align) 45629047Sbde cy_addr cy_iobase; 45729047Sbde int cy_align; 45818901Sdg{ 45929047Sbde int cyu; 46029047Sbde u_char firmware_version; 46129047Sbde int i; 46229047Sbde cy_addr iobase; 46318901Sdg 46418679Sdg for (cyu = 0; cyu < CY_MAX_CD1400s; ++cyu) { 46529047Sbde iobase = cy_iobase + (cy_chip_offset[cyu] << cy_align); 4666261Sjkh 4679406Sbde /* wait for chip to become ready for new command */ 46811424Sdg for (i = 0; i < 10; i++) { 4699406Sbde DELAY(50); 47018901Sdg if (!cd_inb(iobase, CD1400_CCR, cy_align)) 4719406Sbde break; 4729406Sbde } 4736261Sjkh 4749406Sbde /* clear the GFRCR register */ 47518901Sdg cd_outb(iobase, CD1400_GFRCR, cy_align, 0); 4766261Sjkh 4779406Sbde /* issue a reset command */ 47818901Sdg cd_outb(iobase, CD1400_CCR, cy_align, 4799406Sbde CD1400_CCR_CMDRESET | CD1400_CCR_FULLRESET); 4806261Sjkh 48191314Sbde /* XXX bogus initialization to avoid a gcc bug/warning. */ 48291314Sbde firmware_version = 0; 48391314Sbde 4849406Sbde /* wait for the CD1400 to initialize itself */ 48511424Sdg for (i = 0; i < 200; i++) { 4869406Sbde DELAY(50); 4879406Sbde 4889406Sbde /* retrieve firmware version */ 48929047Sbde firmware_version = cd_inb(iobase, CD1400_GFRCR, 49029047Sbde cy_align); 49111424Sdg if ((firmware_version & 0xf0) == 0x40) 4929406Sbde break; 4939406Sbde } 4949406Sbde 4959406Sbde /* 4969406Sbde * Anything in the 0x40-0x4F range is fine. 4979406Sbde * If one CD1400 is bad then we don't support higher 4989406Sbde * numbered good ones on this board. 4999406Sbde */ 50011424Sdg if ((firmware_version & 0xf0) != 0x40) 5019406Sbde break; 5026261Sjkh } 50318901Sdg return (cyu); 5046261Sjkh} 5056261Sjkh 5069406Sbdestatic int 5079406Sbdesioattach(isdp) 50829047Sbde struct isa_device *isdp; 5096261Sjkh{ 510123104Sbde int adapter; 511123104Sbde struct com_s *com; 5126261Sjkh 513123104Sbde com = cyattach_common((cy_addr) isdp->id_maddr, 0); 514123104Sbde if (com == NULL) 5159406Sbde return (0); 516123104Sbde adapter = com->unit / CY_MAX_PORTS; 51729047Sbde 51818901Sdg /* 51918901Sdg * XXX 52018901Sdg * This kludge is to allow ISA/PCI device specifications in the 52118901Sdg * kernel config file to be in any order. 52218901Sdg */ 52318901Sdg if (isdp->id_unit != adapter) { 52418901Sdg printf("cy%d: attached as cy%d\n", isdp->id_unit, adapter); 52529047Sbde isdp->id_unit = adapter; /* XXX */ 52618901Sdg } 527123104Sbde 528123104Sbde isdp->id_ointr = cyointr; 52918901Sdg return (1); 53018901Sdg} 5316261Sjkh 532123104Sbdevoid * 53318901Sdgcyattach_common(cy_iobase, cy_align) 53429047Sbde cy_addr cy_iobase; 53529047Sbde int cy_align; 53618901Sdg{ 53729047Sbde int adapter; 53829047Sbde int cyu; 53938303Sbde u_char firmware_version; 54029047Sbde cy_addr iobase; 54181580Sbde int minorbase; 54229047Sbde int ncyu; 54329047Sbde int unit; 54418901Sdg 54518901Sdg adapter = cy_total_devices; 54618901Sdg if ((u_int)adapter >= NCY) { 54729047Sbde printf( 54829047Sbde "cy%d: can't attach adapter: insufficient cy devices configured\n", 54929047Sbde adapter); 550123104Sbde return (NULL); 55118901Sdg } 55218901Sdg ncyu = cy_units(cy_iobase, cy_align); 55318901Sdg if (ncyu == 0) 554123104Sbde return (NULL); 55518901Sdg cy_nr_cd1400s[adapter] = ncyu; 55618901Sdg cy_total_devices++; 55718901Sdg 55818901Sdg unit = adapter * CY_MAX_PORTS; 55918679Sdg for (cyu = 0; cyu < ncyu; ++cyu) { 5609406Sbde int cdu; 5616261Sjkh 56229047Sbde iobase = (cy_addr) (cy_iobase 56329047Sbde + (cy_chip_offset[cyu] << cy_align)); 56438303Sbde firmware_version = cd_inb(iobase, CD1400_GFRCR, cy_align); 56529047Sbde 5669406Sbde /* Set up a receive timeout period of than 1+ ms. */ 56738303Sbde cd_outb(iobase, CD1400_PPR, cy_align, 56838303Sbde howmany(CY_CLOCK(firmware_version) 56938303Sbde / CD1400_PPR_PRESCALER, 1000)); 57038303Sbde 5719406Sbde for (cdu = 0; cdu < CD1400_NO_OF_CHANNELS; ++cdu, ++unit) { 5729406Sbde struct com_s *com; 5739406Sbde int s; 5746261Sjkh 57569781Sdwmalone com = malloc(sizeof *com, M_DEVBUF, M_NOWAIT | M_ZERO); 57629047Sbde if (com == NULL) 57729047Sbde break; 57829047Sbde com->unit = unit; 57938303Sbde com->gfrcr_image = firmware_version; 58038303Sbde if (CY_RTS_DTR_SWAPPED(firmware_version)) { 58138303Sbde com->mcr_dtr = MCR_RTS; 58238303Sbde com->mcr_rts = MCR_DTR; 58338303Sbde com->mcr_rts_reg = CD1400_MSVR2; 58438303Sbde } else { 58538303Sbde com->mcr_dtr = MCR_DTR; 58638303Sbde com->mcr_rts = MCR_RTS; 58738303Sbde com->mcr_rts_reg = CD1400_MSVR1; 58838303Sbde } 58929047Sbde com->dtr_wait = 3 * hz; 59029047Sbde com->obufs[0].l_head = com->obuf1; 59129047Sbde com->obufs[1].l_head = com->obuf2; 5926261Sjkh 59329047Sbde com->cy_align = cy_align; 5949406Sbde com->cy_iobase = cy_iobase; 59529047Sbde com->iobase = iobase; 59641293Sbde com->car = ~CD1400_CAR_CHAN; 5976261Sjkh 59829047Sbde /* 59929047Sbde * We don't use all the flags from <sys/ttydefaults.h> since they 60029047Sbde * are only relevant for logins. It's important to have echo off 60129047Sbde * initially so that the line doesn't start blathering before the 60229047Sbde * echo flag can be turned off. 60329047Sbde */ 60429047Sbde com->it_in.c_iflag = 0; 60529047Sbde com->it_in.c_oflag = 0; 60629047Sbde com->it_in.c_cflag = TTYDEF_CFLAG; 60729047Sbde com->it_in.c_lflag = 0; 60829047Sbde if (unit == comconsole) { 60929047Sbde com->it_in.c_iflag = TTYDEF_IFLAG; 61029047Sbde com->it_in.c_oflag = TTYDEF_OFLAG; 61129047Sbde com->it_in.c_cflag = TTYDEF_CFLAG | CLOCAL; 61229047Sbde com->it_in.c_lflag = TTYDEF_LFLAG; 61329047Sbde com->lt_out.c_cflag = com->lt_in.c_cflag = CLOCAL; 61429047Sbde } 61543611Sbde if (siosetwater(com, com->it_in.c_ispeed) != 0) { 61643611Sbde free(com, M_DEVBUF); 617123104Sbde return (NULL); 61843611Sbde } 61929047Sbde termioschars(&com->it_in); 62029047Sbde com->it_in.c_ispeed = com->it_in.c_ospeed = comdefaultrate; 62129047Sbde com->it_out = com->it_in; 6226261Sjkh 62329047Sbde s = spltty(); 62429047Sbde com_addr(unit) = com; 62529047Sbde splx(s); 62612962Sbde 627122799Sbde if (sio_fast_ih == NULL) { 62872242Sjhb swi_add(&tty_ithd, "tty:cy", siopoll, NULL, SWI_TTY, 0, 629122799Sbde &sio_fast_ih); 630122799Sbde swi_add(&clk_ithd, "tty:cy", siopoll, NULL, SWI_TTY, 0, 631122799Sbde &sio_slow_ih); 63238445Sbde } 63381580Sbde minorbase = UNIT_TO_MINOR(unit); 63481580Sbde make_dev(&sio_cdevsw, minorbase, 63537683Sbde UID_ROOT, GID_WHEEL, 0600, "ttyc%r%r", adapter, 63629047Sbde unit % CY_MAX_PORTS); 63781580Sbde make_dev(&sio_cdevsw, minorbase | CONTROL_INIT_STATE, 63837683Sbde UID_ROOT, GID_WHEEL, 0600, "ttyic%r%r", adapter, 63929047Sbde unit % CY_MAX_PORTS); 64081580Sbde make_dev(&sio_cdevsw, minorbase | CONTROL_LOCK_STATE, 64137683Sbde UID_ROOT, GID_WHEEL, 0600, "ttylc%r%r", adapter, 64229047Sbde unit % CY_MAX_PORTS); 64381580Sbde make_dev(&sio_cdevsw, minorbase | CALLOUT_MASK, 64437683Sbde UID_UUCP, GID_DIALER, 0660, "cuac%r%r", adapter, 64529047Sbde unit % CY_MAX_PORTS); 64681580Sbde make_dev(&sio_cdevsw, minorbase | CALLOUT_MASK | CONTROL_INIT_STATE, 64737683Sbde UID_UUCP, GID_DIALER, 0660, "cuaic%r%r", adapter, 64829047Sbde unit % CY_MAX_PORTS); 64981580Sbde make_dev(&sio_cdevsw, minorbase | CALLOUT_MASK | CONTROL_LOCK_STATE, 65037683Sbde UID_UUCP, GID_DIALER, 0660, "cualc%r%r", adapter, 65129047Sbde unit % CY_MAX_PORTS); 6529406Sbde } 6536261Sjkh } 6546261Sjkh 6559406Sbde /* ensure an edge for the next interrupt */ 65641309Sbde cy_outb(cy_iobase, CY_CLEAR_INTR, cy_align, 0); 6576261Sjkh 658123104Sbde return (com_addr(adapter * CY_MAX_PORTS)); 6596261Sjkh} 6606261Sjkh 66112675Sjulianstatic int 66283366Sjuliansioopen(dev, flag, mode, td) 6639406Sbde dev_t dev; 6649406Sbde int flag; 6659406Sbde int mode; 66683366Sjulian struct thread *td; 6676261Sjkh{ 6689406Sbde struct com_s *com; 6699406Sbde int error; 6709406Sbde int mynor; 6719406Sbde int s; 6726261Sjkh struct tty *tp; 6739406Sbde int unit; 6746261Sjkh 6759406Sbde mynor = minor(dev); 6769406Sbde unit = MINOR_TO_UNIT(mynor); 6779406Sbde if ((u_int) unit >= NSIO || (com = com_addr(unit)) == NULL) 6786261Sjkh return (ENXIO); 6799406Sbde if (mynor & CONTROL_MASK) 6809406Sbde return (0); 68181576Sbde tp = dev->si_tty = com->tp = ttymalloc(com->tp); 6829406Sbde s = spltty(); 6839406Sbde /* 6849406Sbde * We jump to this label after all non-interrupted sleeps to pick 6859406Sbde * up any changes of the device state. 6869406Sbde */ 6879406Sbdeopen_top: 6889406Sbde while (com->state & CS_DTR_OFF) { 6899406Sbde error = tsleep(&com->dtr_wait, TTIPRI | PCATCH, "cydtr", 0); 6909406Sbde if (error != 0) 6919406Sbde goto out; 6929406Sbde } 6939406Sbde if (tp->t_state & TS_ISOPEN) { 6949406Sbde /* 6959406Sbde * The device is open, so everything has been initialized. 6969406Sbde * Handle conflicts. 6979406Sbde */ 6989406Sbde if (mynor & CALLOUT_MASK) { 6999406Sbde if (!com->active_out) { 7009406Sbde error = EBUSY; 7019406Sbde goto out; 7029406Sbde } 7039406Sbde } else { 7049406Sbde if (com->active_out) { 7059406Sbde if (flag & O_NONBLOCK) { 7069406Sbde error = EBUSY; 7079406Sbde goto out; 7089406Sbde } 7099406Sbde error = tsleep(&com->active_out, 7109406Sbde TTIPRI | PCATCH, "cybi", 0); 7119406Sbde if (error != 0) 7129406Sbde goto out; 7139406Sbde goto open_top; 7149406Sbde } 7156261Sjkh } 71643425Sphk if (tp->t_state & TS_XCLUDE && 71793593Sjhb suser(td)) { 7189406Sbde error = EBUSY; 7199406Sbde goto out; 7209406Sbde } 7219406Sbde } else { 7229406Sbde /* 7239406Sbde * The device isn't open, so there are no conflicts. 7249406Sbde * Initialize it. Initialization is done twice in many 7259406Sbde * cases: to preempt sleeping callin opens if we are 7269406Sbde * callout, and to complete a callin open after DCD rises. 7279406Sbde */ 7289406Sbde tp->t_oproc = comstart; 72951654Sphk tp->t_stop = comstop; 7309406Sbde tp->t_param = comparam; 7319406Sbde tp->t_dev = dev; 7329406Sbde tp->t_termios = mynor & CALLOUT_MASK 7339406Sbde ? com->it_out : com->it_in; 73442045Sbde 73542045Sbde /* Encode per-board unit in LIVR for access in intr routines. */ 73642045Sbde cd_setreg(com, CD1400_LIVR, 73742045Sbde (unit & CD1400_xIVR_CHAN) << CD1400_xIVR_CHAN_SHIFT); 73842045Sbde 73942045Sbde (void)commctl(com, TIOCM_DTR | TIOCM_RTS, DMSET); 7409406Sbde#if 0 7419406Sbde com->poll = com->no_irq; 7429406Sbde com->poll_output = com->loses_outints; 7439406Sbde#endif 7449406Sbde ++com->wopeners; 7459406Sbde error = comparam(tp, &tp->t_termios); 7469406Sbde --com->wopeners; 7479406Sbde if (error != 0) 7489406Sbde goto out; 7499406Sbde#if 0 7509406Sbde if (com->hasfifo) { 7519406Sbde /* 75242045Sbde * (Re)enable and flush fifos. 7539406Sbde * 7549406Sbde * Certain SMC chips cause problems if the fifos 7559406Sbde * are enabled while input is ready. Turn off the 7569406Sbde * fifo if necessary to clear the input. We test 7579406Sbde * the input ready bit after enabling the fifos 7589406Sbde * since we've already enabled them in comparam() 7599406Sbde * and to handle races between enabling and fresh 7609406Sbde * input. 7619406Sbde */ 7629406Sbde while (TRUE) { 7639406Sbde outb(iobase + com_fifo, 7649406Sbde FIFO_RCV_RST | FIFO_XMT_RST 76512962Sbde | com->fifo_image); 7669406Sbde DELAY(100); 7679406Sbde if (!(inb(com->line_status_port) & LSR_RXRDY)) 7689406Sbde break; 7699406Sbde outb(iobase + com_fifo, 0); 7709406Sbde DELAY(100); 7719406Sbde (void) inb(com->data_port); 7729406Sbde } 7739406Sbde } 7746261Sjkh 77588088Sjhb critical_enter(); 77672262Sjhb COM_LOCK(); 7779406Sbde (void) inb(com->line_status_port); 7789406Sbde (void) inb(com->data_port); 7799406Sbde com->prev_modem_status = com->last_modem_status 7809406Sbde = inb(com->modem_status_port); 7819406Sbde outb(iobase + com_ier, IER_ERXRDY | IER_ETXRDY | IER_ERLS 7829406Sbde | IER_EMSC); 78372262Sjhb COM_UNLOCK(); 78488088Sjhb critical_exit(); 7859406Sbde#else /* !0 */ 78642045Sbde /* 78742045Sbde * Flush fifos. This requires a full channel reset which 78842045Sbde * also disables the transmitter and receiver. Recover 78942045Sbde * from this. 79042045Sbde */ 79142045Sbde cd1400_channel_cmd(com, 79242045Sbde CD1400_CCR_CMDRESET | CD1400_CCR_CHANRESET); 79342045Sbde cd1400_channel_cmd(com, com->channel_control); 79442045Sbde 79588088Sjhb critical_enter(); 79672262Sjhb COM_LOCK(); 7979406Sbde com->prev_modem_status = com->last_modem_status 79841293Sbde = cd_getreg(com, CD1400_MSVR2); 79941293Sbde cd_setreg(com, CD1400_SRER, 80041293Sbde com->intr_enable 80141293Sbde = CD1400_SRER_MDMCH | CD1400_SRER_RXDATA); 80272262Sjhb COM_UNLOCK(); 80388088Sjhb critical_exit(); 8049406Sbde#endif /* 0 */ 8059406Sbde /* 8069406Sbde * Handle initial DCD. Callout devices get a fake initial 8079406Sbde * DCD (trapdoor DCD). If we are callout, then any sleeping 8089406Sbde * callin opens get woken up and resume sleeping on "cybi" 8099406Sbde * instead of "cydcd". 8109406Sbde */ 8119406Sbde /* 8129406Sbde * XXX `mynor & CALLOUT_MASK' should be 8139406Sbde * `tp->t_cflag & (SOFT_CARRIER | TRAPDOOR_CARRIER) where 8149406Sbde * TRAPDOOR_CARRIER is the default initial state for callout 8159406Sbde * devices and SOFT_CARRIER is like CLOCAL except it hides 8169406Sbde * the true carrier. 8179406Sbde */ 8189406Sbde if (com->prev_modem_status & MSR_DCD || mynor & CALLOUT_MASK) 8199406Sbde (*linesw[tp->t_line].l_modem)(tp, 1); 8209406Sbde } 8219406Sbde /* 8229406Sbde * Wait for DCD if necessary. 8239406Sbde */ 8249406Sbde if (!(tp->t_state & TS_CARR_ON) && !(mynor & CALLOUT_MASK) 8259406Sbde && !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK)) { 8269406Sbde ++com->wopeners; 8279406Sbde error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "cydcd", 0); 8289406Sbde --com->wopeners; 8299406Sbde if (error != 0) 8309406Sbde goto out; 8319406Sbde goto open_top; 8329406Sbde } 8339406Sbde error = (*linesw[tp->t_line].l_open)(dev, tp); 8349406Sbde disc_optim(tp, &tp->t_termios, com); 8359406Sbde if (tp->t_state & TS_ISOPEN && mynor & CALLOUT_MASK) 8369406Sbde com->active_out = TRUE; 8379406Sbde siosettimeout(); 8389406Sbdeout: 8399406Sbde splx(s); 8409406Sbde if (!(tp->t_state & TS_ISOPEN) && com->wopeners == 0) 8419406Sbde comhardclose(com); 8426261Sjkh return (error); 8439406Sbde} 8446261Sjkh 84512675Sjulianstatic int 84683366Sjuliansioclose(dev, flag, mode, td) 8479406Sbde dev_t dev; 8489406Sbde int flag; 8499406Sbde int mode; 85083366Sjulian struct thread *td; 8516261Sjkh{ 8529406Sbde struct com_s *com; 8539406Sbde int mynor; 8546261Sjkh int s; 8559406Sbde struct tty *tp; 8566261Sjkh 8579406Sbde mynor = minor(dev); 8589406Sbde if (mynor & CONTROL_MASK) 8599406Sbde return (0); 8609406Sbde com = com_addr(MINOR_TO_UNIT(mynor)); 8619406Sbde tp = com->tp; 8626261Sjkh s = spltty(); 86341903Sbde cd_etc(com, CD1400_ETC_STOPBREAK); 8649406Sbde (*linesw[tp->t_line].l_close)(tp, flag); 8659406Sbde disc_optim(tp, &tp->t_termios, com); 86651654Sphk comstop(tp, FREAD | FWRITE); 8679406Sbde comhardclose(com); 8689406Sbde ttyclose(tp); 8699406Sbde siosettimeout(); 8706261Sjkh splx(s); 8716261Sjkh#ifdef broken /* session holds a ref to the tty; can't deallocate */ 8726261Sjkh ttyfree(tp); 87381576Sbde com->tp = NULL; 8746261Sjkh#endif 8759406Sbde return (0); 8769406Sbde} 8776261Sjkh 8789406Sbdestatic void 8799406Sbdecomhardclose(com) 8809406Sbde struct com_s *com; 8819406Sbde{ 8829406Sbde cy_addr iobase; 8839406Sbde int s; 8849406Sbde struct tty *tp; 8859406Sbde int unit; 8866261Sjkh 8879406Sbde unit = com->unit; 8889406Sbde iobase = com->iobase; 8899406Sbde s = spltty(); 8909406Sbde#if 0 8919406Sbde com->poll = FALSE; 8929406Sbde com->poll_output = FALSE; 8939406Sbde#endif 8949406Sbde com->do_timestamp = 0; 8959406Sbde#if 0 8969406Sbde outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); 89741903Sbde#else 89841903Sbde /* XXX */ 89988088Sjhb critical_enter(); 90072262Sjhb COM_LOCK(); 90141903Sbde com->etc = ETC_NONE; 90241903Sbde cd_setreg(com, CD1400_COR2, com->cor[1] &= ~CD1400_COR2_ETC); 90372262Sjhb COM_UNLOCK(); 90488088Sjhb critical_exit(); 90541903Sbde cd1400_channel_cmd(com, CD1400_CCR_CMDRESET | CD1400_CCR_FTF); 9069406Sbde#endif 9076261Sjkh 9089406Sbde { 9099406Sbde#if 0 9109406Sbde outb(iobase + com_ier, 0); 9119406Sbde#else 91288088Sjhb critical_enter(); 91372262Sjhb COM_LOCK(); 91441293Sbde cd_setreg(com, CD1400_SRER, com->intr_enable = 0); 91572262Sjhb COM_UNLOCK(); 91688088Sjhb critical_exit(); 9179406Sbde#endif 9189406Sbde tp = com->tp; 91943314Sdillon if ((tp->t_cflag & HUPCL) 9209406Sbde /* 9219406Sbde * XXX we will miss any carrier drop between here and the 9229406Sbde * next open. Perhaps we should watch DCD even when the 9239406Sbde * port is closed; it is not sufficient to check it at 9249406Sbde * the next open because it might go up and down while 9259406Sbde * we're not watching. 9269406Sbde */ 92743314Sdillon || (!com->active_out 9289406Sbde && !(com->prev_modem_status & MSR_DCD) 92943314Sdillon && !(com->it_in.c_cflag & CLOCAL)) 9309406Sbde || !(tp->t_state & TS_ISOPEN)) { 9319406Sbde (void)commctl(com, TIOCM_DTR, DMBIC); 9326261Sjkh 9339406Sbde /* Disable receiver (leave transmitter enabled). */ 9349406Sbde com->channel_control = CD1400_CCR_CMDCHANCTL 9359406Sbde | CD1400_CCR_XMTEN 9369406Sbde | CD1400_CCR_RCVDIS; 93741293Sbde cd1400_channel_cmd(com, com->channel_control); 9386261Sjkh 93941385Sbde if (com->dtr_wait != 0 && !(com->state & CS_DTR_OFF)) { 9409406Sbde timeout(siodtrwakeup, com, com->dtr_wait); 9419406Sbde com->state |= CS_DTR_OFF; 9429406Sbde } 9439406Sbde } 9449406Sbde } 94512962Sbde#if 0 94612962Sbde if (com->hasfifo) { 94712962Sbde /* 94812962Sbde * Disable fifos so that they are off after controlled 94912962Sbde * reboots. Some BIOSes fail to detect 16550s when the 95012962Sbde * fifos are enabled. 95112962Sbde */ 95212962Sbde outb(iobase + com_fifo, 0); 95312962Sbde } 95412962Sbde#endif 9559406Sbde com->active_out = FALSE; 9569406Sbde wakeup(&com->active_out); 9579406Sbde wakeup(TSA_CARR_ON(tp)); /* restart any wopeners */ 9589406Sbde splx(s); 9599406Sbde} 9609406Sbde 96112675Sjulianstatic int 9629406Sbdesiowrite(dev, uio, flag) 9639406Sbde dev_t dev; 9649406Sbde struct uio *uio; 9659406Sbde int flag; 9666261Sjkh{ 9679406Sbde int mynor; 9689406Sbde struct tty *tp; 9699406Sbde int unit; 9706261Sjkh 9719406Sbde mynor = minor(dev); 9729406Sbde if (mynor & CONTROL_MASK) 9739406Sbde return (ENODEV); 9749406Sbde 9759406Sbde unit = MINOR_TO_UNIT(mynor); 9769406Sbde tp = com_addr(unit)->tp; 9779406Sbde /* 9789406Sbde * (XXX) We disallow virtual consoles if the physical console is 9799406Sbde * a serial port. This is in case there is a display attached that 9809406Sbde * is not the console. In that situation we don't need/want the X 9819406Sbde * server taking over the console. 9829406Sbde */ 98311671Sbde if (constty != NULL && unit == comconsole) 9849406Sbde constty = NULL; 9856261Sjkh#ifdef Smarts 9866261Sjkh /* XXX duplicate ttwrite(), but without so much output processing on 9876261Sjkh * CR & LF chars. Hardly worth the effort, given that high-throughput 9886261Sjkh * sessions are raw anyhow. 9896261Sjkh */ 9906261Sjkh#else 9919406Sbde return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); 9926261Sjkh#endif 9939406Sbde} 9946261Sjkh 9956261Sjkhstatic void 9969406Sbdesiodtrwakeup(chan) 9979406Sbde void *chan; 9986261Sjkh{ 9999406Sbde struct com_s *com; 10006261Sjkh 10019406Sbde com = (struct com_s *)chan; 10029406Sbde com->state &= ~CS_DTR_OFF; 10039406Sbde wakeup(&com->dtr_wait); 10049406Sbde} 10056261Sjkh 100665557Sjasone/* 100772262Sjhb * This function: 100872262Sjhb * a) needs to be called with COM_LOCK() held, and 100972262Sjhb * b) needs to return with COM_LOCK() held. 101065557Sjasone */ 101143611Sbdestatic void 101288088Sjhbsioinput(com) 101343611Sbde struct com_s *com; 101443611Sbde{ 101543611Sbde u_char *buf; 101643611Sbde int incc; 101743611Sbde u_char line_status; 101843611Sbde int recv_data; 101943611Sbde struct tty *tp; 102043611Sbde 102143611Sbde buf = com->ibuf; 102243611Sbde tp = com->tp; 102343611Sbde if (!(tp->t_state & TS_ISOPEN)) { 102443611Sbde com_events -= (com->iptr - com->ibuf); 102543611Sbde com->iptr = com->ibuf; 102643611Sbde return; 102743611Sbde } 102843611Sbde if (tp->t_state & TS_CAN_BYPASS_L_RINT) { 102943611Sbde /* 103043611Sbde * Avoid the grotesquely inefficient lineswitch routine 103143611Sbde * (ttyinput) in "raw" mode. It usually takes about 450 103243611Sbde * instructions (that's without canonical processing or echo!). 103343611Sbde * slinput is reasonably fast (usually 40 instructions plus 103443611Sbde * call overhead). 103543611Sbde */ 103665557Sjasone 103743611Sbde do { 103865557Sjasone /* 103965557Sjasone * This may look odd, but it is using save-and-enable 104065557Sjasone * semantics instead of the save-and-disable semantics 104165557Sjasone * that are used everywhere else. 104265557Sjasone */ 104372262Sjhb COM_UNLOCK(); 104488088Sjhb critical_exit(); 104543611Sbde incc = com->iptr - buf; 104643611Sbde if (tp->t_rawq.c_cc + incc > tp->t_ihiwat 104743611Sbde && (com->state & CS_RTS_IFLOW 104843611Sbde || tp->t_iflag & IXOFF) 104943611Sbde && !(tp->t_state & TS_TBLOCK)) 105043611Sbde ttyblock(tp); 105143611Sbde com->delta_error_counts[CE_TTY_BUF_OVERFLOW] 105243611Sbde += b_to_q((char *)buf, incc, &tp->t_rawq); 105343611Sbde buf += incc; 105443611Sbde tk_nin += incc; 105543611Sbde tk_rawcc += incc; 105643611Sbde tp->t_rawcc += incc; 105743611Sbde ttwakeup(tp); 105843611Sbde if (tp->t_state & TS_TTSTOP 105943611Sbde && (tp->t_iflag & IXANY 106043611Sbde || tp->t_cc[VSTART] == tp->t_cc[VSTOP])) { 106143611Sbde tp->t_state &= ~TS_TTSTOP; 106243611Sbde tp->t_lflag &= ~FLUSHO; 106343611Sbde comstart(tp); 106443611Sbde } 106588088Sjhb critical_enter(); 106672262Sjhb COM_LOCK(); 106743611Sbde } while (buf < com->iptr); 106843611Sbde } else { 106943611Sbde do { 107065557Sjasone /* 107165557Sjasone * This may look odd, but it is using save-and-enable 107265557Sjasone * semantics instead of the save-and-disable semantics 107365557Sjasone * that are used everywhere else. 107465557Sjasone */ 107572262Sjhb COM_UNLOCK(); 107688088Sjhb critical_exit(); 107743611Sbde line_status = buf[com->ierroff]; 107843611Sbde recv_data = *buf++; 107943611Sbde if (line_status 108043611Sbde & (LSR_BI | LSR_FE | LSR_OE | LSR_PE)) { 108143611Sbde if (line_status & LSR_BI) 108243611Sbde recv_data |= TTY_BI; 108343611Sbde if (line_status & LSR_FE) 108443611Sbde recv_data |= TTY_FE; 108543611Sbde if (line_status & LSR_OE) 108643611Sbde recv_data |= TTY_OE; 108743611Sbde if (line_status & LSR_PE) 108843611Sbde recv_data |= TTY_PE; 108943611Sbde } 109043611Sbde (*linesw[tp->t_line].l_rint)(recv_data, tp); 109188088Sjhb critical_enter(); 109272262Sjhb COM_LOCK(); 109343611Sbde } while (buf < com->iptr); 109443611Sbde } 109543611Sbde com_events -= (com->iptr - com->ibuf); 109643611Sbde com->iptr = com->ibuf; 109743611Sbde 109843611Sbde /* 109943611Sbde * There is now room for another low-level buffer full of input, 110043611Sbde * so enable RTS if it is now disabled and there is room in the 110143611Sbde * high-level buffer. 110243611Sbde */ 110343611Sbde if ((com->state & CS_RTS_IFLOW) && !(com->mcr_image & com->mcr_rts) && 110443611Sbde !(tp->t_state & TS_TBLOCK)) 110543611Sbde#if 0 110643611Sbde outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); 110743611Sbde#else 110843611Sbde cd_setreg(com, com->mcr_rts_reg, 110943611Sbde com->mcr_image |= com->mcr_rts); 111043611Sbde#endif 111143611Sbde} 111243611Sbde 1113123104Sbdestatic void 1114123104Sbdecyointr(int unit) 1115123104Sbde{ 1116123104Sbde siointr1(com_addr(unit * CY_MAX_PORTS)); 1117123104Sbde} 1118123104Sbde 11199406Sbdevoid 1120123104Sbdesiointr1(vcom) 1121123104Sbde void *vcom; 11226261Sjkh{ 1123123104Sbde struct com_s *basecom; 112429047Sbde int baseu; 112529047Sbde int cy_align; 112629047Sbde cy_addr cy_iobase; 112729047Sbde int cyu; 112829047Sbde cy_addr iobase; 112929047Sbde u_char status; 1130123104Sbde int unit; 11316261Sjkh 113272262Sjhb COM_LOCK(); /* XXX could this be placed down lower in the loop? */ 113328921Sfsmp 1134123104Sbde basecom = (struct com_s *)vcom; 1135123104Sbde baseu = basecom->unit; 1136123104Sbde cy_align = basecom->cy_align; 1137123104Sbde cy_iobase = basecom->cy_iobase; 1138123104Sbde unit = baseu / CY_MAX_PORTS; 11396261Sjkh 11409406Sbde /* check each CD1400 in turn */ 114118685Sdg for (cyu = 0; cyu < cy_nr_cd1400s[unit]; ++cyu) { 114229047Sbde iobase = (cy_addr) (cy_iobase 114329047Sbde + (cy_chip_offset[cyu] << cy_align)); 11449406Sbde /* poll to see if it has any work */ 114518901Sdg status = cd_inb(iobase, CD1400_SVRR, cy_align); 11469406Sbde if (status == 0) 11479406Sbde continue; 11486261Sjkh#ifdef CyDebug 11499406Sbde ++cy_svrr_probes; 11506261Sjkh#endif 11519406Sbde /* service requests as appropriate, giving priority to RX */ 11529406Sbde if (status & CD1400_SVRR_RXRDY) { 11539406Sbde struct com_s *com; 11549406Sbde u_int count; 11559406Sbde u_char *ioptr; 11569406Sbde u_char line_status; 11579406Sbde u_char recv_data; 11589406Sbde u_char serv_type; 11599406Sbde#ifdef PollMode 11609406Sbde u_char save_rir; 11616261Sjkh#endif 11626261Sjkh 11639406Sbde#ifdef PollMode 116418901Sdg save_rir = cd_inb(iobase, CD1400_RIR, cy_align); 11656261Sjkh 11669406Sbde /* enter rx service */ 116718901Sdg cd_outb(iobase, CD1400_CAR, cy_align, save_rir); 116841293Sbde com_addr(baseu + cyu * CD1400_NO_OF_CHANNELS)->car 116941293Sbde = save_rir & CD1400_CAR_CHAN; 11706261Sjkh 117118901Sdg serv_type = cd_inb(iobase, CD1400_RIVR, cy_align); 11729406Sbde com = com_addr(baseu 11739406Sbde + ((serv_type >> CD1400_xIVR_CHAN_SHIFT) 11749406Sbde & CD1400_xIVR_CHAN)); 11756261Sjkh#else 11769406Sbde /* ack receive service */ 117741309Sbde serv_type = cy_inb(iobase, CY8_SVCACKR, cy_align); 11786261Sjkh 11799406Sbde com = com_addr(baseu + 11809406Sbde + ((serv_type >> CD1400_xIVR_CHAN_SHIFT) 11819406Sbde & CD1400_xIVR_CHAN)); 11826261Sjkh#endif 11836261Sjkh 11849406Sbde if (serv_type & CD1400_RIVR_EXCEPTION) { 11859406Sbde ++com->recv_exception; 118618901Sdg line_status = cd_inb(iobase, CD1400_RDSR, cy_align); 11879406Sbde /* break/unnattached error bits or real input? */ 118818901Sdg recv_data = cd_inb(iobase, CD1400_RDSR, cy_align); 11899406Sbde#ifndef SOFT_HOTCHAR 11909406Sbde if (line_status & CD1400_RDSR_SPECIAL 11919406Sbde && com->hotchar != 0) 1192122799Sbde swi_sched(sio_fast_ih, 0); 119367551Sjhb 11946261Sjkh#endif 11959406Sbde#if 1 /* XXX "intelligent" PFO error handling would break O error handling */ 11969406Sbde if (line_status & (LSR_PE|LSR_FE|LSR_BI)) { 11979406Sbde /* 11989406Sbde Don't store PE if IGNPAR and BI if IGNBRK, 11999406Sbde this hack allows "raw" tty optimization 12009406Sbde works even if IGN* is set. 12019406Sbde */ 12029406Sbde if ( com->tp == NULL 12039406Sbde || !(com->tp->t_state & TS_ISOPEN) 120443314Sdillon || ((line_status & (LSR_PE|LSR_FE)) 120543314Sdillon && (com->tp->t_iflag & IGNPAR)) 120643314Sdillon || ((line_status & LSR_BI) 120743314Sdillon && (com->tp->t_iflag & IGNBRK))) 12089406Sbde goto cont; 12099406Sbde if ( (line_status & (LSR_PE|LSR_FE)) 12109406Sbde && (com->tp->t_state & TS_CAN_BYPASS_L_RINT) 12119406Sbde && ((line_status & LSR_FE) 121243314Sdillon || ((line_status & LSR_PE) 121343314Sdillon && (com->tp->t_iflag & INPCK)))) 12149406Sbde recv_data = 0; 12159406Sbde } 12169406Sbde#endif /* 1 */ 12179406Sbde ++com->bytes_in; 12189406Sbde#ifdef SOFT_HOTCHAR 12199406Sbde if (com->hotchar != 0 && recv_data == com->hotchar) 1220122799Sbde swi_sched(sio_fast_ih, 0); 12216261Sjkh#endif 12229406Sbde ioptr = com->iptr; 12239406Sbde if (ioptr >= com->ibufend) 12249406Sbde CE_RECORD(com, CE_INTERRUPT_BUF_OVERFLOW); 12259406Sbde else { 122617354Sbde if (com->do_timestamp) 122717354Sbde microtime(&com->timestamp); 12289406Sbde ++com_events; 12299406Sbde ioptr[0] = recv_data; 123043611Sbde ioptr[com->ierroff] = line_status; 12319406Sbde com->iptr = ++ioptr; 12329406Sbde if (ioptr == com->ihighwater 123338303Sbde && com->state & CS_RTS_IFLOW) 12349406Sbde#if 0 12359406Sbde outb(com->modem_ctl_port, 12369406Sbde com->mcr_image &= ~MCR_RTS); 12379406Sbde#else 123838303Sbde cd_outb(iobase, com->mcr_rts_reg, 123938303Sbde cy_align, 124038303Sbde com->mcr_image &= 124138303Sbde ~com->mcr_rts); 12426261Sjkh#endif 12439406Sbde if (line_status & LSR_OE) 12449406Sbde CE_RECORD(com, CE_OVERRUN); 12459406Sbde } 12469406Sbde goto cont; 12479406Sbde } else { 12489406Sbde int ifree; 12496261Sjkh 125018901Sdg count = cd_inb(iobase, CD1400_RDCR, cy_align); 125134661Sdg if (!count) 125234661Sdg goto cont; 12539406Sbde com->bytes_in += count; 12549406Sbde ioptr = com->iptr; 12559406Sbde ifree = com->ibufend - ioptr; 12569406Sbde if (count > ifree) { 12579406Sbde count -= ifree; 12589406Sbde com_events += ifree; 125917354Sbde if (ifree != 0) { 126017354Sbde if (com->do_timestamp) 126117354Sbde microtime(&com->timestamp); 126217354Sbde do { 126317354Sbde recv_data = cd_inb(iobase, 126429047Sbde CD1400_RDSR, 126529047Sbde cy_align); 12669406Sbde#ifdef SOFT_HOTCHAR 126717354Sbde if (com->hotchar != 0 126817354Sbde && recv_data 126917354Sbde == com->hotchar) 1270122799Sbde swi_sched(sio_fast_ih, 1271122799Sbde 0); 12726261Sjkh#endif 127317354Sbde ioptr[0] = recv_data; 127443611Sbde ioptr[com->ierroff] = 0; 127517354Sbde ++ioptr; 127617354Sbde } while (--ifree != 0); 12779406Sbde } 12789406Sbde com->delta_error_counts 12799406Sbde [CE_INTERRUPT_BUF_OVERFLOW] += count; 12809406Sbde do { 128129047Sbde recv_data = cd_inb(iobase, CD1400_RDSR, 128229047Sbde cy_align); 12839406Sbde#ifdef SOFT_HOTCHAR 12849406Sbde if (com->hotchar != 0 12859406Sbde && recv_data == com->hotchar) 1286122799Sbde swi_sched(sio_fast_ih, 0); 12876261Sjkh#endif 12889406Sbde } while (--count != 0); 12899406Sbde } else { 129017354Sbde if (com->do_timestamp) 129117354Sbde microtime(&com->timestamp); 12929406Sbde if (ioptr <= com->ihighwater 12939406Sbde && ioptr + count > com->ihighwater 12949406Sbde && com->state & CS_RTS_IFLOW) 12959406Sbde#if 0 12969406Sbde outb(com->modem_ctl_port, 12979406Sbde com->mcr_image &= ~MCR_RTS); 12986261Sjkh#else 129938303Sbde cd_outb(iobase, com->mcr_rts_reg, 130038303Sbde cy_align, 130138303Sbde com->mcr_image 130238303Sbde &= ~com->mcr_rts); 13036261Sjkh#endif 13049406Sbde com_events += count; 13059406Sbde do { 130629047Sbde recv_data = cd_inb(iobase, CD1400_RDSR, 130729047Sbde cy_align); 13089406Sbde#ifdef SOFT_HOTCHAR 13099406Sbde if (com->hotchar != 0 13109406Sbde && recv_data == com->hotchar) 1311122799Sbde swi_sched(sio_fast_ih, 0); 13126261Sjkh#endif 13139406Sbde ioptr[0] = recv_data; 131443611Sbde ioptr[com->ierroff] = 0; 13159406Sbde ++ioptr; 13169406Sbde } while (--count != 0); 13176261Sjkh } 13189406Sbde com->iptr = ioptr; 13196261Sjkh } 13209406Sbdecont: 13216261Sjkh 13229406Sbde /* terminate service context */ 13236261Sjkh#ifdef PollMode 132418901Sdg cd_outb(iobase, CD1400_RIR, cy_align, 13259406Sbde save_rir 13269406Sbde & ~(CD1400_RIR_RDIREQ | CD1400_RIR_RBUSY)); 13276261Sjkh#else 132818901Sdg cd_outb(iobase, CD1400_EOSRR, cy_align, 0); 13296261Sjkh#endif 13309406Sbde } 13319406Sbde if (status & CD1400_SVRR_MDMCH) { 13329406Sbde struct com_s *com; 13339406Sbde u_char modem_status; 13346261Sjkh#ifdef PollMode 13359406Sbde u_char save_mir; 13366261Sjkh#else 13379406Sbde u_char vector; 13386261Sjkh#endif 13396261Sjkh 13406261Sjkh#ifdef PollMode 134118901Sdg save_mir = cd_inb(iobase, CD1400_MIR, cy_align); 13426261Sjkh 13439406Sbde /* enter modem service */ 134418901Sdg cd_outb(iobase, CD1400_CAR, cy_align, save_mir); 134541293Sbde com_addr(baseu + cyu * CD1400_NO_OF_CHANNELS)->car 134641293Sbde = save_mir & CD1400_CAR_CHAN; 13476261Sjkh 13489406Sbde com = com_addr(baseu + cyu * CD1400_NO_OF_CHANNELS 13499406Sbde + (save_mir & CD1400_MIR_CHAN)); 13506261Sjkh#else 13519406Sbde /* ack modem service */ 135241309Sbde vector = cy_inb(iobase, CY8_SVCACKM, cy_align); 13536261Sjkh 13549406Sbde com = com_addr(baseu 13559406Sbde + ((vector >> CD1400_xIVR_CHAN_SHIFT) 13569406Sbde & CD1400_xIVR_CHAN)); 13579406Sbde#endif 13589406Sbde ++com->mdm; 135918901Sdg modem_status = cd_inb(iobase, CD1400_MSVR2, cy_align); 13609406Sbde if (modem_status != com->last_modem_status) { 136117354Sbde if (com->do_dcd_timestamp 136217354Sbde && !(com->last_modem_status & MSR_DCD) 136317354Sbde && modem_status & MSR_DCD) 136417354Sbde microtime(&com->dcd_timestamp); 136517354Sbde 13669406Sbde /* 13679406Sbde * Schedule high level to handle DCD changes. Note 13689406Sbde * that we don't use the delta bits anywhere. Some 13699406Sbde * UARTs mess them up, and it's easy to remember the 13709406Sbde * previous bits and calculate the delta. 13719406Sbde */ 13729406Sbde com->last_modem_status = modem_status; 13739406Sbde if (!(com->state & CS_CHECKMSR)) { 13749406Sbde com_events += LOTS_OF_EVENTS; 13759406Sbde com->state |= CS_CHECKMSR; 1376122799Sbde swi_sched(sio_fast_ih, 0); 13779406Sbde } 13786261Sjkh 13799406Sbde#ifdef SOFT_CTS_OFLOW 13809406Sbde /* handle CTS change immediately for crisp flow ctl */ 13819406Sbde if (com->state & CS_CTS_OFLOW) { 13829406Sbde if (modem_status & MSR_CTS) { 13839406Sbde com->state |= CS_ODEVREADY; 13849406Sbde if (com->state >= (CS_BUSY | CS_TTGO 13859406Sbde | CS_ODEVREADY) 13869406Sbde && !(com->intr_enable 13879406Sbde & CD1400_SRER_TXRDY)) 138829047Sbde cd_outb(iobase, CD1400_SRER, 138929047Sbde cy_align, 13909406Sbde com->intr_enable 139141388Sbde = com->intr_enable 139241388Sbde & ~CD1400_SRER_TXMPTY 139341388Sbde | CD1400_SRER_TXRDY); 13949406Sbde } else { 13959406Sbde com->state &= ~CS_ODEVREADY; 139629047Sbde if (com->intr_enable 139729047Sbde & CD1400_SRER_TXRDY) 139829047Sbde cd_outb(iobase, CD1400_SRER, 139929047Sbde cy_align, 14009406Sbde com->intr_enable 140141388Sbde = com->intr_enable 140241388Sbde & ~CD1400_SRER_TXRDY 140341388Sbde | CD1400_SRER_TXMPTY); 14049406Sbde } 14059406Sbde } 14066261Sjkh#endif 14079406Sbde } 14086261Sjkh 14099406Sbde /* terminate service context */ 14106261Sjkh#ifdef PollMode 141118901Sdg cd_outb(iobase, CD1400_MIR, cy_align, 14129406Sbde save_mir 14139406Sbde & ~(CD1400_MIR_RDIREQ | CD1400_MIR_RBUSY)); 14146261Sjkh#else 141518901Sdg cd_outb(iobase, CD1400_EOSRR, cy_align, 0); 14166261Sjkh#endif 14179406Sbde } 14189406Sbde if (status & CD1400_SVRR_TXRDY) { 14199406Sbde struct com_s *com; 14206261Sjkh#ifdef PollMode 14219406Sbde u_char save_tir; 14226261Sjkh#else 14239406Sbde u_char vector; 14246261Sjkh#endif 14256261Sjkh 14266261Sjkh#ifdef PollMode 142718901Sdg save_tir = cd_inb(iobase, CD1400_TIR, cy_align); 14289406Sbde 14299406Sbde /* enter tx service */ 143018901Sdg cd_outb(iobase, CD1400_CAR, cy_align, save_tir); 143141293Sbde com_addr(baseu + cyu * CD1400_NO_OF_CHANNELS)->car 143241293Sbde = save_tir & CD1400_CAR_CHAN; 143341293Sbde 14349406Sbde com = com_addr(baseu 14359406Sbde + cyu * CD1400_NO_OF_CHANNELS 14369406Sbde + (save_tir & CD1400_TIR_CHAN)); 14376261Sjkh#else 14389406Sbde /* ack transmit service */ 143941309Sbde vector = cy_inb(iobase, CY8_SVCACKT, cy_align); 14406261Sjkh 14419406Sbde com = com_addr(baseu 14429406Sbde + ((vector >> CD1400_xIVR_CHAN_SHIFT) 14439406Sbde & CD1400_xIVR_CHAN)); 14446261Sjkh#endif 14456261Sjkh 144641903Sbde if (com->etc != ETC_NONE) { 144741903Sbde if (com->intr_enable & CD1400_SRER_TXRDY) { 144841903Sbde /* 144941903Sbde * Here due to sloppy SRER_TXRDY 145041903Sbde * enabling. Ignore. Come back when 145141903Sbde * tx is empty. 145241903Sbde */ 145341903Sbde cd_outb(iobase, CD1400_SRER, cy_align, 145441903Sbde com->intr_enable 145543314Sdillon = (com->intr_enable 145643314Sdillon & ~CD1400_SRER_TXRDY) 145741903Sbde | CD1400_SRER_TXMPTY); 145841903Sbde goto terminate_tx_service; 145941903Sbde } 146041903Sbde switch (com->etc) { 146141903Sbde case CD1400_ETC_SENDBREAK: 146241903Sbde case CD1400_ETC_STOPBREAK: 146341903Sbde /* 146441903Sbde * Start the command. Come back on 146541903Sbde * next tx empty interrupt, hopefully 146641903Sbde * after command has been executed. 146741903Sbde */ 146841903Sbde cd_outb(iobase, CD1400_COR2, cy_align, 146941903Sbde com->cor[1] |= CD1400_COR2_ETC); 147041903Sbde cd_outb(iobase, CD1400_TDR, cy_align, 147141903Sbde CD1400_ETC_CMD); 147241903Sbde cd_outb(iobase, CD1400_TDR, cy_align, 147341903Sbde com->etc); 147441903Sbde if (com->etc == CD1400_ETC_SENDBREAK) 147541903Sbde com->etc = ETC_BREAK_STARTING; 147641903Sbde else 147741903Sbde com->etc = ETC_BREAK_ENDING; 147841903Sbde goto terminate_tx_service; 147941903Sbde case ETC_BREAK_STARTING: 148041903Sbde /* 148141903Sbde * BREAK is now on. Continue with 148241903Sbde * SRER_TXMPTY processing, hopefully 148341903Sbde * don't come back. 148441903Sbde */ 148541903Sbde com->etc = ETC_BREAK_STARTED; 148641903Sbde break; 148741903Sbde case ETC_BREAK_STARTED: 148841903Sbde /* 148941903Sbde * Came back due to sloppy SRER_TXMPTY 149041903Sbde * enabling. Hope again. 149141903Sbde */ 149241903Sbde break; 149341903Sbde case ETC_BREAK_ENDING: 149441903Sbde /* 149541903Sbde * BREAK is now off. Continue with 149641903Sbde * SRER_TXMPTY processing and don't 149741903Sbde * come back. The SWI handler will 149841903Sbde * restart tx interrupts if necessary. 149941903Sbde */ 150041903Sbde cd_outb(iobase, CD1400_COR2, cy_align, 150141903Sbde com->cor[1] 150241903Sbde &= ~CD1400_COR2_ETC); 150341903Sbde com->etc = ETC_BREAK_ENDED; 150441903Sbde if (!(com->state & CS_ODONE)) { 150541903Sbde com_events += LOTS_OF_EVENTS; 150641903Sbde com->state |= CS_ODONE; 1507122799Sbde swi_sched(sio_fast_ih, 0); 150841903Sbde } 150941903Sbde break; 151041903Sbde case ETC_BREAK_ENDED: 151141903Sbde /* 151241903Sbde * Shouldn't get here. Hope again. 151341903Sbde */ 151441903Sbde break; 151541903Sbde } 151641903Sbde } 151741388Sbde if (com->intr_enable & CD1400_SRER_TXMPTY) { 151841388Sbde if (!(com->extra_state & CSE_ODONE)) { 151941388Sbde com_events += LOTS_OF_EVENTS; 152041388Sbde com->extra_state |= CSE_ODONE; 1521122799Sbde swi_sched(sio_fast_ih, 0); 152241388Sbde } 152341388Sbde cd_outb(iobase, CD1400_SRER, cy_align, 152441388Sbde com->intr_enable 152541388Sbde &= ~CD1400_SRER_TXMPTY); 152641388Sbde goto terminate_tx_service; 152741388Sbde } 15289406Sbde if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) { 15299406Sbde u_char *ioptr; 15309406Sbde u_int ocount; 15316261Sjkh 15329406Sbde ioptr = com->obufq.l_head; 15339406Sbde ocount = com->obufq.l_tail - ioptr; 15349406Sbde if (ocount > CD1400_TX_FIFO_SIZE) 15359406Sbde ocount = CD1400_TX_FIFO_SIZE; 15369406Sbde com->bytes_out += ocount; 15379406Sbde do 153829047Sbde cd_outb(iobase, CD1400_TDR, cy_align, 153929047Sbde *ioptr++); 15409406Sbde while (--ocount != 0); 15419406Sbde com->obufq.l_head = ioptr; 15429406Sbde if (ioptr >= com->obufq.l_tail) { 15439406Sbde struct lbq *qp; 15446261Sjkh 15459406Sbde qp = com->obufq.l_next; 15469406Sbde qp->l_queued = FALSE; 15479406Sbde qp = qp->l_next; 15489406Sbde if (qp != NULL) { 15499406Sbde com->obufq.l_head = qp->l_head; 15509406Sbde com->obufq.l_tail = qp->l_tail; 15519406Sbde com->obufq.l_next = qp; 15529406Sbde } else { 15539406Sbde /* output just completed */ 15549406Sbde com->state &= ~CS_BUSY; 155541388Sbde 155641388Sbde /* 155741388Sbde * The setting of CSE_ODONE may be 155841388Sbde * stale here. We currently only 155941388Sbde * use it when CS_BUSY is set, and 156041388Sbde * fixing it when we clear CS_BUSY 156141388Sbde * is easiest. 156241388Sbde */ 156341388Sbde if (com->extra_state & CSE_ODONE) { 156441388Sbde com_events -= LOTS_OF_EVENTS; 156541388Sbde com->extra_state &= ~CSE_ODONE; 156641388Sbde } 156741388Sbde 156818901Sdg cd_outb(iobase, CD1400_SRER, cy_align, 15699406Sbde com->intr_enable 157043314Sdillon = (com->intr_enable 157143314Sdillon & ~CD1400_SRER_TXRDY) 157241388Sbde | CD1400_SRER_TXMPTY); 15739406Sbde } 15749406Sbde if (!(com->state & CS_ODONE)) { 15759406Sbde com_events += LOTS_OF_EVENTS; 15769406Sbde com->state |= CS_ODONE; 157729047Sbde 157829047Sbde /* handle at high level ASAP */ 1579122799Sbde swi_sched(sio_fast_ih, 0); 15809406Sbde } 15819406Sbde } 15829406Sbde } 15836261Sjkh 15849406Sbde /* terminate service context */ 158541388Sbdeterminate_tx_service: 15866261Sjkh#ifdef PollMode 158718901Sdg cd_outb(iobase, CD1400_TIR, cy_align, 15889406Sbde save_tir 15899406Sbde & ~(CD1400_TIR_RDIREQ | CD1400_TIR_RBUSY)); 15906261Sjkh#else 159118901Sdg cd_outb(iobase, CD1400_EOSRR, cy_align, 0); 15926261Sjkh#endif 15939406Sbde } 15946261Sjkh } 15956261Sjkh 15969406Sbde /* ensure an edge for the next interrupt */ 159741309Sbde cy_outb(cy_iobase, CY_CLEAR_INTR, cy_align, 0); 15986261Sjkh 1599122799Sbde swi_sched(sio_slow_ih, SWI_DELAY); 160028921Sfsmp 160172262Sjhb COM_UNLOCK(); 16029406Sbde} 16036261Sjkh 160412675Sjulianstatic int 160583366Sjuliansioioctl(dev, cmd, data, flag, td) 16069406Sbde dev_t dev; 160736735Sdfr u_long cmd; 16089406Sbde caddr_t data; 16099406Sbde int flag; 161083366Sjulian struct thread *td; 16116261Sjkh{ 16129406Sbde struct com_s *com; 16136261Sjkh int error; 16149406Sbde int mynor; 16159406Sbde int s; 16169406Sbde struct tty *tp; 16179406Sbde#if defined(COMPAT_43) || defined(COMPAT_SUNOS) 16189406Sbde int oldcmd; 16199406Sbde struct termios term; 16209406Sbde#endif 16216261Sjkh 16229406Sbde mynor = minor(dev); 16239406Sbde com = com_addr(MINOR_TO_UNIT(mynor)); 16249406Sbde if (mynor & CONTROL_MASK) { 16259406Sbde struct termios *ct; 16269406Sbde 16279406Sbde switch (mynor & CONTROL_MASK) { 16289406Sbde case CONTROL_INIT_STATE: 16299406Sbde ct = mynor & CALLOUT_MASK ? &com->it_out : &com->it_in; 16309406Sbde break; 16319406Sbde case CONTROL_LOCK_STATE: 16329406Sbde ct = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in; 16339406Sbde break; 16349406Sbde default: 16359406Sbde return (ENODEV); /* /dev/nodev */ 16369406Sbde } 16379406Sbde switch (cmd) { 16389406Sbde case TIOCSETA: 163993593Sjhb error = suser(td); 16409406Sbde if (error != 0) 16419406Sbde return (error); 16429406Sbde *ct = *(struct termios *)data; 16439406Sbde return (0); 16449406Sbde case TIOCGETA: 16459406Sbde *(struct termios *)data = *ct; 16469406Sbde return (0); 16479406Sbde case TIOCGETD: 16489406Sbde *(int *)data = TTYDISC; 16499406Sbde return (0); 16509406Sbde case TIOCGWINSZ: 16519406Sbde bzero(data, sizeof(struct winsize)); 16529406Sbde return (0); 16539406Sbde default: 16549406Sbde return (ENOTTY); 16559406Sbde } 16569406Sbde } 16579406Sbde tp = com->tp; 16589406Sbde#if defined(COMPAT_43) || defined(COMPAT_SUNOS) 16599406Sbde term = tp->t_termios; 16609406Sbde oldcmd = cmd; 16619406Sbde error = ttsetcompat(tp, &cmd, data, &term); 16629406Sbde if (error != 0) 16636261Sjkh return (error); 16649406Sbde if (cmd != oldcmd) 16659406Sbde data = (caddr_t)&term; 16666261Sjkh#endif 16679406Sbde if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { 16689406Sbde int cc; 16699406Sbde struct termios *dt = (struct termios *)data; 16709406Sbde struct termios *lt = mynor & CALLOUT_MASK 16719406Sbde ? &com->lt_out : &com->lt_in; 16729406Sbde 16739406Sbde dt->c_iflag = (tp->t_iflag & lt->c_iflag) 16749406Sbde | (dt->c_iflag & ~lt->c_iflag); 16759406Sbde dt->c_oflag = (tp->t_oflag & lt->c_oflag) 16769406Sbde | (dt->c_oflag & ~lt->c_oflag); 16779406Sbde dt->c_cflag = (tp->t_cflag & lt->c_cflag) 16789406Sbde | (dt->c_cflag & ~lt->c_cflag); 16799406Sbde dt->c_lflag = (tp->t_lflag & lt->c_lflag) 16809406Sbde | (dt->c_lflag & ~lt->c_lflag); 16819406Sbde for (cc = 0; cc < NCCS; ++cc) 16829406Sbde if (lt->c_cc[cc] != 0) 16839406Sbde dt->c_cc[cc] = tp->t_cc[cc]; 16849406Sbde if (lt->c_ispeed != 0) 16859406Sbde dt->c_ispeed = tp->t_ispeed; 16869406Sbde if (lt->c_ospeed != 0) 16879406Sbde dt->c_ospeed = tp->t_ospeed; 16889406Sbde } 168983366Sjulian error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, td); 169031577Sbde if (error != ENOIOCTL) 16916261Sjkh return (error); 16929406Sbde s = spltty(); 16939406Sbde error = ttioctl(tp, cmd, data, flag); 16949406Sbde disc_optim(tp, &tp->t_termios, com); 169531577Sbde if (error != ENOIOCTL) { 16969406Sbde splx(s); 16979406Sbde return (error); 16989406Sbde } 16996261Sjkh switch (cmd) { 170041903Sbde case TIOCSBRK: 17019406Sbde#if 0 17029406Sbde outb(iobase + com_cfcr, com->cfcr_image |= CFCR_SBREAK); 170341903Sbde#else 170441903Sbde cd_etc(com, CD1400_ETC_SENDBREAK); 170541903Sbde#endif 17066261Sjkh break; 17076261Sjkh case TIOCCBRK: 170841903Sbde#if 0 17099406Sbde outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); 171041903Sbde#else 171141903Sbde cd_etc(com, CD1400_ETC_STOPBREAK); 171241903Sbde#endif 17136261Sjkh break; 17146261Sjkh case TIOCSDTR: 17159406Sbde (void)commctl(com, TIOCM_DTR, DMBIS); 17166261Sjkh break; 17176261Sjkh case TIOCCDTR: 17189406Sbde (void)commctl(com, TIOCM_DTR, DMBIC); 17196261Sjkh break; 172019718Sbde /* 172119718Sbde * XXX should disallow changing MCR_RTS if CS_RTS_IFLOW is set. The 172219718Sbde * changes get undone on the next call to comparam(). 172319718Sbde */ 17246261Sjkh case TIOCMSET: 17259406Sbde (void)commctl(com, *(int *)data, DMSET); 17266261Sjkh break; 17276261Sjkh case TIOCMBIS: 17289406Sbde (void)commctl(com, *(int *)data, DMBIS); 17296261Sjkh break; 17306261Sjkh case TIOCMBIC: 17319406Sbde (void)commctl(com, *(int *)data, DMBIC); 17326261Sjkh break; 17339406Sbde case TIOCMGET: 17349406Sbde *(int *)data = commctl(com, 0, DMGET); 17356261Sjkh break; 17366261Sjkh case TIOCMSDTRWAIT: 17379406Sbde /* must be root since the wait applies to following logins */ 173893593Sjhb error = suser(td); 17399406Sbde if (error != 0) { 17409406Sbde splx(s); 17419406Sbde return (error); 17429406Sbde } 17439406Sbde com->dtr_wait = *(int *)data * hz / 100; 17449406Sbde break; 17456261Sjkh case TIOCMGDTRWAIT: 17469406Sbde *(int *)data = com->dtr_wait * 100 / hz; 17476261Sjkh break; 17489406Sbde case TIOCTIMESTAMP: 17499406Sbde com->do_timestamp = TRUE; 17509406Sbde *(struct timeval *)data = com->timestamp; 17519406Sbde break; 175217354Sbde case TIOCDCDTIMESTAMP: 175317354Sbde com->do_dcd_timestamp = TRUE; 175417354Sbde *(struct timeval *)data = com->dcd_timestamp; 175517354Sbde break; 17566261Sjkh default: 17579406Sbde splx(s); 17586261Sjkh return (ENOTTY); 17596261Sjkh } 17609406Sbde splx(s); 17619406Sbde return (0); 17629406Sbde} 17636261Sjkh 176438246Sbdestatic void 176567584Sjhbsiopoll(void *arg) 17669406Sbde{ 17679406Sbde int unit; 17686261Sjkh 17699406Sbde#ifdef CyDebug 17709406Sbde ++cy_timeouts; 17719406Sbde#endif 17729406Sbde if (com_events == 0) 17739406Sbde return; 17749406Sbderepeat: 17759406Sbde for (unit = 0; unit < NSIO; ++unit) { 17769406Sbde struct com_s *com; 17779406Sbde int incc; 17789406Sbde struct tty *tp; 17796261Sjkh 17809406Sbde com = com_addr(unit); 17819406Sbde if (com == NULL) 17829406Sbde continue; 17839406Sbde tp = com->tp; 17849406Sbde if (tp == NULL) { 17859406Sbde /* 17869406Sbde * XXX forget any events related to closed devices 17879406Sbde * (actually never opened devices) so that we don't 17889406Sbde * loop. 17899406Sbde */ 179088088Sjhb critical_enter(); 179172262Sjhb COM_LOCK(); 17929406Sbde incc = com->iptr - com->ibuf; 17939406Sbde com->iptr = com->ibuf; 17949406Sbde if (com->state & CS_CHECKMSR) { 17959406Sbde incc += LOTS_OF_EVENTS; 17969406Sbde com->state &= ~CS_CHECKMSR; 17979406Sbde } 17989406Sbde com_events -= incc; 179972262Sjhb COM_UNLOCK(); 180088088Sjhb critical_exit(); 18019406Sbde if (incc != 0) 18029406Sbde log(LOG_DEBUG, 18039406Sbde "sio%d: %d events for device with no tp\n", 18049406Sbde unit, incc); 18059406Sbde continue; 18069406Sbde } 180743611Sbde if (com->iptr != com->ibuf) { 180888088Sjhb critical_enter(); 180972262Sjhb COM_LOCK(); 181088088Sjhb sioinput(com); 181172262Sjhb COM_UNLOCK(); 181288088Sjhb critical_exit(); 18139406Sbde } 18149406Sbde if (com->state & CS_CHECKMSR) { 18159406Sbde u_char delta_modem_status; 18166261Sjkh 181788088Sjhb critical_enter(); 181872262Sjhb COM_LOCK(); 181988088Sjhb sioinput(com); 18209406Sbde delta_modem_status = com->last_modem_status 18219406Sbde ^ com->prev_modem_status; 18229406Sbde com->prev_modem_status = com->last_modem_status; 18239406Sbde com_events -= LOTS_OF_EVENTS; 18249406Sbde com->state &= ~CS_CHECKMSR; 182572262Sjhb COM_UNLOCK(); 182688088Sjhb critical_exit(); 18279406Sbde if (delta_modem_status & MSR_DCD) 18289406Sbde (*linesw[tp->t_line].l_modem) 18299406Sbde (tp, com->prev_modem_status & MSR_DCD); 18309406Sbde } 183141388Sbde if (com->extra_state & CSE_ODONE) { 183288088Sjhb critical_enter(); 183372262Sjhb COM_LOCK(); 183441388Sbde com_events -= LOTS_OF_EVENTS; 183541388Sbde com->extra_state &= ~CSE_ODONE; 183672262Sjhb COM_UNLOCK(); 183788088Sjhb critical_exit(); 183841388Sbde if (!(com->state & CS_BUSY)) { 183941388Sbde tp->t_state &= ~TS_BUSY; 184041388Sbde ttwwakeup(com->tp); 184141388Sbde } 184241903Sbde if (com->etc != ETC_NONE) { 184341903Sbde if (com->etc == ETC_BREAK_ENDED) 184441903Sbde com->etc = ETC_NONE; 184541903Sbde wakeup(&com->etc); 184641903Sbde } 184741388Sbde } 184841903Sbde if (com->state & CS_ODONE) { 184988088Sjhb critical_enter(); 185072262Sjhb COM_LOCK(); 185141903Sbde com_events -= LOTS_OF_EVENTS; 185241903Sbde com->state &= ~CS_ODONE; 185372262Sjhb COM_UNLOCK(); 185488088Sjhb critical_exit(); 185541903Sbde (*linesw[tp->t_line].l_start)(tp); 185641903Sbde } 18579406Sbde if (com_events == 0) 18589406Sbde break; 18599406Sbde } 18609406Sbde if (com_events >= LOTS_OF_EVENTS) 18619406Sbde goto repeat; 18629406Sbde} 18636261Sjkh 18649406Sbdestatic int 18659406Sbdecomparam(tp, t) 18669406Sbde struct tty *tp; 18679406Sbde struct termios *t; 18689406Sbde{ 18699406Sbde int bits; 18709406Sbde int cflag; 18719406Sbde struct com_s *com; 18729406Sbde u_char cor_change; 187338303Sbde u_long cy_clock; 18749406Sbde int idivisor; 18759406Sbde int iflag; 18769406Sbde int iprescaler; 18779406Sbde int itimeout; 18789406Sbde int odivisor; 18799406Sbde int oprescaler; 18809406Sbde u_char opt; 18819406Sbde int s; 18829406Sbde int unit; 18836261Sjkh 188438302Sbde unit = DEV_TO_UNIT(tp->t_dev); 188538302Sbde com = com_addr(unit); 188638302Sbde 18879406Sbde /* check requested parameters */ 188838303Sbde cy_clock = CY_CLOCK(com->gfrcr_image); 188938302Sbde idivisor = comspeed(t->c_ispeed, cy_clock, &iprescaler); 1890120512Sbde if (idivisor <= 0) 18919406Sbde return (EINVAL); 1892120512Sbde odivisor = comspeed(t->c_ospeed != 0 ? t->c_ospeed : tp->t_ospeed, 1893120512Sbde cy_clock, &oprescaler); 1894120512Sbde if (odivisor <= 0) 18959406Sbde return (EINVAL); 18966261Sjkh 18979406Sbde /* parameters are OK, convert them to the com struct and the device */ 189838303Sbde s = spltty(); 1899120512Sbde if (t->c_ospeed == 0) 19009406Sbde (void)commctl(com, TIOCM_DTR, DMBIC); /* hang up line */ 19019406Sbde else 19029406Sbde (void)commctl(com, TIOCM_DTR, DMBIS); 19036261Sjkh 190443611Sbde (void) siosetwater(com, t->c_ispeed); 190543611Sbde 190643611Sbde /* XXX we don't actually change the speed atomically. */ 190743611Sbde 1908120512Sbde cd_setreg(com, CD1400_RBPR, idivisor); 1909120512Sbde cd_setreg(com, CD1400_RCOR, iprescaler); 1910120512Sbde cd_setreg(com, CD1400_TBPR, odivisor); 1911120512Sbde cd_setreg(com, CD1400_TCOR, oprescaler); 19126261Sjkh 19136261Sjkh /* 19146261Sjkh * channel control 19156261Sjkh * receiver enable 19166261Sjkh * transmitter enable (always set) 19176261Sjkh */ 19189406Sbde cflag = t->c_cflag; 19199406Sbde opt = CD1400_CCR_CMDCHANCTL | CD1400_CCR_XMTEN 19209406Sbde | (cflag & CREAD ? CD1400_CCR_RCVEN : CD1400_CCR_RCVDIS); 19219406Sbde if (opt != com->channel_control) { 19229406Sbde com->channel_control = opt; 192341293Sbde cd1400_channel_cmd(com, opt); 19246261Sjkh } 19256261Sjkh 19266261Sjkh#ifdef Smarts 19276261Sjkh /* set special chars */ 19289406Sbde /* XXX if one is _POSIX_VDISABLE, can't use some others */ 19299406Sbde if (t->c_cc[VSTOP] != _POSIX_VDISABLE) 193041293Sbde cd_setreg(com, CD1400_SCHR1, t->c_cc[VSTOP]); 19319406Sbde if (t->c_cc[VSTART] != _POSIX_VDISABLE) 193241293Sbde cd_setreg(com, CD1400_SCHR2, t->c_cc[VSTART]); 19339406Sbde if (t->c_cc[VINTR] != _POSIX_VDISABLE) 193441293Sbde cd_setreg(com, CD1400_SCHR3, t->c_cc[VINTR]); 19359406Sbde if (t->c_cc[VSUSP] != _POSIX_VDISABLE) 193641293Sbde cd_setreg(com, CD1400_SCHR4, t->c_cc[VSUSP]); 19376261Sjkh#endif 19386261Sjkh 19396261Sjkh /* 19406261Sjkh * set channel option register 1 - 19416261Sjkh * parity mode 19426261Sjkh * stop bits 19436261Sjkh * char length 19446261Sjkh */ 19456261Sjkh opt = 0; 19466261Sjkh /* parity */ 19476261Sjkh if (cflag & PARENB) { 19489406Sbde if (cflag & PARODD) 19499406Sbde opt |= CD1400_COR1_PARODD; 19509406Sbde opt |= CD1400_COR1_PARNORMAL; 19516261Sjkh } 19529406Sbde iflag = t->c_iflag; 19536261Sjkh if (!(iflag & INPCK)) 19549406Sbde opt |= CD1400_COR1_NOINPCK; 19559406Sbde bits = 1 + 1; 19566261Sjkh /* stop bits */ 19579406Sbde if (cflag & CSTOPB) { 19589406Sbde ++bits; 19599406Sbde opt |= CD1400_COR1_STOP2; 19609406Sbde } 19616261Sjkh /* char length */ 19629406Sbde switch (cflag & CSIZE) { 19639406Sbde case CS5: 19649406Sbde bits += 5; 19659406Sbde opt |= CD1400_COR1_CS5; 19669406Sbde break; 19679406Sbde case CS6: 19689406Sbde bits += 6; 19699406Sbde opt |= CD1400_COR1_CS6; 19709406Sbde break; 19719406Sbde case CS7: 19729406Sbde bits += 7; 19739406Sbde opt |= CD1400_COR1_CS7; 19749406Sbde break; 19759406Sbde default: 19769406Sbde bits += 8; 19779406Sbde opt |= CD1400_COR1_CS8; 19789406Sbde break; 19796261Sjkh } 19809406Sbde cor_change = 0; 19819406Sbde if (opt != com->cor[0]) { 19829406Sbde cor_change |= CD1400_CCR_COR1; 198341293Sbde cd_setreg(com, CD1400_COR1, com->cor[0] = opt); 19849406Sbde } 19856261Sjkh 19866261Sjkh /* 19879406Sbde * Set receive time-out period, normally to max(one char time, 5 ms). 19889406Sbde */ 1989120512Sbde itimeout = (1000 * bits + t->c_ispeed - 1) / t->c_ispeed; 19909406Sbde#ifdef SOFT_HOTCHAR 19919406Sbde#define MIN_RTP 1 19929406Sbde#else 19939406Sbde#define MIN_RTP 5 19949406Sbde#endif 1995120512Sbde if (itimeout < MIN_RTP) 1996120512Sbde itimeout = MIN_RTP; 19979406Sbde if (!(t->c_lflag & ICANON) && t->c_cc[VMIN] != 0 && t->c_cc[VTIME] != 0 19989406Sbde && t->c_cc[VTIME] * 10 > itimeout) 19999406Sbde itimeout = t->c_cc[VTIME] * 10; 20009406Sbde if (itimeout > 255) 20019406Sbde itimeout = 255; 200241293Sbde cd_setreg(com, CD1400_RTPR, itimeout); 20039406Sbde 20049406Sbde /* 20056261Sjkh * set channel option register 2 - 20066261Sjkh * flow control 20076261Sjkh */ 20086261Sjkh opt = 0; 20096261Sjkh#ifdef Smarts 20106261Sjkh if (iflag & IXANY) 20119406Sbde opt |= CD1400_COR2_IXANY; 20126261Sjkh if (iflag & IXOFF) 20139406Sbde opt |= CD1400_COR2_IXOFF; 20146261Sjkh#endif 20159406Sbde#ifndef SOFT_CTS_OFLOW 20166261Sjkh if (cflag & CCTS_OFLOW) 20179406Sbde opt |= CD1400_COR2_CCTS_OFLOW; 20186261Sjkh#endif 201988088Sjhb critical_enter(); 202072262Sjhb COM_LOCK(); 20219406Sbde if (opt != com->cor[1]) { 20229406Sbde cor_change |= CD1400_CCR_COR2; 202341293Sbde cd_setreg(com, CD1400_COR2, com->cor[1] = opt); 20246261Sjkh } 202572262Sjhb COM_UNLOCK(); 202688088Sjhb critical_exit(); 20276261Sjkh 20286261Sjkh /* 20296261Sjkh * set channel option register 3 - 20306261Sjkh * receiver FIFO interrupt threshold 20316261Sjkh * flow control 20326261Sjkh */ 20339406Sbde opt = RxFifoThreshold; 20346261Sjkh#ifdef Smarts 20356261Sjkh if (t->c_lflag & ICANON) 20369406Sbde opt |= CD1400_COR3_SCD34; /* detect INTR & SUSP chars */ 20376261Sjkh if (iflag & IXOFF) 20389406Sbde /* detect and transparently handle START and STOP chars */ 20399406Sbde opt |= CD1400_COR3_FCT | CD1400_COR3_SCD12; 20406261Sjkh#endif 20419406Sbde if (opt != com->cor[2]) { 20429406Sbde cor_change |= CD1400_CCR_COR3; 204341293Sbde cd_setreg(com, CD1400_COR3, com->cor[2] = opt); 20446261Sjkh } 20456261Sjkh 20466261Sjkh /* notify the CD1400 if COR1-3 have changed */ 20479406Sbde if (cor_change) 204841293Sbde cd1400_channel_cmd(com, CD1400_CCR_CMDCORCHG | cor_change); 20496261Sjkh 20506261Sjkh /* 20516261Sjkh * set channel option register 4 - 20526261Sjkh * CR/NL processing 20536261Sjkh * break processing 20546261Sjkh * received exception processing 20556261Sjkh */ 20566261Sjkh opt = 0; 20576261Sjkh if (iflag & IGNCR) 20589406Sbde opt |= CD1400_COR4_IGNCR; 20596261Sjkh#ifdef Smarts 20606261Sjkh /* 20616261Sjkh * we need a new ttyinput() for this, as we don't want to 20626261Sjkh * have ICRNL && INLCR being done in both layers, or to have 20636261Sjkh * synchronisation problems 20646261Sjkh */ 20656261Sjkh if (iflag & ICRNL) 20669406Sbde opt |= CD1400_COR4_ICRNL; 20676261Sjkh if (iflag & INLCR) 20689406Sbde opt |= CD1400_COR4_INLCR; 20696261Sjkh#endif 20706261Sjkh if (iflag & IGNBRK) 207141905Sbde opt |= CD1400_COR4_IGNBRK | CD1400_COR4_NOBRKINT; 207241905Sbde /* 207341905Sbde * The `-ignbrk -brkint parmrk' case is not handled by the hardware, 207441905Sbde * so only tell the hardware about -brkint if -parmrk. 207541905Sbde */ 207641905Sbde if (!(iflag & (BRKINT | PARMRK))) 20779406Sbde opt |= CD1400_COR4_NOBRKINT; 20789406Sbde#if 0 20799406Sbde /* XXX using this "intelligence" breaks reporting of overruns. */ 20806261Sjkh if (iflag & IGNPAR) 20819406Sbde opt |= CD1400_COR4_PFO_DISCARD; 20826261Sjkh else { 20839406Sbde if (iflag & PARMRK) 20849406Sbde opt |= CD1400_COR4_PFO_ESC; 20859406Sbde else 20869406Sbde opt |= CD1400_COR4_PFO_NUL; 20879406Sbde } 20886261Sjkh#else 20899406Sbde opt |= CD1400_COR4_PFO_EXCEPTION; 20906261Sjkh#endif 209141293Sbde cd_setreg(com, CD1400_COR4, opt); 20926261Sjkh 20936261Sjkh /* 20946261Sjkh * set channel option register 5 - 20956261Sjkh */ 20966261Sjkh opt = 0; 20976261Sjkh if (iflag & ISTRIP) 20989406Sbde opt |= CD1400_COR5_ISTRIP; 20999406Sbde if (t->c_iflag & IEXTEN) 21009406Sbde /* enable LNEXT (e.g. ctrl-v quoting) handling */ 21019406Sbde opt |= CD1400_COR5_LNEXT; 21026261Sjkh#ifdef Smarts 21036261Sjkh if (t->c_oflag & ONLCR) 21049406Sbde opt |= CD1400_COR5_ONLCR; 21056261Sjkh if (t->c_oflag & OCRNL) 21069406Sbde opt |= CD1400_COR5_OCRNL; 21076261Sjkh#endif 210841293Sbde cd_setreg(com, CD1400_COR5, opt); 21096261Sjkh 21106261Sjkh /* 211120152Sbde * We always generate modem status change interrupts for CD changes. 211220152Sbde * Among other things, this is necessary to track TS_CARR_ON for 211320152Sbde * pstat to print even when the driver doesn't care. CD changes 211420152Sbde * should be rare so interrupts for them are not worth extra code to 211520152Sbde * avoid. We avoid interrupts for other modem status changes (except 211620152Sbde * for CTS changes when SOFT_CTS_OFLOW is configured) since this is 211720152Sbde * simplest and best. 21189406Sbde */ 211920152Sbde 21209406Sbde /* 21216261Sjkh * set modem change option register 1 21226261Sjkh * generate modem interrupts on which 1 -> 0 input transitions 21236261Sjkh * also controls auto-DTR output flow-control, which we don't use 21246261Sjkh */ 212520152Sbde opt = CD1400_MCOR1_CDzd; 21269406Sbde#ifdef SOFT_CTS_OFLOW 21279406Sbde if (cflag & CCTS_OFLOW) 21289406Sbde opt |= CD1400_MCOR1_CTSzd; 21299406Sbde#endif 213041293Sbde cd_setreg(com, CD1400_MCOR1, opt); 21316261Sjkh 21326261Sjkh /* 21336261Sjkh * set modem change option register 2 21346261Sjkh * generate modem interrupts on specific 0 -> 1 input transitions 21356261Sjkh */ 213620152Sbde opt = CD1400_MCOR2_CDod; 21379406Sbde#ifdef SOFT_CTS_OFLOW 21389406Sbde if (cflag & CCTS_OFLOW) 21399406Sbde opt |= CD1400_MCOR2_CTSod; 21409406Sbde#endif 214141293Sbde cd_setreg(com, CD1400_MCOR2, opt); 21426261Sjkh 21439406Sbde /* 21449406Sbde * XXX should have done this long ago, but there is too much state 21459406Sbde * to change all atomically. 21469406Sbde */ 214788088Sjhb critical_enter(); 214872262Sjhb COM_LOCK(); 21496261Sjkh 21509406Sbde com->state &= ~CS_TTGO; 21519406Sbde if (!(tp->t_state & TS_TTSTOP)) 21529406Sbde com->state |= CS_TTGO; 215319718Sbde if (cflag & CRTS_IFLOW) { 215419718Sbde com->state |= CS_RTS_IFLOW; 215519718Sbde /* 215619718Sbde * If CS_RTS_IFLOW just changed from off to on, the change 215719718Sbde * needs to be propagated to MCR_RTS. This isn't urgent, 215819718Sbde * so do it later by calling comstart() instead of repeating 215919718Sbde * a lot of code from comstart() here. 216019718Sbde */ 216119718Sbde } else if (com->state & CS_RTS_IFLOW) { 21629406Sbde com->state &= ~CS_RTS_IFLOW; 216319718Sbde /* 216419718Sbde * CS_RTS_IFLOW just changed from on to off. Force MCR_RTS 216519718Sbde * on here, since comstart() won't do it later. 216619718Sbde */ 216719718Sbde#if 0 216819718Sbde outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); 216919718Sbde#else 217041293Sbde cd_setreg(com, com->mcr_rts_reg, 217141293Sbde com->mcr_image |= com->mcr_rts); 217219718Sbde#endif 217319718Sbde } 21746261Sjkh 21759406Sbde /* 21769406Sbde * Set up state to handle output flow control. 21779406Sbde * XXX - worth handling MDMBUF (DCD) flow control at the lowest level? 21789406Sbde * Now has 10+ msec latency, while CTS flow has 50- usec latency. 21799406Sbde */ 21809406Sbde com->state |= CS_ODEVREADY; 21819406Sbde#ifdef SOFT_CTS_OFLOW 21829406Sbde com->state &= ~CS_CTS_OFLOW; 21839406Sbde if (cflag & CCTS_OFLOW) { 21849406Sbde com->state |= CS_CTS_OFLOW; 21859406Sbde if (!(com->last_modem_status & MSR_CTS)) 21869406Sbde com->state &= ~CS_ODEVREADY; 21879406Sbde } 21889406Sbde#endif 21899406Sbde /* XXX shouldn't call functions while intrs are disabled. */ 21909406Sbde disc_optim(tp, t, com); 21919406Sbde#if 0 21929406Sbde /* 21939406Sbde * Recover from fiddling with CS_TTGO. We used to call siointr1() 21949406Sbde * unconditionally, but that defeated the careful discarding of 21959406Sbde * stale input in sioopen(). 21969406Sbde */ 21979406Sbde if (com->state >= (CS_BUSY | CS_TTGO)) 21989406Sbde siointr1(com); 21999406Sbde#endif 22009406Sbde if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) { 22019406Sbde if (!(com->intr_enable & CD1400_SRER_TXRDY)) 220241293Sbde cd_setreg(com, CD1400_SRER, 220341388Sbde com->intr_enable 220443314Sdillon = (com->intr_enable & ~CD1400_SRER_TXMPTY) 220541388Sbde | CD1400_SRER_TXRDY); 22069406Sbde } else { 22079406Sbde if (com->intr_enable & CD1400_SRER_TXRDY) 220841293Sbde cd_setreg(com, CD1400_SRER, 220941388Sbde com->intr_enable 221043314Sdillon = (com->intr_enable & ~CD1400_SRER_TXRDY) 221141388Sbde | CD1400_SRER_TXMPTY); 22129406Sbde } 22136261Sjkh 221472262Sjhb COM_UNLOCK(); 221588088Sjhb critical_exit(); 22169406Sbde splx(s); 221719718Sbde comstart(tp); 221843611Sbde if (com->ibufold != NULL) { 221943611Sbde free(com->ibufold, M_DEVBUF); 222043611Sbde com->ibufold = NULL; 222143611Sbde } 22229406Sbde return (0); 22239406Sbde} 22249406Sbde 222543611Sbdestatic int 222643611Sbdesiosetwater(com, speed) 222743611Sbde struct com_s *com; 222843611Sbde speed_t speed; 222943611Sbde{ 223043611Sbde int cp4ticks; 223143611Sbde u_char *ibuf; 223243611Sbde int ibufsize; 223343611Sbde struct tty *tp; 223443611Sbde 223543611Sbde /* 223643611Sbde * Make the buffer size large enough to handle a softtty interrupt 223743611Sbde * latency of about 2 ticks without loss of throughput or data 223843611Sbde * (about 3 ticks if input flow control is not used or not honoured, 223943611Sbde * but a bit less for CS5-CS7 modes). 224043611Sbde */ 224143611Sbde cp4ticks = speed / 10 / hz * 4; 224243611Sbde for (ibufsize = 128; ibufsize < cp4ticks;) 224343611Sbde ibufsize <<= 1; 224443611Sbde if (ibufsize == com->ibufsize) { 224543611Sbde return (0); 224643611Sbde } 224743611Sbde 224843611Sbde /* 224943611Sbde * Allocate input buffer. The extra factor of 2 in the size is 225043611Sbde * to allow for an error byte for each input byte. 225143611Sbde */ 225243611Sbde ibuf = malloc(2 * ibufsize, M_DEVBUF, M_NOWAIT); 225343611Sbde if (ibuf == NULL) { 225443611Sbde return (ENOMEM); 225543611Sbde } 225643611Sbde 225743611Sbde /* Initialize non-critical variables. */ 225843611Sbde com->ibufold = com->ibuf; 225943611Sbde com->ibufsize = ibufsize; 226043611Sbde tp = com->tp; 226143611Sbde if (tp != NULL) { 226243611Sbde tp->t_ififosize = 2 * ibufsize; 226343611Sbde tp->t_ispeedwat = (speed_t)-1; 226443611Sbde tp->t_ospeedwat = (speed_t)-1; 226543611Sbde } 226643611Sbde 226743611Sbde /* 226843611Sbde * Read current input buffer, if any. Continue with interrupts 226943611Sbde * disabled. 227043611Sbde */ 227188088Sjhb critical_enter(); 227272262Sjhb COM_LOCK(); 227343611Sbde if (com->iptr != com->ibuf) 227488088Sjhb sioinput(com); 227543611Sbde 227643611Sbde /*- 227743611Sbde * Initialize critical variables, including input buffer watermarks. 227843611Sbde * The external device is asked to stop sending when the buffer 227943611Sbde * exactly reaches high water, or when the high level requests it. 228043611Sbde * The high level is notified immediately (rather than at a later 228143611Sbde * clock tick) when this watermark is reached. 228243611Sbde * The buffer size is chosen so the watermark should almost never 228343611Sbde * be reached. 228443611Sbde * The low watermark is invisibly 0 since the buffer is always 228543611Sbde * emptied all at once. 228643611Sbde */ 228743611Sbde com->iptr = com->ibuf = ibuf; 228843611Sbde com->ibufend = ibuf + ibufsize; 228943611Sbde com->ierroff = ibufsize; 229043611Sbde com->ihighwater = ibuf + 3 * ibufsize / 4; 229165557Sjasone 229272262Sjhb COM_UNLOCK(); 229388088Sjhb critical_exit(); 229443611Sbde return (0); 229543611Sbde} 229643611Sbde 22979406Sbdestatic void 22989406Sbdecomstart(tp) 22999406Sbde struct tty *tp; 23006261Sjkh{ 23019406Sbde struct com_s *com; 23026261Sjkh int s; 23039406Sbde#ifdef CyDebug 23049406Sbde bool_t started; 23059406Sbde#endif 23069406Sbde int unit; 23076261Sjkh 23089406Sbde unit = DEV_TO_UNIT(tp->t_dev); 23099406Sbde com = com_addr(unit); 23109406Sbde s = spltty(); 23119406Sbde 23126261Sjkh#ifdef CyDebug 23139406Sbde ++com->start_count; 23149406Sbde started = FALSE; 23156261Sjkh#endif 23166261Sjkh 231788088Sjhb critical_enter(); 231872262Sjhb COM_LOCK(); 23199406Sbde if (tp->t_state & TS_TTSTOP) { 23209406Sbde com->state &= ~CS_TTGO; 23219406Sbde if (com->intr_enable & CD1400_SRER_TXRDY) 232241293Sbde cd_setreg(com, CD1400_SRER, 232341388Sbde com->intr_enable 232443314Sdillon = (com->intr_enable & ~CD1400_SRER_TXRDY) 232541388Sbde | CD1400_SRER_TXMPTY); 23269406Sbde } else { 23279406Sbde com->state |= CS_TTGO; 23289406Sbde if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY) 23299406Sbde && !(com->intr_enable & CD1400_SRER_TXRDY)) 233041293Sbde cd_setreg(com, CD1400_SRER, 233141388Sbde com->intr_enable 233243314Sdillon = (com->intr_enable & ~CD1400_SRER_TXMPTY) 233341388Sbde | CD1400_SRER_TXRDY); 23349406Sbde } 23359406Sbde if (tp->t_state & TS_TBLOCK) { 233638303Sbde if (com->mcr_image & com->mcr_rts && com->state & CS_RTS_IFLOW) 23379406Sbde#if 0 23389406Sbde outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); 23399406Sbde#else 234041293Sbde cd_setreg(com, com->mcr_rts_reg, 234141293Sbde com->mcr_image &= ~com->mcr_rts); 23429406Sbde#endif 23439406Sbde } else { 234438303Sbde if (!(com->mcr_image & com->mcr_rts) 234538302Sbde && com->iptr < com->ihighwater 234619718Sbde && com->state & CS_RTS_IFLOW) 23479406Sbde#if 0 23489406Sbde outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); 23499406Sbde#else 235041293Sbde cd_setreg(com, com->mcr_rts_reg, 235141293Sbde com->mcr_image |= com->mcr_rts); 23529406Sbde#endif 23539406Sbde } 235472262Sjhb COM_UNLOCK(); 235588088Sjhb critical_exit(); 23569406Sbde if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) { 235732045Sbde ttwwakeup(tp); 23589406Sbde splx(s); 23596261Sjkh return; 23609406Sbde } 23619406Sbde if (tp->t_outq.c_cc != 0) { 23629406Sbde struct lbq *qp; 23639406Sbde struct lbq *next; 23646261Sjkh 23659406Sbde if (!com->obufs[0].l_queued) { 23669406Sbde#ifdef CyDebug 23679406Sbde started = TRUE; 23689406Sbde#endif 23699406Sbde com->obufs[0].l_tail 23709406Sbde = com->obuf1 + q_to_b(&tp->t_outq, com->obuf1, 23719406Sbde sizeof com->obuf1); 23729406Sbde com->obufs[0].l_next = NULL; 23739406Sbde com->obufs[0].l_queued = TRUE; 237488088Sjhb critical_enter(); 237572262Sjhb COM_LOCK(); 23769406Sbde if (com->state & CS_BUSY) { 23779406Sbde qp = com->obufq.l_next; 23789406Sbde while ((next = qp->l_next) != NULL) 23799406Sbde qp = next; 23809406Sbde qp->l_next = &com->obufs[0]; 23819406Sbde } else { 23829406Sbde com->obufq.l_head = com->obufs[0].l_head; 23839406Sbde com->obufq.l_tail = com->obufs[0].l_tail; 23849406Sbde com->obufq.l_next = &com->obufs[0]; 23859406Sbde com->state |= CS_BUSY; 23869406Sbde if (com->state >= (CS_BUSY | CS_TTGO 23879406Sbde | CS_ODEVREADY)) 238841293Sbde cd_setreg(com, CD1400_SRER, 238941293Sbde com->intr_enable 239043314Sdillon = (com->intr_enable 239143314Sdillon & ~CD1400_SRER_TXMPTY) 239241388Sbde | CD1400_SRER_TXRDY); 23939406Sbde } 239472262Sjhb COM_UNLOCK(); 239588088Sjhb critical_exit(); 23969406Sbde } 23979406Sbde if (tp->t_outq.c_cc != 0 && !com->obufs[1].l_queued) { 23989406Sbde#ifdef CyDebug 23999406Sbde started = TRUE; 24009406Sbde#endif 24019406Sbde com->obufs[1].l_tail 24029406Sbde = com->obuf2 + q_to_b(&tp->t_outq, com->obuf2, 24039406Sbde sizeof com->obuf2); 24049406Sbde com->obufs[1].l_next = NULL; 24059406Sbde com->obufs[1].l_queued = TRUE; 240688088Sjhb critical_enter(); 240772262Sjhb COM_LOCK(); 24089406Sbde if (com->state & CS_BUSY) { 24099406Sbde qp = com->obufq.l_next; 24109406Sbde while ((next = qp->l_next) != NULL) 24119406Sbde qp = next; 24129406Sbde qp->l_next = &com->obufs[1]; 24139406Sbde } else { 24149406Sbde com->obufq.l_head = com->obufs[1].l_head; 24159406Sbde com->obufq.l_tail = com->obufs[1].l_tail; 24169406Sbde com->obufq.l_next = &com->obufs[1]; 24179406Sbde com->state |= CS_BUSY; 24189406Sbde if (com->state >= (CS_BUSY | CS_TTGO 24199406Sbde | CS_ODEVREADY)) 242041293Sbde cd_setreg(com, CD1400_SRER, 242141293Sbde com->intr_enable 242243314Sdillon = (com->intr_enable 242343314Sdillon & ~CD1400_SRER_TXMPTY) 242441388Sbde | CD1400_SRER_TXRDY); 24259406Sbde } 242672262Sjhb COM_UNLOCK(); 242788088Sjhb critical_exit(); 24289406Sbde } 24299406Sbde tp->t_state |= TS_BUSY; 24309406Sbde } 24319406Sbde#ifdef CyDebug 24329406Sbde if (started) 24339406Sbde ++com->start_real; 24349406Sbde#endif 24359406Sbde#if 0 243688088Sjhb critical_enter(); 243772262Sjhb COM_LOCK(); 243812962Sbde if (com->state >= (CS_BUSY | CS_TTGO)) 24399406Sbde siointr1(com); /* fake interrupt to start output */ 244072262Sjhb COM_UNLOCK(); 244188088Sjhb critical_exit(); 24429406Sbde#endif 24439626Sbde ttwwakeup(tp); 24449406Sbde splx(s); 24459406Sbde} 24466261Sjkh 244712675Sjulianstatic void 244851654Sphkcomstop(tp, rw) 24499406Sbde struct tty *tp; 24509406Sbde int rw; 24519406Sbde{ 24529406Sbde struct com_s *com; 245341903Sbde bool_t wakeup_etc; 24546261Sjkh 24559754Sbde com = com_addr(DEV_TO_UNIT(tp->t_dev)); 245641903Sbde wakeup_etc = FALSE; 245788088Sjhb critical_enter(); 245872262Sjhb COM_LOCK(); 24599406Sbde if (rw & FWRITE) { 24609406Sbde com->obufs[0].l_queued = FALSE; 24619406Sbde com->obufs[1].l_queued = FALSE; 246241388Sbde if (com->extra_state & CSE_ODONE) { 246341388Sbde com_events -= LOTS_OF_EVENTS; 246441388Sbde com->extra_state &= ~CSE_ODONE; 246541903Sbde if (com->etc != ETC_NONE) { 246641903Sbde if (com->etc == ETC_BREAK_ENDED) 246741903Sbde com->etc = ETC_NONE; 246841903Sbde wakeup_etc = TRUE; 246941903Sbde } 247041388Sbde } 24719406Sbde com->tp->t_state &= ~TS_BUSY; 247241903Sbde if (com->state & CS_ODONE) 247341903Sbde com_events -= LOTS_OF_EVENTS; 247441903Sbde com->state &= ~(CS_ODONE | CS_BUSY); 24759406Sbde } 24769406Sbde if (rw & FREAD) { 247741908Sbde /* XXX no way to reset only input fifo. */ 24789406Sbde com_events -= (com->iptr - com->ibuf); 24799406Sbde com->iptr = com->ibuf; 24809406Sbde } 248172262Sjhb COM_UNLOCK(); 248288088Sjhb critical_exit(); 248341903Sbde if (wakeup_etc) 248441903Sbde wakeup(&com->etc); 248541908Sbde if (rw & FWRITE && com->etc == ETC_NONE) 248641908Sbde cd1400_channel_cmd(com, CD1400_CCR_CMDRESET | CD1400_CCR_FTF); 24879754Sbde comstart(tp); 24889406Sbde} 24899406Sbde 24909406Sbdestatic int 24919406Sbdecommctl(com, bits, how) 24929406Sbde struct com_s *com; 24939406Sbde int bits; 24949406Sbde int how; 24959406Sbde{ 24969406Sbde int mcr; 24979406Sbde int msr; 24989406Sbde 24999406Sbde if (how == DMGET) { 25009406Sbde if (com->channel_control & CD1400_CCR_RCVEN) 25019406Sbde bits |= TIOCM_LE; 25029406Sbde mcr = com->mcr_image; 250338303Sbde if (mcr & com->mcr_dtr) 25049406Sbde bits |= TIOCM_DTR; 250538303Sbde if (mcr & com->mcr_rts) 25069406Sbde /* XXX wired on for Cyclom-8Ys */ 25079406Sbde bits |= TIOCM_RTS; 250820152Sbde 250920152Sbde /* 251020152Sbde * We must read the modem status from the hardware because 251120152Sbde * we don't generate modem status change interrupts for all 251220152Sbde * changes, so com->prev_modem_status is not guaranteed to 251320152Sbde * be up to date. This is safe, unlike for sio, because 251420152Sbde * reading the status register doesn't clear pending modem 251520152Sbde * status change interrupts. 251620152Sbde */ 251741293Sbde msr = cd_getreg(com, CD1400_MSVR2); 251820152Sbde 25199406Sbde if (msr & MSR_CTS) 25209406Sbde bits |= TIOCM_CTS; 25219406Sbde if (msr & MSR_DCD) 25229406Sbde bits |= TIOCM_CD; 25239406Sbde if (msr & MSR_DSR) 25249406Sbde bits |= TIOCM_DSR; 25259406Sbde if (msr & MSR_RI) 25269406Sbde /* XXX not connected except for Cyclom-16Y? */ 25279406Sbde bits |= TIOCM_RI; 25289406Sbde return (bits); 25296261Sjkh } 25309406Sbde mcr = 0; 253138303Sbde if (bits & TIOCM_DTR) 253238303Sbde mcr |= com->mcr_dtr; 253338303Sbde if (bits & TIOCM_RTS) 253438303Sbde mcr |= com->mcr_rts; 253588088Sjhb critical_enter(); 253672262Sjhb COM_LOCK(); 25379406Sbde switch (how) { 25389406Sbde case DMSET: 25399406Sbde com->mcr_image = mcr; 254041293Sbde cd_setreg(com, CD1400_MSVR1, mcr); 254141293Sbde cd_setreg(com, CD1400_MSVR2, mcr); 25429406Sbde break; 25439406Sbde case DMBIS: 25449406Sbde com->mcr_image = mcr = com->mcr_image | mcr; 254541293Sbde cd_setreg(com, CD1400_MSVR1, mcr); 254641293Sbde cd_setreg(com, CD1400_MSVR2, mcr); 25479406Sbde break; 25489406Sbde case DMBIC: 25499406Sbde com->mcr_image = mcr = com->mcr_image & ~mcr; 255041293Sbde cd_setreg(com, CD1400_MSVR1, mcr); 255141293Sbde cd_setreg(com, CD1400_MSVR2, mcr); 25529406Sbde break; 25539406Sbde } 255472262Sjhb COM_UNLOCK(); 255588088Sjhb critical_exit(); 25569406Sbde return (0); 25579406Sbde} 25586261Sjkh 25599406Sbdestatic void 25609406Sbdesiosettimeout() 25619406Sbde{ 25629406Sbde struct com_s *com; 25639406Sbde bool_t someopen; 25649406Sbde int unit; 25656261Sjkh 25669406Sbde /* 25679406Sbde * Set our timeout period to 1 second if no polled devices are open. 25689406Sbde * Otherwise set it to max(1/200, 1/hz). 25699406Sbde * Enable timeouts iff some device is open. 25709406Sbde */ 257129677Sgibbs untimeout(comwakeup, (void *)NULL, sio_timeout_handle); 25729406Sbde sio_timeout = hz; 25739406Sbde someopen = FALSE; 25749406Sbde for (unit = 0; unit < NSIO; ++unit) { 25759406Sbde com = com_addr(unit); 25769406Sbde if (com != NULL && com->tp != NULL 25779406Sbde && com->tp->t_state & TS_ISOPEN) { 25789406Sbde someopen = TRUE; 25799406Sbde#if 0 25809406Sbde if (com->poll || com->poll_output) { 25819406Sbde sio_timeout = hz > 200 ? hz / 200 : 1; 25829406Sbde break; 25839406Sbde } 25849406Sbde#endif 25859406Sbde } 25869406Sbde } 25879406Sbde if (someopen) { 25889406Sbde sio_timeouts_until_log = hz / sio_timeout; 258929677Sgibbs sio_timeout_handle = timeout(comwakeup, (void *)NULL, 259029677Sgibbs sio_timeout); 25919406Sbde } else { 25929406Sbde /* Flush error messages, if any. */ 25939406Sbde sio_timeouts_until_log = 1; 25949406Sbde comwakeup((void *)NULL); 259529677Sgibbs untimeout(comwakeup, (void *)NULL, sio_timeout_handle); 25969406Sbde } 25979406Sbde} 25986261Sjkh 25999406Sbdestatic void 26009406Sbdecomwakeup(chan) 26019406Sbde void *chan; 26026261Sjkh{ 26039406Sbde struct com_s *com; 26049406Sbde int unit; 26056261Sjkh 260629677Sgibbs sio_timeout_handle = timeout(comwakeup, (void *)NULL, sio_timeout); 26076261Sjkh 26089406Sbde#if 0 26099406Sbde /* 26109406Sbde * Recover from lost output interrupts. 26119406Sbde * Poll any lines that don't use interrupts. 26129406Sbde */ 26139406Sbde for (unit = 0; unit < NSIO; ++unit) { 26149406Sbde com = com_addr(unit); 26159406Sbde if (com != NULL 26169406Sbde && (com->state >= (CS_BUSY | CS_TTGO) || com->poll)) { 261788088Sjhb critical_enter(); 261872262Sjhb COM_LOCK(); 26199406Sbde siointr1(com); 262072262Sjhb COM_UNLOCK(); 262188088Sjhb critical_exit(); 26229406Sbde } 26239406Sbde } 26249406Sbde#endif 26256261Sjkh 26269406Sbde /* 26279406Sbde * Check for and log errors, but not too often. 26289406Sbde */ 26299406Sbde if (--sio_timeouts_until_log > 0) 26309406Sbde return; 26319406Sbde sio_timeouts_until_log = hz / sio_timeout; 26329406Sbde for (unit = 0; unit < NSIO; ++unit) { 26339406Sbde int errnum; 26346261Sjkh 26359406Sbde com = com_addr(unit); 26369406Sbde if (com == NULL) 26379406Sbde continue; 26389406Sbde for (errnum = 0; errnum < CE_NTYPES; ++errnum) { 26399406Sbde u_int delta; 26409406Sbde u_long total; 26416261Sjkh 264288088Sjhb critical_enter(); 264372262Sjhb COM_LOCK(); 26449406Sbde delta = com->delta_error_counts[errnum]; 26459406Sbde com->delta_error_counts[errnum] = 0; 264672262Sjhb COM_UNLOCK(); 264788088Sjhb critical_exit(); 26489406Sbde if (delta == 0) 26499406Sbde continue; 26509406Sbde total = com->error_counts[errnum] += delta; 26519406Sbde log(LOG_ERR, "cy%d: %u more %s%s (total %lu)\n", 26529406Sbde unit, delta, error_desc[errnum], 26539406Sbde delta == 1 ? "" : "s", total); 26549406Sbde } 26559406Sbde } 26566261Sjkh} 26576261Sjkh 26589406Sbdestatic void 26599406Sbdedisc_optim(tp, t, com) 26609406Sbde struct tty *tp; 26619406Sbde struct termios *t; 26629406Sbde struct com_s *com; 26636712Spst{ 26649406Sbde#ifndef SOFT_HOTCHAR 26659406Sbde u_char opt; 26669406Sbde#endif 26676261Sjkh 26689406Sbde /* 26699406Sbde * XXX can skip a lot more cases if Smarts. Maybe 26709757Sbde * (IGNCR | ISTRIP | IXON) in c_iflag. But perhaps we 26719406Sbde * shouldn't skip if (TS_CNTTB | TS_LNCH) is set in t_state. 26729406Sbde */ 26739757Sbde if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON)) 26749406Sbde && (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK)) 26759757Sbde && (!(t->c_iflag & PARMRK) 26769757Sbde || (t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK)) 26779757Sbde && !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN)) 26789406Sbde && linesw[tp->t_line].l_rint == ttyinput) 26799406Sbde tp->t_state |= TS_CAN_BYPASS_L_RINT; 26809406Sbde else 26819406Sbde tp->t_state &= ~TS_CAN_BYPASS_L_RINT; 268233322Sphk com->hotchar = linesw[tp->t_line].l_hotchar; 26839406Sbde#ifndef SOFT_HOTCHAR 26849406Sbde opt = com->cor[2] & ~CD1400_COR3_SCD34; 26859406Sbde if (com->hotchar != 0) { 268641293Sbde cd_setreg(com, CD1400_SCHR3, com->hotchar); 268741293Sbde cd_setreg(com, CD1400_SCHR4, com->hotchar); 26889406Sbde opt |= CD1400_COR3_SCD34; 26899406Sbde } 26909406Sbde if (opt != com->cor[2]) { 269141293Sbde cd_setreg(com, CD1400_COR3, com->cor[2] = opt); 269241293Sbde cd1400_channel_cmd(com, CD1400_CCR_CMDCORCHG | CD1400_CCR_COR3); 26939406Sbde } 26949406Sbde#endif 26956712Spst} 26966712Spst 26979406Sbde#ifdef Smarts 26989406Sbde/* standard line discipline input routine */ 26996261Sjkhint 27009406Sbdecyinput(c, tp) 27019406Sbde int c; 27029406Sbde struct tty *tp; 27036261Sjkh{ 27049406Sbde /* XXX duplicate ttyinput(), but without the IXOFF/IXON/ISTRIP/IPARMRK 27059406Sbde * bits, as they are done by the CD1400. Hardly worth the effort, 27069406Sbde * given that high-throughput sessions are raw anyhow. 27079406Sbde */ 27089406Sbde} 27099406Sbde#endif /* Smarts */ 27106261Sjkh 27119406Sbdestatic int 271238303Sbdecomspeed(speed, cy_clock, prescaler_io) 27139406Sbde speed_t speed; 271438303Sbde u_long cy_clock; 27159406Sbde int *prescaler_io; 27169406Sbde{ 27179406Sbde int actual; 27189406Sbde int error; 27199406Sbde int divider; 27209406Sbde int prescaler; 27219406Sbde int prescaler_unit; 27226261Sjkh 27239406Sbde if (speed == 0) 27249406Sbde return (0); 27259406Sbde if (speed < 0 || speed > 150000) 27269406Sbde return (-1); 27276261Sjkh 27289406Sbde /* determine which prescaler to use */ 27299406Sbde for (prescaler_unit = 4, prescaler = 2048; prescaler_unit; 27309406Sbde prescaler_unit--, prescaler >>= 2) { 273138303Sbde if (cy_clock / prescaler / speed > 63) 27329406Sbde break; 27339406Sbde } 27346261Sjkh 273538303Sbde divider = (cy_clock / prescaler * 2 / speed + 1) / 2; /* round off */ 27369406Sbde if (divider > 255) 27379406Sbde divider = 255; 273838303Sbde actual = cy_clock/prescaler/divider; 27396261Sjkh 274037959Sbde /* 10 times error in percent: */ 274137959Sbde error = ((actual - (long)speed) * 2000 / (long)speed + 1) / 2; 274237959Sbde 27439406Sbde /* 3.0% max error tolerance */ 27449406Sbde if (error < -30 || error > 30) 27459406Sbde return (-1); 27466261Sjkh 27476261Sjkh#if 0 27489406Sbde printf("prescaler = %d (%d)\n", prescaler, prescaler_unit); 27499406Sbde printf("divider = %d (%x)\n", divider, divider); 27509406Sbde printf("actual = %d\n", actual); 27519406Sbde printf("error = %d\n", error); 27526261Sjkh#endif 27536261Sjkh 27549406Sbde *prescaler_io = prescaler_unit; 27559406Sbde return (divider); 27569406Sbde} 27576261Sjkh 27586261Sjkhstatic void 275941293Sbdecd1400_channel_cmd(com, cmd) 276041293Sbde struct com_s *com; 276141293Sbde int cmd; 27626261Sjkh{ 276341940Sbde cd1400_channel_cmd_wait(com); 276441940Sbde cd_setreg(com, CD1400_CCR, cmd); 276541940Sbde cd1400_channel_cmd_wait(com); 276641940Sbde} 27676261Sjkh 276841940Sbdestatic void 276941940Sbdecd1400_channel_cmd_wait(com) 277041940Sbde struct com_s *com; 277141940Sbde{ 277241940Sbde struct timeval start; 277341940Sbde struct timeval tv; 277441940Sbde long usec; 27758876Srgrimes 277641940Sbde if (cd_getreg(com, CD1400_CCR) == 0) 277741940Sbde return; 277841940Sbde microtime(&start); 277941940Sbde for (;;) { 278041940Sbde if (cd_getreg(com, CD1400_CCR) == 0) 278141940Sbde return; 278241940Sbde microtime(&tv); 278341940Sbde usec = 1000000 * (tv.tv_sec - start.tv_sec) + 278441940Sbde tv.tv_usec - start.tv_usec; 278541940Sbde if (usec >= 5000) { 278641940Sbde log(LOG_ERR, 278741940Sbde "cy%d: channel command timeout (%ld usec)\n", 278841940Sbde com->unit, usec); 278941940Sbde return; 279041940Sbde } 279141940Sbde } 27929406Sbde} 27936261Sjkh 279441903Sbdestatic void 279541903Sbdecd_etc(com, etc) 279641903Sbde struct com_s *com; 279741903Sbde int etc; 279841903Sbde{ 279965557Sjasone 280041903Sbde /* 280141903Sbde * We can't change the hardware's ETC state while there are any 280241903Sbde * characters in the tx fifo, since those characters would be 280341903Sbde * interpreted as commands! Unputting characters from the fifo 280441903Sbde * is difficult, so we wait up to 12 character times for the fifo 280541903Sbde * to drain. The command will be delayed for up to 2 character 280641903Sbde * times for the tx to become empty. Unputting characters from 280741903Sbde * the tx holding and shift registers is impossible, so we wait 280841903Sbde * for the tx to become empty so that the command is sure to be 280941903Sbde * executed soon after we issue it. 281041903Sbde */ 281188088Sjhb critical_enter(); 281272262Sjhb COM_LOCK(); 281365557Sjasone if (com->etc == etc) 281441903Sbde goto wait; 281543314Sdillon if ((etc == CD1400_ETC_SENDBREAK 281641903Sbde && (com->etc == ETC_BREAK_STARTING 281743314Sdillon || com->etc == ETC_BREAK_STARTED)) 281843314Sdillon || (etc == CD1400_ETC_STOPBREAK 281941903Sbde && (com->etc == ETC_BREAK_ENDING || com->etc == ETC_BREAK_ENDED 282043314Sdillon || com->etc == ETC_NONE))) { 282172262Sjhb COM_UNLOCK(); 282288088Sjhb critical_exit(); 282341903Sbde return; 282441903Sbde } 282541903Sbde com->etc = etc; 282641903Sbde cd_setreg(com, CD1400_SRER, 282741903Sbde com->intr_enable 282843314Sdillon = (com->intr_enable & ~CD1400_SRER_TXRDY) | CD1400_SRER_TXMPTY); 282941903Sbdewait: 283072262Sjhb COM_UNLOCK(); 283188088Sjhb critical_exit(); 283241903Sbde while (com->etc == etc 283341903Sbde && tsleep(&com->etc, TTIPRI | PCATCH, "cyetc", 0) == 0) 283441903Sbde continue; 283541903Sbde} 283641903Sbde 283741293Sbdestatic int 283841293Sbdecd_getreg(com, reg) 283941293Sbde struct com_s *com; 284041293Sbde int reg; 284141293Sbde{ 284241293Sbde struct com_s *basecom; 284341293Sbde u_char car; 284441293Sbde int cy_align; 284574903Sjhb register_t eflags; 284641293Sbde cy_addr iobase; 2847120504Sbde#ifdef SMP 2848120504Sbde int need_unlock; 2849120504Sbde#endif 285041293Sbde int val; 285141293Sbde 285241293Sbde basecom = com_addr(com->unit & ~(CD1400_NO_OF_CHANNELS - 1)); 285341293Sbde car = com->unit & CD1400_CAR_CHAN; 285441293Sbde cy_align = com->cy_align; 285541293Sbde iobase = com->iobase; 285674903Sjhb eflags = read_eflags(); 285788088Sjhb critical_enter(); 2858120504Sbde#ifdef SMP 2859120504Sbde need_unlock = 0; 2860122771Sbde if (!mtx_owned(&sio_lock)) { 286172262Sjhb COM_LOCK(); 2862120504Sbde need_unlock = 1; 2863120504Sbde } 2864120504Sbde#endif 286541293Sbde if (basecom->car != car) 286641293Sbde cd_outb(iobase, CD1400_CAR, cy_align, basecom->car = car); 286741293Sbde val = cd_inb(iobase, reg, cy_align); 2868120504Sbde#ifdef SMP 2869120504Sbde if (need_unlock) 287072262Sjhb COM_UNLOCK(); 2871120504Sbde#endif 287288088Sjhb critical_exit(); 287341293Sbde return (val); 287441293Sbde} 287541293Sbde 287641293Sbdestatic void 287741293Sbdecd_setreg(com, reg, val) 287841293Sbde struct com_s *com; 287941293Sbde int reg; 288041293Sbde int val; 288141293Sbde{ 288241293Sbde struct com_s *basecom; 288341293Sbde u_char car; 288441293Sbde int cy_align; 288574903Sjhb register_t eflags; 288641293Sbde cy_addr iobase; 2887120504Sbde#ifdef SMP 2888120504Sbde int need_unlock; 2889120504Sbde#endif 289041293Sbde 289141293Sbde basecom = com_addr(com->unit & ~(CD1400_NO_OF_CHANNELS - 1)); 289241293Sbde car = com->unit & CD1400_CAR_CHAN; 289341293Sbde cy_align = com->cy_align; 289441293Sbde iobase = com->iobase; 289574903Sjhb eflags = read_eflags(); 289688088Sjhb critical_enter(); 2897120504Sbde#ifdef SMP 2898120504Sbde need_unlock = 0; 2899122771Sbde if (!mtx_owned(&sio_lock)) { 290072262Sjhb COM_LOCK(); 2901120504Sbde need_unlock = 1; 2902120504Sbde } 2903120504Sbde#endif 290441293Sbde if (basecom->car != car) 290541293Sbde cd_outb(iobase, CD1400_CAR, cy_align, basecom->car = car); 290641293Sbde cd_outb(iobase, reg, cy_align, val); 2907120504Sbde#ifdef SMP 2908120504Sbde if (need_unlock) 290972262Sjhb COM_UNLOCK(); 2910120504Sbde#endif 291188088Sjhb critical_exit(); 291241293Sbde} 291341293Sbde 29146261Sjkh#ifdef CyDebug 29156261Sjkh/* useful in ddb */ 29166261Sjkhvoid 29179406Sbdecystatus(unit) 29189406Sbde int unit; 29196261Sjkh{ 29209406Sbde struct com_s *com; 29219406Sbde cy_addr iobase; 29229406Sbde u_int ocount; 29239406Sbde struct tty *tp; 29246261Sjkh 29259406Sbde com = com_addr(unit); 29266261Sjkh printf("info for channel %d\n", unit); 29276261Sjkh printf("------------------\n"); 29289406Sbde printf("total cyclom service probes:\t%d\n", cy_svrr_probes); 29299406Sbde printf("calls to upper layer:\t\t%d\n", cy_timeouts); 29309406Sbde if (com == NULL) 29319406Sbde return; 29329406Sbde iobase = com->iobase; 29336261Sjkh printf("\n"); 29349406Sbde printf("cd1400 base address:\\tt%p\n", iobase); 29359406Sbde printf("saved channel_control:\t\t0x%02x\n", com->channel_control); 29369406Sbde printf("saved cor1-3:\t\t\t0x%02x 0x%02x 0x%02x\n", 29379406Sbde com->cor[0], com->cor[1], com->cor[2]); 29389406Sbde printf("service request enable reg:\t0x%02x (0x%02x cached)\n", 293941293Sbde cd_getreg(com, CD1400_SRER), com->intr_enable); 29409406Sbde printf("service request register:\t0x%02x\n", 294118925Sdg cd_inb(iobase, CD1400_SVRR, com->cy_align)); 29429406Sbde printf("modem status:\t\t\t0x%02x (0x%02x cached)\n", 294341293Sbde cd_getreg(com, CD1400_MSVR2), com->prev_modem_status); 29449406Sbde printf("rx/tx/mdm interrupt registers:\t0x%02x 0x%02x 0x%02x\n", 294518925Sdg cd_inb(iobase, CD1400_RIR, com->cy_align), 294618925Sdg cd_inb(iobase, CD1400_TIR, com->cy_align), 294718925Sdg cd_inb(iobase, CD1400_MIR, com->cy_align)); 29486261Sjkh printf("\n"); 29499406Sbde printf("com state:\t\t\t0x%02x\n", com->state); 29509406Sbde printf("calls to comstart():\t\t%d (%d useful)\n", 29519406Sbde com->start_count, com->start_real); 29529406Sbde printf("rx buffer chars free:\t\t%d\n", com->iptr - com->ibuf); 29539406Sbde ocount = 0; 29549406Sbde if (com->obufs[0].l_queued) 29559406Sbde ocount += com->obufs[0].l_tail - com->obufs[0].l_head; 29569406Sbde if (com->obufs[1].l_queued) 29579406Sbde ocount += com->obufs[1].l_tail - com->obufs[1].l_head; 29589406Sbde printf("tx buffer chars:\t\t%u\n", ocount); 29599406Sbde printf("received chars:\t\t\t%d\n", com->bytes_in); 29609406Sbde printf("received exceptions:\t\t%d\n", com->recv_exception); 29619406Sbde printf("modem signal deltas:\t\t%d\n", com->mdm); 29629406Sbde printf("transmitted chars:\t\t%d\n", com->bytes_out); 29639406Sbde printf("\n"); 29649406Sbde tp = com->tp; 29659406Sbde if (tp != NULL) { 29669406Sbde printf("tty state:\t\t\t0x%08x\n", tp->t_state); 296729047Sbde printf( 296829047Sbde "upper layer queue lengths:\t%d raw, %d canon, %d output\n", 29699406Sbde tp->t_rawq.c_cc, tp->t_canq.c_cc, tp->t_outq.c_cc); 29709406Sbde } else 29716261Sjkh printf("tty state:\t\t\tclosed\n"); 29729406Sbde} 29739406Sbde#endif /* CyDebug */ 2974