sio.c revision 59888
151078Speter/*- 251078Speter * Copyright (c) 1991 The Regents of the University of California. 351078Speter * All rights reserved. 451078Speter * 551078Speter * Redistribution and use in source and binary forms, with or without 651078Speter * modification, are permitted provided that the following conditions 751078Speter * are met: 851078Speter * 1. Redistributions of source code must retain the above copyright 951078Speter * notice, this list of conditions and the following disclaimer. 1051078Speter * 2. Redistributions in binary form must reproduce the above copyright 1151078Speter * notice, this list of conditions and the following disclaimer in the 1251078Speter * documentation and/or other materials provided with the distribution. 1351078Speter * 3. All advertising materials mentioning features or use of this software 1451078Speter * must display the following acknowledgement: 1551078Speter * This product includes software developed by the University of 1651078Speter * California, Berkeley and its contributors. 1751078Speter * 4. Neither the name of the University nor the names of its contributors 1851078Speter * may be used to endorse or promote products derived from this software 1951078Speter * without specific prior written permission. 2051078Speter * 2151078Speter * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2251078Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2351078Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2451078Speter * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2551078Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2651078Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2751078Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2851078Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2951078Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3051078Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3151078Speter * SUCH DAMAGE. 3251078Speter * 3351078Speter * $FreeBSD: head/sys/dev/sio/sio.c 59888 2000-05-02 05:54:11Z tanimura $ 3451078Speter * from: @(#)com.c 7.5 (Berkeley) 5/16/91 3551078Speter * from: i386/isa sio.c,v 1.234 3651078Speter */ 3751078Speter 3851078Speter#include "opt_comconsole.h" 3951078Speter#include "opt_compat.h" 4051078Speter#include "opt_ddb.h" 4151078Speter#include "opt_sio.h" 4254386Simp#include "card.h" 4358885Simp#include "pci.h" 4451078Speter#include "sio.h" 4551078Speter 4651078Speter/* 4751078Speter * Serial driver, based on 386BSD-0.1 com driver. 4851078Speter * Mostly rewritten to use pseudo-DMA. 4951078Speter * Works for National Semiconductor NS8250-NS16550AF UARTs. 5051078Speter * COM driver, based on HP dca driver. 5151078Speter * 5251078Speter * Changes for PC-Card integration: 5351078Speter * - Added PC-Card driver table and handlers 5451078Speter */ 5551078Speter#include <sys/param.h> 5651078Speter#include <sys/systm.h> 5751078Speter#include <sys/reboot.h> 5851078Speter#include <sys/malloc.h> 5951078Speter#include <sys/tty.h> 6051078Speter#include <sys/proc.h> 6151078Speter#include <sys/module.h> 6251078Speter#include <sys/conf.h> 6351078Speter#include <sys/dkstat.h> 6451078Speter#include <sys/fcntl.h> 6551078Speter#include <sys/interrupt.h> 6651078Speter#include <sys/kernel.h> 6751078Speter#include <sys/syslog.h> 6851078Speter#include <sys/sysctl.h> 6951078Speter#include <sys/bus.h> 7051078Speter#include <machine/bus.h> 7151078Speter#include <sys/rman.h> 7258377Sphk#include <sys/timetc.h> 7351078Speter#include <sys/timepps.h> 7451078Speter 7551078Speter#include <isa/isareg.h> 7651078Speter#include <isa/isavar.h> 7758885Simp#if NPCI > 0 7858885Simp#include <pci/pcireg.h> 7958885Simp#include <pci/pcivar.h> 8058885Simp#endif 8151078Speter#include <machine/lock.h> 8251078Speter 8351078Speter#include <machine/clock.h> 8451078Speter#include <machine/ipl.h> 8551078Speter#ifndef SMP 8651078Speter#include <machine/lock.h> 8751078Speter#endif 8851078Speter#include <machine/resource.h> 8951078Speter 9051078Speter#include <isa/sioreg.h> 9151078Speter 9251078Speter#ifdef COM_ESP 9351078Speter#include <isa/ic/esp.h> 9451078Speter#endif 9551078Speter#include <isa/ic/ns16550.h> 9651078Speter 9751078Speter#ifndef __i386__ 9851078Speter#define disable_intr() 9951078Speter#define enable_intr() 10051078Speter#endif 10151078Speter 10251078Speter#ifdef SMP 10351078Speter#define disable_intr() COM_DISABLE_INTR() 10451078Speter#define enable_intr() COM_ENABLE_INTR() 10551078Speter#endif /* SMP */ 10651078Speter 10751078Speter#define LOTS_OF_EVENTS 64 /* helps separate urgent events from input */ 10851078Speter 10951078Speter#define CALLOUT_MASK 0x80 11051078Speter#define CONTROL_MASK 0x60 11151078Speter#define CONTROL_INIT_STATE 0x20 11251078Speter#define CONTROL_LOCK_STATE 0x40 11351078Speter#define DEV_TO_UNIT(dev) (MINOR_TO_UNIT(minor(dev))) 11451078Speter#define MINOR_MAGIC_MASK (CALLOUT_MASK | CONTROL_MASK) 11551078Speter#define MINOR_TO_UNIT(mynor) ((mynor) & ~MINOR_MAGIC_MASK) 11651078Speter 11751078Speter#ifdef COM_MULTIPORT 11851078Speter/* checks in flags for multiport and which is multiport "master chip" 11951078Speter * for a given card 12051078Speter */ 12151078Speter#define COM_ISMULTIPORT(flags) ((flags) & 0x01) 12251078Speter#define COM_MPMASTER(flags) (((flags) >> 8) & 0x0ff) 12351078Speter#define COM_NOTAST4(flags) ((flags) & 0x04) 12451078Speter#endif /* COM_MULTIPORT */ 12551078Speter 12651078Speter#define COM_CONSOLE(flags) ((flags) & 0x10) 12751078Speter#define COM_FORCECONSOLE(flags) ((flags) & 0x20) 12851078Speter#define COM_LLCONSOLE(flags) ((flags) & 0x40) 12951078Speter#define COM_DEBUGGER(flags) ((flags) & 0x80) 13051078Speter#define COM_LOSESOUTINTS(flags) ((flags) & 0x08) 13151078Speter#define COM_NOFIFO(flags) ((flags) & 0x02) 13251078Speter#define COM_ST16650A(flags) ((flags) & 0x20000) 13351078Speter#define COM_C_NOPROBE (0x40000) 13451078Speter#define COM_NOPROBE(flags) ((flags) & COM_C_NOPROBE) 13551078Speter#define COM_C_IIR_TXRDYBUG (0x80000) 13651078Speter#define COM_IIR_TXRDYBUG(flags) ((flags) & COM_C_IIR_TXRDYBUG) 13751078Speter#define COM_FIFOSIZE(flags) (((flags) & 0xff000000) >> 24) 13851078Speter 13951078Speter#define com_scr 7 /* scratch register for 16450-16550 (R/W) */ 14051078Speter 14151078Speter/* 14251078Speter * com state bits. 14351078Speter * (CS_BUSY | CS_TTGO) and (CS_BUSY | CS_TTGO | CS_ODEVREADY) must be higher 14451078Speter * than the other bits so that they can be tested as a group without masking 14551078Speter * off the low bits. 14651078Speter * 14751078Speter * The following com and tty flags correspond closely: 14851078Speter * CS_BUSY = TS_BUSY (maintained by comstart(), siopoll() and 14953344Speter * comstop()) 15051078Speter * CS_TTGO = ~TS_TTSTOP (maintained by comparam() and comstart()) 15151078Speter * CS_CTS_OFLOW = CCTS_OFLOW (maintained by comparam()) 15251078Speter * CS_RTS_IFLOW = CRTS_IFLOW (maintained by comparam()) 15351078Speter * TS_FLUSH is not used. 15451078Speter * XXX I think TIOCSETA doesn't clear TS_TTSTOP when it clears IXON. 15551078Speter * XXX CS_*FLOW should be CF_*FLOW in com->flags (control flags not state). 15651078Speter */ 15751078Speter#define CS_BUSY 0x80 /* output in progress */ 15851078Speter#define CS_TTGO 0x40 /* output not stopped by XOFF */ 15951078Speter#define CS_ODEVREADY 0x20 /* external device h/w ready (CTS) */ 16051078Speter#define CS_CHECKMSR 1 /* check of MSR scheduled */ 16151078Speter#define CS_CTS_OFLOW 2 /* use CTS output flow control */ 16251078Speter#define CS_DTR_OFF 0x10 /* DTR held off */ 16351078Speter#define CS_ODONE 4 /* output completed */ 16451078Speter#define CS_RTS_IFLOW 8 /* use RTS input flow control */ 16551078Speter#define CSE_BUSYCHECK 1 /* siobusycheck() scheduled */ 16651078Speter 16751078Speterstatic char const * const error_desc[] = { 16851078Speter#define CE_OVERRUN 0 16951078Speter "silo overflow", 17051078Speter#define CE_INTERRUPT_BUF_OVERFLOW 1 17151078Speter "interrupt-level buffer overflow", 17251078Speter#define CE_TTY_BUF_OVERFLOW 2 17351078Speter "tty-level buffer overflow", 17451078Speter}; 17551078Speter 17651078Speter#define CE_NTYPES 3 17751078Speter#define CE_RECORD(com, errnum) (++(com)->delta_error_counts[errnum]) 17851078Speter 17951078Speter/* types. XXX - should be elsewhere */ 18051078Spetertypedef u_int Port_t; /* hardware port */ 18151078Spetertypedef u_char bool_t; /* boolean */ 18251078Speter 18351078Speter/* queue of linear buffers */ 18451078Speterstruct lbq { 18551078Speter u_char *l_head; /* next char to process */ 18651078Speter u_char *l_tail; /* one past the last char to process */ 18751078Speter struct lbq *l_next; /* next in queue */ 18851078Speter bool_t l_queued; /* nonzero if queued */ 18951078Speter}; 19051078Speter 19151078Speter/* com device structure */ 19251078Speterstruct com_s { 19351078Speter u_int flags; /* Copy isa device flags */ 19451078Speter u_char state; /* miscellaneous flag bits */ 19551078Speter bool_t active_out; /* nonzero if the callout device is open */ 19651078Speter u_char cfcr_image; /* copy of value written to CFCR */ 19751078Speter#ifdef COM_ESP 19851078Speter bool_t esp; /* is this unit a hayes esp board? */ 19951078Speter#endif 20051078Speter u_char extra_state; /* more flag bits, separate for order trick */ 20151078Speter u_char fifo_image; /* copy of value written to FIFO */ 20251078Speter bool_t hasfifo; /* nonzero for 16550 UARTs */ 20351078Speter bool_t st16650a; /* Is a Startech 16650A or RTS/CTS compat */ 20451078Speter bool_t loses_outints; /* nonzero if device loses output interrupts */ 20551078Speter u_char mcr_image; /* copy of value written to MCR */ 20651078Speter#ifdef COM_MULTIPORT 20751078Speter bool_t multiport; /* is this unit part of a multiport device? */ 20851078Speter#endif /* COM_MULTIPORT */ 20951078Speter bool_t no_irq; /* nonzero if irq is not attached */ 21051078Speter bool_t gone; /* hardware disappeared */ 21151078Speter bool_t poll; /* nonzero if polling is required */ 21251078Speter bool_t poll_output; /* nonzero if polling for output is required */ 21351078Speter int unit; /* unit number */ 21451078Speter int dtr_wait; /* time to hold DTR down on close (* 1/hz) */ 21551078Speter u_int tx_fifo_size; 21651078Speter u_int wopeners; /* # processes waiting for DCD in open() */ 21751078Speter 21851078Speter /* 21951078Speter * The high level of the driver never reads status registers directly 22051078Speter * because there would be too many side effects to handle conveniently. 22151078Speter * Instead, it reads copies of the registers stored here by the 22251078Speter * interrupt handler. 22351078Speter */ 22451078Speter u_char last_modem_status; /* last MSR read by intr handler */ 22551078Speter u_char prev_modem_status; /* last MSR handled by high level */ 22651078Speter 22751078Speter u_char hotchar; /* ldisc-specific char to be handled ASAP */ 22851078Speter u_char *ibuf; /* start of input buffer */ 22951078Speter u_char *ibufend; /* end of input buffer */ 23051078Speter u_char *ibufold; /* old input buffer, to be freed */ 23151078Speter u_char *ihighwater; /* threshold in input buffer */ 23251078Speter u_char *iptr; /* next free spot in input buffer */ 23351078Speter int ibufsize; /* size of ibuf (not include error bytes) */ 23451078Speter int ierroff; /* offset of error bytes in ibuf */ 23551078Speter 23651078Speter struct lbq obufq; /* head of queue of output buffers */ 23751078Speter struct lbq obufs[2]; /* output buffers */ 23851078Speter 23951078Speter Port_t data_port; /* i/o ports */ 24051078Speter#ifdef COM_ESP 24151078Speter Port_t esp_port; 24251078Speter#endif 24351078Speter Port_t int_id_port; 24451078Speter Port_t iobase; 24551078Speter Port_t modem_ctl_port; 24651078Speter Port_t line_status_port; 24751078Speter Port_t modem_status_port; 24851078Speter Port_t intr_ctl_port; /* Ports of IIR register */ 24951078Speter 25051078Speter struct tty *tp; /* cross reference */ 25151078Speter 25251078Speter /* Initial state. */ 25351078Speter struct termios it_in; /* should be in struct tty */ 25451078Speter struct termios it_out; 25551078Speter 25651078Speter /* Lock state. */ 25751078Speter struct termios lt_in; /* should be in struct tty */ 25851078Speter struct termios lt_out; 25951078Speter 26051078Speter bool_t do_timestamp; 26151078Speter bool_t do_dcd_timestamp; 26251078Speter struct timeval timestamp; 26351078Speter struct timeval dcd_timestamp; 26451078Speter struct pps_state pps; 26551078Speter 26651078Speter u_long bytes_in; /* statistics */ 26751078Speter u_long bytes_out; 26851078Speter u_int delta_error_counts[CE_NTYPES]; 26951078Speter u_long error_counts[CE_NTYPES]; 27051078Speter 27151078Speter struct resource *irqres; 27251078Speter struct resource *ioportres; 27354386Simp void *cookie; 27451078Speter 27551078Speter /* 27651078Speter * Data area for output buffers. Someday we should build the output 27751078Speter * buffer queue without copying data. 27851078Speter */ 27951078Speter u_char obuf1[256]; 28051078Speter u_char obuf2[256]; 28151078Speter}; 28251078Speter 28351078Speter#ifdef COM_ESP 28451078Speterstatic int espattach __P((struct com_s *com, Port_t esp_port)); 28551078Speter#endif 28658885Simpstatic int sioattach __P((device_t dev, int rid)); 28752471Simpstatic int sio_isa_attach __P((device_t dev)); 28851078Speter 28951078Speterstatic timeout_t siobusycheck; 29051078Speterstatic timeout_t siodtrwakeup; 29151078Speterstatic void comhardclose __P((struct com_s *com)); 29251078Speterstatic void sioinput __P((struct com_s *com)); 29351078Speterstatic void siointr1 __P((struct com_s *com)); 29451078Speterstatic void siointr __P((void *arg)); 29551078Speterstatic int commctl __P((struct com_s *com, int bits, int how)); 29651078Speterstatic int comparam __P((struct tty *tp, struct termios *t)); 29751078Speterstatic swihand_t siopoll; 29858885Simpstatic int sioprobe __P((device_t dev, int xrid)); 29952471Simpstatic int sio_isa_probe __P((device_t dev)); 30051078Speterstatic void siosettimeout __P((void)); 30151078Speterstatic int siosetwater __P((struct com_s *com, speed_t speed)); 30251078Speterstatic void comstart __P((struct tty *tp)); 30351654Sphkstatic void comstop __P((struct tty *tp, int rw)); 30451078Speterstatic timeout_t comwakeup; 30551078Speterstatic void disc_optim __P((struct tty *tp, struct termios *t, 30651078Speter struct com_s *com)); 30751078Speter 30852471Simp#if NCARD > 0 30952471Simpstatic int sio_pccard_attach __P((device_t dev)); 31054386Simpstatic int sio_pccard_detach __P((device_t dev)); 31152471Simpstatic int sio_pccard_probe __P((device_t dev)); 31252471Simp#endif /* NCARD > 0 */ 31351078Speter 31458885Simp#if NPCI > 0 31558885Simpstatic int sio_pci_attach __P((device_t dev)); 31658885Simpstatic void sio_pci_kludge_unit __P((device_t dev)); 31758885Simpstatic int sio_pci_probe __P((device_t dev)); 31858885Simp#endif /* NPCI > 0 */ 31958885Simp 32051078Speterstatic char driver_name[] = "sio"; 32151078Speter 32251078Speter/* table and macro for fast conversion from a unit number to its com struct */ 32351078Speterstatic devclass_t sio_devclass; 32451078Speter#define com_addr(unit) ((struct com_s *) \ 32551078Speter devclass_get_softc(sio_devclass, unit)) 32651078Speter 32752471Simpstatic device_method_t sio_isa_methods[] = { 32851078Speter /* Device interface */ 32952471Simp DEVMETHOD(device_probe, sio_isa_probe), 33052471Simp DEVMETHOD(device_attach, sio_isa_attach), 33151078Speter 33251078Speter { 0, 0 } 33351078Speter}; 33451078Speter 33552471Simpstatic driver_t sio_isa_driver = { 33651078Speter driver_name, 33752471Simp sio_isa_methods, 33851078Speter sizeof(struct com_s), 33951078Speter}; 34051078Speter 34152471Simp#if NCARD > 0 34252471Simpstatic device_method_t sio_pccard_methods[] = { 34352471Simp /* Device interface */ 34452471Simp DEVMETHOD(device_probe, sio_pccard_probe), 34552471Simp DEVMETHOD(device_attach, sio_pccard_attach), 34652471Simp DEVMETHOD(device_detach, sio_pccard_detach), 34752471Simp 34852471Simp { 0, 0 } 34952471Simp}; 35052471Simp 35152471Simpstatic driver_t sio_pccard_driver = { 35252471Simp driver_name, 35352471Simp sio_pccard_methods, 35452471Simp sizeof(struct com_s), 35552471Simp}; 35658885Simp#endif /* NCARD > 0 */ 35752471Simp 35858885Simp#if NPCI > 0 35958885Simpstatic device_method_t sio_pci_methods[] = { 36058885Simp /* Device interface */ 36158885Simp DEVMETHOD(device_probe, sio_pci_probe), 36258885Simp DEVMETHOD(device_attach, sio_pci_attach), 36358885Simp 36458885Simp { 0, 0 } 36558885Simp}; 36658885Simp 36758885Simpstatic driver_t sio_pci_driver = { 36858885Simp driver_name, 36958885Simp sio_pci_methods, 37058885Simp sizeof(struct com_s), 37158885Simp}; 37258885Simp#endif /* NPCI > 0 */ 37358885Simp 37451078Speterstatic d_open_t sioopen; 37551078Speterstatic d_close_t sioclose; 37651078Speterstatic d_read_t sioread; 37751078Speterstatic d_write_t siowrite; 37851078Speterstatic d_ioctl_t sioioctl; 37951078Speter 38051078Speter#define CDEV_MAJOR 28 38151078Speterstatic struct cdevsw sio_cdevsw = { 38251078Speter /* open */ sioopen, 38351078Speter /* close */ sioclose, 38451078Speter /* read */ sioread, 38551078Speter /* write */ siowrite, 38651078Speter /* ioctl */ sioioctl, 38751654Sphk /* poll */ ttypoll, 38851078Speter /* mmap */ nommap, 38951078Speter /* strategy */ nostrategy, 39051078Speter /* name */ driver_name, 39151078Speter /* maj */ CDEV_MAJOR, 39251078Speter /* dump */ nodump, 39351078Speter /* psize */ nopsize, 39451078Speter /* flags */ D_TTY, 39551078Speter /* bmaj */ -1 39651078Speter}; 39751078Speter 39851078Speterint comconsole = -1; 39951078Speterstatic volatile speed_t comdefaultrate = CONSPEED; 40051078Speter#ifdef __alpha__ 40151078Speterstatic volatile speed_t gdbdefaultrate = CONSPEED; 40251078Speter#endif 40351078Speterstatic u_int com_events; /* input chars + weighted output completions */ 40451078Speterstatic Port_t siocniobase; 40551078Speterstatic int siocnunit; 40651078Speterstatic Port_t siogdbiobase; 40751078Speterstatic int siogdbunit = -1; 40851078Speterstatic bool_t sio_registered; 40951078Speterstatic int sio_timeout; 41051078Speterstatic int sio_timeouts_until_log; 41151078Speterstatic struct callout_handle sio_timeout_handle 41251078Speter = CALLOUT_HANDLE_INITIALIZER(&sio_timeout_handle); 41353344Speterstatic int sio_numunits; 41451078Speter 41551078Speterstatic struct speedtab comspeedtab[] = { 41651078Speter { 0, 0 }, 41751078Speter { 50, COMBRD(50) }, 41851078Speter { 75, COMBRD(75) }, 41951078Speter { 110, COMBRD(110) }, 42051078Speter { 134, COMBRD(134) }, 42151078Speter { 150, COMBRD(150) }, 42251078Speter { 200, COMBRD(200) }, 42351078Speter { 300, COMBRD(300) }, 42451078Speter { 600, COMBRD(600) }, 42551078Speter { 1200, COMBRD(1200) }, 42651078Speter { 1800, COMBRD(1800) }, 42751078Speter { 2400, COMBRD(2400) }, 42851078Speter { 4800, COMBRD(4800) }, 42951078Speter { 9600, COMBRD(9600) }, 43051078Speter { 19200, COMBRD(19200) }, 43151078Speter { 38400, COMBRD(38400) }, 43251078Speter { 57600, COMBRD(57600) }, 43351078Speter { 115200, COMBRD(115200) }, 43451078Speter { -1, -1 } 43551078Speter}; 43651078Speter 43751078Speter#ifdef COM_ESP 43851078Speter/* XXX configure this properly. */ 43951078Speterstatic Port_t likely_com_ports[] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, }; 44051078Speterstatic Port_t likely_esp_ports[] = { 0x140, 0x180, 0x280, 0 }; 44151078Speter#endif 44251078Speter 44351078Speter/* 44451078Speter * handle sysctl read/write requests for console speed 44551078Speter * 44651078Speter * In addition to setting comdefaultrate for I/O through /dev/console, 44751078Speter * also set the initial and lock values for the /dev/ttyXX device 44851078Speter * if there is one associated with the console. Finally, if the /dev/tty 44951078Speter * device has already been open, change the speed on the open running port 45051078Speter * itself. 45151078Speter */ 45251078Speter 45351078Speterstatic int 45451078Spetersysctl_machdep_comdefaultrate SYSCTL_HANDLER_ARGS 45551078Speter{ 45651078Speter int error, s; 45751078Speter speed_t newspeed; 45851078Speter struct com_s *com; 45951078Speter struct tty *tp; 46051078Speter 46151078Speter newspeed = comdefaultrate; 46251078Speter 46351078Speter error = sysctl_handle_opaque(oidp, &newspeed, sizeof newspeed, req); 46451078Speter if (error || !req->newptr) 46551078Speter return (error); 46651078Speter 46751078Speter comdefaultrate = newspeed; 46851078Speter 46951078Speter if (comconsole < 0) /* serial console not selected? */ 47051078Speter return (0); 47151078Speter 47251078Speter com = com_addr(comconsole); 47357915Simp if (com == NULL) 47451078Speter return (ENXIO); 47551078Speter 47651078Speter /* 47751078Speter * set the initial and lock rates for /dev/ttydXX and /dev/cuaXX 47851078Speter * (note, the lock rates really are boolean -- if non-zero, disallow 47951078Speter * speed changes) 48051078Speter */ 48151078Speter com->it_in.c_ispeed = com->it_in.c_ospeed = 48251078Speter com->lt_in.c_ispeed = com->lt_in.c_ospeed = 48351078Speter com->it_out.c_ispeed = com->it_out.c_ospeed = 48451078Speter com->lt_out.c_ispeed = com->lt_out.c_ospeed = comdefaultrate; 48551078Speter 48651078Speter /* 48751078Speter * if we're open, change the running rate too 48851078Speter */ 48951078Speter tp = com->tp; 49051078Speter if (tp && (tp->t_state & TS_ISOPEN)) { 49151078Speter tp->t_termios.c_ispeed = 49251078Speter tp->t_termios.c_ospeed = comdefaultrate; 49351078Speter s = spltty(); 49451078Speter error = comparam(tp, &tp->t_termios); 49551078Speter splx(s); 49651078Speter } 49751078Speter return error; 49851078Speter} 49951078Speter 50051078SpeterSYSCTL_PROC(_machdep, OID_AUTO, conspeed, CTLTYPE_INT | CTLFLAG_RW, 50151078Speter 0, 0, sysctl_machdep_comdefaultrate, "I", ""); 50251078Speter 50353344Speter#define SET_FLAG(dev, bit) device_set_flags(dev, device_get_flags(dev) | (bit)) 50453344Speter#define CLR_FLAG(dev, bit) device_set_flags(dev, device_get_flags(dev) & ~(bit)) 50553344Speter 50651078Speter#if NCARD > 0 50752471Simpstatic int 50852471Simpsio_pccard_probe(dev) 50952471Simp device_t dev; 51051078Speter{ 51154386Simp /* Do not probe IRQ - pccard doesn't turn on the interrupt line */ 51254386Simp /* until bus_setup_intr */ 51353344Speter SET_FLAG(dev, COM_C_NOPROBE); 51453344Speter 51558885Simp return (sioprobe(dev, 0)); 51652471Simp} 51751078Speter 51852471Simpstatic int 51952471Simpsio_pccard_attach(dev) 52052471Simp device_t dev; 52152471Simp{ 52258885Simp return (sioattach(dev, 0)); 52351078Speter} 52451078Speter 52551078Speter/* 52652471Simp * sio_detach - unload the driver and clear the table. 52751078Speter * XXX TODO: 52851078Speter * This is usually called when the card is ejected, but 52951078Speter * can be caused by a modunload of a controller driver. 53051078Speter * The idea is to reset the driver's view of the device 53151078Speter * and ensure that any driver entry points such as 53251078Speter * read and write do not hang. 53351078Speter */ 53453978Simpstatic int 53552471Simpsio_pccard_detach(dev) 53652471Simp device_t dev; 53751078Speter{ 53851078Speter struct com_s *com; 53951078Speter 54052471Simp com = (struct com_s *) device_get_softc(dev); 54157915Simp if (com == NULL) { 54252471Simp device_printf(dev, "NULL com in siounload\n"); 54354386Simp return (0); 54451078Speter } 54557915Simp if (com->iobase == 0) { 54652471Simp device_printf(dev, "already unloaded!\n"); 54754386Simp return (0); 54851078Speter } 54954386Simp com->gone = 1; 55054386Simp if (com->irqres) { 55154386Simp bus_teardown_intr(dev, com->irqres, com->cookie); 55254386Simp bus_release_resource(dev, SYS_RES_IRQ, 0, com->irqres); 55354386Simp } 55454386Simp if (com->ioportres) 55554386Simp bus_release_resource(dev, SYS_RES_IOPORT, 0, com->ioportres); 55651078Speter if (com->tp && (com->tp->t_state & TS_ISOPEN)) { 55757915Simp device_printf(dev, "still open, forcing close\n"); 55851078Speter com->tp->t_gen++; 55951078Speter ttyclose(com->tp); 56051078Speter ttwakeup(com->tp); 56151078Speter ttwwakeup(com->tp); 56251078Speter } else { 56351078Speter if (com->ibuf != NULL) 56451078Speter free(com->ibuf, M_DEVBUF); 56551078Speter } 56657915Simp device_printf(dev, "unloaded\n"); 56753978Simp return (0); 56851078Speter} 56951078Speter#endif /* NCARD > 0 */ 57051078Speter 57158885Simp#if NPCI > 0 57258885Simpstruct pci_ids { 57358885Simp u_int32_t type; 57458885Simp const char *desc; 57558885Simp int rid; 57658885Simp}; 57751078Speter 57858885Simpstatic struct pci_ids pci_ids[] = { 57958885Simp { 0x100812b9, "3COM PCI FaxModem", 0x10 }, 58058885Simp { 0x048011c1, "ActionTec 56k FAX PCI Modem", 0x14 }, 58158885Simp { 0x00000000, NULL, 0 } 58258885Simp}; 58358885Simp 58458885Simpstatic int 58558885Simpsio_pci_attach(dev) 58658885Simp device_t dev; 58758885Simp{ 58858885Simp u_int32_t type; 58958885Simp struct pci_ids *id; 59058885Simp 59158885Simp type = pci_get_devid(dev); 59258885Simp id = pci_ids; 59358885Simp while (id->type && id->type != type) 59458885Simp id++; 59558885Simp if (id->desc == NULL) 59658885Simp return (ENXIO); 59758885Simp sio_pci_kludge_unit(dev); 59858885Simp return (sioattach(dev, id->rid)); 59958885Simp} 60058885Simp 60158885Simp/* 60258885Simp * Don't cut and paste this to other drivers. It is a horrible kludge 60358885Simp * which will fail to work and also be unnecessary in future versions. 60458885Simp */ 60558885Simpstatic void 60658885Simpsio_pci_kludge_unit(dev) 60758885Simp device_t dev; 60858885Simp{ 60958885Simp devclass_t dc; 61058885Simp int err; 61158885Simp int start; 61258885Simp int unit; 61358885Simp 61458885Simp unit = 0; 61558885Simp start = 0; 61658885Simp while (resource_int_value("sio", unit, "port", &start) == 0 && 61758885Simp start > 0) 61858885Simp unit++; 61958885Simp if (device_get_unit(dev) < unit) { 62058885Simp dc = device_get_devclass(dev); 62158885Simp while (devclass_get_device(dc, unit)) 62258885Simp unit++; 62358885Simp device_printf(dev, "moving to sio%d\n", unit); 62458885Simp err = device_set_unit(dev, unit); /* EVIL DO NOT COPY */ 62558885Simp if (err) 62658885Simp device_printf(dev, "error moving device %d\n", err); 62758885Simp } 62858885Simp} 62958885Simp 63058885Simpstatic int 63158885Simpsio_pci_probe(dev) 63258885Simp device_t dev; 63358885Simp{ 63458885Simp u_int32_t type; 63558885Simp struct pci_ids *id; 63658885Simp 63758885Simp type = pci_get_devid(dev); 63858885Simp id = pci_ids; 63958885Simp while (id->type && id->type != type) 64058885Simp id++; 64158885Simp if (id->desc == NULL) 64258885Simp return (ENXIO); 64358885Simp device_set_desc(dev, id->desc); 64458885Simp return (sioprobe(dev, id->rid)); 64558885Simp} 64658885Simp#endif /* NPCI > 0 */ 64758885Simp 64851078Speterstatic struct isa_pnp_id sio_ids[] = { 64951078Speter {0x0005d041, "Standard PC COM port"}, /* PNP0500 */ 65051078Speter {0x0105d041, "16550A-compatible COM port"}, /* PNP0501 */ 65151078Speter {0x0205d041, "Multiport serial device (non-intelligent 16550)"}, /* PNP0502 */ 65251078Speter {0x1005d041, "Generic IRDA-compatible device"}, /* PNP0510 */ 65351078Speter {0x1105d041, "Generic IRDA-compatible device"}, /* PNP0511 */ 65454944Speter /* Devices that do not have a compatid */ 65554944Speter {0x7602a904, NULL}, /* AEI0276 - 56K v.90 Fax Modem (LKT) */ 65654944Speter {0x00007905, NULL}, /* AKY0000 - 56K Plug&Play Modem */ 65754944Speter {0x01405407, NULL}, /* AZT4001 - AZT3000 PnP SOUND DEVICE, MODEM */ 65854944Speter {0x56039008, NULL}, /* BDP0356 - Best Data 56x2 */ 65954944Speter {0x36339008, NULL}, /* BDP3336 - Best Data Prods. 336F */ 66054944Speter {0x0014490a, NULL}, /* BRI1400 - Boca 33.6 PnP */ 66154944Speter {0x0015490a, NULL}, /* BRI1500 - Internal Fax Data */ 66254944Speter {0x0034490a, NULL}, /* BRI3400 - Internal ACF Modem */ 66354944Speter {0x00b4490a, NULL}, /* BRIB400 - Boca 56k PnP */ 66454944Speter {0x0030320d, NULL}, /* CIR3000 - Cirrus Logic V43 */ 66556229Speter {0x0100440e, NULL}, /* CRD0001 - Cardinal MVP288IV ? */ 66659888Stanimura {0x0000aa1a, NULL}, /* FUJ0000 - FUJITSU Modem 33600 PNP/I2 */ 66754944Speter {0x1200c31e, NULL}, /* GVC0012 - VF1128HV-R9 (win modem?) */ 66854944Speter {0x0303c31e, NULL}, /* GVC0303 - MaxTech 33.6 PnP D/F/V */ 66955124Speter {0x0505c31e, NULL}, /* GVC0505 - GVC 56k Faxmodem */ 67054944Speter {0x0050c31e, NULL}, /* GVC5000 - some GVC modem */ 67154944Speter {0x3800f91e, NULL}, /* GWY0038 - Telepath with v.90 */ 67254944Speter {0x9062f91e, NULL}, /* GWY6290 - Telepath with x2 Technology */ 67354944Speter {0x0000f435, NULL}, /* MOT0000 - Motorola ModemSURFR 33.6 Intern */ 67454944Speter {0x5015f435, NULL}, /* MOT1550 - Motorola ModemSURFR 56K Modem */ 67554944Speter {0xf015f435, NULL}, /* MOT15F0 - Motorola VoiceSURFR 56K Modem */ 67654944Speter {0x6045f435, NULL}, /* MOT4560 - Motorola ? */ 67754944Speter {0x61e7a338, NULL}, /* NECE761 - 33.6Modem */ 67854944Speter {0x39804f3f, NULL}, /* OZO8039 - Zoom 56k flex */ 67957769Speter {0x3024a341, NULL}, /* PMC2430 - Pace 56 Voice Internal Modem */ 68054944Speter {0x1000eb49, NULL}, /* ROK0010 - Rockwell ? */ 68154944Speter {0x5002734a, NULL}, /* RSS0250 - 5614Jx3(G) Internal Modem */ 68254944Speter {0xc100ad4d, NULL}, /* SMM00C1 - Leopard 56k PnP */ 68356229Speter {0x9012b04e, NULL}, /* SUP1290 - Supra ? */ 68454944Speter {0x1013b04e, NULL}, /* SUP1310 - SupraExpress 336i PnP */ 68554944Speter {0x8013b04e, NULL}, /* SUP1380 - SupraExpress 288i PnP Voice */ 68654944Speter {0x8113b04e, NULL}, /* SUP1381 - SupraExpress 336i PnP Voice */ 68754944Speter {0x5016b04e, NULL}, /* SUP1650 - Supra 336i Sp Intl */ 68854944Speter {0x7420b04e, NULL}, /* SUP2070 - Supra ? */ 68954944Speter {0x8020b04e, NULL}, /* SUP2080 - Supra ? */ 69054944Speter {0x8420b04e, NULL}, /* SUP2084 - SupraExpress 56i PnP */ 69156229Speter {0x7121b04e, NULL}, /* SUP2171 - SupraExpress 56i Sp? */ 69254944Speter {0x8024b04e, NULL}, /* SUP2480 - Supra ? */ 69354944Speter {0x01007256, NULL}, /* USR0001 - U.S. Robotics Inc., Sportster W */ 69454944Speter {0x02007256, NULL}, /* USR0002 - U.S. Robotics Inc. Sportster 33. */ 69554944Speter {0x04007256, NULL}, /* USR0004 - USR Sportster 14.4k */ 69654944Speter {0x06007256, NULL}, /* USR0006 - USR Sportster 33.6k */ 69756229Speter {0x11007256, NULL}, /* USR0011 - USR ? */ 69854944Speter {0x01017256, NULL}, /* USR0101 - USR ? */ 69954944Speter {0x30207256, NULL}, /* USR2030 - U.S.Robotics Inc. Sportster 560 */ 70054944Speter {0x50207256, NULL}, /* USR2050 - U.S.Robotics Inc. Sportster 33. */ 70154944Speter {0x70207256, NULL}, /* USR2070 - U.S.Robotics Inc. Sportster 560 */ 70254944Speter {0x30307256, NULL}, /* USR3030 - U.S. Robotics 56K FAX INT */ 70354944Speter {0x31307256, NULL}, /* USR3031 - U.S. Robotics 56K FAX INT */ 70458848Speter {0x50307256, NULL}, /* USR3050 - U.S. Robotics 56K FAX INT */ 70554944Speter {0x70307256, NULL}, /* USR3070 - U.S. Robotics 56K Voice INT */ 70656229Speter {0x90307256, NULL}, /* USR3090 - USR ? */ 70754944Speter {0x90917256, NULL}, /* USR9190 - USR 56k Voice INT */ 70854944Speter {0x0300695c, NULL}, /* WCI0003 - Fax/Voice/Modem/Speakphone/Asvd */ 70954944Speter {0x61f7896a, NULL}, /* ZTIF761 - Zoom ComStar 33.6 */ 71051078Speter {0} 71151078Speter}; 71251078Speter 71356229Speter 71456229Speter 71551078Speterstatic int 71652471Simpsio_isa_probe(dev) 71752471Simp device_t dev; 71852471Simp{ 71952471Simp /* Check isapnp ids */ 72052471Simp if (ISA_PNP_PROBE(device_get_parent(dev), dev, sio_ids) == ENXIO) 72152471Simp return (ENXIO); 72258885Simp return (sioprobe(dev, 0)); 72352471Simp} 72452471Simp 72552471Simpstatic int 72658885Simpsioprobe(dev, xrid) 72751078Speter device_t dev; 72858885Simp int xrid; 72951078Speter{ 73053344Speter#if 0 73151078Speter static bool_t already_init; 73253344Speter device_t xdev; 73353344Speter#endif 73451078Speter bool_t failures[10]; 73551078Speter int fn; 73651078Speter device_t idev; 73751078Speter Port_t iobase; 73851078Speter intrmask_t irqmap[4]; 73951078Speter intrmask_t irqs; 74051078Speter u_char mcr_image; 74151078Speter int result; 74254206Speter u_long xirq; 74351088Speter u_int flags = device_get_flags(dev); 74451078Speter int rid; 74551078Speter struct resource *port; 74651078Speter 74758885Simp rid = xrid; 74851078Speter port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 74951078Speter 0, ~0, IO_COMSIZE, RF_ACTIVE); 75051078Speter if (!port) 75157915Simp return (ENXIO); 75251078Speter 75353344Speter#if 0 75453344Speter /* 75553344Speter * XXX this is broken - when we are first called, there are no 75653344Speter * previously configured IO ports. We could hard code 75753344Speter * 0x3f8, 0x2f8, 0x3e8, 0x2e8 etc but that's probably worse. 75853344Speter * This code has been doing nothing since the conversion since 75953344Speter * "count" is zero the first time around. 76053344Speter */ 76151078Speter if (!already_init) { 76251078Speter /* 76351078Speter * Turn off MCR_IENABLE for all likely serial ports. An unused 76451078Speter * port with its MCR_IENABLE gate open will inhibit interrupts 76551078Speter * from any used port that shares the interrupt vector. 76651078Speter * XXX the gate enable is elsewhere for some multiports. 76751078Speter */ 76851078Speter device_t *devs; 76953344Speter int count, i, xioport; 77051078Speter 77151078Speter devclass_get_devices(sio_devclass, &devs, &count); 77251078Speter for (i = 0; i < count; i++) { 77351078Speter xdev = devs[i]; 77454194Speter if (device_is_enabled(xdev) && 77554194Speter bus_get_resource(xdev, SYS_RES_IOPORT, 0, &xioport, 77654194Speter NULL) == 0) 77753344Speter outb(xioport + com_mcr, 0); 77851078Speter } 77951078Speter free(devs, M_TEMP); 78051078Speter already_init = TRUE; 78151078Speter } 78253344Speter#endif 78351078Speter 78451078Speter if (COM_LLCONSOLE(flags)) { 78551078Speter printf("sio%d: reserved for low-level i/o\n", 78651078Speter device_get_unit(dev)); 78756788Sbde bus_release_resource(dev, SYS_RES_IOPORT, rid, port); 78851078Speter return (ENXIO); 78951078Speter } 79051078Speter 79151078Speter /* 79251078Speter * If the device is on a multiport card and has an AST/4 79351078Speter * compatible interrupt control register, initialize this 79451078Speter * register and prepare to leave MCR_IENABLE clear in the mcr. 79551078Speter * Otherwise, prepare to set MCR_IENABLE in the mcr. 79651078Speter * Point idev to the device struct giving the correct id_irq. 79751078Speter * This is the struct for the master device if there is one. 79851078Speter */ 79951078Speter idev = dev; 80051078Speter mcr_image = MCR_IENABLE; 80151078Speter#ifdef COM_MULTIPORT 80257234Sbde if (COM_ISMULTIPORT(flags)) { 80354206Speter Port_t xiobase; 80454206Speter u_long io; 80554206Speter 80651078Speter idev = devclass_get_device(sio_devclass, COM_MPMASTER(flags)); 80751078Speter if (idev == NULL) { 80851078Speter printf("sio%d: master device %d not configured\n", 80951078Speter device_get_unit(dev), COM_MPMASTER(flags)); 81051078Speter idev = dev; 81151078Speter } 81257234Sbde if (!COM_NOTAST4(flags)) { 81357234Sbde if (bus_get_resource(idev, SYS_RES_IOPORT, 0, &io, 81457234Sbde NULL) == 0) { 81557234Sbde xiobase = io; 81657234Sbde if (bus_get_resource(idev, SYS_RES_IRQ, 0, 81757234Sbde NULL, NULL) == 0) 81857234Sbde outb(xiobase + com_scr, 0x80); 81957234Sbde else 82057234Sbde outb(xiobase + com_scr, 0); 82157234Sbde } 82257234Sbde mcr_image = 0; 82351078Speter } 82451078Speter } 82551078Speter#endif /* COM_MULTIPORT */ 82654194Speter if (bus_get_resource(idev, SYS_RES_IRQ, 0, NULL, NULL) != 0) 82751078Speter mcr_image = 0; 82851078Speter 82951078Speter bzero(failures, sizeof failures); 83051078Speter iobase = rman_get_start(port); 83151078Speter 83251078Speter /* 83351078Speter * We don't want to get actual interrupts, just masked ones. 83451078Speter * Interrupts from this line should already be masked in the ICU, 83551078Speter * but mask them in the processor as well in case there are some 83651078Speter * (misconfigured) shared interrupts. 83751078Speter */ 83851078Speter disable_intr(); 83951078Speter/* EXTRA DELAY? */ 84051078Speter 84151078Speter /* 84251078Speter * Initialize the speed and the word size and wait long enough to 84351078Speter * drain the maximum of 16 bytes of junk in device output queues. 84451078Speter * The speed is undefined after a master reset and must be set 84551078Speter * before relying on anything related to output. There may be 84651078Speter * junk after a (very fast) soft reboot and (apparently) after 84751078Speter * master reset. 84851078Speter * XXX what about the UART bug avoided by waiting in comparam()? 84951078Speter * We don't want to to wait long enough to drain at 2 bps. 85051078Speter */ 85151078Speter if (iobase == siocniobase) 85251078Speter DELAY((16 + 1) * 1000000 / (comdefaultrate / 10)); 85351078Speter else { 85451078Speter outb(iobase + com_cfcr, CFCR_DLAB | CFCR_8BITS); 85551078Speter outb(iobase + com_dlbl, COMBRD(SIO_TEST_SPEED) & 0xff); 85651078Speter outb(iobase + com_dlbh, (u_int) COMBRD(SIO_TEST_SPEED) >> 8); 85751078Speter outb(iobase + com_cfcr, CFCR_8BITS); 85851078Speter DELAY((16 + 1) * 1000000 / (SIO_TEST_SPEED / 10)); 85951078Speter } 86051078Speter 86151078Speter /* 86251078Speter * Enable the interrupt gate and disable device interupts. This 86351078Speter * should leave the device driving the interrupt line low and 86451078Speter * guarantee an edge trigger if an interrupt can be generated. 86551078Speter */ 86651078Speter/* EXTRA DELAY? */ 86751078Speter outb(iobase + com_mcr, mcr_image); 86851078Speter outb(iobase + com_ier, 0); 86951078Speter DELAY(1000); /* XXX */ 87051078Speter irqmap[0] = isa_irq_pending(); 87151078Speter 87251078Speter /* 87351078Speter * Attempt to set loopback mode so that we can send a null byte 87451078Speter * without annoying any external device. 87551078Speter */ 87651078Speter/* EXTRA DELAY? */ 87751078Speter outb(iobase + com_mcr, mcr_image | MCR_LOOPBACK); 87851078Speter 87951078Speter /* 88051078Speter * Attempt to generate an output interrupt. On 8250's, setting 88151078Speter * IER_ETXRDY generates an interrupt independent of the current 88251078Speter * setting and independent of whether the THR is empty. On 16450's, 88351078Speter * setting IER_ETXRDY generates an interrupt independent of the 88451078Speter * current setting. On 16550A's, setting IER_ETXRDY only 88551078Speter * generates an interrupt when IER_ETXRDY is not already set. 88651078Speter */ 88751078Speter outb(iobase + com_ier, IER_ETXRDY); 88851078Speter 88951078Speter /* 89051078Speter * On some 16x50 incompatibles, setting IER_ETXRDY doesn't generate 89151078Speter * an interrupt. They'd better generate one for actually doing 89251078Speter * output. Loopback may be broken on the same incompatibles but 89351078Speter * it's unlikely to do more than allow the null byte out. 89451078Speter */ 89551078Speter outb(iobase + com_data, 0); 89651078Speter DELAY((1 + 2) * 1000000 / (SIO_TEST_SPEED / 10)); 89751078Speter 89851078Speter /* 89951078Speter * Turn off loopback mode so that the interrupt gate works again 90051078Speter * (MCR_IENABLE was hidden). This should leave the device driving 90151078Speter * an interrupt line high. It doesn't matter if the interrupt 90251078Speter * line oscillates while we are not looking at it, since interrupts 90351078Speter * are disabled. 90451078Speter */ 90551078Speter/* EXTRA DELAY? */ 90651078Speter outb(iobase + com_mcr, mcr_image); 90751078Speter 90851078Speter /* 90952471Simp * Some pcmcia cards have the "TXRDY bug", so we check everyone 91051078Speter * for IIR_TXRDY implementation ( Palido 321s, DC-1S... ) 91151078Speter */ 91253370Speter if (COM_NOPROBE(flags)) { 91353370Speter /* Reading IIR register twice */ 91453370Speter for (fn = 0; fn < 2; fn ++) { 91553370Speter DELAY(10000); 91653370Speter failures[6] = inb(iobase + com_iir); 91753370Speter } 91853370Speter /* Check IIR_TXRDY clear ? */ 91953370Speter result = 0; 92053370Speter if (failures[6] & IIR_TXRDY) { 92153370Speter /* Nop, Double check with clearing IER */ 92253370Speter outb(iobase + com_ier, 0); 92353370Speter if (inb(iobase + com_iir) & IIR_NOPEND) { 92453370Speter /* Ok. we're familia this gang */ 92553370Speter SET_FLAG(dev, COM_C_IIR_TXRDYBUG); 92653370Speter } else { 92753370Speter /* Unknown, Just omit this chip.. XXX */ 92853370Speter result = ENXIO; 92953370Speter } 93051078Speter } else { 93153370Speter /* OK. this is well-known guys */ 93253370Speter CLR_FLAG(dev, COM_C_IIR_TXRDYBUG); 93351078Speter } 93453344Speter outb(iobase + com_cfcr, CFCR_8BITS); 93553344Speter enable_intr(); 93653344Speter bus_release_resource(dev, SYS_RES_IOPORT, rid, port); 93753344Speter return (iobase == siocniobase ? 0 : result); 93853344Speter } 93953344Speter 94051078Speter /* 94151078Speter * Check that 94251078Speter * o the CFCR, IER and MCR in UART hold the values written to them 94351078Speter * (the values happen to be all distinct - this is good for 94451078Speter * avoiding false positive tests from bus echoes). 94551078Speter * o an output interrupt is generated and its vector is correct. 94651078Speter * o the interrupt goes away when the IIR in the UART is read. 94751078Speter */ 94851078Speter/* EXTRA DELAY? */ 94951078Speter failures[0] = inb(iobase + com_cfcr) - CFCR_8BITS; 95051078Speter failures[1] = inb(iobase + com_ier) - IER_ETXRDY; 95151078Speter failures[2] = inb(iobase + com_mcr) - mcr_image; 95251078Speter DELAY(10000); /* Some internal modems need this time */ 95351078Speter irqmap[1] = isa_irq_pending(); 95451078Speter failures[4] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_TXRDY; 95551078Speter DELAY(1000); /* XXX */ 95651078Speter irqmap[2] = isa_irq_pending(); 95751078Speter failures[6] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_NOPEND; 95851078Speter 95951078Speter /* 96051078Speter * Turn off all device interrupts and check that they go off properly. 96151078Speter * Leave MCR_IENABLE alone. For ports without a master port, it gates 96251078Speter * the OUT2 output of the UART to 96351078Speter * the ICU input. Closing the gate would give a floating ICU input 96451078Speter * (unless there is another device driving it) and spurious interrupts. 96551078Speter * (On the system that this was first tested on, the input floats high 96651078Speter * and gives a (masked) interrupt as soon as the gate is closed.) 96751078Speter */ 96851078Speter outb(iobase + com_ier, 0); 96951078Speter outb(iobase + com_cfcr, CFCR_8BITS); /* dummy to avoid bus echo */ 97051078Speter failures[7] = inb(iobase + com_ier); 97151078Speter DELAY(1000); /* XXX */ 97251078Speter irqmap[3] = isa_irq_pending(); 97351078Speter failures[9] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_NOPEND; 97451078Speter 97551078Speter enable_intr(); 97651078Speter 97751078Speter irqs = irqmap[1] & ~irqmap[0]; 97854194Speter if (bus_get_resource(idev, SYS_RES_IRQ, 0, &xirq, NULL) == 0 && 97954194Speter ((1 << xirq) & irqs) == 0) 98051078Speter printf( 98154206Speter "sio%d: configured irq %ld not in bitmap of probed irqs %#x\n", 98253344Speter device_get_unit(dev), xirq, irqs); 98351078Speter if (bootverbose) 98451078Speter printf("sio%d: irq maps: %#x %#x %#x %#x\n", 98551078Speter device_get_unit(dev), 98651078Speter irqmap[0], irqmap[1], irqmap[2], irqmap[3]); 98751078Speter 98851078Speter result = 0; 98951078Speter for (fn = 0; fn < sizeof failures; ++fn) 99051078Speter if (failures[fn]) { 99151078Speter outb(iobase + com_mcr, 0); 99251078Speter result = ENXIO; 99351078Speter if (bootverbose) { 99451078Speter printf("sio%d: probe failed test(s):", 99551078Speter device_get_unit(dev)); 99651078Speter for (fn = 0; fn < sizeof failures; ++fn) 99751078Speter if (failures[fn]) 99851078Speter printf(" %d", fn); 99951078Speter printf("\n"); 100051078Speter } 100151078Speter break; 100251078Speter } 100351078Speter bus_release_resource(dev, SYS_RES_IOPORT, rid, port); 100451078Speter return (iobase == siocniobase ? 0 : result); 100551078Speter} 100651078Speter 100751078Speter#ifdef COM_ESP 100851078Speterstatic int 100951078Speterespattach(com, esp_port) 101051078Speter struct com_s *com; 101151078Speter Port_t esp_port; 101251078Speter{ 101351078Speter u_char dips; 101451078Speter u_char val; 101551078Speter 101651078Speter /* 101751078Speter * Check the ESP-specific I/O port to see if we're an ESP 101851078Speter * card. If not, return failure immediately. 101951078Speter */ 102051078Speter if ((inb(esp_port) & 0xf3) == 0) { 102151078Speter printf(" port 0x%x is not an ESP board?\n", esp_port); 102251078Speter return (0); 102351078Speter } 102451078Speter 102551078Speter /* 102651078Speter * We've got something that claims to be a Hayes ESP card. 102751078Speter * Let's hope so. 102851078Speter */ 102951078Speter 103051078Speter /* Get the dip-switch configuration */ 103151078Speter outb(esp_port + ESP_CMD1, ESP_GETDIPS); 103251078Speter dips = inb(esp_port + ESP_STATUS1); 103351078Speter 103451078Speter /* 103551078Speter * Bits 0,1 of dips say which COM port we are. 103651078Speter */ 103751078Speter if (com->iobase == likely_com_ports[dips & 0x03]) 103851078Speter printf(" : ESP"); 103951078Speter else { 104051078Speter printf(" esp_port has com %d\n", dips & 0x03); 104151078Speter return (0); 104251078Speter } 104351078Speter 104451078Speter /* 104551078Speter * Check for ESP version 2.0 or later: bits 4,5,6 = 010. 104651078Speter */ 104751078Speter outb(esp_port + ESP_CMD1, ESP_GETTEST); 104851078Speter val = inb(esp_port + ESP_STATUS1); /* clear reg 1 */ 104951078Speter val = inb(esp_port + ESP_STATUS2); 105051078Speter if ((val & 0x70) < 0x20) { 105151078Speter printf("-old (%o)", val & 0x70); 105251078Speter return (0); 105351078Speter } 105451078Speter 105551078Speter /* 105651078Speter * Check for ability to emulate 16550: bit 7 == 1 105751078Speter */ 105851078Speter if ((dips & 0x80) == 0) { 105951078Speter printf(" slave"); 106051078Speter return (0); 106151078Speter } 106251078Speter 106351078Speter /* 106451078Speter * Okay, we seem to be a Hayes ESP card. Whee. 106551078Speter */ 106651078Speter com->esp = TRUE; 106751078Speter com->esp_port = esp_port; 106851078Speter return (1); 106951078Speter} 107051078Speter#endif /* COM_ESP */ 107151078Speter 107251078Speterstatic int 107352471Simpsio_isa_attach(dev) 107452471Simp device_t dev; 107552471Simp{ 107658885Simp return (sioattach(dev, 0)); 107752471Simp} 107852471Simp 107952471Simpstatic int 108058885Simpsioattach(dev, xrid) 108151078Speter device_t dev; 108258885Simp int xrid; 108351078Speter{ 108451078Speter struct com_s *com; 108551078Speter#ifdef COM_ESP 108651078Speter Port_t *espp; 108751078Speter#endif 108851078Speter Port_t iobase; 108951078Speter int unit; 109053344Speter u_int flags; 109151078Speter int rid; 109251078Speter struct resource *port; 109353344Speter int ret; 109451078Speter 109558885Simp rid = xrid; 109651078Speter port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 109751078Speter 0, ~0, IO_COMSIZE, RF_ACTIVE); 109851078Speter if (!port) 109957915Simp return (ENXIO); 110051078Speter 110151078Speter iobase = rman_get_start(port); 110251078Speter unit = device_get_unit(dev); 110351078Speter com = device_get_softc(dev); 110453344Speter flags = device_get_flags(dev); 110551078Speter 110653344Speter if (unit >= sio_numunits) 110753344Speter sio_numunits = unit + 1; 110851078Speter /* 110951078Speter * sioprobe() has initialized the device registers as follows: 111051078Speter * o cfcr = CFCR_8BITS. 111151078Speter * It is most important that CFCR_DLAB is off, so that the 111251078Speter * data port is not hidden when we enable interrupts. 111351078Speter * o ier = 0. 111451078Speter * Interrupts are only enabled when the line is open. 111551078Speter * o mcr = MCR_IENABLE, or 0 if the port has AST/4 compatible 111651078Speter * interrupt control register or the config specifies no irq. 111751078Speter * Keeping MCR_DTR and MCR_RTS off might stop the external 111851078Speter * device from sending before we are ready. 111951078Speter */ 112051078Speter bzero(com, sizeof *com); 112151078Speter com->unit = unit; 112251078Speter com->ioportres = port; 112351078Speter com->cfcr_image = CFCR_8BITS; 112451078Speter com->dtr_wait = 3 * hz; 112551078Speter com->loses_outints = COM_LOSESOUTINTS(flags) != 0; 112657234Sbde com->no_irq = bus_get_resource(dev, SYS_RES_IRQ, 0, NULL, NULL) != 0; 112751078Speter com->tx_fifo_size = 1; 112851078Speter com->obufs[0].l_head = com->obuf1; 112951078Speter com->obufs[1].l_head = com->obuf2; 113051078Speter 113151078Speter com->iobase = iobase; 113251078Speter com->data_port = iobase + com_data; 113351078Speter com->int_id_port = iobase + com_iir; 113451078Speter com->modem_ctl_port = iobase + com_mcr; 113551078Speter com->mcr_image = inb(com->modem_ctl_port); 113651078Speter com->line_status_port = iobase + com_lsr; 113751078Speter com->modem_status_port = iobase + com_msr; 113851078Speter com->intr_ctl_port = iobase + com_ier; 113951078Speter 114051078Speter /* 114151078Speter * We don't use all the flags from <sys/ttydefaults.h> since they 114251078Speter * are only relevant for logins. It's important to have echo off 114351078Speter * initially so that the line doesn't start blathering before the 114451078Speter * echo flag can be turned off. 114551078Speter */ 114651078Speter com->it_in.c_iflag = 0; 114751078Speter com->it_in.c_oflag = 0; 114851078Speter com->it_in.c_cflag = TTYDEF_CFLAG; 114951078Speter com->it_in.c_lflag = 0; 115051078Speter if (unit == comconsole) { 115151078Speter com->it_in.c_iflag = TTYDEF_IFLAG; 115251078Speter com->it_in.c_oflag = TTYDEF_OFLAG; 115351078Speter com->it_in.c_cflag = TTYDEF_CFLAG | CLOCAL; 115451078Speter com->it_in.c_lflag = TTYDEF_LFLAG; 115551078Speter com->lt_out.c_cflag = com->lt_in.c_cflag = CLOCAL; 115651078Speter com->lt_out.c_ispeed = com->lt_out.c_ospeed = 115751078Speter com->lt_in.c_ispeed = com->lt_in.c_ospeed = 115851078Speter com->it_in.c_ispeed = com->it_in.c_ospeed = comdefaultrate; 115951078Speter } else 116051078Speter com->it_in.c_ispeed = com->it_in.c_ospeed = TTYDEF_SPEED; 116151078Speter if (siosetwater(com, com->it_in.c_ispeed) != 0) { 116251078Speter enable_intr(); 116356788Sbde /* 116456788Sbde * Leave i/o resources allocated if this is a `cn'-level 116556788Sbde * console, so that other devices can't snarf them. 116656788Sbde */ 116756788Sbde if (iobase != siocniobase) 116856788Sbde bus_release_resource(dev, SYS_RES_IOPORT, rid, port); 116956788Sbde return (ENOMEM); 117051078Speter } 117151078Speter enable_intr(); 117251078Speter termioschars(&com->it_in); 117351078Speter com->it_out = com->it_in; 117451078Speter 117551078Speter /* attempt to determine UART type */ 117651078Speter printf("sio%d: type", unit); 117751078Speter 117851078Speter 117951078Speter#ifdef COM_MULTIPORT 118051078Speter if (!COM_ISMULTIPORT(flags) && !COM_IIR_TXRDYBUG(flags)) 118151078Speter#else 118251078Speter if (!COM_IIR_TXRDYBUG(flags)) 118351078Speter#endif 118451078Speter { 118551078Speter u_char scr; 118651078Speter u_char scr1; 118751078Speter u_char scr2; 118851078Speter 118951078Speter scr = inb(iobase + com_scr); 119051078Speter outb(iobase + com_scr, 0xa5); 119151078Speter scr1 = inb(iobase + com_scr); 119251078Speter outb(iobase + com_scr, 0x5a); 119351078Speter scr2 = inb(iobase + com_scr); 119451078Speter outb(iobase + com_scr, scr); 119551078Speter if (scr1 != 0xa5 || scr2 != 0x5a) { 119651078Speter printf(" 8250"); 119751078Speter goto determined_type; 119851078Speter } 119951078Speter } 120051078Speter outb(iobase + com_fifo, FIFO_ENABLE | FIFO_RX_HIGH); 120151078Speter DELAY(100); 120251078Speter com->st16650a = 0; 120351078Speter switch (inb(com->int_id_port) & IIR_FIFO_MASK) { 120451078Speter case FIFO_RX_LOW: 120551078Speter printf(" 16450"); 120651078Speter break; 120751078Speter case FIFO_RX_MEDL: 120851078Speter printf(" 16450?"); 120951078Speter break; 121051078Speter case FIFO_RX_MEDH: 121151078Speter printf(" 16550?"); 121251078Speter break; 121351078Speter case FIFO_RX_HIGH: 121451078Speter if (COM_NOFIFO(flags)) { 121551078Speter printf(" 16550A fifo disabled"); 121651078Speter } else { 121751078Speter com->hasfifo = TRUE; 121851078Speter if (COM_ST16650A(flags)) { 121951078Speter com->st16650a = 1; 122051078Speter com->tx_fifo_size = 32; 122151078Speter printf(" ST16650A"); 122251078Speter } else { 122351078Speter com->tx_fifo_size = COM_FIFOSIZE(flags); 122451078Speter printf(" 16550A"); 122551078Speter } 122651078Speter } 122751078Speter#ifdef COM_ESP 122851078Speter for (espp = likely_esp_ports; *espp != 0; espp++) 122951078Speter if (espattach(com, *espp)) { 123051078Speter com->tx_fifo_size = 1024; 123151078Speter break; 123251078Speter } 123351078Speter#endif 123451078Speter if (!com->st16650a) { 123551078Speter if (!com->tx_fifo_size) 123651078Speter com->tx_fifo_size = 16; 123751078Speter else 123851078Speter printf(" lookalike with %d bytes FIFO", 123951078Speter com->tx_fifo_size); 124051078Speter } 124151078Speter 124251078Speter break; 124351078Speter } 124451078Speter 124551078Speter#ifdef COM_ESP 124651078Speter if (com->esp) { 124751078Speter /* 124851078Speter * Set 16550 compatibility mode. 124951078Speter * We don't use the ESP_MODE_SCALE bit to increase the 125051078Speter * fifo trigger levels because we can't handle large 125151078Speter * bursts of input. 125251078Speter * XXX flow control should be set in comparam(), not here. 125351078Speter */ 125451078Speter outb(com->esp_port + ESP_CMD1, ESP_SETMODE); 125551078Speter outb(com->esp_port + ESP_CMD2, ESP_MODE_RTS | ESP_MODE_FIFO); 125651078Speter 125751078Speter /* Set RTS/CTS flow control. */ 125851078Speter outb(com->esp_port + ESP_CMD1, ESP_SETFLOWTYPE); 125951078Speter outb(com->esp_port + ESP_CMD2, ESP_FLOW_RTS); 126051078Speter outb(com->esp_port + ESP_CMD2, ESP_FLOW_CTS); 126151078Speter 126251078Speter /* Set flow-control levels. */ 126351078Speter outb(com->esp_port + ESP_CMD1, ESP_SETRXFLOW); 126451078Speter outb(com->esp_port + ESP_CMD2, HIBYTE(768)); 126551078Speter outb(com->esp_port + ESP_CMD2, LOBYTE(768)); 126651078Speter outb(com->esp_port + ESP_CMD2, HIBYTE(512)); 126751078Speter outb(com->esp_port + ESP_CMD2, LOBYTE(512)); 126851078Speter } 126951078Speter#endif /* COM_ESP */ 127051078Speter outb(iobase + com_fifo, 0); 127151078Speterdetermined_type: ; 127251078Speter 127351078Speter#ifdef COM_MULTIPORT 127451078Speter if (COM_ISMULTIPORT(flags)) { 127553344Speter device_t masterdev; 127653344Speter 127751078Speter com->multiport = TRUE; 127851078Speter printf(" (multiport"); 127951078Speter if (unit == COM_MPMASTER(flags)) 128051078Speter printf(" master"); 128151078Speter printf(")"); 128253344Speter masterdev = devclass_get_device(sio_devclass, 128353344Speter COM_MPMASTER(flags)); 128457234Sbde com->no_irq = (masterdev == NULL || bus_get_resource(masterdev, 128557234Sbde SYS_RES_IRQ, 0, NULL, NULL) != 0); 128651078Speter } 128751078Speter#endif /* COM_MULTIPORT */ 128851078Speter if (unit == comconsole) 128951078Speter printf(", console"); 129053344Speter if (COM_IIR_TXRDYBUG(flags)) 129151078Speter printf(" with a bogus IIR_TXRDY register"); 129251078Speter printf("\n"); 129351078Speter 129451078Speter if (!sio_registered) { 129551078Speter register_swi(SWI_TTY, siopoll); 129651078Speter sio_registered = TRUE; 129751078Speter } 129851078Speter make_dev(&sio_cdevsw, unit, 129951078Speter UID_ROOT, GID_WHEEL, 0600, "ttyd%r", unit); 130051078Speter make_dev(&sio_cdevsw, unit | CONTROL_INIT_STATE, 130151078Speter UID_ROOT, GID_WHEEL, 0600, "ttyid%r", unit); 130251078Speter make_dev(&sio_cdevsw, unit | CONTROL_LOCK_STATE, 130351078Speter UID_ROOT, GID_WHEEL, 0600, "ttyld%r", unit); 130451078Speter make_dev(&sio_cdevsw, unit | CALLOUT_MASK, 130551078Speter UID_UUCP, GID_DIALER, 0660, "cuaa%r", unit); 130651078Speter make_dev(&sio_cdevsw, unit | CALLOUT_MASK | CONTROL_INIT_STATE, 130751078Speter UID_UUCP, GID_DIALER, 0660, "cuaia%r", unit); 130851078Speter make_dev(&sio_cdevsw, unit | CALLOUT_MASK | CONTROL_LOCK_STATE, 130951078Speter UID_UUCP, GID_DIALER, 0660, "cuala%r", unit); 131051078Speter com->flags = flags; 131151078Speter com->pps.ppscap = PPS_CAPTUREASSERT | PPS_CAPTURECLEAR; 131251078Speter pps_init(&com->pps); 131351078Speter 131451078Speter rid = 0; 131551078Speter com->irqres = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0ul, ~0ul, 1, 131653344Speter RF_ACTIVE); 131753344Speter if (com->irqres) { 131853344Speter ret = BUS_SETUP_INTR(device_get_parent(dev), dev, com->irqres, 131954194Speter INTR_TYPE_TTY | INTR_TYPE_FAST, 132054386Simp siointr, com, &com->cookie); 132154194Speter if (ret) { 132254194Speter ret = BUS_SETUP_INTR(device_get_parent(dev), dev, 132354194Speter com->irqres, INTR_TYPE_TTY, 132454386Simp siointr, com, &com->cookie); 132554194Speter if (ret == 0) 132654194Speter device_printf(dev, "unable to activate interrupt in fast mode - using normal mode"); 132754194Speter } 132853344Speter if (ret) 132953344Speter device_printf(dev, "could not activate interrupt\n"); 133053344Speter } 133151078Speter 133251078Speter return (0); 133351078Speter} 133451078Speter 133551078Speterstatic int 133651078Spetersioopen(dev, flag, mode, p) 133751078Speter dev_t dev; 133851078Speter int flag; 133951078Speter int mode; 134051078Speter struct proc *p; 134151078Speter{ 134251078Speter struct com_s *com; 134351078Speter int error; 134451078Speter Port_t iobase; 134551078Speter int mynor; 134651078Speter int s; 134751078Speter struct tty *tp; 134851078Speter int unit; 134951078Speter 135051078Speter mynor = minor(dev); 135151078Speter unit = MINOR_TO_UNIT(mynor); 135253344Speter com = com_addr(unit); 135353344Speter if (com == NULL) 135451078Speter return (ENXIO); 135551078Speter if (com->gone) 135651078Speter return (ENXIO); 135751078Speter if (mynor & CONTROL_MASK) 135851078Speter return (0); 135951078Speter tp = dev->si_tty = com->tp = ttymalloc(com->tp); 136051078Speter s = spltty(); 136151078Speter /* 136251078Speter * We jump to this label after all non-interrupted sleeps to pick 136351078Speter * up any changes of the device state. 136451078Speter */ 136551078Speteropen_top: 136651078Speter while (com->state & CS_DTR_OFF) { 136751078Speter error = tsleep(&com->dtr_wait, TTIPRI | PCATCH, "siodtr", 0); 136851078Speter if (com_addr(unit) == NULL) 136951078Speter return (ENXIO); 137051078Speter if (error != 0 || com->gone) 137151078Speter goto out; 137251078Speter } 137351078Speter if (tp->t_state & TS_ISOPEN) { 137451078Speter /* 137551078Speter * The device is open, so everything has been initialized. 137651078Speter * Handle conflicts. 137751078Speter */ 137851078Speter if (mynor & CALLOUT_MASK) { 137951078Speter if (!com->active_out) { 138051078Speter error = EBUSY; 138151078Speter goto out; 138251078Speter } 138351078Speter } else { 138451078Speter if (com->active_out) { 138551078Speter if (flag & O_NONBLOCK) { 138651078Speter error = EBUSY; 138751078Speter goto out; 138851078Speter } 138951078Speter error = tsleep(&com->active_out, 139051078Speter TTIPRI | PCATCH, "siobi", 0); 139151078Speter if (com_addr(unit) == NULL) 139251078Speter return (ENXIO); 139351078Speter if (error != 0 || com->gone) 139451078Speter goto out; 139551078Speter goto open_top; 139651078Speter } 139751078Speter } 139851078Speter if (tp->t_state & TS_XCLUDE && 139951078Speter suser(p)) { 140051078Speter error = EBUSY; 140151078Speter goto out; 140251078Speter } 140351078Speter } else { 140451078Speter /* 140551078Speter * The device isn't open, so there are no conflicts. 140651078Speter * Initialize it. Initialization is done twice in many 140751078Speter * cases: to preempt sleeping callin opens if we are 140851078Speter * callout, and to complete a callin open after DCD rises. 140951078Speter */ 141051078Speter tp->t_oproc = comstart; 141151078Speter tp->t_param = comparam; 141251654Sphk tp->t_stop = comstop; 141351078Speter tp->t_dev = dev; 141451078Speter tp->t_termios = mynor & CALLOUT_MASK 141551078Speter ? com->it_out : com->it_in; 141651078Speter (void)commctl(com, TIOCM_DTR | TIOCM_RTS, DMSET); 141751078Speter com->poll = com->no_irq; 141851078Speter com->poll_output = com->loses_outints; 141951078Speter ++com->wopeners; 142051078Speter error = comparam(tp, &tp->t_termios); 142151078Speter --com->wopeners; 142251078Speter if (error != 0) 142351078Speter goto out; 142451078Speter /* 142551078Speter * XXX we should goto open_top if comparam() slept. 142651078Speter */ 142751078Speter iobase = com->iobase; 142851078Speter if (com->hasfifo) { 142951078Speter /* 143051078Speter * (Re)enable and drain fifos. 143151078Speter * 143251078Speter * Certain SMC chips cause problems if the fifos 143351078Speter * are enabled while input is ready. Turn off the 143451078Speter * fifo if necessary to clear the input. We test 143551078Speter * the input ready bit after enabling the fifos 143651078Speter * since we've already enabled them in comparam() 143751078Speter * and to handle races between enabling and fresh 143851078Speter * input. 143951078Speter */ 144051078Speter while (TRUE) { 144151078Speter outb(iobase + com_fifo, 144251078Speter FIFO_RCV_RST | FIFO_XMT_RST 144351078Speter | com->fifo_image); 144451078Speter /* 144551078Speter * XXX the delays are for superstitious 144651078Speter * historical reasons. It must be less than 144751078Speter * the character time at the maximum 144851078Speter * supported speed (87 usec at 115200 bps 144951078Speter * 8N1). Otherwise we might loop endlessly 145051078Speter * if data is streaming in. We used to use 145151078Speter * delays of 100. That usually worked 145251078Speter * because DELAY(100) used to usually delay 145351078Speter * for about 85 usec instead of 100. 145451078Speter */ 145551078Speter DELAY(50); 145651078Speter if (!(inb(com->line_status_port) & LSR_RXRDY)) 145751078Speter break; 145851078Speter outb(iobase + com_fifo, 0); 145951078Speter DELAY(50); 146051078Speter (void) inb(com->data_port); 146151078Speter } 146251078Speter } 146351078Speter 146451078Speter disable_intr(); 146551078Speter (void) inb(com->line_status_port); 146651078Speter (void) inb(com->data_port); 146751078Speter com->prev_modem_status = com->last_modem_status 146851078Speter = inb(com->modem_status_port); 146951078Speter if (COM_IIR_TXRDYBUG(com->flags)) { 147051078Speter outb(com->intr_ctl_port, IER_ERXRDY | IER_ERLS 147151078Speter | IER_EMSC); 147251078Speter } else { 147351078Speter outb(com->intr_ctl_port, IER_ERXRDY | IER_ETXRDY 147451078Speter | IER_ERLS | IER_EMSC); 147551078Speter } 147651078Speter enable_intr(); 147751078Speter /* 147851078Speter * Handle initial DCD. Callout devices get a fake initial 147951078Speter * DCD (trapdoor DCD). If we are callout, then any sleeping 148051078Speter * callin opens get woken up and resume sleeping on "siobi" 148151078Speter * instead of "siodcd". 148251078Speter */ 148351078Speter /* 148451078Speter * XXX `mynor & CALLOUT_MASK' should be 148551078Speter * `tp->t_cflag & (SOFT_CARRIER | TRAPDOOR_CARRIER) where 148651078Speter * TRAPDOOR_CARRIER is the default initial state for callout 148751078Speter * devices and SOFT_CARRIER is like CLOCAL except it hides 148851078Speter * the true carrier. 148951078Speter */ 149051078Speter if (com->prev_modem_status & MSR_DCD || mynor & CALLOUT_MASK) 149151078Speter (*linesw[tp->t_line].l_modem)(tp, 1); 149251078Speter } 149351078Speter /* 149451078Speter * Wait for DCD if necessary. 149551078Speter */ 149651078Speter if (!(tp->t_state & TS_CARR_ON) && !(mynor & CALLOUT_MASK) 149751078Speter && !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK)) { 149851078Speter ++com->wopeners; 149951078Speter error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "siodcd", 0); 150051078Speter if (com_addr(unit) == NULL) 150151078Speter return (ENXIO); 150251078Speter --com->wopeners; 150351078Speter if (error != 0 || com->gone) 150451078Speter goto out; 150551078Speter goto open_top; 150651078Speter } 150751078Speter error = (*linesw[tp->t_line].l_open)(dev, tp); 150851078Speter disc_optim(tp, &tp->t_termios, com); 150951078Speter if (tp->t_state & TS_ISOPEN && mynor & CALLOUT_MASK) 151051078Speter com->active_out = TRUE; 151151078Speter siosettimeout(); 151251078Speterout: 151351078Speter splx(s); 151451078Speter if (!(tp->t_state & TS_ISOPEN) && com->wopeners == 0) 151551078Speter comhardclose(com); 151651078Speter return (error); 151751078Speter} 151851078Speter 151951078Speterstatic int 152051078Spetersioclose(dev, flag, mode, p) 152151078Speter dev_t dev; 152251078Speter int flag; 152351078Speter int mode; 152451078Speter struct proc *p; 152551078Speter{ 152651078Speter struct com_s *com; 152751078Speter int mynor; 152851078Speter int s; 152951078Speter struct tty *tp; 153051078Speter 153151078Speter mynor = minor(dev); 153251078Speter if (mynor & CONTROL_MASK) 153351078Speter return (0); 153451078Speter com = com_addr(MINOR_TO_UNIT(mynor)); 153557915Simp if (com == NULL) 153657915Simp return (ENODEV); 153751078Speter tp = com->tp; 153851078Speter s = spltty(); 153951078Speter (*linesw[tp->t_line].l_close)(tp, flag); 154051078Speter disc_optim(tp, &tp->t_termios, com); 154151654Sphk comstop(tp, FREAD | FWRITE); 154251078Speter comhardclose(com); 154351078Speter ttyclose(tp); 154451078Speter siosettimeout(); 154551078Speter splx(s); 154651078Speter if (com->gone) { 154751078Speter printf("sio%d: gone\n", com->unit); 154851078Speter s = spltty(); 154951078Speter if (com->ibuf != NULL) 155051078Speter free(com->ibuf, M_DEVBUF); 155151078Speter bzero(tp, sizeof *tp); 155251078Speter splx(s); 155351078Speter } 155451078Speter return (0); 155551078Speter} 155651078Speter 155751078Speterstatic void 155851078Spetercomhardclose(com) 155951078Speter struct com_s *com; 156051078Speter{ 156151078Speter Port_t iobase; 156251078Speter int s; 156351078Speter struct tty *tp; 156451078Speter int unit; 156551078Speter 156651078Speter unit = com->unit; 156751078Speter iobase = com->iobase; 156851078Speter s = spltty(); 156951078Speter com->poll = FALSE; 157051078Speter com->poll_output = FALSE; 157151078Speter com->do_timestamp = FALSE; 157251078Speter com->do_dcd_timestamp = FALSE; 157351078Speter com->pps.ppsparam.mode = 0; 157451078Speter outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); 157551078Speter { 157651078Speter outb(iobase + com_ier, 0); 157751078Speter tp = com->tp; 157851078Speter if (tp->t_cflag & HUPCL 157951078Speter /* 158051078Speter * XXX we will miss any carrier drop between here and the 158151078Speter * next open. Perhaps we should watch DCD even when the 158251078Speter * port is closed; it is not sufficient to check it at 158351078Speter * the next open because it might go up and down while 158451078Speter * we're not watching. 158551078Speter */ 158651078Speter || (!com->active_out 158751078Speter && !(com->prev_modem_status & MSR_DCD) 158851078Speter && !(com->it_in.c_cflag & CLOCAL)) 158951078Speter || !(tp->t_state & TS_ISOPEN)) { 159051078Speter (void)commctl(com, TIOCM_DTR, DMBIC); 159151078Speter if (com->dtr_wait != 0 && !(com->state & CS_DTR_OFF)) { 159251078Speter timeout(siodtrwakeup, com, com->dtr_wait); 159351078Speter com->state |= CS_DTR_OFF; 159451078Speter } 159551078Speter } 159651078Speter } 159751078Speter if (com->hasfifo) { 159851078Speter /* 159951078Speter * Disable fifos so that they are off after controlled 160051078Speter * reboots. Some BIOSes fail to detect 16550s when the 160151078Speter * fifos are enabled. 160251078Speter */ 160351078Speter outb(iobase + com_fifo, 0); 160451078Speter } 160551078Speter com->active_out = FALSE; 160651078Speter wakeup(&com->active_out); 160751078Speter wakeup(TSA_CARR_ON(tp)); /* restart any wopeners */ 160851078Speter splx(s); 160951078Speter} 161051078Speter 161151078Speterstatic int 161251078Spetersioread(dev, uio, flag) 161351078Speter dev_t dev; 161451078Speter struct uio *uio; 161551078Speter int flag; 161651078Speter{ 161751078Speter int mynor; 161851078Speter struct com_s *com; 161951078Speter 162051078Speter mynor = minor(dev); 162151078Speter if (mynor & CONTROL_MASK) 162251078Speter return (ENODEV); 162351078Speter com = com_addr(MINOR_TO_UNIT(mynor)); 162457915Simp if (com == NULL || com->gone) 162551078Speter return (ENODEV); 162651078Speter return ((*linesw[com->tp->t_line].l_read)(com->tp, uio, flag)); 162751078Speter} 162851078Speter 162951078Speterstatic int 163051078Spetersiowrite(dev, uio, flag) 163151078Speter dev_t dev; 163251078Speter struct uio *uio; 163351078Speter int flag; 163451078Speter{ 163551078Speter int mynor; 163651078Speter struct com_s *com; 163751078Speter int unit; 163851078Speter 163951078Speter mynor = minor(dev); 164051078Speter if (mynor & CONTROL_MASK) 164151078Speter return (ENODEV); 164251078Speter 164351078Speter unit = MINOR_TO_UNIT(mynor); 164451078Speter com = com_addr(unit); 164557915Simp if (com == NULL || com->gone) 164651078Speter return (ENODEV); 164751078Speter /* 164851078Speter * (XXX) We disallow virtual consoles if the physical console is 164951078Speter * a serial port. This is in case there is a display attached that 165051078Speter * is not the console. In that situation we don't need/want the X 165151078Speter * server taking over the console. 165251078Speter */ 165351078Speter if (constty != NULL && unit == comconsole) 165451078Speter constty = NULL; 165551078Speter return ((*linesw[com->tp->t_line].l_write)(com->tp, uio, flag)); 165651078Speter} 165751078Speter 165851078Speterstatic void 165951078Spetersiobusycheck(chan) 166051078Speter void *chan; 166151078Speter{ 166251078Speter struct com_s *com; 166351078Speter int s; 166451078Speter 166551078Speter com = (struct com_s *)chan; 166651078Speter 166751078Speter /* 166851078Speter * Clear TS_BUSY if low-level output is complete. 166951078Speter * spl locking is sufficient because siointr1() does not set CS_BUSY. 167051078Speter * If siointr1() clears CS_BUSY after we look at it, then we'll get 167151078Speter * called again. Reading the line status port outside of siointr1() 167251078Speter * is safe because CS_BUSY is clear so there are no output interrupts 167351078Speter * to lose. 167451078Speter */ 167551078Speter s = spltty(); 167651078Speter if (com->state & CS_BUSY) 167751078Speter com->extra_state &= ~CSE_BUSYCHECK; /* False alarm. */ 167851078Speter else if ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY)) 167951078Speter == (LSR_TSRE | LSR_TXRDY)) { 168051078Speter com->tp->t_state &= ~TS_BUSY; 168151078Speter ttwwakeup(com->tp); 168251078Speter com->extra_state &= ~CSE_BUSYCHECK; 168351078Speter } else 168451078Speter timeout(siobusycheck, com, hz / 100); 168551078Speter splx(s); 168651078Speter} 168751078Speter 168851078Speterstatic void 168951078Spetersiodtrwakeup(chan) 169051078Speter void *chan; 169151078Speter{ 169251078Speter struct com_s *com; 169351078Speter 169451078Speter com = (struct com_s *)chan; 169551078Speter com->state &= ~CS_DTR_OFF; 169651078Speter wakeup(&com->dtr_wait); 169751078Speter} 169851078Speter 169951078Speterstatic void 170051078Spetersioinput(com) 170151078Speter struct com_s *com; 170251078Speter{ 170351078Speter u_char *buf; 170451078Speter int incc; 170551078Speter u_char line_status; 170651078Speter int recv_data; 170751078Speter struct tty *tp; 170851078Speter 170951078Speter buf = com->ibuf; 171051078Speter tp = com->tp; 171151078Speter if (!(tp->t_state & TS_ISOPEN) || !(tp->t_cflag & CREAD)) { 171251078Speter com_events -= (com->iptr - com->ibuf); 171351078Speter com->iptr = com->ibuf; 171451078Speter return; 171551078Speter } 171651078Speter if (tp->t_state & TS_CAN_BYPASS_L_RINT) { 171751078Speter /* 171851078Speter * Avoid the grotesquely inefficient lineswitch routine 171951078Speter * (ttyinput) in "raw" mode. It usually takes about 450 172051078Speter * instructions (that's without canonical processing or echo!). 172151078Speter * slinput is reasonably fast (usually 40 instructions plus 172251078Speter * call overhead). 172351078Speter */ 172451078Speter do { 172551078Speter enable_intr(); 172651078Speter incc = com->iptr - buf; 172751078Speter if (tp->t_rawq.c_cc + incc > tp->t_ihiwat 172851078Speter && (com->state & CS_RTS_IFLOW 172951078Speter || tp->t_iflag & IXOFF) 173051078Speter && !(tp->t_state & TS_TBLOCK)) 173151078Speter ttyblock(tp); 173251078Speter com->delta_error_counts[CE_TTY_BUF_OVERFLOW] 173351078Speter += b_to_q((char *)buf, incc, &tp->t_rawq); 173451078Speter buf += incc; 173551078Speter tk_nin += incc; 173651078Speter tk_rawcc += incc; 173751078Speter tp->t_rawcc += incc; 173851078Speter ttwakeup(tp); 173951078Speter if (tp->t_state & TS_TTSTOP 174051078Speter && (tp->t_iflag & IXANY 174151078Speter || tp->t_cc[VSTART] == tp->t_cc[VSTOP])) { 174251078Speter tp->t_state &= ~TS_TTSTOP; 174351078Speter tp->t_lflag &= ~FLUSHO; 174451078Speter comstart(tp); 174551078Speter } 174651078Speter disable_intr(); 174751078Speter } while (buf < com->iptr); 174851078Speter } else { 174951078Speter do { 175051078Speter enable_intr(); 175151078Speter line_status = buf[com->ierroff]; 175251078Speter recv_data = *buf++; 175351078Speter if (line_status 175451078Speter & (LSR_BI | LSR_FE | LSR_OE | LSR_PE)) { 175551078Speter if (line_status & LSR_BI) 175651078Speter recv_data |= TTY_BI; 175751078Speter if (line_status & LSR_FE) 175851078Speter recv_data |= TTY_FE; 175951078Speter if (line_status & LSR_OE) 176051078Speter recv_data |= TTY_OE; 176151078Speter if (line_status & LSR_PE) 176251078Speter recv_data |= TTY_PE; 176351078Speter } 176451078Speter (*linesw[tp->t_line].l_rint)(recv_data, tp); 176551078Speter disable_intr(); 176651078Speter } while (buf < com->iptr); 176751078Speter } 176851078Speter com_events -= (com->iptr - com->ibuf); 176951078Speter com->iptr = com->ibuf; 177051078Speter 177151078Speter /* 177251078Speter * There is now room for another low-level buffer full of input, 177351078Speter * so enable RTS if it is now disabled and there is room in the 177451078Speter * high-level buffer. 177551078Speter */ 177651078Speter if ((com->state & CS_RTS_IFLOW) && !(com->mcr_image & MCR_RTS) && 177751078Speter !(tp->t_state & TS_TBLOCK)) 177851078Speter outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); 177951078Speter} 178051078Speter 178151078Spetervoid 178251078Spetersiointr(arg) 178351078Speter void *arg; 178451078Speter{ 178551078Speter#ifndef COM_MULTIPORT 178651078Speter COM_LOCK(); 178751078Speter siointr1((struct com_s *) arg); 178851078Speter COM_UNLOCK(); 178951078Speter#else /* COM_MULTIPORT */ 179051078Speter bool_t possibly_more_intrs; 179151078Speter int unit; 179251078Speter struct com_s *com; 179351078Speter 179451078Speter /* 179551078Speter * Loop until there is no activity on any port. This is necessary 179651078Speter * to get an interrupt edge more than to avoid another interrupt. 179751078Speter * If the IRQ signal is just an OR of the IRQ signals from several 179851078Speter * devices, then the edge from one may be lost because another is 179951078Speter * on. 180051078Speter */ 180151078Speter COM_LOCK(); 180251078Speter do { 180351078Speter possibly_more_intrs = FALSE; 180453344Speter for (unit = 0; unit < sio_numunits; ++unit) { 180551078Speter com = com_addr(unit); 180651078Speter /* 180751078Speter * XXX COM_LOCK(); 180851078Speter * would it work here, or be counter-productive? 180951078Speter */ 181051078Speter if (com != NULL 181151078Speter && !com->gone 181251078Speter && (inb(com->int_id_port) & IIR_IMASK) 181351078Speter != IIR_NOPEND) { 181451078Speter siointr1(com); 181551078Speter possibly_more_intrs = TRUE; 181651078Speter } 181751078Speter /* XXX COM_UNLOCK(); */ 181851078Speter } 181951078Speter } while (possibly_more_intrs); 182051078Speter COM_UNLOCK(); 182151078Speter#endif /* COM_MULTIPORT */ 182251078Speter} 182351078Speter 182451078Speterstatic void 182551078Spetersiointr1(com) 182651078Speter struct com_s *com; 182751078Speter{ 182851078Speter u_char line_status; 182951078Speter u_char modem_status; 183051078Speter u_char *ioptr; 183151078Speter u_char recv_data; 183251078Speter u_char int_ctl; 183351078Speter u_char int_ctl_new; 183451078Speter struct timecounter *tc; 183551078Speter u_int count; 183651078Speter 183751078Speter int_ctl = inb(com->intr_ctl_port); 183851078Speter int_ctl_new = int_ctl; 183951078Speter 184051078Speter while (!com->gone) { 184151078Speter if (com->pps.ppsparam.mode & PPS_CAPTUREBOTH) { 184251078Speter modem_status = inb(com->modem_status_port); 184351078Speter if ((modem_status ^ com->last_modem_status) & MSR_DCD) { 184451078Speter tc = timecounter; 184551078Speter count = tc->tc_get_timecount(tc); 184651078Speter pps_event(&com->pps, tc, count, 184751078Speter (modem_status & MSR_DCD) ? 184851078Speter PPS_CAPTUREASSERT : PPS_CAPTURECLEAR); 184951078Speter } 185051078Speter } 185151078Speter line_status = inb(com->line_status_port); 185251078Speter 185351078Speter /* input event? (check first to help avoid overruns) */ 185451078Speter while (line_status & LSR_RCV_MASK) { 185551078Speter /* break/unnattached error bits or real input? */ 185651078Speter if (!(line_status & LSR_RXRDY)) 185751078Speter recv_data = 0; 185851078Speter else 185951078Speter recv_data = inb(com->data_port); 186051078Speter if (line_status & (LSR_BI | LSR_FE | LSR_PE)) { 186151078Speter /* 186251078Speter * Don't store BI if IGNBRK or FE/PE if IGNPAR. 186351078Speter * Otherwise, push the work to a higher level 186451078Speter * (to handle PARMRK) if we're bypassing. 186551078Speter * Otherwise, convert BI/FE and PE+INPCK to 0. 186651078Speter * 186751078Speter * This makes bypassing work right in the 186851078Speter * usual "raw" case (IGNBRK set, and IGNPAR 186951078Speter * and INPCK clear). 187051078Speter * 187151078Speter * Note: BI together with FE/PE means just BI. 187251078Speter */ 187351078Speter if (line_status & LSR_BI) { 187451078Speter#if defined(DDB) && defined(BREAK_TO_DEBUGGER) 187551078Speter if (com->unit == comconsole) { 187651078Speter breakpoint(); 187751078Speter goto cont; 187851078Speter } 187951078Speter#endif 188051078Speter if (com->tp == NULL 188151078Speter || com->tp->t_iflag & IGNBRK) 188251078Speter goto cont; 188351078Speter } else { 188451078Speter if (com->tp == NULL 188551078Speter || com->tp->t_iflag & IGNPAR) 188651078Speter goto cont; 188751078Speter } 188851078Speter if (com->tp->t_state & TS_CAN_BYPASS_L_RINT 188951078Speter && (line_status & (LSR_BI | LSR_FE) 189051078Speter || com->tp->t_iflag & INPCK)) 189151078Speter recv_data = 0; 189251078Speter } 189351078Speter ++com->bytes_in; 189451078Speter if (com->hotchar != 0 && recv_data == com->hotchar) 189551078Speter setsofttty(); 189651078Speter ioptr = com->iptr; 189751078Speter if (ioptr >= com->ibufend) 189851078Speter CE_RECORD(com, CE_INTERRUPT_BUF_OVERFLOW); 189951078Speter else { 190051078Speter if (com->do_timestamp) 190151078Speter microtime(&com->timestamp); 190251078Speter ++com_events; 190351078Speter schedsofttty(); 190451078Speter#if 0 /* for testing input latency vs efficiency */ 190551078Speterif (com->iptr - com->ibuf == 8) 190651078Speter setsofttty(); 190751078Speter#endif 190851078Speter ioptr[0] = recv_data; 190951078Speter ioptr[com->ierroff] = line_status; 191051078Speter com->iptr = ++ioptr; 191151078Speter if (ioptr == com->ihighwater 191251078Speter && com->state & CS_RTS_IFLOW) 191351078Speter outb(com->modem_ctl_port, 191451078Speter com->mcr_image &= ~MCR_RTS); 191551078Speter if (line_status & LSR_OE) 191651078Speter CE_RECORD(com, CE_OVERRUN); 191751078Speter } 191851078Spetercont: 191951078Speter /* 192051078Speter * "& 0x7F" is to avoid the gcc-1.40 generating a slow 192151078Speter * jump from the top of the loop to here 192251078Speter */ 192351078Speter line_status = inb(com->line_status_port) & 0x7F; 192451078Speter } 192551078Speter 192651078Speter /* modem status change? (always check before doing output) */ 192751078Speter modem_status = inb(com->modem_status_port); 192851078Speter if (modem_status != com->last_modem_status) { 192951078Speter if (com->do_dcd_timestamp 193051078Speter && !(com->last_modem_status & MSR_DCD) 193151078Speter && modem_status & MSR_DCD) 193251078Speter microtime(&com->dcd_timestamp); 193351078Speter 193451078Speter /* 193551078Speter * Schedule high level to handle DCD changes. Note 193651078Speter * that we don't use the delta bits anywhere. Some 193751078Speter * UARTs mess them up, and it's easy to remember the 193851078Speter * previous bits and calculate the delta. 193951078Speter */ 194051078Speter com->last_modem_status = modem_status; 194151078Speter if (!(com->state & CS_CHECKMSR)) { 194251078Speter com_events += LOTS_OF_EVENTS; 194351078Speter com->state |= CS_CHECKMSR; 194451078Speter setsofttty(); 194551078Speter } 194651078Speter 194751078Speter /* handle CTS change immediately for crisp flow ctl */ 194851078Speter if (com->state & CS_CTS_OFLOW) { 194951078Speter if (modem_status & MSR_CTS) 195051078Speter com->state |= CS_ODEVREADY; 195151078Speter else 195251078Speter com->state &= ~CS_ODEVREADY; 195351078Speter } 195451078Speter } 195551078Speter 195651078Speter /* output queued and everything ready? */ 195751078Speter if (line_status & LSR_TXRDY 195851078Speter && com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) { 195951078Speter ioptr = com->obufq.l_head; 196051078Speter if (com->tx_fifo_size > 1) { 196151078Speter u_int ocount; 196251078Speter 196351078Speter ocount = com->obufq.l_tail - ioptr; 196451078Speter if (ocount > com->tx_fifo_size) 196551078Speter ocount = com->tx_fifo_size; 196651078Speter com->bytes_out += ocount; 196751078Speter do 196851078Speter outb(com->data_port, *ioptr++); 196951078Speter while (--ocount != 0); 197051078Speter } else { 197151078Speter outb(com->data_port, *ioptr++); 197251078Speter ++com->bytes_out; 197351078Speter } 197451078Speter com->obufq.l_head = ioptr; 197551078Speter if (COM_IIR_TXRDYBUG(com->flags)) { 197651078Speter int_ctl_new = int_ctl | IER_ETXRDY; 197751078Speter } 197851078Speter if (ioptr >= com->obufq.l_tail) { 197951078Speter struct lbq *qp; 198051078Speter 198151078Speter qp = com->obufq.l_next; 198251078Speter qp->l_queued = FALSE; 198351078Speter qp = qp->l_next; 198451078Speter if (qp != NULL) { 198551078Speter com->obufq.l_head = qp->l_head; 198651078Speter com->obufq.l_tail = qp->l_tail; 198751078Speter com->obufq.l_next = qp; 198851078Speter } else { 198951078Speter /* output just completed */ 199053344Speter if (COM_IIR_TXRDYBUG(com->flags)) { 199151078Speter int_ctl_new = int_ctl & ~IER_ETXRDY; 199251078Speter } 199351078Speter com->state &= ~CS_BUSY; 199451078Speter } 199551078Speter if (!(com->state & CS_ODONE)) { 199651078Speter com_events += LOTS_OF_EVENTS; 199751078Speter com->state |= CS_ODONE; 199851078Speter setsofttty(); /* handle at high level ASAP */ 199951078Speter } 200051078Speter } 200153344Speter if (COM_IIR_TXRDYBUG(com->flags) && (int_ctl != int_ctl_new)) { 200251078Speter outb(com->intr_ctl_port, int_ctl_new); 200351078Speter } 200451078Speter } 200551078Speter 200651078Speter /* finished? */ 200751078Speter#ifndef COM_MULTIPORT 200851078Speter if ((inb(com->int_id_port) & IIR_IMASK) == IIR_NOPEND) 200951078Speter#endif /* COM_MULTIPORT */ 201051078Speter return; 201151078Speter } 201251078Speter} 201351078Speter 201451078Speterstatic int 201551078Spetersioioctl(dev, cmd, data, flag, p) 201651078Speter dev_t dev; 201751078Speter u_long cmd; 201851078Speter caddr_t data; 201951078Speter int flag; 202051078Speter struct proc *p; 202151078Speter{ 202251078Speter struct com_s *com; 202351078Speter int error; 202451078Speter Port_t iobase; 202551078Speter int mynor; 202651078Speter int s; 202751078Speter struct tty *tp; 202851078Speter#if defined(COMPAT_43) || defined(COMPAT_SUNOS) 202951078Speter u_long oldcmd; 203051078Speter struct termios term; 203151078Speter#endif 203251078Speter 203351078Speter mynor = minor(dev); 203451078Speter com = com_addr(MINOR_TO_UNIT(mynor)); 203557915Simp if (com == NULL || com->gone) 203651078Speter return (ENODEV); 203751078Speter iobase = com->iobase; 203851078Speter if (mynor & CONTROL_MASK) { 203951078Speter struct termios *ct; 204051078Speter 204151078Speter switch (mynor & CONTROL_MASK) { 204251078Speter case CONTROL_INIT_STATE: 204351078Speter ct = mynor & CALLOUT_MASK ? &com->it_out : &com->it_in; 204451078Speter break; 204551078Speter case CONTROL_LOCK_STATE: 204651078Speter ct = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in; 204751078Speter break; 204851078Speter default: 204951078Speter return (ENODEV); /* /dev/nodev */ 205051078Speter } 205151078Speter switch (cmd) { 205251078Speter case TIOCSETA: 205351078Speter error = suser(p); 205451078Speter if (error != 0) 205551078Speter return (error); 205651078Speter *ct = *(struct termios *)data; 205751078Speter return (0); 205851078Speter case TIOCGETA: 205951078Speter *(struct termios *)data = *ct; 206051078Speter return (0); 206151078Speter case TIOCGETD: 206251078Speter *(int *)data = TTYDISC; 206351078Speter return (0); 206451078Speter case TIOCGWINSZ: 206551078Speter bzero(data, sizeof(struct winsize)); 206651078Speter return (0); 206751078Speter default: 206851078Speter return (ENOTTY); 206951078Speter } 207051078Speter } 207151078Speter tp = com->tp; 207251078Speter#if defined(COMPAT_43) || defined(COMPAT_SUNOS) 207351078Speter term = tp->t_termios; 207451078Speter oldcmd = cmd; 207551078Speter error = ttsetcompat(tp, &cmd, data, &term); 207651078Speter if (error != 0) 207751078Speter return (error); 207851078Speter if (cmd != oldcmd) 207951078Speter data = (caddr_t)&term; 208051078Speter#endif 208151078Speter if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { 208251078Speter int cc; 208351078Speter struct termios *dt = (struct termios *)data; 208451078Speter struct termios *lt = mynor & CALLOUT_MASK 208551078Speter ? &com->lt_out : &com->lt_in; 208651078Speter 208751078Speter dt->c_iflag = (tp->t_iflag & lt->c_iflag) 208851078Speter | (dt->c_iflag & ~lt->c_iflag); 208951078Speter dt->c_oflag = (tp->t_oflag & lt->c_oflag) 209051078Speter | (dt->c_oflag & ~lt->c_oflag); 209151078Speter dt->c_cflag = (tp->t_cflag & lt->c_cflag) 209251078Speter | (dt->c_cflag & ~lt->c_cflag); 209351078Speter dt->c_lflag = (tp->t_lflag & lt->c_lflag) 209451078Speter | (dt->c_lflag & ~lt->c_lflag); 209551078Speter for (cc = 0; cc < NCCS; ++cc) 209651078Speter if (lt->c_cc[cc] != 0) 209751078Speter dt->c_cc[cc] = tp->t_cc[cc]; 209851078Speter if (lt->c_ispeed != 0) 209951078Speter dt->c_ispeed = tp->t_ispeed; 210051078Speter if (lt->c_ospeed != 0) 210151078Speter dt->c_ospeed = tp->t_ospeed; 210251078Speter } 210351078Speter error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); 210451078Speter if (error != ENOIOCTL) 210551078Speter return (error); 210651078Speter s = spltty(); 210751078Speter error = ttioctl(tp, cmd, data, flag); 210851078Speter disc_optim(tp, &tp->t_termios, com); 210951078Speter if (error != ENOIOCTL) { 211051078Speter splx(s); 211151078Speter return (error); 211251078Speter } 211351078Speter switch (cmd) { 211451078Speter case TIOCSBRK: 211551078Speter outb(iobase + com_cfcr, com->cfcr_image |= CFCR_SBREAK); 211651078Speter break; 211751078Speter case TIOCCBRK: 211851078Speter outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); 211951078Speter break; 212051078Speter case TIOCSDTR: 212151078Speter (void)commctl(com, TIOCM_DTR, DMBIS); 212251078Speter break; 212351078Speter case TIOCCDTR: 212451078Speter (void)commctl(com, TIOCM_DTR, DMBIC); 212551078Speter break; 212651078Speter /* 212751078Speter * XXX should disallow changing MCR_RTS if CS_RTS_IFLOW is set. The 212851078Speter * changes get undone on the next call to comparam(). 212951078Speter */ 213051078Speter case TIOCMSET: 213151078Speter (void)commctl(com, *(int *)data, DMSET); 213251078Speter break; 213351078Speter case TIOCMBIS: 213451078Speter (void)commctl(com, *(int *)data, DMBIS); 213551078Speter break; 213651078Speter case TIOCMBIC: 213751078Speter (void)commctl(com, *(int *)data, DMBIC); 213851078Speter break; 213951078Speter case TIOCMGET: 214051078Speter *(int *)data = commctl(com, 0, DMGET); 214151078Speter break; 214251078Speter case TIOCMSDTRWAIT: 214351078Speter /* must be root since the wait applies to following logins */ 214451078Speter error = suser(p); 214551078Speter if (error != 0) { 214651078Speter splx(s); 214751078Speter return (error); 214851078Speter } 214951078Speter com->dtr_wait = *(int *)data * hz / 100; 215051078Speter break; 215151078Speter case TIOCMGDTRWAIT: 215251078Speter *(int *)data = com->dtr_wait * 100 / hz; 215351078Speter break; 215451078Speter case TIOCTIMESTAMP: 215551078Speter com->do_timestamp = TRUE; 215651078Speter *(struct timeval *)data = com->timestamp; 215751078Speter break; 215851078Speter case TIOCDCDTIMESTAMP: 215951078Speter com->do_dcd_timestamp = TRUE; 216051078Speter *(struct timeval *)data = com->dcd_timestamp; 216151078Speter break; 216251078Speter default: 216351078Speter splx(s); 216451078Speter error = pps_ioctl(cmd, data, &com->pps); 216551078Speter if (error == ENODEV) 216651078Speter error = ENOTTY; 216751078Speter return (error); 216851078Speter } 216951078Speter splx(s); 217051078Speter return (0); 217151078Speter} 217251078Speter 217351078Speterstatic void 217451078Spetersiopoll() 217551078Speter{ 217651078Speter int unit; 217751078Speter 217851078Speter if (com_events == 0) 217951078Speter return; 218051078Speterrepeat: 218153344Speter for (unit = 0; unit < sio_numunits; ++unit) { 218251078Speter struct com_s *com; 218351078Speter int incc; 218451078Speter struct tty *tp; 218551078Speter 218651078Speter com = com_addr(unit); 218751078Speter if (com == NULL) 218851078Speter continue; 218951078Speter tp = com->tp; 219051078Speter if (tp == NULL || com->gone) { 219151078Speter /* 219251078Speter * Discard any events related to never-opened or 219351078Speter * going-away devices. 219451078Speter */ 219551078Speter disable_intr(); 219651078Speter incc = com->iptr - com->ibuf; 219751078Speter com->iptr = com->ibuf; 219851078Speter if (com->state & CS_CHECKMSR) { 219951078Speter incc += LOTS_OF_EVENTS; 220051078Speter com->state &= ~CS_CHECKMSR; 220151078Speter } 220251078Speter com_events -= incc; 220351078Speter enable_intr(); 220451078Speter continue; 220551078Speter } 220651078Speter if (com->iptr != com->ibuf) { 220751078Speter disable_intr(); 220851078Speter sioinput(com); 220951078Speter enable_intr(); 221051078Speter } 221151078Speter if (com->state & CS_CHECKMSR) { 221251078Speter u_char delta_modem_status; 221351078Speter 221451078Speter disable_intr(); 221551078Speter delta_modem_status = com->last_modem_status 221651078Speter ^ com->prev_modem_status; 221751078Speter com->prev_modem_status = com->last_modem_status; 221851078Speter com_events -= LOTS_OF_EVENTS; 221951078Speter com->state &= ~CS_CHECKMSR; 222051078Speter enable_intr(); 222151078Speter if (delta_modem_status & MSR_DCD) 222251078Speter (*linesw[tp->t_line].l_modem) 222351078Speter (tp, com->prev_modem_status & MSR_DCD); 222451078Speter } 222551078Speter if (com->state & CS_ODONE) { 222651078Speter disable_intr(); 222751078Speter com_events -= LOTS_OF_EVENTS; 222851078Speter com->state &= ~CS_ODONE; 222951078Speter enable_intr(); 223051078Speter if (!(com->state & CS_BUSY) 223151078Speter && !(com->extra_state & CSE_BUSYCHECK)) { 223251078Speter timeout(siobusycheck, com, hz / 100); 223351078Speter com->extra_state |= CSE_BUSYCHECK; 223451078Speter } 223551078Speter (*linesw[tp->t_line].l_start)(tp); 223651078Speter } 223751078Speter if (com_events == 0) 223851078Speter break; 223951078Speter } 224051078Speter if (com_events >= LOTS_OF_EVENTS) 224151078Speter goto repeat; 224251078Speter} 224351078Speter 224451078Speterstatic int 224551078Spetercomparam(tp, t) 224651078Speter struct tty *tp; 224751078Speter struct termios *t; 224851078Speter{ 224951078Speter u_int cfcr; 225051078Speter int cflag; 225151078Speter struct com_s *com; 225251078Speter int divisor; 225351078Speter u_char dlbh; 225451078Speter u_char dlbl; 225551078Speter Port_t iobase; 225651078Speter int s; 225751078Speter int unit; 225851078Speter 225951078Speter /* do historical conversions */ 226051078Speter if (t->c_ispeed == 0) 226151078Speter t->c_ispeed = t->c_ospeed; 226251078Speter 226351078Speter /* check requested parameters */ 226451078Speter divisor = ttspeedtab(t->c_ospeed, comspeedtab); 226551078Speter if (divisor < 0 || (divisor > 0 && t->c_ispeed != t->c_ospeed)) 226651078Speter return (EINVAL); 226751078Speter 226851078Speter /* parameters are OK, convert them to the com struct and the device */ 226951078Speter unit = DEV_TO_UNIT(tp->t_dev); 227051078Speter com = com_addr(unit); 227157915Simp if (com == NULL) 227257915Simp return (ENODEV); 227351078Speter iobase = com->iobase; 227451078Speter s = spltty(); 227551078Speter if (divisor == 0) 227651078Speter (void)commctl(com, TIOCM_DTR, DMBIC); /* hang up line */ 227751078Speter else 227851078Speter (void)commctl(com, TIOCM_DTR, DMBIS); 227951078Speter cflag = t->c_cflag; 228051078Speter switch (cflag & CSIZE) { 228151078Speter case CS5: 228251078Speter cfcr = CFCR_5BITS; 228351078Speter break; 228451078Speter case CS6: 228551078Speter cfcr = CFCR_6BITS; 228651078Speter break; 228751078Speter case CS7: 228851078Speter cfcr = CFCR_7BITS; 228951078Speter break; 229051078Speter default: 229151078Speter cfcr = CFCR_8BITS; 229251078Speter break; 229351078Speter } 229451078Speter if (cflag & PARENB) { 229551078Speter cfcr |= CFCR_PENAB; 229651078Speter if (!(cflag & PARODD)) 229751078Speter cfcr |= CFCR_PEVEN; 229851078Speter } 229951078Speter if (cflag & CSTOPB) 230051078Speter cfcr |= CFCR_STOPB; 230151078Speter 230251078Speter if (com->hasfifo && divisor != 0) { 230351078Speter /* 230451078Speter * Use a fifo trigger level low enough so that the input 230551078Speter * latency from the fifo is less than about 16 msec and 230651078Speter * the total latency is less than about 30 msec. These 230751078Speter * latencies are reasonable for humans. Serial comms 230851078Speter * protocols shouldn't expect anything better since modem 230951078Speter * latencies are larger. 231051078Speter */ 231151078Speter com->fifo_image = t->c_ospeed <= 4800 231251078Speter ? FIFO_ENABLE : FIFO_ENABLE | FIFO_RX_HIGH; 231351078Speter#ifdef COM_ESP 231451078Speter /* 231551078Speter * The Hayes ESP card needs the fifo DMA mode bit set 231651078Speter * in compatibility mode. If not, it will interrupt 231751078Speter * for each character received. 231851078Speter */ 231951078Speter if (com->esp) 232051078Speter com->fifo_image |= FIFO_DMA_MODE; 232151078Speter#endif 232251078Speter outb(iobase + com_fifo, com->fifo_image); 232351078Speter } 232451078Speter 232551078Speter /* 232651078Speter * This returns with interrupts disabled so that we can complete 232751078Speter * the speed change atomically. Keeping interrupts disabled is 232851078Speter * especially important while com_data is hidden. 232951078Speter */ 233051078Speter (void) siosetwater(com, t->c_ispeed); 233151078Speter 233251078Speter if (divisor != 0) { 233351078Speter outb(iobase + com_cfcr, cfcr | CFCR_DLAB); 233451078Speter /* 233551078Speter * Only set the divisor registers if they would change, 233651078Speter * since on some 16550 incompatibles (UMC8669F), setting 233751078Speter * them while input is arriving them loses sync until 233851078Speter * data stops arriving. 233951078Speter */ 234051078Speter dlbl = divisor & 0xFF; 234151078Speter if (inb(iobase + com_dlbl) != dlbl) 234251078Speter outb(iobase + com_dlbl, dlbl); 234351078Speter dlbh = (u_int) divisor >> 8; 234451078Speter if (inb(iobase + com_dlbh) != dlbh) 234551078Speter outb(iobase + com_dlbh, dlbh); 234651078Speter } 234751078Speter 234851078Speter 234951078Speter outb(iobase + com_cfcr, com->cfcr_image = cfcr); 235051078Speter 235151078Speter if (!(tp->t_state & TS_TTSTOP)) 235251078Speter com->state |= CS_TTGO; 235351078Speter 235451078Speter if (cflag & CRTS_IFLOW) { 235551078Speter if (com->st16650a) { 235651078Speter outb(iobase + com_cfcr, 0xbf); 235751078Speter outb(iobase + com_fifo, inb(iobase + com_fifo) | 0x40); 235851078Speter } 235951078Speter com->state |= CS_RTS_IFLOW; 236051078Speter /* 236151078Speter * If CS_RTS_IFLOW just changed from off to on, the change 236251078Speter * needs to be propagated to MCR_RTS. This isn't urgent, 236351078Speter * so do it later by calling comstart() instead of repeating 236451078Speter * a lot of code from comstart() here. 236551078Speter */ 236651078Speter } else if (com->state & CS_RTS_IFLOW) { 236751078Speter com->state &= ~CS_RTS_IFLOW; 236851078Speter /* 236951078Speter * CS_RTS_IFLOW just changed from on to off. Force MCR_RTS 237051078Speter * on here, since comstart() won't do it later. 237151078Speter */ 237251078Speter outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); 237351078Speter if (com->st16650a) { 237451078Speter outb(iobase + com_cfcr, 0xbf); 237551078Speter outb(iobase + com_fifo, inb(iobase + com_fifo) & ~0x40); 237651078Speter } 237751078Speter } 237851078Speter 237951078Speter 238051078Speter /* 238151078Speter * Set up state to handle output flow control. 238251078Speter * XXX - worth handling MDMBUF (DCD) flow control at the lowest level? 238351078Speter * Now has 10+ msec latency, while CTS flow has 50- usec latency. 238451078Speter */ 238551078Speter com->state |= CS_ODEVREADY; 238651078Speter com->state &= ~CS_CTS_OFLOW; 238751078Speter if (cflag & CCTS_OFLOW) { 238851078Speter com->state |= CS_CTS_OFLOW; 238951078Speter if (!(com->last_modem_status & MSR_CTS)) 239051078Speter com->state &= ~CS_ODEVREADY; 239151078Speter if (com->st16650a) { 239251078Speter outb(iobase + com_cfcr, 0xbf); 239351078Speter outb(iobase + com_fifo, inb(iobase + com_fifo) | 0x80); 239451078Speter } 239551078Speter } else { 239651078Speter if (com->st16650a) { 239751078Speter outb(iobase + com_cfcr, 0xbf); 239851078Speter outb(iobase + com_fifo, inb(iobase + com_fifo) & ~0x80); 239951078Speter } 240051078Speter } 240151078Speter 240251078Speter 240351078Speter outb(iobase + com_cfcr, com->cfcr_image); 240451078Speter 240551078Speter 240651078Speter /* XXX shouldn't call functions while intrs are disabled. */ 240751078Speter disc_optim(tp, t, com); 240851078Speter /* 240951078Speter * Recover from fiddling with CS_TTGO. We used to call siointr1() 241051078Speter * unconditionally, but that defeated the careful discarding of 241151078Speter * stale input in sioopen(). 241251078Speter */ 241351078Speter if (com->state >= (CS_BUSY | CS_TTGO)) 241451078Speter siointr1(com); 241551078Speter 241651078Speter enable_intr(); 241751078Speter splx(s); 241851078Speter comstart(tp); 241951078Speter if (com->ibufold != NULL) { 242051078Speter free(com->ibufold, M_DEVBUF); 242151078Speter com->ibufold = NULL; 242251078Speter } 242351078Speter return (0); 242451078Speter} 242551078Speter 242651078Speterstatic int 242751078Spetersiosetwater(com, speed) 242851078Speter struct com_s *com; 242951078Speter speed_t speed; 243051078Speter{ 243151078Speter int cp4ticks; 243251078Speter u_char *ibuf; 243351078Speter int ibufsize; 243451078Speter struct tty *tp; 243551078Speter 243651078Speter /* 243751078Speter * Make the buffer size large enough to handle a softtty interrupt 243851078Speter * latency of about 2 ticks without loss of throughput or data 243951078Speter * (about 3 ticks if input flow control is not used or not honoured, 244051078Speter * but a bit less for CS5-CS7 modes). 244151078Speter */ 244251078Speter cp4ticks = speed / 10 / hz * 4; 244351078Speter for (ibufsize = 128; ibufsize < cp4ticks;) 244451078Speter ibufsize <<= 1; 244551078Speter if (ibufsize == com->ibufsize) { 244651078Speter disable_intr(); 244751078Speter return (0); 244851078Speter } 244951078Speter 245051078Speter /* 245151078Speter * Allocate input buffer. The extra factor of 2 in the size is 245251078Speter * to allow for an error byte for each input byte. 245351078Speter */ 245451078Speter ibuf = malloc(2 * ibufsize, M_DEVBUF, M_NOWAIT); 245551078Speter if (ibuf == NULL) { 245651078Speter disable_intr(); 245751078Speter return (ENOMEM); 245851078Speter } 245951078Speter 246051078Speter /* Initialize non-critical variables. */ 246151078Speter com->ibufold = com->ibuf; 246251078Speter com->ibufsize = ibufsize; 246351078Speter tp = com->tp; 246451078Speter if (tp != NULL) { 246551078Speter tp->t_ififosize = 2 * ibufsize; 246651078Speter tp->t_ispeedwat = (speed_t)-1; 246751078Speter tp->t_ospeedwat = (speed_t)-1; 246851078Speter } 246951078Speter 247051078Speter /* 247151078Speter * Read current input buffer, if any. Continue with interrupts 247251078Speter * disabled. 247351078Speter */ 247451078Speter disable_intr(); 247551078Speter if (com->iptr != com->ibuf) 247651078Speter sioinput(com); 247751078Speter 247851078Speter /*- 247951078Speter * Initialize critical variables, including input buffer watermarks. 248051078Speter * The external device is asked to stop sending when the buffer 248151078Speter * exactly reaches high water, or when the high level requests it. 248251078Speter * The high level is notified immediately (rather than at a later 248351078Speter * clock tick) when this watermark is reached. 248451078Speter * The buffer size is chosen so the watermark should almost never 248551078Speter * be reached. 248651078Speter * The low watermark is invisibly 0 since the buffer is always 248751078Speter * emptied all at once. 248851078Speter */ 248951078Speter com->iptr = com->ibuf = ibuf; 249051078Speter com->ibufend = ibuf + ibufsize; 249151078Speter com->ierroff = ibufsize; 249251078Speter com->ihighwater = ibuf + 3 * ibufsize / 4; 249351078Speter return (0); 249451078Speter} 249551078Speter 249651078Speterstatic void 249751078Spetercomstart(tp) 249851078Speter struct tty *tp; 249951078Speter{ 250051078Speter struct com_s *com; 250151078Speter int s; 250251078Speter int unit; 250351078Speter 250451078Speter unit = DEV_TO_UNIT(tp->t_dev); 250551078Speter com = com_addr(unit); 250657915Simp if (com == NULL) 250757915Simp return; 250851078Speter s = spltty(); 250951078Speter disable_intr(); 251051078Speter if (tp->t_state & TS_TTSTOP) 251151078Speter com->state &= ~CS_TTGO; 251251078Speter else 251351078Speter com->state |= CS_TTGO; 251451078Speter if (tp->t_state & TS_TBLOCK) { 251551078Speter if (com->mcr_image & MCR_RTS && com->state & CS_RTS_IFLOW) 251651078Speter outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); 251751078Speter } else { 251851078Speter if (!(com->mcr_image & MCR_RTS) && com->iptr < com->ihighwater 251951078Speter && com->state & CS_RTS_IFLOW) 252051078Speter outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); 252151078Speter } 252251078Speter enable_intr(); 252351078Speter if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) { 252451078Speter ttwwakeup(tp); 252551078Speter splx(s); 252651078Speter return; 252751078Speter } 252851078Speter if (tp->t_outq.c_cc != 0) { 252951078Speter struct lbq *qp; 253051078Speter struct lbq *next; 253151078Speter 253251078Speter if (!com->obufs[0].l_queued) { 253351078Speter com->obufs[0].l_tail 253451078Speter = com->obuf1 + q_to_b(&tp->t_outq, com->obuf1, 253551078Speter sizeof com->obuf1); 253651078Speter com->obufs[0].l_next = NULL; 253751078Speter com->obufs[0].l_queued = TRUE; 253851078Speter disable_intr(); 253951078Speter if (com->state & CS_BUSY) { 254051078Speter qp = com->obufq.l_next; 254151078Speter while ((next = qp->l_next) != NULL) 254251078Speter qp = next; 254351078Speter qp->l_next = &com->obufs[0]; 254451078Speter } else { 254551078Speter com->obufq.l_head = com->obufs[0].l_head; 254651078Speter com->obufq.l_tail = com->obufs[0].l_tail; 254751078Speter com->obufq.l_next = &com->obufs[0]; 254851078Speter com->state |= CS_BUSY; 254951078Speter } 255051078Speter enable_intr(); 255151078Speter } 255251078Speter if (tp->t_outq.c_cc != 0 && !com->obufs[1].l_queued) { 255351078Speter com->obufs[1].l_tail 255451078Speter = com->obuf2 + q_to_b(&tp->t_outq, com->obuf2, 255551078Speter sizeof com->obuf2); 255651078Speter com->obufs[1].l_next = NULL; 255751078Speter com->obufs[1].l_queued = TRUE; 255851078Speter disable_intr(); 255951078Speter if (com->state & CS_BUSY) { 256051078Speter qp = com->obufq.l_next; 256151078Speter while ((next = qp->l_next) != NULL) 256251078Speter qp = next; 256351078Speter qp->l_next = &com->obufs[1]; 256451078Speter } else { 256551078Speter com->obufq.l_head = com->obufs[1].l_head; 256651078Speter com->obufq.l_tail = com->obufs[1].l_tail; 256751078Speter com->obufq.l_next = &com->obufs[1]; 256851078Speter com->state |= CS_BUSY; 256951078Speter } 257051078Speter enable_intr(); 257151078Speter } 257251078Speter tp->t_state |= TS_BUSY; 257351078Speter } 257451078Speter disable_intr(); 257551078Speter if (com->state >= (CS_BUSY | CS_TTGO)) 257651078Speter siointr1(com); /* fake interrupt to start output */ 257751078Speter enable_intr(); 257851078Speter ttwwakeup(tp); 257951078Speter splx(s); 258051078Speter} 258151078Speter 258251078Speterstatic void 258351654Sphkcomstop(tp, rw) 258451078Speter struct tty *tp; 258551078Speter int rw; 258651078Speter{ 258751078Speter struct com_s *com; 258851078Speter 258951078Speter com = com_addr(DEV_TO_UNIT(tp->t_dev)); 259057915Simp if (com == NULL || com->gone) 259151078Speter return; 259251078Speter disable_intr(); 259351078Speter if (rw & FWRITE) { 259451078Speter if (com->hasfifo) 259551078Speter#ifdef COM_ESP 259651078Speter /* XXX avoid h/w bug. */ 259751078Speter if (!com->esp) 259851078Speter#endif 259951078Speter outb(com->iobase + com_fifo, 260051078Speter FIFO_XMT_RST | com->fifo_image); 260151078Speter com->obufs[0].l_queued = FALSE; 260251078Speter com->obufs[1].l_queued = FALSE; 260351078Speter if (com->state & CS_ODONE) 260451078Speter com_events -= LOTS_OF_EVENTS; 260551078Speter com->state &= ~(CS_ODONE | CS_BUSY); 260651078Speter com->tp->t_state &= ~TS_BUSY; 260751078Speter } 260851078Speter if (rw & FREAD) { 260951078Speter if (com->hasfifo) 261051078Speter#ifdef COM_ESP 261151078Speter /* XXX avoid h/w bug. */ 261251078Speter if (!com->esp) 261351078Speter#endif 261451078Speter outb(com->iobase + com_fifo, 261551078Speter FIFO_RCV_RST | com->fifo_image); 261651078Speter com_events -= (com->iptr - com->ibuf); 261751078Speter com->iptr = com->ibuf; 261851078Speter } 261951078Speter enable_intr(); 262051078Speter comstart(tp); 262151078Speter} 262251078Speter 262351078Speterstatic int 262451078Spetercommctl(com, bits, how) 262551078Speter struct com_s *com; 262651078Speter int bits; 262751078Speter int how; 262851078Speter{ 262951078Speter int mcr; 263051078Speter int msr; 263151078Speter 263251078Speter if (how == DMGET) { 263351078Speter bits = TIOCM_LE; /* XXX - always enabled while open */ 263451078Speter mcr = com->mcr_image; 263551078Speter if (mcr & MCR_DTR) 263651078Speter bits |= TIOCM_DTR; 263751078Speter if (mcr & MCR_RTS) 263851078Speter bits |= TIOCM_RTS; 263951078Speter msr = com->prev_modem_status; 264051078Speter if (msr & MSR_CTS) 264151078Speter bits |= TIOCM_CTS; 264251078Speter if (msr & MSR_DCD) 264351078Speter bits |= TIOCM_CD; 264451078Speter if (msr & MSR_DSR) 264551078Speter bits |= TIOCM_DSR; 264651078Speter /* 264751078Speter * XXX - MSR_RI is naturally volatile, and we make MSR_TERI 264851078Speter * more volatile by reading the modem status a lot. Perhaps 264951078Speter * we should latch both bits until the status is read here. 265051078Speter */ 265151078Speter if (msr & (MSR_RI | MSR_TERI)) 265251078Speter bits |= TIOCM_RI; 265351078Speter return (bits); 265451078Speter } 265551078Speter mcr = 0; 265651078Speter if (bits & TIOCM_DTR) 265751078Speter mcr |= MCR_DTR; 265851078Speter if (bits & TIOCM_RTS) 265951078Speter mcr |= MCR_RTS; 266051078Speter if (com->gone) 266151078Speter return(0); 266251078Speter disable_intr(); 266351078Speter switch (how) { 266451078Speter case DMSET: 266551078Speter outb(com->modem_ctl_port, 266651078Speter com->mcr_image = mcr | (com->mcr_image & MCR_IENABLE)); 266751078Speter break; 266851078Speter case DMBIS: 266951078Speter outb(com->modem_ctl_port, com->mcr_image |= mcr); 267051078Speter break; 267151078Speter case DMBIC: 267251078Speter outb(com->modem_ctl_port, com->mcr_image &= ~mcr); 267351078Speter break; 267451078Speter } 267551078Speter enable_intr(); 267651078Speter return (0); 267751078Speter} 267851078Speter 267951078Speterstatic void 268051078Spetersiosettimeout() 268151078Speter{ 268251078Speter struct com_s *com; 268351078Speter bool_t someopen; 268451078Speter int unit; 268551078Speter 268651078Speter /* 268751078Speter * Set our timeout period to 1 second if no polled devices are open. 268851078Speter * Otherwise set it to max(1/200, 1/hz). 268951078Speter * Enable timeouts iff some device is open. 269051078Speter */ 269151078Speter untimeout(comwakeup, (void *)NULL, sio_timeout_handle); 269251078Speter sio_timeout = hz; 269351078Speter someopen = FALSE; 269453344Speter for (unit = 0; unit < sio_numunits; ++unit) { 269551078Speter com = com_addr(unit); 269651078Speter if (com != NULL && com->tp != NULL 269751078Speter && com->tp->t_state & TS_ISOPEN && !com->gone) { 269851078Speter someopen = TRUE; 269951078Speter if (com->poll || com->poll_output) { 270051078Speter sio_timeout = hz > 200 ? hz / 200 : 1; 270151078Speter break; 270251078Speter } 270351078Speter } 270451078Speter } 270551078Speter if (someopen) { 270651078Speter sio_timeouts_until_log = hz / sio_timeout; 270751078Speter sio_timeout_handle = timeout(comwakeup, (void *)NULL, 270851078Speter sio_timeout); 270951078Speter } else { 271051078Speter /* Flush error messages, if any. */ 271151078Speter sio_timeouts_until_log = 1; 271251078Speter comwakeup((void *)NULL); 271351078Speter untimeout(comwakeup, (void *)NULL, sio_timeout_handle); 271451078Speter } 271551078Speter} 271651078Speter 271751078Speterstatic void 271851078Spetercomwakeup(chan) 271951078Speter void *chan; 272051078Speter{ 272151078Speter struct com_s *com; 272251078Speter int unit; 272351078Speter 272451078Speter sio_timeout_handle = timeout(comwakeup, (void *)NULL, sio_timeout); 272551078Speter 272651078Speter /* 272751078Speter * Recover from lost output interrupts. 272851078Speter * Poll any lines that don't use interrupts. 272951078Speter */ 273053344Speter for (unit = 0; unit < sio_numunits; ++unit) { 273151078Speter com = com_addr(unit); 273251078Speter if (com != NULL && !com->gone 273351078Speter && (com->state >= (CS_BUSY | CS_TTGO) || com->poll)) { 273451078Speter disable_intr(); 273551078Speter siointr1(com); 273651078Speter enable_intr(); 273751078Speter } 273851078Speter } 273951078Speter 274051078Speter /* 274151078Speter * Check for and log errors, but not too often. 274251078Speter */ 274351078Speter if (--sio_timeouts_until_log > 0) 274451078Speter return; 274551078Speter sio_timeouts_until_log = hz / sio_timeout; 274653344Speter for (unit = 0; unit < sio_numunits; ++unit) { 274751078Speter int errnum; 274851078Speter 274951078Speter com = com_addr(unit); 275051078Speter if (com == NULL) 275151078Speter continue; 275251078Speter if (com->gone) 275351078Speter continue; 275451078Speter for (errnum = 0; errnum < CE_NTYPES; ++errnum) { 275551078Speter u_int delta; 275651078Speter u_long total; 275751078Speter 275851078Speter disable_intr(); 275951078Speter delta = com->delta_error_counts[errnum]; 276051078Speter com->delta_error_counts[errnum] = 0; 276151078Speter enable_intr(); 276251078Speter if (delta == 0) 276351078Speter continue; 276451078Speter total = com->error_counts[errnum] += delta; 276551078Speter log(LOG_ERR, "sio%d: %u more %s%s (total %lu)\n", 276651078Speter unit, delta, error_desc[errnum], 276751078Speter delta == 1 ? "" : "s", total); 276851078Speter } 276951078Speter } 277051078Speter} 277151078Speter 277251078Speterstatic void 277351078Speterdisc_optim(tp, t, com) 277451078Speter struct tty *tp; 277551078Speter struct termios *t; 277651078Speter struct com_s *com; 277751078Speter{ 277851078Speter if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON)) 277951078Speter && (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK)) 278051078Speter && (!(t->c_iflag & PARMRK) 278151078Speter || (t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK)) 278251078Speter && !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN)) 278351078Speter && linesw[tp->t_line].l_rint == ttyinput) 278451078Speter tp->t_state |= TS_CAN_BYPASS_L_RINT; 278551078Speter else 278651078Speter tp->t_state &= ~TS_CAN_BYPASS_L_RINT; 278751078Speter com->hotchar = linesw[tp->t_line].l_hotchar; 278851078Speter} 278951078Speter 279051078Speter/* 279151078Speter * Following are all routines needed for SIO to act as console 279251078Speter */ 279351078Speter#include <sys/cons.h> 279451078Speter 279551078Speterstruct siocnstate { 279651078Speter u_char dlbl; 279751078Speter u_char dlbh; 279851078Speter u_char ier; 279951078Speter u_char cfcr; 280051078Speter u_char mcr; 280151078Speter}; 280251078Speter 280351078Speterstatic speed_t siocngetspeed __P((Port_t, struct speedtab *)); 280451078Speterstatic void siocnclose __P((struct siocnstate *sp, Port_t iobase)); 280551078Speterstatic void siocnopen __P((struct siocnstate *sp, Port_t iobase, int speed)); 280651078Speterstatic void siocntxwait __P((Port_t iobase)); 280751078Speter 280851078Speterstatic cn_probe_t siocnprobe; 280951078Speterstatic cn_init_t siocninit; 281051078Speterstatic cn_checkc_t siocncheckc; 281151078Speterstatic cn_getc_t siocngetc; 281251078Speterstatic cn_putc_t siocnputc; 281351078Speter 281451078Speter#ifdef __i386__ 281555823SyokotaCONS_DRIVER(sio, siocnprobe, siocninit, NULL, siocngetc, siocncheckc, 281655823Syokota siocnputc, NULL); 281751078Speter#endif 281851078Speter 281951078Speter/* To get the GDB related variables */ 282051078Speter#if DDB > 0 282151078Speter#include <ddb/ddb.h> 282251078Speter#endif 282351078Speter 282451078Speterstatic void 282551078Spetersiocntxwait(iobase) 282651078Speter Port_t iobase; 282751078Speter{ 282851078Speter int timo; 282951078Speter 283051078Speter /* 283151078Speter * Wait for any pending transmission to finish. Required to avoid 283251078Speter * the UART lockup bug when the speed is changed, and for normal 283351078Speter * transmits. 283451078Speter */ 283551078Speter timo = 100000; 283651078Speter while ((inb(iobase + com_lsr) & (LSR_TSRE | LSR_TXRDY)) 283751078Speter != (LSR_TSRE | LSR_TXRDY) && --timo != 0) 283851078Speter ; 283951078Speter} 284051078Speter 284151078Speter/* 284251078Speter * Read the serial port specified and try to figure out what speed 284351078Speter * it's currently running at. We're assuming the serial port has 284451078Speter * been initialized and is basicly idle. This routine is only intended 284551078Speter * to be run at system startup. 284651078Speter * 284751078Speter * If the value read from the serial port doesn't make sense, return 0. 284851078Speter */ 284951078Speter 285051078Speterstatic speed_t 285151078Spetersiocngetspeed(iobase, table) 285251078Speter Port_t iobase; 285351078Speter struct speedtab *table; 285451078Speter{ 285551078Speter int code; 285651078Speter u_char dlbh; 285751078Speter u_char dlbl; 285851078Speter u_char cfcr; 285951078Speter 286051078Speter cfcr = inb(iobase + com_cfcr); 286151078Speter outb(iobase + com_cfcr, CFCR_DLAB | cfcr); 286251078Speter 286351078Speter dlbl = inb(iobase + com_dlbl); 286451078Speter dlbh = inb(iobase + com_dlbh); 286551078Speter 286651078Speter outb(iobase + com_cfcr, cfcr); 286751078Speter 286851078Speter code = dlbh << 8 | dlbl; 286951078Speter 287053344Speter for (; table->sp_speed != -1; table++) 287151078Speter if (table->sp_code == code) 287251078Speter return (table->sp_speed); 287351078Speter 287458885Simp return (0); /* didn't match anything sane */ 287551078Speter} 287651078Speter 287751078Speterstatic void 287851078Spetersiocnopen(sp, iobase, speed) 287951078Speter struct siocnstate *sp; 288051078Speter Port_t iobase; 288151078Speter int speed; 288251078Speter{ 288351078Speter int divisor; 288451078Speter u_char dlbh; 288551078Speter u_char dlbl; 288651078Speter 288751078Speter /* 288851078Speter * Save all the device control registers except the fifo register 288951078Speter * and set our default ones (cs8 -parenb speed=comdefaultrate). 289051078Speter * We can't save the fifo register since it is read-only. 289151078Speter */ 289251078Speter sp->ier = inb(iobase + com_ier); 289351078Speter outb(iobase + com_ier, 0); /* spltty() doesn't stop siointr() */ 289451078Speter siocntxwait(iobase); 289551078Speter sp->cfcr = inb(iobase + com_cfcr); 289651078Speter outb(iobase + com_cfcr, CFCR_DLAB | CFCR_8BITS); 289751078Speter sp->dlbl = inb(iobase + com_dlbl); 289851078Speter sp->dlbh = inb(iobase + com_dlbh); 289951078Speter /* 290051078Speter * Only set the divisor registers if they would change, since on 290151078Speter * some 16550 incompatibles (Startech), setting them clears the 290251078Speter * data input register. This also reduces the effects of the 290351078Speter * UMC8669F bug. 290451078Speter */ 290551078Speter divisor = ttspeedtab(speed, comspeedtab); 290651078Speter dlbl = divisor & 0xFF; 290751078Speter if (sp->dlbl != dlbl) 290851078Speter outb(iobase + com_dlbl, dlbl); 290951078Speter dlbh = (u_int) divisor >> 8; 291051078Speter if (sp->dlbh != dlbh) 291151078Speter outb(iobase + com_dlbh, dlbh); 291251078Speter outb(iobase + com_cfcr, CFCR_8BITS); 291351078Speter sp->mcr = inb(iobase + com_mcr); 291451078Speter /* 291551078Speter * We don't want interrupts, but must be careful not to "disable" 291651078Speter * them by clearing the MCR_IENABLE bit, since that might cause 291751078Speter * an interrupt by floating the IRQ line. 291851078Speter */ 291951078Speter outb(iobase + com_mcr, (sp->mcr & MCR_IENABLE) | MCR_DTR | MCR_RTS); 292051078Speter} 292151078Speter 292251078Speterstatic void 292351078Spetersiocnclose(sp, iobase) 292451078Speter struct siocnstate *sp; 292551078Speter Port_t iobase; 292651078Speter{ 292751078Speter /* 292851078Speter * Restore the device control registers. 292951078Speter */ 293051078Speter siocntxwait(iobase); 293151078Speter outb(iobase + com_cfcr, CFCR_DLAB | CFCR_8BITS); 293251078Speter if (sp->dlbl != inb(iobase + com_dlbl)) 293351078Speter outb(iobase + com_dlbl, sp->dlbl); 293451078Speter if (sp->dlbh != inb(iobase + com_dlbh)) 293551078Speter outb(iobase + com_dlbh, sp->dlbh); 293651078Speter outb(iobase + com_cfcr, sp->cfcr); 293751078Speter /* 293851078Speter * XXX damp oscillations of MCR_DTR and MCR_RTS by not restoring them. 293951078Speter */ 294051078Speter outb(iobase + com_mcr, sp->mcr | MCR_DTR | MCR_RTS); 294151078Speter outb(iobase + com_ier, sp->ier); 294251078Speter} 294351078Speter 294451078Speterstatic void 294551078Spetersiocnprobe(cp) 294651078Speter struct consdev *cp; 294751078Speter{ 294851078Speter speed_t boot_speed; 294951078Speter u_char cfcr; 295051078Speter int s, unit; 295151078Speter struct siocnstate sp; 295251078Speter 295351078Speter /* 295451078Speter * Find our first enabled console, if any. If it is a high-level 295551078Speter * console device, then initialize it and return successfully. 295651078Speter * If it is a low-level console device, then initialize it and 295751078Speter * return unsuccessfully. It must be initialized in both cases 295851078Speter * for early use by console drivers and debuggers. Initializing 295951078Speter * the hardware is not necessary in all cases, since the i/o 296051078Speter * routines initialize it on the fly, but it is necessary if 296151078Speter * input might arrive while the hardware is switched back to an 296251078Speter * uninitialized state. We can't handle multiple console devices 296351078Speter * yet because our low-level routines don't take a device arg. 296451078Speter * We trust the user to set the console flags properly so that we 296551078Speter * don't need to probe. 296651078Speter */ 296751078Speter cp->cn_pri = CN_DEAD; 296851078Speter 296951078Speter for (unit = 0; unit < 16; unit++) { /* XXX need to know how many */ 297051078Speter int flags; 297151078Speter int disabled; 297251078Speter if (resource_int_value("sio", unit, "disabled", &disabled) == 0) { 297351078Speter if (disabled) 297451078Speter continue; 297551078Speter } 297651078Speter if (resource_int_value("sio", unit, "flags", &flags)) 297751078Speter continue; 297851078Speter if (COM_CONSOLE(flags) || COM_DEBUGGER(flags)) { 297951078Speter int port; 298051078Speter Port_t iobase; 298151078Speter 298251078Speter if (resource_int_value("sio", unit, "port", &port)) 298351078Speter continue; 298451078Speter iobase = port; 298551078Speter s = spltty(); 298651078Speter if (boothowto & RB_SERIAL) { 298751078Speter boot_speed = siocngetspeed(iobase, comspeedtab); 298851078Speter if (boot_speed) 298951078Speter comdefaultrate = boot_speed; 299051078Speter } 299151078Speter 299251078Speter /* 299351078Speter * Initialize the divisor latch. We can't rely on 299451078Speter * siocnopen() to do this the first time, since it 299551078Speter * avoids writing to the latch if the latch appears 299651078Speter * to have the correct value. Also, if we didn't 299751078Speter * just read the speed from the hardware, then we 299851078Speter * need to set the speed in hardware so that 299951078Speter * switching it later is null. 300051078Speter */ 300151078Speter cfcr = inb(iobase + com_cfcr); 300251078Speter outb(iobase + com_cfcr, CFCR_DLAB | cfcr); 300351078Speter outb(iobase + com_dlbl, 300451078Speter COMBRD(comdefaultrate) & 0xff); 300551078Speter outb(iobase + com_dlbh, 300651078Speter (u_int) COMBRD(comdefaultrate) >> 8); 300751078Speter outb(iobase + com_cfcr, cfcr); 300851078Speter 300951078Speter siocnopen(&sp, iobase, comdefaultrate); 301051078Speter 301151078Speter splx(s); 301251078Speter if (COM_CONSOLE(flags) && !COM_LLCONSOLE(flags)) { 301351078Speter cp->cn_dev = makedev(CDEV_MAJOR, unit); 301451078Speter cp->cn_pri = COM_FORCECONSOLE(flags) 301551078Speter || boothowto & RB_SERIAL 301651078Speter ? CN_REMOTE : CN_NORMAL; 301751078Speter siocniobase = iobase; 301851078Speter siocnunit = unit; 301951078Speter } 302051078Speter if (COM_DEBUGGER(flags)) { 302151078Speter printf("sio%d: gdb debugging port\n", unit); 302251078Speter siogdbiobase = iobase; 302351078Speter siogdbunit = unit; 302451078Speter#if DDB > 0 302551078Speter gdbdev = makedev(CDEV_MAJOR, unit); 302651078Speter gdb_getc = siocngetc; 302751078Speter gdb_putc = siocnputc; 302851078Speter#endif 302951078Speter } 303051078Speter } 303151078Speter } 303251078Speter#ifdef __i386__ 303351078Speter#if DDB > 0 303451078Speter /* 303551078Speter * XXX Ugly Compatability. 303651078Speter * If no gdb port has been specified, set it to be the console 303751078Speter * as some configuration files don't specify the gdb port. 303851078Speter */ 303951078Speter if (gdbdev == NODEV && (boothowto & RB_GDB)) { 304051078Speter printf("Warning: no GDB port specified. Defaulting to sio%d.\n", 304151078Speter siocnunit); 304251078Speter printf("Set flag 0x80 on desired GDB port in your\n"); 304351078Speter printf("configuration file (currently sio only).\n"); 304451078Speter siogdbiobase = siocniobase; 304551078Speter siogdbunit = siocnunit; 304651078Speter gdbdev = makedev(CDEV_MAJOR, siocnunit); 304751078Speter gdb_getc = siocngetc; 304851078Speter gdb_putc = siocnputc; 304951078Speter } 305051078Speter#endif 305151078Speter#endif 305251078Speter} 305351078Speter 305451078Speter#ifdef __alpha__ 305551078Speter 305655868SgallatinCONS_DRIVER(sio, NULL, NULL, NULL, siocngetc, siocncheckc, siocnputc, NULL); 305751078Speter 305851078Speterint 305951078Spetersiocnattach(port, speed) 306051078Speter int port; 306151078Speter int speed; 306251078Speter{ 306351078Speter int s; 306451078Speter u_char cfcr; 306551078Speter struct siocnstate sp; 306651078Speter 306751078Speter siocniobase = port; 306851078Speter comdefaultrate = speed; 306951078Speter sio_consdev.cn_pri = CN_NORMAL; 307051078Speter sio_consdev.cn_dev = makedev(CDEV_MAJOR, 0); 307151078Speter 307251078Speter s = spltty(); 307351078Speter 307451078Speter /* 307551078Speter * Initialize the divisor latch. We can't rely on 307651078Speter * siocnopen() to do this the first time, since it 307751078Speter * avoids writing to the latch if the latch appears 307851078Speter * to have the correct value. Also, if we didn't 307951078Speter * just read the speed from the hardware, then we 308051078Speter * need to set the speed in hardware so that 308151078Speter * switching it later is null. 308251078Speter */ 308351078Speter cfcr = inb(siocniobase + com_cfcr); 308451078Speter outb(siocniobase + com_cfcr, CFCR_DLAB | cfcr); 308551078Speter outb(siocniobase + com_dlbl, 308651078Speter COMBRD(comdefaultrate) & 0xff); 308751078Speter outb(siocniobase + com_dlbh, 308851078Speter (u_int) COMBRD(comdefaultrate) >> 8); 308951078Speter outb(siocniobase + com_cfcr, cfcr); 309051078Speter 309151078Speter siocnopen(&sp, siocniobase, comdefaultrate); 309251078Speter splx(s); 309351078Speter 309451078Speter cn_tab = &sio_consdev; 309558885Simp return (0); 309651078Speter} 309751078Speter 309851078Speterint 309951078Spetersiogdbattach(port, speed) 310051078Speter int port; 310151078Speter int speed; 310251078Speter{ 310351078Speter int s; 310451078Speter u_char cfcr; 310551078Speter struct siocnstate sp; 310651078Speter 310751078Speter siogdbiobase = port; 310851078Speter gdbdefaultrate = speed; 310951078Speter 311051078Speter s = spltty(); 311151078Speter 311251078Speter /* 311351078Speter * Initialize the divisor latch. We can't rely on 311451078Speter * siocnopen() to do this the first time, since it 311551078Speter * avoids writing to the latch if the latch appears 311651078Speter * to have the correct value. Also, if we didn't 311751078Speter * just read the speed from the hardware, then we 311851078Speter * need to set the speed in hardware so that 311951078Speter * switching it later is null. 312051078Speter */ 312151078Speter cfcr = inb(siogdbiobase + com_cfcr); 312251078Speter outb(siogdbiobase + com_cfcr, CFCR_DLAB | cfcr); 312351078Speter outb(siogdbiobase + com_dlbl, 312451078Speter COMBRD(gdbdefaultrate) & 0xff); 312551078Speter outb(siogdbiobase + com_dlbh, 312651078Speter (u_int) COMBRD(gdbdefaultrate) >> 8); 312751078Speter outb(siogdbiobase + com_cfcr, cfcr); 312851078Speter 312951078Speter siocnopen(&sp, siogdbiobase, gdbdefaultrate); 313051078Speter splx(s); 313151078Speter 313258885Simp return (0); 313351078Speter} 313451078Speter 313551078Speter#endif 313651078Speter 313751078Speterstatic void 313851078Spetersiocninit(cp) 313951078Speter struct consdev *cp; 314051078Speter{ 314151078Speter comconsole = DEV_TO_UNIT(cp->cn_dev); 314251078Speter} 314351078Speter 314451078Speterstatic int 314551078Spetersiocncheckc(dev) 314651078Speter dev_t dev; 314751078Speter{ 314851078Speter int c; 314951078Speter Port_t iobase; 315051078Speter int s; 315151078Speter struct siocnstate sp; 315251078Speter 315351078Speter if (minor(dev) == siogdbunit) 315451078Speter iobase = siogdbiobase; 315551078Speter else 315651078Speter iobase = siocniobase; 315751078Speter s = spltty(); 315851078Speter siocnopen(&sp, iobase, comdefaultrate); 315951078Speter if (inb(iobase + com_lsr) & LSR_RXRDY) 316051078Speter c = inb(iobase + com_data); 316151078Speter else 316251078Speter c = -1; 316351078Speter siocnclose(&sp, iobase); 316451078Speter splx(s); 316551078Speter return (c); 316651078Speter} 316751078Speter 316851078Speter 316951078Speterint 317051078Spetersiocngetc(dev) 317151078Speter dev_t dev; 317251078Speter{ 317351078Speter int c; 317451078Speter Port_t iobase; 317551078Speter int s; 317651078Speter struct siocnstate sp; 317751078Speter 317851078Speter if (minor(dev) == siogdbunit) 317951078Speter iobase = siogdbiobase; 318051078Speter else 318151078Speter iobase = siocniobase; 318251078Speter s = spltty(); 318351078Speter siocnopen(&sp, iobase, comdefaultrate); 318451078Speter while (!(inb(iobase + com_lsr) & LSR_RXRDY)) 318551078Speter ; 318651078Speter c = inb(iobase + com_data); 318751078Speter siocnclose(&sp, iobase); 318851078Speter splx(s); 318951078Speter return (c); 319051078Speter} 319151078Speter 319251078Spetervoid 319351078Spetersiocnputc(dev, c) 319451078Speter dev_t dev; 319551078Speter int c; 319651078Speter{ 319751078Speter int s; 319851078Speter struct siocnstate sp; 319951078Speter Port_t iobase; 320051078Speter 320151078Speter if (minor(dev) == siogdbunit) 320251078Speter iobase = siogdbiobase; 320351078Speter else 320451078Speter iobase = siocniobase; 320551078Speter s = spltty(); 320651078Speter siocnopen(&sp, iobase, comdefaultrate); 320751078Speter siocntxwait(iobase); 320851078Speter outb(iobase + com_data, c); 320951078Speter siocnclose(&sp, iobase); 321051078Speter splx(s); 321151078Speter} 321251078Speter 321351078Speter#ifdef __alpha__ 321451078Speterint 321551078Spetersiogdbgetc() 321651078Speter{ 321751078Speter int c; 321851078Speter Port_t iobase; 321951078Speter int s; 322051078Speter struct siocnstate sp; 322151078Speter 322251078Speter iobase = siogdbiobase; 322351078Speter s = spltty(); 322451078Speter siocnopen(&sp, iobase, gdbdefaultrate); 322551078Speter while (!(inb(iobase + com_lsr) & LSR_RXRDY)) 322651078Speter ; 322751078Speter c = inb(iobase + com_data); 322851078Speter siocnclose(&sp, iobase); 322951078Speter splx(s); 323051078Speter return (c); 323151078Speter} 323251078Speter 323351078Spetervoid 323451078Spetersiogdbputc(c) 323551078Speter int c; 323651078Speter{ 323751078Speter int s; 323851078Speter struct siocnstate sp; 323951078Speter 324051078Speter s = spltty(); 324151078Speter siocnopen(&sp, siogdbiobase, gdbdefaultrate); 324251078Speter siocntxwait(siogdbiobase); 324351078Speter outb(siogdbiobase + com_data, c); 324451078Speter siocnclose(&sp, siogdbiobase); 324551078Speter splx(s); 324651078Speter} 324751078Speter#endif 324851078Speter 324952471SimpDRIVER_MODULE(sio, isa, sio_isa_driver, sio_devclass, 0, 0); 325052471Simp#if NCARD > 0 325152471SimpDRIVER_MODULE(sio, pccard, sio_pccard_driver, sio_devclass, 0, 0); 325252471Simp#endif 325358885Simp#if NPCI > 0 325458885SimpDRIVER_MODULE(sio, pci, sio_pci_driver, sio_devclass, 0, 0); 325558885Simp#endif 3256