diff options
Diffstat (limited to 'sys/i386/isa/stallion.c')
-rw-r--r-- | sys/i386/isa/stallion.c | 3072 |
1 files changed, 0 insertions, 3072 deletions
diff --git a/sys/i386/isa/stallion.c b/sys/i386/isa/stallion.c deleted file mode 100644 index 9a99f74..0000000 --- a/sys/i386/isa/stallion.c +++ /dev/null @@ -1,3072 +0,0 @@ -/*****************************************************************************/ - -/* - * stallion.c -- stallion multiport serial driver. - * - * Copyright (c) 1995-1996 Greg Ungerer (gerg@stallion.oz.au). - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Greg Ungerer. - * 4. Neither the name of the author nor the names of any co-contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - -/*****************************************************************************/ - -#define TTYDEFCHARS 1 - -#include "opt_compat.h" -#include "opt_tty.h" - -#include <sys/param.h> -#include <sys/systm.h> -#include <sys/kernel.h> -#include <sys/malloc.h> -#include <sys/tty.h> -#include <sys/conf.h> -#include <sys/fcntl.h> -#include <sys/bus.h> -#include <i386/isa/isa_device.h> -#include <i386/isa/ic/scd1400.h> -#include <machine/comstats.h> - -#warning "The stallion pci attachment is broken and not compiled" -#define NPCI 0 -#if NPCI > 0 -#ifndef COMPAT_OLDPCI -#error "The stallion pci driver requires the old pci compatibility shims" -#endif -#include <dev/pci/pcivar.h> -#include <dev/pci/pcireg.h> -#endif - -/*****************************************************************************/ - -/* - * Define the version level of the kernel - so we can compile in the - * appropriate bits of code. By default this will compile for a 2.1 - * level kernel. - */ -#define VFREEBSD 220 - -#if VFREEBSD >= 220 -#define STATIC static -#else -#define STATIC -#endif - -/*****************************************************************************/ - -/* - * Define different board types. At the moment I have only declared - * those boards that this driver supports. But I will use the standard - * "assigned" board numbers. In the future this driver will support - * some of the other Stallion boards. Currently supported boards are - * abbreviated as EIO = EasyIO and ECH = EasyConnection 8/32. - */ -#define BRD_EASYIO 20 -#define BRD_ECH 21 -#define BRD_ECHMC 22 -#define BRD_ECHPCI 26 - -/* - * When using the BSD "config" stuff there is no easy way to specifiy - * a secondary IO address region. So it is hard wired here. Also the - * shared interrupt information is hard wired here... - */ -static unsigned int stl_ioshared = 0x280; -static unsigned int stl_irqshared = 0; - -/*****************************************************************************/ - -/* - * Define important driver limitations. - */ -#define STL_MAXBRDS 8 -#define STL_MAXPANELS 4 -#define STL_PORTSPERPANEL 16 -#define STL_PORTSPERBRD 64 - -/* - * Define the important minor number break down bits. These have been - * chosen to be "compatible" with the standard sio driver minor numbers. - * Extra high bits are used to distinguish between boards. - */ -#define STL_CALLOUTDEV 0x80 -#define STL_CTRLLOCK 0x40 -#define STL_CTRLINIT 0x20 -#define STL_CTRLDEV (STL_CTRLLOCK | STL_CTRLINIT) - -#define STL_MEMDEV 0x07000000 - -#define STL_DEFSPEED TTYDEF_SPEED -#define STL_DEFCFLAG (CS8 | CREAD | HUPCL) - -/* - * I haven't really decided (or measured) what buffer sizes give - * a good balance between performance and memory usage. These seem - * to work pretty well... - */ -#define STL_RXBUFSIZE 2048 -#define STL_TXBUFSIZE 2048 - -#define STL_TXBUFLOW (STL_TXBUFSIZE / 4) -#define STL_RXBUFHIGH (3 * STL_RXBUFSIZE / 4) - -/*****************************************************************************/ - -/* - * Define our local driver identity first. Set up stuff to deal with - * all the local structures required by a serial tty driver. - */ -static const char stl_drvname[] = "stl"; -static const char stl_longdrvname[] = "Stallion Multiport Serial Driver"; -static const char stl_drvversion[] = "1.0.0"; -static int stl_brdprobed[STL_MAXBRDS]; - -static int stl_nrbrds = 0; -static int stl_doingtimeout = 0; - -static const char __file__[] = /*__FILE__*/ "stallion.c"; - -/* - * Define global stats structures. Not used often, and can be - * re-used for each stats call. - */ -static combrd_t stl_brdstats; -static comstats_t stl_comstats; - -/*****************************************************************************/ - -/* - * Define a set of structures to hold all the board/panel/port info - * for our ports. These will be dynamically allocated as required. - */ - -/* - * Define a ring queue structure for each port. This will hold the - * TX data waiting to be output. Characters are fed into this buffer - * from the line discipline (or even direct from user space!) and - * then fed into the UARTs during interrupts. Will use a clasic ring - * queue here for this. The good thing about this type of ring queue - * is that the head and tail pointers can be updated without interrupt - * protection - since "write" code only needs to change the head, and - * interrupt code only needs to change the tail. - */ -typedef struct { - char *buf; - char *endbuf; - char *head; - char *tail; -} stlrq_t; - -/* - * Port, panel and board structures to hold status info about each. - * The board structure contains pointers to structures for each panel - * connected to it, and in turn each panel structure contains pointers - * for each port structure for each port on that panel. Note that - * the port structure also contains the board and panel number that it - * is associated with, this makes it (fairly) easy to get back to the - * board/panel info for a port. Also note that the tty struct is at - * the top of the structure, this is important, since the code uses - * this fact to get the port struct pointer from the tty struct - * pointer! - */ -typedef struct { - struct tty tty; - int portnr; - int panelnr; - int brdnr; - int ioaddr; - int uartaddr; - int pagenr; - int callout; - int brklen; - int dtrwait; - int dotimestamp; - int waitopens; - int hotchar; - unsigned int state; - unsigned int hwid; - unsigned int sigs; - unsigned int rxignoremsk; - unsigned int rxmarkmsk; - unsigned long clk; - struct termios initintios; - struct termios initouttios; - struct termios lockintios; - struct termios lockouttios; - struct timeval timestamp; - comstats_t stats; - stlrq_t tx; - stlrq_t rx; - stlrq_t rxstatus; -} stlport_t; - -typedef struct { - int panelnr; - int brdnr; - int pagenr; - int nrports; - int iobase; - unsigned int hwid; - unsigned int ackmask; - stlport_t *ports[STL_PORTSPERPANEL]; -} stlpanel_t; - -typedef struct { - int brdnr; - int brdtype; - int unitid; - int state; - int nrpanels; - int nrports; - int irq; - int irqtype; - unsigned int ioaddr1; - unsigned int ioaddr2; - unsigned int iostatus; - unsigned int ioctrl; - unsigned int ioctrlval; - unsigned int hwid; - unsigned long clk; - stlpanel_t *panels[STL_MAXPANELS]; - stlport_t *ports[STL_PORTSPERBRD]; -} stlbrd_t; - -static stlbrd_t *stl_brds[STL_MAXBRDS]; - -/* - * Per board state flags. Used with the state field of the board struct. - * Not really much here yet! - */ -#define BRD_FOUND 0x1 - -/* - * Define the port structure state flags. These set of flags are - * modified at interrupt time - so setting and reseting them needs - * to be atomic. - */ -#define ASY_TXLOW 0x1 -#define ASY_RXDATA 0x2 -#define ASY_DCDCHANGE 0x4 -#define ASY_DTRWAIT 0x8 -#define ASY_RTSFLOW 0x10 -#define ASY_RTSFLOWMODE 0x20 -#define ASY_CTSFLOWMODE 0x40 - -#define ASY_ACTIVE (ASY_TXLOW | ASY_RXDATA | ASY_DCDCHANGE) - -/* - * Define an array of board names as printable strings. Handy for - * referencing boards when printing trace and stuff. - */ -static char *stl_brdnames[] = { - (char *) NULL, - (char *) NULL, - (char *) NULL, - (char *) NULL, - (char *) NULL, - (char *) NULL, - (char *) NULL, - (char *) NULL, - (char *) NULL, - (char *) NULL, - (char *) NULL, - (char *) NULL, - (char *) NULL, - (char *) NULL, - (char *) NULL, - (char *) NULL, - (char *) NULL, - (char *) NULL, - (char *) NULL, - (char *) NULL, - "EasyIO", - "EC8/32-AT", - "EC8/32-MC", - (char *) NULL, - (char *) NULL, - (char *) NULL, - "EC8/32-PCI", -}; - -/*****************************************************************************/ - -/* - * Hardware ID bits for the EasyIO and ECH boards. These defines apply - * to the directly accessible io ports of these boards (not the cd1400 - * uarts - they are in scd1400.h). - */ -#define EIO_8PORTRS 0x04 -#define EIO_4PORTRS 0x05 -#define EIO_8PORTDI 0x00 -#define EIO_8PORTM 0x06 -#define EIO_IDBITMASK 0x07 -#define EIO_INTRPEND 0x08 -#define EIO_INTEDGE 0x00 -#define EIO_INTLEVEL 0x08 - -#define ECH_ID 0xa0 -#define ECH_IDBITMASK 0xe0 -#define ECH_BRDENABLE 0x08 -#define ECH_BRDDISABLE 0x00 -#define ECH_INTENABLE 0x01 -#define ECH_INTDISABLE 0x00 -#define ECH_INTLEVEL 0x02 -#define ECH_INTEDGE 0x00 -#define ECH_INTRPEND 0x01 -#define ECH_BRDRESET 0x01 - -#define ECHMC_INTENABLE 0x01 -#define ECHMC_BRDRESET 0x02 - -#define ECH_PNLSTATUS 2 -#define ECH_PNL16PORT 0x20 -#define ECH_PNLIDMASK 0x07 -#define ECH_PNLINTRPEND 0x80 -#define ECH_ADDR2MASK 0x1e0 - -#define EIO_CLK 25000000 -#define EIO_CLK8M 20000000 -#define ECH_CLK EIO_CLK - -/* - * Define the offsets within the register bank for all io registers. - * These io address offsets are common to both the EIO and ECH. - */ -#define EREG_ADDR 0 -#define EREG_DATA 4 -#define EREG_RXACK 5 -#define EREG_TXACK 6 -#define EREG_MDACK 7 - -#define EREG_BANKSIZE 8 - -/* - * Define the PCI vendor and device id for ECH8/32-PCI. - */ -#define STL_PCIDEVID 0xd001100b - -/* - * Define the vector mapping bits for the programmable interrupt board - * hardware. These bits encode the interrupt for the board to use - it - * is software selectable (except the EIO-8M). - */ -static unsigned char stl_vecmap[] = { - 0xff, 0xff, 0xff, 0x04, 0x06, 0x05, 0xff, 0x07, - 0xff, 0xff, 0x00, 0x02, 0x01, 0xff, 0xff, 0x03 -}; - -/* - * Set up enable and disable macros for the ECH boards. They require - * the secondary io address space to be activated and deactivated. - * This way all ECH boards can share their secondary io region. - * If this is an ECH-PCI board then also need to set the page pointer - * to point to the correct page. - */ -#define BRDENABLE(brdnr,pagenr) \ - if (stl_brds[(brdnr)]->brdtype == BRD_ECH) \ - outb(stl_brds[(brdnr)]->ioctrl, \ - (stl_brds[(brdnr)]->ioctrlval | ECH_BRDENABLE));\ - else if (stl_brds[(brdnr)]->brdtype == BRD_ECHPCI) \ - outb(stl_brds[(brdnr)]->ioctrl, (pagenr)); - -#define BRDDISABLE(brdnr) \ - if (stl_brds[(brdnr)]->brdtype == BRD_ECH) \ - outb(stl_brds[(brdnr)]->ioctrl, \ - (stl_brds[(brdnr)]->ioctrlval | ECH_BRDDISABLE)); - -/* - * Define the cd1400 baud rate clocks. These are used when calculating - * what clock and divisor to use for the required baud rate. Also - * define the maximum baud rate allowed, and the default base baud. - */ -static int stl_cd1400clkdivs[] = { - CD1400_CLK0, CD1400_CLK1, CD1400_CLK2, CD1400_CLK3, CD1400_CLK4 -}; - -#define STL_MAXBAUD 230400 - -/*****************************************************************************/ - -/* - * Define macros to extract a brd and port number from a minor number. - * This uses the extended minor number range in the upper 2 bytes of - * the device number. This gives us plenty of minor numbers to play - * with... - */ -#define MKDEV2BRD(m) ((minor(m) & 0x00700000) >> 20) -#define MKDEV2PORT(m) ((minor(m) & 0x1f) | ((minor(m) & 0x00010000) >> 11)) - -/* - * Define some handy local macros... - */ -#ifndef MIN -#define MIN(a,b) (((a) <= (b)) ? (a) : (b)) -#endif - -/*****************************************************************************/ - -/* - * Declare all those functions in this driver! First up is the set of - * externally visible functions. - */ - -static int stlprobe(struct isa_device *idp); -static int stlattach(struct isa_device *idp); - -STATIC d_open_t stlopen; -STATIC d_close_t stlclose; -STATIC d_ioctl_t stlioctl; - -/* - * Internal function prototypes. - */ -static stlport_t *stl_dev2port(dev_t dev); -static int stl_findfreeunit(void); -static int stl_rawopen(stlport_t *portp); -static int stl_rawclose(stlport_t *portp); -static int stl_param(struct tty *tp, struct termios *tiosp); -static void stl_start(struct tty *tp); -static void stl_stop(struct tty *tp, int); -static void stl_ttyoptim(stlport_t *portp, struct termios *tiosp); -static void stl_dotimeout(void); -static void stl_poll(void *arg); -static void stl_rxprocess(stlport_t *portp); -static void stl_dtrwakeup(void *arg); -static int stl_brdinit(stlbrd_t *brdp); -static int stl_initeio(stlbrd_t *brdp); -static int stl_initech(stlbrd_t *brdp); -static int stl_initports(stlbrd_t *brdp, stlpanel_t *panelp); -static ointhand2_t stlintr; -static __inline void stl_txisr(stlpanel_t *panelp, int ioaddr); -static __inline void stl_rxisr(stlpanel_t *panelp, int ioaddr); -static __inline void stl_mdmisr(stlpanel_t *panelp, int ioaddr); -static void stl_setreg(stlport_t *portp, int regnr, int value); -static int stl_getreg(stlport_t *portp, int regnr); -static int stl_updatereg(stlport_t *portp, int regnr, int value); -static int stl_getsignals(stlport_t *portp); -static void stl_setsignals(stlport_t *portp, int dtr, int rts); -static void stl_flowcontrol(stlport_t *portp, int hw, int sw); -static void stl_ccrwait(stlport_t *portp); -static void stl_enablerxtx(stlport_t *portp, int rx, int tx); -static void stl_startrxtx(stlport_t *portp, int rx, int tx); -static void stl_disableintrs(stlport_t *portp); -static void stl_sendbreak(stlport_t *portp, long len); -static void stl_flush(stlport_t *portp, int flag); -static int stl_memioctl(dev_t dev, unsigned long cmd, caddr_t data, - int flag, struct thread *td); -static int stl_getbrdstats(caddr_t data); -static int stl_getportstats(stlport_t *portp, caddr_t data); -static int stl_clrportstats(stlport_t *portp, caddr_t data); -static stlport_t *stl_getport(int brdnr, int panelnr, int portnr); - -#if NPCI > 0 -static const char *stlpciprobe(pcici_t tag, pcidi_t type); -static void stlpciattach(pcici_t tag, int unit); -static void stlpciintr(void * arg); -#endif - -/*****************************************************************************/ - -/* - * Declare the driver isa structure. - */ -struct isa_driver stldriver = { - INTR_TYPE_TTY, - stlprobe, - stlattach, - "stl" -}; -COMPAT_ISA_DRIVER(stl, stldriver); - -/*****************************************************************************/ - -#if NPCI > 0 - -/* - * Declare the driver pci structure. - */ -static unsigned long stl_count; - -static struct pci_device stlpcidriver = { - "stl", - stlpciprobe, - stlpciattach, - &stl_count, - NULL, -}; - -COMPAT_PCI_DRIVER (stlpci, stlpcidriver); - -#endif - -/*****************************************************************************/ - -#if VFREEBSD >= 220 - -/* - * FreeBSD-2.2+ kernel linkage. - */ - -static struct cdevsw stl_cdevsw = { - .d_version = D_VERSION, - .d_open = stlopen, - .d_close = stlclose, - .d_ioctl = stlioctl, - .d_name = "stl", - .d_flags = D_TTY | D_NEEDGIANT, -}; - -#endif - -/*****************************************************************************/ - -/* - * Probe for some type of EasyIO or EasyConnection 8/32 board at - * the supplied address. All we do is check if we can find the - * board ID for the board... (Note, PCI boards not checked here, - * they are done in the stlpciprobe() routine). - */ - -static int stlprobe(struct isa_device *idp) -{ - unsigned int status; - -#if DEBUG - printf("stlprobe(idp=%x): unit=%d iobase=%x\n", (int) idp, - idp->id_unit, idp->id_iobase); -#endif - - if (idp->id_unit > STL_MAXBRDS) - return(0); - - status = inb(idp->id_iobase + 1); - if ((status & ECH_IDBITMASK) == ECH_ID) { - stl_brdprobed[idp->id_unit] = BRD_ECH; - return(1); - } - - status = inb(idp->id_iobase + 2); - switch (status & EIO_IDBITMASK) { - case EIO_8PORTRS: - case EIO_8PORTM: - case EIO_8PORTDI: - case EIO_4PORTRS: - stl_brdprobed[idp->id_unit] = BRD_EASYIO; - return(1); - default: - break; - } - - return(0); -} - -/*****************************************************************************/ - -/* - * Find an available internal board number (unit number). The problem - * is that the same unit numbers can be assigned to different boards - * detected during the ISA and PCI initialization phases. - */ - -static int stl_findfreeunit() -{ - int i; - - for (i = 0; (i < STL_MAXBRDS); i++) - if (stl_brds[i] == (stlbrd_t *) NULL) - break; - return((i >= STL_MAXBRDS) ? -1 : i); -} - -/*****************************************************************************/ - -/* - * Allocate resources for and initialize the specified board. - */ - -static int stlattach(struct isa_device *idp) -{ - stlbrd_t *brdp; - -#if DEBUG - printf("stlattach(idp=%p): unit=%d iobase=%x\n", (void *) idp, - idp->id_unit, idp->id_iobase); -#endif - - idp->id_ointr = stlintr; - - brdp = (stlbrd_t *) malloc(sizeof(stlbrd_t), M_TTYS, M_NOWAIT | M_ZERO); - if (brdp == (stlbrd_t *) NULL) { - printf("STALLION: failed to allocate memory (size=%d)\n", - sizeof(stlbrd_t)); - return(0); - } - - if ((brdp->brdnr = stl_findfreeunit()) < 0) { - printf("STALLION: too many boards found, max=%d\n", - STL_MAXBRDS); - return(0); - } - if (brdp->brdnr >= stl_nrbrds) - stl_nrbrds = brdp->brdnr + 1; - - brdp->unitid = idp->id_unit; - brdp->brdtype = stl_brdprobed[idp->id_unit]; - brdp->ioaddr1 = idp->id_iobase; - brdp->ioaddr2 = stl_ioshared; - brdp->irq = ffs(idp->id_irq) - 1; - brdp->irqtype = stl_irqshared; - stl_brdinit(brdp); - - if (0) { - make_dev(&stl_cdevsw, 0, 0, 0, 0, "stallion_is_broken"); - } - return(1); -} - -/*****************************************************************************/ - -#if NPCI > 0 - -/* - * Probe specifically for the PCI boards. We need to be a little - * carefull here, since it looks sort like a Nat Semi IDE chip... - */ - -static const char *stlpciprobe(pcici_t tag, pcidi_t type) -{ - unsigned long class; - -#if DEBUG - printf("stlpciprobe(tag=%x,type=%x)\n", (int) &tag, (int) type); -#endif - - switch (type) { - case STL_PCIDEVID: - break; - default: - return((char *) NULL); - } - - class = pci_conf_read(tag, PCI_CLASS_REG); - if ((class & PCI_CLASS_MASK) == PCI_CLASS_MASS_STORAGE) - return((char *) NULL); - - return("Stallion EasyConnection 8/32-PCI"); -} - -/*****************************************************************************/ - -/* - * Allocate resources for and initialize the specified PCI board. - */ - -void stlpciattach(pcici_t tag, int unit) -{ - stlbrd_t *brdp; - -#if DEBUG - printf("stlpciattach(tag=%x,unit=%x)\n", (int) &tag, unit); -#endif - - brdp = (stlbrd_t *) malloc(sizeof(stlbrd_t), M_TTYS, M_NOWAIT | M_ZERO); - if (brdp == (stlbrd_t *) NULL) { - printf("STALLION: failed to allocate memory (size=%d)\n", - sizeof(stlbrd_t)); - return; - } - - if ((unit < 0) || (unit > STL_MAXBRDS)) { - printf("STALLION: bad PCI board unit number=%d\n", unit); - return; - } - -/* - * Allocate us a new driver unique unit number. - */ - if ((brdp->brdnr = stl_findfreeunit()) < 0) { - printf("STALLION: too many boards found, max=%d\n", - STL_MAXBRDS); - return; - } - if (brdp->brdnr >= stl_nrbrds) - stl_nrbrds = brdp->brdnr + 1; - - brdp->unitid = 0; - brdp->brdtype = BRD_ECHPCI; - brdp->ioaddr1 = ((unsigned int) pci_conf_read(tag, 0x14)) & 0xfffc; - brdp->ioaddr2 = ((unsigned int) pci_conf_read(tag, 0x10)) & 0xfffc; - brdp->irq = ((int) pci_conf_read(tag, 0x3c)) & 0xff; - brdp->irqtype = 0; - if (pci_map_int(tag, stlpciintr, (void *) NULL, &tty_imask) == 0) { - printf("STALLION: failed to map interrupt irq=%d for unit=%d\n", - brdp->irq, brdp->brdnr); - return; - } - -#if 0 - printf("%s(%d): ECH-PCI iobase=%x iopage=%x irq=%d\n", __file__, __LINE__, brdp->ioaddr2, brdp->ioaddr1, brdp->irq); -#endif - stl_brdinit(brdp); -} - -#endif - -/*****************************************************************************/ - -STATIC int stlopen(dev_t dev, int flag, int mode, struct thread *td) -{ - struct tty *tp; - stlport_t *portp; - int error, callout, x; - -#if DEBUG - printf("stlopen(dev=%x,flag=%x,mode=%x,p=%x)\n", (int) dev, flag, - mode, (int) td); -#endif - -/* - * Firstly check if the supplied device number is a valid device. - */ - if (minor(dev) & STL_MEMDEV) - return(0); - - portp = stl_dev2port(dev); - if (portp == (stlport_t *) NULL) - return(ENXIO); - tp = &portp->tty; - dev->si_tty = tp; - callout = minor(dev) & STL_CALLOUTDEV; - error = 0; - - x = spltty(); - -stlopen_restart: -/* - * Wait here for the DTR drop timeout period to expire. - */ - while (portp->state & ASY_DTRWAIT) { - error = tsleep(&portp->dtrwait, (TTIPRI | PCATCH), - "stldtr", 0); - if (error) - goto stlopen_end; - } - -/* - * We have a valid device, so now we check if it is already open. - * If not then initialize the port hardware and set up the tty - * struct as required. - */ - if ((tp->t_state & TS_ISOPEN) == 0) { - tp->t_oproc = stl_start; - tp->t_stop = stl_stop; - tp->t_param = stl_param; - tp->t_dev = dev; - tp->t_termios = callout ? portp->initouttios : - portp->initintios; - stl_rawopen(portp); - if ((portp->sigs & TIOCM_CD) || callout) - (*linesw[tp->t_line].l_modem)(tp, 1); - } else { - if (callout) { - if (portp->callout == 0) { - error = EBUSY; - goto stlopen_end; - } - } else { - if (portp->callout != 0) { - if (flag & O_NONBLOCK) { - error = EBUSY; - goto stlopen_end; - } - error = tsleep(&portp->callout, - (TTIPRI | PCATCH), "stlcall", 0); - if (error) - goto stlopen_end; - goto stlopen_restart; - } - } - if ((tp->t_state & TS_XCLUDE) && - suser(td)) { - error = EBUSY; - goto stlopen_end; - } - } - -/* - * If this port is not the callout device and we do not have carrier - * then we need to sleep, waiting for it to be asserted. - */ - if (((tp->t_state & TS_CARR_ON) == 0) && !callout && - ((tp->t_cflag & CLOCAL) == 0) && - ((flag & O_NONBLOCK) == 0)) { - portp->waitopens++; - error = tsleep(TSA_CARR_ON(tp), (TTIPRI | PCATCH), "stldcd", 0); - portp->waitopens--; - if (error) - goto stlopen_end; - goto stlopen_restart; - } - -/* - * Open the line discipline. - */ - error = (*linesw[tp->t_line].l_open)(dev, tp); - stl_ttyoptim(portp, &tp->t_termios); - if ((tp->t_state & TS_ISOPEN) && callout) - portp->callout = 1; - -/* - * If for any reason we get to here and the port is not actually - * open then close of the physical hardware - no point leaving it - * active when the open failed... - */ -stlopen_end: - splx(x); - if (((tp->t_state & TS_ISOPEN) == 0) && (portp->waitopens == 0)) - stl_rawclose(portp); - - return(error); -} - -/*****************************************************************************/ - -STATIC int stlclose(dev_t dev, int flag, int mode, struct thread *td) -{ - struct tty *tp; - stlport_t *portp; - int x; - -#if DEBUG - printf("stlclose(dev=%s,flag=%x,mode=%x,p=%p)\n", devtoname(dev), - flag, mode, (void *) td); -#endif - - if (minor(dev) & STL_MEMDEV) - return(0); - - portp = stl_dev2port(dev); - if (portp == (stlport_t *) NULL) - return(ENXIO); - tp = &portp->tty; - - x = spltty(); - (*linesw[tp->t_line].l_close)(tp, flag); - stl_ttyoptim(portp, &tp->t_termios); - stl_rawclose(portp); - ttyclose(tp); - splx(x); - return(0); -} - -/*****************************************************************************/ - -#if VFREEBSD >= 220 - -STATIC void stl_stop(struct tty *tp, int rw) -{ -#if DEBUG - printf("stl_stop(tp=%x,rw=%x)\n", (int) tp, rw); -#endif - - stl_flush((stlport_t *) tp, rw); -} - -#else - -STATIC int stlstop(struct tty *tp, int rw) -{ -#if DEBUG - printf("stlstop(tp=%x,rw=%x)\n", (int) tp, rw); -#endif - - stl_flush((stlport_t *) tp, rw); - return(0); -} - -#endif - -/*****************************************************************************/ - -STATIC int stlioctl(dev_t dev, unsigned long cmd, caddr_t data, int flag, - struct thread *td) -{ - struct termios *newtios, *localtios; - struct tty *tp; - stlport_t *portp; - int error, i, x; - -#if DEBUG - printf("stlioctl(dev=%s,cmd=%lx,data=%p,flag=%x,p=%p)\n", - devtoname(dev), cmd, (void *) data, flag, (void *) td); -#endif - - if (minor(dev) & STL_MEMDEV) - return(stl_memioctl(dev, cmd, data, flag, td)); - - portp = stl_dev2port(dev); - if (portp == (stlport_t *) NULL) - return(ENODEV); - tp = &portp->tty; - error = 0; - -/* - * First up handle ioctls on the control devices. - */ - if (minor(dev) & STL_CTRLDEV) { - if ((minor(dev) & STL_CTRLDEV) == STL_CTRLINIT) - localtios = (minor(dev) & STL_CALLOUTDEV) ? - &portp->initouttios : &portp->initintios; - else if ((minor(dev) & STL_CTRLDEV) == STL_CTRLLOCK) - localtios = (minor(dev) & STL_CALLOUTDEV) ? - &portp->lockouttios : &portp->lockintios; - else - return(ENODEV); - - switch (cmd) { - case TIOCSETA: - if ((error = suser(td)) == 0) - *localtios = *((struct termios *) data); - break; - case TIOCGETA: - *((struct termios *) data) = *localtios; - break; - case TIOCGETD: - *((int *) data) = TTYDISC; - break; - case TIOCGWINSZ: - bzero(data, sizeof(struct winsize)); - break; - default: - error = ENOTTY; - break; - } - return(error); - } - -/* - * Deal with 4.3 compatibility issues if we have too... - */ -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) - if (1) { - struct termios tios; - unsigned long oldcmd; - - tios = tp->t_termios; - oldcmd = cmd; - if ((error = ttsetcompat(tp, &cmd, data, &tios))) - return(error); - if (cmd != oldcmd) - data = (caddr_t) &tios; - } -#endif - -/* - * Carry out some pre-cmd processing work first... - * Hmmm, not so sure we want this, disable for now... - */ - if ((cmd == TIOCSETA) || (cmd == TIOCSETAW) || (cmd == TIOCSETAF)) { - newtios = (struct termios *) data; - localtios = (minor(dev) & STL_CALLOUTDEV) ? - &portp->lockouttios : &portp->lockintios; - - newtios->c_iflag = (tp->t_iflag & localtios->c_iflag) | - (newtios->c_iflag & ~localtios->c_iflag); - newtios->c_oflag = (tp->t_oflag & localtios->c_oflag) | - (newtios->c_oflag & ~localtios->c_oflag); - newtios->c_cflag = (tp->t_cflag & localtios->c_cflag) | - (newtios->c_cflag & ~localtios->c_cflag); - newtios->c_lflag = (tp->t_lflag & localtios->c_lflag) | - (newtios->c_lflag & ~localtios->c_lflag); - for (i = 0; (i < NCCS); i++) { - if (localtios->c_cc[i] != 0) - newtios->c_cc[i] = tp->t_cc[i]; - } - if (localtios->c_ispeed != 0) - newtios->c_ispeed = tp->t_ispeed; - if (localtios->c_ospeed != 0) - newtios->c_ospeed = tp->t_ospeed; - } - -/* - * Call the line discipline and the common command processing to - * process this command (if they can). - */ - error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, td); - if (error != ENOIOCTL) - return(error); - - x = spltty(); - error = ttioctl(tp, cmd, data, flag); - stl_ttyoptim(portp, &tp->t_termios); - if (error != ENOIOCTL) { - splx(x); - return(error); - } - - error = 0; - -/* - * Process local commands here. These are all commands that only we - * can take care of (they all rely on actually doing something special - * to the actual hardware). - */ - switch (cmd) { - case TIOCSBRK: - stl_sendbreak(portp, -1); - break; - case TIOCCBRK: - stl_sendbreak(portp, -2); - break; - case TIOCSDTR: - stl_setsignals(portp, 1, -1); - break; - case TIOCCDTR: - stl_setsignals(portp, 0, -1); - break; - case TIOCMSET: - i = *((int *) data); - stl_setsignals(portp, ((i & TIOCM_DTR) ? 1 : 0), - ((i & TIOCM_RTS) ? 1 : 0)); - break; - case TIOCMBIS: - i = *((int *) data); - stl_setsignals(portp, ((i & TIOCM_DTR) ? 1 : -1), - ((i & TIOCM_RTS) ? 1 : -1)); - break; - case TIOCMBIC: - i = *((int *) data); - stl_setsignals(portp, ((i & TIOCM_DTR) ? 0 : -1), - ((i & TIOCM_RTS) ? 0 : -1)); - break; - case TIOCMGET: - *((int *) data) = (stl_getsignals(portp) | TIOCM_LE); - break; - case TIOCMSDTRWAIT: - if ((error = suser(td)) == 0) - portp->dtrwait = *((int *) data) * hz / 100; - break; - case TIOCMGDTRWAIT: - *((int *) data) = portp->dtrwait * 100 / hz; - break; - case TIOCTIMESTAMP: - portp->dotimestamp = 1; - *((struct timeval *) data) = portp->timestamp; - break; - default: - error = ENOTTY; - break; - } - splx(x); - - return(error); -} - -/*****************************************************************************/ - -/* - * Convert the specified minor device number into a port struct - * pointer. Return NULL if the device number is not a valid port. - */ - -STATIC stlport_t *stl_dev2port(dev_t dev) -{ - stlbrd_t *brdp; - - brdp = stl_brds[MKDEV2BRD(dev)]; - if (brdp == (stlbrd_t *) NULL) - return((stlport_t *) NULL); - return(brdp->ports[MKDEV2PORT(dev)]); -} - -/*****************************************************************************/ - -/* - * Initialize the port hardware. This involves enabling the transmitter - * and receiver, setting the port configuration, and setting the initial - * signal state. - */ - -static int stl_rawopen(stlport_t *portp) -{ -#if DEBUG - printf("stl_rawopen(portp=%p): brdnr=%d panelnr=%d portnr=%d\n", - (void *) portp, portp->brdnr, portp->panelnr, portp->portnr); -#endif - stl_param(&portp->tty, &portp->tty.t_termios); - portp->sigs = stl_getsignals(portp); - stl_setsignals(portp, 1, 1); - stl_enablerxtx(portp, 1, 1); - stl_startrxtx(portp, 1, 0); - return(0); -} - -/*****************************************************************************/ - -/* - * Shutdown the hardware of a port. Disable its transmitter and - * receiver, and maybe drop signals if appropriate. - */ - -static int stl_rawclose(stlport_t *portp) -{ - struct tty *tp; - -#if DEBUG - printf("stl_rawclose(portp=%p): brdnr=%d panelnr=%d portnr=%d\n", - (void *) portp, portp->brdnr, portp->panelnr, portp->portnr); -#endif - - tp = &portp->tty; - stl_disableintrs(portp); - stl_enablerxtx(portp, 0, 0); - stl_flush(portp, (FWRITE | FREAD)); - if (tp->t_cflag & HUPCL) { - stl_setsignals(portp, 0, 0); - if (portp->dtrwait != 0) { - portp->state |= ASY_DTRWAIT; - timeout(stl_dtrwakeup, portp, portp->dtrwait); - } - } - portp->callout = 0; - portp->brklen = 0; - portp->state &= ~(ASY_ACTIVE | ASY_RTSFLOW); - wakeup(&portp->callout); - wakeup(TSA_CARR_ON(tp)); - return(0); -} - -/*****************************************************************************/ - -/* - * Clear the DTR waiting flag, and wake up any sleepers waiting for - * DTR wait period to finish. - */ - -static void stl_dtrwakeup(void *arg) -{ - stlport_t *portp; - - portp = (stlport_t *) arg; - portp->state &= ~ASY_DTRWAIT; - wakeup(&portp->dtrwait); -} - -/*****************************************************************************/ - -/* - * Start (or continue) the transfer of TX data on this port. If the - * port is not currently busy then load up the interrupt ring queue - * buffer and kick of the transmitter. If the port is running low on - * TX data then refill the ring queue. This routine is also used to - * activate input flow control! - */ - -static void stl_start(struct tty *tp) -{ - stlport_t *portp; - unsigned int len, stlen; - char *head, *tail; - int count, x; - - portp = (stlport_t *) tp; - -#if DEBUG - printf("stl_start(tp=%x): brdnr=%d portnr=%d\n", (int) tp, - portp->brdnr, portp->portnr); -#endif - - x = spltty(); - -/* - * Check if the ports input has been blocked, and take appropriate action. - * Not very often do we really need to do anything, so make it quick. - */ - if (tp->t_state & TS_TBLOCK) { - if ((portp->state & ASY_RTSFLOW) == 0) - stl_flowcontrol(portp, 0, -1); - } else { - if (portp->state & ASY_RTSFLOW) - stl_flowcontrol(portp, 1, -1); - } - -#if VFREEBSD == 205 -/* - * Check if the output cooked clist buffers are near empty, wake up - * the line discipline to fill it up. - */ - if (tp->t_outq.c_cc <= tp->t_lowat) { - if (tp->t_state & TS_ASLEEP) { - tp->t_state &= ~TS_ASLEEP; - wakeup(&tp->t_outq); - } - selwakeuppri(&tp->t_wsel, TTOPRI); - } -#endif - - if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) { - splx(x); - return; - } - -/* - * Copy data from the clists into the interrupt ring queue. This will - * require at most 2 copys... What we do is calculate how many chars - * can fit into the ring queue, and how many can fit in 1 copy. If after - * the first copy there is still more room then do the second copy. - * The beauty of this type of ring queue is that we do not need to - * spl protect our-selves, since we only ever update the head pointer, - * and the interrupt routine only ever updates the tail pointer. - */ - if (tp->t_outq.c_cc != 0) { - head = portp->tx.head; - tail = portp->tx.tail; - if (head >= tail) { - len = STL_TXBUFSIZE - (head - tail) - 1; - stlen = portp->tx.endbuf - head; - } else { - len = tail - head - 1; - stlen = len; - } - - if (len > 0) { - stlen = MIN(len, stlen); - count = q_to_b(&tp->t_outq, head, stlen); - len -= count; - head += count; - if (head >= portp->tx.endbuf) { - head = portp->tx.buf; - if (len > 0) { - stlen = q_to_b(&tp->t_outq, head, len); - head += stlen; - count += stlen; - } - } - portp->tx.head = head; - if (count > 0) - stl_startrxtx(portp, -1, 1); - } - -/* - * If we sent something, make sure we are called again. - */ - tp->t_state |= TS_BUSY; - } - -#if VFREEBSD != 205 -/* - * Do any writer wakeups. - */ - ttwwakeup(tp); -#endif - - splx(x); -} - -/*****************************************************************************/ - -static void stl_flush(stlport_t *portp, int flag) -{ - char *head, *tail; - int len, x; - -#if DEBUG - printf("stl_flush(portp=%x,flag=%x)\n", (int) portp, flag); -#endif - - if (portp == (stlport_t *) NULL) - return; - - x = spltty(); - - if (flag & FWRITE) { - BRDENABLE(portp->brdnr, portp->pagenr); - stl_setreg(portp, CAR, (portp->portnr & 0x03)); - stl_ccrwait(portp); - stl_setreg(portp, CCR, CCR_TXFLUSHFIFO); - stl_ccrwait(portp); - portp->tx.tail = portp->tx.head; - BRDDISABLE(portp->brdnr); - } - -/* - * The only thing to watch out for when flushing the read side is - * the RX status buffer. The interrupt code relys on the status - * bytes as being zeroed all the time (it does not bother setting - * a good char status to 0, it expects that it already will be). - * We also need to un-flow the RX channel if flow control was - * active. - */ - if (flag & FREAD) { - head = portp->rx.head; - tail = portp->rx.tail; - if (head != tail) { - if (head >= tail) { - len = head - tail; - } else { - len = portp->rx.endbuf - tail; - bzero(portp->rxstatus.buf, - (head - portp->rx.buf)); - } - bzero((tail + STL_RXBUFSIZE), len); - portp->rx.tail = head; - } - - if ((portp->state & ASY_RTSFLOW) && - ((portp->tty.t_state & TS_TBLOCK) == 0)) - stl_flowcontrol(portp, 1, -1); - } - - splx(x); -} - -/*****************************************************************************/ - -/* - * These functions get/set/update the registers of the cd1400 UARTs. - * Access to the cd1400 registers is via an address/data io port pair. - * (Maybe should make this inline...) - */ - -static int stl_getreg(stlport_t *portp, int regnr) -{ - outb(portp->ioaddr, (regnr + portp->uartaddr)); - return(inb(portp->ioaddr + EREG_DATA)); -} - -/*****************************************************************************/ - -static void stl_setreg(stlport_t *portp, int regnr, int value) -{ - outb(portp->ioaddr, (regnr + portp->uartaddr)); - outb((portp->ioaddr + EREG_DATA), value); -} - -/*****************************************************************************/ - -static int stl_updatereg(stlport_t *portp, int regnr, int value) -{ - outb(portp->ioaddr, (regnr + portp->uartaddr)); - if (inb(portp->ioaddr + EREG_DATA) != value) { - outb((portp->ioaddr + EREG_DATA), value); - return(1); - } - return(0); -} - -/*****************************************************************************/ - -/* - * Wait for the command register to be ready. We will poll this, since - * it won't usually take too long to be ready, and it is only really - * used for non-critical actions. - */ - -static void stl_ccrwait(stlport_t *portp) -{ - int i; - - for (i = 0; (i < CCR_MAXWAIT); i++) { - if (stl_getreg(portp, CCR) == 0) { - return; - } - } - - printf("STALLION: cd1400 device not responding, brd=%d panel=%d" - "port=%d\n", portp->brdnr, portp->panelnr, portp->portnr); -} - -/*****************************************************************************/ - -/* - * Transmit interrupt handler. This has gotta be fast! Handling TX - * chars is pretty simple, stuff as many as possible from the TX buffer - * into the cd1400 FIFO. Must also handle TX breaks here, since they - * are embedded as commands in the data stream. Oh no, had to use a goto! - * This could be optimized more, will do when I get time... - * In practice it is possible that interrupts are enabled but that the - * port has been hung up. Need to handle not having any TX buffer here, - * this is done by using the side effect that head and tail will also - * be NULL if the buffer has been freed. - */ - -static __inline void stl_txisr(stlpanel_t *panelp, int ioaddr) -{ - stlport_t *portp; - int len, stlen; - char *head, *tail; - unsigned char ioack, srer; - -#if DEBUG - printf("stl_txisr(panelp=%x,ioaddr=%x)\n", (int) panelp, ioaddr); -#endif - - ioack = inb(ioaddr + EREG_TXACK); - if (((ioack & panelp->ackmask) != 0) || - ((ioack & ACK_TYPMASK) != ACK_TYPTX)) { - printf("STALLION: bad TX interrupt ack value=%x\n", ioack); - return; - } - portp = panelp->ports[(ioack >> 3)]; - -/* - * Unfortunately we need to handle breaks in the data stream, since - * this is the only way to generate them on the cd1400. Do it now if - * a break is to be sent. Some special cases here: brklen is -1 then - * start sending an un-timed break, if brklen is -2 then stop sending - * an un-timed break, if brklen is -3 then we have just sent an - * un-timed break and do not want any data to go out, if brklen is -4 - * then a break has just completed so clean up the port settings. - */ - if (portp->brklen != 0) { - if (portp->brklen >= -1) { - outb(ioaddr, (TDR + portp->uartaddr)); - outb((ioaddr + EREG_DATA), ETC_CMD); - outb((ioaddr + EREG_DATA), ETC_STARTBREAK); - if (portp->brklen > 0) { - outb((ioaddr + EREG_DATA), ETC_CMD); - outb((ioaddr + EREG_DATA), ETC_DELAY); - outb((ioaddr + EREG_DATA), portp->brklen); - outb((ioaddr + EREG_DATA), ETC_CMD); - outb((ioaddr + EREG_DATA), ETC_STOPBREAK); - portp->brklen = -4; - } else { - portp->brklen = -3; - } - } else if (portp->brklen == -2) { - outb(ioaddr, (TDR + portp->uartaddr)); - outb((ioaddr + EREG_DATA), ETC_CMD); - outb((ioaddr + EREG_DATA), ETC_STOPBREAK); - portp->brklen = -4; - } else if (portp->brklen == -3) { - outb(ioaddr, (SRER + portp->uartaddr)); - srer = inb(ioaddr + EREG_DATA); - srer &= ~(SRER_TXDATA | SRER_TXEMPTY); - outb((ioaddr + EREG_DATA), srer); - } else { - outb(ioaddr, (COR2 + portp->uartaddr)); - outb((ioaddr + EREG_DATA), - (inb(ioaddr + EREG_DATA) & ~COR2_ETC)); - portp->brklen = 0; - } - goto stl_txalldone; - } - - head = portp->tx.head; - tail = portp->tx.tail; - len = (head >= tail) ? (head - tail) : (STL_TXBUFSIZE - (tail - head)); - if ((len == 0) || ((len < STL_TXBUFLOW) && - ((portp->state & ASY_TXLOW) == 0))) { - portp->state |= ASY_TXLOW; - stl_dotimeout(); - } - - if (len == 0) { - outb(ioaddr, (SRER + portp->uartaddr)); - srer = inb(ioaddr + EREG_DATA); - if (srer & SRER_TXDATA) { - srer = (srer & ~SRER_TXDATA) | SRER_TXEMPTY; - } else { - srer &= ~(SRER_TXDATA | SRER_TXEMPTY); - portp->tty.t_state &= ~TS_BUSY; - } - outb((ioaddr + EREG_DATA), srer); - } else { - len = MIN(len, CD1400_TXFIFOSIZE); - portp->stats.txtotal += len; - stlen = MIN(len, (portp->tx.endbuf - tail)); - outb(ioaddr, (TDR + portp->uartaddr)); - outsb((ioaddr + EREG_DATA), tail, stlen); - len -= stlen; - tail += stlen; - if (tail >= portp->tx.endbuf) - tail = portp->tx.buf; - if (len > 0) { - outsb((ioaddr + EREG_DATA), tail, len); - tail += len; - } - portp->tx.tail = tail; - } - -stl_txalldone: - outb(ioaddr, (EOSRR + portp->uartaddr)); - outb((ioaddr + EREG_DATA), 0); -} - -/*****************************************************************************/ - -/* - * Receive character interrupt handler. Determine if we have good chars - * or bad chars and then process appropriately. Good chars are easy - * just shove the lot into the RX buffer and set all status bytes to 0. - * If a bad RX char then process as required. This routine needs to be - * fast! - */ - -static __inline void stl_rxisr(stlpanel_t *panelp, int ioaddr) -{ - stlport_t *portp; - struct tty *tp; - unsigned int ioack, len, buflen, stlen; - unsigned char status; - char ch; - char *head, *tail; - static char unwanted[CD1400_RXFIFOSIZE]; - -#if DEBUG - printf("stl_rxisr(panelp=%x,ioaddr=%x)\n", (int) panelp, ioaddr); -#endif - - ioack = inb(ioaddr + EREG_RXACK); - if ((ioack & panelp->ackmask) != 0) { - printf("STALLION: bad RX interrupt ack value=%x\n", ioack); - return; - } - portp = panelp->ports[(ioack >> 3)]; - tp = &portp->tty; - -/* - * First up, caluclate how much room there is in the RX ring queue. - * We also want to keep track of the longest possible copy length, - * this has to allow for the wrapping of the ring queue. - */ - head = portp->rx.head; - tail = portp->rx.tail; - if (head >= tail) { - buflen = STL_RXBUFSIZE - (head - tail) - 1; - stlen = portp->rx.endbuf - head; - } else { - buflen = tail - head - 1; - stlen = buflen; - } - -/* - * Check if the input buffer is near full. If so then we should take - * some flow control action... It is very easy to do hardware and - * software flow control from here since we have the port selected on - * the UART. - */ - if (buflen <= (STL_RXBUFSIZE - STL_RXBUFHIGH)) { - if (((portp->state & ASY_RTSFLOW) == 0) && - (portp->state & ASY_RTSFLOWMODE)) { - portp->state |= ASY_RTSFLOW; - stl_setreg(portp, MCOR1, - (stl_getreg(portp, MCOR1) & 0xf0)); - stl_setreg(portp, MSVR2, 0); - portp->stats.rxrtsoff++; - } - } - -/* - * OK we are set, process good data... If the RX ring queue is full - * just chuck the chars - don't leave them in the UART. - */ - if ((ioack & ACK_TYPMASK) == ACK_TYPRXGOOD) { - outb(ioaddr, (RDCR + portp->uartaddr)); - len = inb(ioaddr + EREG_DATA); - if (buflen == 0) { - outb(ioaddr, (RDSR + portp->uartaddr)); - insb((ioaddr + EREG_DATA), &unwanted[0], len); - portp->stats.rxlost += len; - portp->stats.rxtotal += len; - } else { - len = MIN(len, buflen); - portp->stats.rxtotal += len; - stlen = MIN(len, stlen); - if (len > 0) { - outb(ioaddr, (RDSR + portp->uartaddr)); - insb((ioaddr + EREG_DATA), head, stlen); - head += stlen; - if (head >= portp->rx.endbuf) { - head = portp->rx.buf; - len -= stlen; - insb((ioaddr + EREG_DATA), head, len); - head += len; - } - } - } - } else if ((ioack & ACK_TYPMASK) == ACK_TYPRXBAD) { - outb(ioaddr, (RDSR + portp->uartaddr)); - status = inb(ioaddr + EREG_DATA); - ch = inb(ioaddr + EREG_DATA); - if (status & ST_BREAK) - portp->stats.rxbreaks++; - if (status & ST_FRAMING) - portp->stats.rxframing++; - if (status & ST_PARITY) - portp->stats.rxparity++; - if (status & ST_OVERRUN) - portp->stats.rxoverrun++; - if (status & ST_SCHARMASK) { - if ((status & ST_SCHARMASK) == ST_SCHAR1) - portp->stats.txxon++; - if ((status & ST_SCHARMASK) == ST_SCHAR2) - portp->stats.txxoff++; - goto stl_rxalldone; - } - if ((portp->rxignoremsk & status) == 0) { - if ((tp->t_state & TS_CAN_BYPASS_L_RINT) && - ((status & ST_FRAMING) || - ((status & ST_PARITY) && (tp->t_iflag & INPCK)))) - ch = 0; - if ((portp->rxmarkmsk & status) == 0) - status = 0; - *(head + STL_RXBUFSIZE) = status; - *head++ = ch; - if (head >= portp->rx.endbuf) - head = portp->rx.buf; - } - } else { - printf("STALLION: bad RX interrupt ack value=%x\n", ioack); - return; - } - - portp->rx.head = head; - portp->state |= ASY_RXDATA; - stl_dotimeout(); - -stl_rxalldone: - outb(ioaddr, (EOSRR + portp->uartaddr)); - outb((ioaddr + EREG_DATA), 0); -} - -/*****************************************************************************/ - -/* - * Modem interrupt handler. The is called when the modem signal line - * (DCD) has changed state. Leave most of the work to the off-level - * processing routine. - */ - -static __inline void stl_mdmisr(stlpanel_t *panelp, int ioaddr) -{ - stlport_t *portp; - unsigned int ioack; - unsigned char misr; - -#if DEBUG - printf("stl_mdmisr(panelp=%x,ioaddr=%x)\n", (int) panelp, ioaddr); -#endif - - ioack = inb(ioaddr + EREG_MDACK); - if (((ioack & panelp->ackmask) != 0) || - ((ioack & ACK_TYPMASK) != ACK_TYPMDM)) { - printf("STALLION: bad MODEM interrupt ack value=%x\n", ioack); - return; - } - portp = panelp->ports[(ioack >> 3)]; - - outb(ioaddr, (MISR + portp->uartaddr)); - misr = inb(ioaddr + EREG_DATA); - if (misr & MISR_DCD) { - portp->state |= ASY_DCDCHANGE; - portp->stats.modem++; - stl_dotimeout(); - } - - outb(ioaddr, (EOSRR + portp->uartaddr)); - outb((ioaddr + EREG_DATA), 0); -} - -/*****************************************************************************/ - -/* - * Interrupt handler for EIO and ECH boards. This code ain't all that - * pretty, but the idea is to make it as fast as possible. This code is - * well suited to be assemblerized :-) We don't use the general purpose - * register access functions here, for speed we will go strait to the - * io register. - */ - -static void stlintr(int unit) -{ - stlbrd_t *brdp; - stlpanel_t *panelp; - unsigned char svrtype; - int i, panelnr, iobase; - int cnt; - -#if DEBUG - printf("stlintr(unit=%d)\n", unit); -#endif - - cnt = 0; - panelp = (stlpanel_t *) NULL; - for (i = 0; (i < stl_nrbrds); ) { - if ((brdp = stl_brds[i]) == (stlbrd_t *) NULL) { - i++; - continue; - } - if (brdp->state == 0) { - i++; - continue; - } -/* - * The following section of code handles the subtle differences - * between board types. It is sort of similar, but different - * enough to handle each separately. - */ - if (brdp->brdtype == BRD_EASYIO) { - if ((inb(brdp->iostatus) & EIO_INTRPEND) == 0) { - i++; - continue; - } - panelp = brdp->panels[0]; - iobase = panelp->iobase; - outb(iobase, SVRR); - svrtype = inb(iobase + EREG_DATA); - if (brdp->nrports > 4) { - outb(iobase, (SVRR + 0x80)); - svrtype |= inb(iobase + EREG_DATA); - } - } else if (brdp->brdtype == BRD_ECH) { - if ((inb(brdp->iostatus) & ECH_INTRPEND) == 0) { - i++; - continue; - } - outb(brdp->ioctrl, (brdp->ioctrlval | ECH_BRDENABLE)); - for (panelnr = 0; (panelnr < brdp->nrpanels); panelnr++) { - panelp = brdp->panels[panelnr]; - iobase = panelp->iobase; - if (inb(iobase + ECH_PNLSTATUS) & ECH_PNLINTRPEND) - break; - if (panelp->nrports > 8) { - iobase += 0x8; - if (inb(iobase + ECH_PNLSTATUS) & ECH_PNLINTRPEND) - break; - } - } - if (panelnr >= brdp->nrpanels) { - i++; - continue; - } - outb(iobase, SVRR); - svrtype = inb(iobase + EREG_DATA); - outb(iobase, (SVRR + 0x80)); - svrtype |= inb(iobase + EREG_DATA); - } else if (brdp->brdtype == BRD_ECHPCI) { - iobase = brdp->ioaddr2; - for (panelnr = 0; (panelnr < brdp->nrpanels); panelnr++) { - panelp = brdp->panels[panelnr]; - outb(brdp->ioctrl, panelp->pagenr); - if (inb(iobase + ECH_PNLSTATUS) & ECH_PNLINTRPEND) - break; - if (panelp->nrports > 8) { - outb(brdp->ioctrl, (panelp->pagenr + 1)); - if (inb(iobase + ECH_PNLSTATUS) & ECH_PNLINTRPEND) - break; - } - } - if (panelnr >= brdp->nrpanels) { - i++; - continue; - } - outb(iobase, SVRR); - svrtype = inb(iobase + EREG_DATA); - outb(iobase, (SVRR + 0x80)); - svrtype |= inb(iobase + EREG_DATA); - } else if (brdp->brdtype == BRD_ECHMC) { - if ((inb(brdp->iostatus) & ECH_INTRPEND) == 0) { - i++; - continue; - } - for (panelnr = 0; (panelnr < brdp->nrpanels); panelnr++) { - panelp = brdp->panels[panelnr]; - iobase = panelp->iobase; - if (inb(iobase + ECH_PNLSTATUS) & ECH_PNLINTRPEND) - break; - if (panelp->nrports > 8) { - iobase += 0x8; - if (inb(iobase + ECH_PNLSTATUS) & ECH_PNLINTRPEND) - break; - } - } - if (panelnr >= brdp->nrpanels) { - i++; - continue; - } - outb(iobase, SVRR); - svrtype = inb(iobase + EREG_DATA); - outb(iobase, (SVRR + 0x80)); - svrtype |= inb(iobase + EREG_DATA); - } else { - printf("STALLION: unknown board type=%x\n", brdp->brdtype); - i++; - continue; - } - -/* - * We have determined what type of service is required for a - * port. From here on in the service of a port is the same no - * matter what the board type... - */ - if (svrtype & SVRR_RX) - stl_rxisr(panelp, iobase); - if (svrtype & SVRR_TX) - stl_txisr(panelp, iobase); - if (svrtype & SVRR_MDM) - stl_mdmisr(panelp, iobase); - - if (brdp->brdtype == BRD_ECH) - outb(brdp->ioctrl, (brdp->ioctrlval | ECH_BRDDISABLE)); - } -} - -/*****************************************************************************/ - -#if NPCI > 0 - -static void stlpciintr(void *arg) -{ - stlintr(0); -} - -#endif - -/*****************************************************************************/ - -/* - * If we haven't scheduled a timeout then do it, some port needs high - * level processing. - */ - -static void stl_dotimeout() -{ -#if DEBUG - printf("stl_dotimeout()\n"); -#endif - - if (stl_doingtimeout == 0) { - timeout(stl_poll, 0, 1); - stl_doingtimeout++; - } -} - -/*****************************************************************************/ - -/* - * Service "software" level processing. Too slow or painfull to be done - * at real hardware interrupt time. This way we might also be able to - * do some service on other waiting ports as well... - */ - -static void stl_poll(void *arg) -{ - stlbrd_t *brdp; - stlport_t *portp; - struct tty *tp; - int brdnr, portnr, rearm, x; - -#if DEBUG - printf("stl_poll()\n"); -#endif - - stl_doingtimeout = 0; - rearm = 0; - - x = spltty(); - for (brdnr = 0; (brdnr < stl_nrbrds); brdnr++) { - if ((brdp = stl_brds[brdnr]) == (stlbrd_t *) NULL) - continue; - for (portnr = 0; (portnr < brdp->nrports); portnr++) { - if ((portp = brdp->ports[portnr]) == (stlport_t *) NULL) - continue; - if ((portp->state & ASY_ACTIVE) == 0) - continue; - tp = &portp->tty; - - if (portp->state & ASY_RXDATA) - stl_rxprocess(portp); - if (portp->state & ASY_DCDCHANGE) { - portp->state &= ~ASY_DCDCHANGE; - portp->sigs = stl_getsignals(portp); - (*linesw[tp->t_line].l_modem)(tp, - (portp->sigs & TIOCM_CD)); - } - if (portp->state & ASY_TXLOW) { - portp->state &= ~ASY_TXLOW; - (*linesw[tp->t_line].l_start)(tp); - } - - if (portp->state & ASY_ACTIVE) - rearm++; - } - } - splx(x); - - if (rearm) - stl_dotimeout(); -} - -/*****************************************************************************/ - -/* - * Process the RX data that has been buffered up in the RX ring queue. - */ - -static void stl_rxprocess(stlport_t *portp) -{ - struct tty *tp; - unsigned int len, stlen, lostlen; - char *head, *tail; - char status; - int ch; - -#if DEBUG - printf("stl_rxprocess(portp=%x): brdnr=%d portnr=%d\n", (int) portp, - portp->brdnr, portp->portnr); -#endif - - tp = &portp->tty; - portp->state &= ~ASY_RXDATA; - - if ((tp->t_state & TS_ISOPEN) == 0) { - stl_flush(portp, FREAD); - return; - } - -/* - * Calculate the amount of data in the RX ring queue. Also calculate - * the largest single copy size... - */ - head = portp->rx.head; - tail = portp->rx.tail; - if (head >= tail) { - len = head - tail; - stlen = len; - } else { - len = STL_RXBUFSIZE - (tail - head); - stlen = portp->rx.endbuf - tail; - } - - if (tp->t_state & TS_CAN_BYPASS_L_RINT) { - if (len > 0) { - if (((tp->t_rawq.c_cc + len) >= TTYHOG) && - ((portp->state & ASY_RTSFLOWMODE) || - (tp->t_iflag & IXOFF)) && - ((tp->t_state & TS_TBLOCK) == 0)) { - ch = TTYHOG - tp->t_rawq.c_cc - 1; - len = (ch > 0) ? ch : 0; - stlen = MIN(stlen, len); - ttyblock(tp); - } - lostlen = b_to_q(tail, stlen, &tp->t_rawq); - tail += stlen; - len -= stlen; - if (tail >= portp->rx.endbuf) { - tail = portp->rx.buf; - lostlen += b_to_q(tail, len, &tp->t_rawq); - tail += len; - } - portp->stats.rxlost += lostlen; - ttwakeup(tp); - portp->rx.tail = tail; - } - } else { - while (portp->rx.tail != head) { - ch = (unsigned char) *(portp->rx.tail); - status = *(portp->rx.tail + STL_RXBUFSIZE); - if (status) { - *(portp->rx.tail + STL_RXBUFSIZE) = 0; - if (status & ST_BREAK) - ch |= TTY_BI; - if (status & ST_FRAMING) - ch |= TTY_FE; - if (status & ST_PARITY) - ch |= TTY_PE; - if (status & ST_OVERRUN) - ch |= TTY_OE; - } - (*linesw[tp->t_line].l_rint)(ch, tp); - if (portp->rx.tail == head) - break; - - if (++(portp->rx.tail) >= portp->rx.endbuf) - portp->rx.tail = portp->rx.buf; - } - } - - if (head != portp->rx.tail) - portp->state |= ASY_RXDATA; - -/* - * If we where flow controled then maybe the buffer is low enough that - * we can re-activate it. - */ - if ((portp->state & ASY_RTSFLOW) && ((tp->t_state & TS_TBLOCK) == 0)) - stl_flowcontrol(portp, 1, -1); -} - -/*****************************************************************************/ - -/* - * Set up the cd1400 registers for a port based on the termios port - * settings. - */ - -static int stl_param(struct tty *tp, struct termios *tiosp) -{ - stlport_t *portp; - unsigned int clkdiv; - unsigned char cor1, cor2, cor3; - unsigned char cor4, cor5, ccr; - unsigned char srer, sreron, sreroff; - unsigned char mcor1, mcor2, rtpr; - unsigned char clk, div; - int x; - - portp = (stlport_t *) tp; - -#if DEBUG - printf("stl_param(tp=%x,tiosp=%x): brdnr=%d portnr=%d\n", (int) tp, - (int) tiosp, portp->brdnr, portp->portnr); -#endif - - cor1 = 0; - cor2 = 0; - cor3 = 0; - cor4 = 0; - cor5 = 0; - ccr = 0; - rtpr = 0; - clk = 0; - div = 0; - mcor1 = 0; - mcor2 = 0; - sreron = 0; - sreroff = 0; - -/* - * Set up the RX char ignore mask with those RX error types we - * can ignore. We could have used some special modes of the cd1400 - * UART to help, but it is better this way because we can keep stats - * on the number of each type of RX exception event. - */ - portp->rxignoremsk = 0; - if (tiosp->c_iflag & IGNPAR) - portp->rxignoremsk |= (ST_PARITY | ST_FRAMING | ST_OVERRUN); - if (tiosp->c_iflag & IGNBRK) - portp->rxignoremsk |= ST_BREAK; - - portp->rxmarkmsk = ST_OVERRUN; - if (tiosp->c_iflag & (INPCK | PARMRK)) - portp->rxmarkmsk |= (ST_PARITY | ST_FRAMING); - if (tiosp->c_iflag & BRKINT) - portp->rxmarkmsk |= ST_BREAK; - -/* - * Go through the char size, parity and stop bits and set all the - * option registers appropriately. - */ - switch (tiosp->c_cflag & CSIZE) { - case CS5: - cor1 |= COR1_CHL5; - break; - case CS6: - cor1 |= COR1_CHL6; - break; - case CS7: - cor1 |= COR1_CHL7; - break; - default: - cor1 |= COR1_CHL8; - break; - } - - if (tiosp->c_cflag & CSTOPB) - cor1 |= COR1_STOP2; - else - cor1 |= COR1_STOP1; - - if (tiosp->c_cflag & PARENB) { - if (tiosp->c_cflag & PARODD) - cor1 |= (COR1_PARENB | COR1_PARODD); - else - cor1 |= (COR1_PARENB | COR1_PAREVEN); - } else { - cor1 |= COR1_PARNONE; - } - - if (tiosp->c_iflag & ISTRIP) - cor5 |= COR5_ISTRIP; - -/* - * Set the RX FIFO threshold at 6 chars. This gives a bit of breathing - * space for hardware flow control and the like. This should be set to - * VMIN. Also here we will set the RX data timeout to 10ms - this should - * really be based on VTIME... - */ - cor3 |= FIFO_RXTHRESHOLD; - rtpr = 2; - -/* - * Calculate the baud rate timers. For now we will just assume that - * the input and output baud are the same. Could have used a baud - * table here, but this way we can generate virtually any baud rate - * we like! - */ - if (tiosp->c_ispeed == 0) - tiosp->c_ispeed = tiosp->c_ospeed; - if ((tiosp->c_ospeed < 0) || (tiosp->c_ospeed > STL_MAXBAUD)) - return(EINVAL); - - if (tiosp->c_ospeed > 0) { - for (clk = 0; (clk < CD1400_NUMCLKS); clk++) { - clkdiv = ((portp->clk / stl_cd1400clkdivs[clk]) / - tiosp->c_ospeed); - if (clkdiv < 0x100) - break; - } - div = (unsigned char) clkdiv; - } - -/* - * Check what form of modem signaling is required and set it up. - */ - if ((tiosp->c_cflag & CLOCAL) == 0) { - mcor1 |= MCOR1_DCD; - mcor2 |= MCOR2_DCD; - sreron |= SRER_MODEM; - } - -/* - * Setup cd1400 enhanced modes if we can. In particular we want to - * handle as much of the flow control as possbile automatically. As - * well as saving a few CPU cycles it will also greatly improve flow - * control reliablilty. - */ - if (tiosp->c_iflag & IXON) { - cor2 |= COR2_TXIBE; - cor3 |= COR3_SCD12; - if (tiosp->c_iflag & IXANY) - cor2 |= COR2_IXM; - } - - if (tiosp->c_cflag & CCTS_OFLOW) - cor2 |= COR2_CTSAE; - if (tiosp->c_cflag & CRTS_IFLOW) - mcor1 |= FIFO_RTSTHRESHOLD; - -/* - * All cd1400 register values calculated so go through and set them - * all up. - */ -#if DEBUG - printf("SETPORT: portnr=%d panelnr=%d brdnr=%d\n", portp->portnr, - portp->panelnr, portp->brdnr); - printf(" cor1=%x cor2=%x cor3=%x cor4=%x cor5=%x\n", cor1, cor2, - cor3, cor4, cor5); - printf(" mcor1=%x mcor2=%x rtpr=%x sreron=%x sreroff=%x\n", - mcor1, mcor2, rtpr, sreron, sreroff); - printf(" tcor=%x tbpr=%x rcor=%x rbpr=%x\n", clk, div, clk, div); - printf(" schr1=%x schr2=%x schr3=%x schr4=%x\n", - tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP], tiosp->c_cc[VSTART], - tiosp->c_cc[VSTOP]); -#endif - - x = spltty(); - BRDENABLE(portp->brdnr, portp->pagenr); - stl_setreg(portp, CAR, (portp->portnr & 0x3)); - srer = stl_getreg(portp, SRER); - stl_setreg(portp, SRER, 0); - ccr += stl_updatereg(portp, COR1, cor1); - ccr += stl_updatereg(portp, COR2, cor2); - ccr += stl_updatereg(portp, COR3, cor3); - if (ccr) { - stl_ccrwait(portp); - stl_setreg(portp, CCR, CCR_CORCHANGE); - } - stl_setreg(portp, COR4, cor4); - stl_setreg(portp, COR5, cor5); - stl_setreg(portp, MCOR1, mcor1); - stl_setreg(portp, MCOR2, mcor2); - if (tiosp->c_ospeed == 0) { - stl_setreg(portp, MSVR1, 0); - } else { - stl_setreg(portp, MSVR1, MSVR1_DTR); - stl_setreg(portp, TCOR, clk); - stl_setreg(portp, TBPR, div); - stl_setreg(portp, RCOR, clk); - stl_setreg(portp, RBPR, div); - } - stl_setreg(portp, SCHR1, tiosp->c_cc[VSTART]); - stl_setreg(portp, SCHR2, tiosp->c_cc[VSTOP]); - stl_setreg(portp, SCHR3, tiosp->c_cc[VSTART]); - stl_setreg(portp, SCHR4, tiosp->c_cc[VSTOP]); - stl_setreg(portp, RTPR, rtpr); - mcor1 = stl_getreg(portp, MSVR1); - if (mcor1 & MSVR1_DCD) - portp->sigs |= TIOCM_CD; - else - portp->sigs &= ~TIOCM_CD; - stl_setreg(portp, SRER, ((srer & ~sreroff) | sreron)); - BRDDISABLE(portp->brdnr); - portp->state &= ~(ASY_RTSFLOWMODE | ASY_CTSFLOWMODE); - portp->state |= ((tiosp->c_cflag & CRTS_IFLOW) ? ASY_RTSFLOWMODE : 0); - portp->state |= ((tiosp->c_cflag & CCTS_OFLOW) ? ASY_CTSFLOWMODE : 0); - stl_ttyoptim(portp, tiosp); - splx(x); - - return(0); -} - -/*****************************************************************************/ - -/* - * Action the flow control as required. The hw and sw args inform the - * routine what flow control methods it should try. - */ - -static void stl_flowcontrol(stlport_t *portp, int hw, int sw) -{ - unsigned char *head, *tail; - int len, hwflow, x; - -#if DEBUG - printf("stl_flowcontrol(portp=%x,hw=%d,sw=%d)\n", (int) portp, hw, sw); -#endif - - hwflow = -1; - - if (portp->state & ASY_RTSFLOWMODE) { - if (hw == 0) { - if ((portp->state & ASY_RTSFLOW) == 0) - hwflow = 0; - } else if (hw > 0) { - if (portp->state & ASY_RTSFLOW) { - head = portp->rx.head; - tail = portp->rx.tail; - len = (head >= tail) ? (head - tail) : - (STL_RXBUFSIZE - (tail - head)); - if (len < STL_RXBUFHIGH) - hwflow = 1; - } - } - } - -/* - * We have worked out what to do, if anything. So now apply it to the - * UART port. - */ - if (hwflow >= 0) { - x = spltty(); - BRDENABLE(portp->brdnr, portp->pagenr); - stl_setreg(portp, CAR, (portp->portnr & 0x03)); - if (hwflow == 0) { - portp->state |= ASY_RTSFLOW; - stl_setreg(portp, MCOR1, - (stl_getreg(portp, MCOR1) & 0xf0)); - stl_setreg(portp, MSVR2, 0); - portp->stats.rxrtsoff++; - } else if (hwflow > 0) { - portp->state &= ~ASY_RTSFLOW; - stl_setreg(portp, MSVR2, MSVR2_RTS); - stl_setreg(portp, MCOR1, - (stl_getreg(portp, MCOR1) | FIFO_RTSTHRESHOLD)); - portp->stats.rxrtson++; - } - BRDDISABLE(portp->brdnr); - splx(x); - } -} - - -/*****************************************************************************/ - -/* - * Set the state of the DTR and RTS signals. - */ - -static void stl_setsignals(stlport_t *portp, int dtr, int rts) -{ - unsigned char msvr1, msvr2; - int x; - -#if DEBUG - printf("stl_setsignals(portp=%x,dtr=%d,rts=%d)\n", (int) portp, - dtr, rts); -#endif - - msvr1 = 0; - msvr2 = 0; - if (dtr > 0) - msvr1 = MSVR1_DTR; - if (rts > 0) - msvr2 = MSVR2_RTS; - - x = spltty(); - BRDENABLE(portp->brdnr, portp->pagenr); - stl_setreg(portp, CAR, (portp->portnr & 0x03)); - if (rts >= 0) - stl_setreg(portp, MSVR2, msvr2); - if (dtr >= 0) - stl_setreg(portp, MSVR1, msvr1); - BRDDISABLE(portp->brdnr); - splx(x); -} - -/*****************************************************************************/ - -/* - * Get the state of the signals. - */ - -static int stl_getsignals(stlport_t *portp) -{ - unsigned char msvr1, msvr2; - int sigs, x; - -#if DEBUG - printf("stl_getsignals(portp=%x)\n", (int) portp); -#endif - - x = spltty(); - BRDENABLE(portp->brdnr, portp->pagenr); - stl_setreg(portp, CAR, (portp->portnr & 0x3)); - msvr1 = stl_getreg(portp, MSVR1); - msvr2 = stl_getreg(portp, MSVR2); - BRDDISABLE(portp->brdnr); - splx(x); - - sigs = 0; - sigs |= (msvr1 & MSVR1_DCD) ? TIOCM_CD : 0; - sigs |= (msvr1 & MSVR1_CTS) ? TIOCM_CTS : 0; - sigs |= (msvr1 & MSVR1_RI) ? TIOCM_RI : 0; - sigs |= (msvr1 & MSVR1_DSR) ? TIOCM_DSR : 0; - sigs |= (msvr1 & MSVR1_DTR) ? TIOCM_DTR : 0; - sigs |= (msvr2 & MSVR2_RTS) ? TIOCM_RTS : 0; - return(sigs); -} - -/*****************************************************************************/ - -/* - * Enable or disable the Transmitter and/or Receiver. - */ - -static void stl_enablerxtx(stlport_t *portp, int rx, int tx) -{ - unsigned char ccr; - int x; - -#if DEBUG - printf("stl_enablerxtx(portp=%x,rx=%d,tx=%d)\n", (int) portp, rx, tx); -#endif - - ccr = 0; - if (tx == 0) - ccr |= CCR_TXDISABLE; - else if (tx > 0) - ccr |= CCR_TXENABLE; - if (rx == 0) - ccr |= CCR_RXDISABLE; - else if (rx > 0) - ccr |= CCR_RXENABLE; - - x = spltty(); - BRDENABLE(portp->brdnr, portp->pagenr); - stl_setreg(portp, CAR, (portp->portnr & 0x03)); - stl_ccrwait(portp); - stl_setreg(portp, CCR, ccr); - stl_ccrwait(portp); - BRDDISABLE(portp->brdnr); - splx(x); -} - -/*****************************************************************************/ - -/* - * Start or stop the Transmitter and/or Receiver. - */ - -static void stl_startrxtx(stlport_t *portp, int rx, int tx) -{ - unsigned char sreron, sreroff; - int x; - -#if DEBUG - printf("stl_startrxtx(portp=%x,rx=%d,tx=%d)\n", (int) portp, rx, tx); -#endif - - sreron = 0; - sreroff = 0; - if (tx == 0) - sreroff |= (SRER_TXDATA | SRER_TXEMPTY); - else if (tx == 1) - sreron |= SRER_TXDATA; - else if (tx >= 2) - sreron |= SRER_TXEMPTY; - if (rx == 0) - sreroff |= SRER_RXDATA; - else if (rx > 0) - sreron |= SRER_RXDATA; - - x = spltty(); - BRDENABLE(portp->brdnr, portp->pagenr); - stl_setreg(portp, CAR, (portp->portnr & 0x3)); - stl_setreg(portp, SRER, - ((stl_getreg(portp, SRER) & ~sreroff) | sreron)); - BRDDISABLE(portp->brdnr); - if (tx > 0) - portp->tty.t_state |= TS_BUSY; - splx(x); -} - -/*****************************************************************************/ - -/* - * Disable all interrupts from this port. - */ - -static void stl_disableintrs(stlport_t *portp) -{ - int x; - -#if DEBUG - printf("stl_disableintrs(portp=%x)\n", (int) portp); -#endif - - x = spltty(); - BRDENABLE(portp->brdnr, portp->pagenr); - stl_setreg(portp, CAR, (portp->portnr & 0x3)); - stl_setreg(portp, SRER, 0); - BRDDISABLE(portp->brdnr); - splx(x); -} - -/*****************************************************************************/ - -static void stl_sendbreak(stlport_t *portp, long len) -{ - int x; - -#if DEBUG - printf("stl_sendbreak(portp=%x,len=%d)\n", (int) portp, (int) len); -#endif - - x = spltty(); - BRDENABLE(portp->brdnr, portp->pagenr); - stl_setreg(portp, CAR, (portp->portnr & 0x3)); - stl_setreg(portp, COR2, (stl_getreg(portp, COR2) | COR2_ETC)); - stl_setreg(portp, SRER, - ((stl_getreg(portp, SRER) & ~SRER_TXDATA) | SRER_TXEMPTY)); - BRDDISABLE(portp->brdnr); - if (len > 0) { - len = len / 5; - portp->brklen = (len > 255) ? 255 : len; - } else { - portp->brklen = len; - } - splx(x); - portp->stats.txbreaks++; -} - -/*****************************************************************************/ - -/* - * Enable l_rint processing bypass mode if tty modes allow it. - */ - -static void stl_ttyoptim(stlport_t *portp, struct termios *tiosp) -{ - struct tty *tp; - - tp = &portp->tty; - if (((tiosp->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR)) == 0) && - (((tiosp->c_iflag & BRKINT) == 0) || (tiosp->c_iflag & IGNBRK)) && - (((tiosp->c_iflag & PARMRK) == 0) || - ((tiosp->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK))) && - ((tiosp->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN)) ==0) && - (linesw[tp->t_line].l_rint == ttyinput)) - tp->t_state |= TS_CAN_BYPASS_L_RINT; - else - tp->t_state &= ~TS_CAN_BYPASS_L_RINT; - portp->hotchar = linesw[tp->t_line].l_hotchar; -} - -/*****************************************************************************/ - -/* - * Try and find and initialize all the ports on a panel. We don't care - * what sort of board these ports are on - since the port io registers - * are almost identical when dealing with ports. - */ - -static int stl_initports(stlbrd_t *brdp, stlpanel_t *panelp) -{ - stlport_t *portp; - unsigned int chipmask; - unsigned int gfrcr; - int nrchips, uartaddr, ioaddr; - int i, j; - -#if DEBUG - printf("stl_initports(panelp=%x)\n", (int) panelp); -#endif - - BRDENABLE(panelp->brdnr, panelp->pagenr); - -/* - * Check that each chip is present and started up OK. - */ - chipmask = 0; - nrchips = panelp->nrports / CD1400_PORTS; - for (i = 0; (i < nrchips); i++) { - if (brdp->brdtype == BRD_ECHPCI) { - outb(brdp->ioctrl, (panelp->pagenr + (i >> 1))); - ioaddr = panelp->iobase; - } else { - ioaddr = panelp->iobase + (EREG_BANKSIZE * (i >> 1)); - } - uartaddr = (i & 0x01) ? 0x080 : 0; - outb(ioaddr, (GFRCR + uartaddr)); - outb((ioaddr + EREG_DATA), 0); - outb(ioaddr, (CCR + uartaddr)); - outb((ioaddr + EREG_DATA), CCR_RESETFULL); - outb((ioaddr + EREG_DATA), CCR_RESETFULL); - outb(ioaddr, (GFRCR + uartaddr)); - for (j = 0; (j < CCR_MAXWAIT); j++) { - gfrcr = inb(ioaddr + EREG_DATA); - if ((gfrcr > 0x40) && (gfrcr < 0x60)) - break; - } - if (j >= CCR_MAXWAIT) { - printf("STALLION: cd1400 not responding, brd=%d " - "panel=%d chip=%d\n", panelp->brdnr, - panelp->panelnr, i); - continue; - } - chipmask |= (0x1 << i); - outb(ioaddr, (PPR + uartaddr)); - outb((ioaddr + EREG_DATA), PPR_SCALAR); - } - -/* - * All cd1400's are initialized (if found!). Now go through and setup - * each ports data structures. Also init the LIVR register of cd1400 - * for each port. - */ - ioaddr = panelp->iobase; - for (i = 0; (i < panelp->nrports); i++) { - if (brdp->brdtype == BRD_ECHPCI) { - outb(brdp->ioctrl, (panelp->pagenr + (i >> 3))); - ioaddr = panelp->iobase; - } else { - ioaddr = panelp->iobase + (EREG_BANKSIZE * (i >> 3)); - } - if ((chipmask & (0x1 << (i / 4))) == 0) - continue; - portp = (stlport_t *) malloc(sizeof(stlport_t), M_TTYS, - M_NOWAIT | M_ZERO); - if (portp == (stlport_t *) NULL) { - printf("STALLION: failed to allocate port memory " - "(size=%d)\n", sizeof(stlport_t)); - break; - } - - portp->portnr = i; - portp->brdnr = panelp->brdnr; - portp->panelnr = panelp->panelnr; - portp->clk = brdp->clk; - portp->ioaddr = ioaddr; - portp->uartaddr = (i & 0x4) << 5; - portp->pagenr = panelp->pagenr + (i >> 3); - portp->hwid = stl_getreg(portp, GFRCR); - stl_setreg(portp, CAR, (i & 0x3)); - stl_setreg(portp, LIVR, (i << 3)); - panelp->ports[i] = portp; - - j = STL_TXBUFSIZE + (2 * STL_RXBUFSIZE); - portp->tx.buf = (char *) malloc(j, M_TTYS, M_NOWAIT); - if (portp->tx.buf == (char *) NULL) { - printf("STALLION: failed to allocate buffer memory " - "(size=%d)\n", j); - break; - } - portp->tx.endbuf = portp->tx.buf + STL_TXBUFSIZE; - portp->tx.head = portp->tx.buf; - portp->tx.tail = portp->tx.buf; - portp->rx.buf = portp->tx.buf + STL_TXBUFSIZE; - portp->rx.endbuf = portp->rx.buf + STL_RXBUFSIZE; - portp->rx.head = portp->rx.buf; - portp->rx.tail = portp->rx.buf; - portp->rxstatus.buf = portp->rx.buf + STL_RXBUFSIZE; - portp->rxstatus.endbuf = portp->rxstatus.buf + STL_RXBUFSIZE; - portp->rxstatus.head = portp->rxstatus.buf; - portp->rxstatus.tail = portp->rxstatus.buf; - bzero(portp->rxstatus.head, STL_RXBUFSIZE); - - portp->initintios.c_ispeed = STL_DEFSPEED; - portp->initintios.c_ospeed = STL_DEFSPEED; - portp->initintios.c_cflag = STL_DEFCFLAG; - portp->initintios.c_iflag = 0; - portp->initintios.c_oflag = 0; - portp->initintios.c_lflag = 0; - bcopy(&ttydefchars[0], &portp->initintios.c_cc[0], - sizeof(portp->initintios.c_cc)); - portp->initouttios = portp->initintios; - portp->dtrwait = 3 * hz; - } - - BRDDISABLE(panelp->brdnr); - return(0); -} - -/*****************************************************************************/ - -/* - * Try to find and initialize an EasyIO board. - */ - -static int stl_initeio(stlbrd_t *brdp) -{ - stlpanel_t *panelp; - unsigned int status; - -#if DEBUG - printf("stl_initeio(brdp=%x)\n", (int) brdp); -#endif - - brdp->ioctrl = brdp->ioaddr1 + 1; - brdp->iostatus = brdp->ioaddr1 + 2; - brdp->clk = EIO_CLK; - - status = inb(brdp->iostatus); - switch (status & EIO_IDBITMASK) { - case EIO_8PORTM: - brdp->clk = EIO_CLK8M; - /* FALLTHROUGH */ - case EIO_8PORTRS: - case EIO_8PORTDI: - brdp->nrports = 8; - break; - case EIO_4PORTRS: - brdp->nrports = 4; - break; - default: - return(ENODEV); - } - -/* - * Check that the supplied IRQ is good and then use it to setup the - * programmable interrupt bits on EIO board. Also set the edge/level - * triggered interrupt bit. - */ - if ((brdp->irq < 0) || (brdp->irq > 15) || - (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) { - printf("STALLION: invalid irq=%d for brd=%d\n", brdp->irq, - brdp->brdnr); - return(EINVAL); - } - outb(brdp->ioctrl, (stl_vecmap[brdp->irq] | - ((brdp->irqtype) ? EIO_INTLEVEL : EIO_INTEDGE))); - - panelp = (stlpanel_t *) malloc(sizeof(stlpanel_t), M_TTYS, - M_NOWAIT | M_ZERO); - if (panelp == (stlpanel_t *) NULL) { - printf("STALLION: failed to allocate memory (size=%d)\n", - sizeof(stlpanel_t)); - return(ENOMEM); - } - - panelp->brdnr = brdp->brdnr; - panelp->panelnr = 0; - panelp->nrports = brdp->nrports; - panelp->iobase = brdp->ioaddr1; - panelp->hwid = status; - brdp->panels[0] = panelp; - brdp->nrpanels = 1; - brdp->hwid = status; - brdp->state |= BRD_FOUND; - return(0); -} - -/*****************************************************************************/ - -/* - * Try to find an ECH board and initialize it. This code is capable of - * dealing with all types of ECH board. - */ - -static int stl_initech(stlbrd_t *brdp) -{ - stlpanel_t *panelp; - unsigned int status, nxtid; - int panelnr, ioaddr, i; - -#if DEBUG - printf("stl_initech(brdp=%x)\n", (int) brdp); -#endif - -/* - * Set up the initial board register contents for boards. This varys a - * bit between the different board types. So we need to handle each - * separately. Also do a check that the supplied IRQ is good. - */ - if (brdp->brdtype == BRD_ECH) { - brdp->ioctrl = brdp->ioaddr1 + 1; - brdp->iostatus = brdp->ioaddr1 + 1; - status = inb(brdp->iostatus); - if ((status & ECH_IDBITMASK) != ECH_ID) - return(ENODEV); - brdp->hwid = status; - - if ((brdp->irq < 0) || (brdp->irq > 15) || - (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) { - printf("STALLION: invalid irq=%d for brd=%d\n", - brdp->irq, brdp->brdnr); - return(EINVAL); - } - status = ((brdp->ioaddr2 & ECH_ADDR2MASK) >> 1); - status |= (stl_vecmap[brdp->irq] << 1); - outb(brdp->ioaddr1, (status | ECH_BRDRESET)); - brdp->ioctrlval = ECH_INTENABLE | - ((brdp->irqtype) ? ECH_INTLEVEL : ECH_INTEDGE); - outb(brdp->ioctrl, (brdp->ioctrlval | ECH_BRDENABLE)); - outb(brdp->ioaddr1, status); - } else if (brdp->brdtype == BRD_ECHMC) { - brdp->ioctrl = brdp->ioaddr1 + 0x20; - brdp->iostatus = brdp->ioctrl; - status = inb(brdp->iostatus); - if ((status & ECH_IDBITMASK) != ECH_ID) - return(ENODEV); - brdp->hwid = status; - - if ((brdp->irq < 0) || (brdp->irq > 15) || - (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) { - printf("STALLION: invalid irq=%d for brd=%d\n", - brdp->irq, brdp->brdnr); - return(EINVAL); - } - outb(brdp->ioctrl, ECHMC_BRDRESET); - outb(brdp->ioctrl, ECHMC_INTENABLE); - } else if (brdp->brdtype == BRD_ECHPCI) { - brdp->ioctrl = brdp->ioaddr1 + 2; - } - - brdp->clk = ECH_CLK; - -/* - * Scan through the secondary io address space looking for panels. - * As we find'em allocate and initialize panel structures for each. - */ - ioaddr = brdp->ioaddr2; - panelnr = 0; - nxtid = 0; - - for (i = 0; (i < STL_MAXPANELS); i++) { - if (brdp->brdtype == BRD_ECHPCI) { - outb(brdp->ioctrl, nxtid); - ioaddr = brdp->ioaddr2; - } - status = inb(ioaddr + ECH_PNLSTATUS); - if ((status & ECH_PNLIDMASK) != nxtid) - break; - panelp = (stlpanel_t *) malloc(sizeof(stlpanel_t), M_TTYS, - M_NOWAIT | M_ZERO); - if (panelp == (stlpanel_t *) NULL) { - printf("STALLION: failed to allocate memory" - "(size=%d)\n", sizeof(stlpanel_t)); - break; - } - panelp->brdnr = brdp->brdnr; - panelp->panelnr = panelnr; - panelp->iobase = ioaddr; - panelp->pagenr = nxtid; - panelp->hwid = status; - if (status & ECH_PNL16PORT) { - if ((brdp->nrports + 16) > 32) - break; - panelp->nrports = 16; - panelp->ackmask = 0x80; - brdp->nrports += 16; - ioaddr += (EREG_BANKSIZE * 2); - nxtid += 2; - } else { - panelp->nrports = 8; - panelp->ackmask = 0xc0; - brdp->nrports += 8; - ioaddr += EREG_BANKSIZE; - nxtid++; - } - brdp->panels[panelnr++] = panelp; - brdp->nrpanels++; - if (ioaddr >= (brdp->ioaddr2 + 0x20)) - break; - } - - if (brdp->brdtype == BRD_ECH) - outb(brdp->ioctrl, (brdp->ioctrlval | ECH_BRDDISABLE)); - - brdp->state |= BRD_FOUND; - return(0); -} - -/*****************************************************************************/ - -/* - * Initialize and configure the specified board. This firstly probes - * for the board, if it is found then the board is initialized and - * then all its ports are initialized as well. - */ - -static int stl_brdinit(stlbrd_t *brdp) -{ - stlpanel_t *panelp; - int i, j, k; - -#if DEBUG - printf("stl_brdinit(brdp=%x): unit=%d type=%d io1=%x io2=%x irq=%d\n", - (int) brdp, brdp->brdnr, brdp->brdtype, brdp->ioaddr1, - brdp->ioaddr2, brdp->irq); -#endif - - switch (brdp->brdtype) { - case BRD_EASYIO: - stl_initeio(brdp); - break; - case BRD_ECH: - case BRD_ECHMC: - case BRD_ECHPCI: - stl_initech(brdp); - break; - default: - printf("STALLION: unit=%d is unknown board type=%d\n", - brdp->brdnr, brdp->brdtype); - return(ENODEV); - } - - stl_brds[brdp->brdnr] = brdp; - if ((brdp->state & BRD_FOUND) == 0) { -#if 0 - printf("STALLION: %s board not found, unit=%d io=%x irq=%d\n", - stl_brdnames[brdp->brdtype], brdp->brdnr, - brdp->ioaddr1, brdp->irq); -#endif - return(ENODEV); - } - - for (i = 0, k = 0; (i < STL_MAXPANELS); i++) { - panelp = brdp->panels[i]; - if (panelp != (stlpanel_t *) NULL) { - stl_initports(brdp, panelp); - for (j = 0; (j < panelp->nrports); j++) - brdp->ports[k++] = panelp->ports[j]; - } - } - - printf("stl%d: %s (driver version %s) unit=%d nrpanels=%d nrports=%d\n", - brdp->unitid, stl_brdnames[brdp->brdtype], stl_drvversion, - brdp->brdnr, brdp->nrpanels, brdp->nrports); - return(0); -} - -/*****************************************************************************/ - -/* - * Return the board stats structure to user app. - */ - -static int stl_getbrdstats(caddr_t data) -{ - stlbrd_t *brdp; - stlpanel_t *panelp; - int i; - - stl_brdstats = *((combrd_t *) data); - if (stl_brdstats.brd >= STL_MAXBRDS) - return(-ENODEV); - brdp = stl_brds[stl_brdstats.brd]; - if (brdp == (stlbrd_t *) NULL) - return(-ENODEV); - - bzero(&stl_brdstats, sizeof(combrd_t)); - stl_brdstats.brd = brdp->brdnr; - stl_brdstats.type = brdp->brdtype; - stl_brdstats.hwid = brdp->hwid; - stl_brdstats.state = brdp->state; - stl_brdstats.ioaddr = brdp->ioaddr1; - stl_brdstats.ioaddr2 = brdp->ioaddr2; - stl_brdstats.irq = brdp->irq; - stl_brdstats.nrpanels = brdp->nrpanels; - stl_brdstats.nrports = brdp->nrports; - for (i = 0; (i < brdp->nrpanels); i++) { - panelp = brdp->panels[i]; - stl_brdstats.panels[i].panel = i; - stl_brdstats.panels[i].hwid = panelp->hwid; - stl_brdstats.panels[i].nrports = panelp->nrports; - } - - *((combrd_t *) data) = stl_brdstats;; - return(0); -} - -/*****************************************************************************/ - -/* - * Resolve the referenced port number into a port struct pointer. - */ - -static stlport_t *stl_getport(int brdnr, int panelnr, int portnr) -{ - stlbrd_t *brdp; - stlpanel_t *panelp; - - if ((brdnr < 0) || (brdnr >= STL_MAXBRDS)) - return((stlport_t *) NULL); - brdp = stl_brds[brdnr]; - if (brdp == (stlbrd_t *) NULL) - return((stlport_t *) NULL); - if ((panelnr < 0) || (panelnr >= brdp->nrpanels)) - return((stlport_t *) NULL); - panelp = brdp->panels[panelnr]; - if (panelp == (stlpanel_t *) NULL) - return((stlport_t *) NULL); - if ((portnr < 0) || (portnr >= panelp->nrports)) - return((stlport_t *) NULL); - return(panelp->ports[portnr]); -} - -/*****************************************************************************/ - -/* - * Return the port stats structure to user app. A NULL port struct - * pointer passed in means that we need to find out from the app - * what port to get stats for (used through board control device). - */ - -static int stl_getportstats(stlport_t *portp, caddr_t data) -{ - unsigned char *head, *tail; - - if (portp == (stlport_t *) NULL) { - stl_comstats = *((comstats_t *) data); - portp = stl_getport(stl_comstats.brd, stl_comstats.panel, - stl_comstats.port); - if (portp == (stlport_t *) NULL) - return(-ENODEV); - } - - portp->stats.state = portp->state; - /*portp->stats.flags = portp->flags;*/ - portp->stats.hwid = portp->hwid; - portp->stats.ttystate = portp->tty.t_state; - portp->stats.cflags = portp->tty.t_cflag; - portp->stats.iflags = portp->tty.t_iflag; - portp->stats.oflags = portp->tty.t_oflag; - portp->stats.lflags = portp->tty.t_lflag; - - head = portp->tx.head; - tail = portp->tx.tail; - portp->stats.txbuffered = ((head >= tail) ? (head - tail) : - (STL_TXBUFSIZE - (tail - head))); - - head = portp->rx.head; - tail = portp->rx.tail; - portp->stats.rxbuffered = (head >= tail) ? (head - tail) : - (STL_RXBUFSIZE - (tail - head)); - - portp->stats.signals = (unsigned long) stl_getsignals(portp); - - *((comstats_t *) data) = portp->stats; - return(0); -} - -/*****************************************************************************/ - -/* - * Clear the port stats structure. We also return it zeroed out... - */ - -static int stl_clrportstats(stlport_t *portp, caddr_t data) -{ - if (portp == (stlport_t *) NULL) { - stl_comstats = *((comstats_t *) data); - portp = stl_getport(stl_comstats.brd, stl_comstats.panel, - stl_comstats.port); - if (portp == (stlport_t *) NULL) - return(-ENODEV); - } - - bzero(&portp->stats, sizeof(comstats_t)); - portp->stats.brd = portp->brdnr; - portp->stats.panel = portp->panelnr; - portp->stats.port = portp->portnr; - *((comstats_t *) data) = stl_comstats; - return(0); -} - -/*****************************************************************************/ - -/* - * The "staliomem" device is used for stats collection in this driver. - */ - -static int stl_memioctl(dev_t dev, unsigned long cmd, caddr_t data, int flag, - struct thread *td) -{ - stlbrd_t *brdp; - int brdnr, rc; - -#if DEBUG - printf("stl_memioctl(dev=%s,cmd=%lx,data=%p,flag=%x)\n", - devtoname(dev), cmd, (void *) data, flag); -#endif - - brdnr = minor(dev) & 0x7; - brdp = stl_brds[brdnr]; - if (brdp == (stlbrd_t *) NULL) - return(ENODEV); - if (brdp->state == 0) - return(ENODEV); - - rc = 0; - - switch (cmd) { - case COM_GETPORTSTATS: - rc = stl_getportstats((stlport_t *) NULL, data); - break; - case COM_CLRPORTSTATS: - rc = stl_clrportstats((stlport_t *) NULL, data); - break; - case COM_GETBRDSTATS: - rc = stl_getbrdstats(data); - break; - default: - rc = ENOTTY; - break; - } - - return(rc); -} - -/*****************************************************************************/ |