sio.c revision 45267
118334Speter/*- 218334Speter * Copyright (c) 1991 The Regents of the University of California. 352561Sobrien * All rights reserved. 418334Speter * 518334Speter * Redistribution and use in source and binary forms, with or without 618334Speter * modification, are permitted provided that the following conditions 718334Speter * are met: 818334Speter * 1. Redistributions of source code must retain the above copyright 918334Speter * notice, this list of conditions and the following disclaimer. 1018334Speter * 2. Redistributions in binary form must reproduce the above copyright 1118334Speter * notice, this list of conditions and the following disclaimer in the 1218334Speter * documentation and/or other materials provided with the distribution. 1318334Speter * 3. All advertising materials mentioning features or use of this software 1418334Speter * must display the following acknowledgement: 1518334Speter * This product includes software developed by the University of 1618334Speter * California, Berkeley and its contributors. 1718334Speter * 4. Neither the name of the University nor the names of its contributors 1818334Speter * may be used to endorse or promote products derived from this software 1918334Speter * without specific prior written permission. 2018334Speter * 2118334Speter * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2218334Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2318334Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2418334Speter * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2518334Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2618334Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2718334Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2818334Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2952561Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3052561Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3118334Speter * SUCH DAMAGE. 3218334Speter * 3318334Speter * from: @(#)com.c 7.5 (Berkeley) 5/16/91 3418334Speter * $Id: sio.c,v 1.84 1999/04/01 13:44:15 kato Exp $ 3518334Speter */ 3650600Sobrien 3750600Sobrien#include "opt_comconsole.h" 3850600Sobrien#include "opt_compat.h" 3950600Sobrien#include "opt_ddb.h" 4050600Sobrien#include "opt_devfs.h" 4150600Sobrien#include "opt_sio.h" 4250600Sobrien#include "sio.h" 4350600Sobrien#include "pnp.h" 4450600Sobrien 4550600Sobrien/* 4650600Sobrien * Serial driver, based on 386BSD-0.1 com driver. 4718334Speter * Mostly rewritten to use pseudo-DMA. 4818334Speter * Works for National Semiconductor NS8250-NS16550AF UARTs. 4918334Speter * COM driver, based on HP dca driver. 5018334Speter * 5118334Speter * Changes for PC-Card integration: 5218334Speter * - Added PC-Card driver table and handlers 5318334Speter */ 5418334Speter/*=============================================================== 5518334Speter * 386BSD(98),FreeBSD-1.1x(98) com driver. 5618334Speter * ----- 5718334Speter * modified for PC9801 by M.Ishii 5850600Sobrien * Kyoto University Microcomputer Club (KMC) 5918334Speter * Chou "TEFUTEFU" Hirotomi 6018334Speter * Kyoto Univ. the faculty of medicine 6118334Speter *=============================================================== 6250600Sobrien * FreeBSD-2.0.1(98) sio driver. 6350600Sobrien * ----- 6450600Sobrien * modified for pc98 Internal i8251 and MICRO CORE MC16550II 6550600Sobrien * T.Koike(hfc01340@niftyserve.or.jp) 6650600Sobrien * implement kernel device configuration 6750600Sobrien * aizu@orient.center.nitech.ac.jp 6850600Sobrien * 6918334Speter * Notes. 7018334Speter * ----- 7118334Speter * PC98 localization based on 386BSD(98) com driver. Using its PC98 local 7218334Speter * functions. 7318334Speter * This driver is under debugging,has bugs. 7418334Speter * 7518334Speter * 1) config 7618334Speter * options COM_MULTIPORT #if using MC16550II 7752561Sobrien * device sio0 at nec? port 0x30 tty irq 4 #internal 7852561Sobrien * device sio1 at nec? port 0xd2 tty irq 5 flags 0x101 #mc1 7952561Sobrien * device sio2 at nec? port 0x8d2 tty flags 0x101 #mc2 8052561Sobrien * # ~~~~~iobase ~~multi port flag 8118334Speter * # ~ master device is sio1 8218334Speter * 2) device 8318334Speter * cd /dev; MAKEDEV ttyd0 ttyd1 .. 8418334Speter * 3) /etc/rc.serial 8518334Speter * 57600bps is too fast for sio0(internal8251) 8618334Speter * my ex. 8718334Speter * #set default speed 9600 8818334Speter * modem() 8918334Speter * : 9018334Speter * stty </dev/ttyid$i crtscts 9600 9118334Speter * : # ~~~~ default speed(can change after init.) 9218334Speter * modem 0 1 2 9318334Speter * 4) COMCONSOLE 9418334Speter * not changed. 9518334Speter * 5) PC9861K,PIO9032B,B98_01 9618334Speter * not tested. 9718334Speter */ 9818334Speter/* 9952561Sobrien * modified for AIWA B98-01 10018334Speter * by T.Hatanou <hatanou@yasuda.comm.waseda.ac.jp> last update: 15 Sep.1995 10118334Speter * 10252561Sobrien * How to configure... 10318334Speter * # options COM_MULTIPORT # support for MICROCORE MC16550II 10418334Speter * ... comment-out this line, which will conflict with B98_01. 10518334Speter * options "B98_01" # support for AIWA B98-01 10618334Speter * device sio1 at nec? port 0x00d1 tty irq ? 10718334Speter * device sio2 at nec? port 0x00d5 tty irq ? 10818334Speter * ... you can leave these lines `irq ?', irq will be autodetected. 10918334Speter */ 11018334Speter/* 11118334Speter * Modified by Y.Takahashi of Kogakuin University. 11218334Speter */ 11318334Speter 11418334Speter#ifdef PC98 11518334Speter#define COM_IF_INTERNAL 0x00 11618334Speter#define COM_IF_PC9861K_1 0x01 11718334Speter#define COM_IF_PC9861K_2 0x02 11818334Speter#define COM_IF_IND_SS_1 0x03 11918334Speter#define COM_IF_IND_SS_2 0x04 12018334Speter#define COM_IF_PIO9032B_1 0x05 12118334Speter#define COM_IF_PIO9032B_2 0x06 12218334Speter#define COM_IF_B98_01_1 0x07 12318334Speter#define COM_IF_B98_01_2 0x08 12418334Speter#define COM_IF_END1 COM_IF_B98_01_2 12518334Speter#define COM_IF_RSA98 0x10 /* same as COM_IF_NS16550 */ 12618334Speter#define COM_IF_NS16550 0x11 12718334Speter#define COM_IF_SECOND_CCU 0x12 /* same as COM_IF_NS16550 */ 12818334Speter#define COM_IF_MC16550II 0x13 12918334Speter#define COM_IF_MCRS98 0x14 /* same as COM_IF_MC16550II */ 13018334Speter#define COM_IF_RSB3000 0x15 13118334Speter#define COM_IF_RSB384 0x16 13218334Speter#define COM_IF_MODEM_CARD 0x17 /* same as COM_IF_NS16550 */ 13318334Speter#define COM_IF_RSA98III 0x18 13418334Speter#define COM_IF_ESP98 0x19 13518334Speter#define COM_IF_END2 COM_IF_ESP98 13618334Speter#endif /* PC98 */ 13718334Speter 13818334Speter#include <sys/param.h> 13918334Speter#include <sys/systm.h> 14018334Speter#include <sys/reboot.h> 14118334Speter#include <sys/malloc.h> 14218334Speter#include <sys/tty.h> 14318334Speter#include <sys/proc.h> 14418334Speter#include <sys/conf.h> 14518334Speter#include <sys/dkstat.h> 14618334Speter#include <sys/fcntl.h> 14718334Speter#include <sys/interrupt.h> 14818334Speter#include <sys/kernel.h> 14918334Speter#include <sys/syslog.h> 15018334Speter#include <sys/sysctl.h> 15118334Speter#ifdef DEVFS 15218334Speter#include <sys/devfsext.h> 15352561Sobrien#endif 15418334Speter#include <sys/timepps.h> 15518334Speter 15652561Sobrien#include <machine/clock.h> 15718334Speter#include <machine/ipl.h> 15818334Speter#ifndef SMP 15918334Speter#include <machine/lock.h> 16018334Speter#endif 16118334Speter 16218334Speter#ifdef PC98 16318334Speter#include <pc98/pc98/pc98.h> 16418334Speter#include <pc98/pc98/pc98_machdep.h> 16550600Sobrien#include <i386/isa/icu.h> 16618334Speter#include <i386/isa/ic/i8251.h> 16718334Speter#else 16818334Speter#include <i386/isa/isa.h> 16918334Speter#endif 17018334Speter#include <i386/isa/isa_device.h> 17118334Speter#include <i386/isa/sioreg.h> 17218334Speter#include <i386/isa/intr_machdep.h> 17318334Speter 17418334Speter#ifdef COM_ESP 17518334Speter#include <i386/isa/ic/esp.h> 17618334Speter#endif 17718334Speter#include <i386/isa/ic/ns16550.h> 17818334Speter#ifdef PC98 17950600Sobrien#include <i386/isa/ic/rsa.h> 18050600Sobrien#endif 18118334Speter 18218334Speter#include "card.h" 18350600Sobrien#if NCARD > 0 18418334Speter#include <sys/module.h> 18518334Speter#include <pccard/cardinfo.h> 18618334Speter#include <pccard/slot.h> 18718334Speter#endif 18818334Speter 18918334Speter#if NPNP > 0 19018334Speter#include <i386/isa/pnp.h> 19118334Speter#endif 19218334Speter 19318334Speter#ifdef SMP 19418334Speter#define disable_intr() COM_DISABLE_INTR() 19518334Speter#define enable_intr() COM_ENABLE_INTR() 19618334Speter#endif /* SMP */ 19718334Speter 19818334Speter#ifndef EXTRA_SIO 19918334Speter#if NPNP > 0 20018334Speter#define EXTRA_SIO MAX_PNP_CARDS 20118334Speter#else 20218334Speter#define EXTRA_SIO 0 20318334Speter#endif 20418334Speter#endif 20518334Speter 20618334Speter#define NSIOTOT (NSIO + EXTRA_SIO) 20718334Speter 20818334Speter#define LOTS_OF_EVENTS 64 /* helps separate urgent events from input */ 20918334Speter 21018334Speter#define CALLOUT_MASK 0x80 21118334Speter#define CONTROL_MASK 0x60 21218334Speter#define CONTROL_INIT_STATE 0x20 21350600Sobrien#define CONTROL_LOCK_STATE 0x40 21418334Speter#define DEV_TO_UNIT(dev) (MINOR_TO_UNIT(minor(dev))) 21518334Speter#define MINOR_MAGIC_MASK (CALLOUT_MASK | CONTROL_MASK) 21618334Speter#define MINOR_TO_UNIT(mynor) ((mynor) & ~MINOR_MAGIC_MASK) 21718334Speter 21818334Speter#ifdef COM_MULTIPORT 21918334Speter/* checks in flags for multiport and which is multiport "master chip" 22018334Speter * for a given card 22118334Speter */ 22218334Speter#define COM_ISMULTIPORT(dev) ((dev)->id_flags & 0x01) 22318334Speter#define COM_MPMASTER(dev) (((dev)->id_flags >> 8) & 0x0ff) 22418334Speter#define COM_NOTAST4(dev) ((dev)->id_flags & 0x04) 22518334Speter#endif /* COM_MULTIPORT */ 22618334Speter 22718334Speter#define COM_CONSOLE(dev) ((dev)->id_flags & 0x10) 22818334Speter#define COM_FORCECONSOLE(dev) ((dev)->id_flags & 0x20) 22918334Speter#define COM_LLCONSOLE(dev) ((dev)->id_flags & 0x40) 23018334Speter#define COM_LOSESOUTINTS(dev) ((dev)->id_flags & 0x08) 23118334Speter#define COM_NOFIFO(dev) ((dev)->id_flags & 0x02) 23218334Speter#define COM_ST16650A(dev) ((dev)->id_flags & 0x20000) 23318334Speter#define COM_C_NOPROBE (0x40000) 23418334Speter#define COM_NOPROBE(dev) ((dev)->id_flags & COM_C_NOPROBE) 23518334Speter#define COM_C_IIR_TXRDYBUG (0x80000) 23618334Speter#define COM_IIR_TXRDYBUG(dev) ((dev)->id_flags & COM_C_IIR_TXRDYBUG) 23718334Speter#define COM_FIFOSIZE(dev) (((dev)->id_flags & 0xff000000) >> 24) 23818334Speter 23918334Speter#ifdef PC98 24018334Speter#define com_emr com_msr /* Extension mode register for RSB-2000/3000 */ 24118334Speter#else 24218334Speter#define com_scr 7 /* scratch register for 16450-16550 (R/W) */ 24350600Sobrien#endif 24418334Speter 24518334Speter/* 24618334Speter * com state bits. 24718334Speter * (CS_BUSY | CS_TTGO) and (CS_BUSY | CS_TTGO | CS_ODEVREADY) must be higher 24818334Speter * than the other bits so that they can be tested as a group without masking 24918334Speter * off the low bits. 25018334Speter * 25118334Speter * The following com and tty flags correspond closely: 25218334Speter * CS_BUSY = TS_BUSY (maintained by comstart(), siopoll() and 25318334Speter * siostop()) 25418334Speter * CS_TTGO = ~TS_TTSTOP (maintained by comparam() and comstart()) 25518334Speter * CS_CTS_OFLOW = CCTS_OFLOW (maintained by comparam()) 25618334Speter * CS_RTS_IFLOW = CRTS_IFLOW (maintained by comparam()) 25718334Speter * TS_FLUSH is not used. 25818334Speter * XXX I think TIOCSETA doesn't clear TS_TTSTOP when it clears IXON. 25918334Speter * XXX CS_*FLOW should be CF_*FLOW in com->flags (control flags not state). 26018334Speter */ 26118334Speter#define CS_BUSY 0x80 /* output in progress */ 26218334Speter#define CS_TTGO 0x40 /* output not stopped by XOFF */ 26318334Speter#define CS_ODEVREADY 0x20 /* external device h/w ready (CTS) */ 26418334Speter#define CS_CHECKMSR 1 /* check of MSR scheduled */ 26518334Speter#define CS_CTS_OFLOW 2 /* use CTS output flow control */ 26618334Speter#define CS_DTR_OFF 0x10 /* DTR held off */ 26718334Speter#define CS_ODONE 4 /* output completed */ 26818334Speter#define CS_RTS_IFLOW 8 /* use RTS input flow control */ 26918334Speter#define CSE_BUSYCHECK 1 /* siobusycheck() scheduled */ 27018334Speter 27118334Speterstatic char const * const error_desc[] = { 27218334Speter#define CE_OVERRUN 0 27318334Speter "silo overflow", 27450600Sobrien#define CE_INTERRUPT_BUF_OVERFLOW 1 27518334Speter "interrupt-level buffer overflow", 27618334Speter#define CE_TTY_BUF_OVERFLOW 2 27718334Speter "tty-level buffer overflow", 27818334Speter}; 27918334Speter 28018334Speter#define CE_NTYPES 3 28118334Speter#define CE_RECORD(com, errnum) (++(com)->delta_error_counts[errnum]) 28218334Speter 28318334Speter/* types. XXX - should be elsewhere */ 28418334Spetertypedef u_int Port_t; /* hardware port */ 28518334Spetertypedef u_char bool_t; /* boolean */ 28618334Speter 28718334Speter/* queue of linear buffers */ 28818334Speterstruct lbq { 28918334Speter u_char *l_head; /* next char to process */ 29018334Speter u_char *l_tail; /* one past the last char to process */ 29118334Speter struct lbq *l_next; /* next in queue */ 29218334Speter bool_t l_queued; /* nonzero if queued */ 29318334Speter}; 29450600Sobrien 29518334Speter/* com device structure */ 29618334Speterstruct com_s { 29718334Speter u_int id_flags; /* Copy isa device falgas */ 29818334Speter u_char state; /* miscellaneous flag bits */ 29918334Speter bool_t active_out; /* nonzero if the callout device is open */ 30018334Speter u_char cfcr_image; /* copy of value written to CFCR */ 30118334Speter#ifdef COM_ESP 30218334Speter bool_t esp; /* is this unit a hayes esp board? */ 30318334Speter#endif 30418334Speter u_char extra_state; /* more flag bits, separate for order trick */ 30518334Speter u_char fifo_image; /* copy of value written to FIFO */ 30618334Speter bool_t hasfifo; /* nonzero for 16550 UARTs */ 30718334Speter bool_t st16650a; /* Is a Startech 16650A or RTS/CTS compat */ 30818334Speter bool_t loses_outints; /* nonzero if device loses output interrupts */ 30918334Speter u_char mcr_image; /* copy of value written to MCR */ 31018334Speter#ifdef COM_MULTIPORT 31118334Speter bool_t multiport; /* is this unit part of a multiport device? */ 31218334Speter#endif /* COM_MULTIPORT */ 31350600Sobrien bool_t no_irq; /* nonzero if irq is not attached */ 31418334Speter bool_t gone; /* hardware disappeared */ 31518334Speter bool_t poll; /* nonzero if polling is required */ 31618334Speter bool_t poll_output; /* nonzero if polling for output is required */ 31718334Speter int unit; /* unit number */ 31818334Speter int dtr_wait; /* time to hold DTR down on close (* 1/hz) */ 31918334Speter u_int tx_fifo_size; 32018334Speter u_int wopeners; /* # processes waiting for DCD in open() */ 32118334Speter 32218334Speter /* 32318334Speter * The high level of the driver never reads status registers directly 32418334Speter * because there would be too many side effects to handle conveniently. 32518334Speter * Instead, it reads copies of the registers stored here by the 32618334Speter * interrupt handler. 32718334Speter */ 32818334Speter u_char last_modem_status; /* last MSR read by intr handler */ 32918334Speter u_char prev_modem_status; /* last MSR handled by high level */ 33018334Speter 33118334Speter u_char hotchar; /* ldisc-specific char to be handled ASAP */ 33218334Speter u_char *ibuf; /* start of input buffer */ 33318334Speter u_char *ibufend; /* end of input buffer */ 33418334Speter u_char *ibufold; /* old input buffer, to be freed */ 33518334Speter u_char *ihighwater; /* threshold in input buffer */ 33618334Speter u_char *iptr; /* next free spot in input buffer */ 33718334Speter int ibufsize; /* size of ibuf (not include error bytes) */ 33818334Speter int ierroff; /* offset of error bytes in ibuf */ 33918334Speter 34018334Speter struct lbq obufq; /* head of queue of output buffers */ 34118334Speter struct lbq obufs[2]; /* output buffers */ 34218334Speter 34318334Speter#ifdef PC98 34418334Speter Port_t cmd_port; 34518334Speter Port_t sts_port; 34618334Speter Port_t in_modem_port; 34718334Speter Port_t intr_ctrl_port; 34818334Speter int intr_enable; 34918334Speter int pc98_prev_modem_status; 35018334Speter int pc98_modem_delta; 35118334Speter int modem_car_chg_timer; 35218334Speter int pc98_prev_siocmd; 35318334Speter int pc98_prev_siomod; 35418334Speter int modem_checking; 35518334Speter int pc98_if_type; 35618334Speter#endif /* PC98 */ 35718334Speter Port_t data_port; /* i/o ports */ 35818334Speter#ifdef COM_ESP 35918334Speter Port_t esp_port; 36018334Speter#endif 36118334Speter Port_t int_id_port; 36218334Speter Port_t iobase; 36318334Speter#ifdef PC98 36418334Speter Port_t rsabase; /* iobase address of a I/O-DATA RSA board */ 36518334Speter#endif 36618334Speter Port_t modem_ctl_port; 36718334Speter Port_t line_status_port; 36818334Speter Port_t modem_status_port; 36918334Speter Port_t intr_ctl_port; /* Ports of IIR register */ 37018334Speter 37118334Speter struct tty *tp; /* cross reference */ 37218334Speter 37318334Speter /* Initial state. */ 37418334Speter struct termios it_in; /* should be in struct tty */ 37518334Speter struct termios it_out; 37618334Speter 37718334Speter /* Lock state. */ 37818334Speter struct termios lt_in; /* should be in struct tty */ 37918334Speter struct termios lt_out; 38018334Speter 38118334Speter bool_t do_timestamp; 38218334Speter bool_t do_dcd_timestamp; 38318334Speter struct timeval timestamp; 38418334Speter struct timeval dcd_timestamp; 38518334Speter struct pps_state pps; 38618334Speter 38718334Speter u_long bytes_in; /* statistics */ 38818334Speter u_long bytes_out; 38918334Speter u_int delta_error_counts[CE_NTYPES]; 39018334Speter u_long error_counts[CE_NTYPES]; 39118334Speter 39218334Speter /* 39318334Speter * Data area for output buffers. Someday we should build the output 39418334Speter * buffer queue without copying data. 39518334Speter */ 39618334Speter#ifdef PC98 39718334Speter int obufsize; 39818334Speter u_char *obuf1; 39918334Speter u_char *obuf2; 40018334Speter#else 40118334Speter u_char obuf1[256]; 40218334Speter u_char obuf2[256]; 40318334Speter#endif 40418334Speter#ifdef DEVFS 40518334Speter void *devfs_token_ttyd; 40618334Speter void *devfs_token_ttyl; 40718334Speter void *devfs_token_ttyi; 40818334Speter void *devfs_token_cuaa; 40918334Speter void *devfs_token_cual; 41018334Speter void *devfs_token_cuai; 41150600Sobrien#endif 41250600Sobrien}; 41350600Sobrien 41450600Sobrien#ifdef COM_ESP 41550600Sobrienstatic int espattach __P((struct isa_device *isdp, struct com_s *com, 41650600Sobrien Port_t esp_port)); 41750600Sobrien#endif 41818334Speterstatic int sioattach __P((struct isa_device *dev)); 41918334Speterstatic timeout_t siobusycheck; 42018334Speterstatic timeout_t siodtrwakeup; 42118334Speterstatic void comhardclose __P((struct com_s *com)); 42218334Speterstatic void sioinput __P((struct com_s *com)); 42318334Speterstatic ointhand2_t siointr; 42418334Speterstatic void siointr1 __P((struct com_s *com)); 42518334Speterstatic int commctl __P((struct com_s *com, int bits, int how)); 42618334Speterstatic int comparam __P((struct tty *tp, struct termios *t)); 42718334Speterstatic swihand_t siopoll; 42818334Speterstatic int sioprobe __P((struct isa_device *dev)); 42918334Speterstatic void siosettimeout __P((void)); 43018334Speterstatic int siosetwater __P((struct com_s *com, speed_t speed)); 43118334Speterstatic void comstart __P((struct tty *tp)); 43218334Speterstatic timeout_t comwakeup; 43318334Speterstatic void disc_optim __P((struct tty *tp, struct termios *t, 43418334Speter struct com_s *com)); 43518334Speter 43618334Speter 43718334Speterstatic char driver_name[] = "sio"; 43818334Speter 43918334Speter/* table and macro for fast conversion from a unit number to its com struct */ 44018334Speterstatic struct com_s *p_com_addr[NSIOTOT]; 44118334Speter#define com_addr(unit) (p_com_addr[unit]) 44218334Speter 44318334Speterstruct isa_driver siodriver = { 44450600Sobrien sioprobe, sioattach, driver_name 44518334Speter}; 44618334Speter 44718334Speterstatic d_open_t sioopen; 44818334Speterstatic d_close_t sioclose; 44918334Speterstatic d_read_t sioread; 45018334Speterstatic d_write_t siowrite; 45118334Speterstatic d_ioctl_t sioioctl; 45218334Speterstatic d_stop_t siostop; 45318334Speterstatic d_devtotty_t siodevtotty; 45418334Speter 45518334Speter#define CDEV_MAJOR 28 45618334Speterstatic struct cdevsw sio_cdevsw = { 45718334Speter sioopen, sioclose, sioread, siowrite, 45818334Speter sioioctl, siostop, noreset, siodevtotty, 45918334Speter ttpoll, nommap, NULL, driver_name, 46018334Speter NULL, -1, nodump, nopsize, 46118334Speter D_TTY, 46218334Speter}; 46318334Speter 46418334Speterstatic int comconsole = -1; 46518334Speterstatic volatile speed_t comdefaultrate = CONSPEED; 46618334Speterstatic u_int com_events; /* input chars + weighted output completions */ 46718334Speterstatic Port_t siocniobase; 46818334Speterstatic bool_t sio_registered; 46918334Speterstatic int sio_timeout; 47018334Speterstatic int sio_timeouts_until_log; 47118334Speterstatic struct callout_handle sio_timeout_handle 47218334Speter = CALLOUT_HANDLE_INITIALIZER(&sio_timeout_handle); 47318334Speter#if 0 /* XXX */ 47418334Speterstatic struct tty *sio_tty[NSIOTOT]; 47518334Speter#else 47618334Speterstatic struct tty sio_tty[NSIOTOT]; 47718334Speter#endif 47818334Speterstatic const int nsio_tty = NSIOTOT; 47918334Speter 48018334Speter#ifdef PC98 48118334Speterstruct siodev { 48218334Speter short if_type; 48318334Speter short irq; 48418334Speter Port_t cmd, sts, ctrl, mod; 48518334Speter}; 48618334Speterstatic int sysclock; 48718334Speter 48818334Speter#define COM_INT_DISABLE {int previpri; previpri=spltty(); 48918334Speter#define COM_INT_ENABLE splx(previpri);} 49018334Speter#define IEN_TxFLAG IEN_Tx 49118334Speter 49218334Speter#define COM_CARRIER_DETECT_EMULATE 0 49318334Speter#define PC98_CHECK_MODEM_INTERVAL (hz/10) 49418334Speter#define DCD_OFF_TOLERANCE 2 49518334Speter#define DCD_ON_RECOGNITION 2 49618334Speter#define IS_8251(if_type) (!(if_type & 0x10)) 49718334Speter#define COM1_EXT_CLOCK 0x40000 49818334Speter 49918334Speterstatic void commint __P((dev_t dev)); 50018334Speterstatic void com_tiocm_set __P((struct com_s *com, int msr)); 50118334Speterstatic void com_tiocm_bis __P((struct com_s *com, int msr)); 50218334Speterstatic void com_tiocm_bic __P((struct com_s *com, int msr)); 50318334Speterstatic int com_tiocm_get __P((struct com_s *com)); 50418334Speterstatic int com_tiocm_get_delta __P((struct com_s *com)); 50518334Speterstatic void pc98_msrint_start __P((dev_t dev)); 50618334Speterstatic void com_cflag_and_speed_set __P((struct com_s *com, int cflag, int speed)); 50718334Speterstatic int pc98_ttspeedtab __P((struct com_s *com, int speed)); 50818334Speterstatic int pc98_get_modem_status __P((struct com_s *com)); 50918334Speterstatic timeout_t pc98_check_msr; 51018334Speterstatic void pc98_set_baud_rate __P((struct com_s *com, int count)); 51118334Speterstatic void pc98_i8251_reset __P((struct com_s *com, int mode, int command)); 51218334Speterstatic void pc98_disable_i8251_interrupt __P((struct com_s *com, int mod)); 51318334Speterstatic void pc98_enable_i8251_interrupt __P((struct com_s *com, int mod)); 51418334Speterstatic int pc98_check_i8251_interrupt __P((struct com_s *com)); 51518334Speterstatic int pc98_i8251_get_cmd __P((struct com_s *com)); 51618334Speterstatic int pc98_i8251_get_mod __P((struct com_s *com)); 51718334Speterstatic void pc98_i8251_set_cmd __P((struct com_s *com, int x)); 51818334Speterstatic void pc98_i8251_or_cmd __P((struct com_s *com, int x)); 51918334Speterstatic void pc98_i8251_clear_cmd __P((struct com_s *com, int x)); 52018334Speterstatic void pc98_i8251_clear_or_cmd __P((struct com_s *com, int clr, int x)); 52118334Speterstatic int pc98_check_if_type __P((struct isa_device *dev, struct siodev *iod)); 52218334Speterstatic void pc98_check_sysclock __P((void)); 52318334Speterstatic int pc98_set_ioport __P((struct com_s *com, int id_flags)); 52418334Speter 52518334Speter#define com_int_Tx_disable(com) \ 52618334Speter pc98_disable_i8251_interrupt(com,IEN_Tx|IEN_TxEMP) 52718334Speter#define com_int_Tx_enable(com) \ 52818334Speter pc98_enable_i8251_interrupt(com,IEN_TxFLAG) 52918334Speter#define com_int_Rx_disable(com) \ 53018334Speter pc98_disable_i8251_interrupt(com,IEN_Rx) 53118334Speter#define com_int_Rx_enable(com) \ 53218334Speter pc98_enable_i8251_interrupt(com,IEN_Rx) 53318334Speter#define com_int_TxRx_disable(com) \ 53418334Speter pc98_disable_i8251_interrupt(com,IEN_Tx|IEN_TxEMP|IEN_Rx) 53518334Speter#define com_int_TxRx_enable(com) \ 53618334Speter pc98_enable_i8251_interrupt(com,IEN_TxFLAG|IEN_Rx) 53718334Speter#define com_send_break_on(com) \ 53818334Speter pc98_i8251_or_cmd(com,CMD8251_SBRK) 53918334Speter#define com_send_break_off(com) \ 54018334Speter pc98_i8251_clear_cmd(com,CMD8251_SBRK) 54118334Speter 54218334Speterstatic struct speedtab pc98speedtab[] = { /* internal RS232C interface */ 54318334Speter { 0, 0, }, 54418334Speter { 50, 50, }, 54518334Speter { 75, 75, }, 54618334Speter { 150, 150, }, 54718334Speter { 200, 200, }, 54818334Speter { 300, 300, }, 54918334Speter { 600, 600, }, 55018334Speter { 1200, 1200, }, 55118334Speter { 2400, 2400, }, 55218334Speter { 4800, 4800, }, 55318334Speter { 9600, 9600, }, 55450600Sobrien { 19200, 19200, }, 55518334Speter { 38400, 38400, }, 55618334Speter { 51200, 51200, }, 55718334Speter { 76800, 76800, }, 55818334Speter { 20800, 20800, }, 55918334Speter { 31200, 31200, }, 56018334Speter { 41600, 41600, }, 56118334Speter { 62400, 62400, }, 56218334Speter { -1, -1 } 56318334Speter}; 56418334Speterstatic struct speedtab pc98fast_speedtab[] = { 56518334Speter { 9600, 0x80 | COMBRD(9600), }, 56618334Speter { 19200, 0x80 | COMBRD(19200), }, 56718334Speter { 38400, 0x80 | COMBRD(38400), }, 56818334Speter { 57600, 0x80 | COMBRD(57600), }, 56918334Speter { 115200, 0x80 | COMBRD(115200), }, 57018334Speter { -1, -1 } 57118334Speter}; 57218334Speterstatic struct speedtab comspeedtab_pio9032b[] = { 57318334Speter { 300, 6, }, 57418334Speter { 600, 5, }, 57518334Speter { 1200, 4, }, 57618334Speter { 2400, 3, }, 57718334Speter { 4800, 2, }, 57818334Speter { 9600, 1, }, 57918334Speter { 19200, 0, }, 58018334Speter { 38400, 7, }, 58118334Speter { -1, -1 } 58218334Speter}; 58318334Speterstatic struct speedtab comspeedtab_b98_01[] = { 58418334Speter { 75, 11, }, 58518334Speter { 150, 10, }, 58618334Speter { 300, 9, }, 58718334Speter { 600, 8, }, 58818334Speter { 1200, 7, }, 58918334Speter { 2400, 6, }, 59018334Speter { 4800, 5, }, 59118334Speter { 9600, 4, }, 59218334Speter { 19200, 3, }, 59318334Speter { 38400, 2, }, 59418334Speter { 76800, 1, }, 59518334Speter { 153600, 0, }, 59618334Speter { -1, -1 } 59718334Speter}; 59818334Speterstatic struct speedtab comspeedtab_mc16550[] = { 59918334Speter { 300, 1536, }, 60018334Speter { 600, 768, }, 60118334Speter { 1200, 384, }, 60218334Speter { 2400, 192, }, 60318334Speter { 4800, 96, }, 60418334Speter { 9600, 48, }, 60518334Speter { 19200, 24, }, 60618334Speter { 38400, 12, }, 60718334Speter { 57600, 8, }, 60818334Speter { 115200, 4, }, 60918334Speter { 153600, 3, }, 61018334Speter { 230400, 2, }, 61118334Speter { 460800, 1, }, 61218334Speter { -1, -1 } 61318334Speter}; 61418334Speterstatic struct speedtab comspeedtab_rsb384[] = { 61518334Speter { 300, 3840, }, 61618334Speter { 600, 1920, }, 61718334Speter { 1200, 960, }, 61818334Speter { 2400, 480, }, 61918334Speter { 4800, 240, }, 62018334Speter { 9600, 120, }, 62118334Speter { 19200, 60, }, 62218334Speter { 38400, 30, }, 62318334Speter { 57600, 20, }, 62418334Speter { 115200, 10, }, 62518334Speter { 128000, 9, }, 62618334Speter { 144000, 8, }, 62718334Speter { 192000, 6, }, 62818334Speter { 230400, 5, }, 62918334Speter { 288000, 4, }, 63018334Speter { 384000, 3, }, 63118334Speter { 576000, 2, }, 63218334Speter { 1152000, 1, }, 63318334Speter { -1, -1 } 63418334Speter}; 63518334Speterstatic struct speedtab comspeedtab_rsa[] = { 63618334Speter { 0, 0 }, 63718334Speter { 50, COMBRD_RSA(50) }, 63818334Speter { 75, COMBRD_RSA(75) }, 63918334Speter { 110, COMBRD_RSA(110) }, 64018334Speter { 134, COMBRD_RSA(134) }, 64118334Speter { 150, COMBRD_RSA(150) }, 64218334Speter { 200, COMBRD_RSA(200) }, 64318334Speter { 300, COMBRD_RSA(300) }, 64418334Speter { 600, COMBRD_RSA(600) }, 64518334Speter { 1200, COMBRD_RSA(1200) }, 64618334Speter { 1800, COMBRD_RSA(1800) }, 64718334Speter { 2400, COMBRD_RSA(2400) }, 64818334Speter { 4800, COMBRD_RSA(4800) }, 64918334Speter { 9600, COMBRD_RSA(9600) }, 65018334Speter { 19200, COMBRD_RSA(19200) }, 65118334Speter { 38400, COMBRD_RSA(38400) }, 65218334Speter { 57600, COMBRD_RSA(57600) }, 65318334Speter { 115200, COMBRD_RSA(115200) }, 65418334Speter { 230400, COMBRD_RSA(230400) }, 65518334Speter { 460800, COMBRD_RSA(460800) }, 65618334Speter { 921600, COMBRD_RSA(921600) }, 65718334Speter { -1, -1 } 65818334Speter}; 65918334Speter#endif /* PC98 */ 66018334Speter 66118334Speterstatic struct speedtab comspeedtab[] = { 66218334Speter { 0, 0 }, 66318334Speter { 50, COMBRD(50) }, 66418334Speter { 75, COMBRD(75) }, 66550600Sobrien { 110, COMBRD(110) }, 66618334Speter { 134, COMBRD(134) }, 66718334Speter { 150, COMBRD(150) }, 66818334Speter { 200, COMBRD(200) }, 66918334Speter { 300, COMBRD(300) }, 67018334Speter { 600, COMBRD(600) }, 67118334Speter { 1200, COMBRD(1200) }, 67218334Speter { 1800, COMBRD(1800) }, 67318334Speter { 2400, COMBRD(2400) }, 67418334Speter { 4800, COMBRD(4800) }, 67518334Speter { 9600, COMBRD(9600) }, 67618334Speter { 19200, COMBRD(19200) }, 67718334Speter { 38400, COMBRD(38400) }, 67818334Speter { 57600, COMBRD(57600) }, 67918334Speter { 115200, COMBRD(115200) }, 68018334Speter { -1, -1 } 68118334Speter}; 68218334Speter 68318334Speter#ifdef PC98 68418334Speterstruct { 68518334Speter char *name; 68618334Speter short port_table[7]; 68718334Speter short irr_mask; 68818334Speter struct speedtab *speedtab; 68918334Speter short check_irq; 69018334Speter} if_8251_type[] = { 69118334Speter /* COM_IF_INTERNAL */ 69250600Sobrien { " (internal)", {0x30, 0x32, 0x32, 0x33, 0x35, -1, -1}, 69318334Speter -1, pc98speedtab, 1 }, 69418334Speter /* COM_IF_PC9861K_1 */ 69518334Speter { " (PC9861K)", {0xb1, 0xb3, 0xb3, 0xb0, 0xb0, -1, -1}, 69618334Speter 3, NULL, 1 }, 69718334Speter /* COM_IF_PC9861K_2 */ 69818334Speter { " (PC9861K)", {0xb9, 0xbb, 0xbb, 0xb2, 0xb2, -1, -1}, 69918334Speter 3, NULL, 1 }, 70018334Speter /* COM_IF_IND_SS_1 */ 70118334Speter { " (IND-SS)", {0xb1, 0xb3, 0xb3, 0xb0, 0xb0, 0xb3, -1}, 70218334Speter 3, comspeedtab_mc16550, 1 }, 70318334Speter /* COM_IF_IND_SS_2 */ 70418334Speter { " (IND-SS)", {0xb9, 0xbb, 0xbb, 0xb2, 0xb2, 0xbb, -1}, 70518334Speter 3, comspeedtab_mc16550, 1 }, 70618334Speter /* COM_IF_PIO9032B_1 */ 70718334Speter { " (PIO9032B)", {0xb1, 0xb3, 0xb3, 0xb0, 0xb0, 0xb8, -1}, 70818334Speter 7, comspeedtab_pio9032b, 1 }, 70918334Speter /* COM_IF_PIO9032B_2 */ 71018334Speter { " (PIO9032B)", {0xb9, 0xbb, 0xbb, 0xb2, 0xb2, 0xba, -1}, 71118334Speter 7, comspeedtab_pio9032b, 1 }, 71218334Speter /* COM_IF_B98_01_1 */ 71318334Speter { " (B98-01)", {0xb1, 0xb3, 0xb3, 0xb0, 0xb0, 0xd1, 0xd3}, 71418334Speter 7, comspeedtab_b98_01, 0 }, 71518334Speter /* COM_IF_B98_01_2 */ 71618334Speter { " (B98-01)", {0xb9, 0xbb, 0xbb, 0xb2, 0xb2, 0xd5, 0xd7}, 71718334Speter 7, comspeedtab_b98_01, 0 }, 71850600Sobrien}; 71918334Speter#define PC98SIO_data_port(type) (if_8251_type[type].port_table[0]) 72018334Speter#define PC98SIO_cmd_port(type) (if_8251_type[type].port_table[1]) 72118334Speter#define PC98SIO_sts_port(type) (if_8251_type[type].port_table[2]) 72218334Speter#define PC98SIO_in_modem_port(type) (if_8251_type[type].port_table[3]) 72318334Speter#define PC98SIO_intr_ctrl_port(type) (if_8251_type[type].port_table[4]) 72418334Speter#define PC98SIO_baud_rate_port(type) (if_8251_type[type].port_table[5]) 72518334Speter#define PC98SIO_func_port(type) (if_8251_type[type].port_table[6]) 72618334Speter 72718334Speterstruct { 72818334Speter char *name; 72918334Speter short irr_read; 73018334Speter short irr_write; 73150600Sobrien short port_shift; 73218334Speter short io_size; 73318334Speter struct speedtab *speedtab; 73418334Speter} if_16550a_type[] = { 73518334Speter /* COM_IF_RSA98 */ 73618334Speter { " (RSA-98)", -1, -1, 0, IO_COMSIZE, comspeedtab }, 73718334Speter /* COM_IF_NS16550 */ 73818334Speter { "", -1, -1, 0, IO_COMSIZE, comspeedtab }, 73950600Sobrien /* COM_IF_SECOND_CCU */ 74018334Speter { "", -1, -1, 0, IO_COMSIZE, comspeedtab }, 74118334Speter /* COM_IF_MC16550II */ 74218334Speter { " (MC16550II)", -1, 0x1000, 8, 1, comspeedtab_mc16550 }, 74318334Speter /* COM_IF_MCRS98 */ 74418334Speter { " (MC-RS98)", -1, 0x1000, 8, 1, comspeedtab_mc16550 }, 74518334Speter /* COM_IF_RSB3000 */ 74618334Speter { " (RSB-3000)", 0xbf, -1, 1, 1, comspeedtab_rsb384 }, 74718334Speter /* COM_IF_RSB384 */ 74818334Speter { " (RSB-384)", 0xbf, -1, 1, 1, comspeedtab_rsb384 }, 74918334Speter /* COM_IF_MODEM_CARD */ 75018334Speter { "", -1, -1, 0, IO_COMSIZE, comspeedtab }, 75118334Speter /* COM_IF_RSA98III */ 75218334Speter { " (RSA-98III)", -1, -1, 0, 16, comspeedtab_rsa }, 75318334Speter /* COM_IF_ESP98 */ 75418334Speter { " (ESP98)", -1, -1, 1, 1, comspeedtab_mc16550 }, 75518334Speter}; 75618334Speter#endif /* PC98 */ 75718334Speter 75818334Speter#ifdef COM_ESP 75950600Sobrien#ifdef PC98 76018334Speter 76118334Speter/* XXX configure this properly. */ 76218334Speterstatic Port_t likely_com_ports[] = { 0, 0xb0, 0xb1, 0 }; 76318334Speterstatic Port_t likely_esp_ports[] = { 0xc0d0, 0 }; 76418334Speter 76518334Speter#define ESP98_CMD1 (ESP_CMD1 * 0x100) 76618334Speter#define ESP98_CMD2 (ESP_CMD2 * 0x100) 76718334Speter#define ESP98_STATUS1 (ESP_STATUS1 * 0x100) 76818334Speter#define ESP98_STATUS2 (ESP_STATUS2 * 0x100) 76918334Speter 77018334Speter#else /* PC98 */ 77118334Speter 77218334Speter/* XXX configure this properly. */ 77318334Speterstatic Port_t likely_com_ports[] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, }; 77418334Speterstatic Port_t likely_esp_ports[] = { 0x140, 0x180, 0x280, 0 }; 77518334Speter 77618334Speter#endif /* PC98 */ 77752561Sobrien#endif 77818334Speter 77918334Speter/* 78018334Speter * handle sysctl read/write requests for console speed 78118334Speter * 78250600Sobrien * In addition to setting comdefaultrate for I/O through /dev/console, 78318334Speter * also set the initial and lock values for the /dev/ttyXX device 78418334Speter * if there is one associated with the console. Finally, if the /dev/tty 78518334Speter * device has already been open, change the speed on the open running port 78618334Speter * itself. 78718334Speter */ 78818334Speter 78918334Speterstatic int 79018334Spetersysctl_machdep_comdefaultrate SYSCTL_HANDLER_ARGS 79118334Speter{ 79218334Speter int error, s; 79318334Speter speed_t newspeed; 79418334Speter struct com_s *com; 79518334Speter struct tty *tp; 79618334Speter 79718334Speter newspeed = comdefaultrate; 79818334Speter 79918334Speter error = sysctl_handle_opaque(oidp, &newspeed, sizeof newspeed, req); 80018334Speter if (error || !req->newptr) 80118334Speter return (error); 80218334Speter 80318334Speter comdefaultrate = newspeed; 80418334Speter 80518334Speter if (comconsole < 0) /* serial console not selected? */ 80618334Speter return (0); 80718334Speter 80818334Speter com = com_addr(comconsole); 80952561Sobrien if (!com) 81018334Speter return (ENXIO); 81150600Sobrien 81218334Speter /* 81318334Speter * set the initial and lock rates for /dev/ttydXX and /dev/cuaXX 81418334Speter * (note, the lock rates really are boolean -- if non-zero, disallow 81518334Speter * speed changes) 81618334Speter */ 81718334Speter com->it_in.c_ispeed = com->it_in.c_ospeed = 81818334Speter com->lt_in.c_ispeed = com->lt_in.c_ospeed = 81952561Sobrien com->it_out.c_ispeed = com->it_out.c_ospeed = 82018334Speter com->lt_out.c_ispeed = com->lt_out.c_ospeed = comdefaultrate; 82118334Speter 82218334Speter /* 82318334Speter * if we're open, change the running rate too 82450600Sobrien */ 82518334Speter tp = com->tp; 82618334Speter if (tp && (tp->t_state & TS_ISOPEN)) { 82718334Speter tp->t_termios.c_ispeed = 82818334Speter tp->t_termios.c_ospeed = comdefaultrate; 82918334Speter s = spltty(); 83018334Speter error = comparam(tp, &tp->t_termios); 83118334Speter splx(s); 83218334Speter } 83318334Speter return error; 83418334Speter} 83518334Speter 83618334SpeterSYSCTL_PROC(_machdep, OID_AUTO, conspeed, CTLTYPE_INT | CTLFLAG_RW, 83718334Speter 0, 0, sysctl_machdep_comdefaultrate, "I", ""); 83818334Speter 83918334Speter#if NCARD > 0 84018334Speter/* 84118334Speter * PC-Card (PCMCIA) specific code. 84218334Speter */ 84318334Speterstatic int sioinit __P((struct pccard_devinfo *)); 84418334Speterstatic void siounload __P((struct pccard_devinfo *)); 84518334Speterstatic int card_intr __P((struct pccard_devinfo *)); 84618334Speter 84718334SpeterPCCARD_MODULE(sio, sioinit, siounload, card_intr, 0, tty_imask); 84818334Speter 84918334Speter/* 85018334Speter * Initialize the device - called from Slot manager. 85152561Sobrien */ 85218334Speterint 85350600Sobriensioinit(struct pccard_devinfo *devi) 85418334Speter{ 85518334Speter 85618334Speter /* validate unit number. */ 85718334Speter if (devi->isahd.id_unit >= (NSIOTOT)) 85818334Speter return(ENODEV); 85918334Speter /* Make sure it isn't already probed. */ 86018334Speter if (com_addr(devi->isahd.id_unit)) 86118334Speter return(EBUSY); 86218334Speter 86318334Speter /* It's already probed as serial by Upper */ 86418334Speter devi->isahd.id_flags |= COM_C_NOPROBE; 86518334Speter 86650600Sobrien /* 86718334Speter * Probe the device. If a value is returned, the 86818334Speter * device was found at the location. 86918334Speter */ 87018334Speter if (sioprobe(&devi->isahd) == 0) 87118334Speter return(ENXIO); 87218334Speter if (sioattach(&devi->isahd) == 0) 87318334Speter return(ENXIO); 87418334Speter 87518334Speter return(0); 87618334Speter} 87718334Speter 87818334Speter/* 87918334Speter * siounload - unload the driver and clear the table. 88018334Speter * XXX TODO: 88118334Speter * This is usually called when the card is ejected, but 88218334Speter * can be caused by a modunload of a controller driver. 88318334Speter * The idea is to reset the driver's view of the device 88418334Speter * and ensure that any driver entry points such as 88518334Speter * read and write do not hang. 88618334Speter */ 88718334Speterstatic void 88818334Spetersiounload(struct pccard_devinfo *devi) 88918334Speter{ 89018334Speter struct com_s *com; 89118334Speter 89218334Speter if (!devi) { 89318334Speter printf("NULL devi in siounload\n"); 89418334Speter return; 89550600Sobrien } 89618334Speter com = com_addr(devi->isahd.id_unit); 89718334Speter if (!com) { 89818334Speter printf("NULL com in siounload\n"); 89918334Speter return; 90018334Speter } 90118334Speter if (!com->iobase) { 90218334Speter printf("sio%d already unloaded!\n",devi->isahd.id_unit); 90318334Speter return; 90418334Speter } 90518334Speter if (com->tp && (com->tp->t_state & TS_ISOPEN)) { 90618334Speter com->gone = 1; 90718334Speter printf("sio%d: unload\n", devi->isahd.id_unit); 90818334Speter com->tp->t_gen++; 90918334Speter ttyclose(com->tp); 91018334Speter ttwakeup(com->tp); 91118334Speter ttwwakeup(com->tp); 91218334Speter } else { 91318334Speter com_addr(com->unit) = NULL; 91418334Speter if (com->ibuf != NULL) 91518334Speter free(com->ibuf, M_DEVBUF); 91618334Speter free(com, M_DEVBUF); 91718334Speter printf("sio%d: unload,gone\n", devi->isahd.id_unit); 91818334Speter } 91918334Speter} 92018334Speter 92118334Speter/* 92218334Speter * card_intr - Shared interrupt called from 92318334Speter * front end of PC-Card handler. 92418334Speter */ 92518334Speterstatic int 92618334Spetercard_intr(struct pccard_devinfo *devi) 92718334Speter{ 92818334Speter struct com_s *com; 92918334Speter 93018334Speter COM_LOCK(); 93118334Speter com = com_addr(devi->isahd.id_unit); 93218334Speter if (com && !com->gone) 93318334Speter siointr1(com_addr(devi->isahd.id_unit)); 93418334Speter COM_UNLOCK(); 93518334Speter return(1); 93618334Speter} 93718334Speter#endif /* NCARD > 0 */ 93818334Speter 93918334Speterstatic int 94018334Spetersioprobe(dev) 94118334Speter struct isa_device *dev; 94218334Speter{ 94318334Speter static bool_t already_init; 94418334Speter bool_t failures[10]; 94518334Speter int fn; 94618334Speter struct isa_device *idev; 94718334Speter Port_t iobase; 94818334Speter intrmask_t irqmap[4]; 94952561Sobrien intrmask_t irqs; 95018334Speter u_char mcr_image; 95118334Speter int result; 95218334Speter struct isa_device *xdev; 95318334Speter#ifdef PC98 95418334Speter int irqout=0; 95550600Sobrien int ret = 0; 95618334Speter int tmp; 95718334Speter int port_shift = 0; 95818334Speter struct siodev iod; 95950600Sobrien Port_t rsabase = NULL; 96018334Speter#endif 96118334Speter 96218334Speter if (!already_init) { 96318334Speter /* 96450600Sobrien * Turn off MCR_IENABLE for all likely serial ports. An unused 96518334Speter * port with its MCR_IENABLE gate open will inhibit interrupts 96618334Speter * from any used port that shares the interrupt vector. 96718334Speter * XXX the gate enable is elsewhere for some multiports. 96852561Sobrien */ 96918334Speter for (xdev = isa_devtab_tty; xdev->id_driver != NULL; xdev++) 97018334Speter#ifdef PC98 97118334Speter if (xdev->id_driver == &siodriver && xdev->id_enabled) { 97218334Speter tmp = (xdev->id_flags >> 24) & 0xff; 97318334Speter if (IS_8251(tmp)) 97450600Sobrien outb((xdev->id_iobase & 0xff00) | PC98SIO_cmd_port(tmp & 0x0f), 0xf2); 97518334Speter else 97618334Speter if (tmp == COM_IF_RSA98III) { 97718334Speter rsabase = xdev->id_iobase & 0xfff0; 97850600Sobrien#if 0 97918334Speter if (rsabase != xdev->id_iobase) 98018334Speter return(0); 98118334Speter#endif 98218334Speter outb(xdev->id_iobase + 8 + (com_mcr << if_16550a_type[tmp & 0x0f].port_shift), 0); 98350600Sobrien } else 98418334Speter outb(xdev->id_iobase + (com_mcr << if_16550a_type[tmp & 0x0f].port_shift), 0); 98518334Speter } 98618334Speter#else 98718334Speter if (xdev->id_driver == &siodriver && xdev->id_enabled) 98818334Speter outb(xdev->id_iobase + com_mcr, 0); 98918334Speter#endif 99018334Speter already_init = TRUE; 99118334Speter } 99218334Speter 99350600Sobrien if (COM_LLCONSOLE(dev)) { 99418334Speter printf("sio%d: reserved for low-level i/o\n", dev->id_unit); 99518334Speter return (0); 99618334Speter } 99750600Sobrien 99818334Speter#ifdef PC98 99918334Speter DELAY(10); 100018334Speter 100118334Speter /* 100250600Sobrien * If the port is i8251 UART (internal, B98_01) 100318334Speter */ 100418334Speter if (pc98_check_if_type(dev, &iod) == -1) 100518334Speter return 0; 100618334Speter if (iod.irq > 0) 100718334Speter dev->id_irq = 1 << iod.irq; 100818334Speter if (IS_8251(iod.if_type)) { 100918334Speter outb(iod.cmd, 0); 101018334Speter DELAY(10); 101118334Speter outb(iod.cmd, 0); 101218334Speter DELAY(10); 101318334Speter outb(iod.cmd, 0); 101418334Speter DELAY(10); 101518334Speter outb(iod.cmd, CMD8251_RESET); 101618334Speter DELAY(1000); /* for a while...*/ 101718334Speter outb(iod.cmd, 0xf2); /* MODE (dummy) */ 101818334Speter DELAY(10); 101918334Speter outb(iod.cmd, 0x01); /* CMD (dummy) */ 102018334Speter DELAY(1000); /* for a while...*/ 102118334Speter if (( inb(iod.sts) & STS8251_TxEMP ) == 0 ) { 102218334Speter ret = 0; 102318334Speter } 102418334Speter if (if_8251_type[iod.if_type & 0x0f].check_irq) { 102518334Speter COM_INT_DISABLE 102618334Speter tmp = ( inb( iod.ctrl ) & ~(IEN_Rx|IEN_TxEMP|IEN_Tx)); 102718334Speter outb( iod.ctrl, tmp|IEN_TxEMP ); 102818334Speter DELAY(10); 102918334Speter ret = isa_irq_pending() ? 4 : 0; 103018334Speter outb( iod.ctrl, tmp ); 103118334Speter COM_INT_ENABLE 103218334Speter } else { 103318334Speter /* 103418334Speter * B98_01 doesn't activate TxEMP interrupt line 103518334Speter * when being reset, so we can't check irq pending. 103618334Speter */ 103718334Speter ret = 4; 103818334Speter } 103918334Speter if (epson_machine_id==0x20) { /* XXX */ 104018334Speter ret = 4; 104150600Sobrien } 104218334Speter return ret; 104318334Speter } 104418334Speter#endif /* PC98 */ 104518334Speter /* 104618334Speter * If the device is on a multiport card and has an AST/4 104718334Speter * compatible interrupt control register, initialize this 104818334Speter * register and prepare to leave MCR_IENABLE clear in the mcr. 104918334Speter * Otherwise, prepare to set MCR_IENABLE in the mcr. 105018334Speter * Point idev to the device struct giving the correct id_irq. 105118334Speter * This is the struct for the master device if there is one. 105218334Speter */ 105318334Speter idev = dev; 105418334Speter mcr_image = MCR_IENABLE; 105518334Speter#ifdef PC98 105618334Speter if (iod.if_type == COM_IF_RSA98III) { 105718334Speter mcr_image = 0; 105850600Sobrien rsabase = idev->id_iobase & 0xfff0; 105950600Sobrien if (rsabase != idev->id_iobase) 106018334Speter return(0); 106118334Speter outb(rsabase + rsa_msr, 0x04); 106218334Speter outb(rsabase + rsa_frr, 0x00); 106318334Speter if ((inb(rsabase + rsa_srr) & 0x36) != 0x36) 106418334Speter return (0); 106550600Sobrien outb(rsabase + rsa_ier, 0x00); 106618334Speter outb(rsabase + rsa_frr, 0x00); 106718334Speter outb(rsabase + rsa_tivsr, 0x00); 106818334Speter outb(rsabase + rsa_tcr, 0x00); 106918334Speter } 107050600Sobrien#endif /* PC98 */ 107118334Speter#ifdef COM_MULTIPORT 107218334Speter if (COM_ISMULTIPORT(dev)) { 107318334Speter idev = find_isadev(isa_devtab_tty, &siodriver, 107452561Sobrien COM_MPMASTER(dev)); 107518334Speter if (idev == NULL) { 107618334Speter printf("sio%d: master device %d not configured\n", 107718334Speter dev->id_unit, COM_MPMASTER(dev)); 107818334Speter dev->id_irq = 0; 107918334Speter idev = dev; 108018334Speter } 108118334Speter#ifndef PC98 108218334Speter if (!COM_NOTAST4(dev)) { 108318334Speter outb(idev->id_iobase + com_scr, 108418334Speter idev->id_irq ? 0x80 : 0); 108518334Speter mcr_image = 0; 108618334Speter } 108718334Speter#endif /* !PC98 */ 108850600Sobrien } 108918334Speter#endif /* COM_MULTIPORT */ 109018334Speter if (idev->id_irq == 0) 109118334Speter mcr_image = 0; 109218334Speter 109318334Speter#ifdef PC98 109418334Speter tmp = if_16550a_type[iod.if_type & 0x0f].irr_write; 109518334Speter if (tmp != -1) { 109618334Speter /* MC16550II */ 109718334Speter switch (idev->id_irq) { 109818334Speter case IRQ3: irqout = 4; break; 109918334Speter case IRQ5: irqout = 5; break; 110018334Speter case IRQ6: irqout = 6; break; 110118334Speter case IRQ12: irqout = 7; break; 110218334Speter default: 110318334Speter printf("sio%d: irq configuration error\n", dev->id_unit); 110418334Speter return (0); 110518334Speter } 110618334Speter outb((dev->id_iobase & 0x00ff) | tmp, irqout); 110718334Speter } 110818334Speter port_shift = if_16550a_type[iod.if_type & 0x0f].port_shift; 110918334Speter#endif 111050600Sobrien bzero(failures, sizeof failures); 111118334Speter#ifdef PC98 111218334Speter if (iod.if_type == COM_IF_RSA98III) 111318334Speter iobase = dev->id_iobase + 8; 111418334Speter else 111518334Speter#endif 111618334Speter iobase = dev->id_iobase; 111718334Speter 111818334Speter /* 111918334Speter * We don't want to get actual interrupts, just masked ones. 112018334Speter * Interrupts from this line should already be masked in the ICU, 112118334Speter * but mask them in the processor as well in case there are some 112218334Speter * (misconfigured) shared interrupts. 112318334Speter */ 112418334Speter disable_intr(); 112518334Speter/* EXTRA DELAY? */ 112618334Speter 112718334Speter /* 112818334Speter * Initialize the speed and the word size and wait long enough to 112918334Speter * drain the maximum of 16 bytes of junk in device output queues. 113018334Speter * The speed is undefined after a master reset and must be set 113118334Speter * before relying on anything related to output. There may be 113218334Speter * junk after a (very fast) soft reboot and (apparently) after 113318334Speter * master reset. 113418334Speter * XXX what about the UART bug avoided by waiting in comparam()? 113518334Speter * We don't want to to wait long enough to drain at 2 bps. 113618334Speter */ 113718334Speter if (iobase == siocniobase) 113818334Speter DELAY((16 + 1) * 1000000 / (comdefaultrate / 10)); 113918334Speter else { 114018334Speter#ifdef PC98 114118334Speter tmp = ttspeedtab(SIO_TEST_SPEED, 114218334Speter if_16550a_type[iod.if_type & 0x0f].speedtab); 114318334Speter outb(iobase + (com_cfcr << port_shift), CFCR_DLAB|CFCR_8BITS); 114418334Speter outb(iobase + (com_dlbl << port_shift), tmp & 0xff); 114518334Speter outb(iobase + (com_dlbh << port_shift), (tmp >> 8) & 0xff); 114618334Speter outb(iobase + (com_cfcr << port_shift), CFCR_8BITS); 114718334Speter#else 114818334Speter outb(iobase + com_cfcr, CFCR_DLAB | CFCR_8BITS); 114918334Speter outb(iobase + com_dlbl, COMBRD(SIO_TEST_SPEED) & 0xff); 115018334Speter outb(iobase + com_dlbh, (u_int) COMBRD(SIO_TEST_SPEED) >> 8); 115118334Speter outb(iobase + com_cfcr, CFCR_8BITS); 115218334Speter#endif 115318334Speter DELAY((16 + 1) * 1000000 / (SIO_TEST_SPEED / 10)); 115418334Speter } 115518334Speter 115618334Speter /* 115718334Speter * Enable the interrupt gate and disable device interupts. This 115818334Speter * should leave the device driving the interrupt line low and 115918334Speter * guarantee an edge trigger if an interrupt can be generated. 116018334Speter */ 116118334Speter/* EXTRA DELAY? */ 116218334Speter#ifdef PC98 116318334Speter outb(iobase + (com_mcr << port_shift), mcr_image); 116450600Sobrien outb(iobase + (com_ier << port_shift), 0); 116518334Speter#else 116618334Speter outb(iobase + com_mcr, mcr_image); 116718334Speter outb(iobase + com_ier, 0); 116818334Speter#endif 116918334Speter DELAY(1000); /* XXX */ 117018334Speter irqmap[0] = isa_irq_pending(); 117118334Speter 117218334Speter /* 117318334Speter * Attempt to set loopback mode so that we can send a null byte 117418334Speter * without annoying any external device. 117518334Speter */ 117618334Speter/* EXTRA DELAY? */ 117718334Speter#ifdef PC98 117850600Sobrien outb(iobase + (com_mcr << port_shift), mcr_image | MCR_LOOPBACK); 117950600Sobrien#else 118050600Sobrien outb(iobase + com_mcr, mcr_image | MCR_LOOPBACK); 118150600Sobrien#endif 118250600Sobrien 118318334Speter /* 118418334Speter * Attempt to generate an output interrupt. On 8250's, setting 118518334Speter * IER_ETXRDY generates an interrupt independent of the current 118618334Speter * setting and independent of whether the THR is empty. On 16450's, 118718334Speter * setting IER_ETXRDY generates an interrupt independent of the 118818334Speter * current setting. On 16550A's, setting IER_ETXRDY only 118918334Speter * generates an interrupt when IER_ETXRDY is not already set. 119018334Speter */ 119118334Speter#ifdef PC98 119218334Speter outb(iobase + (com_ier << port_shift), IER_ETXRDY); 119318334Speter if (iod.if_type == COM_IF_RSA98III) { 119418334Speter outb(rsabase + rsa_ier, 0x04); 119518334Speter } 119618334Speter#else 119718334Speter outb(iobase + com_ier, IER_ETXRDY); 119818334Speter#endif /* PC98 */ 119918334Speter 120018334Speter /* 120118334Speter * On some 16x50 incompatibles, setting IER_ETXRDY doesn't generate 120218334Speter * an interrupt. They'd better generate one for actually doing 120318334Speter * output. Loopback may be broken on the same incompatibles but 120418334Speter * it's unlikely to do more than allow the null byte out. 120518334Speter */ 120618334Speter#ifdef PC98 120718334Speter outb(iobase + (com_data << port_shift), 0); 120818334Speter#else 120918334Speter outb(iobase + com_data, 0); 121018334Speter#endif 121118334Speter DELAY((1 + 2) * 1000000 / (SIO_TEST_SPEED / 10)); 121218334Speter 121318334Speter /* 121418334Speter * Turn off loopback mode so that the interrupt gate works again 121518334Speter * (MCR_IENABLE was hidden). This should leave the device driving 121618334Speter * an interrupt line high. It doesn't matter if the interrupt 121718334Speter * line oscillates while we are not looking at it, since interrupts 121818334Speter * are disabled. 121918334Speter */ 122018334Speter/* EXTRA DELAY? */ 122118334Speter#ifdef PC98 122218334Speter outb(iobase + (com_mcr << port_shift), mcr_image); 122318334Speter#else 122418334Speter outb(iobase + com_mcr, mcr_image); 122518334Speter#endif /* PC98 */ 122618334Speter 122718334Speter /* 122818334Speter * It's a definitly Serial PCMCIA(16550A), but still be required 122918334Speter * for IIR_TXRDY implementation ( Palido 321s, DC-1S... ) 123018334Speter */ 123118334Speter if ( COM_NOPROBE(dev) ) { 123218334Speter /* Reading IIR register twice */ 123318334Speter for ( fn = 0; fn < 2; fn ++ ) { 123418334Speter DELAY(10000); 123518334Speter#ifdef PC98 123618334Speter failures[6] = inb(iobase + (com_iir << port_shift)); 123718334Speter#else 123818334Speter failures[6] = inb(iobase + com_iir); 123918334Speter#endif 124018334Speter } 124118334Speter /* Check IIR_TXRDY clear ? */ 124218334Speter#ifdef PC98 124318334Speter result = if_16550a_type[iod.if_type & 0x0f].io_size; 124418334Speter#else 124518334Speter result = IO_COMSIZE; 124618334Speter#endif 124718334Speter if ( failures[6] & IIR_TXRDY ) { 124818334Speter /* Nop, Double check with clearing IER */ 124918334Speter#ifdef PC98 125018334Speter outb(iobase + (com_ier << port_shift), 0); 125118334Speter if (inb(iobase + 125218334Speter (com_iir << port_shift)) & IIR_NOPEND) { 125318334Speter#else 125418334Speter outb(iobase + com_ier, 0); 125518334Speter if ( inb(iobase + com_iir) & IIR_NOPEND ) { 125618334Speter#endif 125718334Speter /* Ok. we're familia this gang */ 125818334Speter dev->id_flags |= COM_C_IIR_TXRDYBUG; /* Set IIR_TXRDYBUG */ 125918334Speter } else { 126018334Speter /* Unknow, Just omit this chip.. XXX*/ 126118334Speter result = 0; 126218334Speter } 126318334Speter } else { 126418334Speter /* OK. this is well-known guys */ 126518334Speter dev->id_flags &= ~COM_C_IIR_TXRDYBUG; /*Clear IIR_TXRDYBUG*/ 126618334Speter } 126718334Speter#ifdef PC98 126818334Speter outb(iobase + (com_cfcr << port_shift), CFCR_8BITS); 126918334Speter#else 127018334Speter outb(iobase + com_cfcr, CFCR_8BITS); 127118334Speter#endif 127218334Speter enable_intr(); 127318334Speter return (iobase == siocniobase ? IO_COMSIZE : result); 127418334Speter } 127518334Speter 127618334Speter /* 127718334Speter * Check that 127818334Speter * o the CFCR, IER and MCR in UART hold the values written to them 127918334Speter * (the values happen to be all distinct - this is good for 128018334Speter * avoiding false positive tests from bus echoes). 128118334Speter * o an output interrupt is generated and its vector is correct. 128218334Speter * o the interrupt goes away when the IIR in the UART is read. 128318334Speter */ 128418334Speter/* EXTRA DELAY? */ 128518334Speter#ifdef PC98 128618334Speter failures[0] = inb(iobase + (com_cfcr << port_shift)) - CFCR_8BITS; 128718334Speter failures[1] = inb(iobase + (com_ier << port_shift)) - IER_ETXRDY; 128818334Speter failures[2] = inb(iobase + (com_mcr << port_shift)) - mcr_image; 128918334Speter#else 129018334Speter failures[0] = inb(iobase + com_cfcr) - CFCR_8BITS; 129118334Speter failures[1] = inb(iobase + com_ier) - IER_ETXRDY; 129218334Speter failures[2] = inb(iobase + com_mcr) - mcr_image; 129318334Speter#endif 129418334Speter DELAY(10000); /* Some internal modems need this time */ 129518334Speter irqmap[1] = isa_irq_pending(); 129618334Speter#ifdef PC98 129718334Speter failures[4] = (inb(iobase + (com_iir << port_shift)) & IIR_IMASK) 129818334Speter - IIR_TXRDY; 129918334Speter if (iod.if_type == COM_IF_RSA98III) { 130018334Speter inb(rsabase + rsa_srr); 130118334Speter } 130218334Speter#else 130318334Speter failures[4] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_TXRDY; 130418334Speter#endif 130518334Speter DELAY(1000); /* XXX */ 130618334Speter irqmap[2] = isa_irq_pending(); 130718334Speter#ifdef PC98 130818334Speter failures[6] = (inb(iobase + (com_iir << port_shift)) & IIR_IMASK) 130918334Speter - IIR_NOPEND; 131018334Speter if (iod.if_type == COM_IF_RSA98III) { 131118334Speter inb(rsabase + rsa_srr); 131218334Speter } 131318334Speter#else 131418334Speter failures[6] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_NOPEND; 131518334Speter#endif 131618334Speter 131718334Speter /* 131818334Speter * Turn off all device interrupts and check that they go off properly. 131918334Speter * Leave MCR_IENABLE alone. For ports without a master port, it gates 132018334Speter * the OUT2 output of the UART to 132118334Speter * the ICU input. Closing the gate would give a floating ICU input 132218334Speter * (unless there is another device driving it) and spurious interrupts. 132318334Speter * (On the system that this was first tested on, the input floats high 132418334Speter * and gives a (masked) interrupt as soon as the gate is closed.) 132518334Speter */ 132618334Speter#ifdef PC98 132718334Speter outb(iobase + (com_ier << port_shift), 0); 132818334Speter outb(iobase + (com_cfcr << port_shift), CFCR_8BITS); 132918334Speter failures[7] = inb(iobase + (com_ier << port_shift)); 133018334Speter if (iod.if_type == COM_IF_RSA98III) { 133118334Speter outb(rsabase + rsa_ier, 0x00); 133218334Speter } 133318334Speter#else 133418334Speter outb(iobase + com_ier, 0); 133518334Speter outb(iobase + com_cfcr, CFCR_8BITS); /* dummy to avoid bus echo */ 133618334Speter failures[7] = inb(iobase + com_ier); 133718334Speter#endif 133818334Speter DELAY(1000); /* XXX */ 133918334Speter irqmap[3] = isa_irq_pending(); 134018334Speter#ifdef PC98 134118334Speter failures[9] = (inb(iobase + (com_iir << port_shift)) & IIR_IMASK) 134218334Speter - IIR_NOPEND; 134318334Speter if (iod.if_type == COM_IF_RSA98III) { 134418334Speter inb(rsabase + rsa_srr); 134518334Speter outb(rsabase + rsa_frr, 0x00); 134618334Speter } 134718334Speter#else 134818334Speter failures[9] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_NOPEND; 134918334Speter#endif 135018334Speter 135118334Speter enable_intr(); 135218334Speter 135318334Speter irqs = irqmap[1] & ~irqmap[0]; 135418334Speter if (idev->id_irq != 0 && (idev->id_irq & irqs) == 0) 135518334Speter printf( 135618334Speter "sio%d: configured irq %d not in bitmap of probed irqs %#x\n", 135718334Speter dev->id_unit, ffs(idev->id_irq) - 1, irqs); 135818334Speter if (bootverbose) 135918334Speter printf("sio%d: irq maps: %#x %#x %#x %#x\n", 136018334Speter dev->id_unit, irqmap[0], irqmap[1], irqmap[2], irqmap[3]); 136118334Speter 136218334Speter#ifdef PC98 136318334Speter result = if_16550a_type[iod.if_type & 0x0f].io_size; 136418334Speter#else 136518334Speter result = IO_COMSIZE; 136618334Speter#endif 136718334Speter for (fn = 0; fn < sizeof failures; ++fn) 136818334Speter if (failures[fn]) { 136918334Speter#ifdef PC98 137018334Speter outb(iobase + (com_mcr << port_shift), 0); 137118334Speter#else 137218334Speter outb(iobase + com_mcr, 0); 137318334Speter#endif 137418334Speter result = 0; 137518334Speter if (bootverbose) { 137618334Speter printf("sio%d: probe failed test(s):", 137718334Speter dev->id_unit); 137818334Speter for (fn = 0; fn < sizeof failures; ++fn) 137918334Speter if (failures[fn]) 138018334Speter printf(" %d", fn); 138150600Sobrien printf("\n"); 138250600Sobrien } 138350600Sobrien break; 138418334Speter } 138518334Speter return (iobase == siocniobase ? IO_COMSIZE : result); 138618334Speter} 138718334Speter 138818334Speter#ifdef COM_ESP 138918334Speterstatic int 139018334Speterespattach(isdp, com, esp_port) 139118334Speter struct isa_device *isdp; 139218334Speter struct com_s *com; 139350600Sobrien Port_t esp_port; 139418334Speter{ 139518334Speter u_char dips; 139618334Speter u_char val; 139718334Speter 139818334Speter /* 139918334Speter * Check the ESP-specific I/O port to see if we're an ESP 140018334Speter * card. If not, return failure immediately. 140118334Speter */ 140218334Speter if ((inb(esp_port) & 0xf3) == 0) { 140318334Speter printf(" port 0x%x is not an ESP board?\n", esp_port); 140418334Speter return (0); 140518334Speter } 140618334Speter 140718334Speter /* 140818334Speter * We've got something that claims to be a Hayes ESP card. 140918334Speter * Let's hope so. 141018334Speter */ 141118334Speter 141252561Sobrien /* Get the dip-switch configuration */ 141352561Sobrien#ifdef PC98 141452561Sobrien outb(esp_port + ESP98_CMD1, ESP_GETDIPS); 141518334Speter dips = inb(esp_port + ESP98_STATUS1); 141650600Sobrien#else 141750600Sobrien outb(esp_port + ESP_CMD1, ESP_GETDIPS); 141818334Speter dips = inb(esp_port + ESP_STATUS1); 141918334Speter#endif 142018334Speter 142118334Speter /* 142218334Speter * Bits 0,1 of dips say which COM port we are. 142318334Speter */ 142418334Speter#ifdef PC98 142518334Speter if ((com->iobase & 0xff) == likely_com_ports[dips & 0x03]) 142618334Speter#else 142718334Speter if (com->iobase == likely_com_ports[dips & 0x03]) 142818334Speter#endif 142918334Speter printf(" : ESP"); 143018334Speter else { 143118334Speter printf(" esp_port has com %d\n", dips & 0x03); 143218334Speter return (0); 143318334Speter } 143418334Speter 143518334Speter /* 143618334Speter * Check for ESP version 2.0 or later: bits 4,5,6 = 010. 143718334Speter */ 143818334Speter#ifdef PC98 143918334Speter outb(esp_port + ESP98_CMD1, ESP_GETTEST); 144018334Speter val = inb(esp_port + ESP98_STATUS1); /* clear reg 1 */ 144118334Speter val = inb(esp_port + ESP98_STATUS2); 144218334Speter#else 144318334Speter outb(esp_port + ESP_CMD1, ESP_GETTEST); 144450600Sobrien val = inb(esp_port + ESP_STATUS1); /* clear reg 1 */ 144518334Speter val = inb(esp_port + ESP_STATUS2); 144618334Speter#endif 144718334Speter if ((val & 0x70) < 0x20) { 144818334Speter printf("-old (%o)", val & 0x70); 144918334Speter return (0); 145018334Speter } 145118334Speter 145218334Speter /* 145318334Speter * Check for ability to emulate 16550: bit 7 == 1 145418334Speter */ 145518334Speter if ((dips & 0x80) == 0) { 145618334Speter printf(" slave"); 145718334Speter return (0); 145818334Speter } 145918334Speter 146050600Sobrien /* 146150600Sobrien * Okay, we seem to be a Hayes ESP card. Whee. 146250600Sobrien */ 146318334Speter com->esp = TRUE; 146418334Speter com->esp_port = esp_port; 146518334Speter return (1); 146618334Speter} 146718334Speter#endif /* COM_ESP */ 146818334Speter 146918334Speterstatic int 147018334Spetersioattach(isdp) 147118334Speter struct isa_device *isdp; 147218334Speter{ 147318334Speter struct com_s *com; 147418334Speter dev_t dev; 147518334Speter#ifdef COM_ESP 147618334Speter Port_t *espp; 147718334Speter#endif 147818334Speter#ifdef COM_MULTIPORT 147918334Speter struct isa_device *idev; 148018334Speter#endif 148118334Speter Port_t iobase; 148218334Speter int s; 148318334Speter int unit; 148418334Speter#ifdef PC98 148518334Speter int port_shift = 0; 148650600Sobrien u_long obufsize; 148718334Speter#endif 148850600Sobrien 148918334Speter isdp->id_ointr = siointr; 149050600Sobrien isdp->id_ri_flags |= RI_FAST; 149150600Sobrien#ifdef PC98 149250600Sobrien if (((isdp->id_flags >> 24) & 0xff) == COM_IF_RSA98III) 149350600Sobrien iobase = isdp->id_iobase + 8; 149450600Sobrien else 149550600Sobrien#endif 149650600Sobrien iobase = isdp->id_iobase; 149750600Sobrien unit = isdp->id_unit; 149850600Sobrien#ifndef PC98 149950600Sobrien com = malloc(sizeof *com, M_DEVBUF, M_NOWAIT); 150050600Sobrien#else 150150600Sobrien obufsize = 256; 150250600Sobrien if (((isdp->id_flags >> 24) & 0xff) == COM_IF_RSA98III) 150350600Sobrien obufsize = 2048; 150450600Sobrien com = malloc((sizeof *com) + obufsize * 2, M_DEVBUF, M_NOWAIT); 150550600Sobrien#endif 150650600Sobrien if (com == NULL) 150750600Sobrien return (0); 150850600Sobrien 150950600Sobrien /* 151050600Sobrien * sioprobe() has initialized the device registers as follows: 151150600Sobrien * o cfcr = CFCR_8BITS. 151250600Sobrien * It is most important that CFCR_DLAB is off, so that the 151350600Sobrien * data port is not hidden when we enable interrupts. 151450600Sobrien * o ier = 0. 151550600Sobrien * Interrupts are only enabled when the line is open. 151650600Sobrien * o mcr = MCR_IENABLE, or 0 if the port has AST/4 compatible 151750600Sobrien * interrupt control register or the config specifies no irq. 151850600Sobrien * Keeping MCR_DTR and MCR_RTS off might stop the external 151950600Sobrien * device from sending before we are ready. 152050600Sobrien */ 152150600Sobrien bzero(com, sizeof *com); 152250600Sobrien#ifdef PC98 152350600Sobrien com->obufsize = obufsize; 152450600Sobrien com->obuf1 = (u_char *)com + (sizeof *com); 152550600Sobrien com->obuf2 = com->obuf1 + obufsize; 152650600Sobrien bzero(com->obuf1, obufsize * 2); 152750600Sobrien#endif 152850600Sobrien com->unit = unit; 152950600Sobrien com->cfcr_image = CFCR_8BITS; 153050600Sobrien com->dtr_wait = 3 * hz; 153150600Sobrien com->loses_outints = COM_LOSESOUTINTS(isdp) != 0; 153250600Sobrien com->no_irq = isdp->id_irq == 0; 153350600Sobrien com->tx_fifo_size = 1; 153450600Sobrien com->obufs[0].l_head = com->obuf1; 153550600Sobrien com->obufs[1].l_head = com->obuf2; 153650600Sobrien 153750600Sobrien com->iobase = iobase; 153850600Sobrien#ifdef PC98 153950600Sobrien if (pc98_set_ioport(com, isdp->id_flags) == -1) { 154050600Sobrien com->pc98_if_type = (isdp->id_flags >> 24) & 0xff; 154150600Sobrien port_shift = if_16550a_type[com->pc98_if_type & 0x0f].port_shift; 154250600Sobrien com->data_port = iobase + (com_data << port_shift); 154350600Sobrien com->int_id_port = iobase + (com_iir << port_shift); 154450600Sobrien com->modem_ctl_port = iobase + (com_mcr << port_shift); 154550600Sobrien com->mcr_image = inb(com->modem_ctl_port); 154650600Sobrien com->line_status_port = iobase + (com_lsr << port_shift); 154750600Sobrien com->modem_status_port = iobase + (com_msr << port_shift); 154850600Sobrien com->intr_ctl_port = iobase + (com_ier << port_shift); 154950600Sobrien } 155050600Sobrien#else /* not PC98 */ 155150600Sobrien com->data_port = iobase + com_data; 155250600Sobrien com->int_id_port = iobase + com_iir; 155350600Sobrien com->modem_ctl_port = iobase + com_mcr; 155450600Sobrien com->mcr_image = inb(com->modem_ctl_port); 155550600Sobrien com->line_status_port = iobase + com_lsr; 155650600Sobrien com->modem_status_port = iobase + com_msr; 155750600Sobrien com->intr_ctl_port = iobase + com_ier; 155850600Sobrien#endif 155950600Sobrien 156050600Sobrien /* 156150600Sobrien * We don't use all the flags from <sys/ttydefaults.h> since they 156250600Sobrien * are only relevant for logins. It's important to have echo off 156350600Sobrien * initially so that the line doesn't start blathering before the 156450600Sobrien * echo flag can be turned off. 156550600Sobrien */ 156650600Sobrien com->it_in.c_iflag = 0; 156750600Sobrien com->it_in.c_oflag = 0; 156850600Sobrien com->it_in.c_cflag = TTYDEF_CFLAG; 156950600Sobrien com->it_in.c_lflag = 0; 157050600Sobrien if (unit == comconsole) { 157150600Sobrien#ifdef PC98 157250600Sobrien if (IS_8251(com->pc98_if_type)) 157350600Sobrien DELAY(100000); 157450600Sobrien#endif 157550600Sobrien com->it_in.c_iflag = TTYDEF_IFLAG; 157650600Sobrien com->it_in.c_oflag = TTYDEF_OFLAG; 157750600Sobrien com->it_in.c_cflag = TTYDEF_CFLAG | CLOCAL; 157850600Sobrien com->it_in.c_lflag = TTYDEF_LFLAG; 157950600Sobrien com->lt_out.c_cflag = com->lt_in.c_cflag = CLOCAL; 158050600Sobrien com->lt_out.c_ispeed = com->lt_out.c_ospeed = 158150600Sobrien com->lt_in.c_ispeed = com->lt_in.c_ospeed = 158250600Sobrien com->it_in.c_ispeed = com->it_in.c_ospeed = comdefaultrate; 158350600Sobrien } else 158450600Sobrien com->it_in.c_ispeed = com->it_in.c_ospeed = TTYDEF_SPEED; 158550600Sobrien if (siosetwater(com, com->it_in.c_ispeed) != 0) { 158650600Sobrien enable_intr(); 158750600Sobrien free(com, M_DEVBUF); 158850600Sobrien return (0); 158950600Sobrien } 159050600Sobrien enable_intr(); 159150600Sobrien termioschars(&com->it_in); 159218334Speter com->it_out = com->it_in; 159318334Speter 159418334Speter /* attempt to determine UART type */ 159518334Speter printf("sio%d: type", unit); 159618334Speter 159718334Speter 159818334Speter#ifndef PC98 159918334Speter#ifdef COM_MULTIPORT 160018334Speter if (!COM_ISMULTIPORT(isdp) && !COM_IIR_TXRDYBUG(isdp)) 160150600Sobrien#else 160218334Speter if (!COM_IIR_TXRDYBUG(isdp)) 160318334Speter#endif 160418334Speter { 160550600Sobrien u_char scr; 160618334Speter u_char scr1; 160718334Speter u_char scr2; 160818334Speter 160918334Speter scr = inb(iobase + com_scr); 161018334Speter outb(iobase + com_scr, 0xa5); 161150600Sobrien scr1 = inb(iobase + com_scr); 161218334Speter outb(iobase + com_scr, 0x5a); 161318334Speter scr2 = inb(iobase + com_scr); 161450600Sobrien outb(iobase + com_scr, scr); 161550600Sobrien if (scr1 != 0xa5 || scr2 != 0x5a) { 161650600Sobrien printf(" 8250"); 161718334Speter goto determined_type; 161818334Speter } 161950600Sobrien } 162018334Speter#endif /* !PC98 */ 162118334Speter#ifdef PC98 162218334Speter if (IS_8251(com->pc98_if_type)) { 162318334Speter com_int_TxRx_disable( com ); 162418334Speter com_cflag_and_speed_set( com, com->it_in.c_cflag, comdefaultrate ); 162518334Speter com_tiocm_bic( com, TIOCM_DTR|TIOCM_RTS|TIOCM_LE ); 162618334Speter com_send_break_off( com ); 162718334Speter printf(" 8251%s", if_8251_type[com->pc98_if_type & 0x0f].name); 162818334Speter } else { 162918334Speter outb(iobase + (com_fifo << port_shift), FIFO_ENABLE | FIFO_RX_HIGH); 163018334Speter#else 163118334Speter outb(iobase + com_fifo, FIFO_ENABLE | FIFO_RX_HIGH); 163218334Speter#endif /* PC98 */ 163318334Speter DELAY(100); 163418334Speter com->st16650a = 0; 163518334Speter switch (inb(com->int_id_port) & IIR_FIFO_MASK) { 163618334Speter case FIFO_RX_LOW: 163718334Speter printf(" 16450"); 163818334Speter break; 163918334Speter case FIFO_RX_MEDL: 164018334Speter printf(" 16450?"); 164118334Speter break; 164218334Speter case FIFO_RX_MEDH: 164350600Sobrien printf(" 16550?"); 164418334Speter break; 164518334Speter case FIFO_RX_HIGH: 164618334Speter if (COM_NOFIFO(isdp)) { 164718334Speter printf(" 16550A fifo disabled"); 164818334Speter } else { 164918334Speter com->hasfifo = TRUE; 165018334Speter#ifdef PC98 165118334Speter com->tx_fifo_size = 0; /* XXX flag conflicts. */ 165218334Speter printf(" 16550A"); 165318334Speter#else 165418334Speter if (COM_ST16650A(isdp)) { 165518334Speter com->st16650a = 1; 165618334Speter com->tx_fifo_size = 32; 165718334Speter printf(" ST16650A"); 165818334Speter } else { 165918334Speter com->tx_fifo_size = COM_FIFOSIZE(isdp); 166018334Speter printf(" 16550A"); 166118334Speter } 166218334Speter#endif 166318334Speter } 166418334Speter#ifdef PC98 166518334Speter if (com->pc98_if_type == COM_IF_RSA98III) { 166618334Speter com->tx_fifo_size = 2048; 166718334Speter com->rsabase = isdp->id_iobase; 166818334Speter outb(com->rsabase + rsa_ier, 0x00); 166918334Speter outb(com->rsabase + rsa_frr, 0x00); 167018334Speter } 167118334Speter#endif 167218334Speter 167318334Speter#ifdef COM_ESP 167450600Sobrien#ifdef PC98 167518334Speter if (com->pc98_if_type == COM_IF_ESP98) 167650600Sobrien#endif 167718334Speter for (espp = likely_esp_ports; *espp != 0; espp++) 167850600Sobrien if (espattach(isdp, com, *espp)) { 167950600Sobrien com->tx_fifo_size = 1024; 168050600Sobrien break; 168150600Sobrien } 168218334Speter#endif 168318334Speter if (!com->st16650a) { 168418334Speter if (!com->tx_fifo_size) 168518334Speter com->tx_fifo_size = 16; 168618334Speter else 168718334Speter printf(" lookalike with %d bytes FIFO", 168818334Speter com->tx_fifo_size); 168918334Speter } 169018334Speter 169118334Speter break; 169218334Speter } 169318334Speter 169418334Speter#ifdef PC98 169518334Speter if (com->pc98_if_type == COM_IF_RSB3000) { 169618334Speter /* Set RSB-2000/3000 Extended Buffer mode. */ 169718334Speter u_char lcr; 169818334Speter lcr = inb(iobase + (com_cfcr << port_shift)); 169918334Speter outb(iobase + (com_cfcr << port_shift), lcr | CFCR_DLAB); 170018334Speter outb(iobase + (com_emr << port_shift), EMR_EXBUFF | EMR_EFMODE); 170118334Speter outb(iobase + (com_cfcr << port_shift), lcr); 170218334Speter } 170318334Speter#endif 170418334Speter 170518334Speter#ifdef COM_ESP 170618334Speter if (com->esp) { 170718334Speter /* 170818334Speter * Set 16550 compatibility mode. 170950600Sobrien * We don't use the ESP_MODE_SCALE bit to increase the 171018334Speter * fifo trigger levels because we can't handle large 171118334Speter * bursts of input. 171218334Speter * XXX flow control should be set in comparam(), not here. 171318334Speter */ 171418334Speter#ifdef PC98 171518334Speter outb(com->esp_port + ESP98_CMD1, ESP_SETMODE); 171618334Speter outb(com->esp_port + ESP98_CMD2, ESP_MODE_RTS | ESP_MODE_FIFO); 171718334Speter#else 171818334Speter outb(com->esp_port + ESP_CMD1, ESP_SETMODE); 171918334Speter outb(com->esp_port + ESP_CMD2, ESP_MODE_RTS | ESP_MODE_FIFO); 172018334Speter#endif 172118334Speter 172218334Speter /* Set RTS/CTS flow control. */ 172318334Speter#ifdef PC98 172418334Speter outb(com->esp_port + ESP98_CMD1, ESP_SETFLOWTYPE); 172518334Speter outb(com->esp_port + ESP98_CMD2, ESP_FLOW_RTS); 172650600Sobrien outb(com->esp_port + ESP98_CMD2, ESP_FLOW_CTS); 172750600Sobrien#else 172850600Sobrien outb(com->esp_port + ESP_CMD1, ESP_SETFLOWTYPE); 172950600Sobrien outb(com->esp_port + ESP_CMD2, ESP_FLOW_RTS); 173050600Sobrien outb(com->esp_port + ESP_CMD2, ESP_FLOW_CTS); 173150600Sobrien#endif 173218334Speter 173350600Sobrien /* Set flow-control levels. */ 173418334Speter#ifdef PC98 173550600Sobrien outb(com->esp_port + ESP98_CMD1, ESP_SETRXFLOW); 173618334Speter outb(com->esp_port + ESP98_CMD2, HIBYTE(768)); 173750600Sobrien outb(com->esp_port + ESP98_CMD2, LOBYTE(768)); 173850600Sobrien outb(com->esp_port + ESP98_CMD2, HIBYTE(512)); 173950600Sobrien outb(com->esp_port + ESP98_CMD2, LOBYTE(512)); 174050600Sobrien#else 174150600Sobrien outb(com->esp_port + ESP_CMD1, ESP_SETRXFLOW); 174250600Sobrien outb(com->esp_port + ESP_CMD2, HIBYTE(768)); 174350600Sobrien outb(com->esp_port + ESP_CMD2, LOBYTE(768)); 174450600Sobrien outb(com->esp_port + ESP_CMD2, HIBYTE(512)); 174550600Sobrien outb(com->esp_port + ESP_CMD2, LOBYTE(512)); 174650600Sobrien#endif 174718334Speter 174850600Sobrien#ifdef PC98 174950600Sobrien /* Set UART clock prescaler. */ 175050600Sobrien outb(com->esp_port + ESP98_CMD1, ESP_SETCLOCK); 175150600Sobrien outb(com->esp_port + ESP98_CMD2, 2); /* 4 times */ 175250600Sobrien#endif 175318334Speter } 175450600Sobrien#endif /* COM_ESP */ 175550600Sobrien#ifdef PC98 175650600Sobrien printf("%s", if_16550a_type[com->pc98_if_type & 0x0f].name); 175750600Sobrien outb(iobase + (com_fifo << port_shift), 0); 175850600Sobrien#else 175950600Sobrien outb(iobase + com_fifo, 0); 176050600Sobriendetermined_type: ; 176150600Sobrien#endif 176250600Sobrien 176350600Sobrien#ifdef COM_MULTIPORT 176450600Sobrien if (COM_ISMULTIPORT(isdp)) { 176550600Sobrien com->multiport = TRUE; 176650600Sobrien printf(" (multiport"); 176750600Sobrien if (unit == COM_MPMASTER(isdp)) 176850600Sobrien printf(" master"); 176950600Sobrien printf(")"); 177050600Sobrien idev = find_isadev(isa_devtab_tty, &siodriver, 177150600Sobrien COM_MPMASTER(isdp)); 177250600Sobrien com->no_irq = (idev == NULL || idev->id_irq == 0); 177350600Sobrien } 177450600Sobrien#endif /* COM_MULTIPORT */ 177550600Sobrien#ifdef PC98 177650600Sobrien } 177750600Sobrien#endif 177850600Sobrien if (unit == comconsole) 177950600Sobrien printf(", console"); 178050600Sobrien if ( COM_IIR_TXRDYBUG(isdp) ) 178150600Sobrien printf(" with a bogus IIR_TXRDY register"); 178250600Sobrien printf("\n"); 178350600Sobrien 178450600Sobrien s = spltty(); 178550600Sobrien com_addr(unit) = com; 178650600Sobrien splx(s); 178750600Sobrien 178850600Sobrien if (!sio_registered) { 178950600Sobrien dev = makedev(CDEV_MAJOR, 0); 179050600Sobrien cdevsw_add(&dev, &sio_cdevsw, NULL); 179150600Sobrien register_swi(SWI_TTY, siopoll); 179250600Sobrien sio_registered = TRUE; 179350600Sobrien } 179450600Sobrien#ifdef DEVFS 179550600Sobrien com->devfs_token_ttyd = devfs_add_devswf(&sio_cdevsw, 179650600Sobrien unit, DV_CHR, 179750600Sobrien UID_ROOT, GID_WHEEL, 0600, "ttyd%r", unit); 179850600Sobrien com->devfs_token_ttyi = devfs_add_devswf(&sio_cdevsw, 179950600Sobrien unit | CONTROL_INIT_STATE, DV_CHR, 180050600Sobrien UID_ROOT, GID_WHEEL, 0600, "ttyid%r", unit); 180150600Sobrien com->devfs_token_ttyl = devfs_add_devswf(&sio_cdevsw, 180250600Sobrien unit | CONTROL_LOCK_STATE, DV_CHR, 180350600Sobrien UID_ROOT, GID_WHEEL, 0600, "ttyld%r", unit); 180450600Sobrien com->devfs_token_cuaa = devfs_add_devswf(&sio_cdevsw, 180550600Sobrien unit | CALLOUT_MASK, DV_CHR, 180650600Sobrien UID_UUCP, GID_DIALER, 0660, "cuaa%r", unit); 180750600Sobrien com->devfs_token_cuai = devfs_add_devswf(&sio_cdevsw, 180850600Sobrien unit | CALLOUT_MASK | CONTROL_INIT_STATE, DV_CHR, 180950600Sobrien UID_UUCP, GID_DIALER, 0660, "cuaia%r", unit); 181050600Sobrien com->devfs_token_cual = devfs_add_devswf(&sio_cdevsw, 181150600Sobrien unit | CALLOUT_MASK | CONTROL_LOCK_STATE, DV_CHR, 181250600Sobrien UID_UUCP, GID_DIALER, 0660, "cuala%r", unit); 181350600Sobrien#endif 181450600Sobrien com->id_flags = isdp->id_flags; /* Heritate id_flags for later */ 181550600Sobrien com->pps.ppscap = PPS_CAPTUREASSERT | PPS_CAPTURECLEAR; 181650600Sobrien pps_init(&com->pps); 181750600Sobrien return (1); 181850600Sobrien} 181950600Sobrien 182050600Sobrienstatic int 182150600Sobriensioopen(dev, flag, mode, p) 182250600Sobrien dev_t dev; 182350600Sobrien int flag; 182450600Sobrien int mode; 182550600Sobrien struct proc *p; 182650600Sobrien{ 182750600Sobrien struct com_s *com; 182850600Sobrien int error; 182950600Sobrien Port_t iobase; 183050600Sobrien int mynor; 183150600Sobrien int s; 183250600Sobrien struct tty *tp; 183350600Sobrien int unit; 183418334Speter#ifdef PC98 183550600Sobrien int port_shift = 0; 183650600Sobrien#endif 183750600Sobrien 183850600Sobrien mynor = minor(dev); 183950600Sobrien unit = MINOR_TO_UNIT(mynor); 184050600Sobrien if ((u_int) unit >= NSIOTOT || (com = com_addr(unit)) == NULL) 184150600Sobrien return (ENXIO); 184250600Sobrien if (com->gone) 184350600Sobrien return (ENXIO); 184450600Sobrien if (mynor & CONTROL_MASK) 184550600Sobrien return (0); 184650600Sobrien#if 0 /* XXX */ 184750600Sobrien tp = com->tp = sio_tty[unit] = ttymalloc(sio_tty[unit]); 184850600Sobrien#else 184950600Sobrien tp = com->tp = &sio_tty[unit]; 185050600Sobrien#endif 185150600Sobrien s = spltty(); 185250600Sobrien 185350600Sobrien#ifdef PC98 185450600Sobrien if (!IS_8251(com->pc98_if_type)) 185550600Sobrien port_shift = if_16550a_type[com->pc98_if_type & 0x0f].port_shift; 185650600Sobrien#endif 185750600Sobrien /* 185850600Sobrien * We jump to this label after all non-interrupted sleeps to pick 185918334Speter * up any changes of the device state. 186050600Sobrien */ 186150600Sobrienopen_top: 186250600Sobrien while (com->state & CS_DTR_OFF) { 186350600Sobrien error = tsleep(&com->dtr_wait, TTIPRI | PCATCH, "siodtr", 0); 186450600Sobrien if (com_addr(unit) == NULL) 186550600Sobrien return (ENXIO); 186650600Sobrien if (error != 0 || com->gone) 186750600Sobrien goto out; 186850600Sobrien } 186950600Sobrien if (tp->t_state & TS_ISOPEN) { 187050600Sobrien /* 187150600Sobrien * The device is open, so everything has been initialized. 187250600Sobrien * Handle conflicts. 187350600Sobrien */ 187450600Sobrien if (mynor & CALLOUT_MASK) { 187550600Sobrien if (!com->active_out) { 187650600Sobrien error = EBUSY; 187750600Sobrien goto out; 187850600Sobrien } 187950600Sobrien } else { 188050600Sobrien if (com->active_out) { 188150600Sobrien if (flag & O_NONBLOCK) { 188250600Sobrien error = EBUSY; 188350600Sobrien goto out; 188450600Sobrien } 188550600Sobrien error = tsleep(&com->active_out, 188650600Sobrien TTIPRI | PCATCH, "siobi", 0); 188750600Sobrien if (com_addr(unit) == NULL) 188850600Sobrien return (ENXIO); 188950600Sobrien if (error != 0 || com->gone) 189050600Sobrien goto out; 189150600Sobrien goto open_top; 189250600Sobrien } 189350600Sobrien } 189450600Sobrien if (tp->t_state & TS_XCLUDE && 189550600Sobrien suser(p->p_ucred, &p->p_acflag)) { 189650600Sobrien error = EBUSY; 189750600Sobrien goto out; 189850600Sobrien } 189950600Sobrien } else { 190050600Sobrien /* 190150600Sobrien * The device isn't open, so there are no conflicts. 190250600Sobrien * Initialize it. Initialization is done twice in many 190350600Sobrien * cases: to preempt sleeping callin opens if we are 190450600Sobrien * callout, and to complete a callin open after DCD rises. 190550600Sobrien */ 190650600Sobrien tp->t_oproc = comstart; 190750600Sobrien tp->t_param = comparam; 190850600Sobrien tp->t_dev = dev; 190950600Sobrien tp->t_termios = mynor & CALLOUT_MASK 191050600Sobrien ? com->it_out : com->it_in; 191150600Sobrien#ifdef PC98 191250600Sobrien if (!IS_8251(com->pc98_if_type)) 191350600Sobrien#endif 191450600Sobrien (void)commctl(com, TIOCM_DTR | TIOCM_RTS, DMSET); 191550600Sobrien com->poll = com->no_irq; 191650600Sobrien com->poll_output = com->loses_outints; 191750600Sobrien ++com->wopeners; 191850600Sobrien error = comparam(tp, &tp->t_termios); 191950600Sobrien --com->wopeners; 192050600Sobrien if (error != 0) 192150600Sobrien goto out; 192250600Sobrien#ifdef PC98 192350600Sobrien if (IS_8251(com->pc98_if_type)) { 192450600Sobrien com_tiocm_bis(com, TIOCM_DTR|TIOCM_RTS); 192550600Sobrien pc98_msrint_start(dev); 192650600Sobrien } 192750600Sobrien#endif 192850600Sobrien /* 192950600Sobrien * XXX we should goto open_top if comparam() slept. 193050600Sobrien */ 193118334Speter iobase = com->iobase; 193250600Sobrien if (com->hasfifo) { 193350600Sobrien /* 193450600Sobrien * (Re)enable and drain fifos. 193550600Sobrien * 193650600Sobrien * Certain SMC chips cause problems if the fifos 193750600Sobrien * are enabled while input is ready. Turn off the 193850600Sobrien * fifo if necessary to clear the input. We test 193950600Sobrien * the input ready bit after enabling the fifos 194050600Sobrien * since we've already enabled them in comparam() 194150600Sobrien * and to handle races between enabling and fresh 194250600Sobrien * input. 194350600Sobrien */ 194450600Sobrien while (TRUE) { 194550600Sobrien#ifdef PC98 194650600Sobrien outb(iobase + (com_fifo << port_shift), 194750600Sobrien FIFO_RCV_RST | FIFO_XMT_RST 194850600Sobrien | com->fifo_image); 194950600Sobrien if (com->pc98_if_type == COM_IF_RSA98III) 195050600Sobrien outb(com->rsabase + rsa_frr , 0x00); 195150600Sobrien#else 195250600Sobrien outb(iobase + com_fifo, 195350600Sobrien FIFO_RCV_RST | FIFO_XMT_RST 195450600Sobrien | com->fifo_image); 195550600Sobrien#endif 195650600Sobrien /* 195750600Sobrien * XXX the delays are for superstitious 195850600Sobrien * historical reasons. It must be less than 195950600Sobrien * the character time at the maximum 196050600Sobrien * supported speed (87 usec at 115200 bps 196150600Sobrien * 8N1). Otherwise we might loop endlessly 196250600Sobrien * if data is streaming in. We used to use 196350600Sobrien * delays of 100. That usually worked 196450600Sobrien * because DELAY(100) used to usually delay 196550600Sobrien * for about 85 usec instead of 100. 196650600Sobrien */ 196750600Sobrien DELAY(50); 196850600Sobrien#ifndef PC98 196950600Sobrien if (!(inb(com->line_status_port) & LSR_RXRDY)) 197050600Sobrien#else 197150600Sobrien if (com->pc98_if_type == COM_IF_RSA98III 197250600Sobrien ? !(inb(com->rsabase + rsa_srr) & 0x08) 197350600Sobrien : !(inb(com->line_status_port) & LSR_RXRDY)) 197450600Sobrien#endif 197550600Sobrien break; 197650600Sobrien#ifdef PC98 197750600Sobrien outb(iobase + (com_fifo << port_shift), 0); 197850600Sobrien#else 197950600Sobrien outb(iobase + com_fifo, 0); 198050600Sobrien#endif 198150600Sobrien DELAY(50); 198250600Sobrien (void) inb(com->data_port); 198350600Sobrien } 198450600Sobrien } 198550600Sobrien 198650600Sobrien disable_intr(); 198750600Sobrien#ifdef PC98 198850600Sobrien if (IS_8251(com->pc98_if_type)) { 198950600Sobrien com_tiocm_bis(com, TIOCM_LE); 199050600Sobrien com->pc98_prev_modem_status = pc98_get_modem_status(com); 199150600Sobrien com_int_Rx_enable(com); 199250600Sobrien } else { 199350600Sobrien#endif 199450600Sobrien (void) inb(com->line_status_port); 199550600Sobrien (void) inb(com->data_port); 199650600Sobrien com->prev_modem_status = com->last_modem_status 199750600Sobrien = inb(com->modem_status_port); 199850600Sobrien if (COM_IIR_TXRDYBUG(com)) { 199950600Sobrien outb(com->intr_ctl_port, IER_ERXRDY | IER_ERLS 200050600Sobrien | IER_EMSC); 200150600Sobrien } else { 200250600Sobrien outb(com->intr_ctl_port, IER_ERXRDY | IER_ETXRDY 200350600Sobrien | IER_ERLS | IER_EMSC); 200450600Sobrien } 200550600Sobrien#ifdef PC98 200650600Sobrien if (com->pc98_if_type == COM_IF_RSA98III) { 200750600Sobrien outb(com->rsabase + rsa_ier, 0x1d); 200850600Sobrien outb(com->intr_ctl_port, IER_ERLS | IER_EMSC); 200950600Sobrien } 201050600Sobrien#endif 201150600Sobrien#ifdef PC98 201250600Sobrien } 201350600Sobrien#endif 201450600Sobrien enable_intr(); 201550600Sobrien /* 201650600Sobrien * Handle initial DCD. Callout devices get a fake initial 201750600Sobrien * DCD (trapdoor DCD). If we are callout, then any sleeping 201850600Sobrien * callin opens get woken up and resume sleeping on "siobi" 201950600Sobrien * instead of "siodcd". 202050600Sobrien */ 202150600Sobrien /* 202250600Sobrien * XXX `mynor & CALLOUT_MASK' should be 202350600Sobrien * `tp->t_cflag & (SOFT_CARRIER | TRAPDOOR_CARRIER) where 202450600Sobrien * TRAPDOOR_CARRIER is the default initial state for callout 202550600Sobrien * devices and SOFT_CARRIER is like CLOCAL except it hides 202650600Sobrien * the true carrier. 202750600Sobrien */ 202850600Sobrien#ifdef PC98 202950600Sobrien if ((IS_8251(com->pc98_if_type) && 203050600Sobrien (pc98_get_modem_status(com) & TIOCM_CAR)) || 203150600Sobrien (!IS_8251(com->pc98_if_type) && 203250600Sobrien (com->prev_modem_status & MSR_DCD)) || 203350600Sobrien mynor & CALLOUT_MASK) 203450600Sobrien#else 203550600Sobrien if (com->prev_modem_status & MSR_DCD || mynor & CALLOUT_MASK) 203650600Sobrien#endif 203750600Sobrien (*linesw[tp->t_line].l_modem)(tp, 1); 203850600Sobrien } 203950600Sobrien /* 204050600Sobrien * Wait for DCD if necessary. 204150600Sobrien */ 204250600Sobrien if (!(tp->t_state & TS_CARR_ON) && !(mynor & CALLOUT_MASK) 204350600Sobrien && !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK)) { 204450600Sobrien ++com->wopeners; 204550600Sobrien error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "siodcd", 0); 204650600Sobrien if (com_addr(unit) == NULL) 204750600Sobrien return (ENXIO); 204850600Sobrien --com->wopeners; 204950600Sobrien if (error != 0 || com->gone) 205050600Sobrien goto out; 205150600Sobrien goto open_top; 205250600Sobrien } 205350600Sobrien error = (*linesw[tp->t_line].l_open)(dev, tp); 205450600Sobrien disc_optim(tp, &tp->t_termios, com); 205550600Sobrien if (tp->t_state & TS_ISOPEN && mynor & CALLOUT_MASK) 205650600Sobrien com->active_out = TRUE; 205750600Sobrien siosettimeout(); 205850600Sobrienout: 205950600Sobrien splx(s); 206050600Sobrien if (!(tp->t_state & TS_ISOPEN) && com->wopeners == 0) 206150600Sobrien comhardclose(com); 206250600Sobrien return (error); 206350600Sobrien} 206450600Sobrien 206550600Sobrienstatic int 206650600Sobriensioclose(dev, flag, mode, p) 206750600Sobrien dev_t dev; 206850600Sobrien int flag; 206950600Sobrien int mode; 207050600Sobrien struct proc *p; 207150600Sobrien{ 207250600Sobrien struct com_s *com; 207350600Sobrien int mynor; 207450600Sobrien int s; 207518334Speter struct tty *tp; 207618334Speter 207750600Sobrien mynor = minor(dev); 207818334Speter if (mynor & CONTROL_MASK) 207950600Sobrien return (0); 208050600Sobrien com = com_addr(MINOR_TO_UNIT(mynor)); 208150600Sobrien tp = com->tp; 208218334Speter s = spltty(); 208350600Sobrien (*linesw[tp->t_line].l_close)(tp, flag); 208450600Sobrien#ifdef PC98 208550600Sobrien com->modem_checking = 0; 208650600Sobrien#endif 208750600Sobrien disc_optim(tp, &tp->t_termios, com); 208850600Sobrien siostop(tp, FREAD | FWRITE); 208950600Sobrien comhardclose(com); 209050600Sobrien ttyclose(tp); 209150600Sobrien siosettimeout(); 209250600Sobrien splx(s); 209350600Sobrien if (com->gone) { 209450600Sobrien printf("sio%d: gone\n", com->unit); 209550600Sobrien s = spltty(); 209650600Sobrien com_addr(com->unit) = NULL; 209750600Sobrien if (com->ibuf != NULL) 209850600Sobrien free(com->ibuf, M_DEVBUF); 209950600Sobrien bzero(tp, sizeof *tp); 210050600Sobrien free(com, M_DEVBUF); 210150600Sobrien splx(s); 210250600Sobrien } 210350600Sobrien return (0); 210450600Sobrien} 210550600Sobrien 210650600Sobrienstatic void 210750600Sobriencomhardclose(com) 210850600Sobrien struct com_s *com; 210950600Sobrien{ 211050600Sobrien Port_t iobase; 211150600Sobrien int s; 211250600Sobrien struct tty *tp; 211350600Sobrien int unit; 211450600Sobrien#ifdef PC98 211550600Sobrien int port_shift = 0; 211650600Sobrien#endif 211750600Sobrien 211850600Sobrien unit = com->unit; 211950600Sobrien iobase = com->iobase; 212050600Sobrien s = spltty(); 212150600Sobrien com->poll = FALSE; 212250600Sobrien com->poll_output = FALSE; 212350600Sobrien com->do_timestamp = FALSE; 212450600Sobrien com->do_dcd_timestamp = FALSE; 212550600Sobrien com->pps.ppsparam.mode = 0; 212650600Sobrien#ifdef PC98 212750600Sobrien if (IS_8251(com->pc98_if_type)) 212850600Sobrien com_send_break_off(com); 212950600Sobrien else { 213050600Sobrien port_shift = if_16550a_type[com->pc98_if_type & 0x0f].port_shift; 213150600Sobrien outb(iobase + (com_cfcr << port_shift), 213250600Sobrien com->cfcr_image &= ~CFCR_SBREAK); 213350600Sobrien } 213450600Sobrien#else 213550600Sobrien outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); 213650600Sobrien#endif 213750600Sobrien { 213850600Sobrien#ifdef PC98 213950600Sobrien int tmp; 214050600Sobrien if (IS_8251(com->pc98_if_type)) 214150600Sobrien com_int_TxRx_disable(com); 214250600Sobrien else 214350600Sobrien outb(iobase + (com_ier << port_shift), 0); 214450600Sobrien if (com->pc98_if_type == COM_IF_RSA98III) { 214550600Sobrien outb(com->rsabase + rsa_ier, 0x00); 214650600Sobrien } 214750600Sobrien#else 214850600Sobrien outb(iobase + com_ier, 0); 214950600Sobrien#endif 215050600Sobrien tp = com->tp; 215150600Sobrien#ifdef PC98 215250600Sobrien if (IS_8251(com->pc98_if_type)) 215350600Sobrien tmp = pc98_get_modem_status(com) & TIOCM_CAR; 215450600Sobrien else 215550600Sobrien tmp = com->prev_modem_status & MSR_DCD; 215650600Sobrien#endif 215718334Speter if (tp->t_cflag & HUPCL 215850600Sobrien /* 215950600Sobrien * XXX we will miss any carrier drop between here and the 216050600Sobrien * next open. Perhaps we should watch DCD even when the 216150600Sobrien * port is closed; it is not sufficient to check it at 216250600Sobrien * the next open because it might go up and down while 216350600Sobrien * we're not watching. 216450600Sobrien */ 216550600Sobrien || !com->active_out 216650600Sobrien#ifdef PC98 216750600Sobrien && !(tmp) 216850600Sobrien#else 216950600Sobrien && !(com->prev_modem_status & MSR_DCD) 217050600Sobrien#endif 217150600Sobrien && !(com->it_in.c_cflag & CLOCAL) 217250600Sobrien || !(tp->t_state & TS_ISOPEN)) { 217350600Sobrien#ifdef PC98 217450600Sobrien if (IS_8251(com->pc98_if_type)) 217550600Sobrien com_tiocm_bic(com, TIOCM_DTR|TIOCM_RTS|TIOCM_LE); 217650600Sobrien else 217750600Sobrien#endif 217850600Sobrien (void)commctl(com, TIOCM_DTR, DMBIC); 217950600Sobrien if (com->dtr_wait != 0 && !(com->state & CS_DTR_OFF)) { 218050600Sobrien timeout(siodtrwakeup, com, com->dtr_wait); 218118334Speter com->state |= CS_DTR_OFF; 218250600Sobrien } 218350600Sobrien } 218450600Sobrien#ifdef PC98 218550600Sobrien else { 218650600Sobrien if (IS_8251(com->pc98_if_type)) 218750600Sobrien com_tiocm_bic(com, TIOCM_LE ); 218850600Sobrien } 218918334Speter#endif 219050600Sobrien } 219150600Sobrien if (com->hasfifo) { 219250600Sobrien /* 219350600Sobrien * Disable fifos so that they are off after controlled 219450600Sobrien * reboots. Some BIOSes fail to detect 16550s when the 219550600Sobrien * fifos are enabled. 219650600Sobrien */ 219750600Sobrien#ifdef PC98 219850600Sobrien outb(iobase + (com_fifo << port_shift), 0); 219950600Sobrien#else 220050600Sobrien outb(iobase + com_fifo, 0); 220150600Sobrien#endif 220250600Sobrien } 220350600Sobrien com->active_out = FALSE; 220450600Sobrien wakeup(&com->active_out); 220550600Sobrien wakeup(TSA_CARR_ON(tp)); /* restart any wopeners */ 220650600Sobrien splx(s); 220750600Sobrien} 220850600Sobrien 220950600Sobrienstatic int 221050600Sobriensioread(dev, uio, flag) 221150600Sobrien dev_t dev; 221250600Sobrien struct uio *uio; 221350600Sobrien int flag; 221450600Sobrien{ 221550600Sobrien int mynor; 221650600Sobrien int unit; 221750600Sobrien struct tty *tp; 221850600Sobrien 221950600Sobrien mynor = minor(dev); 222050600Sobrien if (mynor & CONTROL_MASK) 222150600Sobrien return (ENODEV); 222250600Sobrien unit = MINOR_TO_UNIT(mynor); 222350600Sobrien if (com_addr(unit)->gone) 222450600Sobrien return (ENODEV); 222550600Sobrien tp = com_addr(unit)->tp; 222650600Sobrien return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); 222750600Sobrien} 222850600Sobrien 222950600Sobrienstatic int 223050600Sobriensiowrite(dev, uio, flag) 223150600Sobrien dev_t dev; 223250600Sobrien struct uio *uio; 223350600Sobrien int flag; 223450600Sobrien{ 223550600Sobrien int mynor; 223650600Sobrien struct tty *tp; 223750600Sobrien int unit; 223850600Sobrien 223950600Sobrien mynor = minor(dev); 224050600Sobrien if (mynor & CONTROL_MASK) 224150600Sobrien return (ENODEV); 224250600Sobrien 224350600Sobrien unit = MINOR_TO_UNIT(mynor); 224450600Sobrien if (com_addr(unit)->gone) 224550600Sobrien return (ENODEV); 224650600Sobrien tp = com_addr(unit)->tp; 224750600Sobrien /* 224850600Sobrien * (XXX) We disallow virtual consoles if the physical console is 224950600Sobrien * a serial port. This is in case there is a display attached that 225050600Sobrien * is not the console. In that situation we don't need/want the X 225150600Sobrien * server taking over the console. 225250600Sobrien */ 225350600Sobrien if (constty != NULL && unit == comconsole) 225450600Sobrien constty = NULL; 225550600Sobrien return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); 225618334Speter} 225718334Speter 225850600Sobrienstatic void 225918334Spetersiobusycheck(chan) 226050600Sobrien void *chan; 226150600Sobrien{ 226250600Sobrien struct com_s *com; 226350600Sobrien int s; 226418334Speter 226550600Sobrien com = (struct com_s *)chan; 226650600Sobrien 226718334Speter /* 226850600Sobrien * Clear TS_BUSY if low-level output is complete. 226950600Sobrien * spl locking is sufficient because siointr1() does not set CS_BUSY. 227050600Sobrien * If siointr1() clears CS_BUSY after we look at it, then we'll get 227118334Speter * called again. Reading the line status port outside of siointr1() 227250600Sobrien * is safe because CS_BUSY is clear so there are no output interrupts 227350600Sobrien * to lose. 227450600Sobrien */ 227518334Speter s = spltty(); 227650600Sobrien if (com->state & CS_BUSY) 227750600Sobrien com->extra_state &= ~CSE_BUSYCHECK; /* False alarm. */ 227850600Sobrien#ifdef PC98 227950600Sobrien else if ((IS_8251(com->pc98_if_type) && 228050600Sobrien (inb(com->sts_port) & (STS8251_TxRDY | STS8251_TxEMP)) 228150600Sobrien == (STS8251_TxRDY | STS8251_TxEMP)) || 228250600Sobrien (inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY)) 228350600Sobrien == (LSR_TSRE | LSR_TXRDY)) { 228450600Sobrien#else 228550600Sobrien else if ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY)) 228650600Sobrien == (LSR_TSRE | LSR_TXRDY)) { 228750600Sobrien#endif 228850600Sobrien com->tp->t_state &= ~TS_BUSY; 228950600Sobrien ttwwakeup(com->tp); 229050600Sobrien com->extra_state &= ~CSE_BUSYCHECK; 229150600Sobrien } else 229250600Sobrien timeout(siobusycheck, com, hz / 100); 229350600Sobrien splx(s); 229450600Sobrien} 229550600Sobrien 229650600Sobrienstatic void 229750600Sobriensiodtrwakeup(chan) 229850600Sobrien void *chan; 229950600Sobrien{ 230050600Sobrien struct com_s *com; 230150600Sobrien 230250600Sobrien com = (struct com_s *)chan; 230350600Sobrien com->state &= ~CS_DTR_OFF; 230450600Sobrien wakeup(&com->dtr_wait); 230550600Sobrien} 230650600Sobrien 230750600Sobrienstatic void 230850600Sobriensioinput(com) 230950600Sobrien struct com_s *com; 231050600Sobrien{ 231150600Sobrien u_char *buf; 231250600Sobrien int incc; 231350600Sobrien u_char line_status; 231450600Sobrien int recv_data; 231550600Sobrien struct tty *tp; 231650600Sobrien#ifdef PC98 231750600Sobrien u_char tmp; 231850600Sobrien#endif 231950600Sobrien 232050600Sobrien buf = com->ibuf; 232150600Sobrien tp = com->tp; 232250600Sobrien if (!(tp->t_state & TS_ISOPEN) || !(tp->t_cflag & CREAD)) { 232318334Speter com_events -= (com->iptr - com->ibuf); 232418334Speter com->iptr = com->ibuf; 232550600Sobrien return; 232650600Sobrien } 232750600Sobrien if (tp->t_state & TS_CAN_BYPASS_L_RINT) { 232850600Sobrien /* 232950600Sobrien * Avoid the grotesquely inefficient lineswitch routine 233050600Sobrien * (ttyinput) in "raw" mode. It usually takes about 450 233150600Sobrien * instructions (that's without canonical processing or echo!). 233250600Sobrien * slinput is reasonably fast (usually 40 instructions plus 233350600Sobrien * call overhead). 233450600Sobrien */ 233550600Sobrien do { 233650600Sobrien enable_intr(); 233750600Sobrien incc = com->iptr - buf; 233850600Sobrien if (tp->t_rawq.c_cc + incc > tp->t_ihiwat 233950600Sobrien && (com->state & CS_RTS_IFLOW 234050600Sobrien || tp->t_iflag & IXOFF) 234150600Sobrien && !(tp->t_state & TS_TBLOCK)) 234250600Sobrien ttyblock(tp); 234350600Sobrien com->delta_error_counts[CE_TTY_BUF_OVERFLOW] 234450600Sobrien += b_to_q((char *)buf, incc, &tp->t_rawq); 234550600Sobrien buf += incc; 234650600Sobrien tk_nin += incc; 234750600Sobrien tk_rawcc += incc; 234850600Sobrien tp->t_rawcc += incc; 234950600Sobrien ttwakeup(tp); 235050600Sobrien if (tp->t_state & TS_TTSTOP 235150600Sobrien && (tp->t_iflag & IXANY 235250600Sobrien || tp->t_cc[VSTART] == tp->t_cc[VSTOP])) { 235350600Sobrien tp->t_state &= ~TS_TTSTOP; 235418334Speter tp->t_lflag &= ~FLUSHO; 235518334Speter comstart(tp); 235650600Sobrien } 235718334Speter disable_intr(); 235818334Speter } while (buf < com->iptr); 235950600Sobrien } else { 236018334Speter do { 236150600Sobrien enable_intr(); 236250600Sobrien line_status = buf[com->ierroff]; 236350600Sobrien recv_data = *buf++; 236450600Sobrien if (line_status 236550600Sobrien & (LSR_BI | LSR_FE | LSR_OE | LSR_PE)) { 236650600Sobrien if (line_status & LSR_BI) 236750600Sobrien recv_data |= TTY_BI; 236850600Sobrien if (line_status & LSR_FE) 236950600Sobrien recv_data |= TTY_FE; 237050600Sobrien if (line_status & LSR_OE) 237150600Sobrien recv_data |= TTY_OE; 237250600Sobrien if (line_status & LSR_PE) 237350600Sobrien recv_data |= TTY_PE; 237450600Sobrien } 237550600Sobrien (*linesw[tp->t_line].l_rint)(recv_data, tp); 237650600Sobrien disable_intr(); 237750600Sobrien } while (buf < com->iptr); 237818334Speter } 237918334Speter com_events -= (com->iptr - com->ibuf); 238050600Sobrien com->iptr = com->ibuf; 238118334Speter 238218334Speter /* 238350600Sobrien * There is now room for another low-level buffer full of input, 238418334Speter * so enable RTS if it is now disabled and there is room in the 238550600Sobrien * high-level buffer. 238618334Speter */ 238750600Sobrien#ifdef PC98 238818334Speter if (IS_8251(com->pc98_if_type)) 238950600Sobrien tmp = com_tiocm_get(com) & TIOCM_RTS; 239050600Sobrien else 239150600Sobrien tmp = com->mcr_image & MCR_RTS; 239250600Sobrien if ((com->state & CS_RTS_IFLOW) && !(tmp) && 239350600Sobrien !(tp->t_state & TS_TBLOCK)) 239450600Sobrien if (IS_8251(com->pc98_if_type)) 239550600Sobrien com_tiocm_bis(com, TIOCM_RTS); 239650600Sobrien else 239750600Sobrien outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); 239818334Speter#else 239950600Sobrien if ((com->state & CS_RTS_IFLOW) && !(com->mcr_image & MCR_RTS) && 240050600Sobrien !(tp->t_state & TS_TBLOCK)) 240150600Sobrien outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); 240250600Sobrien#endif 240350600Sobrien} 240450600Sobrien 240550600Sobrienstatic void 240650600Sobriensiointr(unit) 240750600Sobrien int unit; 240850600Sobrien{ 240950600Sobrien#ifndef COM_MULTIPORT 241050600Sobrien COM_LOCK(); 241150600Sobrien siointr1(com_addr(unit)); 241250600Sobrien COM_UNLOCK(); 241350600Sobrien#else /* COM_MULTIPORT */ 241450600Sobrien struct com_s *com; 241550600Sobrien bool_t possibly_more_intrs; 241650600Sobrien#ifdef PC98 241750600Sobrien u_char rsa_buf_status; 241850600Sobrien#endif 241950600Sobrien 242050600Sobrien /* 242150600Sobrien * Loop until there is no activity on any port. This is necessary 242250600Sobrien * to get an interrupt edge more than to avoid another interrupt. 242350600Sobrien * If the IRQ signal is just an OR of the IRQ signals from several 242450600Sobrien * devices, then the edge from one may be lost because another is 242550600Sobrien * on. 242650600Sobrien */ 242750600Sobrien COM_LOCK(); 242850600Sobrien do { 242950600Sobrien possibly_more_intrs = FALSE; 243050600Sobrien for (unit = 0; unit < NSIOTOT; ++unit) { 243150600Sobrien com = com_addr(unit); 243250600Sobrien /* 243350600Sobrien * XXX COM_LOCK(); 243450600Sobrien * would it work here, or be counter-productive? 243550600Sobrien */ 243650600Sobrien#ifdef PC98 243750600Sobrien if (com != NULL 243850600Sobrien && !com->gone 243950600Sobrien && IS_8251(com->pc98_if_type)){ 244050600Sobrien siointr1(com); 244150600Sobrien } else 244250600Sobrien#endif /* PC98 */ 244350600Sobrien#ifdef PC98 244418334Speter if (com != NULL 244518334Speter && !com->gone 244650600Sobrien && com->pc98_if_type == COM_IF_RSA98III) { 244750600Sobrien rsa_buf_status = inb(com->rsabase + rsa_srr) & 0xc9; 244850600Sobrien if ((rsa_buf_status & 0xc8) 244918334Speter || !(rsa_buf_status & 0x01)) { 245018334Speter siointr1(com); 245118334Speter if(rsa_buf_status 245218334Speter != (inb(com->rsabase + rsa_srr) & 0xc9)) 245318334Speter possibly_more_intrs = TRUE; 245418334Speter } 245518334Speter } else 245618334Speter#endif 245718334Speter if (com != NULL 245818334Speter && !com->gone 245918334Speter && (inb(com->int_id_port) & IIR_IMASK) 246018334Speter != IIR_NOPEND) { 246118334Speter siointr1(com); 246218334Speter possibly_more_intrs = TRUE; 246318334Speter } 246418334Speter /* XXX COM_UNLOCK(); */ 246518334Speter } 246618334Speter } while (possibly_more_intrs); 246718334Speter COM_UNLOCK(); 246818334Speter#endif /* COM_MULTIPORT */ 246950600Sobrien} 247018334Speter 247118334Speterstatic void 247218334Spetersiointr1(com) 247318334Speter struct com_s *com; 247418334Speter{ 247518334Speter u_char line_status; 247618334Speter u_char modem_status; 247718334Speter u_char *ioptr; 247818334Speter u_char recv_data; 247918334Speter u_char int_ctl; 248018334Speter u_char int_ctl_new; 248118334Speter struct timecounter *tc; 248218334Speter u_int count; 248318334Speter 248418334Speter#ifdef PC98 248518334Speter u_char tmp=0; 248618334Speter u_char rsa_buf_status = 0; 248718334Speter int rsa_tx_fifo_size=0; 248818334Speter recv_data=0; 248918334Speter#endif /* PC98 */ 249018334Speter 249118334Speter int_ctl = inb(com->intr_ctl_port); 249218334Speter int_ctl_new = int_ctl; 249318334Speter 249418334Speter while (!com->gone) { 249518334Speter#ifdef PC98 249618334Speterstatus_read:; 249718334Speter if (IS_8251(com->pc98_if_type)) { 249818334Speter tmp = inb(com->sts_port); 249950600Sobrienmore_intr: 250018334Speter line_status = 0; 250118334Speter if (tmp & STS8251_TxRDY) line_status |= LSR_TXRDY; 250218334Speter if (tmp & STS8251_RxRDY) line_status |= LSR_RXRDY; 250318334Speter if (tmp & STS8251_TxEMP) line_status |= LSR_TSRE; 250418334Speter if (tmp & STS8251_PE) line_status |= LSR_PE; 250518334Speter if (tmp & STS8251_OE) line_status |= LSR_OE; 250618334Speter if (tmp & STS8251_FE) line_status |= LSR_FE; 250718334Speter if (tmp & STS8251_BD_SD) line_status |= LSR_BI; 250818334Speter } else 250918334Speter#endif /* PC98 */ 251018334Speter if (com->pps.ppsparam.mode & PPS_CAPTUREBOTH) { 251118334Speter modem_status = inb(com->modem_status_port); 251218334Speter if ((modem_status ^ com->last_modem_status) & MSR_DCD) { 251318334Speter tc = timecounter; 251418334Speter count = tc->tc_get_timecount(tc); 251518334Speter pps_event(&com->pps, tc, count, 251618334Speter (modem_status & MSR_DCD) ? 251718334Speter PPS_CAPTURECLEAR : PPS_CAPTUREASSERT); 251818334Speter } 251918334Speter } 252018334Speter line_status = inb(com->line_status_port); 252118334Speter#ifdef PC98 252218334Speter if (com->pc98_if_type == COM_IF_RSA98III) 252318334Speter rsa_buf_status = inb(com->rsabase + rsa_srr); 252418334Speter#endif /* PC98 */ 252518334Speter 252618334Speter /* input event? (check first to help avoid overruns) */ 252718334Speter#ifndef PC98 252818334Speter while (line_status & LSR_RCV_MASK) { 252918334Speter#else 253018334Speter while ((line_status & LSR_RCV_MASK) 253118334Speter || (com->pc98_if_type == COM_IF_RSA98III 253218334Speter && (rsa_buf_status & 0x08))) { 253318334Speter#endif /* PC98 */ 253418334Speter /* break/unnattached error bits or real input? */ 253518334Speter#ifdef PC98 253618334Speter if (IS_8251(com->pc98_if_type)) { 253718334Speter recv_data = inb(com->data_port); 253818334Speter if (tmp & 0x78) { 253918334Speter pc98_i8251_or_cmd(com,CMD8251_ER); 254018334Speter recv_data = 0; 254118334Speter } 254218334Speter } else { 254318334Speter#endif /* PC98 */ 254418334Speter#ifdef PC98 254518334Speter if (com->pc98_if_type == COM_IF_RSA98III) { 254618334Speter if (!(rsa_buf_status & 0x08)) 254718334Speter recv_data = 0; 254818334Speter else { 254918334Speter recv_data = inb(com->data_port); 255018334Speter } 255118334Speter } else 255218334Speter#endif 255318334Speter if (!(line_status & LSR_RXRDY)) 255418334Speter recv_data = 0; 255518334Speter else 255618334Speter recv_data = inb(com->data_port); 255718334Speter#ifdef PC98 255818334Speter } 255918334Speter#endif 256018334Speter if (line_status & (LSR_BI | LSR_FE | LSR_PE)) { 256118334Speter /* 256218334Speter * Don't store BI if IGNBRK or FE/PE if IGNPAR. 256318334Speter * Otherwise, push the work to a higher level 256418334Speter * (to handle PARMRK) if we're bypassing. 256518334Speter * Otherwise, convert BI/FE and PE+INPCK to 0. 256618334Speter * 256718334Speter * This makes bypassing work right in the 256818334Speter * usual "raw" case (IGNBRK set, and IGNPAR 256918334Speter * and INPCK clear). 257018334Speter * 257118334Speter * Note: BI together with FE/PE means just BI. 257218334Speter */ 257318334Speter if (line_status & LSR_BI) { 257418334Speter#if defined(DDB) && defined(BREAK_TO_DEBUGGER) 257518334Speter if (com->unit == comconsole) { 257618334Speter breakpoint(); 257718334Speter goto cont; 257818334Speter } 257918334Speter#endif 258018334Speter if (com->tp == NULL 258152561Sobrien || com->tp->t_iflag & IGNBRK) 258218334Speter goto cont; 258318334Speter } else { 258418334Speter if (com->tp == NULL 258518334Speter || com->tp->t_iflag & IGNPAR) 258618334Speter goto cont; 258718334Speter } 258818334Speter if (com->tp->t_state & TS_CAN_BYPASS_L_RINT 258918334Speter && (line_status & (LSR_BI | LSR_FE) 259018334Speter || com->tp->t_iflag & INPCK)) 259118334Speter recv_data = 0; 259250600Sobrien } 259350600Sobrien ++com->bytes_in; 259450600Sobrien if (com->hotchar != 0 && recv_data == com->hotchar) 259550600Sobrien setsofttty(); 259650600Sobrien ioptr = com->iptr; 259750600Sobrien if (ioptr >= com->ibufend) 259818334Speter CE_RECORD(com, CE_INTERRUPT_BUF_OVERFLOW); 259918334Speter else { 260018334Speter if (com->do_timestamp) 260150600Sobrien microtime(&com->timestamp); 260250600Sobrien ++com_events; 260350600Sobrien schedsofttty(); 260450600Sobrien#if 0 /* for testing input latency vs efficiency */ 260550600Sobrienif (com->iptr - com->ibuf == 8) 260650600Sobrien setsofttty(); 260750600Sobrien#endif 260850600Sobrien ioptr[0] = recv_data; 260950600Sobrien ioptr[com->ierroff] = line_status; 261050600Sobrien com->iptr = ++ioptr; 261150600Sobrien if (ioptr == com->ihighwater 261250600Sobrien && com->state & CS_RTS_IFLOW) 261318334Speter#ifdef PC98 261418334Speter if (IS_8251(com->pc98_if_type)) 261518334Speter com_tiocm_bic(com, TIOCM_RTS); 261618334Speter else 261718334Speter#endif 261818334Speter outb(com->modem_ctl_port, 261918334Speter com->mcr_image &= ~MCR_RTS); 262052561Sobrien if (line_status & LSR_OE) 262118334Speter CE_RECORD(com, CE_OVERRUN); 262218334Speter } 262318334Spetercont: 262418334Speter /* 262518334Speter * "& 0x7F" is to avoid the gcc-1.40 generating a slow 262618334Speter * jump from the top of the loop to here 262718334Speter */ 262818334Speter#ifdef PC98 262918334Speter if (IS_8251(com->pc98_if_type)) 263018334Speter goto status_read; 263118334Speter else 263218334Speter#endif 263318334Speter line_status = inb(com->line_status_port) & 0x7F; 263418334Speter#ifdef PC98 263518334Speter if (com->pc98_if_type == COM_IF_RSA98III) 263618334Speter rsa_buf_status = inb(com->rsabase + rsa_srr); 263750600Sobrien#endif /* PC98 */ 263818334Speter } 263918334Speter 264018334Speter /* modem status change? (always check before doing output) */ 264118334Speter#ifdef PC98 264218334Speter if (!IS_8251(com->pc98_if_type)) { 264318334Speter#endif 264418334Speter modem_status = inb(com->modem_status_port); 264518334Speter if (modem_status != com->last_modem_status) { 264618334Speter if (com->do_dcd_timestamp 264718334Speter && !(com->last_modem_status & MSR_DCD) 264818334Speter && modem_status & MSR_DCD) 264918334Speter microtime(&com->dcd_timestamp); 265018334Speter 265118334Speter /* 265218334Speter * Schedule high level to handle DCD changes. Note 265318334Speter * that we don't use the delta bits anywhere. Some 265418334Speter * UARTs mess them up, and it's easy to remember the 265518334Speter * previous bits and calculate the delta. 265618334Speter */ 265718334Speter com->last_modem_status = modem_status; 265818334Speter if (!(com->state & CS_CHECKMSR)) { 265918334Speter com_events += LOTS_OF_EVENTS; 266018334Speter com->state |= CS_CHECKMSR; 266118334Speter setsofttty(); 266218334Speter } 266318334Speter 266418334Speter /* handle CTS change immediately for crisp flow ctl */ 266518334Speter if (com->state & CS_CTS_OFLOW) { 266618334Speter if (modem_status & MSR_CTS) 266718334Speter com->state |= CS_ODEVREADY; 266818334Speter else 266918334Speter com->state &= ~CS_ODEVREADY; 267018334Speter } 267118334Speter } 267218334Speter#ifdef PC98 267318334Speter } 267418334Speter#endif 267518334Speter 267618334Speter /* output queued and everything ready? */ 267718334Speter#ifndef PC98 267818334Speter if (line_status & LSR_TXRDY 267918334Speter && com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) { 268018334Speter#else 268118334Speter if (((com->pc98_if_type == COM_IF_RSA98III) 268218334Speter ? (rsa_buf_status & 0x02) 268318334Speter : (line_status & LSR_TXRDY)) 268418334Speter && com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) { 268550600Sobrien#endif 268618334Speter ioptr = com->obufq.l_head; 268718334Speter if (com->tx_fifo_size > 1) { 268818334Speter u_int ocount; 268918334Speter 269050600Sobrien ocount = com->obufq.l_tail - ioptr; 269118334Speter#ifdef PC98 269250600Sobrien if (com->pc98_if_type == COM_IF_RSA98III) { 269318334Speter rsa_buf_status = inb(com->rsabase + rsa_srr); 269418334Speter rsa_tx_fifo_size = 1024; 269518334Speter if (!(rsa_buf_status & 0x01)) 269618334Speter rsa_tx_fifo_size = 2048; 269718334Speter if (ocount > rsa_tx_fifo_size) 269818334Speter ocount = rsa_tx_fifo_size; 269918334Speter } else 270018334Speter#endif 270118334Speter if (ocount > com->tx_fifo_size) 270218334Speter ocount = com->tx_fifo_size; 270318334Speter com->bytes_out += ocount; 270418334Speter do 270518334Speter outb(com->data_port, *ioptr++); 270618334Speter while (--ocount != 0); 270718334Speter } else { 270818334Speter outb(com->data_port, *ioptr++); 270918334Speter ++com->bytes_out; 271018334Speter } 271118334Speter#ifdef PC98 271218334Speter if (IS_8251(com->pc98_if_type)) 271318334Speter if (!(pc98_check_i8251_interrupt(com) & IEN_TxFLAG)) 271418334Speter com_int_Tx_enable(com); 271518334Speter#endif 271618334Speter com->obufq.l_head = ioptr; 271718334Speter if (COM_IIR_TXRDYBUG(com)) { 271818334Speter int_ctl_new = int_ctl | IER_ETXRDY; 271918334Speter } 272050600Sobrien if (ioptr >= com->obufq.l_tail) { 272118334Speter struct lbq *qp; 272250600Sobrien 272350600Sobrien qp = com->obufq.l_next; 272450600Sobrien qp->l_queued = FALSE; 272550600Sobrien qp = qp->l_next; 272650600Sobrien if (qp != NULL) { 272750600Sobrien com->obufq.l_head = qp->l_head; 272850600Sobrien com->obufq.l_tail = qp->l_tail; 272950600Sobrien com->obufq.l_next = qp; 273050600Sobrien } else { 273150600Sobrien /* output just completed */ 273250600Sobrien if ( COM_IIR_TXRDYBUG(com) ) { 273350600Sobrien int_ctl_new = int_ctl & ~IER_ETXRDY; 273450600Sobrien } 273550600Sobrien com->state &= ~CS_BUSY; 273650600Sobrien#if defined(PC98) 273750600Sobrien if (IS_8251(com->pc98_if_type)) 273850600Sobrien if ( pc98_check_i8251_interrupt(com) & IEN_TxFLAG ) 273950600Sobrien com_int_Tx_disable(com); 274050600Sobrien#endif 274150600Sobrien } 274250600Sobrien if (!(com->state & CS_ODONE)) { 274350600Sobrien com_events += LOTS_OF_EVENTS; 274450600Sobrien com->state |= CS_ODONE; 274550600Sobrien setsofttty(); /* handle at high level ASAP */ 274650600Sobrien } 274750600Sobrien } 274850600Sobrien if ( COM_IIR_TXRDYBUG(com) && (int_ctl != int_ctl_new)) { 274950600Sobrien#ifdef PC98 275050600Sobrien if (com->pc98_if_type == COM_IF_RSA98III) { 275150600Sobrien int_ctl_new &= ~(IER_ETXRDY | IER_ERXRDY); 275250600Sobrien outb(com->intr_ctl_port, int_ctl_new); 275350600Sobrien outb(com->rsabase + rsa_ier, 0x1d); 275450600Sobrien } else 275550600Sobrien#endif 275650600Sobrien outb(com->intr_ctl_port, int_ctl_new); 275750600Sobrien } 275850600Sobrien } 275950600Sobrien#ifdef PC98 276050600Sobrien else if (line_status & LSR_TXRDY) { 276150600Sobrien if (IS_8251(com->pc98_if_type)) 276250600Sobrien if ( pc98_check_i8251_interrupt(com) & IEN_TxFLAG ) 276350600Sobrien com_int_Tx_disable(com); 276450600Sobrien } 276550600Sobrien if (IS_8251(com->pc98_if_type)) 276650600Sobrien if ((tmp = inb(com->sts_port)) & STS8251_RxRDY) 276718334Speter goto more_intr; 276818334Speter#endif 276918334Speter 277018334Speter /* finished? */ 277118334Speter#ifndef COM_MULTIPORT 277218334Speter#ifdef PC98 277318334Speter if (IS_8251(com->pc98_if_type)) 277418334Speter return; 277518334Speter#endif 277618334Speter if ((inb(com->int_id_port) & IIR_IMASK) == IIR_NOPEND) 277750600Sobrien#endif /* COM_MULTIPORT */ 277818334Speter return; 277918334Speter } 278018334Speter} 278118334Speter 278218334Speterstatic int 278318334Spetersioioctl(dev, cmd, data, flag, p) 278418334Speter dev_t dev; 278518334Speter u_long cmd; 278618334Speter caddr_t data; 278718334Speter int flag; 278818334Speter struct proc *p; 278918334Speter{ 279018334Speter struct com_s *com; 279118334Speter int error; 279250600Sobrien Port_t iobase; 279350600Sobrien int mynor; 279450600Sobrien int s; 279550600Sobrien struct tty *tp; 279650600Sobrien#if defined(COMPAT_43) || defined(COMPAT_SUNOS) 279750600Sobrien int oldcmd; 279850600Sobrien struct termios term; 279950600Sobrien#endif 280050600Sobrien 280150600Sobrien mynor = minor(dev); 280250600Sobrien com = com_addr(MINOR_TO_UNIT(mynor)); 280350600Sobrien if (com->gone) 280450600Sobrien return (ENODEV); 280550600Sobrien iobase = com->iobase; 280650600Sobrien if (mynor & CONTROL_MASK) { 280750600Sobrien struct termios *ct; 280850600Sobrien 280950600Sobrien switch (mynor & CONTROL_MASK) { 281050600Sobrien case CONTROL_INIT_STATE: 281150600Sobrien ct = mynor & CALLOUT_MASK ? &com->it_out : &com->it_in; 281250600Sobrien break; 281350600Sobrien case CONTROL_LOCK_STATE: 281450600Sobrien ct = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in; 281550600Sobrien break; 281650600Sobrien default: 281718334Speter return (ENODEV); /* /dev/nodev */ 281818334Speter } 281952561Sobrien switch (cmd) { 282018334Speter case TIOCSETA: 282118334Speter error = suser(p->p_ucred, &p->p_acflag); 282218334Speter if (error != 0) 282318334Speter return (error); 282418334Speter *ct = *(struct termios *)data; 282518334Speter return (0); 282650600Sobrien case TIOCGETA: 282718334Speter *(struct termios *)data = *ct; 282818334Speter return (0); 282918334Speter case TIOCGETD: 283018334Speter *(int *)data = TTYDISC; 283118334Speter return (0); 283250600Sobrien case TIOCGWINSZ: 283350600Sobrien bzero(data, sizeof(struct winsize)); 283450600Sobrien return (0); 283550600Sobrien default: 283650600Sobrien return (ENOTTY); 283750600Sobrien } 283818334Speter } 283918334Speter tp = com->tp; 284018334Speter#if defined(COMPAT_43) || defined(COMPAT_SUNOS) 284118334Speter term = tp->t_termios; 284218334Speter oldcmd = cmd; 284318334Speter error = ttsetcompat(tp, &cmd, data, &term); 284418334Speter if (error != 0) 284518334Speter return (error); 284650600Sobrien if (cmd != oldcmd) 284750600Sobrien data = (caddr_t)&term; 284850600Sobrien#endif 284950600Sobrien if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { 285050600Sobrien int cc; 285150600Sobrien struct termios *dt = (struct termios *)data; 285218334Speter struct termios *lt = mynor & CALLOUT_MASK 285318334Speter ? &com->lt_out : &com->lt_in; 285450600Sobrien 285518334Speter dt->c_iflag = (tp->t_iflag & lt->c_iflag) 285650600Sobrien | (dt->c_iflag & ~lt->c_iflag); 285718334Speter dt->c_oflag = (tp->t_oflag & lt->c_oflag) 285818334Speter | (dt->c_oflag & ~lt->c_oflag); 285918334Speter dt->c_cflag = (tp->t_cflag & lt->c_cflag) 286018334Speter | (dt->c_cflag & ~lt->c_cflag); 286118334Speter dt->c_lflag = (tp->t_lflag & lt->c_lflag) 286218334Speter | (dt->c_lflag & ~lt->c_lflag); 286318334Speter for (cc = 0; cc < NCCS; ++cc) 286418334Speter if (lt->c_cc[cc] != 0) 286518334Speter dt->c_cc[cc] = tp->t_cc[cc]; 286618334Speter if (lt->c_ispeed != 0) 286718334Speter dt->c_ispeed = tp->t_ispeed; 286818334Speter if (lt->c_ospeed != 0) 286918334Speter dt->c_ospeed = tp->t_ospeed; 287018334Speter } 287118334Speter error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); 287218334Speter if (error != ENOIOCTL) 287318334Speter return (error); 287418334Speter s = spltty(); 287518334Speter error = ttioctl(tp, cmd, data, flag); 287650600Sobrien disc_optim(tp, &tp->t_termios, com); 287718334Speter if (error != ENOIOCTL) { 287850600Sobrien splx(s); 287918334Speter return (error); 288018334Speter } 288118334Speter#ifdef PC98 288218334Speter if (IS_8251(com->pc98_if_type)) { 288318334Speter switch (cmd) { 288418334Speter case TIOCSBRK: 288518334Speter com_send_break_on( com ); 288618334Speter break; 288718334Speter case TIOCCBRK: 288818334Speter com_send_break_off( com ); 288918334Speter break; 289018334Speter case TIOCSDTR: 289118334Speter com_tiocm_bis(com, TIOCM_DTR | TIOCM_RTS ); 289218334Speter break; 289318334Speter case TIOCCDTR: 289418334Speter com_tiocm_bic(com, TIOCM_DTR); 289518334Speter break; 289618334Speter /* 289718334Speter * XXX should disallow changing MCR_RTS if CS_RTS_IFLOW is set. The 289850600Sobrien * changes get undone on the next call to comparam(). 289918334Speter */ 290018334Speter case TIOCMSET: 290152561Sobrien com_tiocm_set( com, *(int *)data ); 290218334Speter break; 290318334Speter case TIOCMBIS: 290418334Speter com_tiocm_bis( com, *(int *)data ); 290518334Speter break; 290618334Speter case TIOCMBIC: 290718334Speter com_tiocm_bic( com, *(int *)data ); 290818334Speter break; 290918334Speter case TIOCMGET: 291018334Speter *(int *)data = com_tiocm_get(com); 291118334Speter break; 291218334Speter case TIOCMSDTRWAIT: 291352561Sobrien /* must be root since the wait applies to following logins */ 291452561Sobrien error = suser(p->p_ucred, &p->p_acflag); 291552561Sobrien if (error != 0) { 291652561Sobrien splx(s); 291752561Sobrien return (error); 291852561Sobrien } 291952561Sobrien com->dtr_wait = *(int *)data * hz / 100; 292050600Sobrien break; 292152561Sobrien case TIOCMGDTRWAIT: 292218334Speter *(int *)data = com->dtr_wait * 100 / hz; 292318334Speter break; 292418334Speter case TIOCTIMESTAMP: 292518334Speter com->do_timestamp = TRUE; 292618334Speter *(struct timeval *)data = com->timestamp; 292718334Speter break; 292818334Speter case TIOCDCDTIMESTAMP: 292918334Speter com->do_dcd_timestamp = TRUE; 293018334Speter *(struct timeval *)data = com->dcd_timestamp; 293118334Speter break; 293218334Speter default: 293318334Speter splx(s); 293418334Speter return (ENOTTY); 293550600Sobrien } 293650600Sobrien } else { 293750600Sobrien int port_shift; 293850600Sobrien port_shift = if_16550a_type[com->pc98_if_type & 0x0f].port_shift; 293950600Sobrien#endif 294050600Sobrien switch (cmd) { 294150600Sobrien case TIOCSBRK: 294218334Speter#ifdef PC98 294318334Speter outb(iobase + (com_cfcr << port_shift), 294450600Sobrien com->cfcr_image |= CFCR_SBREAK); 294550600Sobrien#else 294650600Sobrien outb(iobase + com_cfcr, com->cfcr_image |= CFCR_SBREAK); 294750600Sobrien#endif 294850600Sobrien break; 294950600Sobrien case TIOCCBRK: 295050600Sobrien#ifdef PC98 295150600Sobrien outb(iobase + (com_cfcr << port_shift), 295250600Sobrien com->cfcr_image &= ~CFCR_SBREAK); 295350600Sobrien#else 295450600Sobrien outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); 295550600Sobrien#endif 295650600Sobrien break; 295750600Sobrien case TIOCSDTR: 295850600Sobrien (void)commctl(com, TIOCM_DTR, DMBIS); 295950600Sobrien break; 296050600Sobrien case TIOCCDTR: 296150600Sobrien (void)commctl(com, TIOCM_DTR, DMBIC); 296250600Sobrien break; 296350600Sobrien /* 296450600Sobrien * XXX should disallow changing MCR_RTS if CS_RTS_IFLOW is set. The 296550600Sobrien * changes get undone on the next call to comparam(). 296650600Sobrien */ 296750600Sobrien case TIOCMSET: 296850600Sobrien (void)commctl(com, *(int *)data, DMSET); 296950600Sobrien break; 297050600Sobrien case TIOCMBIS: 297150600Sobrien (void)commctl(com, *(int *)data, DMBIS); 297250600Sobrien break; 297350600Sobrien case TIOCMBIC: 297450600Sobrien (void)commctl(com, *(int *)data, DMBIC); 297518334Speter break; 297618334Speter case TIOCMGET: 297718334Speter *(int *)data = commctl(com, 0, DMGET); 297818334Speter break; 297918334Speter case TIOCMSDTRWAIT: 298050600Sobrien /* must be root since the wait applies to following logins */ 298118334Speter error = suser(p->p_ucred, &p->p_acflag); 298218334Speter if (error != 0) { 298318334Speter splx(s); 298418334Speter return (error); 298550600Sobrien } 298618334Speter com->dtr_wait = *(int *)data * hz / 100; 298718334Speter break; 298850600Sobrien case TIOCMGDTRWAIT: 298950600Sobrien *(int *)data = com->dtr_wait * 100 / hz; 299050600Sobrien break; 299150600Sobrien case TIOCTIMESTAMP: 299250600Sobrien com->do_timestamp = TRUE; 299350600Sobrien *(struct timeval *)data = com->timestamp; 299450600Sobrien break; 299550600Sobrien case TIOCDCDTIMESTAMP: 299650600Sobrien com->do_dcd_timestamp = TRUE; 299750600Sobrien *(struct timeval *)data = com->dcd_timestamp; 299850600Sobrien break; 299950600Sobrien default: 300018334Speter splx(s); 300150600Sobrien error = pps_ioctl(cmd, data, &com->pps); 300250600Sobrien if (error == ENODEV) 300350600Sobrien error = ENOTTY; 300450600Sobrien return (error); 300550600Sobrien } 300650600Sobrien#ifdef PC98 300750600Sobrien } 300850600Sobrien#endif 300950600Sobrien splx(s); 301018334Speter return (0); 301150600Sobrien} 301218334Speter 301318334Speterstatic void 301418334Spetersiopoll() 301518334Speter{ 301618334Speter int unit; 301718334Speter 301818334Speter if (com_events == 0) 301918334Speter return; 302050600Sobrienrepeat: 302118334Speter for (unit = 0; unit < NSIOTOT; ++unit) { 302218334Speter struct com_s *com; 302350600Sobrien int incc; 302450600Sobrien struct tty *tp; 302550600Sobrien 302650600Sobrien com = com_addr(unit); 302750600Sobrien if (com == NULL) 302850600Sobrien continue; 302950600Sobrien tp = com->tp; 303050600Sobrien if (tp == NULL || com->gone) { 303150600Sobrien /* 303218334Speter * Discard any events related to never-opened or 303318334Speter * going-away devices. 303418334Speter */ 303518334Speter disable_intr(); 303650600Sobrien incc = com->iptr - com->ibuf; 303718334Speter com->iptr = com->ibuf; 303850600Sobrien if (com->state & CS_CHECKMSR) { 303918334Speter incc += LOTS_OF_EVENTS; 304052561Sobrien com->state &= ~CS_CHECKMSR; 304152561Sobrien } 304250600Sobrien com_events -= incc; 304350600Sobrien enable_intr(); 304418334Speter continue; 304550600Sobrien } 304650600Sobrien if (com->iptr != com->ibuf) { 304750600Sobrien disable_intr(); 304850600Sobrien sioinput(com); 304950600Sobrien enable_intr(); 305050600Sobrien } 305150600Sobrien if (com->state & CS_CHECKMSR) { 305250600Sobrien u_char delta_modem_status; 305350600Sobrien 305450600Sobrien#ifdef PC98 305550600Sobrien if (!IS_8251(com->pc98_if_type)) { 305650600Sobrien#endif 305750600Sobrien disable_intr(); 305850600Sobrien delta_modem_status = com->last_modem_status 305950600Sobrien ^ com->prev_modem_status; 306050600Sobrien com->prev_modem_status = com->last_modem_status; 306150600Sobrien com_events -= LOTS_OF_EVENTS; 306250600Sobrien com->state &= ~CS_CHECKMSR; 306350600Sobrien enable_intr(); 306450600Sobrien if (delta_modem_status & MSR_DCD) 306550600Sobrien (*linesw[tp->t_line].l_modem) 306650600Sobrien (tp, com->prev_modem_status & MSR_DCD); 306750600Sobrien#ifdef PC98 306850600Sobrien } 306950600Sobrien#endif 307050600Sobrien } 307150600Sobrien if (com->state & CS_ODONE) { 307250600Sobrien disable_intr(); 307350600Sobrien com_events -= LOTS_OF_EVENTS; 307450600Sobrien com->state &= ~CS_ODONE; 307550600Sobrien enable_intr(); 307650600Sobrien if (!(com->state & CS_BUSY) 307752561Sobrien && !(com->extra_state & CSE_BUSYCHECK)) { 307852561Sobrien timeout(siobusycheck, com, hz / 100); 307952561Sobrien com->extra_state |= CSE_BUSYCHECK; 308050600Sobrien } 308150600Sobrien (*linesw[tp->t_line].l_start)(tp); 308250600Sobrien } 308350600Sobrien if (com_events == 0) 308450600Sobrien break; 308550600Sobrien } 308650600Sobrien if (com_events >= LOTS_OF_EVENTS) 308750600Sobrien goto repeat; 308850600Sobrien} 308950600Sobrien 309050600Sobrienstatic int 309150600Sobriencomparam(tp, t) 309250600Sobrien struct tty *tp; 309350600Sobrien struct termios *t; 309450600Sobrien{ 309550600Sobrien u_int cfcr; 309650600Sobrien int cflag; 309750600Sobrien struct com_s *com; 309850600Sobrien int divisor; 309950600Sobrien u_char dlbh; 310050600Sobrien u_char dlbl; 310150600Sobrien Port_t iobase; 310250600Sobrien int s; 310350600Sobrien int unit; 310450600Sobrien#ifdef PC98 310550600Sobrien int port_shift = 0; 310650600Sobrien u_char param = 0; 310750600Sobrien#endif 310850600Sobrien 310950600Sobrien#ifdef PC98 311050600Sobrien cfcr = 0; 311150600Sobrien unit = DEV_TO_UNIT(tp->t_dev); 311250600Sobrien com = com_addr(unit); 311350600Sobrien iobase = com->iobase; 311450600Sobrien if (IS_8251(com->pc98_if_type)) { 311550600Sobrien divisor = pc98_ttspeedtab(com, t->c_ospeed); 311650600Sobrien } else { 311750600Sobrien port_shift = if_16550a_type[com->pc98_if_type & 0x0f].port_shift; 311850600Sobrien 311950600Sobrien /* do historical conversions */ 312050600Sobrien if (t->c_ispeed == 0) 312150600Sobrien t->c_ispeed = t->c_ospeed; 312250600Sobrien 312350600Sobrien /* check requested parameters */ 312450600Sobrien divisor = ttspeedtab(t->c_ospeed, 312550600Sobrien if_16550a_type[com->pc98_if_type & 0x0f].speedtab); 312650600Sobrien } 312750600Sobrien#else 312850600Sobrien /* do historical conversions */ 312950600Sobrien if (t->c_ispeed == 0) 313050600Sobrien t->c_ispeed = t->c_ospeed; 313150600Sobrien 313250600Sobrien /* check requested parameters */ 313350600Sobrien divisor = ttspeedtab(t->c_ospeed, comspeedtab); 313450600Sobrien#endif 313550600Sobrien if (divisor < 0 || divisor > 0 && t->c_ispeed != t->c_ospeed) 313650600Sobrien return (EINVAL); 313750600Sobrien 313850600Sobrien /* parameters are OK, convert them to the com struct and the device */ 313950600Sobrien#ifndef PC98 314050600Sobrien unit = DEV_TO_UNIT(tp->t_dev); 314150600Sobrien com = com_addr(unit); 314250600Sobrien iobase = com->iobase; 314350600Sobrien#endif 314450600Sobrien s = spltty(); 314550600Sobrien#ifdef PC98 314650600Sobrien if (IS_8251(com->pc98_if_type)) { 314750600Sobrien if (divisor == 0) 314850600Sobrien com_tiocm_bic( com, TIOCM_DTR|TIOCM_RTS|TIOCM_LE ); 314950600Sobrien else 315050600Sobrien com_tiocm_bis( com, TIOCM_DTR|TIOCM_RTS|TIOCM_LE ); 315150600Sobrien } else { 315250600Sobrien#endif 315350600Sobrien if (divisor == 0) 315450600Sobrien (void)commctl(com, TIOCM_DTR, DMBIC); /* hang up line */ 315550600Sobrien else 315650600Sobrien (void)commctl(com, TIOCM_DTR, DMBIS); 315750600Sobrien#ifdef PC98 315850600Sobrien } 315950600Sobrien#endif 316050600Sobrien cflag = t->c_cflag; 316150600Sobrien#ifdef PC98 316250600Sobrien if (!IS_8251(com->pc98_if_type)) { 316350600Sobrien#endif 316450600Sobrien switch (cflag & CSIZE) { 316550600Sobrien case CS5: 316650600Sobrien cfcr = CFCR_5BITS; 316750600Sobrien break; 316850600Sobrien case CS6: 316950600Sobrien cfcr = CFCR_6BITS; 317050600Sobrien break; 317150600Sobrien case CS7: 317250600Sobrien cfcr = CFCR_7BITS; 317350600Sobrien break; 317450600Sobrien default: 317550600Sobrien cfcr = CFCR_8BITS; 317650600Sobrien break; 317750600Sobrien } 317850600Sobrien if (cflag & PARENB) { 317918334Speter cfcr |= CFCR_PENAB; 318050600Sobrien if (!(cflag & PARODD)) 318150600Sobrien cfcr |= CFCR_PEVEN; 318218334Speter } 318350600Sobrien if (cflag & CSTOPB) 318450600Sobrien cfcr |= CFCR_STOPB; 318550600Sobrien 318650600Sobrien if (com->hasfifo && divisor != 0) { 318750600Sobrien /* 318850600Sobrien * Use a fifo trigger level low enough so that the input 318950600Sobrien * latency from the fifo is less than about 16 msec and 319050600Sobrien * the total latency is less than about 30 msec. These 319150600Sobrien * latencies are reasonable for humans. Serial comms 319218334Speter * protocols shouldn't expect anything better since modem 319318334Speter * latencies are larger. 319450600Sobrien */ 319518334Speter com->fifo_image = t->c_ospeed <= 4800 319650600Sobrien ? FIFO_ENABLE : FIFO_ENABLE | FIFO_RX_HIGH; 319750600Sobrien#ifdef COM_ESP 319850600Sobrien /* 319952561Sobrien * The Hayes ESP card needs the fifo DMA mode bit set 320052561Sobrien * in compatibility mode. If not, it will interrupt 320152561Sobrien * for each character received. 320252561Sobrien */ 320352561Sobrien if (com->esp) 320452561Sobrien com->fifo_image |= FIFO_DMA_MODE; 320552561Sobrien#endif 320652561Sobrien#ifdef PC98 320752561Sobrien outb(iobase + (com_fifo << port_shift), com->fifo_image); 320852561Sobrien#else 320952561Sobrien outb(iobase + com_fifo, com->fifo_image); 321050600Sobrien#endif 321118334Speter } 321250600Sobrien#ifdef PC98 321350600Sobrien } 321418334Speter#endif 321550600Sobrien 321650600Sobrien /* 321750600Sobrien * This returns with interrupts disabled so that we can complete 321850600Sobrien * the speed change atomically. Keeping interrupts disabled is 321950600Sobrien * especially important while com_data is hidden. 322050600Sobrien */ 322150600Sobrien (void) siosetwater(com, t->c_ispeed); 322250600Sobrien 322350600Sobrien#ifdef PC98 322450600Sobrien if (IS_8251(com->pc98_if_type)) 322550600Sobrien com_cflag_and_speed_set(com, cflag, t->c_ospeed); 322618334Speter else { 322750600Sobrien#endif 322850600Sobrien if (divisor != 0) { 322950600Sobrien#ifdef PC98 323050600Sobrien outb(iobase + (com_cfcr << port_shift), cfcr | CFCR_DLAB); 323150600Sobrien#else 323218334Speter outb(iobase + com_cfcr, cfcr | CFCR_DLAB); 323350600Sobrien#endif 323450600Sobrien /* 323550600Sobrien * Only set the divisor registers if they would change, 323650600Sobrien * since on some 16550 incompatibles (UMC8669F), setting 323750600Sobrien * them while input is arriving them loses sync until 323850600Sobrien * data stops arriving. 323918334Speter */ 324018334Speter dlbl = divisor & 0xFF; 324150600Sobrien#ifdef PC98 324250600Sobrien if (inb(iobase + (com_dlbl << port_shift)) != dlbl) 324318334Speter outb(iobase + (com_dlbl << port_shift), dlbl); 324450600Sobrien dlbh = (u_int) divisor >> 8; 324550600Sobrien if (inb(iobase + (com_dlbh << port_shift)) != dlbh) 324650600Sobrien outb(iobase + (com_dlbh << port_shift), dlbh); 324750600Sobrien#else 324850600Sobrien if (inb(iobase + com_dlbl) != dlbl) 324950600Sobrien outb(iobase + com_dlbl, dlbl); 325018334Speter dlbh = (u_int) divisor >> 8; 325150600Sobrien if (inb(iobase + com_dlbh) != dlbh) 325250600Sobrien outb(iobase + com_dlbh, dlbh); 325350600Sobrien#endif 325450600Sobrien } 325550600Sobrien 325650600Sobrien 325750600Sobrien#ifdef PC98 325850600Sobrien } 325952561Sobrien outb(iobase + (com_cfcr << port_shift), com->cfcr_image = cfcr); 326052561Sobrien#else 326150600Sobrien outb(iobase + com_cfcr, com->cfcr_image = cfcr); 326250600Sobrien#endif 326350600Sobrien 326450600Sobrien if (!(tp->t_state & TS_TTSTOP)) 326550600Sobrien com->state |= CS_TTGO; 326650600Sobrien 326750600Sobrien if (cflag & CRTS_IFLOW) { 326850600Sobrien if (com->st16650a) { 326950600Sobrien outb(iobase + com_cfcr, 0xbf); 327050600Sobrien outb(iobase + com_fifo, inb(iobase + com_fifo) | 0x40); 327150600Sobrien } 327250600Sobrien com->state |= CS_RTS_IFLOW; 327350600Sobrien /* 327450600Sobrien * If CS_RTS_IFLOW just changed from off to on, the change 327550600Sobrien * needs to be propagated to MCR_RTS. This isn't urgent, 327650600Sobrien * so do it later by calling comstart() instead of repeating 327718334Speter * a lot of code from comstart() here. 327850600Sobrien */ 327950600Sobrien } else if (com->state & CS_RTS_IFLOW) { 328050600Sobrien com->state &= ~CS_RTS_IFLOW; 328150600Sobrien /* 328250600Sobrien * CS_RTS_IFLOW just changed from on to off. Force MCR_RTS 328350600Sobrien * on here, since comstart() won't do it later. 328450600Sobrien */ 328550600Sobrien#ifdef PC98 328650600Sobrien if (IS_8251(com->pc98_if_type)) 328750600Sobrien com_tiocm_bis(com, TIOCM_RTS); 328818334Speter else 328950600Sobrien#endif 329050600Sobrien outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); 329150600Sobrien if (com->st16650a) { 329250600Sobrien outb(iobase + com_cfcr, 0xbf); 329350600Sobrien outb(iobase + com_fifo, inb(iobase + com_fifo) & ~0x40); 329450600Sobrien } 329550600Sobrien } 329650600Sobrien 329750600Sobrien 329850600Sobrien /* 329950600Sobrien * Set up state to handle output flow control. 330050600Sobrien * XXX - worth handling MDMBUF (DCD) flow control at the lowest level? 330150600Sobrien * Now has 10+ msec latency, while CTS flow has 50- usec latency. 330250600Sobrien */ 330350600Sobrien com->state |= CS_ODEVREADY; 330450600Sobrien com->state &= ~CS_CTS_OFLOW; 330550600Sobrien#ifdef PC98 330650600Sobrien if (com->pc98_if_type == COM_IF_RSA98III) { 330750600Sobrien param = inb(com->rsabase + rsa_msr); 330818334Speter outb(com->rsabase + rsa_msr, param & 0x14); 330950600Sobrien } 331050600Sobrien#endif 331150600Sobrien if (cflag & CCTS_OFLOW) { 331250600Sobrien com->state |= CS_CTS_OFLOW; 331350600Sobrien#ifdef PC98 331450600Sobrien if (IS_8251(com->pc98_if_type)) { 331550600Sobrien if (!(pc98_get_modem_status(com) & TIOCM_CTS)) 331650600Sobrien com->state &= ~CS_ODEVREADY; 331750600Sobrien } else { 331850600Sobrien#endif 331950600Sobrien#ifdef PC98 332050600Sobrien if (com->pc98_if_type == COM_IF_RSA98III) { 332150600Sobrien /* Set automatic flow control mode */ 332250600Sobrien outb(com->rsabase + rsa_msr, param | 0x08); 332350600Sobrien } else 332450600Sobrien#endif 332550600Sobrien if (!(com->last_modem_status & MSR_CTS)) 332650600Sobrien com->state &= ~CS_ODEVREADY; 332718334Speter if (com->st16650a) { 332850600Sobrien outb(iobase + com_cfcr, 0xbf); 332918334Speter outb(iobase + com_fifo, inb(iobase + com_fifo) | 0x80); 333050600Sobrien } 333118334Speter#ifdef PC98 333250600Sobrien } 333350600Sobrien#endif 333450600Sobrien } else { 333550600Sobrien if (com->st16650a) { 333650600Sobrien outb(iobase + com_cfcr, 0xbf); 333752561Sobrien outb(iobase + com_fifo, inb(iobase + com_fifo) & ~0x80); 333852561Sobrien } 333950600Sobrien } 334050600Sobrien 334150600Sobrien 334250600Sobrien#ifdef PC98 334350600Sobrien outb(iobase + (com_cfcr << port_shift), com->cfcr_image); 334450600Sobrien#else 334550600Sobrien outb(iobase + com_cfcr, com->cfcr_image); 334650600Sobrien#endif 334750600Sobrien 334850600Sobrien 334950600Sobrien /* XXX shouldn't call functions while intrs are disabled. */ 335050600Sobrien disc_optim(tp, t, com); 335150600Sobrien /* 335250600Sobrien * Recover from fiddling with CS_TTGO. We used to call siointr1() 335350600Sobrien * unconditionally, but that defeated the careful discarding of 335450600Sobrien * stale input in sioopen(). 335550600Sobrien */ 335650600Sobrien if (com->state >= (CS_BUSY | CS_TTGO)) 335750600Sobrien siointr1(com); 335850600Sobrien 335950600Sobrien enable_intr(); 336050600Sobrien splx(s); 336150600Sobrien comstart(tp); 336250600Sobrien if (com->ibufold != NULL) { 336350600Sobrien free(com->ibufold, M_DEVBUF); 336450600Sobrien com->ibufold = NULL; 336518334Speter } 336650600Sobrien return (0); 336750600Sobrien} 336850600Sobrien 336950600Sobrienstatic int 337050600Sobriensiosetwater(com, speed) 337150600Sobrien struct com_s *com; 337250600Sobrien speed_t speed; 337350600Sobrien{ 337450600Sobrien int cp4ticks; 337550600Sobrien u_char *ibuf; 337650600Sobrien int ibufsize; 337750600Sobrien struct tty *tp; 337850600Sobrien 337950600Sobrien /* 338050600Sobrien * Make the buffer size large enough to handle a softtty interrupt 338150600Sobrien * latency of about 2 ticks without loss of throughput or data 338250600Sobrien * (about 3 ticks if input flow control is not used or not honoured, 338350600Sobrien * but a bit less for CS5-CS7 modes). 338450600Sobrien */ 338550600Sobrien cp4ticks = speed / 10 / hz * 4; 338650600Sobrien for (ibufsize = 128; ibufsize < cp4ticks;) 338750600Sobrien ibufsize <<= 1; 338850600Sobrien#ifdef PC98 338950600Sobrien if (com->pc98_if_type == COM_IF_RSA98III) 339050600Sobrien ibufsize = 2048; 339118334Speter#endif 339250600Sobrien if (ibufsize == com->ibufsize) { 339350600Sobrien disable_intr(); 339418334Speter return (0); 339552561Sobrien } 339652561Sobrien 339752561Sobrien /* 339852561Sobrien * Allocate input buffer. The extra factor of 2 in the size is 339952561Sobrien * to allow for an error byte for each input byte. 340052561Sobrien */ 340152561Sobrien ibuf = malloc(2 * ibufsize, M_DEVBUF, M_NOWAIT); 340252561Sobrien if (ibuf == NULL) { 340352561Sobrien disable_intr(); 340452561Sobrien return (ENOMEM); 340552561Sobrien } 340652561Sobrien 340752561Sobrien /* Initialize non-critical variables. */ 340852561Sobrien com->ibufold = com->ibuf; 340952561Sobrien com->ibufsize = ibufsize; 341052561Sobrien tp = com->tp; 341152561Sobrien if (tp != NULL) { 341252561Sobrien tp->t_ififosize = 2 * ibufsize; 341352561Sobrien tp->t_ispeedwat = (speed_t)-1; 341452561Sobrien tp->t_ospeedwat = (speed_t)-1; 341550600Sobrien } 341650600Sobrien 341750600Sobrien /* 341850600Sobrien * Read current input buffer, if any. Continue with interrupts 341950600Sobrien * disabled. 342050600Sobrien */ 342150600Sobrien disable_intr(); 342250600Sobrien if (com->iptr != com->ibuf) 342350600Sobrien sioinput(com); 342450600Sobrien 342550600Sobrien /*- 342650600Sobrien * Initialize critical variables, including input buffer watermarks. 342750600Sobrien * The external device is asked to stop sending when the buffer 342850600Sobrien * exactly reaches high water, or when the high level requests it. 342950600Sobrien * The high level is notified immediately (rather than at a later 343050600Sobrien * clock tick) when this watermark is reached. 343118334Speter * The buffer size is chosen so the watermark should almost never 343250600Sobrien * be reached. 343318334Speter * The low watermark is invisibly 0 since the buffer is always 343418334Speter * emptied all at once. 343550600Sobrien */ 343650600Sobrien com->iptr = com->ibuf = ibuf; 343750600Sobrien com->ibufend = ibuf + ibufsize; 343850600Sobrien com->ierroff = ibufsize; 343918334Speter com->ihighwater = ibuf + 3 * ibufsize / 4; 344050600Sobrien return (0); 344150600Sobrien} 344218334Speter 344350600Sobrienstatic void 344450600Sobriencomstart(tp) 344550600Sobrien struct tty *tp; 344650600Sobrien{ 344718334Speter struct com_s *com; 344850600Sobrien int s; 344950600Sobrien int unit; 345018334Speter#ifdef PC98 345150600Sobrien int tmp; 345250600Sobrien#endif 345350600Sobrien 345450600Sobrien unit = DEV_TO_UNIT(tp->t_dev); 345518334Speter com = com_addr(unit); 345650600Sobrien s = spltty(); 345750600Sobrien disable_intr(); 345850600Sobrien if (tp->t_state & TS_TTSTOP) 345950600Sobrien com->state &= ~CS_TTGO; 346050600Sobrien else 346150600Sobrien com->state |= CS_TTGO; 346250600Sobrien if (tp->t_state & TS_TBLOCK) { 346350600Sobrien#ifdef PC98 346450600Sobrien if (IS_8251(com->pc98_if_type)) 346550600Sobrien tmp = com_tiocm_get(com) & TIOCM_RTS; 346650600Sobrien else 346750600Sobrien tmp = com->mcr_image & MCR_RTS; 346850600Sobrien if (tmp && (com->state & CS_RTS_IFLOW)) 346950600Sobrien#else 347050600Sobrien if (com->mcr_image & MCR_RTS && com->state & CS_RTS_IFLOW) 347150600Sobrien#endif 347250600Sobrien#ifdef PC98 347350600Sobrien if (IS_8251(com->pc98_if_type)) 347450600Sobrien com_tiocm_bic(com, TIOCM_RTS); 347550600Sobrien else 347650600Sobrien#endif 347718334Speter outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); 347818334Speter } else { 347950600Sobrien#ifdef PC98 348018334Speter if (IS_8251(com->pc98_if_type)) 348118334Speter tmp = com_tiocm_get(com) & TIOCM_RTS; 348252561Sobrien else 348352561Sobrien tmp = com->mcr_image & MCR_RTS; 348452561Sobrien if (!(tmp) && com->iptr < com->ihighwater 348552561Sobrien && com->state & CS_RTS_IFLOW) 348652561Sobrien#else 348752561Sobrien if (!(com->mcr_image & MCR_RTS) && com->iptr < com->ihighwater 348852561Sobrien && com->state & CS_RTS_IFLOW) 348952561Sobrien#endif 349052561Sobrien#ifdef PC98 349152561Sobrien if (IS_8251(com->pc98_if_type)) 349252561Sobrien com_tiocm_bis(com, TIOCM_RTS); 349352561Sobrien else 349452561Sobrien#endif 349550600Sobrien outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); 349652561Sobrien } 349752561Sobrien enable_intr(); 349818334Speter if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) { 349952561Sobrien ttwwakeup(tp); 350052561Sobrien#ifdef PC98 350152561Sobrien/* if(IS_8251(com->pc98_if_type)) 350250600Sobrien com_int_Tx_enable(com); */ 350350600Sobrien#endif 350452561Sobrien splx(s); 350550600Sobrien return; 350650600Sobrien } 350718334Speter if (tp->t_outq.c_cc != 0) { 350850600Sobrien struct lbq *qp; 350950600Sobrien struct lbq *next; 351050600Sobrien 351152561Sobrien if (!com->obufs[0].l_queued) { 351252561Sobrien com->obufs[0].l_tail 351352561Sobrien = com->obuf1 + q_to_b(&tp->t_outq, com->obuf1, 351452561Sobrien#ifndef PC98 351552561Sobrien sizeof com->obuf1); 351652561Sobrien#else 351752561Sobrien com->obufsize); 351852561Sobrien#endif 351952561Sobrien com->obufs[0].l_next = NULL; 352052561Sobrien com->obufs[0].l_queued = TRUE; 352152561Sobrien disable_intr(); 352252561Sobrien if (com->state & CS_BUSY) { 352352561Sobrien qp = com->obufq.l_next; 352452561Sobrien while ((next = qp->l_next) != NULL) 352550600Sobrien qp = next; 352650600Sobrien qp->l_next = &com->obufs[0]; 352750600Sobrien } else { 352852561Sobrien com->obufq.l_head = com->obufs[0].l_head; 352950600Sobrien com->obufq.l_tail = com->obufs[0].l_tail; 353050600Sobrien com->obufq.l_next = &com->obufs[0]; 353150600Sobrien com->state |= CS_BUSY; 353250600Sobrien } 353350600Sobrien enable_intr(); 353452561Sobrien } 353550600Sobrien if (tp->t_outq.c_cc != 0 && !com->obufs[1].l_queued) { 353650600Sobrien com->obufs[1].l_tail 353750600Sobrien = com->obuf2 + q_to_b(&tp->t_outq, com->obuf2, 353852561Sobrien#ifndef PC98 353952561Sobrien sizeof com->obuf2); 354052561Sobrien#else 354152561Sobrien com->obufsize); 354252561Sobrien#endif 354352561Sobrien com->obufs[1].l_next = NULL; 354452561Sobrien com->obufs[1].l_queued = TRUE; 354552561Sobrien disable_intr(); 354652561Sobrien if (com->state & CS_BUSY) { 354750600Sobrien qp = com->obufq.l_next; 354850600Sobrien while ((next = qp->l_next) != NULL) 354950600Sobrien qp = next; 355052561Sobrien qp->l_next = &com->obufs[1]; 355152561Sobrien } else { 355252561Sobrien com->obufq.l_head = com->obufs[1].l_head; 355352561Sobrien com->obufq.l_tail = com->obufs[1].l_tail; 355450600Sobrien com->obufq.l_next = &com->obufs[1]; 355550600Sobrien com->state |= CS_BUSY; 355650600Sobrien } 355752561Sobrien enable_intr(); 355818334Speter } 355950600Sobrien tp->t_state |= TS_BUSY; 356050600Sobrien } 356150600Sobrien disable_intr(); 356250600Sobrien if (com->state >= (CS_BUSY | CS_TTGO)) 356350600Sobrien siointr1(com); /* fake interrupt to start output */ 356450600Sobrien enable_intr(); 356550600Sobrien#ifdef PC98 356650600Sobrien/* if(IS_8251(com->pc98_if_type)) 356750600Sobrien com_int_Tx_enable(com); */ 356850600Sobrien#endif 356950600Sobrien ttwwakeup(tp); 357050600Sobrien splx(s); 357150600Sobrien} 357250600Sobrien 357350600Sobrienstatic void 357452561Sobriensiostop(tp, rw) 357552561Sobrien struct tty *tp; 357652561Sobrien int rw; 357752561Sobrien{ 357852561Sobrien struct com_s *com; 357952561Sobrien#ifdef PC98 358052561Sobrien int port_shift = 0; 358152561Sobrien int rsa98_tmp = 0; 358252561Sobrien#endif 358352561Sobrien 358452561Sobrien com = com_addr(DEV_TO_UNIT(tp->t_dev)); 358552561Sobrien if (com->gone) 358652561Sobrien return; 358752561Sobrien#ifdef PC98 358852561Sobrien if (IS_8251(com->pc98_if_type)) 358952561Sobrien port_shift = if_16550a_type[com->pc98_if_type & 0x0f].port_shift; 359052561Sobrien#endif 359152561Sobrien disable_intr(); 359252561Sobrien if (rw & FWRITE) { 359352561Sobrien if (com->hasfifo) 359452561Sobrien#ifdef COM_ESP 359552561Sobrien /* XXX avoid h/w bug. */ 359652561Sobrien if (!com->esp) 359752561Sobrien#endif 359852561Sobrien#ifdef PC98 359952561Sobrien outb(com->iobase + (com_fifo << port_shift), 360052561Sobrien FIFO_XMT_RST | com->fifo_image); 360152561Sobrien if (com->pc98_if_type == COM_IF_RSA98III) 360252561Sobrien for(rsa98_tmp = 0; rsa98_tmp < 2048; rsa98_tmp++) 360352561Sobrien outb(com->iobase + (com_fifo << port_shift), 360450600Sobrien FIFO_XMT_RST | com->fifo_image); 360550600Sobrien#else 360652561Sobrien outb(com->iobase + com_fifo, 360752561Sobrien FIFO_XMT_RST | com->fifo_image); 360818334Speter#endif 360952561Sobrien com->obufs[0].l_queued = FALSE; 361052561Sobrien com->obufs[1].l_queued = FALSE; 361152561Sobrien if (com->state & CS_ODONE) 361252561Sobrien com_events -= LOTS_OF_EVENTS; 361352561Sobrien com->state &= ~(CS_ODONE | CS_BUSY); 361452561Sobrien com->tp->t_state &= ~TS_BUSY; 361552561Sobrien } 361652561Sobrien if (rw & FREAD) { 361752561Sobrien if (com->hasfifo) 361850600Sobrien#ifdef COM_ESP 361952561Sobrien /* XXX avoid h/w bug. */ 362050600Sobrien if (!com->esp) 362150600Sobrien#endif 362250600Sobrien#ifdef PC98 362318334Speter if (com->pc98_if_type == COM_IF_RSA98III) { 362452561Sobrien for(rsa98_tmp = 0; rsa98_tmp < 2048; rsa98_tmp++) 362552561Sobrien inb(com->data_port); 362652561Sobrien } 362752561Sobrien outb(com->iobase + (com_fifo << port_shift), 362852561Sobrien FIFO_RCV_RST | com->fifo_image); 362952561Sobrien#else 363052561Sobrien outb(com->iobase + com_fifo, 363152561Sobrien FIFO_RCV_RST | com->fifo_image); 363252561Sobrien#endif 363350600Sobrien com_events -= (com->iptr - com->ibuf); 363450600Sobrien com->iptr = com->ibuf; 363552561Sobrien } 363650600Sobrien enable_intr(); 363750600Sobrien comstart(tp); 363852561Sobrien} 363918334Speter 364050600Sobrienstatic struct tty * 364150600Sobriensiodevtotty(dev) 364250600Sobrien dev_t dev; 364350600Sobrien{ 364450600Sobrien int mynor; 364550600Sobrien int unit; 364650600Sobrien 364718334Speter mynor = minor(dev); 364852561Sobrien if (mynor & CONTROL_MASK) 364952561Sobrien return (NULL); 365052561Sobrien unit = MINOR_TO_UNIT(mynor); 365152561Sobrien if ((u_int) unit >= NSIOTOT) 365250600Sobrien return (NULL); 365318334Speter return (&sio_tty[unit]); 365452561Sobrien} 365550600Sobrien 365650600Sobrienstatic int 365750600Sobriencommctl(com, bits, how) 365850600Sobrien struct com_s *com; 365950600Sobrien int bits; 366050600Sobrien int how; 366118334Speter{ 366250600Sobrien int mcr; 366350600Sobrien int msr; 366450600Sobrien 366550600Sobrien if (how == DMGET) { 366650600Sobrien bits = TIOCM_LE; /* XXX - always enabled while open */ 366718334Speter mcr = com->mcr_image; 366850600Sobrien if (mcr & MCR_DTR) 366950600Sobrien bits |= TIOCM_DTR; 367050600Sobrien if (mcr & MCR_RTS) 367118334Speter bits |= TIOCM_RTS; 367250600Sobrien msr = com->prev_modem_status; 367350600Sobrien if (msr & MSR_CTS) 367418334Speter bits |= TIOCM_CTS; 367550600Sobrien if (msr & MSR_DCD) 367650600Sobrien bits |= TIOCM_CD; 367750600Sobrien if (msr & MSR_DSR) 367850600Sobrien bits |= TIOCM_DSR; 367950600Sobrien /* 368050600Sobrien * XXX - MSR_RI is naturally volatile, and we make MSR_TERI 368150600Sobrien * more volatile by reading the modem status a lot. Perhaps 368250600Sobrien * we should latch both bits until the status is read here. 368350600Sobrien */ 368450600Sobrien if (msr & (MSR_RI | MSR_TERI)) 368550600Sobrien bits |= TIOCM_RI; 368650600Sobrien return (bits); 368750600Sobrien } 368850600Sobrien mcr = 0; 368950600Sobrien if (bits & TIOCM_DTR) 369050600Sobrien mcr |= MCR_DTR; 369150600Sobrien if (bits & TIOCM_RTS) 369218334Speter mcr |= MCR_RTS; 369350600Sobrien if (com->gone) 369452561Sobrien return(0); 369552561Sobrien disable_intr(); 369652561Sobrien switch (how) { 369750600Sobrien case DMSET: 369850600Sobrien outb(com->modem_ctl_port, 369950600Sobrien com->mcr_image = mcr | (com->mcr_image & MCR_IENABLE)); 370052561Sobrien break; 370152561Sobrien case DMBIS: 370252561Sobrien outb(com->modem_ctl_port, com->mcr_image |= mcr); 370352561Sobrien break; 370452561Sobrien case DMBIC: 370552561Sobrien outb(com->modem_ctl_port, com->mcr_image &= ~mcr); 370652561Sobrien break; 370750600Sobrien } 370852561Sobrien enable_intr(); 370952561Sobrien return (0); 371052561Sobrien} 371152561Sobrien 371250600Sobrienstatic void 371352561Sobriensiosettimeout() 371452561Sobrien{ 371552561Sobrien struct com_s *com; 371652561Sobrien bool_t someopen; 371752561Sobrien int unit; 371852561Sobrien 371918334Speter /* 372052561Sobrien * Set our timeout period to 1 second if no polled devices are open. 372152561Sobrien * Otherwise set it to max(1/200, 1/hz). 372252561Sobrien * Enable timeouts iff some device is open. 372352561Sobrien */ 372452561Sobrien untimeout(comwakeup, (void *)NULL, sio_timeout_handle); 372552561Sobrien sio_timeout = hz; 372652561Sobrien someopen = FALSE; 372752561Sobrien for (unit = 0; unit < NSIOTOT; ++unit) { 372852561Sobrien com = com_addr(unit); 372952561Sobrien if (com != NULL && com->tp != NULL 373052561Sobrien && com->tp->t_state & TS_ISOPEN && !com->gone) { 373152561Sobrien someopen = TRUE; 373252561Sobrien if (com->poll || com->poll_output) { 373350600Sobrien sio_timeout = hz > 200 ? hz / 200 : 1; 373452561Sobrien break; 373550600Sobrien } 373652561Sobrien } 373752561Sobrien } 373852561Sobrien if (someopen) { 373950600Sobrien sio_timeouts_until_log = hz / sio_timeout; 374052561Sobrien sio_timeout_handle = timeout(comwakeup, (void *)NULL, 374150600Sobrien sio_timeout); 374252561Sobrien } else { 374350600Sobrien /* Flush error messages, if any. */ 374450600Sobrien sio_timeouts_until_log = 1; 374550600Sobrien comwakeup((void *)NULL); 374650600Sobrien untimeout(comwakeup, (void *)NULL, sio_timeout_handle); 374750600Sobrien } 374850600Sobrien} 374950600Sobrien 375050600Sobrienstatic void 375150600Sobriencomwakeup(chan) 375250600Sobrien void *chan; 375350600Sobrien{ 375452561Sobrien struct com_s *com; 375550600Sobrien int unit; 375652561Sobrien 375750600Sobrien sio_timeout_handle = timeout(comwakeup, (void *)NULL, sio_timeout); 375850600Sobrien 375952561Sobrien /* 376052561Sobrien * Recover from lost output interrupts. 376152561Sobrien * Poll any lines that don't use interrupts. 376252561Sobrien */ 376352561Sobrien for (unit = 0; unit < NSIOTOT; ++unit) { 376452561Sobrien com = com_addr(unit); 376552561Sobrien if (com != NULL && !com->gone 376650600Sobrien && (com->state >= (CS_BUSY | CS_TTGO) || com->poll)) { 376752561Sobrien disable_intr(); 376852561Sobrien siointr1(com); 376952561Sobrien enable_intr(); 377052561Sobrien } 377152561Sobrien } 377250600Sobrien 377352561Sobrien /* 377452561Sobrien * Check for and log errors, but not too often. 377552561Sobrien */ 377650600Sobrien if (--sio_timeouts_until_log > 0) 377750600Sobrien return; 377852561Sobrien sio_timeouts_until_log = hz / sio_timeout; 377952561Sobrien for (unit = 0; unit < NSIOTOT; ++unit) { 378052561Sobrien int errnum; 378152561Sobrien 378252561Sobrien com = com_addr(unit); 378352561Sobrien if (com == NULL) 378452561Sobrien continue; 378552561Sobrien if (com->gone) 378652561Sobrien continue; 378752561Sobrien for (errnum = 0; errnum < CE_NTYPES; ++errnum) { 378852561Sobrien u_int delta; 378952561Sobrien u_long total; 379052561Sobrien 379152561Sobrien disable_intr(); 379252561Sobrien delta = com->delta_error_counts[errnum]; 379352561Sobrien com->delta_error_counts[errnum] = 0; 379450600Sobrien enable_intr(); 379550600Sobrien if (delta == 0) 379650600Sobrien continue; 379750600Sobrien total = com->error_counts[errnum] += delta; 379850600Sobrien log(LOG_ERR, "sio%d: %u more %s%s (total %lu)\n", 379950600Sobrien unit, delta, error_desc[errnum], 380050600Sobrien delta == 1 ? "" : "s", total); 380152561Sobrien } 380252561Sobrien } 380352561Sobrien} 380452561Sobrien 380552561Sobrien#ifdef PC98 380652561Sobrien/* commint is called when modem control line changes */ 380752561Sobrienstatic void 380852561Sobriencommint(dev_t dev) 380952561Sobrien{ 381050600Sobrien register struct tty *tp; 381150600Sobrien int stat,delta; 381252561Sobrien struct com_s *com; 381352561Sobrien int mynor,unit; 381450600Sobrien 381550600Sobrien mynor = minor(dev); 381650600Sobrien unit = MINOR_TO_UNIT(mynor); 381752561Sobrien com = com_addr(unit); 381852561Sobrien tp = com->tp; 381950600Sobrien 382050600Sobrien stat = com_tiocm_get(com); 382150600Sobrien delta = com_tiocm_get_delta(com); 382250600Sobrien 382350600Sobrien if (com->state & CS_CTS_OFLOW) { 382450600Sobrien if (stat & TIOCM_CTS) 382550600Sobrien com->state |= CS_ODEVREADY; 382650600Sobrien else 382750600Sobrien com->state &= ~CS_ODEVREADY; 382850600Sobrien } 382950600Sobrien if ((delta & TIOCM_CAR) && (mynor & CALLOUT_MASK) == 0) { 383050600Sobrien if (stat & TIOCM_CAR ) 383150600Sobrien (void)(*linesw[tp->t_line].l_modem)(tp, 1); 383250600Sobrien else if ((*linesw[tp->t_line].l_modem)(tp, 0) == 0) { 383350600Sobrien /* negate DTR, RTS */ 383450600Sobrien com_tiocm_bic(com, (tp->t_cflag & HUPCL) ? 383550600Sobrien TIOCM_DTR|TIOCM_RTS|TIOCM_LE : TIOCM_LE ); 383650600Sobrien /* disable IENABLE */ 383750600Sobrien com_int_TxRx_disable( com ); 383850600Sobrien } 383950600Sobrien } 384050600Sobrien} 384150600Sobrien#endif 384250600Sobrien 384350600Sobrienstatic void 384450600Sobriendisc_optim(tp, t, com) 384550600Sobrien struct tty *tp; 384650600Sobrien struct termios *t; 384750600Sobrien struct com_s *com; 384850600Sobrien{ 384950600Sobrien if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON)) 385050600Sobrien && (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK)) 385150600Sobrien && (!(t->c_iflag & PARMRK) 385250600Sobrien || (t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK)) 385350600Sobrien && !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN)) 385450600Sobrien && linesw[tp->t_line].l_rint == ttyinput) 385550600Sobrien tp->t_state |= TS_CAN_BYPASS_L_RINT; 385650600Sobrien else 385750600Sobrien tp->t_state &= ~TS_CAN_BYPASS_L_RINT; 385850600Sobrien com->hotchar = linesw[tp->t_line].l_hotchar; 385950600Sobrien} 386050600Sobrien 386150600Sobrien/* 386250600Sobrien * Following are all routines needed for SIO to act as console 386350600Sobrien */ 386450600Sobrien#include <machine/cons.h> 386550600Sobrien 386650600Sobrienstruct siocnstate { 386750600Sobrien u_char dlbl; 386850600Sobrien u_char dlbh; 386950600Sobrien u_char ier; 387050600Sobrien u_char cfcr; 387152561Sobrien u_char mcr; 387252561Sobrien}; 387352561Sobrien 387452561Sobrienstatic speed_t siocngetspeed __P((Port_t, struct speedtab *)); 387552561Sobrienstatic void siocnclose __P((struct siocnstate *sp)); 387650600Sobrienstatic void siocnopen __P((struct siocnstate *sp)); 387752561Sobrienstatic void siocntxwait __P((void)); 387850600Sobrien 387952561Sobrien/* 388050600Sobrien * XXX: sciocnget() and sciocnputc() are not declared static, as they are 388152561Sobrien * referred to from i386/i386/i386-gdbstub.c. 388252561Sobrien */ 388352561Sobrienstatic cn_probe_t siocnprobe; 388450600Sobrienstatic cn_init_t siocninit; 388550600Sobrienstatic cn_checkc_t siocncheckc; 388652561Sobrien cn_getc_t siocngetc; 388752561Sobrien cn_putc_t siocnputc; 388852561Sobrien 388952561SobrienCONS_DRIVER(sio, siocnprobe, siocninit, siocngetc, siocncheckc, siocnputc); 389052561Sobrien 389152561Sobrienstatic void 389252561Sobriensiocntxwait() 389352561Sobrien{ 389452561Sobrien int timo; 389552561Sobrien 389652561Sobrien /* 389752561Sobrien * Wait for any pending transmission to finish. Required to avoid 389852561Sobrien * the UART lockup bug when the speed is changed, and for normal 389952561Sobrien * transmits. 390052561Sobrien */ 390152561Sobrien timo = 100000; 390252561Sobrien while ((inb(siocniobase + com_lsr) & (LSR_TSRE | LSR_TXRDY)) 390352561Sobrien != (LSR_TSRE | LSR_TXRDY) && --timo != 0) 390452561Sobrien ; 390552561Sobrien} 390652561Sobrien 390752561Sobrien/* 390852561Sobrien * Read the serial port specified and try to figure out what speed 390952561Sobrien * it's currently running at. We're assuming the serial port has 391052561Sobrien * been initialized and is basicly idle. This routine is only intended 391152561Sobrien * to be run at system startup. 391252561Sobrien * 391352561Sobrien * If the value read from the serial port doesn't make sense, return 0. 391452561Sobrien */ 391552561Sobrien 391652561Sobrienstatic speed_t 391752561Sobriensiocngetspeed(iobase, table) 391852561Sobrien Port_t iobase; 391952561Sobrien struct speedtab *table; 392052561Sobrien{ 392152561Sobrien int code; 392252561Sobrien u_char dlbh; 392352561Sobrien u_char dlbl; 392452561Sobrien u_char cfcr; 392552561Sobrien 392652561Sobrien cfcr = inb(iobase + com_cfcr); 392752561Sobrien outb(iobase + com_cfcr, CFCR_DLAB | cfcr); 392852561Sobrien 392952561Sobrien dlbl = inb(iobase + com_dlbl); 393052561Sobrien dlbh = inb(iobase + com_dlbh); 393152561Sobrien 393252561Sobrien outb(iobase + com_cfcr, cfcr); 393352561Sobrien 393452561Sobrien code = dlbh << 8 | dlbl; 393550600Sobrien 393650600Sobrien for ( ; table->sp_speed != -1; table++) 393718334Speter if (table->sp_code == code) 393852561Sobrien return (table->sp_speed); 393952561Sobrien 394052561Sobrien return 0; /* didn't match anything sane */ 394152561Sobrien} 394252561Sobrien 394352561Sobrienstatic void 394452561Sobriensiocnopen(sp) 394552561Sobrien struct siocnstate *sp; 394652561Sobrien{ 394752561Sobrien int divisor; 394852561Sobrien u_char dlbh; 394952561Sobrien u_char dlbl; 395052561Sobrien Port_t iobase; 395152561Sobrien 395252561Sobrien /* 395352561Sobrien * Save all the device control registers except the fifo register 395452561Sobrien * and set our default ones (cs8 -parenb speed=comdefaultrate). 395552561Sobrien * We can't save the fifo register since it is read-only. 395652561Sobrien */ 395752561Sobrien iobase = siocniobase; 395852561Sobrien sp->ier = inb(iobase + com_ier); 395952561Sobrien outb(iobase + com_ier, 0); /* spltty() doesn't stop siointr() */ 396052561Sobrien siocntxwait(); 396152561Sobrien sp->cfcr = inb(iobase + com_cfcr); 396252561Sobrien outb(iobase + com_cfcr, CFCR_DLAB | CFCR_8BITS); 396352561Sobrien sp->dlbl = inb(iobase + com_dlbl); 396452561Sobrien sp->dlbh = inb(iobase + com_dlbh); 396552561Sobrien /* 396652561Sobrien * Only set the divisor registers if they would change, since on 396752561Sobrien * some 16550 incompatibles (Startech), setting them clears the 396852561Sobrien * data input register. This also reduces the effects of the 396952561Sobrien * UMC8669F bug. 397052561Sobrien */ 397152561Sobrien divisor = ttspeedtab(comdefaultrate, comspeedtab); 397252561Sobrien dlbl = divisor & 0xFF; 397352561Sobrien if (sp->dlbl != dlbl) 397452561Sobrien outb(iobase + com_dlbl, dlbl); 397552561Sobrien dlbh = (u_int) divisor >> 8; 397652561Sobrien if (sp->dlbh != dlbh) 397752561Sobrien outb(iobase + com_dlbh, dlbh); 397852561Sobrien outb(iobase + com_cfcr, CFCR_8BITS); 397952561Sobrien sp->mcr = inb(iobase + com_mcr); 398052561Sobrien /* 398152561Sobrien * We don't want interrupts, but must be careful not to "disable" 398252561Sobrien * them by clearing the MCR_IENABLE bit, since that might cause 398352561Sobrien * an interrupt by floating the IRQ line. 398452561Sobrien */ 398552561Sobrien outb(iobase + com_mcr, (sp->mcr & MCR_IENABLE) | MCR_DTR | MCR_RTS); 398652561Sobrien} 398752561Sobrien 398852561Sobrienstatic void 398950600Sobriensiocnclose(sp) 399050600Sobrien struct siocnstate *sp; 399118334Speter{ 399218334Speter Port_t iobase; 399318334Speter 399418334Speter /* 399518334Speter * Restore the device control registers. 399618334Speter */ 399718334Speter siocntxwait(); 399818334Speter iobase = siocniobase; 399918334Speter outb(iobase + com_cfcr, CFCR_DLAB | CFCR_8BITS); 400018334Speter if (sp->dlbl != inb(iobase + com_dlbl)) 400118334Speter outb(iobase + com_dlbl, sp->dlbl); 400218334Speter if (sp->dlbh != inb(iobase + com_dlbh)) 400318334Speter outb(iobase + com_dlbh, sp->dlbh); 400418334Speter outb(iobase + com_cfcr, sp->cfcr); 400518334Speter /* 400618334Speter * XXX damp oscillations of MCR_DTR and MCR_RTS by not restoring them. 400718334Speter */ 400818334Speter outb(iobase + com_mcr, sp->mcr | MCR_DTR | MCR_RTS); 400918334Speter outb(iobase + com_ier, sp->ier); 401018334Speter} 401118334Speter 401218334Speterstatic void 401318334Spetersiocnprobe(cp) 401452561Sobrien struct consdev *cp; 401518334Speter{ 401618334Speter speed_t boot_speed; 4017 u_char cfcr; 4018 struct isa_device *dvp; 4019 int s; 4020 struct siocnstate sp; 4021 4022 /* 4023 * Find our first enabled console, if any. If it is a high-level 4024 * console device, then initialize it and return successfully. 4025 * If it is a low-level console device, then initialize it and 4026 * return unsuccessfully. It must be initialized in both cases 4027 * for early use by console drivers and debuggers. Initializing 4028 * the hardware is not necessary in all cases, since the i/o 4029 * routines initialize it on the fly, but it is necessary if 4030 * input might arrive while the hardware is switched back to an 4031 * uninitialized state. We can't handle multiple console devices 4032 * yet because our low-level routines don't take a device arg. 4033 * We trust the user to set the console flags properly so that we 4034 * don't need to probe. 4035 */ 4036 cp->cn_pri = CN_DEAD; 4037 for (dvp = isa_devtab_tty; dvp->id_driver != NULL; dvp++) 4038 if (dvp->id_driver == &siodriver && dvp->id_enabled 4039 && COM_CONSOLE(dvp)) { 4040 siocniobase = dvp->id_iobase; 4041 s = spltty(); 4042 if (boothowto & RB_SERIAL) { 4043 boot_speed = siocngetspeed(siocniobase, 4044 comspeedtab); 4045 if (boot_speed) 4046 comdefaultrate = boot_speed; 4047 } 4048 4049 /* 4050 * Initialize the divisor latch. We can't rely on 4051 * siocnopen() to do this the first time, since it 4052 * avoids writing to the latch if the latch appears 4053 * to have the correct value. Also, if we didn't 4054 * just read the speed from the hardware, then we 4055 * need to set the speed in hardware so that 4056 * switching it later is null. 4057 */ 4058 cfcr = inb(siocniobase + com_cfcr); 4059 outb(siocniobase + com_cfcr, CFCR_DLAB | cfcr); 4060 outb(siocniobase + com_dlbl, 4061 COMBRD(comdefaultrate) & 0xff); 4062 outb(siocniobase + com_dlbh, 4063 (u_int) COMBRD(comdefaultrate) >> 8); 4064 outb(siocniobase + com_cfcr, cfcr); 4065 4066 siocnopen(&sp); 4067 splx(s); 4068 if (!COM_LLCONSOLE(dvp)) { 4069 cp->cn_dev = makedev(CDEV_MAJOR, dvp->id_unit); 4070 cp->cn_pri = COM_FORCECONSOLE(dvp) 4071 || boothowto & RB_SERIAL 4072 ? CN_REMOTE : CN_NORMAL; 4073 } 4074 break; 4075 } 4076} 4077 4078static void 4079siocninit(cp) 4080 struct consdev *cp; 4081{ 4082 comconsole = DEV_TO_UNIT(cp->cn_dev); 4083} 4084 4085static int 4086siocncheckc(dev) 4087 dev_t dev; 4088{ 4089 int c; 4090 Port_t iobase; 4091 int s; 4092 struct siocnstate sp; 4093 4094 iobase = siocniobase; 4095 s = spltty(); 4096 siocnopen(&sp); 4097 if (inb(iobase + com_lsr) & LSR_RXRDY) 4098 c = inb(iobase + com_data); 4099 else 4100 c = -1; 4101 siocnclose(&sp); 4102 splx(s); 4103 return (c); 4104} 4105 4106 4107int 4108siocngetc(dev) 4109 dev_t dev; 4110{ 4111 int c; 4112 Port_t iobase; 4113 int s; 4114 struct siocnstate sp; 4115 4116 iobase = siocniobase; 4117 s = spltty(); 4118 siocnopen(&sp); 4119 while (!(inb(iobase + com_lsr) & LSR_RXRDY)) 4120 ; 4121 c = inb(iobase + com_data); 4122 siocnclose(&sp); 4123 splx(s); 4124 return (c); 4125} 4126 4127void 4128siocnputc(dev, c) 4129 dev_t dev; 4130 int c; 4131{ 4132 int s; 4133 struct siocnstate sp; 4134 4135 s = spltty(); 4136 siocnopen(&sp); 4137 siocntxwait(); 4138 outb(siocniobase + com_data, c); 4139 siocnclose(&sp); 4140 splx(s); 4141} 4142 4143 4144/* 4145 * support PnP cards if we are using 'em 4146 */ 4147 4148#if NPNP > 0 4149 4150static pnpid_t siopnp_ids[] = { 4151 { 0x5015f435, "MOT1550"}, 4152 { 0x8113b04e, "Supra1381"}, 4153 { 0x9012b04e, "Supra1290"}, 4154 { 0x7121b04e, "SupraExpress 56i Sp"}, 4155 { 0x11007256, "USR0011"}, 4156 { 0x30207256, "USR2030"}, 4157 { 0x31307256, "USR3031"}, 4158 { 0x90307256, "USR3090"}, 4159 { 0x0100440e, "Cardinal MVP288IV"}, 4160 { 0 } 4161}; 4162 4163static char *siopnp_probe(u_long csn, u_long vend_id); 4164static void siopnp_attach(u_long csn, u_long vend_id, char *name, 4165 struct isa_device *dev); 4166static u_long nsiopnp = NSIO; 4167 4168static struct pnp_device siopnp = { 4169 "siopnp", 4170 siopnp_probe, 4171 siopnp_attach, 4172 &nsiopnp, 4173 &tty_imask 4174}; 4175DATA_SET (pnpdevice_set, siopnp); 4176 4177static char * 4178siopnp_probe(u_long csn, u_long vend_id) 4179{ 4180 pnpid_t *id; 4181 char *s = NULL; 4182 4183 for(id = siopnp_ids; id->vend_id != 0; id++) { 4184 if (vend_id == id->vend_id) { 4185 s = id->id_str; 4186 break; 4187 } 4188 } 4189 4190 if (s) { 4191 struct pnp_cinfo d; 4192 read_pnp_parms(&d, 0); 4193 if (d.enable == 0 || d.flags & 1) { 4194 printf("CSN %lu is disabled.\n", csn); 4195 return (NULL); 4196 } 4197 4198 } 4199 4200 return (s); 4201} 4202 4203static void 4204siopnp_attach(u_long csn, u_long vend_id, char *name, struct isa_device *dev) 4205{ 4206 struct pnp_cinfo d; 4207 struct isa_device *dvp; 4208 4209 if (dev->id_unit >= NSIOTOT) 4210 return; 4211 4212 if (read_pnp_parms(&d, 0) == 0) { 4213 printf("failed to read pnp parms\n"); 4214 return; 4215 } 4216 4217 write_pnp_parms(&d, 0); 4218 4219 enable_pnp_card(); 4220 4221 dev->id_iobase = d.port[0]; 4222 dev->id_irq = (1 << d.irq[0]); 4223 dev->id_ointr = siointr; 4224 dev->id_ri_flags = RI_FAST; 4225 dev->id_drq = -1; 4226 4227 if (dev->id_driver == NULL) { 4228 dev->id_driver = &siodriver; 4229 dvp = find_isadev(isa_devtab_tty, &siodriver, 0); 4230 if (dvp != NULL) 4231 dev->id_id = dvp->id_id; 4232 } 4233 4234 if ((dev->id_alive = sioprobe(dev)) != 0) 4235 sioattach(dev); 4236 else 4237 printf("sio%d: probe failed\n", dev->id_unit); 4238} 4239#endif 4240 4241#ifdef PC98 4242/* 4243 * pc98 local function 4244 */ 4245 4246static void 4247com_tiocm_set(struct com_s *com, int msr) 4248{ 4249 int s; 4250 int tmp = 0; 4251 int mask = CMD8251_TxEN|CMD8251_RxEN|CMD8251_DTR|CMD8251_RTS; 4252 4253 s=spltty(); 4254 com->pc98_prev_modem_status = ( msr & (TIOCM_LE|TIOCM_DTR|TIOCM_RTS) ) 4255 | ( com->pc98_prev_modem_status & ~(TIOCM_LE|TIOCM_DTR|TIOCM_RTS) ); 4256 tmp |= (CMD8251_TxEN|CMD8251_RxEN); 4257 if ( msr & TIOCM_DTR ) tmp |= CMD8251_DTR; 4258 if ( msr & TIOCM_RTS ) tmp |= CMD8251_RTS; 4259 pc98_i8251_clear_or_cmd( com, mask, tmp ); 4260 splx(s); 4261} 4262 4263static void 4264com_tiocm_bis(struct com_s *com, int msr) 4265{ 4266 int s; 4267 int tmp = 0; 4268 4269 s=spltty(); 4270 com->pc98_prev_modem_status |= ( msr & (TIOCM_LE|TIOCM_DTR|TIOCM_RTS) ); 4271 tmp |= CMD8251_TxEN|CMD8251_RxEN; 4272 if ( msr & TIOCM_DTR ) tmp |= CMD8251_DTR; 4273 if ( msr & TIOCM_RTS ) tmp |= CMD8251_RTS; 4274 4275 pc98_i8251_or_cmd( com, tmp ); 4276 splx(s); 4277} 4278 4279static void 4280com_tiocm_bic(struct com_s *com, int msr) 4281{ 4282 int s; 4283 int tmp = msr; 4284 4285 s=spltty(); 4286 com->pc98_prev_modem_status &= ~( msr & (TIOCM_LE|TIOCM_DTR|TIOCM_RTS) ); 4287 if ( msr & TIOCM_DTR ) tmp |= CMD8251_DTR; 4288 if ( msr & TIOCM_RTS ) tmp |= CMD8251_RTS; 4289 4290 pc98_i8251_clear_cmd( com, tmp ); 4291 splx(s); 4292} 4293 4294static int 4295com_tiocm_get(struct com_s *com) 4296{ 4297 return( com->pc98_prev_modem_status ); 4298} 4299 4300static int 4301com_tiocm_get_delta(struct com_s *com) 4302{ 4303 int tmp; 4304 4305 tmp = com->pc98_modem_delta; 4306 com->pc98_modem_delta = 0; 4307 return( tmp ); 4308} 4309 4310/* convert to TIOCM_?? ( ioctl.h ) */ 4311static int 4312pc98_get_modem_status(struct com_s *com) 4313{ 4314 int stat, stat2; 4315 register int msr; 4316 4317 stat = inb(com->sts_port); 4318 stat2 = inb(com->in_modem_port); 4319 msr = com->pc98_prev_modem_status 4320 & ~(TIOCM_CAR|TIOCM_RI|TIOCM_DSR|TIOCM_CTS); 4321 if ( !(stat2 & CICSCD_CD) ) msr |= TIOCM_CAR; 4322 if ( !(stat2 & CICSCD_CI) ) msr |= TIOCM_RI; 4323 if ( stat & STS8251_DSR ) msr |= TIOCM_DSR; 4324 if ( !(stat2 & CICSCD_CS) ) msr |= TIOCM_CTS; 4325#if COM_CARRIER_DETECT_EMULATE 4326 if ( msr & (TIOCM_DSR|TIOCM_CTS) ) { 4327 msr |= TIOCM_CAR; 4328 } 4329#endif 4330 return(msr); 4331} 4332 4333static void 4334pc98_check_msr(void* chan) 4335{ 4336 int msr, delta; 4337 int s; 4338 register struct tty *tp; 4339 struct com_s *com; 4340 int mynor; 4341 int unit; 4342 dev_t dev; 4343 4344 dev=(dev_t)chan; 4345 mynor = minor(dev); 4346 unit = MINOR_TO_UNIT(mynor); 4347 com = com_addr(unit); 4348 tp = com->tp; 4349 4350 s = spltty(); 4351 msr = pc98_get_modem_status(com); 4352 /* make change flag */ 4353 delta = msr ^ com->pc98_prev_modem_status; 4354 if ( delta & TIOCM_CAR ) { 4355 if ( com->modem_car_chg_timer ) { 4356 if ( -- com->modem_car_chg_timer ) 4357 msr ^= TIOCM_CAR; 4358 } else { 4359 if ((com->modem_car_chg_timer = (msr & TIOCM_CAR) ? 4360 DCD_ON_RECOGNITION : DCD_OFF_TOLERANCE) != 0) 4361 msr ^= TIOCM_CAR; 4362 } 4363 } else 4364 com->modem_car_chg_timer = 0; 4365 delta = ( msr ^ com->pc98_prev_modem_status ) & 4366 (TIOCM_CAR|TIOCM_RI|TIOCM_DSR|TIOCM_CTS); 4367 com->pc98_prev_modem_status = msr; 4368 delta = ( com->pc98_modem_delta |= delta ); 4369 splx(s); 4370 if ( com->modem_checking || (tp->t_state & (TS_ISOPEN)) ) { 4371 if ( delta ) { 4372 commint(dev); 4373 } 4374 timeout(pc98_check_msr, (caddr_t)dev, 4375 PC98_CHECK_MODEM_INTERVAL); 4376 } else { 4377 com->modem_checking = 0; 4378 } 4379} 4380 4381static void 4382pc98_msrint_start(dev_t dev) 4383{ 4384 struct com_s *com; 4385 int mynor; 4386 int unit; 4387 int s = spltty(); 4388 4389 mynor = minor(dev); 4390 unit = MINOR_TO_UNIT(mynor); 4391 com = com_addr(unit); 4392 /* modem control line check routine envoke interval is 1/10 sec */ 4393 if ( com->modem_checking == 0 ) { 4394 com->pc98_prev_modem_status = pc98_get_modem_status(com); 4395 com->pc98_modem_delta = 0; 4396 timeout(pc98_check_msr, (caddr_t)dev, 4397 PC98_CHECK_MODEM_INTERVAL); 4398 com->modem_checking = 1; 4399 } 4400 splx(s); 4401} 4402 4403static void 4404pc98_disable_i8251_interrupt(struct com_s *com, int mod) 4405{ 4406 /* disable interrupt */ 4407 register int tmp; 4408 4409 mod |= ~(IEN_Tx|IEN_TxEMP|IEN_Rx); 4410 COM_INT_DISABLE 4411 tmp = inb( com->intr_ctrl_port ) & ~(IEN_Tx|IEN_TxEMP|IEN_Rx); 4412 outb( com->intr_ctrl_port, (com->intr_enable&=~mod) | tmp ); 4413 COM_INT_ENABLE 4414} 4415 4416static void 4417pc98_enable_i8251_interrupt(struct com_s *com, int mod) 4418{ 4419 register int tmp; 4420 4421 COM_INT_DISABLE 4422 tmp = inb( com->intr_ctrl_port ) & ~(IEN_Tx|IEN_TxEMP|IEN_Rx); 4423 outb( com->intr_ctrl_port, (com->intr_enable|=mod) | tmp ); 4424 COM_INT_ENABLE 4425} 4426 4427static int 4428pc98_check_i8251_interrupt(struct com_s *com) 4429{ 4430 return ( com->intr_enable & 0x07 ); 4431} 4432 4433static void 4434pc98_i8251_clear_cmd(struct com_s *com, int x) 4435{ 4436 int tmp; 4437 4438 COM_INT_DISABLE 4439 tmp = com->pc98_prev_siocmd & ~(x); 4440 outb(com->cmd_port, tmp); 4441 com->pc98_prev_siocmd = tmp & ~(CMD8251_ER|CMD8251_RESET|CMD8251_EH); 4442 COM_INT_ENABLE 4443} 4444 4445static void 4446pc98_i8251_or_cmd(struct com_s *com, int x) 4447{ 4448 int tmp; 4449 4450 COM_INT_DISABLE 4451 tmp = com->pc98_prev_siocmd | (x); 4452 outb(com->cmd_port, tmp); 4453 com->pc98_prev_siocmd = tmp & ~(CMD8251_ER|CMD8251_RESET|CMD8251_EH); 4454 COM_INT_ENABLE 4455} 4456 4457static void 4458pc98_i8251_set_cmd(struct com_s *com, int x) 4459{ 4460 int tmp; 4461 4462 COM_INT_DISABLE 4463 tmp = (x); 4464 outb(com->cmd_port, tmp); 4465 com->pc98_prev_siocmd = tmp & ~(CMD8251_ER|CMD8251_RESET|CMD8251_EH); 4466 COM_INT_ENABLE 4467} 4468 4469static void 4470pc98_i8251_clear_or_cmd(struct com_s *com, int clr, int x) 4471{ 4472 int tmp; 4473 COM_INT_DISABLE 4474 tmp = com->pc98_prev_siocmd & ~(clr); 4475 tmp |= (x); 4476 outb(com->cmd_port, tmp); 4477 com->pc98_prev_siocmd = tmp & ~(CMD8251_ER|CMD8251_RESET|CMD8251_EH); 4478 COM_INT_ENABLE 4479} 4480 4481static int 4482pc98_i8251_get_cmd(struct com_s *com) 4483{ 4484 return com->pc98_prev_siocmd; 4485} 4486 4487static int 4488pc98_i8251_get_mod(struct com_s *com) 4489{ 4490 return com->pc98_prev_siomod; 4491} 4492 4493static void 4494pc98_i8251_reset(struct com_s *com, int mode, int command) 4495{ 4496 outb(com->cmd_port, 0); /* dummy */ 4497 DELAY(2); 4498 outb(com->cmd_port, 0); /* dummy */ 4499 DELAY(2); 4500 outb(com->cmd_port, 0); /* dummy */ 4501 DELAY(2); 4502 outb(com->cmd_port, CMD8251_RESET); /* internal reset */ 4503 DELAY(2); 4504 outb(com->cmd_port, mode ); /* mode register */ 4505 com->pc98_prev_siomod = mode; 4506 DELAY(2); 4507 pc98_i8251_set_cmd( com, (command|CMD8251_ER) ); 4508} 4509 4510static void 4511pc98_check_sysclock(void) 4512{ 4513 /* get system clock from port */ 4514 if ( pc98_machine_type & M_8M ) { 4515 /* 8 MHz system & H98 */ 4516 sysclock = 8; 4517 } else { 4518 /* 5 MHz system */ 4519 sysclock = 5; 4520 } 4521} 4522 4523static void 4524com_cflag_and_speed_set( struct com_s *com, int cflag, int speed) 4525{ 4526 int cfcr=0, count; 4527 int previnterrupt; 4528 4529 count = pc98_ttspeedtab( com, speed ); 4530 if ( count < 0 ) return; 4531 4532 previnterrupt = pc98_check_i8251_interrupt(com); 4533 pc98_disable_i8251_interrupt( com, IEN_Tx|IEN_TxEMP|IEN_Rx ); 4534 4535 switch ( cflag&CSIZE ) { 4536 case CS5: 4537 cfcr = MOD8251_5BITS; break; 4538 case CS6: 4539 cfcr = MOD8251_6BITS; break; 4540 case CS7: 4541 cfcr = MOD8251_7BITS; break; 4542 case CS8: 4543 cfcr = MOD8251_8BITS; break; 4544 } 4545 if ( cflag&PARENB ) { 4546 if ( cflag&PARODD ) 4547 cfcr |= MOD8251_PODD; 4548 else 4549 cfcr |= MOD8251_PEVEN; 4550 } else 4551 cfcr |= MOD8251_PDISAB; 4552 4553 if ( cflag&CSTOPB ) 4554 cfcr |= MOD8251_STOP2; 4555 else 4556 cfcr |= MOD8251_STOP1; 4557 4558 if ( count & 0x10000 ) 4559 cfcr |= MOD8251_CLKX1; 4560 else 4561 cfcr |= MOD8251_CLKX16; 4562 4563 if (epson_machine_id != 0x20) { /* XXX */ 4564 int tmp; 4565 while (!((tmp = inb(com->sts_port)) & STS8251_TxEMP)) 4566 ; 4567 } 4568 /* set baud rate from ospeed */ 4569 pc98_set_baud_rate( com, count ); 4570 4571 if ( cfcr != pc98_i8251_get_mod(com) ) 4572 pc98_i8251_reset(com, cfcr, pc98_i8251_get_cmd(com) ); 4573 4574 pc98_enable_i8251_interrupt( com, previnterrupt ); 4575} 4576 4577static int 4578pc98_ttspeedtab(struct com_s *com, int speed) 4579{ 4580 int if_type, effect_sp, count = -1, mod; 4581 4582 if_type = com->pc98_if_type & 0x0f; 4583 4584 switch (com->pc98_if_type) { 4585 case COM_IF_INTERNAL: 4586 if (PC98SIO_baud_rate_port(if_type) != -1) { 4587 count = ttspeedtab(speed, if_8251_type[if_type].speedtab); 4588 if (count > 0) { 4589 count |= COM1_EXT_CLOCK; 4590 break; 4591 } 4592 } 4593 4594 /* for *1CLK asynchronous! mode, TEFUTEFU */ 4595 mod = (sysclock == 5) ? 2457600 : 1996800; 4596 effect_sp = ttspeedtab( speed, pc98speedtab ); 4597 if ( effect_sp < 0 ) /* XXX */ 4598 effect_sp = ttspeedtab( (speed - 1), pc98speedtab ); 4599 if ( effect_sp <= 0 ) 4600 return effect_sp; 4601 if ( effect_sp == speed ) 4602 mod /= 16; 4603 if ( mod % effect_sp ) 4604 return(-1); 4605 count = mod / effect_sp; 4606 if ( count > 65535 ) 4607 return(-1); 4608 if ( effect_sp != speed ) 4609 count |= 0x10000; 4610 break; 4611 case COM_IF_PC9861K_1: 4612 case COM_IF_PC9861K_2: 4613 count = 1; 4614 break; 4615 case COM_IF_IND_SS_1: 4616 case COM_IF_IND_SS_2: 4617 case COM_IF_PIO9032B_1: 4618 case COM_IF_PIO9032B_2: 4619 if ( speed == 0 ) return 0; 4620 count = ttspeedtab( speed, if_8251_type[if_type].speedtab ); 4621 break; 4622 case COM_IF_B98_01_1: 4623 case COM_IF_B98_01_2: 4624 if ( speed == 0 ) return 0; 4625 count = ttspeedtab( speed, if_8251_type[if_type].speedtab ); 4626#ifdef B98_01_OLD 4627 if (count == 0 || count == 1) { 4628 count += 4; 4629 count |= 0x20000; /* x1 mode for 76800 and 153600 */ 4630 } 4631#endif 4632 break; 4633 } 4634 4635 return count; 4636} 4637 4638static void 4639pc98_set_baud_rate( struct com_s *com, int count ) 4640{ 4641 int if_type, io, s; 4642 4643 if_type = com->pc98_if_type & 0x0f; 4644 io = com->iobase & 0xff00; 4645 4646 switch (com->pc98_if_type) { 4647 case COM_IF_INTERNAL: 4648 if (PC98SIO_baud_rate_port(if_type) != -1) { 4649 if (count & COM1_EXT_CLOCK) { 4650 outb((Port_t)PC98SIO_baud_rate_port(if_type), count & 0xff); 4651 break; 4652 } else { 4653 outb((Port_t)PC98SIO_baud_rate_port(if_type), 0x09); 4654 } 4655 } 4656 4657 if ( count < 0 ) { 4658 printf( "[ Illegal count : %d ]", count ); 4659 return; 4660 } else if ( count == 0 ) 4661 return; 4662 /* set i8253 */ 4663 s = splclock(); 4664 if (count != 3) 4665 outb( 0x77, 0xb6 ); 4666 else 4667 outb( 0x77, 0xb4 ); 4668 outb( 0x5f, 0); 4669 outb( 0x75, count & 0xff ); 4670 outb( 0x5f, 0); 4671 outb( 0x75, (count >> 8) & 0xff ); 4672 splx(s); 4673 break; 4674 case COM_IF_IND_SS_1: 4675 case COM_IF_IND_SS_2: 4676 outb(io | PC98SIO_intr_ctrl_port(if_type), 0); 4677 outb(io | PC98SIO_baud_rate_port(if_type), 0); 4678 outb(io | PC98SIO_baud_rate_port(if_type), 0xc0); 4679 outb(io | PC98SIO_baud_rate_port(if_type), (count >> 8) | 0x80); 4680 outb(io | PC98SIO_baud_rate_port(if_type), count & 0xff); 4681 break; 4682 case COM_IF_PIO9032B_1: 4683 case COM_IF_PIO9032B_2: 4684 outb(io | PC98SIO_baud_rate_port(if_type), count); 4685 break; 4686 case COM_IF_B98_01_1: 4687 case COM_IF_B98_01_2: 4688 outb(io | PC98SIO_baud_rate_port(if_type), count & 0x0f); 4689#ifdef B98_01_OLD 4690 /* 4691 * Some old B98_01 board should be controlled 4692 * in different way, but this hasn't been tested yet. 4693 */ 4694 outb(io | PC98SIO_func_port(if_type), 4695 (count & 0x20000) ? 0xf0 : 0xf2); 4696#endif 4697 break; 4698 } 4699} 4700static int 4701pc98_check_if_type(struct isa_device *dev, struct siodev *iod) 4702{ 4703 int irr, io, if_type, tmp; 4704 static short irq_tab[2][8] = { 4705 { 3, 5, 6, 9, 10, 12, 13, -1}, 4706 { 3, 10, 12, 13, 5, 6, 9, -1} 4707 }; 4708 4709 iod->if_type = if_type = (dev->id_flags >> 24) & 0xff; 4710 if ((if_type < 0 || if_type > COM_IF_END1) && 4711 (if_type < 0x10 || if_type > COM_IF_END2)) 4712 return(-1); 4713 if_type &= 0x0f; 4714 iod->irq = 0; 4715 io = dev->id_iobase & 0xff00; 4716 4717 if (IS_8251(iod->if_type)) { 4718 if (PC98SIO_func_port(if_type) != -1) { 4719 outb(io | PC98SIO_func_port(if_type), 0xf2); 4720 tmp = ttspeedtab(9600, if_8251_type[if_type].speedtab); 4721 if (tmp != -1 && PC98SIO_baud_rate_port(if_type) != -1) 4722 outb(io | PC98SIO_baud_rate_port(if_type), tmp); 4723 } 4724 4725 iod->cmd = io | PC98SIO_cmd_port(if_type); 4726 iod->sts = io | PC98SIO_sts_port(if_type); 4727 iod->mod = io | PC98SIO_in_modem_port(if_type); 4728 iod->ctrl = io | PC98SIO_intr_ctrl_port(if_type); 4729 4730 if (iod->if_type == COM_IF_INTERNAL) { 4731 iod->irq = 4; 4732 4733 /* XXX check new internal port. */ 4734 outb(0x138, 0); 4735 DELAY(10); 4736 for (tmp = 0; tmp < 100; tmp++) { 4737 if ((inb(0x138) & 1) == 0) { 4738 PC98SIO_baud_rate_port(if_type) = 0x13a; 4739 if_8251_type[if_type].name = " (internal fast)"; 4740 if_8251_type[if_type].speedtab = pc98fast_speedtab; 4741 break; 4742 } 4743 DELAY(1); 4744 } 4745 } else { 4746 tmp = inb( iod->mod ) & if_8251_type[if_type].irr_mask; 4747 if ((dev->id_iobase & 0xff) == IO_COM2) 4748 iod->irq = irq_tab[0][tmp]; 4749 else 4750 iod->irq = irq_tab[1][tmp]; 4751 } 4752 } else { 4753 irr = if_16550a_type[if_type].irr_read; 4754#ifdef COM_MULTIPORT 4755 if (!COM_ISMULTIPORT(dev) || dev->id_unit == COM_MPMASTER(dev)) 4756#endif 4757 if (irr != -1) { 4758 tmp = inb(io | irr); 4759 if (dev->id_iobase & 0x01) /* XXX depend on RSB-384 */ 4760 iod->irq = irq_tab[1][tmp >> 3]; 4761 else 4762 iod->irq = irq_tab[0][tmp & 0x07]; 4763 } 4764 } 4765 if ( iod->irq == -1 ) return -1; 4766 4767 return 0; 4768} 4769static int 4770pc98_set_ioport( struct com_s *com, int id_flags ) 4771{ 4772 int io, if_type; 4773 4774 if_type = (id_flags >> 24) & 0xff; 4775 if (IS_8251(if_type)) { 4776 pc98_check_sysclock(); 4777 io = com->iobase & 0xff00; 4778 com->pc98_if_type = if_type; 4779 if_type &= 0x0f; 4780 com->data_port = io | PC98SIO_data_port(if_type); 4781 com->cmd_port = io | PC98SIO_cmd_port(if_type); 4782 com->sts_port = io | PC98SIO_sts_port(if_type); 4783 com->in_modem_port = io | PC98SIO_in_modem_port(if_type); 4784 com->intr_ctrl_port = io | PC98SIO_intr_ctrl_port(if_type); 4785 return 0; 4786 } 4787 4788 return -1; 4789} 4790#endif /* PC98 defined */ 4791