sio.c revision 89447
162587Sitojun/*- 278064Sume * Copyright (c) 1991 The Regents of the University of California. 362587Sitojun * All rights reserved. 4139826Simp * 553541Sshin * Redistribution and use in source and binary forms, with or without 653541Sshin * modification, are permitted provided that the following conditions 753541Sshin * are met: 853541Sshin * 1. Redistributions of source code must retain the above copyright 953541Sshin * notice, this list of conditions and the following disclaimer. 1053541Sshin * 2. Redistributions in binary form must reproduce the above copyright 1153541Sshin * notice, this list of conditions and the following disclaimer in the 1253541Sshin * documentation and/or other materials provided with the distribution. 1353541Sshin * 3. All advertising materials mentioning features or use of this software 1453541Sshin * must display the following acknowledgement: 1553541Sshin * This product includes software developed by the University of 1653541Sshin * California, Berkeley and its contributors. 1753541Sshin * 4. Neither the name of the University nor the names of its contributors 1853541Sshin * may be used to endorse or promote products derived from this software 1953541Sshin * without specific prior written permission. 2053541Sshin * 2153541Sshin * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2253541Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2353541Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2453541Sshin * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2553541Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2653541Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2753541Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2853541Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2953541Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3053541Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3153541Sshin * SUCH DAMAGE. 3253541Sshin * 33139826Simp * $FreeBSD: head/sys/dev/sio/sio.c 89447 2002-01-17 06:21:45Z bmah $ 3453541Sshin * from: @(#)com.c 7.5 (Berkeley) 5/16/91 3553541Sshin * from: i386/isa sio.c,v 1.234 3653541Sshin */ 3753541Sshin 3853541Sshin#include "opt_comconsole.h" 3953541Sshin#include "opt_compat.h" 4053541Sshin#include "opt_ddb.h" 4153541Sshin#include "opt_sio.h" 4253541Sshin 4353541Sshin/* 4453541Sshin * Serial driver, based on 386BSD-0.1 com driver. 4553541Sshin * Mostly rewritten to use pseudo-DMA. 4653541Sshin * Works for National Semiconductor NS8250-NS16550AF UARTs. 4753541Sshin * COM driver, based on HP dca driver. 4853541Sshin * 4953541Sshin * Changes for PC-Card integration: 5053541Sshin * - Added PC-Card driver table and handlers 5153541Sshin */ 5253541Sshin#include <sys/param.h> 5353541Sshin#include <sys/systm.h> 5453541Sshin#include <sys/bus.h> 5553541Sshin#include <sys/conf.h> 5653541Sshin#include <sys/dkstat.h> 5753541Sshin#include <sys/fcntl.h> 5853541Sshin#include <sys/interrupt.h> 5953541Sshin#include <sys/kernel.h> 6053541Sshin#include <sys/lock.h> 6153541Sshin#include <sys/malloc.h> 6253541Sshin#include <sys/module.h> 6353541Sshin#include <sys/mutex.h> 6462587Sitojun#include <sys/proc.h> 6562587Sitojun#include <sys/reboot.h> 6655009Sshin#include <sys/sysctl.h> 6753541Sshin#include <sys/syslog.h> 6853541Sshin#include <sys/tty.h> 6995759Stanimura#include <machine/bus_pio.h> 7095759Stanimura#include <machine/bus.h> 7195759Stanimura#include <sys/rman.h> 7278064Sume#include <sys/timetc.h> 7353541Sshin#include <sys/timepps.h> 7453541Sshin 7595759Stanimura#include <isa/isavar.h> 7653541Sshin 7753541Sshin#include <machine/resource.h> 7895759Stanimura 7995759Stanimura#include <dev/sio/sioreg.h> 8095759Stanimura#include <dev/sio/siovar.h> 8153541Sshin 8253541Sshin#ifdef COM_ESP 8353541Sshin#include <dev/ic/esp.h> 8453541Sshin#endif 8553541Sshin#include <dev/ic/ns16550.h> 8695759Stanimura 8753541Sshin#define LOTS_OF_EVENTS 64 /* helps separate urgent events from input */ 8853541Sshin 8995759Stanimura#define CALLOUT_MASK 0x80 9053541Sshin#define CONTROL_MASK 0x60 9162587Sitojun#define CONTROL_INIT_STATE 0x20 9295759Stanimura#define CONTROL_LOCK_STATE 0x40 93122922Sandre#define DEV_TO_UNIT(dev) (MINOR_TO_UNIT(minor(dev))) 9495759Stanimura#define MINOR_MAGIC_MASK (CALLOUT_MASK | CONTROL_MASK) 9595759Stanimura#define MINOR_TO_UNIT(mynor) ((mynor) & ~MINOR_MAGIC_MASK) 9695759Stanimura 9753541Sshin#ifdef COM_MULTIPORT 98148385Sume/* checks in flags for multiport and which is multiport "master chip" 9953541Sshin * for a given card 10053541Sshin */ 10153541Sshin#define COM_ISMULTIPORT(flags) ((flags) & 0x01) 10253541Sshin#define COM_MPMASTER(flags) (((flags) >> 8) & 0x0ff) 10353541Sshin#define COM_NOTAST4(flags) ((flags) & 0x04) 10453541Sshin#endif /* COM_MULTIPORT */ 10555009Sshin 10653541Sshin#define COM_CONSOLE(flags) ((flags) & 0x10) 107105199Ssam#define COM_FORCECONSOLE(flags) ((flags) & 0x20) 108105199Ssam#define COM_LLCONSOLE(flags) ((flags) & 0x40) 109105199Ssam#define COM_DEBUGGER(flags) ((flags) & 0x80) 110105199Ssam#define COM_LOSESOUTINTS(flags) ((flags) & 0x08) 111105199Ssam#define COM_NOFIFO(flags) ((flags) & 0x02) 11262587Sitojun#define COM_ST16650A(flags) ((flags) & 0x20000) 11353541Sshin#define COM_C_NOPROBE (0x40000) 11462587Sitojun#define COM_NOPROBE(flags) ((flags) & COM_C_NOPROBE) 11553541Sshin#define COM_C_IIR_TXRDYBUG (0x80000) 116141553Srwatson#define COM_IIR_TXRDYBUG(flags) ((flags) & COM_C_IIR_TXRDYBUG) 11762587Sitojun#define COM_FIFOSIZE(flags) (((flags) & 0xff000000) >> 24) 11862587Sitojun 11962587Sitojun#define com_scr 7 /* scratch register for 16450-16550 (R/W) */ 12078064Sume 12162587Sitojun#define sio_getreg(com, off) \ 12253541Sshin (bus_space_read_1((com)->bst, (com)->bsh, (off))) 12362587Sitojun#define sio_setreg(com, off, value) \ 12462587Sitojun (bus_space_write_1((com)->bst, (com)->bsh, (off), (value))) 12562587Sitojun 12662587Sitojun/* 12762587Sitojun * com state bits. 12862587Sitojun * (CS_BUSY | CS_TTGO) and (CS_BUSY | CS_TTGO | CS_ODEVREADY) must be higher 12962587Sitojun * than the other bits so that they can be tested as a group without masking 13062587Sitojun * off the low bits. 13162587Sitojun * 132148892Sume * The following com and tty flags correspond closely: 13362587Sitojun * CS_BUSY = TS_BUSY (maintained by comstart(), siopoll() and 13462587Sitojun * comstop()) 135142681Sume * CS_TTGO = ~TS_TTSTOP (maintained by comparam() and comstart()) 13653541Sshin * CS_CTS_OFLOW = CCTS_OFLOW (maintained by comparam()) 13753541Sshin * CS_RTS_IFLOW = CRTS_IFLOW (maintained by comparam()) 13853541Sshin * TS_FLUSH is not used. 13953541Sshin * XXX I think TIOCSETA doesn't clear TS_TTSTOP when it clears IXON. 14053541Sshin * XXX CS_*FLOW should be CF_*FLOW in com->flags (control flags not state). 14153541Sshin */ 14253541Sshin#define CS_BUSY 0x80 /* output in progress */ 14353541Sshin#define CS_TTGO 0x40 /* output not stopped by XOFF */ 14462587Sitojun#define CS_ODEVREADY 0x20 /* external device h/w ready (CTS) */ 14562587Sitojun#define CS_CHECKMSR 1 /* check of MSR scheduled */ 14662587Sitojun#define CS_CTS_OFLOW 2 /* use CTS output flow control */ 14762587Sitojun#define CS_DTR_OFF 0x10 /* DTR held off */ 14862587Sitojun#define CS_ODONE 4 /* output completed */ 14978064Sume#define CS_RTS_IFLOW 8 /* use RTS input flow control */ 15062587Sitojun#define CSE_BUSYCHECK 1 /* siobusycheck() scheduled */ 15162587Sitojun 15262587Sitojunstatic char const * const error_desc[] = { 15362587Sitojun#define CE_OVERRUN 0 15462587Sitojun "silo overflow", 15562587Sitojun#define CE_INTERRUPT_BUF_OVERFLOW 1 15662587Sitojun "interrupt-level buffer overflow", 15762587Sitojun#define CE_TTY_BUF_OVERFLOW 2 15862587Sitojun "tty-level buffer overflow", 15962587Sitojun}; 16062587Sitojun 16162587Sitojun#define CE_NTYPES 3 16262587Sitojun#define CE_RECORD(com, errnum) (++(com)->delta_error_counts[errnum]) 16362587Sitojun 16462587Sitojun/* types. XXX - should be elsewhere */ 16562587Sitojuntypedef u_int Port_t; /* hardware port */ 16662587Sitojuntypedef u_char bool_t; /* boolean */ 16762587Sitojun 16862587Sitojun/* queue of linear buffers */ 16962587Sitojunstruct lbq { 17062587Sitojun u_char *l_head; /* next char to process */ 17162587Sitojun u_char *l_tail; /* one past the last char to process */ 17262587Sitojun struct lbq *l_next; /* next in queue */ 17378064Sume bool_t l_queued; /* nonzero if queued */ 17462587Sitojun}; 17562587Sitojun 17662587Sitojun/* com device structure */ 17762587Sitojunstruct com_s { 17862587Sitojun u_int flags; /* Copy isa device flags */ 17962587Sitojun u_char state; /* miscellaneous flag bits */ 18062587Sitojun bool_t active_out; /* nonzero if the callout device is open */ 18162587Sitojun u_char cfcr_image; /* copy of value written to CFCR */ 18262587Sitojun#ifdef COM_ESP 18378064Sume bool_t esp; /* is this unit a hayes esp board? */ 18462587Sitojun#endif 18562587Sitojun u_char extra_state; /* more flag bits, separate for order trick */ 18662587Sitojun u_char fifo_image; /* copy of value written to FIFO */ 18762587Sitojun bool_t hasfifo; /* nonzero for 16550 UARTs */ 18862587Sitojun bool_t st16650a; /* Is a Startech 16650A or RTS/CTS compat */ 18962587Sitojun bool_t loses_outints; /* nonzero if device loses output interrupts */ 19062587Sitojun u_char mcr_image; /* copy of value written to MCR */ 19162587Sitojun#ifdef COM_MULTIPORT 19262587Sitojun bool_t multiport; /* is this unit part of a multiport device? */ 19362587Sitojun#endif /* COM_MULTIPORT */ 19462587Sitojun bool_t no_irq; /* nonzero if irq is not attached */ 19562587Sitojun bool_t gone; /* hardware disappeared */ 19662587Sitojun bool_t poll; /* nonzero if polling is required */ 19762587Sitojun bool_t poll_output; /* nonzero if polling for output is required */ 19862587Sitojun int unit; /* unit number */ 19962587Sitojun int dtr_wait; /* time to hold DTR down on close (* 1/hz) */ 20062587Sitojun u_int tx_fifo_size; 20162587Sitojun u_int wopeners; /* # processes waiting for DCD in open() */ 20253541Sshin 203148385Sume /* 204148385Sume * The high level of the driver never reads status registers directly 205148385Sume * because there would be too many side effects to handle conveniently. 206148385Sume * Instead, it reads copies of the registers stored here by the 207148385Sume * interrupt handler. 208148385Sume */ 209148385Sume u_char last_modem_status; /* last MSR read by intr handler */ 210148385Sume u_char prev_modem_status; /* last MSR handled by high level */ 211148385Sume 212148385Sume u_char hotchar; /* ldisc-specific char to be handled ASAP */ 213148385Sume u_char *ibuf; /* start of input buffer */ 214148385Sume u_char *ibufend; /* end of input buffer */ 215148385Sume u_char *ibufold; /* old input buffer, to be freed */ 216148385Sume u_char *ihighwater; /* threshold in input buffer */ 217148385Sume u_char *iptr; /* next free spot in input buffer */ 218148385Sume int ibufsize; /* size of ibuf (not include error bytes) */ 219148385Sume int ierroff; /* offset of error bytes in ibuf */ 220148385Sume 221148385Sume struct lbq obufq; /* head of queue of output buffers */ 222148385Sume struct lbq obufs[2]; /* output buffers */ 223148385Sume 224148385Sume bus_space_tag_t bst; 225148385Sume bus_space_handle_t bsh; 226148385Sume 227148385Sume Port_t data_port; /* i/o ports */ 228148385Sume#ifdef COM_ESP 229148385Sume Port_t esp_port; 230148385Sume#endif 231148385Sume Port_t int_id_port; 232148385Sume Port_t modem_ctl_port; 233148385Sume Port_t line_status_port; 234148385Sume Port_t modem_status_port; 235148385Sume Port_t intr_ctl_port; /* Ports of IIR register */ 236148385Sume 237148385Sume struct tty *tp; /* cross reference */ 23853541Sshin 23953541Sshin /* Initial state. */ 24053541Sshin struct termios it_in; /* should be in struct tty */ 24153541Sshin struct termios it_out; 24253541Sshin 24353541Sshin /* Lock state. */ 24453541Sshin struct termios lt_in; /* should be in struct tty */ 24553541Sshin struct termios lt_out; 24653541Sshin 24762587Sitojun bool_t do_timestamp; 24853541Sshin bool_t do_dcd_timestamp; 24962587Sitojun struct timeval timestamp; 25053541Sshin struct timeval dcd_timestamp; 25153541Sshin struct pps_state pps; 25253541Sshin 25362587Sitojun u_long bytes_in; /* statistics */ 25462587Sitojun u_long bytes_out; 25562587Sitojun u_int delta_error_counts[CE_NTYPES]; 25662587Sitojun u_long error_counts[CE_NTYPES]; 25762587Sitojun 25862587Sitojun struct resource *irqres; 25953541Sshin struct resource *ioportres; 26062587Sitojun void *cookie; 26162587Sitojun dev_t devs[6]; 26253541Sshin 26362587Sitojun /* 26462587Sitojun * Data area for output buffers. Someday we should build the output 26562587Sitojun * buffer queue without copying data. 26662587Sitojun */ 26762587Sitojun u_char obuf1[256]; 26862587Sitojun u_char obuf2[256]; 26962587Sitojun}; 27062587Sitojun 27162587Sitojun#ifdef COM_ESP 27253541Sshinstatic int espattach __P((struct com_s *com, Port_t esp_port)); 27353541Sshin#endif 27453541Sshin 275121472Sumestatic timeout_t siobusycheck; 276121472Sumestatic timeout_t siodtrwakeup; 277121472Sumestatic void comhardclose __P((struct com_s *com)); 278121472Sumestatic void sioinput __P((struct com_s *com)); 279121472Sumestatic void siointr1 __P((struct com_s *com)); 280121472Sumestatic void siointr __P((void *arg)); 281121472Sumestatic int commctl __P((struct com_s *com, int bits, int how)); 282121472Sumestatic int comparam __P((struct tty *tp, struct termios *t)); 283121472Sumestatic void siopoll __P((void *)); 28453541Sshinstatic void siosettimeout __P((void)); 28553541Sshinstatic int siosetwater __P((struct com_s *com, speed_t speed)); 28653541Sshinstatic void comstart __P((struct tty *tp)); 28753541Sshinstatic void comstop __P((struct tty *tp, int rw)); 28853541Sshinstatic timeout_t comwakeup; 28953541Sshinstatic void disc_optim __P((struct tty *tp, struct termios *t, 29053541Sshin struct com_s *com)); 29153541Sshin 292121472Sumechar sio_driver_name[] = "sio"; 293121472Sumestatic struct mtx sio_lock; 294121472Sumestatic int sio_inited; 295121472Sume 29653541Sshin/* table and macro for fast conversion from a unit number to its com struct */ 29753541Sshindevclass_t sio_devclass; 29853541Sshin#define com_addr(unit) ((struct com_s *) \ 29953541Sshin devclass_get_softc(sio_devclass, unit)) /* XXX */ 30053541Sshin 30162587Sitojunstatic d_open_t sioopen; 30262587Sitojunstatic d_close_t sioclose; 30353541Sshinstatic d_read_t sioread; 30462587Sitojunstatic d_write_t siowrite; 30562587Sitojunstatic d_ioctl_t sioioctl; 30662587Sitojun 30753541Sshin#define CDEV_MAJOR 28 30853541Sshinstatic struct cdevsw sio_cdevsw = { 30962587Sitojun /* open */ sioopen, 31062587Sitojun /* close */ sioclose, 31162587Sitojun /* read */ sioread, 31262587Sitojun /* write */ siowrite, 31362587Sitojun /* ioctl */ sioioctl, 31462587Sitojun /* poll */ ttypoll, 31562587Sitojun /* mmap */ nommap, 31662587Sitojun /* strategy */ nostrategy, 31762587Sitojun /* name */ sio_driver_name, 31862587Sitojun /* maj */ CDEV_MAJOR, 31962587Sitojun /* dump */ nodump, 32062587Sitojun /* psize */ nopsize, 32162587Sitojun /* flags */ D_TTY | D_KQFILTER, 32253541Sshin /* kqfilter */ ttykqfilter, 32362587Sitojun}; 32462587Sitojun 32562587Sitojunint comconsole = -1; 32653541Sshinstatic volatile speed_t comdefaultrate = CONSPEED; 32762587Sitojun#ifdef __alpha__ 32862587Sitojunstatic volatile speed_t gdbdefaultrate = CONSPEED; 32962587Sitojun#endif 33062587Sitojunstatic u_int com_events; /* input chars + weighted output completions */ 33153541Sshinstatic Port_t siocniobase; 33262587Sitojun#ifndef __alpha__ 33362587Sitojunstatic int siocnunit; 33453541Sshin#endif 33553541Sshinstatic Port_t siogdbiobase; 33653541Sshinstatic int siogdbunit = -1; 33753541Sshinstatic void *sio_slow_ih; 33853541Sshinstatic void *sio_fast_ih; 33953541Sshinstatic int sio_timeout; 34053541Sshinstatic int sio_timeouts_until_log; 34153541Sshinstatic struct callout_handle sio_timeout_handle 34253541Sshin = CALLOUT_HANDLE_INITIALIZER(&sio_timeout_handle); 34353541Sshinstatic int sio_numunits; 34453541Sshin 34553541Sshinstatic struct speedtab comspeedtab[] = { 34653541Sshin { 0, 0 }, 34753541Sshin { 50, COMBRD(50) }, 34853541Sshin { 75, COMBRD(75) }, 34953541Sshin { 110, COMBRD(110) }, 35053541Sshin { 134, COMBRD(134) }, 35162587Sitojun { 150, COMBRD(150) }, 352111119Simp { 200, COMBRD(200) }, 35362587Sitojun { 300, COMBRD(300) }, 35462587Sitojun { 600, COMBRD(600) }, 35553541Sshin { 1200, COMBRD(1200) }, 35678064Sume { 1800, COMBRD(1800) }, 35753541Sshin { 2400, COMBRD(2400) }, 35853541Sshin { 4800, COMBRD(4800) }, 35953541Sshin { 9600, COMBRD(9600) }, 36053541Sshin { 19200, COMBRD(19200) }, 36153541Sshin { 28800, COMBRD(28800) }, 36253541Sshin { 38400, COMBRD(38400) }, 36353541Sshin { 57600, COMBRD(57600) }, 364121315Sume { 115200, COMBRD(115200) }, 365121315Sume { -1, -1 } 36653541Sshin}; 36753541Sshin 36853541Sshin#ifdef COM_ESP 36953541Sshin/* XXX configure this properly. */ 37053541Sshin/* XXX quite broken for new-bus. */ 37153541Sshinstatic Port_t likely_com_ports[] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, }; 37278064Sumestatic Port_t likely_esp_ports[] = { 0x140, 0x180, 0x280, 0 }; 37378064Sume#endif 374148987Sume 37578064Sume/* 37678064Sume * handle sysctl read/write requests for console speed 37778064Sume * 37878064Sume * In addition to setting comdefaultrate for I/O through /dev/console, 37978064Sume * also set the initial and lock values for the /dev/ttyXX device 38078064Sume * if there is one associated with the console. Finally, if the /dev/tty 38153541Sshin * device has already been open, change the speed on the open running port 38295023Ssuz * itself. 38362587Sitojun */ 38462587Sitojun 38562587Sitojunstatic int 38662587Sitojunsysctl_machdep_comdefaultrate(SYSCTL_HANDLER_ARGS) 38762587Sitojun{ 388148987Sume int error, s; 38962587Sitojun speed_t newspeed; 39062587Sitojun struct com_s *com; 39153541Sshin struct tty *tp; 39253541Sshin 39353541Sshin newspeed = comdefaultrate; 39453541Sshin 39553541Sshin error = sysctl_handle_opaque(oidp, &newspeed, sizeof newspeed, req); 39653541Sshin if (error || !req->newptr) 39753541Sshin return (error); 39853541Sshin 39953541Sshin comdefaultrate = newspeed; 40053541Sshin 40153541Sshin if (comconsole < 0) /* serial console not selected? */ 40253541Sshin return (0); 40353541Sshin 40453541Sshin com = com_addr(comconsole); 40553541Sshin if (com == NULL) 40653541Sshin return (ENXIO); 407165118Sbz 40853541Sshin /* 40962587Sitojun * set the initial and lock rates for /dev/ttydXX and /dev/cuaXX 41053541Sshin * (note, the lock rates really are boolean -- if non-zero, disallow 41195023Ssuz * speed changes) 41262587Sitojun */ 41353541Sshin com->it_in.c_ispeed = com->it_in.c_ospeed = 41453541Sshin com->lt_in.c_ispeed = com->lt_in.c_ospeed = 41553541Sshin com->it_out.c_ispeed = com->it_out.c_ospeed = 41653541Sshin com->lt_out.c_ispeed = com->lt_out.c_ospeed = comdefaultrate; 41753541Sshin 41853541Sshin /* 41953541Sshin * if we're open, change the running rate too 42053541Sshin */ 42153541Sshin tp = com->tp; 42253541Sshin if (tp && (tp->t_state & TS_ISOPEN)) { 42353541Sshin tp->t_termios.c_ispeed = 42453541Sshin tp->t_termios.c_ospeed = comdefaultrate; 42553541Sshin s = spltty(); 42653541Sshin error = comparam(tp, &tp->t_termios); 42753541Sshin splx(s); 42862587Sitojun } 42953541Sshin return error; 43062587Sitojun} 43162587Sitojun 43262587SitojunSYSCTL_PROC(_machdep, OID_AUTO, conspeed, CTLTYPE_INT | CTLFLAG_RW, 43362587Sitojun 0, 0, sysctl_machdep_comdefaultrate, "I", ""); 43462587Sitojun 43562587Sitojun#define SET_FLAG(dev, bit) device_set_flags(dev, device_get_flags(dev) | (bit)) 43662587Sitojun#define CLR_FLAG(dev, bit) device_set_flags(dev, device_get_flags(dev) & ~(bit)) 43753541Sshin 43853541Sshin/* 43953541Sshin * Unload the driver and clear the table. 44078064Sume * XXX this is mostly wrong. 44153541Sshin * XXX TODO: 442165118Sbz * This is usually called when the card is ejected, but 443165118Sbz * can be caused by a modunload of a controller driver. 44453541Sshin * The idea is to reset the driver's view of the device 44553541Sshin * and ensure that any driver entry points such as 44653541Sshin * read and write do not hang. 44753541Sshin */ 44883934Sbrooksint 44953541Sshinsiodetach(dev) 45053541Sshin device_t dev; 451148987Sume{ 45253541Sshin struct com_s *com; 45353541Sshin int i; 45453541Sshin 45553541Sshin com = (struct com_s *) device_get_softc(dev); 45653541Sshin if (com == NULL) { 45753541Sshin device_printf(dev, "NULL com in siounload\n"); 45853541Sshin return (0); 45953541Sshin } 46053541Sshin com->gone = 1; 46153541Sshin for (i = 0 ; i < 6; i++) 46253541Sshin destroy_dev(com->devs[i]); 46353541Sshin if (com->irqres) { 46453541Sshin bus_teardown_intr(dev, com->irqres, com->cookie); 46553541Sshin bus_release_resource(dev, SYS_RES_IRQ, 0, com->irqres); 46653541Sshin } 46753541Sshin if (com->ioportres) 46853541Sshin bus_release_resource(dev, SYS_RES_IOPORT, 0, com->ioportres); 46953541Sshin if (com->tp && (com->tp->t_state & TS_ISOPEN)) { 47053541Sshin device_printf(dev, "still open, forcing close\n"); 47153541Sshin (*linesw[com->tp->t_line].l_close)(com->tp, 0); 47253541Sshin com->tp->t_gen++; 47353541Sshin ttyclose(com->tp); 47453541Sshin ttwakeup(com->tp); 47553541Sshin ttwwakeup(com->tp); 47653541Sshin } else { 47753541Sshin if (com->ibuf != NULL) 47862587Sitojun free(com->ibuf, M_DEVBUF); 47962587Sitojun device_set_softc(dev, NULL); 48053541Sshin free(com, M_DEVBUF); 48162587Sitojun } 48253541Sshin return (0); 48362587Sitojun} 48462587Sitojun 48562587Sitojunint 48662587Sitojunsioprobe(dev, xrid, noprobe) 48753541Sshin device_t dev; 48853541Sshin int xrid; 48953541Sshin int noprobe; 49053541Sshin{ 49153541Sshin#if 0 49253541Sshin static bool_t already_init; 49353541Sshin device_t xdev; 49453541Sshin#endif 49553541Sshin struct com_s *com; 49653541Sshin bool_t failures[10]; 49753541Sshin int fn; 49853541Sshin device_t idev; 499126194Sume Port_t iobase; 500126194Sume intrmask_t irqmap[4]; 50153541Sshin intrmask_t irqs; 50253541Sshin u_char mcr_image; 50362587Sitojun int result; 50462587Sitojun u_long xirq; 50562587Sitojun u_int flags = device_get_flags(dev); 50662587Sitojun int rid; 50753541Sshin struct resource *port; 50853541Sshin 50953541Sshin rid = xrid; 51053541Sshin port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 51153541Sshin 0, ~0, IO_COMSIZE, RF_ACTIVE); 51253541Sshin if (!port) 51353541Sshin return (ENXIO); 514151465Ssuz 515151465Ssuz com = malloc(sizeof(*com), M_DEVBUF, M_NOWAIT | M_ZERO); 51653541Sshin if (com == NULL) 517151465Ssuz return (ENOMEM); 51853541Sshin device_set_softc(dev, com); 51953541Sshin com->bst = rman_get_bustag(port); 52053541Sshin com->bsh = rman_get_bushandle(port); 52153541Sshin 52253541Sshin while (sio_inited != 2) 52353541Sshin if (atomic_cmpset_int(&sio_inited, 0, 1)) { 52453541Sshin mtx_init(&sio_lock, sio_driver_name, (comconsole != -1) ? 52553541Sshin MTX_SPIN | MTX_QUIET : MTX_SPIN); 52653541Sshin atomic_store_rel_int(&sio_inited, 2); 52753541Sshin } 52853541Sshin 52953541Sshin#if 0 53053541Sshin /* 53153541Sshin * XXX this is broken - when we are first called, there are no 53253541Sshin * previously configured IO ports. We could hard code 53353541Sshin * 0x3f8, 0x2f8, 0x3e8, 0x2e8 etc but that's probably worse. 53453541Sshin * This code has been doing nothing since the conversion since 53553541Sshin * "count" is zero the first time around. 53653541Sshin */ 53753541Sshin if (!already_init) { 53853541Sshin /* 53953541Sshin * Turn off MCR_IENABLE for all likely serial ports. An unused 54053541Sshin * port with its MCR_IENABLE gate open will inhibit interrupts 54153541Sshin * from any used port that shares the interrupt vector. 54253541Sshin * XXX the gate enable is elsewhere for some multiports. 54353541Sshin */ 54453541Sshin device_t *devs; 54553541Sshin int count, i, xioport; 54653541Sshin 54753541Sshin devclass_get_devices(sio_devclass, &devs, &count); 54853541Sshin for (i = 0; i < count; i++) { 54962587Sitojun xdev = devs[i]; 55062587Sitojun if (device_is_enabled(xdev) && 55153541Sshin bus_get_resource(xdev, SYS_RES_IOPORT, 0, &xioport, 55262587Sitojun NULL) == 0) 553108741Ssam outb(xioport + com_mcr, 0); 55453541Sshin } 555111119Simp free(devs, M_TEMP); 556111070Ssam already_init = TRUE; 557111070Ssam } 558111070Ssam#endif 55962587Sitojun 560111119Simp if (COM_LLCONSOLE(flags)) { 56162587Sitojun printf("sio%d: reserved for low-level i/o\n", 56262587Sitojun device_get_unit(dev)); 56362587Sitojun bus_release_resource(dev, SYS_RES_IOPORT, rid, port); 56462587Sitojun device_set_softc(dev, NULL); 56562587Sitojun free(com, M_DEVBUF); 56653541Sshin return (ENXIO); 56753541Sshin } 56853541Sshin 56953541Sshin /* 57053541Sshin * If the device is on a multiport card and has an AST/4 57153541Sshin * compatible interrupt control register, initialize this 57253541Sshin * register and prepare to leave MCR_IENABLE clear in the mcr. 57353541Sshin * Otherwise, prepare to set MCR_IENABLE in the mcr. 57453541Sshin * Point idev to the device struct giving the correct id_irq. 57553541Sshin * This is the struct for the master device if there is one. 57653541Sshin */ 57753541Sshin idev = dev; 57862587Sitojun mcr_image = MCR_IENABLE; 579108741Ssam#ifdef COM_MULTIPORT 580108741Ssam if (COM_ISMULTIPORT(flags)) { 58153541Sshin Port_t xiobase; 582120891Sume u_long io; 58362587Sitojun 58453541Sshin idev = devclass_get_device(sio_devclass, COM_MPMASTER(flags)); 58562587Sitojun if (idev == NULL) { 586108741Ssam printf("sio%d: master device %d not configured\n", 587108741Ssam device_get_unit(dev), COM_MPMASTER(flags)); 58862587Sitojun idev = dev; 58953541Sshin } 59053541Sshin if (!COM_NOTAST4(flags)) { 59153541Sshin if (bus_get_resource(idev, SYS_RES_IOPORT, 0, &io, 59253541Sshin NULL) == 0) { 59353541Sshin xiobase = io; 59453541Sshin if (bus_get_resource(idev, SYS_RES_IRQ, 0, 59553541Sshin NULL, NULL) == 0) 59653541Sshin outb(xiobase + com_scr, 0x80); 59753541Sshin else 59853541Sshin outb(xiobase + com_scr, 0); 59953541Sshin } 60053541Sshin mcr_image = 0; 60153541Sshin } 60253541Sshin } 60353541Sshin#endif /* COM_MULTIPORT */ 60453541Sshin if (bus_get_resource(idev, SYS_RES_IRQ, 0, NULL, NULL) != 0) 60553541Sshin mcr_image = 0; 60653541Sshin 60753541Sshin bzero(failures, sizeof failures); 60853541Sshin iobase = rman_get_start(port); 60996116Sume 61096116Sume /* 61196116Sume * We don't want to get actual interrupts, just masked ones. 61253541Sshin * Interrupts from this line should already be masked in the ICU, 61396116Sume * but mask them in the processor as well in case there are some 61453541Sshin * (misconfigured) shared interrupts. 61553541Sshin */ 61653541Sshin mtx_lock_spin(&sio_lock); 617111119Simp/* EXTRA DELAY? */ 61862587Sitojun 61962587Sitojun /* 62062587Sitojun * Initialize the speed and the word size and wait long enough to 62162587Sitojun * drain the maximum of 16 bytes of junk in device output queues. 62262587Sitojun * The speed is undefined after a master reset and must be set 62362587Sitojun * before relying on anything related to output. There may be 62453541Sshin * junk after a (very fast) soft reboot and (apparently) after 62553541Sshin * master reset. 62653541Sshin * XXX what about the UART bug avoided by waiting in comparam()? 62796116Sume * We don't want to to wait long enough to drain at 2 bps. 62853541Sshin */ 62996116Sume if (iobase == siocniobase) 63053541Sshin DELAY((16 + 1) * 1000000 / (comdefaultrate / 10)); 63153541Sshin else { 63253541Sshin sio_setreg(com, com_cfcr, CFCR_DLAB | CFCR_8BITS); 63396116Sume sio_setreg(com, com_dlbl, COMBRD(SIO_TEST_SPEED) & 0xff); 63496116Sume sio_setreg(com, com_dlbh, (u_int) COMBRD(SIO_TEST_SPEED) >> 8); 635120891Sume sio_setreg(com, com_cfcr, CFCR_8BITS); 63653541Sshin DELAY((16 + 1) * 1000000 / (SIO_TEST_SPEED / 10)); 63762587Sitojun } 63853541Sshin 63953541Sshin /* 64053541Sshin * Enable the interrupt gate and disable device interupts. This 64153541Sshin * should leave the device driving the interrupt line low and 64253541Sshin * guarantee an edge trigger if an interrupt can be generated. 64362587Sitojun */ 64462587Sitojun/* EXTRA DELAY? */ 64562587Sitojun sio_setreg(com, com_mcr, mcr_image); 64653541Sshin sio_setreg(com, com_ier, 0); 64753541Sshin DELAY(1000); /* XXX */ 64862587Sitojun irqmap[0] = isa_irq_pending(); 64953541Sshin 65053541Sshin /* 65153541Sshin * Attempt to set loopback mode so that we can send a null byte 65253541Sshin * without annoying any external device. 65353541Sshin */ 65453541Sshin/* EXTRA DELAY? */ 65562587Sitojun sio_setreg(com, com_mcr, mcr_image | MCR_LOOPBACK); 65653541Sshin 657120891Sume /* 65862587Sitojun * Attempt to generate an output interrupt. On 8250's, setting 65962587Sitojun * IER_ETXRDY generates an interrupt independent of the current 66062587Sitojun * setting and independent of whether the THR is empty. On 16450's, 66162587Sitojun * setting IER_ETXRDY generates an interrupt independent of the 66262587Sitojun * current setting. On 16550A's, setting IER_ETXRDY only 66353541Sshin * generates an interrupt when IER_ETXRDY is not already set. 66453541Sshin */ 66553541Sshin sio_setreg(com, com_ier, IER_ETXRDY); 66662587Sitojun 66753541Sshin /* 668120891Sume * On some 16x50 incompatibles, setting IER_ETXRDY doesn't generate 66978064Sume * an interrupt. They'd better generate one for actually doing 67078064Sume * output. Loopback may be broken on the same incompatibles but 67162587Sitojun * it's unlikely to do more than allow the null byte out. 67262587Sitojun */ 67362587Sitojun sio_setreg(com, com_data, 0); 67462587Sitojun DELAY((1 + 2) * 1000000 / (SIO_TEST_SPEED / 10)); 67562587Sitojun 67662587Sitojun /* 67762587Sitojun * Turn off loopback mode so that the interrupt gate works again 678111119Simp * (MCR_IENABLE was hidden). This should leave the device driving 67962587Sitojun * an interrupt line high. It doesn't matter if the interrupt 680111119Simp * line oscillates while we are not looking at it, since interrupts 68162587Sitojun * are disabled. 68262587Sitojun */ 68362587Sitojun/* EXTRA DELAY? */ 68462587Sitojun sio_setreg(com, com_mcr, mcr_image); 68562587Sitojun 686111119Simp /* 687108466Ssam * Some pcmcia cards have the "TXRDY bug", so we check everyone 688108466Ssam * for IIR_TXRDY implementation ( Palido 321s, DC-1S... ) 689108466Ssam */ 690108466Ssam if (noprobe) { 691108466Ssam /* Reading IIR register twice */ 692108466Ssam for (fn = 0; fn < 2; fn ++) { 693108466Ssam DELAY(10000); 694108466Ssam failures[6] = sio_getreg(com, com_iir); 695108466Ssam } 696108466Ssam /* Check IIR_TXRDY clear ? */ 69753541Sshin result = 0; 69853541Sshin if (failures[6] & IIR_TXRDY) { 69953541Sshin /* Nop, Double check with clearing IER */ 70053541Sshin sio_setreg(com, com_ier, 0); 70178064Sume if (sio_getreg(com, com_iir) & IIR_NOPEND) { 70262587Sitojun /* Ok. we're familia this gang */ 70362587Sitojun SET_FLAG(dev, COM_C_IIR_TXRDYBUG); 70462587Sitojun } else { 70562587Sitojun /* Unknown, Just omit this chip.. XXX */ 70653541Sshin result = ENXIO; 70753541Sshin sio_setreg(com, com_mcr, 0); 70853541Sshin } 70953541Sshin } else { 71053541Sshin /* OK. this is well-known guys */ 71153541Sshin CLR_FLAG(dev, COM_C_IIR_TXRDYBUG); 71253541Sshin } 71353541Sshin sio_setreg(com, com_ier, 0); 71453541Sshin sio_setreg(com, com_cfcr, CFCR_8BITS); 71595023Ssuz mtx_unlock_spin(&sio_lock); 71653541Sshin bus_release_resource(dev, SYS_RES_IOPORT, rid, port); 71753541Sshin if (iobase == siocniobase) 71862587Sitojun result = 0; 71953541Sshin if (result != 0) { 72053541Sshin device_set_softc(dev, NULL); 72153541Sshin free(com, M_DEVBUF); 72253541Sshin } 72353541Sshin return (result); 72453541Sshin } 72553541Sshin 72653541Sshin /* 72753541Sshin * Check that 72853541Sshin * o the CFCR, IER and MCR in UART hold the values written to them 72953541Sshin * (the values happen to be all distinct - this is good for 73053541Sshin * avoiding false positive tests from bus echoes). 73153541Sshin * o an output interrupt is generated and its vector is correct. 73253541Sshin * o the interrupt goes away when the IIR in the UART is read. 73353541Sshin */ 73453541Sshin/* EXTRA DELAY? */ 73553541Sshin failures[0] = sio_getreg(com, com_cfcr) - CFCR_8BITS; 73653541Sshin failures[1] = sio_getreg(com, com_ier) - IER_ETXRDY; 73753541Sshin failures[2] = sio_getreg(com, com_mcr) - mcr_image; 73853541Sshin DELAY(10000); /* Some internal modems need this time */ 73953541Sshin irqmap[1] = isa_irq_pending(); 74053541Sshin failures[4] = (sio_getreg(com, com_iir) & IIR_IMASK) - IIR_TXRDY; 74153541Sshin DELAY(1000); /* XXX */ 742111119Simp irqmap[2] = isa_irq_pending(); 74362587Sitojun failures[6] = (sio_getreg(com, com_iir) & IIR_IMASK) - IIR_NOPEND; 74462587Sitojun 74562587Sitojun /* 74662587Sitojun * Turn off all device interrupts and check that they go off properly. 74762587Sitojun * Leave MCR_IENABLE alone. For ports without a master port, it gates 74862587Sitojun * the OUT2 output of the UART to 74953541Sshin * the ICU input. Closing the gate would give a floating ICU input 75053541Sshin * (unless there is another device driving it) and spurious interrupts. 75153541Sshin * (On the system that this was first tested on, the input floats high 75253541Sshin * and gives a (masked) interrupt as soon as the gate is closed.) 75353541Sshin */ 75453541Sshin sio_setreg(com, com_ier, 0); 75553541Sshin sio_setreg(com, com_cfcr, CFCR_8BITS); /* dummy to avoid bus echo */ 75653541Sshin failures[7] = sio_getreg(com, com_ier); 75753541Sshin DELAY(1000); /* XXX */ 758111119Simp irqmap[3] = isa_irq_pending(); 75962587Sitojun failures[9] = (sio_getreg(com, com_iir) & IIR_IMASK) - IIR_NOPEND; 76062587Sitojun 76162587Sitojun mtx_unlock_spin(&sio_lock); 76262587Sitojun 76362587Sitojun irqs = irqmap[1] & ~irqmap[0]; 76462587Sitojun if (bus_get_resource(idev, SYS_RES_IRQ, 0, &xirq, NULL) == 0 && 76553541Sshin ((1 << xirq) & irqs) == 0) 76653541Sshin printf( 76753541Sshin "sio%d: configured irq %ld not in bitmap of probed irqs %#x\n", 76853541Sshin device_get_unit(dev), xirq, irqs); 76953541Sshin printf( 77053541Sshin "sio%d: port may not be enabled in BIOS\n", 77153541Sshin device_get_unit(dev)); 77253541Sshin if (bootverbose) 77353541Sshin printf("sio%d: irq maps: %#x %#x %#x %#x\n", 774111119Simp device_get_unit(dev), 77562587Sitojun irqmap[0], irqmap[1], irqmap[2], irqmap[3]); 77662587Sitojun 77762587Sitojun result = 0; 77862587Sitojun for (fn = 0; fn < sizeof failures; ++fn) 77962587Sitojun if (failures[fn]) { 78062587Sitojun sio_setreg(com, com_mcr, 0); 78153541Sshin result = ENXIO; 78253541Sshin if (bootverbose) { 78353541Sshin printf("sio%d: probe failed test(s):", 78453541Sshin device_get_unit(dev)); 78553541Sshin for (fn = 0; fn < sizeof failures; ++fn) 78653541Sshin if (failures[fn]) 78753541Sshin printf(" %d", fn); 78853541Sshin printf("\n"); 78953541Sshin } 790111119Simp break; 79162587Sitojun } 79262587Sitojun bus_release_resource(dev, SYS_RES_IOPORT, rid, port); 79362587Sitojun if (iobase == siocniobase) 79462587Sitojun result = 0; 79562587Sitojun if (result != 0) { 79662587Sitojun device_set_softc(dev, NULL); 79753541Sshin free(com, M_DEVBUF); 79853541Sshin } 79953541Sshin return (result); 80053541Sshin} 80153541Sshin 80253541Sshin#ifdef COM_ESP 80353541Sshinstatic int 80453541Sshinespattach(com, esp_port) 80553541Sshin struct com_s *com; 806111119Simp Port_t esp_port; 80762587Sitojun{ 80862587Sitojun u_char dips; 80962587Sitojun u_char val; 81062587Sitojun 81162587Sitojun /* 81262587Sitojun * Check the ESP-specific I/O port to see if we're an ESP 81353541Sshin * card. If not, return failure immediately. 81453541Sshin */ 81553541Sshin if ((inb(esp_port) & 0xf3) == 0) { 81653541Sshin printf(" port 0x%x is not an ESP board?\n", esp_port); 81753541Sshin return (0); 81853541Sshin } 81953541Sshin 82053541Sshin /* 82153541Sshin * We've got something that claims to be a Hayes ESP card. 82253541Sshin * Let's hope so. 82353541Sshin */ 82453541Sshin 82578064Sume /* Get the dip-switch configuration */ 82678064Sume outb(esp_port + ESP_CMD1, ESP_GETDIPS); 827165118Sbz dips = inb(esp_port + ESP_STATUS1); 828165118Sbz 82978064Sume /* 83053541Sshin * Bits 0,1 of dips say which COM port we are. 83153541Sshin */ 83253541Sshin if (rman_get_start(com->ioportres) == likely_com_ports[dips & 0x03]) 83353541Sshin printf(" : ESP"); 83453541Sshin else { 83553541Sshin printf(" esp_port has com %d\n", dips & 0x03); 83653541Sshin return (0); 83753541Sshin } 83853541Sshin 839142681Sume /* 84078064Sume * Check for ESP version 2.0 or later: bits 4,5,6 = 010. 841120856Sume */ 84253541Sshin outb(esp_port + ESP_CMD1, ESP_GETTEST); 84378064Sume val = inb(esp_port + ESP_STATUS1); /* clear reg 1 */ 84478064Sume val = inb(esp_port + ESP_STATUS2); 84578064Sume if ((val & 0x70) < 0x20) { 84678064Sume printf("-old (%o)", val & 0x70); 84778064Sume return (0); 84878064Sume } 84978064Sume 85078064Sume /* 85178064Sume * Check for ability to emulate 16550: bit 7 == 1 85278064Sume */ 85378064Sume if ((dips & 0x80) == 0) { 85478064Sume printf(" slave"); 85578064Sume return (0); 85678064Sume } 85778064Sume 85878064Sume /* 85978064Sume * Okay, we seem to be a Hayes ESP card. Whee. 86078064Sume */ 86178064Sume com->esp = TRUE; 86278064Sume com->esp_port = esp_port; 86378064Sume return (1); 86478064Sume} 865142681Sume#endif /* COM_ESP */ 866142681Sume 867113799Sobrienint 86878064Sumesioattach(dev, xrid) 869142681Sume device_t dev; 87078064Sume int xrid; 87178064Sume{ 87278064Sume struct com_s *com; 87378064Sume#ifdef COM_ESP 87478064Sume Port_t *espp; 87578064Sume#endif 87678064Sume Port_t iobase; 87778064Sume int unit; 87878064Sume u_int flags; 87962587Sitojun int rid; 88078064Sume struct resource *port; 881120891Sume int ret; 88278064Sume 88362587Sitojun rid = xrid; 88478064Sume port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 885120891Sume 0, ~0, IO_COMSIZE, RF_ACTIVE); 88678064Sume if (!port) 88778064Sume return (ENXIO); 888120856Sume 88978064Sume iobase = rman_get_start(port); 89062587Sitojun unit = device_get_unit(dev); 89178064Sume com = device_get_softc(dev); 89253541Sshin flags = device_get_flags(dev); 89378064Sume 89478064Sume if (unit >= sio_numunits) 89553541Sshin sio_numunits = unit + 1; 89653541Sshin /* 89753541Sshin * sioprobe() has initialized the device registers as follows: 898120891Sume * o cfcr = CFCR_8BITS. 89953541Sshin * It is most important that CFCR_DLAB is off, so that the 90062587Sitojun * data port is not hidden when we enable interrupts. 90162587Sitojun * o ier = 0. 90262587Sitojun * Interrupts are only enabled when the line is open. 90362587Sitojun * o mcr = MCR_IENABLE, or 0 if the port has AST/4 compatible 90462587Sitojun * interrupt control register or the config specifies no irq. 90562587Sitojun * Keeping MCR_DTR and MCR_RTS off might stop the external 90653541Sshin * device from sending before we are ready. 90795023Ssuz */ 90853541Sshin bzero(com, sizeof *com); 90953541Sshin com->unit = unit; 91078064Sume com->ioportres = port; 91153541Sshin com->bst = rman_get_bustag(port); 91253541Sshin com->bsh = rman_get_bushandle(port); 91353541Sshin com->cfcr_image = CFCR_8BITS; 91462587Sitojun com->dtr_wait = 3 * hz; 915120891Sume com->loses_outints = COM_LOSESOUTINTS(flags) != 0; 916120891Sume com->no_irq = bus_get_resource(dev, SYS_RES_IRQ, 0, NULL, NULL) != 0; 917120892Sume com->tx_fifo_size = 1; 91862587Sitojun com->obufs[0].l_head = com->obuf1; 91962587Sitojun com->obufs[1].l_head = com->obuf2; 920120891Sume 92162587Sitojun com->data_port = iobase + com_data; 92262587Sitojun com->int_id_port = iobase + com_iir; 923120856Sume com->modem_ctl_port = iobase + com_mcr; 92462587Sitojun com->mcr_image = inb(com->modem_ctl_port); 92562587Sitojun com->line_status_port = iobase + com_lsr; 926120891Sume com->modem_status_port = iobase + com_msr; 92753541Sshin com->intr_ctl_port = iobase + com_ier; 92853541Sshin 92953541Sshin /* 93053541Sshin * We don't use all the flags from <sys/ttydefaults.h> since they 93153541Sshin * are only relevant for logins. It's important to have echo off 93253541Sshin * initially so that the line doesn't start blathering before the 93362587Sitojun * echo flag can be turned off. 93462587Sitojun */ 93562587Sitojun com->it_in.c_iflag = 0; 93662587Sitojun com->it_in.c_oflag = 0; 93762587Sitojun com->it_in.c_cflag = TTYDEF_CFLAG; 93862587Sitojun com->it_in.c_lflag = 0; 93962587Sitojun if (unit == comconsole) { 94062587Sitojun com->it_in.c_iflag = TTYDEF_IFLAG; 94162587Sitojun com->it_in.c_oflag = TTYDEF_OFLAG; 94262587Sitojun com->it_in.c_cflag = TTYDEF_CFLAG | CLOCAL; 943120891Sume com->it_in.c_lflag = TTYDEF_LFLAG; 944120891Sume com->lt_out.c_cflag = com->lt_in.c_cflag = CLOCAL; 945120891Sume com->lt_out.c_ispeed = com->lt_out.c_ospeed = 94662587Sitojun com->lt_in.c_ispeed = com->lt_in.c_ospeed = 94762587Sitojun com->it_in.c_ispeed = com->it_in.c_ospeed = comdefaultrate; 948120891Sume } else 94962587Sitojun com->it_in.c_ispeed = com->it_in.c_ospeed = TTYDEF_SPEED; 95062587Sitojun if (siosetwater(com, com->it_in.c_ispeed) != 0) { 951120856Sume mtx_unlock_spin(&sio_lock); 95262587Sitojun /* 95362587Sitojun * Leave i/o resources allocated if this is a `cn'-level 95462587Sitojun * console, so that other devices can't snarf them. 95562587Sitojun */ 95662587Sitojun if (iobase != siocniobase) 95762587Sitojun bus_release_resource(dev, SYS_RES_IOPORT, rid, port); 95862587Sitojun return (ENOMEM); 95962587Sitojun } 96062587Sitojun mtx_unlock_spin(&sio_lock); 96162587Sitojun termioschars(&com->it_in); 96262587Sitojun com->it_out = com->it_in; 96362587Sitojun 96462587Sitojun /* attempt to determine UART type */ 96562587Sitojun printf("sio%d: type", unit); 96662587Sitojun 96762587Sitojun 968120891Sume#ifdef COM_MULTIPORT 969120891Sume if (!COM_ISMULTIPORT(flags) && !COM_IIR_TXRDYBUG(flags)) 970120891Sume#else 97162587Sitojun if (!COM_IIR_TXRDYBUG(flags)) 97262587Sitojun#endif 973120891Sume { 974120891Sume u_char scr; 97562587Sitojun u_char scr1; 97662587Sitojun u_char scr2; 977120856Sume 97862587Sitojun scr = sio_getreg(com, com_scr); 97962587Sitojun sio_setreg(com, com_scr, 0xa5); 98062587Sitojun scr1 = sio_getreg(com, com_scr); 98162587Sitojun sio_setreg(com, com_scr, 0x5a); 98262587Sitojun scr2 = sio_getreg(com, com_scr); 98362587Sitojun sio_setreg(com, com_scr, scr); 98462587Sitojun if (scr1 != 0xa5 || scr2 != 0x5a) { 98562587Sitojun printf(" 8250 or not responding"); 98662587Sitojun goto determined_type; 98762587Sitojun } 98862587Sitojun } 98962587Sitojun sio_setreg(com, com_fifo, FIFO_ENABLE | FIFO_RX_HIGH); 99062587Sitojun DELAY(100); 991120891Sume com->st16650a = 0; 992120891Sume switch (inb(com->int_id_port) & IIR_FIFO_MASK) { 993120891Sume case FIFO_RX_LOW: 99462587Sitojun printf(" 16450"); 99562587Sitojun break; 996120891Sume case FIFO_RX_MEDL: 99762587Sitojun printf(" 16450?"); 99862587Sitojun break; 999120856Sume case FIFO_RX_MEDH: 100062587Sitojun printf(" 16550?"); 100162587Sitojun break; 100262587Sitojun case FIFO_RX_HIGH: 100362587Sitojun if (COM_NOFIFO(flags)) { 100462587Sitojun printf(" 16550A fifo disabled"); 100562587Sitojun } else { 100662587Sitojun com->hasfifo = TRUE; 100762587Sitojun if (COM_ST16650A(flags)) { 100862587Sitojun com->st16650a = 1; 100962587Sitojun com->tx_fifo_size = 32; 101062587Sitojun printf(" ST16650A"); 101162587Sitojun } else { 101262587Sitojun com->tx_fifo_size = COM_FIFOSIZE(flags); 101362587Sitojun printf(" 16550A"); 101453541Sshin } 101562587Sitojun } 101662587Sitojun#ifdef COM_ESP 101795023Ssuz for (espp = likely_esp_ports; *espp != 0; espp++) 101862587Sitojun if (espattach(com, *espp)) { 101962587Sitojun com->tx_fifo_size = 1024; 102062587Sitojun break; 102162587Sitojun } 102262587Sitojun#endif 102353541Sshin if (!com->st16650a) { 102453541Sshin if (!com->tx_fifo_size) 102553541Sshin com->tx_fifo_size = 16; 102678064Sume else 102762587Sitojun printf(" lookalike with %d bytes FIFO", 102853541Sshin com->tx_fifo_size); 102962587Sitojun } 103062587Sitojun 1031120891Sume break; 103262587Sitojun } 103362587Sitojun 1034120856Sume#ifdef COM_ESP 103562587Sitojun if (com->esp) { 103662587Sitojun /* 103778064Sume * Set 16550 compatibility mode. 1038120891Sume * We don't use the ESP_MODE_SCALE bit to increase the 1039120891Sume * fifo trigger levels because we can't handle large 1040120891Sume * bursts of input. 1041120891Sume * XXX flow control should be set in comparam(), not here. 1042120891Sume */ 1043120891Sume outb(com->esp_port + ESP_CMD1, ESP_SETMODE); 1044120891Sume outb(com->esp_port + ESP_CMD2, ESP_MODE_RTS | ESP_MODE_FIFO); 1045120891Sume 104678064Sume /* Set RTS/CTS flow control. */ 1047120891Sume outb(com->esp_port + ESP_CMD1, ESP_SETFLOWTYPE); 104878064Sume outb(com->esp_port + ESP_CMD2, ESP_FLOW_RTS); 104978064Sume outb(com->esp_port + ESP_CMD2, ESP_FLOW_CTS); 105078064Sume 105178064Sume /* Set flow-control levels. */ 105278064Sume outb(com->esp_port + ESP_CMD1, ESP_SETRXFLOW); 105378064Sume outb(com->esp_port + ESP_CMD2, HIBYTE(768)); 105478064Sume outb(com->esp_port + ESP_CMD2, LOBYTE(768)); 1055148385Sume outb(com->esp_port + ESP_CMD2, HIBYTE(512)); 1056121315Sume outb(com->esp_port + ESP_CMD2, LOBYTE(512)); 105778064Sume } 105878064Sume#endif /* COM_ESP */ 105978064Sume sio_setreg(com, com_fifo, 0); 106078064Sumedetermined_type: ; 1061148385Sume 1062121315Sume#ifdef COM_MULTIPORT 1063148385Sume if (COM_ISMULTIPORT(flags)) { 1064148385Sume device_t masterdev; 106578064Sume 106678064Sume com->multiport = TRUE; 106778064Sume printf(" (multiport"); 106878064Sume if (unit == COM_MPMASTER(flags)) 106978064Sume printf(" master"); 107078064Sume printf(")"); 107178064Sume masterdev = devclass_get_device(sio_devclass, 107278064Sume COM_MPMASTER(flags)); 107378064Sume com->no_irq = (masterdev == NULL || bus_get_resource(masterdev, 107478064Sume SYS_RES_IRQ, 0, NULL, NULL) != 0); 107578064Sume } 107662587Sitojun#endif /* COM_MULTIPORT */ 107778064Sume if (unit == comconsole) 107878064Sume printf(", console"); 107978064Sume if (COM_IIR_TXRDYBUG(flags)) 108062587Sitojun printf(" with a bogus IIR_TXRDY register"); 108162587Sitojun printf("\n"); 108253541Sshin 1083120891Sume if (sio_fast_ih == NULL) { 108453541Sshin swi_add(&tty_ithd, "tty:sio", siopoll, NULL, SWI_TTY, 0, 108578064Sume &sio_fast_ih); 1086120891Sume swi_add(&clk_ithd, "tty:sio", siopoll, NULL, SWI_TTY, 0, 108753541Sshin &sio_slow_ih); 108853541Sshin } 1089142681Sume com->devs[0] = make_dev(&sio_cdevsw, unit, 1090120856Sume UID_ROOT, GID_WHEEL, 0600, "ttyd%r", unit); 109153541Sshin com->devs[1] = make_dev(&sio_cdevsw, unit | CONTROL_INIT_STATE, 109278064Sume UID_ROOT, GID_WHEEL, 0600, "ttyid%r", unit); 109353541Sshin com->devs[2] = make_dev(&sio_cdevsw, unit | CONTROL_LOCK_STATE, 1094120856Sume UID_ROOT, GID_WHEEL, 0600, "ttyld%r", unit); 109553541Sshin com->devs[3] = make_dev(&sio_cdevsw, unit | CALLOUT_MASK, 109653541Sshin UID_UUCP, GID_DIALER, 0660, "cuaa%r", unit); 109778064Sume com->devs[4] = make_dev(&sio_cdevsw, 109878064Sume unit | CALLOUT_MASK | CONTROL_INIT_STATE, 109978064Sume UID_UUCP, GID_DIALER, 0660, "cuaia%r", unit); 110078064Sume com->devs[5] = make_dev(&sio_cdevsw, 110162587Sitojun unit | CALLOUT_MASK | CONTROL_LOCK_STATE, 110278064Sume UID_UUCP, GID_DIALER, 0660, "cuala%r", unit); 110378064Sume com->flags = flags; 110478064Sume com->pps.ppscap = PPS_CAPTUREASSERT | PPS_CAPTURECLEAR; 110562587Sitojun pps_init(&com->pps); 1106122922Sandre 110762587Sitojun rid = 0; 1108121472Sume com->irqres = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0ul, ~0ul, 1, 1109121472Sume RF_ACTIVE); 1110121472Sume if (com->irqres) { 1111121472Sume ret = BUS_SETUP_INTR(device_get_parent(dev), dev, com->irqres, 1112121472Sume INTR_TYPE_TTY | INTR_FAST, 1113121472Sume siointr, com, &com->cookie); 1114121472Sume if (ret) { 1115121472Sume ret = BUS_SETUP_INTR(device_get_parent(dev), dev, 1116121472Sume com->irqres, INTR_TYPE_TTY, 1117121472Sume siointr, com, &com->cookie); 1118121472Sume if (ret == 0) 1119121472Sume device_printf(dev, "unable to activate interrupt in fast mode - using normal mode\n"); 1120121472Sume } 1121121472Sume if (ret) 1122121472Sume device_printf(dev, "could not activate interrupt\n"); 1123121472Sume#if defined(DDB) && (defined(BREAK_TO_DEBUGGER) || \ 1124121472Sume defined(ALT_BREAK_TO_DEBUGGER)) 1125121472Sume /* 1126121472Sume * Enable interrupts for early break-to-debugger support 1127121472Sume * on the console. 112878064Sume */ 112978064Sume if (ret == 0 && unit == comconsole) 113078064Sume outb(siocniobase + com_ier, IER_ERXRDY | IER_ERLS | 1131122922Sandre IER_EMSC); 1132122922Sandre#endif 1133122922Sandre } 1134148385Sume 1135148385Sume return (0); 113662587Sitojun} 1137162084Sandre 1138122922Sandrestatic int 1139122922Sandresioopen(dev, flag, mode, td) 114062587Sitojun dev_t dev; 114162587Sitojun int flag; 114262587Sitojun int mode; 114353541Sshin struct thread *td; 114478064Sume{ 114578064Sume struct com_s *com; 1146120891Sume int error; 114762587Sitojun int mynor; 114862587Sitojun int s; 114962587Sitojun struct tty *tp; 115062587Sitojun int unit; 115162587Sitojun 115262587Sitojun mynor = minor(dev); 115353541Sshin unit = MINOR_TO_UNIT(mynor); 115462587Sitojun com = com_addr(unit); 115553541Sshin if (com == NULL) 115653541Sshin return (ENXIO); 115753541Sshin if (com->gone) 115853541Sshin return (ENXIO); 115953541Sshin if (mynor & CONTROL_MASK) 116062587Sitojun return (0); 116153541Sshin tp = dev->si_tty = com->tp = ttymalloc(com->tp); 116262587Sitojun s = spltty(); 116362587Sitojun /* 116453541Sshin * We jump to this label after all non-interrupted sleeps to pick 116553541Sshin * up any changes of the device state. 116653541Sshin */ 116753541Sshinopen_top: 1168148385Sume while (com->state & CS_DTR_OFF) { 116962587Sitojun error = tsleep(&com->dtr_wait, TTIPRI | PCATCH, "siodtr", 0); 117062587Sitojun if (com_addr(unit) == NULL) 117178064Sume return (ENXIO); 117278064Sume if (error != 0 || com->gone) 117353541Sshin goto out; 117462587Sitojun } 117562587Sitojun if (tp->t_state & TS_ISOPEN) { 117662587Sitojun /* 117762587Sitojun * The device is open, so everything has been initialized. 117862587Sitojun * Handle conflicts. 117962587Sitojun */ 118062587Sitojun if (mynor & CALLOUT_MASK) { 1181120891Sume if (!com->active_out) { 118262587Sitojun error = EBUSY; 118362587Sitojun goto out; 118453541Sshin } 118562587Sitojun } else { 118662587Sitojun if (com->active_out) { 118762587Sitojun if (flag & O_NONBLOCK) { 118878064Sume error = EBUSY; 118978064Sume goto out; 119078064Sume } 1191121630Sume error = tsleep(&com->active_out, 119262587Sitojun TTIPRI | PCATCH, "siobi", 0); 1193121630Sume if (com_addr(unit) == NULL) 1194121630Sume return (ENXIO); 1195121630Sume if (error != 0 || com->gone) 1196121630Sume goto out; 1197121630Sume goto open_top; 1198121630Sume } 1199121630Sume } 1200121630Sume if (tp->t_state & TS_XCLUDE && 1201121630Sume suser_td(td)) { 1202121630Sume error = EBUSY; 120378064Sume goto out; 120478064Sume } 120578064Sume } else { 120678064Sume /* 120778064Sume * The device isn't open, so there are no conflicts. 1208121630Sume * Initialize it. Initialization is done twice in many 120962587Sitojun * cases: to preempt sleeping callin opens if we are 121078064Sume * callout, and to complete a callin open after DCD rises. 121162587Sitojun */ 121262587Sitojun tp->t_oproc = comstart; 121362587Sitojun tp->t_param = comparam; 121462587Sitojun tp->t_stop = comstop; 121562587Sitojun tp->t_dev = dev; 121678064Sume tp->t_termios = mynor & CALLOUT_MASK 121778064Sume ? com->it_out : com->it_in; 121878064Sume (void)commctl(com, TIOCM_DTR | TIOCM_RTS, DMSET); 121978064Sume com->poll = com->no_irq; 122062587Sitojun com->poll_output = com->loses_outints; 122162587Sitojun ++com->wopeners; 1222151475Ssuz error = comparam(tp, &tp->t_termios); 122362587Sitojun --com->wopeners; 122462587Sitojun if (error != 0) 122562587Sitojun goto out; 122662587Sitojun /* 122762587Sitojun * XXX we should goto open_top if comparam() slept. 122862587Sitojun */ 122962587Sitojun if (com->hasfifo) { 123062587Sitojun /* 123162587Sitojun * (Re)enable and drain fifos. 123278064Sume * 123378064Sume * Certain SMC chips cause problems if the fifos 123462587Sitojun * are enabled while input is ready. Turn off the 123562587Sitojun * fifo if necessary to clear the input. We test 123662587Sitojun * the input ready bit after enabling the fifos 123778064Sume * since we've already enabled them in comparam() 123878064Sume * and to handle races between enabling and fresh 123978064Sume * input. 124078064Sume */ 124162587Sitojun while (TRUE) { 1242121630Sume sio_setreg(com, com_fifo, 124362587Sitojun FIFO_RCV_RST | FIFO_XMT_RST 124462587Sitojun | com->fifo_image); 124562587Sitojun /* 124662587Sitojun * XXX the delays are for superstitious 124762587Sitojun * historical reasons. It must be less than 124878064Sume * the character time at the maximum 124978064Sume * supported speed (87 usec at 115200 bps 125062587Sitojun * 8N1). Otherwise we might loop endlessly 125162587Sitojun * if data is streaming in. We used to use 125262587Sitojun * delays of 100. That usually worked 125362587Sitojun * because DELAY(100) used to usually delay 125462587Sitojun * for about 85 usec instead of 100. 125562587Sitojun */ 125662587Sitojun DELAY(50); 125762587Sitojun if (!(inb(com->line_status_port) & LSR_RXRDY)) 125862587Sitojun break; 125962587Sitojun sio_setreg(com, com_fifo, 0); 126062587Sitojun DELAY(50); 1261148385Sume (void) inb(com->data_port); 1262148385Sume } 1263121315Sume } 1264121630Sume 1265148385Sume mtx_lock_spin(&sio_lock); 1266148385Sume (void) inb(com->line_status_port); 126762587Sitojun (void) inb(com->data_port); 126878064Sume com->prev_modem_status = com->last_modem_status 126962587Sitojun = inb(com->modem_status_port); 127062587Sitojun if (COM_IIR_TXRDYBUG(com->flags)) { 127162587Sitojun outb(com->intr_ctl_port, IER_ERXRDY | IER_ERLS 127262587Sitojun | IER_EMSC); 1273120891Sume } else { 1274120891Sume outb(com->intr_ctl_port, IER_ERXRDY | IER_ETXRDY 127562587Sitojun | IER_ERLS | IER_EMSC); 127662587Sitojun } 127762587Sitojun mtx_unlock_spin(&sio_lock); 127862587Sitojun /* 127962587Sitojun * Handle initial DCD. Callout devices get a fake initial 128062587Sitojun * DCD (trapdoor DCD). If we are callout, then any sleeping 128162587Sitojun * callin opens get woken up and resume sleeping on "siobi" 128262587Sitojun * instead of "siodcd". 128362587Sitojun */ 128462587Sitojun /* 128562587Sitojun * XXX `mynor & CALLOUT_MASK' should be 128662587Sitojun * `tp->t_cflag & (SOFT_CARRIER | TRAPDOOR_CARRIER) where 128762587Sitojun * TRAPDOOR_CARRIER is the default initial state for callout 128862587Sitojun * devices and SOFT_CARRIER is like CLOCAL except it hides 128962587Sitojun * the true carrier. 129062587Sitojun */ 129162587Sitojun if (com->prev_modem_status & MSR_DCD || mynor & CALLOUT_MASK) 129262587Sitojun (*linesw[tp->t_line].l_modem)(tp, 1); 129362587Sitojun } 129462587Sitojun /* 129562587Sitojun * Wait for DCD if necessary. 129662587Sitojun */ 129762587Sitojun if (!(tp->t_state & TS_CARR_ON) && !(mynor & CALLOUT_MASK) 129862587Sitojun && !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK)) { 129962587Sitojun ++com->wopeners; 1300120891Sume error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "siodcd", 0); 130162587Sitojun if (com_addr(unit) == NULL) 130262587Sitojun return (ENXIO); 130362587Sitojun --com->wopeners; 130462587Sitojun if (error != 0 || com->gone) 130562587Sitojun goto out; 130662587Sitojun goto open_top; 130778064Sume } 130862587Sitojun error = (*linesw[tp->t_line].l_open)(dev, tp); 130962587Sitojun disc_optim(tp, &tp->t_termios, com); 131062587Sitojun if (tp->t_state & TS_ISOPEN && mynor & CALLOUT_MASK) 131162587Sitojun com->active_out = TRUE; 131278064Sume siosettimeout(); 131362587Sitojunout: 131478064Sume splx(s); 131578064Sume if (!(tp->t_state & TS_ISOPEN) && com->wopeners == 0) 131678064Sume comhardclose(com); 131778064Sume return (error); 131878064Sume} 131978064Sume 132078064Sumestatic int 1321151475Ssuzsioclose(dev, flag, mode, td) 132278064Sume dev_t dev; 132378064Sume int flag; 132478064Sume int mode; 132578064Sume struct thread *td; 132678064Sume{ 132778064Sume struct com_s *com; 132878064Sume int mynor; 132978064Sume int s; 133078064Sume struct tty *tp; 133178064Sume 133278064Sume mynor = minor(dev); 133378064Sume if (mynor & CONTROL_MASK) 133478064Sume return (0); 133578064Sume com = com_addr(MINOR_TO_UNIT(mynor)); 133678064Sume if (com == NULL) 133778064Sume return (ENODEV); 133878064Sume tp = com->tp; 1339148892Sume s = spltty(); 134078064Sume (*linesw[tp->t_line].l_close)(tp, flag); 1341120891Sume disc_optim(tp, &tp->t_termios, com); 134278064Sume comstop(tp, FREAD | FWRITE); 134378064Sume comhardclose(com); 1344151475Ssuz ttyclose(tp); 1345151475Ssuz siosettimeout(); 1346151475Ssuz splx(s); 134762587Sitojun if (com->gone) { 134878064Sume printf("sio%d: gone\n", com->unit); 134978064Sume s = spltty(); 1350120891Sume if (com->ibuf != NULL) 135178064Sume free(com->ibuf, M_DEVBUF); 135278064Sume bzero(tp, sizeof *tp); 135378064Sume splx(s); 135478064Sume } 135578064Sume return (0); 135678064Sume} 135778064Sume 135878064Sumestatic void 135978064Sumecomhardclose(com) 136078064Sume struct com_s *com; 136178064Sume{ 136262587Sitojun int s; 136362587Sitojun struct tty *tp; 136478064Sume int unit; 1365111119Simp 136662587Sitojun unit = com->unit; 136762587Sitojun s = spltty(); 1368120856Sume com->poll = FALSE; 136962587Sitojun com->poll_output = FALSE; 1370108466Ssam com->do_timestamp = FALSE; 137153541Sshin com->do_dcd_timestamp = FALSE; 137262587Sitojun com->pps.ppsparam.mode = 0; 137378064Sume sio_setreg(com, com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); 137478064Sume tp = com->tp; 137578064Sume 137678064Sume#if defined(DDB) && (defined(BREAK_TO_DEBUGGER) || \ 137753541Sshin defined(ALT_BREAK_TO_DEBUGGER)) 137862587Sitojun /* 1379111119Simp * Leave interrupts enabled and don't clear DTR if this is the 138053541Sshin * console. This allows us to detect break-to-debugger events 138153541Sshin * while the console device is closed. 138253541Sshin */ 138353541Sshin if (com->unit != comconsole) 138453541Sshin#endif 138553541Sshin { 138653541Sshin sio_setreg(com, com_ier, 0); 138753541Sshin if (tp->t_cflag & HUPCL 138853541Sshin /* 138962587Sitojun * XXX we will miss any carrier drop between here and the 139053541Sshin * next open. Perhaps we should watch DCD even when the 139153541Sshin * port is closed; it is not sufficient to check it at 139253541Sshin * the next open because it might go up and down while 139362587Sitojun * we're not watching. 139478064Sume */ 139562587Sitojun || (!com->active_out 139662587Sitojun && !(com->prev_modem_status & MSR_DCD) 139762587Sitojun && !(com->it_in.c_cflag & CLOCAL)) 139878064Sume || !(tp->t_state & TS_ISOPEN)) { 139978064Sume (void)commctl(com, TIOCM_DTR, DMBIC); 140078064Sume if (com->dtr_wait != 0 && !(com->state & CS_DTR_OFF)) { 140178064Sume timeout(siodtrwakeup, com, com->dtr_wait); 140278064Sume com->state |= CS_DTR_OFF; 140378064Sume } 140478064Sume } 140562587Sitojun } 140678064Sume if (com->hasfifo) { 140762587Sitojun /* 140878064Sume * Disable fifos so that they are off after controlled 140962587Sitojun * reboots. Some BIOSes fail to detect 16550s when the 1410120891Sume * fifos are enabled. 141162587Sitojun */ 141262587Sitojun sio_setreg(com, com_fifo, 0); 141362587Sitojun } 141462587Sitojun com->active_out = FALSE; 141562587Sitojun wakeup(&com->active_out); 141662587Sitojun wakeup(TSA_CARR_ON(tp)); /* restart any wopeners */ 141762587Sitojun splx(s); 141862587Sitojun} 141962587Sitojun 142062587Sitojunstatic int 142162587Sitojunsioread(dev, uio, flag) 142262587Sitojun dev_t dev; 142362587Sitojun struct uio *uio; 142462587Sitojun int flag; 142562587Sitojun{ 142662587Sitojun int mynor; 142753541Sshin struct com_s *com; 142878064Sume 142978064Sume mynor = minor(dev); 143078064Sume if (mynor & CONTROL_MASK) 143178064Sume return (ENODEV); 143262587Sitojun com = com_addr(MINOR_TO_UNIT(mynor)); 143362587Sitojun if (com == NULL || com->gone) 143462587Sitojun return (ENODEV); 1435120891Sume return ((*linesw[com->tp->t_line].l_read)(com->tp, uio, flag)); 143662587Sitojun} 143753541Sshin 143862587Sitojunstatic int 143962587Sitojunsiowrite(dev, uio, flag) 144062587Sitojun dev_t dev; 144153541Sshin struct uio *uio; 144253541Sshin int flag; 144362587Sitojun{ 1444120856Sume int mynor; 144553541Sshin struct com_s *com; 144653541Sshin int unit; 144762587Sitojun 144853541Sshin mynor = minor(dev); 144953541Sshin if (mynor & CONTROL_MASK) 1450120856Sume return (ENODEV); 145153541Sshin 145253541Sshin unit = MINOR_TO_UNIT(mynor); 145353541Sshin com = com_addr(unit); 145453541Sshin if (com == NULL || com->gone) 145562587Sitojun return (ENODEV); 145662587Sitojun /* 145762587Sitojun * (XXX) We disallow virtual consoles if the physical console is 145862587Sitojun * a serial port. This is in case there is a display attached that 145962587Sitojun * is not the console. In that situation we don't need/want the X 146062587Sitojun * server taking over the console. 146162587Sitojun */ 146262587Sitojun if (constty != NULL && unit == comconsole) 146362587Sitojun constty = NULL; 146462587Sitojun return ((*linesw[com->tp->t_line].l_write)(com->tp, uio, flag)); 146562587Sitojun} 146662587Sitojun 146762587Sitojunstatic void 146862587Sitojunsiobusycheck(chan) 146962587Sitojun void *chan; 147062587Sitojun{ 147162587Sitojun struct com_s *com; 147262587Sitojun int s; 147362587Sitojun 147462587Sitojun com = (struct com_s *)chan; 147562587Sitojun 147662587Sitojun /* 1477111119Simp * Clear TS_BUSY if low-level output is complete. 147862587Sitojun * spl locking is sufficient because siointr1() does not set CS_BUSY. 1479111119Simp * If siointr1() clears CS_BUSY after we look at it, then we'll get 148062587Sitojun * called again. Reading the line status port outside of siointr1() 148162587Sitojun * is safe because CS_BUSY is clear so there are no output interrupts 148262587Sitojun * to lose. 148362587Sitojun */ 148462587Sitojun s = spltty(); 148562587Sitojun if (com->state & CS_BUSY) 148662587Sitojun com->extra_state &= ~CSE_BUSYCHECK; /* False alarm. */ 148762587Sitojun else if ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY)) 148862587Sitojun == (LSR_TSRE | LSR_TXRDY)) { 148962587Sitojun com->tp->t_state &= ~TS_BUSY; 149062587Sitojun ttwwakeup(com->tp); 149162587Sitojun com->extra_state &= ~CSE_BUSYCHECK; 149262587Sitojun } else 149362587Sitojun timeout(siobusycheck, com, hz / 100); 149462587Sitojun splx(s); 149562587Sitojun} 149662587Sitojun 149762587Sitojunstatic void 149862587Sitojunsiodtrwakeup(chan) 149962587Sitojun void *chan; 150062587Sitojun{ 150162587Sitojun struct com_s *com; 150262587Sitojun 150362587Sitojun com = (struct com_s *)chan; 150462587Sitojun com->state &= ~CS_DTR_OFF; 150562587Sitojun wakeup(&com->dtr_wait); 150662587Sitojun} 150762587Sitojun 150862587Sitojun/* 150962587Sitojun * Call this function with the sio_lock mutex held. It will return with the 151062587Sitojun * lock still held. 151162587Sitojun */ 151262587Sitojunstatic void 151362587Sitojunsioinput(com) 151462587Sitojun struct com_s *com; 151562587Sitojun{ 151662587Sitojun u_char *buf; 151762587Sitojun int incc; 151862587Sitojun u_char line_status; 151962587Sitojun int recv_data; 152062587Sitojun struct tty *tp; 152162587Sitojun 152262587Sitojun buf = com->ibuf; 152378704Sume tp = com->tp; 152478704Sume if (!(tp->t_state & TS_ISOPEN) || !(tp->t_cflag & CREAD)) { 152578704Sume com_events -= (com->iptr - com->ibuf); 152678704Sume com->iptr = com->ibuf; 152778704Sume return; 152878704Sume } 152962587Sitojun if (tp->t_state & TS_CAN_BYPASS_L_RINT) { 153062587Sitojun /* 153162587Sitojun * Avoid the grotesquely inefficient lineswitch routine 153262587Sitojun * (ttyinput) in "raw" mode. It usually takes about 450 153362587Sitojun * instructions (that's without canonical processing or echo!). 153462587Sitojun * slinput is reasonably fast (usually 40 instructions plus 153562587Sitojun * call overhead). 153662587Sitojun */ 153762587Sitojun do { 153862587Sitojun /* 153962587Sitojun * This may look odd, but it is using save-and-enable 154062587Sitojun * semantics instead of the save-and-disable semantics 154162587Sitojun * that are used everywhere else. 154262587Sitojun */ 154362587Sitojun mtx_unlock_spin(&sio_lock); 154462587Sitojun incc = com->iptr - buf; 154562587Sitojun if (tp->t_rawq.c_cc + incc > tp->t_ihiwat 154662587Sitojun && (com->state & CS_RTS_IFLOW 154795023Ssuz || tp->t_iflag & IXOFF) 154862587Sitojun && !(tp->t_state & TS_TBLOCK)) 154962587Sitojun ttyblock(tp); 155062587Sitojun com->delta_error_counts[CE_TTY_BUF_OVERFLOW] 155162587Sitojun += b_to_q((char *)buf, incc, &tp->t_rawq); 155262587Sitojun buf += incc; 155362587Sitojun tk_nin += incc; 155462587Sitojun tk_rawcc += incc; 155562587Sitojun tp->t_rawcc += incc; 155662587Sitojun ttwakeup(tp); 155762587Sitojun if (tp->t_state & TS_TTSTOP 155878064Sume && (tp->t_iflag & IXANY 155962587Sitojun || tp->t_cc[VSTART] == tp->t_cc[VSTOP])) { 156062587Sitojun tp->t_state &= ~TS_TTSTOP; 156162587Sitojun tp->t_lflag &= ~FLUSHO; 156262587Sitojun comstart(tp); 156362587Sitojun } 156462587Sitojun mtx_lock_spin(&sio_lock); 156562587Sitojun } while (buf < com->iptr); 156662587Sitojun } else { 156762587Sitojun do { 156862587Sitojun /* 156962587Sitojun * This may look odd, but it is using save-and-enable 157062587Sitojun * semantics instead of the save-and-disable semantics 157162587Sitojun * that are used everywhere else. 157262587Sitojun */ 157362587Sitojun mtx_unlock_spin(&sio_lock); 157462587Sitojun line_status = buf[com->ierroff]; 157562587Sitojun recv_data = *buf++; 157662587Sitojun if (line_status 157762587Sitojun & (LSR_BI | LSR_FE | LSR_OE | LSR_PE)) { 157862587Sitojun if (line_status & LSR_BI) 157962587Sitojun recv_data |= TTY_BI; 158062587Sitojun if (line_status & LSR_FE) 158162587Sitojun recv_data |= TTY_FE; 158262587Sitojun if (line_status & LSR_OE) 158362587Sitojun recv_data |= TTY_OE; 158462587Sitojun if (line_status & LSR_PE) 158562587Sitojun recv_data |= TTY_PE; 158662587Sitojun } 158762587Sitojun (*linesw[tp->t_line].l_rint)(recv_data, tp); 158862587Sitojun mtx_lock_spin(&sio_lock); 158962587Sitojun } while (buf < com->iptr); 159062587Sitojun } 159162587Sitojun com_events -= (com->iptr - com->ibuf); 159262587Sitojun com->iptr = com->ibuf; 159362587Sitojun 159462587Sitojun /* 159562587Sitojun * There is now room for another low-level buffer full of input, 159662587Sitojun * so enable RTS if it is now disabled and there is room in the 159762587Sitojun * high-level buffer. 159862587Sitojun */ 159962587Sitojun if ((com->state & CS_RTS_IFLOW) && !(com->mcr_image & MCR_RTS) && 160062587Sitojun !(tp->t_state & TS_TBLOCK)) 160162587Sitojun outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); 160262587Sitojun} 160362587Sitojun 160462587Sitojunvoid 160562587Sitojunsiointr(arg) 160662587Sitojun void *arg; 160762587Sitojun{ 160862587Sitojun struct com_s *com; 160962587Sitojun 161062587Sitojun#ifndef COM_MULTIPORT 161162587Sitojun com = (struct com_s *)arg; 161262587Sitojun 161362587Sitojun mtx_lock_spin(&sio_lock); 161462587Sitojun siointr1(com); 161562587Sitojun mtx_unlock_spin(&sio_lock); 161662587Sitojun#else /* COM_MULTIPORT */ 161762587Sitojun bool_t possibly_more_intrs; 161862587Sitojun int unit; 161962587Sitojun 162062587Sitojun /* 162162587Sitojun * Loop until there is no activity on any port. This is necessary 162253541Sshin * to get an interrupt edge more than to avoid another interrupt. 162353541Sshin * If the IRQ signal is just an OR of the IRQ signals from several 162453541Sshin * devices, then the edge from one may be lost because another is 162578064Sume * on. 162653541Sshin */ 162753541Sshin mtx_lock_spin(&sio_lock); 162853541Sshin do { 1629148892Sume possibly_more_intrs = FALSE; 163053541Sshin for (unit = 0; unit < sio_numunits; ++unit) { 163178064Sume com = com_addr(unit); 163278064Sume /* 163378064Sume * XXX COM_LOCK(); 163453541Sshin * would it work here, or be counter-productive? 163578064Sume */ 163653541Sshin if (com != NULL 163778064Sume && !com->gone 163878064Sume && (inb(com->int_id_port) & IIR_IMASK) 163978064Sume != IIR_NOPEND) { 164078064Sume siointr1(com); 1641120856Sume possibly_more_intrs = TRUE; 164278064Sume } 164378064Sume /* XXX COM_UNLOCK(); */ 164478064Sume } 164578064Sume } while (possibly_more_intrs); 164678064Sume mtx_unlock_spin(&sio_lock); 164778064Sume#endif /* COM_MULTIPORT */ 1648120856Sume} 164978064Sume 165078064Sumestatic void 165178064Sumesiointr1(com) 1652108172Shsu struct com_s *com; 1653120891Sume{ 165453541Sshin u_char line_status; 1655120891Sume u_char modem_status; 165653541Sshin u_char *ioptr; 165753541Sshin u_char recv_data; 165853541Sshin u_char int_ctl; 165953541Sshin u_char int_ctl_new; 166078064Sume struct timecounter *tc; 1661148892Sume u_int count; 166253541Sshin 166353541Sshin int_ctl = inb(com->intr_ctl_port); 166462587Sitojun int_ctl_new = int_ctl; 166562587Sitojun 166662587Sitojun while (!com->gone) { 166762587Sitojun if (com->pps.ppsparam.mode & PPS_CAPTUREBOTH) { 166862587Sitojun modem_status = inb(com->modem_status_port); 166978064Sume if ((modem_status ^ com->last_modem_status) & MSR_DCD) { 167062587Sitojun tc = timecounter; 167162587Sitojun count = tc->tc_get_timecount(tc); 167262587Sitojun pps_event(&com->pps, tc, count, 167362587Sitojun (modem_status & MSR_DCD) ? 167453541Sshin PPS_CAPTUREASSERT : PPS_CAPTURECLEAR); 167578064Sume } 167678064Sume } 167778064Sume line_status = inb(com->line_status_port); 167878064Sume 167953541Sshin /* input event? (check first to help avoid overruns) */ 168078064Sume while (line_status & LSR_RCV_MASK) { 168178064Sume /* break/unnattached error bits or real input? */ 168278064Sume if (!(line_status & LSR_RXRDY)) 168353541Sshin recv_data = 0; 168478064Sume else 168578064Sume recv_data = inb(com->data_port); 168678064Sume#if defined(DDB) && defined(ALT_BREAK_TO_DEBUGGER) 168778064Sume /* 168878064Sume * Solaris implements a new BREAK which is initiated 168978064Sume * by a character sequence CR ~ ^b which is similar 169053541Sshin * to a familiar pattern used on Sun servers by the 169178064Sume * Remote Console. 169278064Sume */ 169378064Sume#define KEY_CRTLB 2 /* ^B */ 169495023Ssuz#define KEY_CR 13 /* CR '\r' */ 169578064Sume#define KEY_TILDE 126 /* ~ */ 169678064Sume 169778064Sume if (com->unit == comconsole) { 169878064Sume static int brk_state1 = 0, brk_state2 = 0; 169978064Sume if (recv_data == KEY_CR) { 170078064Sume brk_state1 = recv_data; 170178064Sume brk_state2 = 0; 170278064Sume } else if (brk_state1 == KEY_CR 170378064Sume && (recv_data == KEY_TILDE 170453541Sshin || recv_data == KEY_CRTLB)) { 170553541Sshin if (recv_data == KEY_TILDE) 170653541Sshin brk_state2 = recv_data; 1707108172Shsu else if (brk_state2 == KEY_TILDE 1708120856Sume && recv_data == KEY_CRTLB) { 170953541Sshin breakpoint(); 171053541Sshin brk_state1 = 0; 171153541Sshin brk_state2 = 0; 171253541Sshin goto cont; 1713108172Shsu } else 171453541Sshin brk_state2 = 0; 1715120856Sume } else 171653541Sshin brk_state1 = 0; 171753541Sshin } 171853541Sshin#endif 171953541Sshin if (line_status & (LSR_BI | LSR_FE | LSR_PE)) { 172053541Sshin /* 172153541Sshin * Don't store BI if IGNBRK or FE/PE if IGNPAR. 172253541Sshin * Otherwise, push the work to a higher level 172353541Sshin * (to handle PARMRK) if we're bypassing. 172478064Sume * Otherwise, convert BI/FE and PE+INPCK to 0. 172578064Sume * 172678064Sume * This makes bypassing work right in the 172778064Sume * usual "raw" case (IGNBRK set, and IGNPAR 172878064Sume * and INPCK clear). 172953541Sshin * 173078064Sume * Note: BI together with FE/PE means just BI. 173178064Sume */ 173253541Sshin if (line_status & LSR_BI) { 173378064Sume#if defined(DDB) && defined(BREAK_TO_DEBUGGER) 1734120856Sume if (com->unit == comconsole) { 1735120891Sume breakpoint(); 1736108172Shsu goto cont; 173778064Sume } 1738120892Sume#endif 1739120891Sume if (com->tp == NULL 174062587Sitojun || com->tp->t_iflag & IGNBRK) 1741120891Sume goto cont; 174253541Sshin } else { 174353541Sshin if (com->tp == NULL 174453541Sshin || com->tp->t_iflag & IGNPAR) 174553541Sshin goto cont; 174678064Sume } 174778064Sume if (com->tp->t_state & TS_CAN_BYPASS_L_RINT 174878064Sume && (line_status & (LSR_BI | LSR_FE) 174978064Sume || com->tp->t_iflag & INPCK)) 175078064Sume recv_data = 0; 175178064Sume } 175278064Sume ++com->bytes_in; 175378064Sume if (com->hotchar != 0 && recv_data == com->hotchar) 175478064Sume swi_sched(sio_fast_ih, 0); 175578064Sume ioptr = com->iptr; 175678064Sume if (ioptr >= com->ibufend) 175778064Sume CE_RECORD(com, CE_INTERRUPT_BUF_OVERFLOW); 1758120891Sume else { 1759120891Sume if (com->do_timestamp) 176078064Sume microtime(&com->timestamp); 176153541Sshin ++com_events; 176253541Sshin swi_sched(sio_slow_ih, SWI_DELAY); 176378064Sume#if 0 /* for testing input latency vs efficiency */ 176478064Sumeif (com->iptr - com->ibuf == 8) 176578064Sume swi_sched(sio_fast_ih, 0); 176678064Sume#endif 176753541Sshin ioptr[0] = recv_data; 176878064Sume ioptr[com->ierroff] = line_status; 176978064Sume com->iptr = ++ioptr; 177078064Sume if (ioptr == com->ihighwater 177153541Sshin && com->state & CS_RTS_IFLOW) 177278064Sume outb(com->modem_ctl_port, 177378064Sume com->mcr_image &= ~MCR_RTS); 177478064Sume if (line_status & LSR_OE) 177578064Sume CE_RECORD(com, CE_OVERRUN); 177678064Sume } 177778064Sumecont: 177853541Sshin /* 177953541Sshin * "& 0x7F" is to avoid the gcc-1.40 generating a slow 178078064Sume * jump from the top of the loop to here 178178064Sume */ 1782120891Sume line_status = inb(com->line_status_port) & 0x7F; 178378064Sume } 178478064Sume 178578064Sume /* modem status change? (always check before doing output) */ 178678064Sume modem_status = inb(com->modem_status_port); 178778064Sume if (modem_status != com->last_modem_status) { 178878064Sume if (com->do_dcd_timestamp 178978064Sume && !(com->last_modem_status & MSR_DCD) 179053541Sshin && modem_status & MSR_DCD) 179178064Sume microtime(&com->dcd_timestamp); 179278064Sume 179378064Sume /* 179478064Sume * Schedule high level to handle DCD changes. Note 179578064Sume * that we don't use the delta bits anywhere. Some 179678064Sume * UARTs mess them up, and it's easy to remember the 179778064Sume * previous bits and calculate the delta. 179878064Sume */ 1799120891Sume com->last_modem_status = modem_status; 1800108172Shsu if (!(com->state & CS_CHECKMSR)) { 1801120856Sume com_events += LOTS_OF_EVENTS; 180278064Sume com->state |= CS_CHECKMSR; 180378064Sume swi_sched(sio_fast_ih, 0); 180478064Sume } 180578064Sume 180678064Sume /* handle CTS change immediately for crisp flow ctl */ 180778064Sume if (com->state & CS_CTS_OFLOW) { 180878064Sume if (modem_status & MSR_CTS) 180978064Sume com->state |= CS_ODEVREADY; 181078064Sume else 181178064Sume com->state &= ~CS_ODEVREADY; 181278064Sume } 181378064Sume } 181478064Sume 181578064Sume /* output queued and everything ready? */ 181678064Sume if (line_status & LSR_TXRDY 181778064Sume && com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) { 181878064Sume ioptr = com->obufq.l_head; 181978064Sume if (com->tx_fifo_size > 1) { 182078064Sume u_int ocount; 182178064Sume 182278064Sume ocount = com->obufq.l_tail - ioptr; 182378064Sume if (ocount > com->tx_fifo_size) 182478064Sume ocount = com->tx_fifo_size; 182578064Sume com->bytes_out += ocount; 182678064Sume do 182778064Sume outb(com->data_port, *ioptr++); 1828120891Sume while (--ocount != 0); 182978064Sume } else { 183078064Sume outb(com->data_port, *ioptr++); 183178064Sume ++com->bytes_out; 183278064Sume } 183378064Sume com->obufq.l_head = ioptr; 1834120891Sume if (COM_IIR_TXRDYBUG(com->flags)) { 1835121315Sume int_ctl_new = int_ctl | IER_ETXRDY; 183678064Sume } 1837120891Sume if (ioptr >= com->obufq.l_tail) { 183878064Sume struct lbq *qp; 1839120891Sume 184053541Sshin qp = com->obufq.l_next; 184153541Sshin qp->l_queued = FALSE; 184253541Sshin qp = qp->l_next; 184353541Sshin if (qp != NULL) { 184453541Sshin com->obufq.l_head = qp->l_head; 184578064Sume com->obufq.l_tail = qp->l_tail; 184678064Sume com->obufq.l_next = qp; 184778064Sume } else { 184878064Sume /* output just completed */ 184978064Sume if (COM_IIR_TXRDYBUG(com->flags)) { 185078064Sume int_ctl_new = int_ctl & ~IER_ETXRDY; 185178064Sume } 1852108172Shsu com->state &= ~CS_BUSY; 1853108172Shsu } 1854120856Sume if (!(com->state & CS_ODONE)) { 185553541Sshin com_events += LOTS_OF_EVENTS; 185653541Sshin com->state |= CS_ODONE; 185753541Sshin /* handle at high level ASAP */ 185853541Sshin swi_sched(sio_fast_ih, 0); 185953541Sshin } 186053541Sshin } 186153541Sshin if (COM_IIR_TXRDYBUG(com->flags) && (int_ctl != int_ctl_new)) { 186253541Sshin outb(com->intr_ctl_port, int_ctl_new); 186353541Sshin } 186453541Sshin } 186553541Sshin 186678064Sume /* finished? */ 186778064Sume#ifndef COM_MULTIPORT 186853541Sshin if ((inb(com->int_id_port) & IIR_IMASK) == IIR_NOPEND) 1869121315Sume#endif /* COM_MULTIPORT */ 187053541Sshin return; 187153541Sshin } 187253541Sshin} 187362587Sitojun 187453541Sshinstatic int 187553541Sshinsioioctl(dev, cmd, data, flag, td) 187662587Sitojun dev_t dev; 187762587Sitojun u_long cmd; 187862587Sitojun caddr_t data; 187962587Sitojun int flag; 1880120891Sume struct thread *td; 188162587Sitojun{ 188262587Sitojun struct com_s *com; 188353541Sshin int error; 1884148385Sume int mynor; 1885148385Sume int s; 1886148385Sume struct tty *tp; 1887148385Sume#if defined(COMPAT_43) || defined(COMPAT_SUNOS) 1888121315Sume u_long oldcmd; 1889148385Sume struct termios term; 1890121315Sume#endif 1891148385Sume 1892148385Sume mynor = minor(dev); 1893148385Sume com = com_addr(MINOR_TO_UNIT(mynor)); 1894148385Sume if (com == NULL || com->gone) 1895148385Sume return (ENODEV); 189653541Sshin if (mynor & CONTROL_MASK) { 1897141545Srwatson struct termios *ct; 1898120891Sume 1899141545Srwatson switch (mynor & CONTROL_MASK) { 1900141545Srwatson case CONTROL_INIT_STATE: 1901141545Srwatson ct = mynor & CALLOUT_MASK ? &com->it_out : &com->it_in; 1902141545Srwatson break; 190353541Sshin case CONTROL_LOCK_STATE: 1904141545Srwatson ct = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in; 190553541Sshin break; 1906141545Srwatson default: 190753541Sshin return (ENODEV); /* /dev/nodev */ 190853541Sshin } 1909141545Srwatson switch (cmd) { 191053541Sshin case TIOCSETA: 191153541Sshin error = suser_td(td); 1912141545Srwatson if (error != 0) 191353541Sshin return (error); 191453541Sshin *ct = *(struct termios *)data; 191553541Sshin return (0); 1916141545Srwatson case TIOCGETA: 191753541Sshin *(struct termios *)data = *ct; 1918121809Sume return (0); 1919121809Sume case TIOCGETD: 1920121809Sume *(int *)data = TTYDISC; 1921121809Sume return (0); 1922121809Sume case TIOCGWINSZ: 1923121809Sume bzero(data, sizeof(struct winsize)); 1924121809Sume return (0); 1925121809Sume default: 1926121809Sume return (ENOTTY); 1927121809Sume } 1928121809Sume } 1929121809Sume tp = com->tp; 1930121809Sume#if defined(COMPAT_43) || defined(COMPAT_SUNOS) 1931121809Sume term = tp->t_termios; 1932121809Sume oldcmd = cmd; 1933121809Sume error = ttsetcompat(tp, &cmd, data, &term); 1934121809Sume if (error != 0) 1935121809Sume return (error); 1936121809Sume if (cmd != oldcmd) 1937121809Sume data = (caddr_t)&term; 1938121809Sume#endif 1939121809Sume if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { 1940121809Sume int cc; 1941145065Sgnn struct termios *dt = (struct termios *)data; 1942145065Sgnn struct termios *lt = mynor & CALLOUT_MASK 1943145065Sgnn ? &com->lt_out : &com->lt_in; 1944145065Sgnn 1945145065Sgnn dt->c_iflag = (tp->t_iflag & lt->c_iflag) 1946145065Sgnn | (dt->c_iflag & ~lt->c_iflag); 1947145065Sgnn dt->c_oflag = (tp->t_oflag & lt->c_oflag) 1948145065Sgnn | (dt->c_oflag & ~lt->c_oflag); 1949121809Sume dt->c_cflag = (tp->t_cflag & lt->c_cflag) 1950121809Sume | (dt->c_cflag & ~lt->c_cflag); 1951121809Sume dt->c_lflag = (tp->t_lflag & lt->c_lflag) 1952121809Sume | (dt->c_lflag & ~lt->c_lflag); 195353541Sshin for (cc = 0; cc < NCCS; ++cc) 1954121674Sume if (lt->c_cc[cc] != 0) 195553541Sshin dt->c_cc[cc] = tp->t_cc[cc]; 195653541Sshin if (lt->c_ispeed != 0) 1957160591Srwatson dt->c_ispeed = tp->t_ispeed; 1958160591Srwatson if (lt->c_ospeed != 0) 1959160591Srwatson dt->c_ospeed = tp->t_ospeed; 1960121315Sume } 1961120891Sume error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, td); 196253541Sshin if (error != ENOIOCTL) 196353541Sshin return (error); 196478064Sume s = spltty(); 196553541Sshin error = ttioctl(tp, cmd, data, flag); 196678064Sume disc_optim(tp, &tp->t_termios, com); 1967160591Srwatson if (error != ENOIOCTL) { 1968160591Srwatson splx(s); 196997658Stanimura return (error); 1970160591Srwatson } 197153541Sshin switch (cmd) { 197253541Sshin case TIOCSBRK: 1973141545Srwatson sio_setreg(com, com_cfcr, com->cfcr_image |= CFCR_SBREAK); 197453541Sshin break; 197553541Sshin case TIOCCBRK: 197653541Sshin sio_setreg(com, com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); 197753541Sshin break; 197853541Sshin case TIOCSDTR: 1979121674Sume (void)commctl(com, TIOCM_DTR, DMBIS); 198053541Sshin break; 198153541Sshin case TIOCCDTR: 1982121809Sume (void)commctl(com, TIOCM_DTR, DMBIC); 1983121809Sume break; 1984121809Sume /* 1985121809Sume * XXX should disallow changing MCR_RTS if CS_RTS_IFLOW is set. The 1986121809Sume * changes get undone on the next call to comparam(). 1987121809Sume */ 1988121809Sume case TIOCMSET: 1989121809Sume (void)commctl(com, *(int *)data, DMSET); 1990145065Sgnn break; 1991145065Sgnn case TIOCMBIS: 1992145065Sgnn (void)commctl(com, *(int *)data, DMBIS); 1993145065Sgnn break; 1994145065Sgnn case TIOCMBIC: 1995145065Sgnn (void)commctl(com, *(int *)data, DMBIC); 1996145065Sgnn break; 1997145065Sgnn case TIOCMGET: 1998145065Sgnn *(int *)data = commctl(com, 0, DMGET); 1999145065Sgnn break; 2000121809Sume case TIOCMSDTRWAIT: 2001121809Sume /* must be root since the wait applies to following logins */ 2002160591Srwatson error = suser_td(td); 2003160591Srwatson if (error != 0) { 2004121315Sume splx(s); 200553541Sshin return (error); 200653541Sshin } 200753541Sshin com->dtr_wait = *(int *)data * hz / 100; 2008160591Srwatson break; 200997658Stanimura case TIOCMGDTRWAIT: 2010160591Srwatson *(int *)data = com->dtr_wait * 100 / hz; 2011141545Srwatson break; 201253541Sshin case TIOCTIMESTAMP: 201353541Sshin com->do_timestamp = TRUE; 201453541Sshin *(struct timeval *)data = com->timestamp; 201553541Sshin break; 2016141545Srwatson case TIOCDCDTIMESTAMP: 201753541Sshin com->do_dcd_timestamp = TRUE; 201853541Sshin *(struct timeval *)data = com->dcd_timestamp; 201953541Sshin break; 202053541Sshin default: 202153541Sshin splx(s); 202262587Sitojun error = pps_ioctl(cmd, data, &com->pps); 202353541Sshin if (error == ENODEV) 202453541Sshin error = ENOTTY; 202553541Sshin return (error); 202653541Sshin } 202753541Sshin splx(s); 202853541Sshin return (0); 202962587Sitojun} 203053541Sshin 203153541Sshin/* software interrupt handler for SWI_TTY */ 203262587Sitojunstatic void 203353541Sshinsiopoll(void *dummy) 203453541Sshin{ 2035148385Sume int unit; 203653541Sshin 203762587Sitojun if (com_events == 0) 203862587Sitojun return; 203978064Sumerepeat: 204078064Sume for (unit = 0; unit < sio_numunits; ++unit) { 204178064Sume struct com_s *com; 204278064Sume int incc; 204362587Sitojun struct tty *tp; 204462587Sitojun 204562587Sitojun com = com_addr(unit); 204653541Sshin if (com == NULL) 204753541Sshin continue; 204853541Sshin tp = com->tp; 204953541Sshin if (tp == NULL || com->gone) { 205062587Sitojun /* 205162587Sitojun * Discard any events related to never-opened or 205262587Sitojun * going-away devices. 205362587Sitojun */ 205462587Sitojun mtx_lock_spin(&sio_lock); 205562587Sitojun incc = com->iptr - com->ibuf; 205662587Sitojun com->iptr = com->ibuf; 205753541Sshin if (com->state & CS_CHECKMSR) { 205862587Sitojun incc += LOTS_OF_EVENTS; 205962587Sitojun com->state &= ~CS_CHECKMSR; 206062587Sitojun } 206162587Sitojun com_events -= incc; 206262587Sitojun mtx_unlock_spin(&sio_lock); 206362587Sitojun continue; 206462587Sitojun } 206553541Sshin if (com->iptr != com->ibuf) { 206662587Sitojun mtx_lock_spin(&sio_lock); 206762587Sitojun sioinput(com); 206862587Sitojun mtx_unlock_spin(&sio_lock); 206962587Sitojun } 207062587Sitojun if (com->state & CS_CHECKMSR) { 207162587Sitojun u_char delta_modem_status; 207262587Sitojun 207353541Sshin mtx_lock_spin(&sio_lock); 207453541Sshin delta_modem_status = com->last_modem_status 207562587Sitojun ^ com->prev_modem_status; 207662587Sitojun com->prev_modem_status = com->last_modem_status; 207762587Sitojun com_events -= LOTS_OF_EVENTS; 207853541Sshin com->state &= ~CS_CHECKMSR; 207953541Sshin mtx_unlock_spin(&sio_lock); 208053541Sshin if (delta_modem_status & MSR_DCD) 208153541Sshin (*linesw[tp->t_line].l_modem) 2082148385Sume (tp, com->prev_modem_status & MSR_DCD); 208353541Sshin } 208453541Sshin if (com->state & CS_ODONE) { 208553541Sshin mtx_lock_spin(&sio_lock); 208653541Sshin com_events -= LOTS_OF_EVENTS; 208753541Sshin com->state &= ~CS_ODONE; 208853541Sshin mtx_unlock_spin(&sio_lock); 208953541Sshin if (!(com->state & CS_BUSY) 2090120891Sume && !(com->extra_state & CSE_BUSYCHECK)) { 209153541Sshin timeout(siobusycheck, com, hz / 100); 2092120891Sume com->extra_state |= CSE_BUSYCHECK; 209362587Sitojun } 209462587Sitojun (*linesw[tp->t_line].l_start)(tp); 2095148385Sume } 2096148385Sume if (com_events == 0) 2097148385Sume break; 209853541Sshin } 2099148385Sume if (com_events >= LOTS_OF_EVENTS) 2100148385Sume goto repeat; 2101148385Sume} 2102148385Sume 2103148385Sumestatic int 2104148385Sumecomparam(tp, t) 2105148385Sume struct tty *tp; 2106148385Sume struct termios *t; 2107148385Sume{ 2108148385Sume u_int cfcr; 2109148385Sume int cflag; 2110148385Sume struct com_s *com; 2111148385Sume int divisor; 2112148385Sume u_char dlbh; 2113148385Sume u_char dlbl; 2114148385Sume int s; 2115148385Sume int unit; 2116148385Sume 2117148385Sume /* do historical conversions */ 211853541Sshin if (t->c_ispeed == 0) 211953541Sshin t->c_ispeed = t->c_ospeed; 212053541Sshin 2121148385Sume /* check requested parameters */ 212278064Sume divisor = ttspeedtab(t->c_ospeed, comspeedtab); 2123148385Sume if (divisor < 0 || (divisor > 0 && t->c_ispeed != t->c_ospeed)) 212478064Sume return (EINVAL); 212578064Sume 212653541Sshin /* parameters are OK, convert them to the com struct and the device */ 212762587Sitojun unit = DEV_TO_UNIT(tp->t_dev); 212895023Ssuz com = com_addr(unit); 212978064Sume if (com == NULL) 213053541Sshin return (ENODEV); 2131148385Sume s = spltty(); 2132148385Sume if (divisor == 0) 2133148385Sume (void)commctl(com, TIOCM_DTR, DMBIC); /* hang up line */ 2134148385Sume else 2135148385Sume (void)commctl(com, TIOCM_DTR, DMBIS); 213678064Sume cflag = t->c_cflag; 2137148385Sume switch (cflag & CSIZE) { 213878064Sume case CS5: 213978064Sume cfcr = CFCR_5BITS; 214078064Sume break; 2141165118Sbz case CS6: 214278064Sume cfcr = CFCR_6BITS; 214378064Sume break; 214478064Sume case CS7: 2145165118Sbz cfcr = CFCR_7BITS; 214678064Sume break; 214778064Sume default: 214878064Sume cfcr = CFCR_8BITS; 214953541Sshin break; 215053541Sshin } 215153541Sshin if (cflag & PARENB) { 215262587Sitojun cfcr |= CFCR_PENAB; 215362587Sitojun if (!(cflag & PARODD)) 215453541Sshin cfcr |= CFCR_PEVEN; 2155121472Sume } 2156121472Sume if (cflag & CSTOPB) 2157121472Sume cfcr |= CFCR_STOPB; 215853541Sshin 2159121161Sume if (com->hasfifo && divisor != 0) { 216078064Sume /* 216178064Sume * Use a fifo trigger level low enough so that the input 216253541Sshin * latency from the fifo is less than about 16 msec and 216353541Sshin * the total latency is less than about 30 msec. These 216453541Sshin * latencies are reasonable for humans. Serial comms 2165120891Sume * protocols shouldn't expect anything better since modem 216653541Sshin * latencies are larger. 216753541Sshin * 216878064Sume * The fifo trigger level cannot be set at RX_HIGH for high 216953541Sshin * speed connections without further work on reducing 217053541Sshin * interrupt disablement times in other parts of the system, 217153541Sshin * without producing silo overflow errors. 217253541Sshin */ 2173105194Ssam com->fifo_image = t->c_ospeed <= 4800 217453541Sshin ? FIFO_ENABLE : FIFO_ENABLE | FIFO_RX_MEDH; 217553541Sshin#ifdef COM_ESP 217653541Sshin /* 217753541Sshin * The Hayes ESP card needs the fifo DMA mode bit set 217853541Sshin * in compatibility mode. If not, it will interrupt 217953541Sshin * for each character received. 218053541Sshin */ 218153541Sshin if (com->esp) 218253541Sshin com->fifo_image |= FIFO_DMA_MODE; 218353541Sshin#endif 218453541Sshin sio_setreg(com, com_fifo, com->fifo_image); 218553541Sshin } 218653541Sshin 218762587Sitojun /* 2188151539Ssuz * This returns with interrupts disabled so that we can complete 218953541Sshin * the speed change atomically. Keeping interrupts disabled is 219053541Sshin * especially important while com_data is hidden. 219153541Sshin */ 219253541Sshin (void) siosetwater(com, t->c_ispeed); 219353541Sshin 219453541Sshin if (divisor != 0) { 219553541Sshin sio_setreg(com, com_cfcr, cfcr | CFCR_DLAB); 219653541Sshin /* 219753541Sshin * Only set the divisor registers if they would change, 2198165118Sbz * since on some 16550 incompatibles (UMC8669F), setting 2199165118Sbz * them while input is arriving them loses sync until 2200165118Sbz * data stops arriving. 220153541Sshin */ 2202165118Sbz dlbl = divisor & 0xFF; 2203165118Sbz if (sio_getreg(com, com_dlbl) != dlbl) 220453541Sshin sio_setreg(com, com_dlbl, dlbl); 220553541Sshin dlbh = (u_int) divisor >> 8; 220653541Sshin if (sio_getreg(com, com_dlbh) != dlbh) 220753541Sshin sio_setreg(com, com_dlbh, dlbh); 220853541Sshin } 220978064Sume 221053541Sshin sio_setreg(com, com_cfcr, com->cfcr_image = cfcr); 221153541Sshin 221253541Sshin if (!(tp->t_state & TS_TTSTOP)) 221353541Sshin com->state |= CS_TTGO; 221462587Sitojun 221553541Sshin if (cflag & CRTS_IFLOW) { 221653541Sshin if (com->st16650a) { 221753541Sshin sio_setreg(com, com_cfcr, 0xbf); 221853541Sshin sio_setreg(com, com_fifo, 221953541Sshin sio_getreg(com, com_fifo) | 0x40); 222053541Sshin } 222153541Sshin com->state |= CS_RTS_IFLOW; 222253541Sshin /* 222353541Sshin * If CS_RTS_IFLOW just changed from off to on, the change 222462587Sitojun * needs to be propagated to MCR_RTS. This isn't urgent, 222562587Sitojun * so do it later by calling comstart() instead of repeating 222653541Sshin * a lot of code from comstart() here. 2227165118Sbz */ 222853541Sshin } else if (com->state & CS_RTS_IFLOW) { 222953541Sshin com->state &= ~CS_RTS_IFLOW; 223053541Sshin /* 223153541Sshin * CS_RTS_IFLOW just changed from on to off. Force MCR_RTS 223253541Sshin * on here, since comstart() won't do it later. 223353541Sshin */ 223462587Sitojun outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); 223553541Sshin if (com->st16650a) { 223662587Sitojun sio_setreg(com, com_cfcr, 0xbf); 223762587Sitojun sio_setreg(com, com_fifo, 223862587Sitojun sio_getreg(com, com_fifo) & ~0x40); 223962587Sitojun } 224062587Sitojun } 224162587Sitojun 224262587Sitojun 224362587Sitojun /* 224462587Sitojun * Set up state to handle output flow control. 224553541Sshin * XXX - worth handling MDMBUF (DCD) flow control at the lowest level? 224662587Sitojun * Now has 10+ msec latency, while CTS flow has 50- usec latency. 224762587Sitojun */ 224862587Sitojun com->state |= CS_ODEVREADY; 224962587Sitojun com->state &= ~CS_CTS_OFLOW; 225053541Sshin if (cflag & CCTS_OFLOW) { 2251148385Sume com->state |= CS_CTS_OFLOW; 2252148385Sume if (!(com->last_modem_status & MSR_CTS)) 2253148385Sume com->state &= ~CS_ODEVREADY; 2254148385Sume if (com->st16650a) { 225553541Sshin sio_setreg(com, com_cfcr, 0xbf); 225653541Sshin sio_setreg(com, com_fifo, 225753541Sshin sio_getreg(com, com_fifo) | 0x80); 225878064Sume } 2259120891Sume } else { 2260120891Sume if (com->st16650a) { 2261165118Sbz sio_setreg(com, com_cfcr, 0xbf); 226278064Sume sio_setreg(com, com_fifo, 226353541Sshin sio_getreg(com, com_fifo) & ~0x80); 226453541Sshin } 226578064Sume } 2266120891Sume 2267120891Sume sio_setreg(com, com_cfcr, com->cfcr_image); 2268165118Sbz 226978064Sume /* XXX shouldn't call functions while intrs are disabled. */ 227053541Sshin disc_optim(tp, t, com); 227153541Sshin /* 227253541Sshin * Recover from fiddling with CS_TTGO. We used to call siointr1() 227353541Sshin * unconditionally, but that defeated the careful discarding of 227453541Sshin * stale input in sioopen(). 227553541Sshin */ 227653541Sshin if (com->state >= (CS_BUSY | CS_TTGO)) 227753541Sshin siointr1(com); 227853541Sshin 227953541Sshin mtx_unlock_spin(&sio_lock); 228053541Sshin splx(s); 228153541Sshin comstart(tp); 228265895Sume if (com->ibufold != NULL) { 228365895Sume free(com->ibufold, M_DEVBUF); 228478064Sume com->ibufold = NULL; 228565895Sume } 228665895Sume return (0); 228778064Sume} 2288120727Ssam 228978064Sume/* 229065895Sume * This function must be called with the sio_lock mutex released and will 229165895Sume * return with it obtained. 229253541Sshin */ 229353541Sshinstatic int 229478064Sumesiosetwater(com, speed) 2295120891Sume struct com_s *com; 2296120891Sume speed_t speed; 2297120891Sume{ 2298165118Sbz int cp4ticks; 2299120891Sume u_char *ibuf; 2300120727Ssam int ibufsize; 230178064Sume struct tty *tp; 230253541Sshin 230353541Sshin /* 230478064Sume * Make the buffer size large enough to handle a softtty interrupt 2305120891Sume * latency of about 2 ticks without loss of throughput or data 2306120891Sume * (about 3 ticks if input flow control is not used or not honoured, 2307120891Sume * but a bit less for CS5-CS7 modes). 230878064Sume */ 230953541Sshin cp4ticks = speed / 10 / hz * 4; 2310120727Ssam for (ibufsize = 128; ibufsize < cp4ticks;) 231153541Sshin ibufsize <<= 1; 231253541Sshin if (ibufsize == com->ibufsize) { 231353541Sshin mtx_lock_spin(&sio_lock); 231478064Sume return (0); 2315120891Sume } 2316120891Sume 2317120891Sume /* 231878064Sume * Allocate input buffer. The extra factor of 2 in the size is 231953541Sshin * to allow for an error byte for each input byte. 232053541Sshin */ 232153541Sshin ibuf = malloc(2 * ibufsize, M_DEVBUF, M_NOWAIT); 232253541Sshin if (ibuf == NULL) { 232353541Sshin mtx_lock_spin(&sio_lock); 232453541Sshin return (ENOMEM); 232553541Sshin } 232653541Sshin 232778064Sume /* Initialize non-critical variables. */ 2328120891Sume com->ibufold = com->ibuf; 2329120891Sume com->ibufsize = ibufsize; 2330120891Sume tp = com->tp; 233178064Sume if (tp != NULL) { 233253541Sshin tp->t_ififosize = 2 * ibufsize; 233353541Sshin tp->t_ispeedwat = (speed_t)-1; 233453541Sshin tp->t_ospeedwat = (speed_t)-1; 233553541Sshin } 233653541Sshin 233753541Sshin /* 233878064Sume * Read current input buffer, if any. Continue with interrupts 2339120891Sume * disabled. 2340120891Sume */ 234178064Sume mtx_lock_spin(&sio_lock); 234262587Sitojun if (com->iptr != com->ibuf) 234353541Sshin sioinput(com); 234453541Sshin 234553541Sshin /*- 234653541Sshin * Initialize critical variables, including input buffer watermarks. 234753541Sshin * The external device is asked to stop sending when the buffer 234853541Sshin * exactly reaches high water, or when the high level requests it. 234953541Sshin * The high level is notified immediately (rather than at a later 235053541Sshin * clock tick) when this watermark is reached. 235153541Sshin * The buffer size is chosen so the watermark should almost never 235253541Sshin * be reached. 235353541Sshin * The low watermark is invisibly 0 since the buffer is always 235453541Sshin * emptied all at once. 235553541Sshin */ 235678064Sume com->iptr = com->ibuf = ibuf; 2357120891Sume com->ibufend = ibuf + ibufsize; 2358120891Sume com->ierroff = ibufsize; 2359165118Sbz com->ihighwater = ibuf + 3 * ibufsize / 4; 2360165118Sbz return (0); 2361120891Sume} 236278064Sume 236353541Sshinstatic void 236453541Sshincomstart(tp) 236553541Sshin struct tty *tp; 236653541Sshin{ 2367120891Sume struct com_s *com; 236853541Sshin int s; 236995023Ssuz int unit; 237053541Sshin 237153541Sshin unit = DEV_TO_UNIT(tp->t_dev); 237253541Sshin com = com_addr(unit); 237353541Sshin if (com == NULL) 237453541Sshin return; 237553541Sshin s = spltty(); 237653541Sshin mtx_lock_spin(&sio_lock); 237753541Sshin if (tp->t_state & TS_TTSTOP) 237853541Sshin com->state &= ~CS_TTGO; 237953541Sshin else 238053541Sshin com->state |= CS_TTGO; 238153541Sshin if (tp->t_state & TS_TBLOCK) { 238253541Sshin if (com->mcr_image & MCR_RTS && com->state & CS_RTS_IFLOW) 238353541Sshin outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); 238453541Sshin } else { 2385120891Sume if (!(com->mcr_image & MCR_RTS) && com->iptr < com->ihighwater 2386120891Sume && com->state & CS_RTS_IFLOW) 238753541Sshin outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); 238853541Sshin } 238953541Sshin mtx_unlock_spin(&sio_lock); 239053541Sshin if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) { 239153541Sshin ttwwakeup(tp); 239253541Sshin splx(s); 239353541Sshin return; 239453541Sshin } 239553541Sshin if (tp->t_outq.c_cc != 0) { 239653541Sshin struct lbq *qp; 2397122062Sume struct lbq *next; 239853541Sshin 239953541Sshin if (!com->obufs[0].l_queued) { 240053541Sshin com->obufs[0].l_tail 240162587Sitojun = com->obuf1 + q_to_b(&tp->t_outq, com->obuf1, 240262587Sitojun sizeof com->obuf1); 240362587Sitojun com->obufs[0].l_next = NULL; 240478064Sume com->obufs[0].l_queued = TRUE; 240578064Sume mtx_lock_spin(&sio_lock); 240678064Sume if (com->state & CS_BUSY) { 240778064Sume qp = com->obufq.l_next; 240878064Sume while ((next = qp->l_next) != NULL) 240953541Sshin qp = next; 241053541Sshin qp->l_next = &com->obufs[0]; 241153541Sshin } else { 241253541Sshin com->obufq.l_head = com->obufs[0].l_head; 241353541Sshin com->obufq.l_tail = com->obufs[0].l_tail; 241453541Sshin com->obufq.l_next = &com->obufs[0]; 241553541Sshin com->state |= CS_BUSY; 241653541Sshin } 241753541Sshin mtx_unlock_spin(&sio_lock); 241853541Sshin } 241953541Sshin if (tp->t_outq.c_cc != 0 && !com->obufs[1].l_queued) { 242053541Sshin com->obufs[1].l_tail 242153541Sshin = com->obuf2 + q_to_b(&tp->t_outq, com->obuf2, 242253541Sshin sizeof com->obuf2); 242353541Sshin com->obufs[1].l_next = NULL; 242453541Sshin com->obufs[1].l_queued = TRUE; 242553541Sshin mtx_lock_spin(&sio_lock); 242662587Sitojun if (com->state & CS_BUSY) { 242753541Sshin qp = com->obufq.l_next; 242862587Sitojun while ((next = qp->l_next) != NULL) 242962587Sitojun qp = next; 243053541Sshin qp->l_next = &com->obufs[1]; 2431151539Ssuz } else { 243253541Sshin com->obufq.l_head = com->obufs[1].l_head; 243353541Sshin com->obufq.l_tail = com->obufs[1].l_tail; 243453541Sshin com->obufq.l_next = &com->obufs[1]; 243553541Sshin com->state |= CS_BUSY; 243653541Sshin } 243753541Sshin mtx_unlock_spin(&sio_lock); 243853541Sshin } 243953541Sshin tp->t_state |= TS_BUSY; 244053541Sshin } 244153541Sshin mtx_lock_spin(&sio_lock); 244253541Sshin if (com->state >= (CS_BUSY | CS_TTGO)) 244353541Sshin siointr1(com); /* fake interrupt to start output */ 244453541Sshin mtx_unlock_spin(&sio_lock); 244562587Sitojun ttwwakeup(tp); 244662587Sitojun splx(s); 244762587Sitojun} 244862587Sitojun 244962587Sitojunstatic void 245053541Sshincomstop(tp, rw) 245153541Sshin struct tty *tp; 245253541Sshin int rw; 245353541Sshin{ 245453541Sshin struct com_s *com; 245553541Sshin 245653541Sshin com = com_addr(DEV_TO_UNIT(tp->t_dev)); 245753541Sshin if (com == NULL || com->gone) 245853541Sshin return; 245953541Sshin mtx_lock_spin(&sio_lock); 246053541Sshin if (rw & FWRITE) { 246153541Sshin if (com->hasfifo) 246253541Sshin#ifdef COM_ESP 246362587Sitojun /* XXX avoid h/w bug. */ 246462587Sitojun if (!com->esp) 246562587Sitojun#endif 2466111119Simp sio_setreg(com, com_fifo, 246762587Sitojun FIFO_XMT_RST | com->fifo_image); 2468111119Simp com->obufs[0].l_queued = FALSE; 246953541Sshin com->obufs[1].l_queued = FALSE; 247053541Sshin if (com->state & CS_ODONE) 247178064Sume com_events -= LOTS_OF_EVENTS; 247278064Sume com->state &= ~(CS_ODONE | CS_BUSY); 247378064Sume com->tp->t_state &= ~TS_BUSY; 247453541Sshin } 247553541Sshin if (rw & FREAD) { 247662587Sitojun if (com->hasfifo) 247762587Sitojun#ifdef COM_ESP 247853541Sshin /* XXX avoid h/w bug. */ 247962587Sitojun if (!com->esp) 248053541Sshin#endif 248153541Sshin sio_setreg(com, com_fifo, 248253541Sshin FIFO_RCV_RST | com->fifo_image); 248362587Sitojun com_events -= (com->iptr - com->ibuf); 248462587Sitojun com->iptr = com->ibuf; 248562587Sitojun } 248662587Sitojun mtx_unlock_spin(&sio_lock); 248753541Sshin comstart(tp); 248853541Sshin} 248953541Sshin 249053541Sshinstatic int 249153541Sshincommctl(com, bits, how) 249253541Sshin struct com_s *com; 249353541Sshin int bits; 249453541Sshin int how; 249553541Sshin{ 249653541Sshin int mcr; 249753541Sshin int msr; 249853541Sshin 249953541Sshin if (how == DMGET) { 250053541Sshin bits = TIOCM_LE; /* XXX - always enabled while open */ 250153541Sshin mcr = com->mcr_image; 250253541Sshin if (mcr & MCR_DTR) 250353541Sshin bits |= TIOCM_DTR; 250462587Sitojun if (mcr & MCR_RTS) 250562587Sitojun bits |= TIOCM_RTS; 250653541Sshin msr = com->prev_modem_status; 250753541Sshin if (msr & MSR_CTS) 250853541Sshin bits |= TIOCM_CTS; 250953541Sshin if (msr & MSR_DCD) 251053541Sshin bits |= TIOCM_CD; 251153541Sshin if (msr & MSR_DSR) 251253541Sshin bits |= TIOCM_DSR; 251353541Sshin /* 251453541Sshin * XXX - MSR_RI is naturally volatile, and we make MSR_TERI 251553541Sshin * more volatile by reading the modem status a lot. Perhaps 251653541Sshin * we should latch both bits until the status is read here. 251753541Sshin */ 251853541Sshin if (msr & (MSR_RI | MSR_TERI)) 251953541Sshin bits |= TIOCM_RI; 252053541Sshin return (bits); 252153541Sshin } 252253541Sshin mcr = 0; 252353541Sshin if (bits & TIOCM_DTR) 252453541Sshin mcr |= MCR_DTR; 252553541Sshin if (bits & TIOCM_RTS) 2526120892Sume mcr |= MCR_RTS; 252753541Sshin if (com->gone) 2528120892Sume return(0); 252953541Sshin mtx_lock_spin(&sio_lock); 253053541Sshin switch (how) { 253153541Sshin case DMSET: 2532120892Sume outb(com->modem_ctl_port, 253353541Sshin com->mcr_image = mcr | (com->mcr_image & MCR_IENABLE)); 2534120892Sume break; 253553541Sshin case DMBIS: 253653541Sshin outb(com->modem_ctl_port, com->mcr_image |= mcr); 253753541Sshin break; 253853541Sshin case DMBIC: 253953541Sshin outb(com->modem_ctl_port, com->mcr_image &= ~mcr); 254053541Sshin break; 254153541Sshin } 2542120892Sume mtx_unlock_spin(&sio_lock); 2543120892Sume return (0); 2544120892Sume} 2545120892Sume 2546120892Sumestatic void 2547120892Sumesiosettimeout() 2548120892Sume{ 254953541Sshin struct com_s *com; 2550120892Sume bool_t someopen; 2551120892Sume int unit; 2552120892Sume 2553120892Sume /* 2554120892Sume * Set our timeout period to 1 second if no polled devices are open. 2555120892Sume * Otherwise set it to max(1/200, 1/hz). 2556120892Sume * Enable timeouts iff some device is open. 2557120892Sume */ 2558120892Sume untimeout(comwakeup, (void *)NULL, sio_timeout_handle); 2559120892Sume sio_timeout = hz; 2560120892Sume someopen = FALSE; 2561120892Sume for (unit = 0; unit < sio_numunits; ++unit) { 2562120892Sume com = com_addr(unit); 2563120892Sume if (com != NULL && com->tp != NULL 2564120892Sume && com->tp->t_state & TS_ISOPEN && !com->gone) { 2565120892Sume someopen = TRUE; 2566120892Sume if (com->poll || com->poll_output) { 2567120892Sume sio_timeout = hz > 200 ? hz / 200 : 1; 2568120892Sume break; 2569120892Sume } 2570120893Sume } 257153541Sshin } 257253541Sshin if (someopen) { 257353541Sshin sio_timeouts_until_log = hz / sio_timeout; 257453541Sshin sio_timeout_handle = timeout(comwakeup, (void *)NULL, 257553541Sshin sio_timeout); 257662587Sitojun } else { 257753541Sshin /* Flush error messages, if any. */ 257853541Sshin sio_timeouts_until_log = 1; 257962587Sitojun comwakeup((void *)NULL); 258062587Sitojun untimeout(comwakeup, (void *)NULL, sio_timeout_handle); 258162587Sitojun } 258253541Sshin} 2583120891Sume 2584120891Sumestatic void 2585120891Sumecomwakeup(chan) 2586120891Sume void *chan; 258753541Sshin{ 2588120891Sume struct com_s *com; 2589120891Sume int unit; 2590120891Sume 2591120891Sume sio_timeout_handle = timeout(comwakeup, (void *)NULL, sio_timeout); 2592120891Sume 2593120891Sume /* 259453541Sshin * Recover from lost output interrupts. 2595120891Sume * Poll any lines that don't use interrupts. 2596120891Sume */ 2597120891Sume for (unit = 0; unit < sio_numunits; ++unit) { 2598120891Sume com = com_addr(unit); 2599120891Sume if (com != NULL && !com->gone 2600120891Sume && (com->state >= (CS_BUSY | CS_TTGO) || com->poll)) { 2601120891Sume mtx_lock_spin(&sio_lock); 260253541Sshin siointr1(com); 260353541Sshin mtx_unlock_spin(&sio_lock); 2604120891Sume } 2605120891Sume } 2606120891Sume 2607120891Sume /* 2608120891Sume * Check for and log errors, but not too often. 2609120891Sume */ 2610120891Sume if (--sio_timeouts_until_log > 0) 2611120891Sume return; 2612120891Sume sio_timeouts_until_log = hz / sio_timeout; 2613120891Sume for (unit = 0; unit < sio_numunits; ++unit) { 2614120891Sume int errnum; 2615120891Sume 2616120891Sume com = com_addr(unit); 2617120891Sume if (com == NULL) 261853541Sshin continue; 2619120891Sume if (com->gone) 2620120891Sume continue; 2621120891Sume for (errnum = 0; errnum < CE_NTYPES; ++errnum) { 2622120891Sume u_int delta; 2623120891Sume u_long total; 2624120891Sume 2625120891Sume mtx_lock_spin(&sio_lock); 262653541Sshin delta = com->delta_error_counts[errnum]; 2627120891Sume com->delta_error_counts[errnum] = 0; 2628120891Sume mtx_unlock_spin(&sio_lock); 2629120891Sume if (delta == 0) 2630120891Sume continue; 2631120891Sume total = com->error_counts[errnum] += delta; 2632120891Sume log(LOG_ERR, "sio%d: %u more %s%s (total %lu)\n", 2633120891Sume unit, delta, error_desc[errnum], 2634120891Sume delta == 1 ? "" : "s", total); 2635120891Sume } 2636120891Sume } 2637120891Sume} 2638120891Sume 263953541Sshinstatic void 2640120891Sumedisc_optim(tp, t, com) 2641120891Sume struct tty *tp; 2642120891Sume struct termios *t; 264353541Sshin struct com_s *com; 264453541Sshin{ 2645120891Sume if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON)) 2646120891Sume && (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK)) 2647120891Sume && (!(t->c_iflag & PARMRK) 2648120891Sume || (t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK)) 2649120891Sume && !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN)) 2650120891Sume && linesw[tp->t_line].l_rint == ttyinput) 265153541Sshin tp->t_state |= TS_CAN_BYPASS_L_RINT; 2652120891Sume else 2653120891Sume tp->t_state &= ~TS_CAN_BYPASS_L_RINT; 2654120891Sume com->hotchar = linesw[tp->t_line].l_hotchar; 2655120891Sume} 2656120891Sume 2657120891Sume/* 2658120891Sume * Following are all routines needed for SIO to act as console 265953541Sshin */ 2660112781Ssuz#include <sys/cons.h> 2661112781Ssuz 2662112781Ssuzstruct siocnstate { 2663112781Ssuz u_char dlbl; 266453541Sshin u_char dlbh; 2665121315Sume u_char ier; 2666121315Sume u_char cfcr; 2667121315Sume u_char mcr; 2668121315Sume}; 2669121315Sume 267053541Sshin#ifndef __alpha__ 267153541Sshinstatic speed_t siocngetspeed __P((Port_t, struct speedtab *)); 267253541Sshin#endif 267353541Sshinstatic void siocnclose __P((struct siocnstate *sp, Port_t iobase)); 2674120891Sumestatic void siocnopen __P((struct siocnstate *sp, Port_t iobase, int speed)); 2675120891Sumestatic void siocntxwait __P((Port_t iobase)); 267653541Sshin 267753541Sshin#ifdef __alpha__ 2678105194Ssamint siocnattach __P((int port, int speed)); 267953541Sshinint siogdbattach __P((int port, int speed)); 268053541Sshinint siogdbgetc __P((void)); 268153541Sshinvoid siogdbputc __P((int c)); 268253541Sshin#else 268353541Sshinstatic cn_probe_t siocnprobe; 268453541Sshinstatic cn_init_t siocninit; 268553541Sshinstatic cn_term_t siocnterm; 268653541Sshin#endif 268753541Sshinstatic cn_checkc_t siocncheckc; 268853541Sshinstatic cn_getc_t siocngetc; 268953541Sshinstatic cn_putc_t siocnputc; 269053541Sshin 269153541Sshin#ifndef __alpha__ 269253541SshinCONS_DRIVER(sio, siocnprobe, siocninit, siocnterm, siocngetc, siocncheckc, 269353541Sshin siocnputc, NULL); 269453541Sshin#endif 269553541Sshin 269653541Sshin/* To get the GDB related variables */ 269753541Sshin#if DDB > 0 269853541Sshin#include <ddb/ddb.h> 269953541Sshin#endif 270053541Sshin 270153541Sshinstatic void 270253541Sshinsiocntxwait(iobase) 270353541Sshin Port_t iobase; 270478064Sume{ 270553541Sshin int timo; 270653541Sshin 270753541Sshin /* 270853541Sshin * Wait for any pending transmission to finish. Required to avoid 270953541Sshin * the UART lockup bug when the speed is changed, and for normal 271053541Sshin * transmits. 271153541Sshin */ 271253541Sshin timo = 100000; 271353541Sshin while ((inb(iobase + com_lsr) & (LSR_TSRE | LSR_TXRDY)) 271478064Sume != (LSR_TSRE | LSR_TXRDY) && --timo != 0) 271553541Sshin ; 271653541Sshin} 271753541Sshin 271853541Sshin#ifndef __alpha__ 271978064Sume 272053541Sshin/* 272153541Sshin * Read the serial port specified and try to figure out what speed 272253541Sshin * it's currently running at. We're assuming the serial port has 272353541Sshin * been initialized and is basicly idle. This routine is only intended 272453541Sshin * to be run at system startup. 272553541Sshin * 272653541Sshin * If the value read from the serial port doesn't make sense, return 0. 272753541Sshin */ 272853541Sshin 272953541Sshinstatic speed_t 273053541Sshinsiocngetspeed(iobase, table) 273153541Sshin Port_t iobase; 273253541Sshin struct speedtab *table; 273353541Sshin{ 273453541Sshin int code; 273553541Sshin u_char dlbh; 273653541Sshin u_char dlbl; 273753541Sshin u_char cfcr; 273853541Sshin 273953541Sshin cfcr = inb(iobase + com_cfcr); 274053541Sshin outb(iobase + com_cfcr, CFCR_DLAB | cfcr); 274153541Sshin 274253541Sshin dlbl = inb(iobase + com_dlbl); 274353541Sshin dlbh = inb(iobase + com_dlbh); 274453541Sshin 274553541Sshin outb(iobase + com_cfcr, cfcr); 274653541Sshin 274753541Sshin code = dlbh << 8 | dlbl; 274853541Sshin 274953541Sshin for (; table->sp_speed != -1; table++) 275053541Sshin if (table->sp_code == code) 275153541Sshin return (table->sp_speed); 275253541Sshin 275353541Sshin return (0); /* didn't match anything sane */ 275453541Sshin} 275553541Sshin 275653541Sshin#endif 275753541Sshin 275853541Sshinstatic void 275953541Sshinsiocnopen(sp, iobase, speed) 276053541Sshin struct siocnstate *sp; 276153541Sshin Port_t iobase; 276253541Sshin int speed; 276353541Sshin{ 276453541Sshin int divisor; 2765120856Sume u_char dlbh; 276653541Sshin u_char dlbl; 276753541Sshin 276853541Sshin /* 276953541Sshin * Save all the device control registers except the fifo register 277053541Sshin * and set our default ones (cs8 -parenb speed=comdefaultrate). 277153541Sshin * We can't save the fifo register since it is read-only. 277253541Sshin */ 277353541Sshin sp->ier = inb(iobase + com_ier); 277453541Sshin outb(iobase + com_ier, 0); /* spltty() doesn't stop siointr() */ 277553541Sshin siocntxwait(iobase); 277653541Sshin sp->cfcr = inb(iobase + com_cfcr); 277753541Sshin outb(iobase + com_cfcr, CFCR_DLAB | CFCR_8BITS); 277853541Sshin sp->dlbl = inb(iobase + com_dlbl); 277953541Sshin sp->dlbh = inb(iobase + com_dlbh); 278053541Sshin /* 278153541Sshin * Only set the divisor registers if they would change, since on 278262587Sitojun * some 16550 incompatibles (Startech), setting them clears the 278353541Sshin * data input register. This also reduces the effects of the 278495023Ssuz * UMC8669F bug. 278553541Sshin */ 278662587Sitojun divisor = ttspeedtab(speed, comspeedtab); 278778064Sume dlbl = divisor & 0xFF; 278878064Sume if (sp->dlbl != dlbl) 278953541Sshin outb(iobase + com_dlbl, dlbl); 279062587Sitojun dlbh = (u_int) divisor >> 8; 279153541Sshin if (sp->dlbh != dlbh) 279253541Sshin outb(iobase + com_dlbh, dlbh); 279362587Sitojun outb(iobase + com_cfcr, CFCR_8BITS); 279453541Sshin sp->mcr = inb(iobase + com_mcr); 2795 /* 2796 * We don't want interrupts, but must be careful not to "disable" 2797 * them by clearing the MCR_IENABLE bit, since that might cause 2798 * an interrupt by floating the IRQ line. 2799 */ 2800 outb(iobase + com_mcr, (sp->mcr & MCR_IENABLE) | MCR_DTR | MCR_RTS); 2801} 2802 2803static void 2804siocnclose(sp, iobase) 2805 struct siocnstate *sp; 2806 Port_t iobase; 2807{ 2808 /* 2809 * Restore the device control registers. 2810 */ 2811 siocntxwait(iobase); 2812 outb(iobase + com_cfcr, CFCR_DLAB | CFCR_8BITS); 2813 if (sp->dlbl != inb(iobase + com_dlbl)) 2814 outb(iobase + com_dlbl, sp->dlbl); 2815 if (sp->dlbh != inb(iobase + com_dlbh)) 2816 outb(iobase + com_dlbh, sp->dlbh); 2817 outb(iobase + com_cfcr, sp->cfcr); 2818 /* 2819 * XXX damp oscillations of MCR_DTR and MCR_RTS by not restoring them. 2820 */ 2821 outb(iobase + com_mcr, sp->mcr | MCR_DTR | MCR_RTS); 2822 outb(iobase + com_ier, sp->ier); 2823} 2824 2825#ifndef __alpha__ 2826 2827static void 2828siocnprobe(cp) 2829 struct consdev *cp; 2830{ 2831 speed_t boot_speed; 2832 u_char cfcr; 2833 int s, unit; 2834 struct siocnstate sp; 2835 2836 /* 2837 * Find our first enabled console, if any. If it is a high-level 2838 * console device, then initialize it and return successfully. 2839 * If it is a low-level console device, then initialize it and 2840 * return unsuccessfully. It must be initialized in both cases 2841 * for early use by console drivers and debuggers. Initializing 2842 * the hardware is not necessary in all cases, since the i/o 2843 * routines initialize it on the fly, but it is necessary if 2844 * input might arrive while the hardware is switched back to an 2845 * uninitialized state. We can't handle multiple console devices 2846 * yet because our low-level routines don't take a device arg. 2847 * We trust the user to set the console flags properly so that we 2848 * don't need to probe. 2849 */ 2850 cp->cn_pri = CN_DEAD; 2851 2852 for (unit = 0; unit < 16; unit++) { /* XXX need to know how many */ 2853 int flags; 2854 int disabled; 2855 if (resource_int_value("sio", unit, "disabled", &disabled) == 0) { 2856 if (disabled) 2857 continue; 2858 } 2859 if (resource_int_value("sio", unit, "flags", &flags)) 2860 continue; 2861 if (COM_CONSOLE(flags) || COM_DEBUGGER(flags)) { 2862 int port; 2863 Port_t iobase; 2864 2865 if (resource_int_value("sio", unit, "port", &port)) 2866 continue; 2867 iobase = port; 2868 s = spltty(); 2869 if (boothowto & RB_SERIAL) { 2870 boot_speed = siocngetspeed(iobase, comspeedtab); 2871 if (boot_speed) 2872 comdefaultrate = boot_speed; 2873 } 2874 2875 /* 2876 * Initialize the divisor latch. We can't rely on 2877 * siocnopen() to do this the first time, since it 2878 * avoids writing to the latch if the latch appears 2879 * to have the correct value. Also, if we didn't 2880 * just read the speed from the hardware, then we 2881 * need to set the speed in hardware so that 2882 * switching it later is null. 2883 */ 2884 cfcr = inb(iobase + com_cfcr); 2885 outb(iobase + com_cfcr, CFCR_DLAB | cfcr); 2886 outb(iobase + com_dlbl, 2887 COMBRD(comdefaultrate) & 0xff); 2888 outb(iobase + com_dlbh, 2889 (u_int) COMBRD(comdefaultrate) >> 8); 2890 outb(iobase + com_cfcr, cfcr); 2891 2892 siocnopen(&sp, iobase, comdefaultrate); 2893 2894 splx(s); 2895 if (COM_CONSOLE(flags) && !COM_LLCONSOLE(flags)) { 2896 cp->cn_dev = makedev(CDEV_MAJOR, unit); 2897 cp->cn_pri = COM_FORCECONSOLE(flags) 2898 || boothowto & RB_SERIAL 2899 ? CN_REMOTE : CN_NORMAL; 2900 siocniobase = iobase; 2901 siocnunit = unit; 2902 } 2903 if (COM_DEBUGGER(flags)) { 2904 printf("sio%d: gdb debugging port\n", unit); 2905 siogdbiobase = iobase; 2906 siogdbunit = unit; 2907#if DDB > 0 2908 gdbdev = makedev(CDEV_MAJOR, unit); 2909 gdb_getc = siocngetc; 2910 gdb_putc = siocnputc; 2911#endif 2912 } 2913 } 2914 } 2915#ifdef __i386__ 2916#if DDB > 0 2917 /* 2918 * XXX Ugly Compatability. 2919 * If no gdb port has been specified, set it to be the console 2920 * as some configuration files don't specify the gdb port. 2921 */ 2922 if (gdbdev == NODEV && (boothowto & RB_GDB)) { 2923 printf("Warning: no GDB port specified. Defaulting to sio%d.\n", 2924 siocnunit); 2925 printf("Set flag 0x80 on desired GDB port in your\n"); 2926 printf("configuration file (currently sio only).\n"); 2927 siogdbiobase = siocniobase; 2928 siogdbunit = siocnunit; 2929 gdbdev = makedev(CDEV_MAJOR, siocnunit); 2930 gdb_getc = siocngetc; 2931 gdb_putc = siocnputc; 2932 } 2933#endif 2934#endif 2935} 2936 2937static void 2938siocninit(cp) 2939 struct consdev *cp; 2940{ 2941 comconsole = DEV_TO_UNIT(cp->cn_dev); 2942} 2943 2944static void 2945siocnterm(cp) 2946 struct consdev *cp; 2947{ 2948 comconsole = -1; 2949} 2950 2951#endif 2952 2953#ifdef __alpha__ 2954 2955CONS_DRIVER(sio, NULL, NULL, NULL, siocngetc, siocncheckc, siocnputc, NULL); 2956 2957int 2958siocnattach(port, speed) 2959 int port; 2960 int speed; 2961{ 2962 int s; 2963 u_char cfcr; 2964 struct siocnstate sp; 2965 2966 siocniobase = port; 2967 comdefaultrate = speed; 2968 sio_consdev.cn_pri = CN_NORMAL; 2969 sio_consdev.cn_dev = makedev(CDEV_MAJOR, 0); 2970 2971 s = spltty(); 2972 2973 /* 2974 * Initialize the divisor latch. We can't rely on 2975 * siocnopen() to do this the first time, since it 2976 * avoids writing to the latch if the latch appears 2977 * to have the correct value. Also, if we didn't 2978 * just read the speed from the hardware, then we 2979 * need to set the speed in hardware so that 2980 * switching it later is null. 2981 */ 2982 cfcr = inb(siocniobase + com_cfcr); 2983 outb(siocniobase + com_cfcr, CFCR_DLAB | cfcr); 2984 outb(siocniobase + com_dlbl, 2985 COMBRD(comdefaultrate) & 0xff); 2986 outb(siocniobase + com_dlbh, 2987 (u_int) COMBRD(comdefaultrate) >> 8); 2988 outb(siocniobase + com_cfcr, cfcr); 2989 2990 siocnopen(&sp, siocniobase, comdefaultrate); 2991 splx(s); 2992 2993 cnadd(&sio_consdev); 2994 return (0); 2995} 2996 2997int 2998siogdbattach(port, speed) 2999 int port; 3000 int speed; 3001{ 3002 int s; 3003 u_char cfcr; 3004 struct siocnstate sp; 3005 int unit = 1; /* XXX !!! */ 3006 3007 siogdbiobase = port; 3008 gdbdefaultrate = speed; 3009 3010 printf("sio%d: gdb debugging port\n", unit); 3011 siogdbunit = unit; 3012#if DDB > 0 3013 gdbdev = makedev(CDEV_MAJOR, unit); 3014 gdb_getc = siocngetc; 3015 gdb_putc = siocnputc; 3016#endif 3017 3018 s = spltty(); 3019 3020 /* 3021 * Initialize the divisor latch. We can't rely on 3022 * siocnopen() to do this the first time, since it 3023 * avoids writing to the latch if the latch appears 3024 * to have the correct value. Also, if we didn't 3025 * just read the speed from the hardware, then we 3026 * need to set the speed in hardware so that 3027 * switching it later is null. 3028 */ 3029 cfcr = inb(siogdbiobase + com_cfcr); 3030 outb(siogdbiobase + com_cfcr, CFCR_DLAB | cfcr); 3031 outb(siogdbiobase + com_dlbl, 3032 COMBRD(gdbdefaultrate) & 0xff); 3033 outb(siogdbiobase + com_dlbh, 3034 (u_int) COMBRD(gdbdefaultrate) >> 8); 3035 outb(siogdbiobase + com_cfcr, cfcr); 3036 3037 siocnopen(&sp, siogdbiobase, gdbdefaultrate); 3038 splx(s); 3039 3040 return (0); 3041} 3042 3043#endif 3044 3045static int 3046siocncheckc(dev) 3047 dev_t dev; 3048{ 3049 int c; 3050 Port_t iobase; 3051 int s; 3052 struct siocnstate sp; 3053 3054 if (minor(dev) == siogdbunit) 3055 iobase = siogdbiobase; 3056 else 3057 iobase = siocniobase; 3058 s = spltty(); 3059 siocnopen(&sp, iobase, comdefaultrate); 3060 if (inb(iobase + com_lsr) & LSR_RXRDY) 3061 c = inb(iobase + com_data); 3062 else 3063 c = -1; 3064 siocnclose(&sp, iobase); 3065 splx(s); 3066 return (c); 3067} 3068 3069 3070int 3071siocngetc(dev) 3072 dev_t dev; 3073{ 3074 int c; 3075 Port_t iobase; 3076 int s; 3077 struct siocnstate sp; 3078 3079 if (minor(dev) == siogdbunit) 3080 iobase = siogdbiobase; 3081 else 3082 iobase = siocniobase; 3083 s = spltty(); 3084 siocnopen(&sp, iobase, comdefaultrate); 3085 while (!(inb(iobase + com_lsr) & LSR_RXRDY)) 3086 ; 3087 c = inb(iobase + com_data); 3088 siocnclose(&sp, iobase); 3089 splx(s); 3090 return (c); 3091} 3092 3093void 3094siocnputc(dev, c) 3095 dev_t dev; 3096 int c; 3097{ 3098 int need_unlock; 3099 int s; 3100 struct siocnstate sp; 3101 Port_t iobase; 3102 3103 if (minor(dev) == siogdbunit) 3104 iobase = siogdbiobase; 3105 else 3106 iobase = siocniobase; 3107 s = spltty(); 3108 need_unlock = 0; 3109 if (sio_inited == 2 && !mtx_owned(&sio_lock)) { 3110 mtx_lock_spin(&sio_lock); 3111 need_unlock = 1; 3112 } 3113 siocnopen(&sp, iobase, comdefaultrate); 3114 siocntxwait(iobase); 3115 outb(iobase + com_data, c); 3116 siocnclose(&sp, iobase); 3117 if (need_unlock) 3118 mtx_unlock_spin(&sio_lock); 3119 splx(s); 3120} 3121 3122#ifdef __alpha__ 3123int 3124siogdbgetc() 3125{ 3126 int c; 3127 Port_t iobase; 3128 int s; 3129 struct siocnstate sp; 3130 3131 iobase = siogdbiobase; 3132 s = spltty(); 3133 siocnopen(&sp, iobase, gdbdefaultrate); 3134 while (!(inb(iobase + com_lsr) & LSR_RXRDY)) 3135 ; 3136 c = inb(iobase + com_data); 3137 siocnclose(&sp, iobase); 3138 splx(s); 3139 return (c); 3140} 3141 3142void 3143siogdbputc(c) 3144 int c; 3145{ 3146 int s; 3147 struct siocnstate sp; 3148 3149 s = spltty(); 3150 siocnopen(&sp, siogdbiobase, gdbdefaultrate); 3151 siocntxwait(siogdbiobase); 3152 outb(siogdbiobase + com_data, c); 3153 siocnclose(&sp, siogdbiobase); 3154 splx(s); 3155} 3156#endif 3157