si.c revision 131981
110015Speter/* 212496Speter * Device driver for Specialix range (SI/XIO) of serial line multiplexors. 310015Speter * 434832Speter * Copyright (C) 1990, 1992, 1998 Specialix International, 510015Speter * Copyright (C) 1993, Andy Rutter <andy@acronym.co.uk> 656505Speter * Copyright (C) 2000, Peter Wemm <peter@netplex.com.au> 710015Speter * 810015Speter * Originally derived from: SunOS 4.x version 910015Speter * Ported from BSDI version to FreeBSD by Peter Wemm. 1010015Speter * 1110015Speter * Redistribution and use in source and binary forms, with or without 1210015Speter * modification, are permitted provided that the following conditions 1310015Speter * are met: 1410015Speter * 1. Redistributions of source code must retain the above copyright 1510015Speter * notices, this list of conditions and the following disclaimer. 1610015Speter * 2. Redistributions in binary form must reproduce the above copyright 1710015Speter * notices, this list of conditions and the following disclaimer in the 1810015Speter * documentation and/or other materials provided with the distribution. 1910015Speter * 3. All advertising materials mentioning features or use of this software 2010015Speter * must display the following acknowledgement: 2110015Speter * This product includes software developed by Andy Rutter of 2210015Speter * Advanced Methods and Tools Ltd. based on original information 2310015Speter * from Specialix International. 2410015Speter * 4. Neither the name of Advanced Methods and Tools, nor Specialix 2510015Speter * International may be used to endorse or promote products derived from 2610015Speter * this software without specific prior written permission. 2710015Speter * 2810015Speter * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY EXPRESS OR IMPLIED 2910015Speter * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 3010015Speter * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN 3110015Speter * NO EVENT SHALL THE AUTHORS BE LIABLE. 3210015Speter * 3310015Speter */ 3410015Speter 35119419Sobrien#include <sys/cdefs.h> 36119419Sobrien__FBSDID("$FreeBSD: head/sys/dev/si/si.c 131981 2004-07-11 15:18:39Z phk $"); 37119419Sobrien 3810015Speter#ifndef lint 3934832Speterstatic const char si_copyright1[] = "@(#) Copyright (C) Specialix International, 1990,1992,1998", 4034832Speter si_copyright2[] = "@(#) Copyright (C) Andy Rutter 1993", 4156505Speter si_copyright3[] = "@(#) Copyright (C) Peter Wemm 2000"; 4210015Speter#endif /* not lint */ 4310015Speter 4431778Seivind#include "opt_compat.h" 4532929Seivind#include "opt_debug_si.h" 46111899Sdas#include "opt_tty.h" 4731778Seivind 4810015Speter#include <sys/param.h> 4910015Speter#include <sys/systm.h> 50130892Sphk#ifndef BURN_BRIDGES 51130344Sphk#if defined(COMPAT_43) 5224207Sbde#include <sys/ioctl_compat.h> 5324207Sbde#endif 54130892Sphk#endif 5510015Speter#include <sys/tty.h> 5610015Speter#include <sys/conf.h> 5724131Sbde#include <sys/fcntl.h> 5810015Speter#include <sys/kernel.h> 5910015Speter#include <sys/malloc.h> 6015683Speter#include <sys/sysctl.h> 6156498Speter#include <sys/bus.h> 6256498Speter#include <machine/bus.h> 6356498Speter#include <sys/rman.h> 6456498Speter#include <machine/resource.h> 6510015Speter 6610015Speter 6712659Sbde#include <vm/vm.h> 6812662Sdg#include <vm/pmap.h> 6912659Sbde 7013353Speter#include <machine/stdarg.h> 7110015Speter 7256498Speter#include <dev/si/sireg.h> 7356505Speter#include <dev/si/sivar.h> 7456498Speter#include <dev/si/si.h> 7556498Speter 7610015Speter/* 7710015Speter * This device driver is designed to interface the Specialix International 7834832Speter * SI, XIO and SX range of serial multiplexor cards to FreeBSD on an ISA, 7934832Speter * EISA or PCI bus machine. 8010015Speter * 8134832Speter * The controller is interfaced to the host via dual port RAM 8234832Speter * and an interrupt. 8333395Speter * 8434832Speter * The code for the Host 1 (very old ISA cards) has not been tested. 8510015Speter */ 8610015Speter 8717547Speter#define POLL /* turn on poller to scan for lost interrupts */ 8817547Speter#define REALPOLL /* on each poll, scan for work regardless */ 8917547Speter#define POLLHZ (hz/10) /* 10 times per second */ 9012496Speter#define SI_I_HIGH_WATER (TTYHOG - 2 * SI_BUFFERSIZE) 9134832Speter#define INT_COUNT 25000 /* max of 125 ints per second */ 9234832Speter#define JET_INT_COUNT 100 /* max of 100 ints per second */ 9315639Speter#define RXINT_COUNT 1 /* one rxint per 10 milliseconds */ 9410015Speter 9510015Speterenum si_mctl { GET, SET, BIS, BIC }; 9610015Speter 9756498Speterstatic void si_command(struct si_port *, int, int); 9856498Speterstatic int si_modem(struct si_port *, enum si_mctl, int); 9956498Speterstatic void si_write_enable(struct si_port *, int); 100130585Sphkstatic int si_Sioctl(struct cdev *, u_long, caddr_t, int, struct thread *); 10156498Speterstatic void si_start(struct tty *); 10256498Speterstatic void si_stop(struct tty *, int); 10325047Sbdestatic timeout_t si_lstart; 10456498Speterstatic void sihardclose(struct si_port *pp); 10510015Speter 10656505Speter#ifdef SI_DEBUG 10756505Speterstatic char *si_mctl2str(enum si_mctl cmd); 10856505Speter#endif 10956505Speter 11056498Speterstatic int siparam(struct tty *, struct termios *); 11110015Speter 11256498Speterstatic void si_modem_state(struct si_port *pp, struct tty *tp, int hi_ip); 11356498Speterstatic char * si_modulename(int host_type, int uart_type); 11410708Speter 11512675Sjulianstatic d_open_t siopen; 11612675Sjulianstatic d_close_t siclose; 11712675Sjulianstatic d_write_t siwrite; 11812675Sjulianstatic d_ioctl_t siioctl; 11912675Sjulian 12047625Sphkstatic struct cdevsw si_cdevsw = { 121126080Sphk .d_version = D_VERSION, 122111815Sphk .d_open = siopen, 123111815Sphk .d_close = siclose, 124111815Sphk .d_write = siwrite, 125111815Sphk .d_ioctl = siioctl, 126111815Sphk .d_name = "si", 127126080Sphk .d_flags = D_TTY | D_NEEDGIANT, 12838485Sbde}; 12912675Sjulian 13010962Speterstatic int si_Nports; 13110962Speterstatic int si_Nmodules; 13210962Speterstatic int si_debug = 0; /* data, not bss, so it's patchable */ 13310015Speter 13434832SpeterSYSCTL_INT(_machdep, OID_AUTO, si_debug, CTLFLAG_RW, &si_debug, 0, ""); 135100743SpeterTUNABLE_INT("machdep.si_debug", &si_debug); 13634832Speter 13756498Speterstatic int si_numunits; 13810044Speter 13956505Speterdevclass_t si_devclass; 14056498Speter 14112174Speter#ifndef B2000 /* not standard, but the hardware knows it. */ 14210015Speter# define B2000 2000 14310015Speter#endif 14410015Speterstatic struct speedtab bdrates[] = { 14550442Speter { B75, CLK75, }, /* 0x0 */ 14650442Speter { B110, CLK110, }, /* 0x1 */ 14750442Speter { B150, CLK150, }, /* 0x3 */ 14850442Speter { B300, CLK300, }, /* 0x4 */ 14950442Speter { B600, CLK600, }, /* 0x5 */ 15050442Speter { B1200, CLK1200, }, /* 0x6 */ 15150442Speter { B2000, CLK2000, }, /* 0x7 */ 15250442Speter { B2400, CLK2400, }, /* 0x8 */ 15350442Speter { B4800, CLK4800, }, /* 0x9 */ 15450442Speter { B9600, CLK9600, }, /* 0xb */ 15550442Speter { B19200, CLK19200, }, /* 0xc */ 15650442Speter { B38400, CLK38400, }, /* 0x2 (out of order!) */ 15750442Speter { B57600, CLK57600, }, /* 0xd */ 15850442Speter { B115200, CLK110, }, /* 0x1 (dupe!, 110 baud on "si") */ 15950442Speter { -1, -1 }, 16010015Speter}; 16110015Speter 16210015Speter 16310015Speter/* populated with approx character/sec rates - translated at card 16410015Speter * initialisation time to chars per tick of the clock */ 16510015Speterstatic int done_chartimes = 0; 16610015Speterstatic struct speedtab chartimes[] = { 16750442Speter { B75, 8, }, 16850442Speter { B110, 11, }, 16950442Speter { B150, 15, }, 17050442Speter { B300, 30, }, 17150442Speter { B600, 60, }, 17250442Speter { B1200, 120, }, 17350442Speter { B2000, 200, }, 17450442Speter { B2400, 240, }, 17550442Speter { B4800, 480, }, 17650442Speter { B9600, 960, }, 17750442Speter { B19200, 1920, }, 17850442Speter { B38400, 3840, }, 17950442Speter { B57600, 5760, }, 18050442Speter { B115200, 11520, }, 18150442Speter { -1, -1 }, 18210015Speter}; 18310015Speterstatic volatile int in_intr = 0; /* Inside interrupt handler? */ 18410015Speter 18510015Speter#ifdef POLL 18615683Speterstatic int si_pollrate; /* in addition to irq */ 18756498Speterstatic int si_realpoll = 0; /* poll HW on timer */ 18815639Speter 18916403SpeterSYSCTL_INT(_machdep, OID_AUTO, si_pollrate, CTLFLAG_RW, &si_pollrate, 0, ""); 19017547SpeterSYSCTL_INT(_machdep, OID_AUTO, si_realpoll, CTLFLAG_RW, &si_realpoll, 0, ""); 19150442Speter 19210015Speterstatic int init_finished = 0; 19356498Speterstatic void si_poll(void *); 19410015Speter#endif 19510015Speter 19610015Speter/* 19710015Speter * Array of adapter types and the corresponding RAM size. The order of 19810015Speter * entries here MUST match the ordinal of the adapter type. 19910015Speter */ 20010015Speterstatic char *si_type[] = { 20110015Speter "EMPTY", 20210015Speter "SIHOST", 20334832Speter "SIMCA", /* FreeBSD does not support Microchannel */ 20410015Speter "SIHOST2", 20510015Speter "SIEISA", 20633395Speter "SIPCI", 20733395Speter "SXPCI", 20833395Speter "SXISA", 20910015Speter}; 21010015Speter 21156498Speter/* 21256498Speter * We have to make an 8 bit version of bcopy, since some cards can't 21356498Speter * deal with 32 bit I/O 21456498Speter */ 21556498Speterstatic void __inline 21656498Spetersi_bcopy(const void *src, void *dst, size_t len) 21756498Speter{ 21856498Speter while (len--) 21956498Speter *(((u_char *)dst)++) = *(((const u_char *)src)++); 22056498Speter} 22156498Speterstatic void __inline 22256498Spetersi_vbcopy(const volatile void *src, void *dst, size_t len) 22356498Speter{ 22456498Speter while (len--) 22556498Speter *(((u_char *)dst)++) = *(((const volatile u_char *)src)++); 22656498Speter} 22756498Speterstatic void __inline 22856498Spetersi_bcopyv(const void *src, volatile void *dst, size_t len) 22956498Speter{ 23056498Speter while (len--) 23156498Speter *(((volatile u_char *)dst)++) = *(((const u_char *)src)++); 23256498Speter} 23356498Speter 23456498Speter 23534832Speter/* 23610015Speter * Attach the device. Initialize the card. 23710015Speter */ 23856505Speterint 23956498Spetersiattach(device_t dev) 24010015Speter{ 24156498Speter int unit; 24256498Speter struct si_softc *sc; 24310015Speter struct si_port *pp; 24410015Speter volatile struct si_channel *ccbp; 24510015Speter volatile struct si_reg *regp; 24610015Speter volatile caddr_t maddr; 24710015Speter struct si_module *modp; 24810015Speter struct speedtab *spt; 24910015Speter int nmodule, nport, x, y; 25012174Speter int uart_type; 25110015Speter 25256498Speter sc = device_get_softc(dev); 25356498Speter unit = device_get_unit(dev); 25410015Speter 25556505Speter sc->sc_typename = si_type[sc->sc_type]; 25656505Speter if (si_numunits < unit + 1) 25756505Speter si_numunits = unit + 1; 25856505Speter 25956498Speter DPRINT((0, DBG_AUTOBOOT, "si%d: siattach\n", unit)); 26010015Speter 26156498Speter#ifdef POLL 26256498Speter if (si_pollrate == 0) { 26356498Speter si_pollrate = POLLHZ; /* in addition to irq */ 26456498Speter#ifdef REALPOLL 26556498Speter si_realpoll = 1; /* scan always */ 26656498Speter#endif 26756498Speter } 26856498Speter#endif 26956498Speter 27033395Speter DPRINT((0, DBG_AUTOBOOT, "si%d: type: %s paddr: %x maddr: %x\n", unit, 27133395Speter sc->sc_typename, sc->sc_paddr, sc->sc_maddr)); 27233395Speter 27310015Speter sc->sc_ports = NULL; /* mark as uninitialised */ 27410015Speter 27510015Speter maddr = sc->sc_maddr; 27610015Speter 27734832Speter /* Stop the CPU first so it won't stomp around while we load */ 27834832Speter 27934832Speter switch (sc->sc_type) { 28034832Speter case SIEISA: 28156498Speter outb(sc->sc_iobase + 2, sc->sc_irq << 4); 28234832Speter break; 28334832Speter case SIPCI: 28434832Speter *(maddr+SIPCIRESET) = 0; 28534832Speter break; 28634832Speter case SIJETPCI: /* fall through to JET ISA */ 28734832Speter case SIJETISA: 28834832Speter *(maddr+SIJETCONFIG) = 0; 28934832Speter break; 29034832Speter case SIHOST2: 29134832Speter *(maddr+SIPLRESET) = 0; 29234832Speter break; 29334832Speter case SIHOST: 29434832Speter *(maddr+SIRESET) = 0; 29534832Speter break; 29634832Speter default: /* this should never happen */ 29734832Speter printf("si%d: unsupported configuration\n", unit); 29856498Speter return EINVAL; 29934832Speter break; 30034832Speter } 30134832Speter 30234832Speter /* OK, now lets download the download code */ 30334832Speter 30436956Ssteve if (SI_ISJET(sc->sc_type)) { 30533395Speter DPRINT((0, DBG_DOWNLOAD, "si%d: jet_download: nbytes %d\n", 30656498Speter unit, si3_t225_dsize)); 30734832Speter si_bcopy(si3_t225_download, maddr + si3_t225_downloadaddr, 30834832Speter si3_t225_dsize); 30934832Speter DPRINT((0, DBG_DOWNLOAD, 31034832Speter "si%d: jet_bootstrap: nbytes %d -> %x\n", 31156498Speter unit, si3_t225_bsize, si3_t225_bootloadaddr)); 31234832Speter si_bcopy(si3_t225_bootstrap, maddr + si3_t225_bootloadaddr, 31334832Speter si3_t225_bsize); 31434832Speter } else { 31533395Speter DPRINT((0, DBG_DOWNLOAD, "si%d: si_download: nbytes %d\n", 31656498Speter unit, si2_z280_dsize)); 31734832Speter si_bcopy(si2_z280_download, maddr + si2_z280_downloadaddr, 31834832Speter si2_z280_dsize); 31933395Speter } 32010015Speter 32134832Speter /* Now start the CPU */ 32234832Speter 32310015Speter switch (sc->sc_type) { 32410015Speter case SIEISA: 32534832Speter /* modify the download code to tell it that it's on an EISA */ 32634832Speter *(maddr + 0x42) = 1; 32756498Speter outb(sc->sc_iobase + 2, (sc->sc_irq << 4) | 4); 32856498Speter (void)inb(sc->sc_iobase + 3); /* reset interrupt */ 32910015Speter break; 33033395Speter case SIPCI: 33134832Speter /* modify the download code to tell it that it's on a PCI */ 33233395Speter *(maddr+0x42) = 1; 33333395Speter *(maddr+SIPCIRESET) = 1; 33433395Speter *(maddr+SIPCIINTCL) = 0; 33533395Speter break; 33633395Speter case SIJETPCI: 33733395Speter *(maddr+SIJETRESET) = 0; 33833395Speter *(maddr+SIJETCONFIG) = SIJETBUSEN|SIJETIRQEN; 33933395Speter break; 34033395Speter case SIJETISA: 34133395Speter *(maddr+SIJETRESET) = 0; 34234832Speter switch (sc->sc_irq) { 34356498Speter case 9: 34434832Speter *(maddr+SIJETCONFIG) = SIJETBUSEN|SIJETIRQEN|0x90; 34534832Speter break; 34656498Speter case 10: 34734832Speter *(maddr+SIJETCONFIG) = SIJETBUSEN|SIJETIRQEN|0xa0; 34834832Speter break; 34956498Speter case 11: 35034832Speter *(maddr+SIJETCONFIG) = SIJETBUSEN|SIJETIRQEN|0xb0; 35134832Speter break; 35256498Speter case 12: 35334832Speter *(maddr+SIJETCONFIG) = SIJETBUSEN|SIJETIRQEN|0xc0; 35434832Speter break; 35556498Speter case 15: 35634832Speter *(maddr+SIJETCONFIG) = SIJETBUSEN|SIJETIRQEN|0xf0; 35734832Speter break; 35834832Speter } 35933395Speter break; 36010015Speter case SIHOST: 36110015Speter *(maddr+SIRESET_CL) = 0; 36210015Speter *(maddr+SIINTCL_CL) = 0; 36310015Speter break; 36410015Speter case SIHOST2: 36510015Speter *(maddr+SIPLRESET) = 0x10; 36610015Speter switch (sc->sc_irq) { 36756498Speter case 11: 36810015Speter *(maddr+SIPLIRQ11) = 0x10; 36910015Speter break; 37056498Speter case 12: 37110015Speter *(maddr+SIPLIRQ12) = 0x10; 37210015Speter break; 37356498Speter case 15: 37410015Speter *(maddr+SIPLIRQ15) = 0x10; 37510015Speter break; 37610015Speter } 37710015Speter *(maddr+SIPLIRQCLR) = 0x10; 37810015Speter break; 37934832Speter default: /* this should _REALLY_ never happen */ 38034832Speter printf("si%d: Uh, it was supported a second ago...\n", unit); 38156498Speter return EINVAL; 38210015Speter } 38310015Speter 38410015Speter DELAY(1000000); /* wait around for a second */ 38510015Speter 38610015Speter regp = (struct si_reg *)maddr; 38710015Speter y = 0; 38810015Speter /* wait max of 5 sec for init OK */ 38910015Speter while (regp->initstat == 0 && y++ < 10) { 39010015Speter DELAY(500000); 39110015Speter } 39210015Speter switch (regp->initstat) { 39310015Speter case 0: 39410015Speter printf("si%d: startup timeout - aborting\n", unit); 39512174Speter sc->sc_type = SIEMPTY; 39656498Speter return EINVAL; 39710015Speter case 1: 39836956Ssteve if (SI_ISJET(sc->sc_type)) { 39934832Speter /* set throttle to 100 times per second */ 40034832Speter regp->int_count = JET_INT_COUNT; 40134832Speter /* rx_intr_count is a NOP in Jet */ 40234832Speter } else { 40334832Speter /* set throttle to 125 times per second */ 40434832Speter regp->int_count = INT_COUNT; 40534832Speter /* rx intr max of 25 times per second */ 40634832Speter regp->rx_int_count = RXINT_COUNT; 40734832Speter } 40810015Speter regp->int_pending = 0; /* no intr pending */ 40910015Speter regp->int_scounter = 0; /* reset counter */ 41010015Speter break; 41110015Speter case 0xff: 41210015Speter /* 41310015Speter * No modules found, so give up on this one. 41410015Speter */ 41510015Speter printf("si%d: %s - no ports found\n", unit, 41610015Speter si_type[sc->sc_type]); 41710015Speter return 0; 41810015Speter default: 41934832Speter printf("si%d: download code version error - initstat %x\n", 42010015Speter unit, regp->initstat); 42156498Speter return EINVAL; 42210015Speter } 42310015Speter 42410015Speter /* 42510015Speter * First time around the ports just count them in order 42610015Speter * to allocate some memory. 42710015Speter */ 42810015Speter nport = 0; 42910015Speter modp = (struct si_module *)(maddr + 0x80); 43010015Speter for (;;) { 43112174Speter DPRINT((0, DBG_DOWNLOAD, "si%d: ccb addr 0x%x\n", unit, modp)); 43234832Speter switch (modp->sm_type) { 43334832Speter case TA4: 43410015Speter DPRINT((0, DBG_DOWNLOAD, 43534832Speter "si%d: Found old TA4 module, 4 ports\n", 43634832Speter unit)); 43734832Speter x = 4; 43810015Speter break; 43934832Speter case TA8: 44034832Speter DPRINT((0, DBG_DOWNLOAD, 44134832Speter "si%d: Found old TA8 module, 8 ports\n", 44234832Speter unit)); 44334832Speter x = 8; 44434832Speter break; 44534832Speter case TA4_ASIC: 44634832Speter DPRINT((0, DBG_DOWNLOAD, 44734832Speter "si%d: Found ASIC TA4 module, 4 ports\n", 44834832Speter unit)); 44934832Speter x = 4; 45034832Speter break; 45134832Speter case TA8_ASIC: 45234832Speter DPRINT((0, DBG_DOWNLOAD, 45334832Speter "si%d: Found ASIC TA8 module, 8 ports\n", 45434832Speter unit)); 45534832Speter x = 8; 45634832Speter break; 45734832Speter case MTA: 45834832Speter DPRINT((0, DBG_DOWNLOAD, 45934832Speter "si%d: Found CD1400 module, 8 ports\n", 46034832Speter unit)); 46134832Speter x = 8; 46234832Speter break; 46334832Speter case SXDC: 46434832Speter DPRINT((0, DBG_DOWNLOAD, 46534832Speter "si%d: Found SXDC module, 8 ports\n", 46634832Speter unit)); 46734832Speter x = 8; 46834832Speter break; 46910015Speter default: 47010015Speter printf("si%d: unknown module type %d\n", 47110015Speter unit, modp->sm_type); 47234832Speter goto try_next; 47310015Speter } 47434832Speter 47534832Speter /* this was limited in firmware and is also a driver issue */ 47634832Speter if ((nport + x) > SI_MAXPORTPERCARD) { 47734832Speter printf("si%d: extra ports ignored\n", unit); 47834832Speter goto try_next; 47934832Speter } 48034832Speter 48134832Speter nport += x; 48234832Speter si_Nports += x; 48334832Speter si_Nmodules++; 48434832Speter 48534832Spetertry_next: 48610015Speter if (modp->sm_next == 0) 48710015Speter break; 48810015Speter modp = (struct si_module *) 48910015Speter (maddr + (unsigned)(modp->sm_next & 0x7fff)); 49010015Speter } 49110015Speter sc->sc_ports = (struct si_port *)malloc(sizeof(struct si_port) * nport, 49269781Sdwmalone M_DEVBUF, M_NOWAIT | M_ZERO); 49310015Speter if (sc->sc_ports == 0) { 49410015Speter printf("si%d: fail to malloc memory for port structs\n", 49510015Speter unit); 49656498Speter return EINVAL; 49710015Speter } 49810015Speter sc->sc_nport = nport; 49910015Speter 50010015Speter /* 50110015Speter * Scan round the ports again, this time initialising. 50210015Speter */ 50310015Speter pp = sc->sc_ports; 50410015Speter nmodule = 0; 50510015Speter modp = (struct si_module *)(maddr + 0x80); 50634832Speter uart_type = 1000; /* arbitary, > uchar_max */ 50710015Speter for (;;) { 50834832Speter switch (modp->sm_type) { 50934832Speter case TA4: 51034832Speter nport = 4; 51110015Speter break; 51234832Speter case TA8: 51334832Speter nport = 8; 51434832Speter break; 51534832Speter case TA4_ASIC: 51634832Speter nport = 4; 51734832Speter break; 51834832Speter case TA8_ASIC: 51934832Speter nport = 8; 52034832Speter break; 52134832Speter case MTA: 52234832Speter nport = 8; 52334832Speter break; 52434832Speter case SXDC: 52534832Speter nport = 8; 52634832Speter break; 52710015Speter default: 52834832Speter goto try_next2; 52910015Speter } 53034832Speter nmodule++; 53134832Speter ccbp = (struct si_channel *)((char *)modp + 0x100); 53234832Speter if (uart_type == 1000) 53334832Speter uart_type = ccbp->type; 53434832Speter else if (uart_type != ccbp->type) 53534832Speter printf("si%d: Warning: module %d mismatch! (%d%s != %d%s)\n", 53634832Speter unit, nmodule, 53734832Speter ccbp->type, si_modulename(sc->sc_type, ccbp->type), 53834832Speter uart_type, si_modulename(sc->sc_type, uart_type)); 53934832Speter 54034832Speter for (x = 0; x < nport; x++, pp++, ccbp++) { 54134832Speter pp->sp_ccb = ccbp; /* save the address */ 54272685Speter pp->sp_tty = ttymalloc(NULL); 54334832Speter pp->sp_pend = IDLE_CLOSE; 54434832Speter pp->sp_state = 0; /* internal flag */ 54534832Speter pp->sp_iin.c_iflag = TTYDEF_IFLAG; 54634832Speter pp->sp_iin.c_oflag = TTYDEF_OFLAG; 54734832Speter pp->sp_iin.c_cflag = TTYDEF_CFLAG; 54834832Speter pp->sp_iin.c_lflag = TTYDEF_LFLAG; 54934832Speter termioschars(&pp->sp_iin); 55034832Speter pp->sp_iin.c_ispeed = pp->sp_iin.c_ospeed = 55134832Speter TTYDEF_SPEED;; 55234832Speter pp->sp_iout = pp->sp_iin; 55334832Speter } 55434832Spetertry_next2: 55510015Speter if (modp->sm_next == 0) { 55634832Speter printf("si%d: card: %s, ports: %d, modules: %d, type: %d%s\n", 55710015Speter unit, 55810015Speter sc->sc_typename, 55910015Speter sc->sc_nport, 56012174Speter nmodule, 56134832Speter uart_type, 56234832Speter si_modulename(sc->sc_type, uart_type)); 56310015Speter break; 56410015Speter } 56510015Speter modp = (struct si_module *) 56610015Speter (maddr + (unsigned)(modp->sm_next & 0x7fff)); 56710015Speter } 56810015Speter if (done_chartimes == 0) { 56910015Speter for (spt = chartimes ; spt->sp_speed != -1; spt++) { 57010015Speter if ((spt->sp_code /= hz) == 0) 57110015Speter spt->sp_code = 1; 57210015Speter } 57310015Speter done_chartimes = 1; 57410015Speter } 57512502Sjulian 57612675Sjulian/* path name devsw minor type uid gid perm*/ 57750442Speter for (x = 0; x < sc->sc_nport; x++) { 57834735Speter /* sync with the manuals that start at 1 */ 57956498Speter y = x + 1 + unit * (1 << SI_CARDSHIFT); 58050442Speter make_dev(&si_cdevsw, x, 0, 0, 0600, "ttyA%02d", y); 58150442Speter make_dev(&si_cdevsw, x + 0x00080, 0, 0, 0600, "cuaA%02d", y); 58250442Speter make_dev(&si_cdevsw, x + 0x10000, 0, 0, 0600, "ttyiA%02d", y); 58350442Speter make_dev(&si_cdevsw, x + 0x10080, 0, 0, 0600, "cuaiA%02d", y); 58450442Speter make_dev(&si_cdevsw, x + 0x20000, 0, 0, 0600, "ttylA%02d", y); 58550442Speter make_dev(&si_cdevsw, x + 0x20080, 0, 0, 0600, "cualA%02d", y); 58612675Sjulian } 58750254Sphk make_dev(&si_cdevsw, 0x40000, 0, 0, 0600, "si_control"); 58856498Speter return (0); 58910015Speter} 59010015Speter 59112675Sjulianstatic int 592130585Sphksiopen(struct cdev *dev, int flag, int mode, struct thread *td) 59310015Speter{ 59410015Speter int oldspl, error; 59510015Speter int card, port; 59656498Speter struct si_softc *sc; 59756498Speter struct tty *tp; 59810015Speter volatile struct si_channel *ccbp; 59910015Speter struct si_port *pp; 60010015Speter int mynor = minor(dev); 60110015Speter 60210015Speter /* quickly let in /dev/si_control */ 60310015Speter if (IS_CONTROLDEV(mynor)) { 60493593Sjhb if ((error = suser(td))) 60510015Speter return(error); 60610015Speter return(0); 60710015Speter } 60810015Speter 60910015Speter card = SI_CARD(mynor); 61056498Speter sc = devclass_get_softc(si_devclass, card); 61156498Speter if (sc == NULL) 61210015Speter return (ENXIO); 61310015Speter 61412174Speter if (sc->sc_type == SIEMPTY) { 61512174Speter DPRINT((0, DBG_OPEN|DBG_FAIL, "si%d: type %s??\n", 61610015Speter card, sc->sc_typename)); 61710015Speter return(ENXIO); 61810015Speter } 61910015Speter 62010015Speter port = SI_PORT(mynor); 62110015Speter if (port >= sc->sc_nport) { 62212174Speter DPRINT((0, DBG_OPEN|DBG_FAIL, "si%d: nports %d\n", 62310015Speter card, sc->sc_nport)); 62410015Speter return(ENXIO); 62510015Speter } 62610015Speter 62710015Speter#ifdef POLL 62810015Speter /* 62910015Speter * We've now got a device, so start the poller. 63010015Speter */ 63110015Speter if (init_finished == 0) { 63215639Speter timeout(si_poll, (caddr_t)0L, si_pollrate); 63310015Speter init_finished = 1; 63410015Speter } 63510015Speter#endif 63610015Speter 63710015Speter /* initial/lock device */ 63810015Speter if (IS_STATE(mynor)) { 63910015Speter return(0); 64010015Speter } 64110015Speter 64210015Speter pp = sc->sc_ports + port; 64310015Speter tp = pp->sp_tty; /* the "real" tty */ 64451654Sphk dev->si_tty = tp; 64510015Speter ccbp = pp->sp_ccb; /* Find control block */ 64650016Snsayer DPRINT((pp, DBG_ENTRY|DBG_OPEN, "siopen(%s,%x,%x,%x)\n", 64783366Sjulian devtoname(dev), flag, mode, td)); 64810015Speter 64910015Speter oldspl = spltty(); /* Keep others out */ 65010015Speter error = 0; 65110015Speter 65210015Speteropen_top: 653131981Sphk error = ttydtrwaitsleep(tp); 654131981Sphk if (error != 0) 655131981Sphk goto out; 65610015Speter 65710015Speter if (tp->t_state & TS_ISOPEN) { 65810015Speter /* 65910015Speter * The device is open, so everything has been initialised. 66010015Speter * handle conflicts. 66110015Speter */ 66210015Speter if (IS_CALLOUT(mynor)) { 66310015Speter if (!pp->sp_active_out) { 66410015Speter error = EBUSY; 66510015Speter goto out; 66610015Speter } 66710015Speter } else { 66810015Speter if (pp->sp_active_out) { 66910015Speter if (flag & O_NONBLOCK) { 67010015Speter error = EBUSY; 67110015Speter goto out; 67210015Speter } 67310015Speter error = tsleep(&pp->sp_active_out, 67410015Speter TTIPRI|PCATCH, "sibi", 0); 67510015Speter if (error != 0) 67610015Speter goto out; 67710015Speter goto open_top; 67810015Speter } 67910015Speter } 68043425Sphk if (tp->t_state & TS_XCLUDE && 68193593Sjhb suser(td)) { 68210015Speter DPRINT((pp, DBG_OPEN|DBG_FAIL, 68310015Speter "already open and EXCLUSIVE set\n")); 68410015Speter error = EBUSY; 68510015Speter goto out; 68610015Speter } 68710015Speter } else { 68810015Speter /* 68910015Speter * The device isn't open, so there are no conflicts. 69010015Speter * Initialize it. Avoid sleep... :-) 69110015Speter */ 69210015Speter DPRINT((pp, DBG_OPEN, "first open\n")); 69310015Speter tp->t_oproc = si_start; 69451654Sphk tp->t_stop = si_stop; 69510015Speter tp->t_param = siparam; 69610015Speter tp->t_dev = dev; 69710015Speter tp->t_termios = mynor & SI_CALLOUT_MASK 69810015Speter ? pp->sp_iout : pp->sp_iin; 69910015Speter 70010015Speter (void) si_modem(pp, SET, TIOCM_DTR|TIOCM_RTS); 70110015Speter 70210015Speter ++pp->sp_wopeners; /* in case of sleep in siparam */ 70310015Speter 70410015Speter error = siparam(tp, &tp->t_termios); 70510015Speter 70610015Speter --pp->sp_wopeners; 70710015Speter if (error != 0) 70810015Speter goto out; 70910015Speter /* XXX: we should goto_top if siparam slept */ 71010015Speter 71110015Speter /* set initial DCD state */ 71210015Speter pp->sp_last_hi_ip = ccbp->hi_ip; 71310015Speter if ((pp->sp_last_hi_ip & IP_DCD) || IS_CALLOUT(mynor)) { 714130077Sphk ttyld_modem(tp, 1); 71510015Speter } 71610015Speter } 71710015Speter 71810015Speter /* whoops! we beat the close! */ 71910015Speter if (pp->sp_state & SS_CLOSING) { 72010015Speter /* try and stop it from proceeding to bash the hardware */ 72110015Speter pp->sp_state &= ~SS_CLOSING; 72210015Speter } 72310015Speter 72410015Speter /* 72510015Speter * Wait for DCD if necessary 72610015Speter */ 72750442Speter if (!(tp->t_state & TS_CARR_ON) && 72850442Speter !IS_CALLOUT(mynor) && 72950442Speter !(tp->t_cflag & CLOCAL) && 73050442Speter !(flag & O_NONBLOCK)) { 73110015Speter ++pp->sp_wopeners; 73210015Speter DPRINT((pp, DBG_OPEN, "sleeping for carrier\n")); 73310015Speter error = tsleep(TSA_CARR_ON(tp), TTIPRI|PCATCH, "sidcd", 0); 73410015Speter --pp->sp_wopeners; 73510015Speter if (error != 0) 73610015Speter goto out; 73710015Speter goto open_top; 73810015Speter } 73910015Speter 740130077Sphk error = ttyld_open(tp, dev); 741131134Sphk ttyldoptim(tp); 74210015Speter if (tp->t_state & TS_ISOPEN && IS_CALLOUT(mynor)) 74310015Speter pp->sp_active_out = TRUE; 74410015Speter 74510015Speter pp->sp_state |= SS_OPEN; /* made it! */ 74610015Speter 74710015Speterout: 74810015Speter splx(oldspl); 74910015Speter 75010015Speter DPRINT((pp, DBG_OPEN, "leaving siopen\n")); 75110015Speter 75210015Speter if (!(tp->t_state & TS_ISOPEN) && pp->sp_wopeners == 0) 75310015Speter sihardclose(pp); 75410015Speter 75510015Speter return(error); 75610015Speter} 75710015Speter 75812675Sjulianstatic int 759130585Sphksiclose(struct cdev *dev, int flag, int mode, struct thread *td) 76010015Speter{ 76156498Speter struct si_port *pp; 76256498Speter struct tty *tp; 76310015Speter int oldspl; 76410015Speter int error = 0; 76510015Speter int mynor = minor(dev); 76610015Speter 76710015Speter if (IS_SPECIAL(mynor)) 76810015Speter return(0); 76910015Speter 77010015Speter oldspl = spltty(); 77110015Speter 77210015Speter pp = MINOR2PP(mynor); 77310015Speter tp = pp->sp_tty; 77410015Speter 77550016Snsayer DPRINT((pp, DBG_ENTRY|DBG_CLOSE, "siclose(%s,%x,%x,%x) sp_state:%x\n", 77683366Sjulian devtoname(dev), flag, mode, td, pp->sp_state)); 77710015Speter 77810015Speter /* did we sleep and loose a race? */ 77910015Speter if (pp->sp_state & SS_CLOSING) { 78010015Speter /* error = ESOMETING? */ 78110015Speter goto out; 78210015Speter } 78310015Speter 78410015Speter /* begin race detection.. */ 78510015Speter pp->sp_state |= SS_CLOSING; 78610015Speter 78710015Speter si_write_enable(pp, 0); /* block writes for ttywait() */ 78810015Speter 78910015Speter /* THIS MAY SLEEP IN TTYWAIT!!! */ 790130077Sphk ttyld_close(tp, flag); 79110015Speter 79210015Speter si_write_enable(pp, 1); 79310015Speter 79410015Speter /* did we sleep and somebody started another open? */ 79510015Speter if (!(pp->sp_state & SS_CLOSING)) { 79610015Speter /* error = ESOMETING? */ 79710015Speter goto out; 79810015Speter } 79910015Speter /* ok. we are now still on the right track.. nuke the hardware */ 80010015Speter 80110015Speter if (pp->sp_state & SS_LSTART) { 80229677Sgibbs untimeout(si_lstart, (caddr_t)pp, pp->lstart_ch); 80310015Speter pp->sp_state &= ~SS_LSTART; 80410015Speter } 80510015Speter 80610015Speter sihardclose(pp); 80710015Speter ttyclose(tp); 80810015Speter pp->sp_state &= ~SS_OPEN; 80910015Speter 81010015Speterout: 81110015Speter DPRINT((pp, DBG_CLOSE|DBG_EXIT, "close done, returning\n")); 81210015Speter splx(oldspl); 81310015Speter return(error); 81410015Speter} 81510015Speter 81610015Speterstatic void 81756498Spetersihardclose(struct si_port *pp) 81810015Speter{ 81910015Speter int oldspl; 82010015Speter struct tty *tp; 82110015Speter volatile struct si_channel *ccbp; 82210015Speter 82310015Speter oldspl = spltty(); 82410015Speter 82510015Speter tp = pp->sp_tty; 82610015Speter ccbp = pp->sp_ccb; /* Find control block */ 82750442Speter if (tp->t_cflag & HUPCL || 82850442Speter (!pp->sp_active_out && 82950442Speter !(ccbp->hi_ip & IP_DCD) && 83050442Speter !(pp->sp_iin.c_cflag && CLOCAL)) || 83150442Speter !(tp->t_state & TS_ISOPEN)) { 83210015Speter 83310015Speter (void) si_modem(pp, BIC, TIOCM_DTR|TIOCM_RTS); 83410015Speter (void) si_command(pp, FCLOSE, SI_NOWAIT); 83510015Speter 836131981Sphk ttydtrwaitstart(tp); 83710015Speter } 83810015Speter pp->sp_active_out = FALSE; 839111748Sdes wakeup(&pp->sp_active_out); 84010015Speter wakeup(TSA_CARR_ON(tp)); 84110015Speter 84210015Speter splx(oldspl); 84310015Speter} 84410015Speter 84512675Sjulianstatic int 846130585Sphksiwrite(struct cdev *dev, struct uio *uio, int flag) 84710015Speter{ 84856498Speter struct si_port *pp; 84956498Speter struct tty *tp; 85010015Speter int error = 0; 85110015Speter int mynor = minor(dev); 85210015Speter int oldspl; 85310015Speter 85410015Speter if (IS_SPECIAL(mynor)) { 85510015Speter DPRINT((0, DBG_ENTRY|DBG_FAIL|DBG_WRITE, "siwrite(CONTROLDEV!!)\n")); 85610015Speter return(ENODEV); 85710015Speter } 85810015Speter pp = MINOR2PP(mynor); 85910015Speter tp = pp->sp_tty; 86050016Snsayer DPRINT((pp, DBG_WRITE, "siwrite(%s,%x,%x)\n", devtoname(dev), uio, flag)); 86110015Speter 86210015Speter oldspl = spltty(); 86310015Speter /* 86410015Speter * If writes are currently blocked, wait on the "real" tty 86510015Speter */ 86610015Speter while (pp->sp_state & SS_BLOCKWRITE) { 86710015Speter pp->sp_state |= SS_WAITWRITE; 86810015Speter DPRINT((pp, DBG_WRITE, "in siwrite, wait for SS_BLOCKWRITE to clear\n")); 86918515Speter if ((error = ttysleep(tp, (caddr_t)pp, TTOPRI|PCATCH, 87018515Speter "siwrite", tp->t_timeout))) { 87117291Speter if (error == EWOULDBLOCK) 87217290Speter error = EIO; 87310015Speter goto out; 87417290Speter } 87510015Speter } 87610015Speter 877130077Sphk error = ttyld_write(tp, uio, flag); 87810015Speterout: 87910015Speter splx(oldspl); 88010015Speter return (error); 88110015Speter} 88210015Speter 88310015Speter 88412675Sjulianstatic int 885130585Sphksiioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) 88610015Speter{ 88710015Speter struct si_port *pp; 88856498Speter struct tty *tp; 88910015Speter int error; 89010015Speter int mynor = minor(dev); 89110015Speter int oldspl; 89210015Speter int blocked = 0; 893130892Sphk#ifndef BURN_BRIDGES 89410015Speter#if defined(COMPAT_43) 89538351Sbde u_long oldcmd; 89610015Speter struct termios term; 89710015Speter#endif 898130892Sphk#endif 89910015Speter 90010015Speter if (IS_SI_IOCTL(cmd)) 90183366Sjulian return(si_Sioctl(dev, cmd, data, flag, td)); 90210015Speter 90310015Speter pp = MINOR2PP(mynor); 90410015Speter tp = pp->sp_tty; 90510015Speter 90650016Snsayer DPRINT((pp, DBG_ENTRY|DBG_IOCTL, "siioctl(%s,%lx,%x,%x)\n", 90750016Snsayer devtoname(dev), cmd, data, flag)); 90810015Speter if (IS_STATE(mynor)) { 90910015Speter struct termios *ct; 91010015Speter 91110015Speter switch (mynor & SI_STATE_MASK) { 91210015Speter case SI_INIT_STATE_MASK: 91310015Speter ct = IS_CALLOUT(mynor) ? &pp->sp_iout : &pp->sp_iin; 91410015Speter break; 91510015Speter case SI_LOCK_STATE_MASK: 91616839Speter ct = IS_CALLOUT(mynor) ? &pp->sp_lout : &pp->sp_lin; 91710015Speter break; 91810015Speter default: 91910015Speter return (ENODEV); 92010015Speter } 92110015Speter switch (cmd) { 92210015Speter case TIOCSETA: 92393593Sjhb error = suser(td); 92410015Speter if (error != 0) 92510015Speter return (error); 92610015Speter *ct = *(struct termios *)data; 92710015Speter return (0); 92810015Speter case TIOCGETA: 92910015Speter *(struct termios *)data = *ct; 93010015Speter return (0); 93110015Speter case TIOCGETD: 93210015Speter *(int *)data = TTYDISC; 93310015Speter return (0); 93410015Speter case TIOCGWINSZ: 93510015Speter bzero(data, sizeof(struct winsize)); 93610015Speter return (0); 93710015Speter default: 93810015Speter return (ENOTTY); 93910015Speter } 94010015Speter } 94110015Speter /* 94210015Speter * Do the old-style ioctl compat routines... 94310015Speter */ 944130892Sphk#ifndef BURN_BRIDGES 94510015Speter#if defined(COMPAT_43) 94610015Speter term = tp->t_termios; 94710015Speter oldcmd = cmd; 94810015Speter error = ttsetcompat(tp, &cmd, data, &term); 94910015Speter if (error != 0) 95010015Speter return (error); 95110015Speter if (cmd != oldcmd) 95210015Speter data = (caddr_t)&term; 95310015Speter#endif 954130892Sphk#endif 95510015Speter /* 95610015Speter * Do the initial / lock state business 95710015Speter */ 95810015Speter if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { 95910015Speter int cc; 96010015Speter struct termios *dt = (struct termios *)data; 96110015Speter struct termios *lt = mynor & SI_CALLOUT_MASK 96210015Speter ? &pp->sp_lout : &pp->sp_lin; 96310015Speter 96450442Speter dt->c_iflag = (tp->t_iflag & lt->c_iflag) | 96550442Speter (dt->c_iflag & ~lt->c_iflag); 96650442Speter dt->c_oflag = (tp->t_oflag & lt->c_oflag) | 96750442Speter (dt->c_oflag & ~lt->c_oflag); 96850442Speter dt->c_cflag = (tp->t_cflag & lt->c_cflag) | 96950442Speter (dt->c_cflag & ~lt->c_cflag); 97050442Speter dt->c_lflag = (tp->t_lflag & lt->c_lflag) | 97150442Speter (dt->c_lflag & ~lt->c_lflag); 97210015Speter for (cc = 0; cc < NCCS; ++cc) 97310015Speter if (lt->c_cc[cc] != 0) 97410015Speter dt->c_cc[cc] = tp->t_cc[cc]; 97510015Speter if (lt->c_ispeed != 0) 97610015Speter dt->c_ispeed = tp->t_ispeed; 97710015Speter if (lt->c_ospeed != 0) 97810015Speter dt->c_ospeed = tp->t_ospeed; 97910015Speter } 98010015Speter 98110015Speter /* 98210015Speter * Block user-level writes to give the ttywait() 98310015Speter * a chance to completely drain for commands 98410015Speter * that require the port to be in a quiescent state. 98510015Speter */ 98610015Speter switch (cmd) { 98734832Speter case TIOCSETAW: 98834832Speter case TIOCSETAF: 98917396Speter case TIOCDRAIN: 990130892Sphk#ifndef BURN_BRIDGES 99117396Speter#ifdef COMPAT_43 99217396Speter case TIOCSETP: 99317396Speter#endif 994130892Sphk#endif 99510015Speter blocked++; /* block writes for ttywait() and siparam() */ 99610015Speter si_write_enable(pp, 0); 99710015Speter } 99810015Speter 999130057Sphk error = ttyioctl(dev, cmd, data, flag, td); 1000131134Sphk ttyldoptim(tp); 1001130057Sphk if (error != ENOTTY) 100210015Speter goto out; 100310015Speter 100410015Speter oldspl = spltty(); 100510015Speter 100650435Speter error = 0; 100710015Speter switch (cmd) { 100810015Speter case TIOCSBRK: 100916575Speter si_command(pp, SBREAK, SI_WAIT); 101010015Speter break; 101110015Speter case TIOCCBRK: 101216575Speter si_command(pp, EBREAK, SI_WAIT); 101310015Speter break; 101410015Speter case TIOCSDTR: 101510015Speter (void) si_modem(pp, SET, TIOCM_DTR|TIOCM_RTS); 101610015Speter break; 101710015Speter case TIOCCDTR: 101810015Speter (void) si_modem(pp, SET, 0); 101910015Speter break; 102010015Speter case TIOCMSET: 102110015Speter (void) si_modem(pp, SET, *(int *)data); 102210015Speter break; 102310015Speter case TIOCMBIS: 102410015Speter (void) si_modem(pp, BIS, *(int *)data); 102510015Speter break; 102610015Speter case TIOCMBIC: 102710015Speter (void) si_modem(pp, BIC, *(int *)data); 102810015Speter break; 102910015Speter case TIOCMGET: 103010015Speter *(int *)data = si_modem(pp, GET, 0); 103110015Speter break; 103210015Speter default: 103310015Speter error = ENOTTY; 103410015Speter } 103510015Speter splx(oldspl); 103650435Speter 103710015Speterout: 103810015Speter DPRINT((pp, DBG_IOCTL|DBG_EXIT, "siioctl ret %d\n", error)); 103910015Speter if (blocked) 104010015Speter si_write_enable(pp, 1); 104110015Speter return(error); 104210015Speter} 104310015Speter 104410015Speter/* 104510015Speter * Handle the Specialix ioctls. All MUST be called via the CONTROL device 104610015Speter */ 104710015Speterstatic int 1048130585Sphksi_Sioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) 104910015Speter{ 105010015Speter struct si_softc *xsc; 105156498Speter struct si_port *xpp; 105210015Speter volatile struct si_reg *regp; 105310015Speter struct si_tcsi *dp; 105410044Speter struct si_pstat *sps; 105511872Sphk int *ip, error = 0; 105610015Speter int oldspl; 105710015Speter int card, port; 105810015Speter int mynor = minor(dev); 105910015Speter 106050016Snsayer DPRINT((0, DBG_ENTRY|DBG_IOCTL, "si_Sioctl(%s,%lx,%x,%x)\n", 106150016Snsayer devtoname(dev), cmd, data, flag)); 106210015Speter 106310044Speter#if 1 106410044Speter DPRINT((0, DBG_IOCTL, "TCSI_PORT=%x\n", TCSI_PORT)); 106510044Speter DPRINT((0, DBG_IOCTL, "TCSI_CCB=%x\n", TCSI_CCB)); 106610044Speter DPRINT((0, DBG_IOCTL, "TCSI_TTY=%x\n", TCSI_TTY)); 106710044Speter#endif 106810044Speter 106910015Speter if (!IS_CONTROLDEV(mynor)) { 107010015Speter DPRINT((0, DBG_IOCTL|DBG_FAIL, "not called from control device!\n")); 107110015Speter return(ENODEV); 107210015Speter } 107310015Speter 107410015Speter oldspl = spltty(); /* better safe than sorry */ 107510015Speter 107610015Speter ip = (int *)data; 107710015Speter 107893593Sjhb#define SUCHECK if ((error = suser(td))) goto out 107910015Speter 108010015Speter switch (cmd) { 108110015Speter case TCSIPORTS: 108210015Speter *ip = si_Nports; 108310015Speter goto out; 108410015Speter case TCSIMODULES: 108510015Speter *ip = si_Nmodules; 108610015Speter goto out; 108710015Speter case TCSISDBG_ALL: 108810015Speter SUCHECK; 108910015Speter si_debug = *ip; 109010015Speter goto out; 109110015Speter case TCSIGDBG_ALL: 109210015Speter *ip = si_debug; 109310015Speter goto out; 109410015Speter default: 109510015Speter /* 109610015Speter * Check that a controller for this port exists 109710015Speter */ 109810044Speter 109910044Speter /* may also be a struct si_pstat, a superset of si_tcsi */ 110010044Speter 110110015Speter dp = (struct si_tcsi *)data; 110210044Speter sps = (struct si_pstat *)data; 110310015Speter card = dp->tc_card; 110456498Speter xsc = devclass_get_softc(si_devclass, card); /* check.. */ 110556498Speter if (xsc == NULL || xsc->sc_type == SIEMPTY) { 110610015Speter error = ENOENT; 110710015Speter goto out; 110810015Speter } 110910015Speter /* 111010015Speter * And check that a port exists 111110015Speter */ 111210015Speter port = dp->tc_port; 111310015Speter if (port < 0 || port >= xsc->sc_nport) { 111410015Speter error = ENOENT; 111510015Speter goto out; 111610015Speter } 111710015Speter xpp = xsc->sc_ports + port; 111810015Speter regp = (struct si_reg *)xsc->sc_maddr; 111910015Speter } 112010015Speter 112110015Speter switch (cmd) { 112210015Speter case TCSIDEBUG: 112310015Speter#ifdef SI_DEBUG 112410015Speter SUCHECK; 112510015Speter if (xpp->sp_debug) 112610015Speter xpp->sp_debug = 0; 112710015Speter else { 112810015Speter xpp->sp_debug = DBG_ALL; 112910015Speter DPRINT((xpp, DBG_IOCTL, "debug toggled %s\n", 113010015Speter (xpp->sp_debug&DBG_ALL)?"ON":"OFF")); 113110015Speter } 113210015Speter break; 113310015Speter#else 113410015Speter error = ENODEV; 113510015Speter goto out; 113610015Speter#endif 113710015Speter case TCSISDBG_LEVEL: 113810015Speter case TCSIGDBG_LEVEL: 113910015Speter#ifdef SI_DEBUG 114010015Speter if (cmd == TCSIGDBG_LEVEL) { 114110015Speter dp->tc_dbglvl = xpp->sp_debug; 114210015Speter } else { 114310015Speter SUCHECK; 114410015Speter xpp->sp_debug = dp->tc_dbglvl; 114510015Speter } 114610015Speter break; 114710015Speter#else 114810015Speter error = ENODEV; 114910015Speter goto out; 115010015Speter#endif 115110015Speter case TCSIGRXIT: 115210015Speter dp->tc_int = regp->rx_int_count; 115310015Speter break; 115410015Speter case TCSIRXIT: 115510015Speter SUCHECK; 115610015Speter regp->rx_int_count = dp->tc_int; 115710015Speter break; 115810015Speter case TCSIGIT: 115910015Speter dp->tc_int = regp->int_count; 116010015Speter break; 116110015Speter case TCSIIT: 116210015Speter SUCHECK; 116310015Speter regp->int_count = dp->tc_int; 116410015Speter break; 116510044Speter case TCSISTATE: 116610044Speter dp->tc_int = xpp->sp_ccb->hi_ip; 116710015Speter break; 116810044Speter /* these next three use a different structure */ 116910044Speter case TCSI_PORT: 117010015Speter SUCHECK; 117134832Speter si_bcopy(xpp, &sps->tc_siport, sizeof(sps->tc_siport)); 117210015Speter break; 117310044Speter case TCSI_CCB: 117410044Speter SUCHECK; 117550442Speter si_vbcopy(xpp->sp_ccb, &sps->tc_ccb, sizeof(sps->tc_ccb)); 117610015Speter break; 117710044Speter case TCSI_TTY: 117810044Speter SUCHECK; 117934832Speter si_bcopy(xpp->sp_tty, &sps->tc_tty, sizeof(sps->tc_tty)); 118010015Speter break; 118110015Speter default: 118210015Speter error = EINVAL; 118310015Speter goto out; 118410015Speter } 118510015Speterout: 118610015Speter splx(oldspl); 118710015Speter return(error); /* success */ 118810015Speter} 118910015Speter 119010015Speter/* 119110015Speter * siparam() : Configure line params 119210015Speter * called at spltty(); 119310015Speter * this may sleep, does not flush, nor wait for drain, nor block writes 119410015Speter * caller must arrange this if it's important.. 119510015Speter */ 119612724Sphkstatic int 119756498Spetersiparam(struct tty *tp, struct termios *t) 119810015Speter{ 119956498Speter struct si_port *pp = TP2PP(tp); 120010015Speter volatile struct si_channel *ccbp; 120110015Speter int oldspl, cflag, iflag, oflag, lflag; 120210015Speter int error = 0; /* shutup gcc */ 120310015Speter int ispeed = 0; /* shutup gcc */ 120410015Speter int ospeed = 0; /* shutup gcc */ 120510161Speter BYTE val; 120610015Speter 120710015Speter DPRINT((pp, DBG_ENTRY|DBG_PARAM, "siparam(%x,%x)\n", tp, t)); 120810015Speter cflag = t->c_cflag; 120910015Speter iflag = t->c_iflag; 121010015Speter oflag = t->c_oflag; 121110015Speter lflag = t->c_lflag; 121210044Speter DPRINT((pp, DBG_PARAM, "OFLAG 0x%x CFLAG 0x%x IFLAG 0x%x LFLAG 0x%x\n", 121310044Speter oflag, cflag, iflag, lflag)); 121410015Speter 121534832Speter /* XXX - if Jet host and SXDC module, use extended baud rates */ 121610015Speter 121710015Speter /* if not hung up.. */ 121810015Speter if (t->c_ospeed != 0) { 121910015Speter /* translate baud rate to firmware values */ 122010015Speter ospeed = ttspeedtab(t->c_ospeed, bdrates); 122110015Speter ispeed = t->c_ispeed ? 122210015Speter ttspeedtab(t->c_ispeed, bdrates) : ospeed; 122310015Speter 122410015Speter /* enforce legit baud rate */ 122510015Speter if (ospeed < 0 || ispeed < 0) 122610015Speter return (EINVAL); 122710015Speter } 122810015Speter 122910015Speter oldspl = spltty(); 123010015Speter 123110015Speter ccbp = pp->sp_ccb; 123210015Speter 123310161Speter /* ========== set hi_break ========== */ 123410161Speter val = 0; 123510161Speter if (iflag & IGNBRK) /* Breaks */ 123610161Speter val |= BR_IGN; 123710161Speter if (iflag & BRKINT) /* Interrupt on break? */ 123810161Speter val |= BR_INT; 123910161Speter if (iflag & PARMRK) /* Parity mark? */ 124010161Speter val |= BR_PARMRK; 124110161Speter if (iflag & IGNPAR) /* Ignore chars with parity errors? */ 124210161Speter val |= BR_PARIGN; 124310161Speter ccbp->hi_break = val; 124410161Speter 124510161Speter /* ========== set hi_csr ========== */ 124610015Speter /* if not hung up.. */ 124710015Speter if (t->c_ospeed != 0) { 124810015Speter /* Set I/O speeds */ 124910161Speter val = (ispeed << 4) | ospeed; 125010015Speter } 125110161Speter ccbp->hi_csr = val; 125210015Speter 125310161Speter /* ========== set hi_mr2 ========== */ 125410161Speter val = 0; 125510015Speter if (cflag & CSTOPB) /* Stop bits */ 125610161Speter val |= MR2_2_STOP; 125710015Speter else 125810161Speter val |= MR2_1_STOP; 125910161Speter /* 126010161Speter * Enable H/W RTS/CTS handshaking. The default TA/MTA is 126110161Speter * a DCE, hence the reverse sense of RTS and CTS 126210161Speter */ 126310161Speter /* Output Flow - RTS must be raised before data can be sent */ 126410161Speter if (cflag & CCTS_OFLOW) 126510161Speter val |= MR2_RTSCONT; 126610161Speter 126716575Speter ccbp->hi_mr2 = val; 126810161Speter 126910161Speter /* ========== set hi_mr1 ========== */ 127010161Speter val = 0; 127110015Speter if (!(cflag & PARENB)) /* Parity */ 127210161Speter val |= MR1_NONE; 127310015Speter else 127410161Speter val |= MR1_WITH; 127510015Speter if (cflag & PARODD) 127610161Speter val |= MR1_ODD; 127710015Speter 127810015Speter if ((cflag & CS8) == CS8) { /* 8 data bits? */ 127910161Speter val |= MR1_8_BITS; 128010015Speter } else if ((cflag & CS7) == CS7) { /* 7 data bits? */ 128110161Speter val |= MR1_7_BITS; 128210015Speter } else if ((cflag & CS6) == CS6) { /* 6 data bits? */ 128310161Speter val |= MR1_6_BITS; 128410015Speter } else { /* Must be 5 */ 128510161Speter val |= MR1_5_BITS; 128610015Speter } 128710161Speter /* 128810161Speter * Enable H/W RTS/CTS handshaking. The default TA/MTA is 128910161Speter * a DCE, hence the reverse sense of RTS and CTS 129010161Speter */ 129110161Speter /* Input Flow - CTS is raised when port is ready to receive data */ 129210161Speter if (cflag & CRTS_IFLOW) 129310161Speter val |= MR1_CTSCONT; 129410015Speter 129510161Speter ccbp->hi_mr1 = val; 129610161Speter 129710161Speter /* ========== set hi_mask ========== */ 129810161Speter val = 0xff; 129910161Speter if ((cflag & CS8) == CS8) { /* 8 data bits? */ 130010161Speter val &= 0xFF; 130110161Speter } else if ((cflag & CS7) == CS7) { /* 7 data bits? */ 130210161Speter val &= 0x7F; 130310161Speter } else if ((cflag & CS6) == CS6) { /* 6 data bits? */ 130410161Speter val &= 0x3F; 130510161Speter } else { /* Must be 5 */ 130610161Speter val &= 0x1F; 130710161Speter } 130810015Speter if (iflag & ISTRIP) 130910161Speter val &= 0x7F; 131010015Speter 131110161Speter ccbp->hi_mask = val; 131210161Speter 131310161Speter /* ========== set hi_prtcl ========== */ 131456592Speter val = SP_DCEN; /* Monitor DCD always, or TIOCMGET misses it */ 131510161Speter if (iflag & IXANY) 131610161Speter val |= SP_TANY; 131710161Speter if (iflag & IXON) 131810161Speter val |= SP_TXEN; 131910161Speter if (iflag & IXOFF) 132010161Speter val |= SP_RXEN; 132110161Speter if (iflag & INPCK) 132210161Speter val |= SP_PAEN; 132310015Speter 132410161Speter ccbp->hi_prtcl = val; 132510161Speter 132610161Speter 132710161Speter /* ========== set hi_{rx|tx}{on|off} ========== */ 132810161Speter /* XXX: the card TOTALLY shields us from the flow control... */ 132910015Speter ccbp->hi_txon = t->c_cc[VSTART]; 133010015Speter ccbp->hi_txoff = t->c_cc[VSTOP]; 133110015Speter 133210015Speter ccbp->hi_rxon = t->c_cc[VSTART]; 133310015Speter ccbp->hi_rxoff = t->c_cc[VSTOP]; 133410015Speter 133510161Speter /* ========== send settings to the card ========== */ 133610015Speter /* potential sleep here */ 133710015Speter if (ccbp->hi_stat == IDLE_CLOSE) /* Not yet open */ 133810015Speter si_command(pp, LOPEN, SI_WAIT); /* open it */ 133910015Speter else 134010015Speter si_command(pp, CONFIG, SI_WAIT); /* change params */ 134110015Speter 134210161Speter /* ========== set DTR etc ========== */ 134310015Speter /* Hangup if ospeed == 0 */ 134410015Speter if (t->c_ospeed == 0) { 134510015Speter (void) si_modem(pp, BIC, TIOCM_DTR|TIOCM_RTS); 134610015Speter } else { 134710015Speter /* 134810015Speter * If the previous speed was 0, may need to re-enable 134934832Speter * the modem signals 135034832Speter */ 135110015Speter (void) si_modem(pp, SET, TIOCM_DTR|TIOCM_RTS); 135210015Speter } 135310015Speter 135410044Speter DPRINT((pp, DBG_PARAM, "siparam, complete: MR1 %x MR2 %x HI_MASK %x PRTCL %x HI_BREAK %x\n", 135510044Speter ccbp->hi_mr1, ccbp->hi_mr2, ccbp->hi_mask, ccbp->hi_prtcl, ccbp->hi_break)); 135610015Speter 135710015Speter splx(oldspl); 135810015Speter return(error); 135910015Speter} 136010015Speter 136110015Speter/* 136210015Speter * Enable or Disable the writes to this channel... 136310015Speter * "state" -> enabled = 1; disabled = 0; 136410015Speter */ 136510015Speterstatic void 136656498Spetersi_write_enable(struct si_port *pp, int state) 136710015Speter{ 136810015Speter int oldspl; 136910015Speter 137010015Speter oldspl = spltty(); 137110015Speter 137210015Speter if (state) { 137310015Speter pp->sp_state &= ~SS_BLOCKWRITE; 137410015Speter if (pp->sp_state & SS_WAITWRITE) { 137510015Speter pp->sp_state &= ~SS_WAITWRITE; 137610015Speter /* thunder away! */ 1377111748Sdes wakeup(pp); 137810015Speter } 137910015Speter } else { 138010015Speter pp->sp_state |= SS_BLOCKWRITE; 138110015Speter } 138210015Speter 138310015Speter splx(oldspl); 138410015Speter} 138510015Speter 138610015Speter/* 138710015Speter * Set/Get state of modem control lines. 138810015Speter * Due to DCE-like behaviour of the adapter, some signals need translation: 138910015Speter * TIOCM_DTR DSR 139010015Speter * TIOCM_RTS CTS 139110015Speter */ 139210015Speterstatic int 139356498Spetersi_modem(struct si_port *pp, enum si_mctl cmd, int bits) 139410015Speter{ 139510015Speter volatile struct si_channel *ccbp; 139610015Speter int x; 139710015Speter 139810015Speter DPRINT((pp, DBG_ENTRY|DBG_MODEM, "si_modem(%x,%s,%x)\n", pp, si_mctl2str(cmd), bits)); 139910015Speter ccbp = pp->sp_ccb; /* Find channel address */ 140010015Speter switch (cmd) { 140110015Speter case GET: 140210015Speter x = ccbp->hi_ip; 140310015Speter bits = TIOCM_LE; 140410015Speter if (x & IP_DCD) bits |= TIOCM_CAR; 140510015Speter if (x & IP_DTR) bits |= TIOCM_DTR; 140610015Speter if (x & IP_RTS) bits |= TIOCM_RTS; 140710015Speter if (x & IP_RI) bits |= TIOCM_RI; 140810015Speter return(bits); 140910015Speter case SET: 141010015Speter ccbp->hi_op &= ~(OP_DSR|OP_CTS); 141110015Speter /* fall through */ 141210015Speter case BIS: 141310015Speter x = 0; 141410015Speter if (bits & TIOCM_DTR) 141510015Speter x |= OP_DSR; 141610015Speter if (bits & TIOCM_RTS) 141710015Speter x |= OP_CTS; 141810015Speter ccbp->hi_op |= x; 141910015Speter break; 142010015Speter case BIC: 142110015Speter if (bits & TIOCM_DTR) 142210015Speter ccbp->hi_op &= ~OP_DSR; 142310015Speter if (bits & TIOCM_RTS) 142410015Speter ccbp->hi_op &= ~OP_CTS; 142510015Speter } 142610015Speter return 0; 142710015Speter} 142810015Speter 142910015Speter/* 143010015Speter * Handle change of modem state 143110015Speter */ 143210015Speterstatic void 143356498Spetersi_modem_state(struct si_port *pp, struct tty *tp, int hi_ip) 143410015Speter{ 143510015Speter /* if a modem dev */ 143610015Speter if (hi_ip & IP_DCD) { 143750442Speter if (!(pp->sp_last_hi_ip & IP_DCD)) { 143810015Speter DPRINT((pp, DBG_INTR, "modem carr on t_line %d\n", 143910015Speter tp->t_line)); 1440130077Sphk (void)ttyld_modem(tp, 1); 144110015Speter } 144210015Speter } else { 144310015Speter if (pp->sp_last_hi_ip & IP_DCD) { 144410015Speter DPRINT((pp, DBG_INTR, "modem carr off\n")); 1445130077Sphk if (ttyld_modem(tp, 0)) 144610015Speter (void) si_modem(pp, SET, 0); 144710015Speter } 144810015Speter } 144910015Speter pp->sp_last_hi_ip = hi_ip; 145010015Speter 145110015Speter} 145210015Speter 145310015Speter/* 145410015Speter * Poller to catch missed interrupts. 145512174Speter * 145612496Speter * Note that the SYSV Specialix drivers poll at 100 times per second to get 145712496Speter * better response. We could really use a "periodic" version timeout(). :-) 145810015Speter */ 145910015Speter#ifdef POLL 146010708Speterstatic void 146110015Spetersi_poll(void *nothing) 146210015Speter{ 146356498Speter struct si_softc *sc; 146456498Speter int i; 146510015Speter volatile struct si_reg *regp; 146656498Speter struct si_port *pp; 146712174Speter int lost, oldspl, port; 146810015Speter 146910015Speter DPRINT((0, DBG_POLL, "si_poll()\n")); 147011609Speter oldspl = spltty(); 147110015Speter if (in_intr) 147210015Speter goto out; 147310015Speter lost = 0; 147456498Speter for (i = 0; i < si_numunits; i++) { 147556498Speter sc = devclass_get_softc(si_devclass, i); 147656498Speter if (sc == NULL || sc->sc_type == SIEMPTY) 147710015Speter continue; 147810015Speter regp = (struct si_reg *)sc->sc_maddr; 147934832Speter 148010015Speter /* 148110015Speter * See if there has been a pending interrupt for 2 seconds 148233395Speter * or so. The test (int_scounter >= 200) won't correspond 148310015Speter * to 2 seconds if int_count gets changed. 148410015Speter */ 148510015Speter if (regp->int_pending != 0) { 148610015Speter if (regp->int_scounter >= 200 && 148710015Speter regp->initstat == 1) { 148812174Speter printf("si%d: lost intr\n", i); 148910015Speter lost++; 149010015Speter } 149110015Speter } else { 149210015Speter regp->int_scounter = 0; 149310015Speter } 149410015Speter 149512174Speter /* 149612174Speter * gripe about no input flow control.. 149712174Speter */ 149812174Speter pp = sc->sc_ports; 149912174Speter for (port = 0; port < sc->sc_nport; pp++, port++) { 150012174Speter if (pp->sp_delta_overflows > 0) { 150112174Speter printf("si%d: %d tty level buffer overflows\n", 150212174Speter i, pp->sp_delta_overflows); 150312174Speter pp->sp_delta_overflows = 0; 150412174Speter } 150512174Speter } 150610015Speter } 150717547Speter if (lost || si_realpoll) 150856498Speter si_intr(NULL); /* call intr with fake vector */ 150911609Speterout: 151010015Speter splx(oldspl); 151110015Speter 151215639Speter timeout(si_poll, (caddr_t)0L, si_pollrate); 151310015Speter} 151410015Speter#endif /* ifdef POLL */ 151510015Speter 151610015Speter/* 151710015Speter * The interrupt handler polls ALL ports on ALL adapters each time 151810015Speter * it is called. 151910015Speter */ 152010015Speter 152112496Speterstatic BYTE si_rxbuf[SI_BUFFERSIZE]; /* input staging area */ 152234832Speterstatic BYTE si_txbuf[SI_BUFFERSIZE]; /* output staging area */ 152310015Speter 152456505Spetervoid 152556498Spetersi_intr(void *arg) 152610015Speter{ 152756498Speter struct si_softc *sc; 152856498Speter struct si_port *pp; 152910015Speter volatile struct si_channel *ccbp; 153056498Speter struct tty *tp; 153110015Speter volatile caddr_t maddr; 153211872Sphk BYTE op, ip; 153312174Speter int x, card, port, n, i, isopen; 153410015Speter volatile BYTE *z; 153510015Speter BYTE c; 153610015Speter 153756498Speter sc = arg; 153856498Speter 153956498Speter DPRINT((0, arg == NULL ? DBG_POLL:DBG_INTR, "si_intr\n")); 154056498Speter if (in_intr) 154110708Speter return; 154210015Speter in_intr = 1; 154310015Speter 154410015Speter /* 154510015Speter * When we get an int we poll all the channels and do ALL pending 154610015Speter * work, not just the first one we find. This allows all cards to 154710015Speter * share the same vector. 154834832Speter * 154934832Speter * XXX - But if we're sharing the vector with something that's NOT 155034832Speter * a SI/XIO/SX card, we may be making more work for ourselves. 155110015Speter */ 155256498Speter for (card = 0; card < si_numunits; card++) { 155356498Speter sc = devclass_get_softc(si_devclass, card); 155456498Speter if (sc == NULL || sc->sc_type == SIEMPTY) 155510015Speter continue; 155612174Speter 155712174Speter /* 155812174Speter * First, clear the interrupt 155912174Speter */ 156010015Speter switch(sc->sc_type) { 156134832Speter case SIHOST: 156210015Speter maddr = sc->sc_maddr; 156310015Speter ((volatile struct si_reg *)maddr)->int_pending = 0; 156410015Speter /* flag nothing pending */ 156510015Speter *(maddr+SIINTCL) = 0x00; /* Set IRQ clear */ 156610015Speter *(maddr+SIINTCL_CL) = 0x00; /* Clear IRQ clear */ 156710015Speter break; 156810015Speter case SIHOST2: 156910015Speter maddr = sc->sc_maddr; 157010015Speter ((volatile struct si_reg *)maddr)->int_pending = 0; 157110015Speter *(maddr+SIPLIRQCLR) = 0x00; 157210015Speter *(maddr+SIPLIRQCLR) = 0x10; 157310015Speter break; 157433395Speter case SIPCI: 157533395Speter maddr = sc->sc_maddr; 157633395Speter ((volatile struct si_reg *)maddr)->int_pending = 0; 157733395Speter *(maddr+SIPCIINTCL) = 0x0; 157833395Speter break; 157934832Speter case SIJETPCI: /* fall through to JETISA case */ 158033395Speter case SIJETISA: 158133395Speter maddr = sc->sc_maddr; 158233395Speter ((volatile struct si_reg *)maddr)->int_pending = 0; 158333395Speter *(maddr+SIJETINTCL) = 0x0; 158433395Speter break; 158510015Speter case SIEISA: 158610015Speter maddr = sc->sc_maddr; 158710015Speter ((volatile struct si_reg *)maddr)->int_pending = 0; 158856498Speter (void)inb(sc->sc_iobase + 3); 158910015Speter break; 159010015Speter case SIEMPTY: 159110015Speter default: 159210015Speter continue; 159310015Speter } 159410015Speter ((volatile struct si_reg *)maddr)->int_scounter = 0; 159510015Speter 159612174Speter /* 159712174Speter * check each port 159812174Speter */ 159950442Speter for (pp = sc->sc_ports, port = 0; port < sc->sc_nport; 160034832Speter pp++, port++) { 160110015Speter ccbp = pp->sp_ccb; 160210015Speter tp = pp->sp_tty; 160310015Speter 160410015Speter /* 160510015Speter * See if a command has completed ? 160610015Speter */ 160710015Speter if (ccbp->hi_stat != pp->sp_pend) { 160810015Speter DPRINT((pp, DBG_INTR, 160934735Speter "si_intr hi_stat = 0x%x, pend = %d\n", 161010015Speter ccbp->hi_stat, pp->sp_pend)); 161110015Speter switch(pp->sp_pend) { 161210015Speter case LOPEN: 161310015Speter case MPEND: 161410015Speter case MOPEN: 161510015Speter case CONFIG: 161616575Speter case SBREAK: 161716575Speter case EBREAK: 161810015Speter pp->sp_pend = ccbp->hi_stat; 161910015Speter /* sleeping in si_command */ 162010015Speter wakeup(&pp->sp_state); 162110015Speter break; 162210015Speter default: 162310015Speter pp->sp_pend = ccbp->hi_stat; 162410015Speter } 162534832Speter } 162610015Speter 162710015Speter /* 162810015Speter * Continue on if it's closed 162910015Speter */ 163010015Speter if (ccbp->hi_stat == IDLE_CLOSE) { 163110015Speter continue; 163210015Speter } 163310015Speter 163410015Speter /* 163510015Speter * Do modem state change if not a local device 163610015Speter */ 163710015Speter si_modem_state(pp, tp, ccbp->hi_ip); 163810015Speter 163910015Speter /* 164034832Speter * Check to see if we should 'receive' characters. 164112174Speter */ 164212174Speter if (tp->t_state & TS_CONNECTED && 164312174Speter tp->t_state & TS_ISOPEN) 164412174Speter isopen = 1; 164512174Speter else 164612174Speter isopen = 0; 164712174Speter 164812174Speter /* 164916575Speter * Do input break processing 165010015Speter */ 165110015Speter if (ccbp->hi_state & ST_BREAK) { 165212174Speter if (isopen) { 1653130077Sphk ttyld_rint(tp, TTY_BI); 165410015Speter } 165510015Speter ccbp->hi_state &= ~ST_BREAK; /* A Bit iffy this */ 165610015Speter DPRINT((pp, DBG_INTR, "si_intr break\n")); 165710015Speter } 165810015Speter 165910015Speter /* 166012174Speter * Do RX stuff - if not open then dump any characters. 166112174Speter * XXX: This is VERY messy and needs to be cleaned up. 166212174Speter * 166312174Speter * XXX: can we leave data in the host adapter buffer 166412174Speter * when the clists are full? That may be dangerous 166512174Speter * if the user cannot get an interrupt signal through. 166610015Speter */ 166710015Speter 166812174Speter more_rx: /* XXX Sorry. the nesting was driving me bats! :-( */ 166912174Speter 167012174Speter if (!isopen) { 167110015Speter ccbp->hi_rxopos = ccbp->hi_rxipos; 167212174Speter goto end_rx; 167312174Speter } 167410015Speter 167512174Speter /* 167615640Speter * If the tty input buffers are blocked, stop emptying 167715640Speter * the incoming buffers and let the auto flow control 167815640Speter * assert.. 167915640Speter */ 168015640Speter if (tp->t_state & TS_TBLOCK) { 168115640Speter goto end_rx; 168215640Speter } 168315640Speter 168415640Speter /* 168512174Speter * Process read characters if not skipped above 168612174Speter */ 168715640Speter op = ccbp->hi_rxopos; 168815640Speter ip = ccbp->hi_rxipos; 168915640Speter c = ip - op; 169012174Speter if (c == 0) { 169112174Speter goto end_rx; 169212174Speter } 169310015Speter 169412174Speter n = c & 0xff; 169515640Speter if (n > 250) 169615640Speter n = 250; 169712174Speter 169812174Speter DPRINT((pp, DBG_INTR, "n = %d, op = %d, ip = %d\n", 169910015Speter n, op, ip)); 170010015Speter 170112174Speter /* 170212174Speter * Suck characters out of host card buffer into the 170312174Speter * "input staging buffer" - so that we dont leave the 170412174Speter * host card in limbo while we're possibly echoing 170512174Speter * characters and possibly flushing input inside the 170612174Speter * ldisc l_rint() routine. 170712174Speter */ 170812496Speter if (n <= SI_BUFFERSIZE - op) { 170910015Speter 171012174Speter DPRINT((pp, DBG_INTR, "\tsingle copy\n")); 171112174Speter z = ccbp->hi_rxbuf + op; 171250442Speter si_vbcopy(z, si_rxbuf, n); 171310015Speter 171412174Speter op += n; 171512174Speter } else { 171612496Speter x = SI_BUFFERSIZE - op; 171710015Speter 171812174Speter DPRINT((pp, DBG_INTR, "\tdouble part 1 %d\n", x)); 171912174Speter z = ccbp->hi_rxbuf + op; 172050442Speter si_vbcopy(z, si_rxbuf, x); 172110015Speter 172234832Speter DPRINT((pp, DBG_INTR, "\tdouble part 2 %d\n", 172334832Speter n - x)); 172412174Speter z = ccbp->hi_rxbuf; 172550442Speter si_vbcopy(z, si_rxbuf + x, n - x); 172610015Speter 172712174Speter op += n; 172812174Speter } 172910015Speter 173012174Speter /* clear collected characters from buffer */ 173112174Speter ccbp->hi_rxopos = op; 173212174Speter 173312174Speter DPRINT((pp, DBG_INTR, "n = %d, op = %d, ip = %d\n", 173410015Speter n, op, ip)); 173510015Speter 173612174Speter /* 173712174Speter * at this point... 173812174Speter * n = number of chars placed in si_rxbuf 173912174Speter */ 174010015Speter 174112174Speter /* 174212174Speter * Avoid the grotesquely inefficient lineswitch 174312174Speter * routine (ttyinput) in "raw" mode. It usually 174412174Speter * takes about 450 instructions (that's without 174512174Speter * canonical processing or echo!). slinput is 174612174Speter * reasonably fast (usually 40 instructions 174712174Speter * plus call overhead). 174812174Speter */ 174912174Speter if (tp->t_state & TS_CAN_BYPASS_L_RINT) { 175010015Speter 175112174Speter /* block if the driver supports it */ 175250442Speter if (tp->t_rawq.c_cc + n >= SI_I_HIGH_WATER && 175350442Speter (tp->t_cflag & CRTS_IFLOW || 175450442Speter tp->t_iflag & IXOFF) && 175550442Speter !(tp->t_state & TS_TBLOCK)) 175612174Speter ttyblock(tp); 175710015Speter 175812174Speter tk_nin += n; 175912174Speter tk_rawcc += n; 176012174Speter tp->t_rawcc += n; 176112174Speter 176212174Speter pp->sp_delta_overflows += 176312174Speter b_to_q((char *)si_rxbuf, n, &tp->t_rawq); 176412174Speter 176512174Speter ttwakeup(tp); 176650442Speter if (tp->t_state & TS_TTSTOP && 176750442Speter (tp->t_iflag & IXANY || 176850442Speter tp->t_cc[VSTART] == tp->t_cc[VSTOP])) { 176912174Speter tp->t_state &= ~TS_TTSTOP; 177012174Speter tp->t_lflag &= ~FLUSHO; 177112174Speter si_start(tp); 177212174Speter } 177312174Speter } else { 177412174Speter /* 177512174Speter * It'd be nice to not have to go through the 177612174Speter * function call overhead for each char here. 177712174Speter * It'd be nice to block input it, saving a 177812174Speter * loop here and the call/return overhead. 177912174Speter */ 178012174Speter for(x = 0; x < n; x++) { 178112174Speter i = si_rxbuf[x]; 1782130077Sphk if (ttyld_rint(tp, i) 178312174Speter == -1) { 178412174Speter pp->sp_delta_overflows++; 178510015Speter } 178612174Speter } 178712174Speter } 178812174Speter goto more_rx; /* try for more until RXbuf is empty */ 178910015Speter 179012174Speter end_rx: /* XXX: Again, sorry about the gotos.. :-) */ 179110015Speter 179210015Speter /* 179310015Speter * Do TX stuff 179410015Speter */ 1795130077Sphk ttyld_start(tp); 179610015Speter 179710015Speter } /* end of for (all ports on this controller) */ 179810015Speter } /* end of for (all controllers) */ 179910015Speter 180011609Speter in_intr = 0; 180156498Speter DPRINT((0, arg == NULL ? DBG_POLL:DBG_INTR, "end si_intr\n")); 180210015Speter} 180310015Speter 180410015Speter/* 180510015Speter * Nudge the transmitter... 180612174Speter * 180712174Speter * XXX: I inherited some funny code here. It implies the host card only 180812174Speter * interrupts when the transmit buffer reaches the low-water-mark, and does 180912174Speter * not interrupt when it's actually hits empty. In some cases, we have 181012174Speter * processes waiting for complete drain, and we need to simulate an interrupt 181112174Speter * about when we think the buffer is going to be empty (and retry if not). 181212174Speter * I really am not certain about this... I *need* the hardware manuals. 181310015Speter */ 181410015Speterstatic void 181556498Spetersi_start(struct tty *tp) 181610015Speter{ 181710015Speter struct si_port *pp; 181810015Speter volatile struct si_channel *ccbp; 181956498Speter struct clist *qp; 182010015Speter BYTE ipos; 182110015Speter int nchar; 182210015Speter int oldspl, count, n, amount, buffer_full; 182310015Speter 182410015Speter oldspl = spltty(); 182510015Speter 182610015Speter qp = &tp->t_outq; 182710015Speter pp = TP2PP(tp); 182810015Speter 182910015Speter DPRINT((pp, DBG_ENTRY|DBG_START, 183010015Speter "si_start(%x) t_state %x sp_state %x t_outq.c_cc %d\n", 183110015Speter tp, tp->t_state, pp->sp_state, qp->c_cc)); 183210015Speter 183310015Speter if (tp->t_state & (TS_TIMEOUT|TS_TTSTOP)) 183410015Speter goto out; 183510015Speter 183610015Speter buffer_full = 0; 183710015Speter ccbp = pp->sp_ccb; 183810015Speter 183910015Speter count = (int)ccbp->hi_txipos - (int)ccbp->hi_txopos; 184010015Speter DPRINT((pp, DBG_START, "count %d\n", (BYTE)count)); 184110015Speter 184210015Speter while ((nchar = qp->c_cc) > 0) { 184310015Speter if ((BYTE)count >= 255) { 184410015Speter buffer_full++; 184510015Speter break; 184610015Speter } 184710015Speter amount = min(nchar, (255 - (BYTE)count)); 184810015Speter ipos = (unsigned int)ccbp->hi_txipos; 184934832Speter n = q_to_b(&tp->t_outq, si_txbuf, amount); 185010015Speter /* will it fit in one lump? */ 185134832Speter if ((SI_BUFFERSIZE - ipos) >= n) { 185250442Speter si_bcopyv(si_txbuf, &ccbp->hi_txbuf[ipos], n); 185310015Speter } else { 185450442Speter si_bcopyv(si_txbuf, &ccbp->hi_txbuf[ipos], 185534832Speter SI_BUFFERSIZE - ipos); 185650442Speter si_bcopyv(si_txbuf + (SI_BUFFERSIZE - ipos), 185750442Speter &ccbp->hi_txbuf[0], n - (SI_BUFFERSIZE - ipos)); 185810015Speter } 185910015Speter ccbp->hi_txipos += n; 186010015Speter count = (int)ccbp->hi_txipos - (int)ccbp->hi_txopos; 186110015Speter } 186210015Speter 186310015Speter if (count != 0 && nchar == 0) { 186410015Speter tp->t_state |= TS_BUSY; 186510015Speter } else { 186610015Speter tp->t_state &= ~TS_BUSY; 186710015Speter } 186810015Speter 186910015Speter /* wakeup time? */ 187010015Speter ttwwakeup(tp); 187110015Speter 187210015Speter DPRINT((pp, DBG_START, "count %d, nchar %d, tp->t_state 0x%x\n", 187310015Speter (BYTE)count, nchar, tp->t_state)); 187410015Speter 187534735Speter if (tp->t_state & TS_BUSY) 187610015Speter { 187710015Speter int time; 187810015Speter 187934735Speter time = ttspeedtab(tp->t_ospeed, chartimes); 188034735Speter 188134735Speter if (time > 0) { 188234735Speter if (time < nchar) 188334735Speter time = nchar / time; 188434735Speter else 188534735Speter time = 2; 188610015Speter } else { 188734735Speter DPRINT((pp, DBG_START, 188834735Speter "bad char time value! %d\n", time)); 188934735Speter time = hz/10; 189010015Speter } 189110015Speter 189210015Speter if ((pp->sp_state & (SS_LSTART|SS_INLSTART)) == SS_LSTART) { 189329677Sgibbs untimeout(si_lstart, (caddr_t)pp, pp->lstart_ch); 189410015Speter } else { 189510015Speter pp->sp_state |= SS_LSTART; 189610015Speter } 189710015Speter DPRINT((pp, DBG_START, "arming lstart, time=%d\n", time)); 189829677Sgibbs pp->lstart_ch = timeout(si_lstart, (caddr_t)pp, time); 189910015Speter } 190010015Speter 190110015Speterout: 190210015Speter splx(oldspl); 190310015Speter DPRINT((pp, DBG_EXIT|DBG_START, "leave si_start()\n")); 190410015Speter} 190510015Speter 190610015Speter/* 190710015Speter * Note: called at splsoftclock from the timeout code 190810015Speter * This has to deal with two things... cause wakeups while waiting for 190910015Speter * tty drains on last process exit, and call l_start at about the right 191010015Speter * time for protocols like ppp. 191110015Speter */ 191210015Speterstatic void 191325047Sbdesi_lstart(void *arg) 191410015Speter{ 191556498Speter struct si_port *pp = arg; 191656498Speter struct tty *tp; 191710015Speter int oldspl; 191810015Speter 191910015Speter DPRINT((pp, DBG_ENTRY|DBG_LSTART, "si_lstart(%x) sp_state %x\n", 192010015Speter pp, pp->sp_state)); 192110015Speter 192210015Speter oldspl = spltty(); 192310015Speter 192410015Speter if ((pp->sp_state & SS_OPEN) == 0 || (pp->sp_state & SS_LSTART) == 0) { 192510015Speter splx(oldspl); 192610015Speter return; 192710015Speter } 192810015Speter pp->sp_state &= ~SS_LSTART; 192910015Speter pp->sp_state |= SS_INLSTART; 193010015Speter 193110015Speter tp = pp->sp_tty; 193210015Speter 193310015Speter /* deal with the process exit case */ 193410015Speter ttwwakeup(tp); 193510015Speter 193612174Speter /* nudge protocols - eg: ppp */ 1937130077Sphk ttyld_start(tp); 193810015Speter 193910015Speter pp->sp_state &= ~SS_INLSTART; 194010015Speter splx(oldspl); 194110015Speter} 194210015Speter 194310015Speter/* 194410015Speter * Stop output on a line. called at spltty(); 194510015Speter */ 1946105215Sphkstatic void 194756498Spetersi_stop(struct tty *tp, int rw) 194810015Speter{ 194910015Speter volatile struct si_channel *ccbp; 195010015Speter struct si_port *pp; 195110015Speter 195210015Speter pp = TP2PP(tp); 195310015Speter ccbp = pp->sp_ccb; 195410015Speter 195551654Sphk DPRINT((TP2PP(tp), DBG_ENTRY|DBG_STOP, "si_stop(%x,%x)\n", tp, rw)); 195610015Speter 195710015Speter /* XXX: must check (rw & FWRITE | FREAD) etc flushing... */ 195810015Speter if (rw & FWRITE) { 195910015Speter /* what level are we meant to be flushing anyway? */ 196010015Speter if (tp->t_state & TS_BUSY) { 196110015Speter si_command(TP2PP(tp), WFLUSH, SI_NOWAIT); 196210015Speter tp->t_state &= ~TS_BUSY; 196310015Speter ttwwakeup(tp); /* Bruce???? */ 196410015Speter } 196510015Speter } 196612174Speter#if 1 /* XXX: this doesn't work right yet.. */ 196712174Speter /* XXX: this may have been failing because we used to call l_rint() 196812174Speter * while we were looping based on these two counters. Now, we collect 196912174Speter * the data and then loop stuffing it into l_rint(), making this 197012174Speter * useless. Should we cause this to blow away the staging buffer? 197112174Speter */ 197210015Speter if (rw & FREAD) { 197310015Speter ccbp->hi_rxopos = ccbp->hi_rxipos; 197410015Speter } 197510015Speter#endif 197610015Speter} 197710015Speter 197810015Speter/* 197934832Speter * Issue a command to the host card CPU. 198010015Speter */ 198110015Speter 198210015Speterstatic void 198356498Spetersi_command(struct si_port *pp, int cmd, int waitflag) 198410015Speter{ 198510015Speter int oldspl; 198610015Speter volatile struct si_channel *ccbp = pp->sp_ccb; 198710015Speter int x; 198810015Speter 198910015Speter DPRINT((pp, DBG_ENTRY|DBG_PARAM, "si_command(%x,%x,%d): hi_stat 0x%x\n", 199010015Speter pp, cmd, waitflag, ccbp->hi_stat)); 199110015Speter 199210015Speter oldspl = spltty(); /* Keep others out */ 199310015Speter 199410015Speter /* wait until it's finished what it was doing.. */ 199516575Speter /* XXX: sits in IDLE_BREAK until something disturbs it or break 199616575Speter * is turned off. */ 199710015Speter while((x = ccbp->hi_stat) != IDLE_OPEN && 199810015Speter x != IDLE_CLOSE && 199916575Speter x != IDLE_BREAK && 200010015Speter x != cmd) { 200110015Speter if (in_intr) { /* Prevent sleep in intr */ 200210015Speter DPRINT((pp, DBG_PARAM, 200310015Speter "cmd intr collision - completing %d\trequested %d\n", 200410015Speter x, cmd)); 200510015Speter splx(oldspl); 200610015Speter return; 200710015Speter } else if (ttysleep(pp->sp_tty, (caddr_t)&pp->sp_state, TTIPRI|PCATCH, 200810015Speter "sicmd1", 1)) { 200910015Speter splx(oldspl); 201010015Speter return; 201110015Speter } 201210015Speter } 201316575Speter /* it should now be in IDLE_{OPEN|CLOSE|BREAK}, or "cmd" */ 201410015Speter 201510015Speter /* if there was a pending command, cause a state-change wakeup */ 201616575Speter switch(pp->sp_pend) { 201716575Speter case LOPEN: 201816575Speter case MPEND: 201916575Speter case MOPEN: 202016575Speter case CONFIG: 202116575Speter case SBREAK: 202216575Speter case EBREAK: 202316575Speter wakeup(&pp->sp_state); 202416575Speter break; 202516575Speter default: 202616575Speter break; 202710015Speter } 202810015Speter 202910015Speter pp->sp_pend = cmd; /* New command pending */ 203010015Speter ccbp->hi_stat = cmd; /* Post it */ 203110015Speter 203210015Speter if (waitflag) { 203310015Speter if (in_intr) { /* If in interrupt handler */ 203410015Speter DPRINT((pp, DBG_PARAM, 203510015Speter "attempt to sleep in si_intr - cmd req %d\n", 203610015Speter cmd)); 203710015Speter splx(oldspl); 203810015Speter return; 203916575Speter } else while(ccbp->hi_stat != IDLE_OPEN && 204016575Speter ccbp->hi_stat != IDLE_BREAK) { 204110015Speter if (ttysleep(pp->sp_tty, (caddr_t)&pp->sp_state, TTIPRI|PCATCH, 204210015Speter "sicmd2", 0)) 204310015Speter break; 204410015Speter } 204510015Speter } 204610015Speter splx(oldspl); 204710015Speter} 204810015Speter 204910015Speter 205010015Speter#ifdef SI_DEBUG 205113353Speter 205256505Spetervoid 205313353Spetersi_dprintf(struct si_port *pp, int flags, const char *fmt, ...) 205410015Speter{ 205513353Speter va_list ap; 205613630Sphk 205710015Speter if ((pp == NULL && (si_debug&flags)) || 205810015Speter (pp != NULL && ((pp->sp_debug&flags) || (si_debug&flags)))) { 205934832Speter if (pp != NULL) 206034832Speter printf("%ci%d(%d): ", 's', 206146679Sphk (int)SI_CARD(minor(pp->sp_tty->t_dev)), 206246679Sphk (int)SI_PORT(minor(pp->sp_tty->t_dev))); 206313630Sphk va_start(ap, fmt); 206413630Sphk vprintf(fmt, ap); 206513353Speter va_end(ap); 206610015Speter } 206710015Speter} 206810015Speter 206910015Speterstatic char * 207056498Spetersi_mctl2str(enum si_mctl cmd) 207110015Speter{ 207210015Speter switch (cmd) { 207334832Speter case GET: 207434832Speter return("GET"); 207534832Speter case SET: 207634832Speter return("SET"); 207734832Speter case BIS: 207834832Speter return("BIS"); 207934832Speter case BIC: 208034832Speter return("BIC"); 208110015Speter } 208210015Speter return("BAD"); 208310015Speter} 208412502Sjulian 208512624Speter#endif /* DEBUG */ 208612502Sjulian 208734832Speterstatic char * 208856498Spetersi_modulename(int host_type, int uart_type) 208934832Speter{ 209034832Speter switch (host_type) { 209134832Speter /* Z280 based cards */ 209250442Speter case SIEISA: 209350442Speter case SIHOST2: 209434832Speter case SIHOST: 209534832Speter case SIPCI: 209634832Speter switch (uart_type) { 209734832Speter case 0: 209834832Speter return(" (XIO)"); 209934832Speter case 1: 210034832Speter return(" (SI)"); 210134832Speter } 210234832Speter break; 210334832Speter /* T225 based hosts */ 210434832Speter case SIJETPCI: 210534832Speter case SIJETISA: 210634832Speter switch (uart_type) { 210734832Speter case 0: 210834832Speter return(" (SI)"); 210934832Speter case 40: 211034832Speter return(" (XIO)"); 211136856Sphk case 72: 211236856Sphk return(" (SXDC)"); 211334832Speter } 211434832Speter break; 211534832Speter } 211634832Speter return(""); 211734832Speter} 2118