si.c revision 131134
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 131134 2004-06-26 09:20:07Z 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); 10556498Speterstatic void sidtrwakeup(void *chan); 10610015Speter 10756505Speter#ifdef SI_DEBUG 10856505Speterstatic char *si_mctl2str(enum si_mctl cmd); 10956505Speter#endif 11056505Speter 11156498Speterstatic int siparam(struct tty *, struct termios *); 11210015Speter 11356498Speterstatic void si_modem_state(struct si_port *pp, struct tty *tp, int hi_ip); 11456498Speterstatic char * si_modulename(int host_type, int uart_type); 11510708Speter 11612675Sjulianstatic d_open_t siopen; 11712675Sjulianstatic d_close_t siclose; 11812675Sjulianstatic d_write_t siwrite; 11912675Sjulianstatic d_ioctl_t siioctl; 12012675Sjulian 12147625Sphkstatic struct cdevsw si_cdevsw = { 122126080Sphk .d_version = D_VERSION, 123111815Sphk .d_open = siopen, 124111815Sphk .d_close = siclose, 125111815Sphk .d_write = siwrite, 126111815Sphk .d_ioctl = siioctl, 127111815Sphk .d_name = "si", 128126080Sphk .d_flags = D_TTY | D_NEEDGIANT, 12938485Sbde}; 13012675Sjulian 13110962Speterstatic int si_Nports; 13210962Speterstatic int si_Nmodules; 13310962Speterstatic int si_debug = 0; /* data, not bss, so it's patchable */ 13410015Speter 13534832SpeterSYSCTL_INT(_machdep, OID_AUTO, si_debug, CTLFLAG_RW, &si_debug, 0, ""); 136100743SpeterTUNABLE_INT("machdep.si_debug", &si_debug); 13734832Speter 13856498Speterstatic int si_numunits; 13910044Speter 14056505Speterdevclass_t si_devclass; 14156498Speter 14212174Speter#ifndef B2000 /* not standard, but the hardware knows it. */ 14310015Speter# define B2000 2000 14410015Speter#endif 14510015Speterstatic struct speedtab bdrates[] = { 14650442Speter { B75, CLK75, }, /* 0x0 */ 14750442Speter { B110, CLK110, }, /* 0x1 */ 14850442Speter { B150, CLK150, }, /* 0x3 */ 14950442Speter { B300, CLK300, }, /* 0x4 */ 15050442Speter { B600, CLK600, }, /* 0x5 */ 15150442Speter { B1200, CLK1200, }, /* 0x6 */ 15250442Speter { B2000, CLK2000, }, /* 0x7 */ 15350442Speter { B2400, CLK2400, }, /* 0x8 */ 15450442Speter { B4800, CLK4800, }, /* 0x9 */ 15550442Speter { B9600, CLK9600, }, /* 0xb */ 15650442Speter { B19200, CLK19200, }, /* 0xc */ 15750442Speter { B38400, CLK38400, }, /* 0x2 (out of order!) */ 15850442Speter { B57600, CLK57600, }, /* 0xd */ 15950442Speter { B115200, CLK110, }, /* 0x1 (dupe!, 110 baud on "si") */ 16050442Speter { -1, -1 }, 16110015Speter}; 16210015Speter 16310015Speter 16410015Speter/* populated with approx character/sec rates - translated at card 16510015Speter * initialisation time to chars per tick of the clock */ 16610015Speterstatic int done_chartimes = 0; 16710015Speterstatic struct speedtab chartimes[] = { 16850442Speter { B75, 8, }, 16950442Speter { B110, 11, }, 17050442Speter { B150, 15, }, 17150442Speter { B300, 30, }, 17250442Speter { B600, 60, }, 17350442Speter { B1200, 120, }, 17450442Speter { B2000, 200, }, 17550442Speter { B2400, 240, }, 17650442Speter { B4800, 480, }, 17750442Speter { B9600, 960, }, 17850442Speter { B19200, 1920, }, 17950442Speter { B38400, 3840, }, 18050442Speter { B57600, 5760, }, 18150442Speter { B115200, 11520, }, 18250442Speter { -1, -1 }, 18310015Speter}; 18410015Speterstatic volatile int in_intr = 0; /* Inside interrupt handler? */ 18510015Speter 18610015Speter#ifdef POLL 18715683Speterstatic int si_pollrate; /* in addition to irq */ 18856498Speterstatic int si_realpoll = 0; /* poll HW on timer */ 18915639Speter 19016403SpeterSYSCTL_INT(_machdep, OID_AUTO, si_pollrate, CTLFLAG_RW, &si_pollrate, 0, ""); 19117547SpeterSYSCTL_INT(_machdep, OID_AUTO, si_realpoll, CTLFLAG_RW, &si_realpoll, 0, ""); 19250442Speter 19310015Speterstatic int init_finished = 0; 19456498Speterstatic void si_poll(void *); 19510015Speter#endif 19610015Speter 19710015Speter/* 19810015Speter * Array of adapter types and the corresponding RAM size. The order of 19910015Speter * entries here MUST match the ordinal of the adapter type. 20010015Speter */ 20110015Speterstatic char *si_type[] = { 20210015Speter "EMPTY", 20310015Speter "SIHOST", 20434832Speter "SIMCA", /* FreeBSD does not support Microchannel */ 20510015Speter "SIHOST2", 20610015Speter "SIEISA", 20733395Speter "SIPCI", 20833395Speter "SXPCI", 20933395Speter "SXISA", 21010015Speter}; 21110015Speter 21256498Speter/* 21356498Speter * We have to make an 8 bit version of bcopy, since some cards can't 21456498Speter * deal with 32 bit I/O 21556498Speter */ 21656498Speterstatic void __inline 21756498Spetersi_bcopy(const void *src, void *dst, size_t len) 21856498Speter{ 21956498Speter while (len--) 22056498Speter *(((u_char *)dst)++) = *(((const u_char *)src)++); 22156498Speter} 22256498Speterstatic void __inline 22356498Spetersi_vbcopy(const volatile void *src, void *dst, size_t len) 22456498Speter{ 22556498Speter while (len--) 22656498Speter *(((u_char *)dst)++) = *(((const volatile u_char *)src)++); 22756498Speter} 22856498Speterstatic void __inline 22956498Spetersi_bcopyv(const void *src, volatile void *dst, size_t len) 23056498Speter{ 23156498Speter while (len--) 23256498Speter *(((volatile u_char *)dst)++) = *(((const u_char *)src)++); 23356498Speter} 23456498Speter 23556498Speter 23634832Speter/* 23710015Speter * Attach the device. Initialize the card. 23810015Speter */ 23956505Speterint 24056498Spetersiattach(device_t dev) 24110015Speter{ 24256498Speter int unit; 24356498Speter struct si_softc *sc; 24410015Speter struct si_port *pp; 24510015Speter volatile struct si_channel *ccbp; 24610015Speter volatile struct si_reg *regp; 24710015Speter volatile caddr_t maddr; 24810015Speter struct si_module *modp; 24910015Speter struct speedtab *spt; 25010015Speter int nmodule, nport, x, y; 25112174Speter int uart_type; 25210015Speter 25356498Speter sc = device_get_softc(dev); 25456498Speter unit = device_get_unit(dev); 25510015Speter 25656505Speter sc->sc_typename = si_type[sc->sc_type]; 25756505Speter if (si_numunits < unit + 1) 25856505Speter si_numunits = unit + 1; 25956505Speter 26056498Speter DPRINT((0, DBG_AUTOBOOT, "si%d: siattach\n", unit)); 26110015Speter 26256498Speter#ifdef POLL 26356498Speter if (si_pollrate == 0) { 26456498Speter si_pollrate = POLLHZ; /* in addition to irq */ 26556498Speter#ifdef REALPOLL 26656498Speter si_realpoll = 1; /* scan always */ 26756498Speter#endif 26856498Speter } 26956498Speter#endif 27056498Speter 27133395Speter DPRINT((0, DBG_AUTOBOOT, "si%d: type: %s paddr: %x maddr: %x\n", unit, 27233395Speter sc->sc_typename, sc->sc_paddr, sc->sc_maddr)); 27333395Speter 27410015Speter sc->sc_ports = NULL; /* mark as uninitialised */ 27510015Speter 27610015Speter maddr = sc->sc_maddr; 27710015Speter 27834832Speter /* Stop the CPU first so it won't stomp around while we load */ 27934832Speter 28034832Speter switch (sc->sc_type) { 28134832Speter case SIEISA: 28256498Speter outb(sc->sc_iobase + 2, sc->sc_irq << 4); 28334832Speter break; 28434832Speter case SIPCI: 28534832Speter *(maddr+SIPCIRESET) = 0; 28634832Speter break; 28734832Speter case SIJETPCI: /* fall through to JET ISA */ 28834832Speter case SIJETISA: 28934832Speter *(maddr+SIJETCONFIG) = 0; 29034832Speter break; 29134832Speter case SIHOST2: 29234832Speter *(maddr+SIPLRESET) = 0; 29334832Speter break; 29434832Speter case SIHOST: 29534832Speter *(maddr+SIRESET) = 0; 29634832Speter break; 29734832Speter default: /* this should never happen */ 29834832Speter printf("si%d: unsupported configuration\n", unit); 29956498Speter return EINVAL; 30034832Speter break; 30134832Speter } 30234832Speter 30334832Speter /* OK, now lets download the download code */ 30434832Speter 30536956Ssteve if (SI_ISJET(sc->sc_type)) { 30633395Speter DPRINT((0, DBG_DOWNLOAD, "si%d: jet_download: nbytes %d\n", 30756498Speter unit, si3_t225_dsize)); 30834832Speter si_bcopy(si3_t225_download, maddr + si3_t225_downloadaddr, 30934832Speter si3_t225_dsize); 31034832Speter DPRINT((0, DBG_DOWNLOAD, 31134832Speter "si%d: jet_bootstrap: nbytes %d -> %x\n", 31256498Speter unit, si3_t225_bsize, si3_t225_bootloadaddr)); 31334832Speter si_bcopy(si3_t225_bootstrap, maddr + si3_t225_bootloadaddr, 31434832Speter si3_t225_bsize); 31534832Speter } else { 31633395Speter DPRINT((0, DBG_DOWNLOAD, "si%d: si_download: nbytes %d\n", 31756498Speter unit, si2_z280_dsize)); 31834832Speter si_bcopy(si2_z280_download, maddr + si2_z280_downloadaddr, 31934832Speter si2_z280_dsize); 32033395Speter } 32110015Speter 32234832Speter /* Now start the CPU */ 32334832Speter 32410015Speter switch (sc->sc_type) { 32510015Speter case SIEISA: 32634832Speter /* modify the download code to tell it that it's on an EISA */ 32734832Speter *(maddr + 0x42) = 1; 32856498Speter outb(sc->sc_iobase + 2, (sc->sc_irq << 4) | 4); 32956498Speter (void)inb(sc->sc_iobase + 3); /* reset interrupt */ 33010015Speter break; 33133395Speter case SIPCI: 33234832Speter /* modify the download code to tell it that it's on a PCI */ 33333395Speter *(maddr+0x42) = 1; 33433395Speter *(maddr+SIPCIRESET) = 1; 33533395Speter *(maddr+SIPCIINTCL) = 0; 33633395Speter break; 33733395Speter case SIJETPCI: 33833395Speter *(maddr+SIJETRESET) = 0; 33933395Speter *(maddr+SIJETCONFIG) = SIJETBUSEN|SIJETIRQEN; 34033395Speter break; 34133395Speter case SIJETISA: 34233395Speter *(maddr+SIJETRESET) = 0; 34334832Speter switch (sc->sc_irq) { 34456498Speter case 9: 34534832Speter *(maddr+SIJETCONFIG) = SIJETBUSEN|SIJETIRQEN|0x90; 34634832Speter break; 34756498Speter case 10: 34834832Speter *(maddr+SIJETCONFIG) = SIJETBUSEN|SIJETIRQEN|0xa0; 34934832Speter break; 35056498Speter case 11: 35134832Speter *(maddr+SIJETCONFIG) = SIJETBUSEN|SIJETIRQEN|0xb0; 35234832Speter break; 35356498Speter case 12: 35434832Speter *(maddr+SIJETCONFIG) = SIJETBUSEN|SIJETIRQEN|0xc0; 35534832Speter break; 35656498Speter case 15: 35734832Speter *(maddr+SIJETCONFIG) = SIJETBUSEN|SIJETIRQEN|0xf0; 35834832Speter break; 35934832Speter } 36033395Speter break; 36110015Speter case SIHOST: 36210015Speter *(maddr+SIRESET_CL) = 0; 36310015Speter *(maddr+SIINTCL_CL) = 0; 36410015Speter break; 36510015Speter case SIHOST2: 36610015Speter *(maddr+SIPLRESET) = 0x10; 36710015Speter switch (sc->sc_irq) { 36856498Speter case 11: 36910015Speter *(maddr+SIPLIRQ11) = 0x10; 37010015Speter break; 37156498Speter case 12: 37210015Speter *(maddr+SIPLIRQ12) = 0x10; 37310015Speter break; 37456498Speter case 15: 37510015Speter *(maddr+SIPLIRQ15) = 0x10; 37610015Speter break; 37710015Speter } 37810015Speter *(maddr+SIPLIRQCLR) = 0x10; 37910015Speter break; 38034832Speter default: /* this should _REALLY_ never happen */ 38134832Speter printf("si%d: Uh, it was supported a second ago...\n", unit); 38256498Speter return EINVAL; 38310015Speter } 38410015Speter 38510015Speter DELAY(1000000); /* wait around for a second */ 38610015Speter 38710015Speter regp = (struct si_reg *)maddr; 38810015Speter y = 0; 38910015Speter /* wait max of 5 sec for init OK */ 39010015Speter while (regp->initstat == 0 && y++ < 10) { 39110015Speter DELAY(500000); 39210015Speter } 39310015Speter switch (regp->initstat) { 39410015Speter case 0: 39510015Speter printf("si%d: startup timeout - aborting\n", unit); 39612174Speter sc->sc_type = SIEMPTY; 39756498Speter return EINVAL; 39810015Speter case 1: 39936956Ssteve if (SI_ISJET(sc->sc_type)) { 40034832Speter /* set throttle to 100 times per second */ 40134832Speter regp->int_count = JET_INT_COUNT; 40234832Speter /* rx_intr_count is a NOP in Jet */ 40334832Speter } else { 40434832Speter /* set throttle to 125 times per second */ 40534832Speter regp->int_count = INT_COUNT; 40634832Speter /* rx intr max of 25 times per second */ 40734832Speter regp->rx_int_count = RXINT_COUNT; 40834832Speter } 40910015Speter regp->int_pending = 0; /* no intr pending */ 41010015Speter regp->int_scounter = 0; /* reset counter */ 41110015Speter break; 41210015Speter case 0xff: 41310015Speter /* 41410015Speter * No modules found, so give up on this one. 41510015Speter */ 41610015Speter printf("si%d: %s - no ports found\n", unit, 41710015Speter si_type[sc->sc_type]); 41810015Speter return 0; 41910015Speter default: 42034832Speter printf("si%d: download code version error - initstat %x\n", 42110015Speter unit, regp->initstat); 42256498Speter return EINVAL; 42310015Speter } 42410015Speter 42510015Speter /* 42610015Speter * First time around the ports just count them in order 42710015Speter * to allocate some memory. 42810015Speter */ 42910015Speter nport = 0; 43010015Speter modp = (struct si_module *)(maddr + 0x80); 43110015Speter for (;;) { 43212174Speter DPRINT((0, DBG_DOWNLOAD, "si%d: ccb addr 0x%x\n", unit, modp)); 43334832Speter switch (modp->sm_type) { 43434832Speter case TA4: 43510015Speter DPRINT((0, DBG_DOWNLOAD, 43634832Speter "si%d: Found old TA4 module, 4 ports\n", 43734832Speter unit)); 43834832Speter x = 4; 43910015Speter break; 44034832Speter case TA8: 44134832Speter DPRINT((0, DBG_DOWNLOAD, 44234832Speter "si%d: Found old TA8 module, 8 ports\n", 44334832Speter unit)); 44434832Speter x = 8; 44534832Speter break; 44634832Speter case TA4_ASIC: 44734832Speter DPRINT((0, DBG_DOWNLOAD, 44834832Speter "si%d: Found ASIC TA4 module, 4 ports\n", 44934832Speter unit)); 45034832Speter x = 4; 45134832Speter break; 45234832Speter case TA8_ASIC: 45334832Speter DPRINT((0, DBG_DOWNLOAD, 45434832Speter "si%d: Found ASIC TA8 module, 8 ports\n", 45534832Speter unit)); 45634832Speter x = 8; 45734832Speter break; 45834832Speter case MTA: 45934832Speter DPRINT((0, DBG_DOWNLOAD, 46034832Speter "si%d: Found CD1400 module, 8 ports\n", 46134832Speter unit)); 46234832Speter x = 8; 46334832Speter break; 46434832Speter case SXDC: 46534832Speter DPRINT((0, DBG_DOWNLOAD, 46634832Speter "si%d: Found SXDC module, 8 ports\n", 46734832Speter unit)); 46834832Speter x = 8; 46934832Speter break; 47010015Speter default: 47110015Speter printf("si%d: unknown module type %d\n", 47210015Speter unit, modp->sm_type); 47334832Speter goto try_next; 47410015Speter } 47534832Speter 47634832Speter /* this was limited in firmware and is also a driver issue */ 47734832Speter if ((nport + x) > SI_MAXPORTPERCARD) { 47834832Speter printf("si%d: extra ports ignored\n", unit); 47934832Speter goto try_next; 48034832Speter } 48134832Speter 48234832Speter nport += x; 48334832Speter si_Nports += x; 48434832Speter si_Nmodules++; 48534832Speter 48634832Spetertry_next: 48710015Speter if (modp->sm_next == 0) 48810015Speter break; 48910015Speter modp = (struct si_module *) 49010015Speter (maddr + (unsigned)(modp->sm_next & 0x7fff)); 49110015Speter } 49210015Speter sc->sc_ports = (struct si_port *)malloc(sizeof(struct si_port) * nport, 49369781Sdwmalone M_DEVBUF, M_NOWAIT | M_ZERO); 49410015Speter if (sc->sc_ports == 0) { 49510015Speter printf("si%d: fail to malloc memory for port structs\n", 49610015Speter unit); 49756498Speter return EINVAL; 49810015Speter } 49910015Speter sc->sc_nport = nport; 50010015Speter 50110015Speter /* 50210015Speter * Scan round the ports again, this time initialising. 50310015Speter */ 50410015Speter pp = sc->sc_ports; 50510015Speter nmodule = 0; 50610015Speter modp = (struct si_module *)(maddr + 0x80); 50734832Speter uart_type = 1000; /* arbitary, > uchar_max */ 50810015Speter for (;;) { 50934832Speter switch (modp->sm_type) { 51034832Speter case TA4: 51134832Speter nport = 4; 51210015Speter break; 51334832Speter case TA8: 51434832Speter nport = 8; 51534832Speter break; 51634832Speter case TA4_ASIC: 51734832Speter nport = 4; 51834832Speter break; 51934832Speter case TA8_ASIC: 52034832Speter nport = 8; 52134832Speter break; 52234832Speter case MTA: 52334832Speter nport = 8; 52434832Speter break; 52534832Speter case SXDC: 52634832Speter nport = 8; 52734832Speter break; 52810015Speter default: 52934832Speter goto try_next2; 53010015Speter } 53134832Speter nmodule++; 53234832Speter ccbp = (struct si_channel *)((char *)modp + 0x100); 53334832Speter if (uart_type == 1000) 53434832Speter uart_type = ccbp->type; 53534832Speter else if (uart_type != ccbp->type) 53634832Speter printf("si%d: Warning: module %d mismatch! (%d%s != %d%s)\n", 53734832Speter unit, nmodule, 53834832Speter ccbp->type, si_modulename(sc->sc_type, ccbp->type), 53934832Speter uart_type, si_modulename(sc->sc_type, uart_type)); 54034832Speter 54134832Speter for (x = 0; x < nport; x++, pp++, ccbp++) { 54234832Speter pp->sp_ccb = ccbp; /* save the address */ 54372685Speter pp->sp_tty = ttymalloc(NULL); 54434832Speter pp->sp_pend = IDLE_CLOSE; 54534832Speter pp->sp_state = 0; /* internal flag */ 54634832Speter pp->sp_dtr_wait = 3 * hz; 54734832Speter pp->sp_iin.c_iflag = TTYDEF_IFLAG; 54834832Speter pp->sp_iin.c_oflag = TTYDEF_OFLAG; 54934832Speter pp->sp_iin.c_cflag = TTYDEF_CFLAG; 55034832Speter pp->sp_iin.c_lflag = TTYDEF_LFLAG; 55134832Speter termioschars(&pp->sp_iin); 55234832Speter pp->sp_iin.c_ispeed = pp->sp_iin.c_ospeed = 55334832Speter TTYDEF_SPEED;; 55434832Speter pp->sp_iout = pp->sp_iin; 55534832Speter } 55634832Spetertry_next2: 55710015Speter if (modp->sm_next == 0) { 55834832Speter printf("si%d: card: %s, ports: %d, modules: %d, type: %d%s\n", 55910015Speter unit, 56010015Speter sc->sc_typename, 56110015Speter sc->sc_nport, 56212174Speter nmodule, 56334832Speter uart_type, 56434832Speter si_modulename(sc->sc_type, uart_type)); 56510015Speter break; 56610015Speter } 56710015Speter modp = (struct si_module *) 56810015Speter (maddr + (unsigned)(modp->sm_next & 0x7fff)); 56910015Speter } 57010015Speter if (done_chartimes == 0) { 57110015Speter for (spt = chartimes ; spt->sp_speed != -1; spt++) { 57210015Speter if ((spt->sp_code /= hz) == 0) 57310015Speter spt->sp_code = 1; 57410015Speter } 57510015Speter done_chartimes = 1; 57610015Speter } 57712502Sjulian 57812675Sjulian/* path name devsw minor type uid gid perm*/ 57950442Speter for (x = 0; x < sc->sc_nport; x++) { 58034735Speter /* sync with the manuals that start at 1 */ 58156498Speter y = x + 1 + unit * (1 << SI_CARDSHIFT); 58250442Speter make_dev(&si_cdevsw, x, 0, 0, 0600, "ttyA%02d", y); 58350442Speter make_dev(&si_cdevsw, x + 0x00080, 0, 0, 0600, "cuaA%02d", y); 58450442Speter make_dev(&si_cdevsw, x + 0x10000, 0, 0, 0600, "ttyiA%02d", y); 58550442Speter make_dev(&si_cdevsw, x + 0x10080, 0, 0, 0600, "cuaiA%02d", y); 58650442Speter make_dev(&si_cdevsw, x + 0x20000, 0, 0, 0600, "ttylA%02d", y); 58750442Speter make_dev(&si_cdevsw, x + 0x20080, 0, 0, 0600, "cualA%02d", y); 58812675Sjulian } 58950254Sphk make_dev(&si_cdevsw, 0x40000, 0, 0, 0600, "si_control"); 59056498Speter return (0); 59110015Speter} 59210015Speter 59312675Sjulianstatic int 594130585Sphksiopen(struct cdev *dev, int flag, int mode, struct thread *td) 59510015Speter{ 59610015Speter int oldspl, error; 59710015Speter int card, port; 59856498Speter struct si_softc *sc; 59956498Speter struct tty *tp; 60010015Speter volatile struct si_channel *ccbp; 60110015Speter struct si_port *pp; 60210015Speter int mynor = minor(dev); 60310015Speter 60410015Speter /* quickly let in /dev/si_control */ 60510015Speter if (IS_CONTROLDEV(mynor)) { 60693593Sjhb if ((error = suser(td))) 60710015Speter return(error); 60810015Speter return(0); 60910015Speter } 61010015Speter 61110015Speter card = SI_CARD(mynor); 61256498Speter sc = devclass_get_softc(si_devclass, card); 61356498Speter if (sc == NULL) 61410015Speter return (ENXIO); 61510015Speter 61612174Speter if (sc->sc_type == SIEMPTY) { 61712174Speter DPRINT((0, DBG_OPEN|DBG_FAIL, "si%d: type %s??\n", 61810015Speter card, sc->sc_typename)); 61910015Speter return(ENXIO); 62010015Speter } 62110015Speter 62210015Speter port = SI_PORT(mynor); 62310015Speter if (port >= sc->sc_nport) { 62412174Speter DPRINT((0, DBG_OPEN|DBG_FAIL, "si%d: nports %d\n", 62510015Speter card, sc->sc_nport)); 62610015Speter return(ENXIO); 62710015Speter } 62810015Speter 62910015Speter#ifdef POLL 63010015Speter /* 63110015Speter * We've now got a device, so start the poller. 63210015Speter */ 63310015Speter if (init_finished == 0) { 63415639Speter timeout(si_poll, (caddr_t)0L, si_pollrate); 63510015Speter init_finished = 1; 63610015Speter } 63710015Speter#endif 63810015Speter 63910015Speter /* initial/lock device */ 64010015Speter if (IS_STATE(mynor)) { 64110015Speter return(0); 64210015Speter } 64310015Speter 64410015Speter pp = sc->sc_ports + port; 64510015Speter tp = pp->sp_tty; /* the "real" tty */ 64651654Sphk dev->si_tty = tp; 64710015Speter ccbp = pp->sp_ccb; /* Find control block */ 64850016Snsayer DPRINT((pp, DBG_ENTRY|DBG_OPEN, "siopen(%s,%x,%x,%x)\n", 64983366Sjulian devtoname(dev), flag, mode, td)); 65010015Speter 65110015Speter oldspl = spltty(); /* Keep others out */ 65210015Speter error = 0; 65310015Speter 65410015Speteropen_top: 65510015Speter while (pp->sp_state & SS_DTR_OFF) { 65610015Speter error = tsleep(&pp->sp_dtr_wait, TTIPRI|PCATCH, "sidtr", 0); 65710015Speter if (error != 0) 65810015Speter goto out; 65910015Speter } 66010015Speter 66110015Speter if (tp->t_state & TS_ISOPEN) { 66210015Speter /* 66310015Speter * The device is open, so everything has been initialised. 66410015Speter * handle conflicts. 66510015Speter */ 66610015Speter if (IS_CALLOUT(mynor)) { 66710015Speter if (!pp->sp_active_out) { 66810015Speter error = EBUSY; 66910015Speter goto out; 67010015Speter } 67110015Speter } else { 67210015Speter if (pp->sp_active_out) { 67310015Speter if (flag & O_NONBLOCK) { 67410015Speter error = EBUSY; 67510015Speter goto out; 67610015Speter } 67710015Speter error = tsleep(&pp->sp_active_out, 67810015Speter TTIPRI|PCATCH, "sibi", 0); 67910015Speter if (error != 0) 68010015Speter goto out; 68110015Speter goto open_top; 68210015Speter } 68310015Speter } 68443425Sphk if (tp->t_state & TS_XCLUDE && 68593593Sjhb suser(td)) { 68610015Speter DPRINT((pp, DBG_OPEN|DBG_FAIL, 68710015Speter "already open and EXCLUSIVE set\n")); 68810015Speter error = EBUSY; 68910015Speter goto out; 69010015Speter } 69110015Speter } else { 69210015Speter /* 69310015Speter * The device isn't open, so there are no conflicts. 69410015Speter * Initialize it. Avoid sleep... :-) 69510015Speter */ 69610015Speter DPRINT((pp, DBG_OPEN, "first open\n")); 69710015Speter tp->t_oproc = si_start; 69851654Sphk tp->t_stop = si_stop; 69910015Speter tp->t_param = siparam; 70010015Speter tp->t_dev = dev; 70110015Speter tp->t_termios = mynor & SI_CALLOUT_MASK 70210015Speter ? pp->sp_iout : pp->sp_iin; 70310015Speter 70410015Speter (void) si_modem(pp, SET, TIOCM_DTR|TIOCM_RTS); 70510015Speter 70610015Speter ++pp->sp_wopeners; /* in case of sleep in siparam */ 70710015Speter 70810015Speter error = siparam(tp, &tp->t_termios); 70910015Speter 71010015Speter --pp->sp_wopeners; 71110015Speter if (error != 0) 71210015Speter goto out; 71310015Speter /* XXX: we should goto_top if siparam slept */ 71410015Speter 71510015Speter /* set initial DCD state */ 71610015Speter pp->sp_last_hi_ip = ccbp->hi_ip; 71710015Speter if ((pp->sp_last_hi_ip & IP_DCD) || IS_CALLOUT(mynor)) { 718130077Sphk ttyld_modem(tp, 1); 71910015Speter } 72010015Speter } 72110015Speter 72210015Speter /* whoops! we beat the close! */ 72310015Speter if (pp->sp_state & SS_CLOSING) { 72410015Speter /* try and stop it from proceeding to bash the hardware */ 72510015Speter pp->sp_state &= ~SS_CLOSING; 72610015Speter } 72710015Speter 72810015Speter /* 72910015Speter * Wait for DCD if necessary 73010015Speter */ 73150442Speter if (!(tp->t_state & TS_CARR_ON) && 73250442Speter !IS_CALLOUT(mynor) && 73350442Speter !(tp->t_cflag & CLOCAL) && 73450442Speter !(flag & O_NONBLOCK)) { 73510015Speter ++pp->sp_wopeners; 73610015Speter DPRINT((pp, DBG_OPEN, "sleeping for carrier\n")); 73710015Speter error = tsleep(TSA_CARR_ON(tp), TTIPRI|PCATCH, "sidcd", 0); 73810015Speter --pp->sp_wopeners; 73910015Speter if (error != 0) 74010015Speter goto out; 74110015Speter goto open_top; 74210015Speter } 74310015Speter 744130077Sphk error = ttyld_open(tp, dev); 745131134Sphk ttyldoptim(tp); 74610015Speter if (tp->t_state & TS_ISOPEN && IS_CALLOUT(mynor)) 74710015Speter pp->sp_active_out = TRUE; 74810015Speter 74910015Speter pp->sp_state |= SS_OPEN; /* made it! */ 75010015Speter 75110015Speterout: 75210015Speter splx(oldspl); 75310015Speter 75410015Speter DPRINT((pp, DBG_OPEN, "leaving siopen\n")); 75510015Speter 75610015Speter if (!(tp->t_state & TS_ISOPEN) && pp->sp_wopeners == 0) 75710015Speter sihardclose(pp); 75810015Speter 75910015Speter return(error); 76010015Speter} 76110015Speter 76212675Sjulianstatic int 763130585Sphksiclose(struct cdev *dev, int flag, int mode, struct thread *td) 76410015Speter{ 76556498Speter struct si_port *pp; 76656498Speter struct tty *tp; 76710015Speter int oldspl; 76810015Speter int error = 0; 76910015Speter int mynor = minor(dev); 77010015Speter 77110015Speter if (IS_SPECIAL(mynor)) 77210015Speter return(0); 77310015Speter 77410015Speter oldspl = spltty(); 77510015Speter 77610015Speter pp = MINOR2PP(mynor); 77710015Speter tp = pp->sp_tty; 77810015Speter 77950016Snsayer DPRINT((pp, DBG_ENTRY|DBG_CLOSE, "siclose(%s,%x,%x,%x) sp_state:%x\n", 78083366Sjulian devtoname(dev), flag, mode, td, pp->sp_state)); 78110015Speter 78210015Speter /* did we sleep and loose a race? */ 78310015Speter if (pp->sp_state & SS_CLOSING) { 78410015Speter /* error = ESOMETING? */ 78510015Speter goto out; 78610015Speter } 78710015Speter 78810015Speter /* begin race detection.. */ 78910015Speter pp->sp_state |= SS_CLOSING; 79010015Speter 79110015Speter si_write_enable(pp, 0); /* block writes for ttywait() */ 79210015Speter 79310015Speter /* THIS MAY SLEEP IN TTYWAIT!!! */ 794130077Sphk ttyld_close(tp, flag); 79510015Speter 79610015Speter si_write_enable(pp, 1); 79710015Speter 79810015Speter /* did we sleep and somebody started another open? */ 79910015Speter if (!(pp->sp_state & SS_CLOSING)) { 80010015Speter /* error = ESOMETING? */ 80110015Speter goto out; 80210015Speter } 80310015Speter /* ok. we are now still on the right track.. nuke the hardware */ 80410015Speter 80510015Speter if (pp->sp_state & SS_LSTART) { 80629677Sgibbs untimeout(si_lstart, (caddr_t)pp, pp->lstart_ch); 80710015Speter pp->sp_state &= ~SS_LSTART; 80810015Speter } 80910015Speter 81010015Speter sihardclose(pp); 81110015Speter ttyclose(tp); 81210015Speter pp->sp_state &= ~SS_OPEN; 81310015Speter 81410015Speterout: 81510015Speter DPRINT((pp, DBG_CLOSE|DBG_EXIT, "close done, returning\n")); 81610015Speter splx(oldspl); 81710015Speter return(error); 81810015Speter} 81910015Speter 82010015Speterstatic void 82156498Spetersihardclose(struct si_port *pp) 82210015Speter{ 82310015Speter int oldspl; 82410015Speter struct tty *tp; 82510015Speter volatile struct si_channel *ccbp; 82610015Speter 82710015Speter oldspl = spltty(); 82810015Speter 82910015Speter tp = pp->sp_tty; 83010015Speter ccbp = pp->sp_ccb; /* Find control block */ 83150442Speter if (tp->t_cflag & HUPCL || 83250442Speter (!pp->sp_active_out && 83350442Speter !(ccbp->hi_ip & IP_DCD) && 83450442Speter !(pp->sp_iin.c_cflag && CLOCAL)) || 83550442Speter !(tp->t_state & TS_ISOPEN)) { 83610015Speter 83710015Speter (void) si_modem(pp, BIC, TIOCM_DTR|TIOCM_RTS); 83810015Speter (void) si_command(pp, FCLOSE, SI_NOWAIT); 83910015Speter 84010015Speter if (pp->sp_dtr_wait != 0) { 84110015Speter timeout(sidtrwakeup, pp, pp->sp_dtr_wait); 84210015Speter pp->sp_state |= SS_DTR_OFF; 84310015Speter } 84410015Speter 84510015Speter } 84610015Speter pp->sp_active_out = FALSE; 847111748Sdes wakeup(&pp->sp_active_out); 84810015Speter wakeup(TSA_CARR_ON(tp)); 84910015Speter 85010015Speter splx(oldspl); 85110015Speter} 85210015Speter 85310015Speter 85410015Speter/* 85510015Speter * called at splsoftclock()... 85610015Speter */ 85710015Speterstatic void 85856498Spetersidtrwakeup(void *chan) 85910015Speter{ 86010015Speter struct si_port *pp; 86110015Speter int oldspl; 86210015Speter 86310015Speter oldspl = spltty(); 86410015Speter 86510015Speter pp = (struct si_port *)chan; 86610015Speter pp->sp_state &= ~SS_DTR_OFF; 86710015Speter wakeup(&pp->sp_dtr_wait); 86810015Speter 86910015Speter splx(oldspl); 87010015Speter} 87110015Speter 87212675Sjulianstatic int 873130585Sphksiwrite(struct cdev *dev, struct uio *uio, int flag) 87410015Speter{ 87556498Speter struct si_port *pp; 87656498Speter struct tty *tp; 87710015Speter int error = 0; 87810015Speter int mynor = minor(dev); 87910015Speter int oldspl; 88010015Speter 88110015Speter if (IS_SPECIAL(mynor)) { 88210015Speter DPRINT((0, DBG_ENTRY|DBG_FAIL|DBG_WRITE, "siwrite(CONTROLDEV!!)\n")); 88310015Speter return(ENODEV); 88410015Speter } 88510015Speter pp = MINOR2PP(mynor); 88610015Speter tp = pp->sp_tty; 88750016Snsayer DPRINT((pp, DBG_WRITE, "siwrite(%s,%x,%x)\n", devtoname(dev), uio, flag)); 88810015Speter 88910015Speter oldspl = spltty(); 89010015Speter /* 89110015Speter * If writes are currently blocked, wait on the "real" tty 89210015Speter */ 89310015Speter while (pp->sp_state & SS_BLOCKWRITE) { 89410015Speter pp->sp_state |= SS_WAITWRITE; 89510015Speter DPRINT((pp, DBG_WRITE, "in siwrite, wait for SS_BLOCKWRITE to clear\n")); 89618515Speter if ((error = ttysleep(tp, (caddr_t)pp, TTOPRI|PCATCH, 89718515Speter "siwrite", tp->t_timeout))) { 89817291Speter if (error == EWOULDBLOCK) 89917290Speter error = EIO; 90010015Speter goto out; 90117290Speter } 90210015Speter } 90310015Speter 904130077Sphk error = ttyld_write(tp, uio, flag); 90510015Speterout: 90610015Speter splx(oldspl); 90710015Speter return (error); 90810015Speter} 90910015Speter 91010015Speter 91112675Sjulianstatic int 912130585Sphksiioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) 91310015Speter{ 91410015Speter struct si_port *pp; 91556498Speter struct tty *tp; 91610015Speter int error; 91710015Speter int mynor = minor(dev); 91810015Speter int oldspl; 91910015Speter int blocked = 0; 920130892Sphk#ifndef BURN_BRIDGES 92110015Speter#if defined(COMPAT_43) 92238351Sbde u_long oldcmd; 92310015Speter struct termios term; 92410015Speter#endif 925130892Sphk#endif 92610015Speter 92710015Speter if (IS_SI_IOCTL(cmd)) 92883366Sjulian return(si_Sioctl(dev, cmd, data, flag, td)); 92910015Speter 93010015Speter pp = MINOR2PP(mynor); 93110015Speter tp = pp->sp_tty; 93210015Speter 93350016Snsayer DPRINT((pp, DBG_ENTRY|DBG_IOCTL, "siioctl(%s,%lx,%x,%x)\n", 93450016Snsayer devtoname(dev), cmd, data, flag)); 93510015Speter if (IS_STATE(mynor)) { 93610015Speter struct termios *ct; 93710015Speter 93810015Speter switch (mynor & SI_STATE_MASK) { 93910015Speter case SI_INIT_STATE_MASK: 94010015Speter ct = IS_CALLOUT(mynor) ? &pp->sp_iout : &pp->sp_iin; 94110015Speter break; 94210015Speter case SI_LOCK_STATE_MASK: 94316839Speter ct = IS_CALLOUT(mynor) ? &pp->sp_lout : &pp->sp_lin; 94410015Speter break; 94510015Speter default: 94610015Speter return (ENODEV); 94710015Speter } 94810015Speter switch (cmd) { 94910015Speter case TIOCSETA: 95093593Sjhb error = suser(td); 95110015Speter if (error != 0) 95210015Speter return (error); 95310015Speter *ct = *(struct termios *)data; 95410015Speter return (0); 95510015Speter case TIOCGETA: 95610015Speter *(struct termios *)data = *ct; 95710015Speter return (0); 95810015Speter case TIOCGETD: 95910015Speter *(int *)data = TTYDISC; 96010015Speter return (0); 96110015Speter case TIOCGWINSZ: 96210015Speter bzero(data, sizeof(struct winsize)); 96310015Speter return (0); 96410015Speter default: 96510015Speter return (ENOTTY); 96610015Speter } 96710015Speter } 96810015Speter /* 96910015Speter * Do the old-style ioctl compat routines... 97010015Speter */ 971130892Sphk#ifndef BURN_BRIDGES 97210015Speter#if defined(COMPAT_43) 97310015Speter term = tp->t_termios; 97410015Speter oldcmd = cmd; 97510015Speter error = ttsetcompat(tp, &cmd, data, &term); 97610015Speter if (error != 0) 97710015Speter return (error); 97810015Speter if (cmd != oldcmd) 97910015Speter data = (caddr_t)&term; 98010015Speter#endif 981130892Sphk#endif 98210015Speter /* 98310015Speter * Do the initial / lock state business 98410015Speter */ 98510015Speter if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { 98610015Speter int cc; 98710015Speter struct termios *dt = (struct termios *)data; 98810015Speter struct termios *lt = mynor & SI_CALLOUT_MASK 98910015Speter ? &pp->sp_lout : &pp->sp_lin; 99010015Speter 99150442Speter dt->c_iflag = (tp->t_iflag & lt->c_iflag) | 99250442Speter (dt->c_iflag & ~lt->c_iflag); 99350442Speter dt->c_oflag = (tp->t_oflag & lt->c_oflag) | 99450442Speter (dt->c_oflag & ~lt->c_oflag); 99550442Speter dt->c_cflag = (tp->t_cflag & lt->c_cflag) | 99650442Speter (dt->c_cflag & ~lt->c_cflag); 99750442Speter dt->c_lflag = (tp->t_lflag & lt->c_lflag) | 99850442Speter (dt->c_lflag & ~lt->c_lflag); 99910015Speter for (cc = 0; cc < NCCS; ++cc) 100010015Speter if (lt->c_cc[cc] != 0) 100110015Speter dt->c_cc[cc] = tp->t_cc[cc]; 100210015Speter if (lt->c_ispeed != 0) 100310015Speter dt->c_ispeed = tp->t_ispeed; 100410015Speter if (lt->c_ospeed != 0) 100510015Speter dt->c_ospeed = tp->t_ospeed; 100610015Speter } 100710015Speter 100810015Speter /* 100910015Speter * Block user-level writes to give the ttywait() 101010015Speter * a chance to completely drain for commands 101110015Speter * that require the port to be in a quiescent state. 101210015Speter */ 101310015Speter switch (cmd) { 101434832Speter case TIOCSETAW: 101534832Speter case TIOCSETAF: 101617396Speter case TIOCDRAIN: 1017130892Sphk#ifndef BURN_BRIDGES 101817396Speter#ifdef COMPAT_43 101917396Speter case TIOCSETP: 102017396Speter#endif 1021130892Sphk#endif 102210015Speter blocked++; /* block writes for ttywait() and siparam() */ 102310015Speter si_write_enable(pp, 0); 102410015Speter } 102510015Speter 1026130057Sphk error = ttyioctl(dev, cmd, data, flag, td); 1027131134Sphk ttyldoptim(tp); 1028130057Sphk if (error != ENOTTY) 102910015Speter goto out; 103010015Speter 103110015Speter oldspl = spltty(); 103210015Speter 103350435Speter error = 0; 103410015Speter switch (cmd) { 103510015Speter case TIOCSBRK: 103616575Speter si_command(pp, SBREAK, SI_WAIT); 103710015Speter break; 103810015Speter case TIOCCBRK: 103916575Speter si_command(pp, EBREAK, SI_WAIT); 104010015Speter break; 104110015Speter case TIOCSDTR: 104210015Speter (void) si_modem(pp, SET, TIOCM_DTR|TIOCM_RTS); 104310015Speter break; 104410015Speter case TIOCCDTR: 104510015Speter (void) si_modem(pp, SET, 0); 104610015Speter break; 104710015Speter case TIOCMSET: 104810015Speter (void) si_modem(pp, SET, *(int *)data); 104910015Speter break; 105010015Speter case TIOCMBIS: 105110015Speter (void) si_modem(pp, BIS, *(int *)data); 105210015Speter break; 105310015Speter case TIOCMBIC: 105410015Speter (void) si_modem(pp, BIC, *(int *)data); 105510015Speter break; 105610015Speter case TIOCMGET: 105710015Speter *(int *)data = si_modem(pp, GET, 0); 105810015Speter break; 105910015Speter case TIOCMSDTRWAIT: 106010015Speter /* must be root since the wait applies to following logins */ 106193593Sjhb error = suser(td); 106250435Speter if (error == 0) 106350435Speter pp->sp_dtr_wait = *(int *)data * hz / 100; 106410015Speter break; 106510015Speter case TIOCMGDTRWAIT: 106610015Speter *(int *)data = pp->sp_dtr_wait * 100 / hz; 106710015Speter break; 106810015Speter default: 106910015Speter error = ENOTTY; 107010015Speter } 107110015Speter splx(oldspl); 107250435Speter 107310015Speterout: 107410015Speter DPRINT((pp, DBG_IOCTL|DBG_EXIT, "siioctl ret %d\n", error)); 107510015Speter if (blocked) 107610015Speter si_write_enable(pp, 1); 107710015Speter return(error); 107810015Speter} 107910015Speter 108010015Speter/* 108110015Speter * Handle the Specialix ioctls. All MUST be called via the CONTROL device 108210015Speter */ 108310015Speterstatic int 1084130585Sphksi_Sioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) 108510015Speter{ 108610015Speter struct si_softc *xsc; 108756498Speter struct si_port *xpp; 108810015Speter volatile struct si_reg *regp; 108910015Speter struct si_tcsi *dp; 109010044Speter struct si_pstat *sps; 109111872Sphk int *ip, error = 0; 109210015Speter int oldspl; 109310015Speter int card, port; 109410015Speter int mynor = minor(dev); 109510015Speter 109650016Snsayer DPRINT((0, DBG_ENTRY|DBG_IOCTL, "si_Sioctl(%s,%lx,%x,%x)\n", 109750016Snsayer devtoname(dev), cmd, data, flag)); 109810015Speter 109910044Speter#if 1 110010044Speter DPRINT((0, DBG_IOCTL, "TCSI_PORT=%x\n", TCSI_PORT)); 110110044Speter DPRINT((0, DBG_IOCTL, "TCSI_CCB=%x\n", TCSI_CCB)); 110210044Speter DPRINT((0, DBG_IOCTL, "TCSI_TTY=%x\n", TCSI_TTY)); 110310044Speter#endif 110410044Speter 110510015Speter if (!IS_CONTROLDEV(mynor)) { 110610015Speter DPRINT((0, DBG_IOCTL|DBG_FAIL, "not called from control device!\n")); 110710015Speter return(ENODEV); 110810015Speter } 110910015Speter 111010015Speter oldspl = spltty(); /* better safe than sorry */ 111110015Speter 111210015Speter ip = (int *)data; 111310015Speter 111493593Sjhb#define SUCHECK if ((error = suser(td))) goto out 111510015Speter 111610015Speter switch (cmd) { 111710015Speter case TCSIPORTS: 111810015Speter *ip = si_Nports; 111910015Speter goto out; 112010015Speter case TCSIMODULES: 112110015Speter *ip = si_Nmodules; 112210015Speter goto out; 112310015Speter case TCSISDBG_ALL: 112410015Speter SUCHECK; 112510015Speter si_debug = *ip; 112610015Speter goto out; 112710015Speter case TCSIGDBG_ALL: 112810015Speter *ip = si_debug; 112910015Speter goto out; 113010015Speter default: 113110015Speter /* 113210015Speter * Check that a controller for this port exists 113310015Speter */ 113410044Speter 113510044Speter /* may also be a struct si_pstat, a superset of si_tcsi */ 113610044Speter 113710015Speter dp = (struct si_tcsi *)data; 113810044Speter sps = (struct si_pstat *)data; 113910015Speter card = dp->tc_card; 114056498Speter xsc = devclass_get_softc(si_devclass, card); /* check.. */ 114156498Speter if (xsc == NULL || xsc->sc_type == SIEMPTY) { 114210015Speter error = ENOENT; 114310015Speter goto out; 114410015Speter } 114510015Speter /* 114610015Speter * And check that a port exists 114710015Speter */ 114810015Speter port = dp->tc_port; 114910015Speter if (port < 0 || port >= xsc->sc_nport) { 115010015Speter error = ENOENT; 115110015Speter goto out; 115210015Speter } 115310015Speter xpp = xsc->sc_ports + port; 115410015Speter regp = (struct si_reg *)xsc->sc_maddr; 115510015Speter } 115610015Speter 115710015Speter switch (cmd) { 115810015Speter case TCSIDEBUG: 115910015Speter#ifdef SI_DEBUG 116010015Speter SUCHECK; 116110015Speter if (xpp->sp_debug) 116210015Speter xpp->sp_debug = 0; 116310015Speter else { 116410015Speter xpp->sp_debug = DBG_ALL; 116510015Speter DPRINT((xpp, DBG_IOCTL, "debug toggled %s\n", 116610015Speter (xpp->sp_debug&DBG_ALL)?"ON":"OFF")); 116710015Speter } 116810015Speter break; 116910015Speter#else 117010015Speter error = ENODEV; 117110015Speter goto out; 117210015Speter#endif 117310015Speter case TCSISDBG_LEVEL: 117410015Speter case TCSIGDBG_LEVEL: 117510015Speter#ifdef SI_DEBUG 117610015Speter if (cmd == TCSIGDBG_LEVEL) { 117710015Speter dp->tc_dbglvl = xpp->sp_debug; 117810015Speter } else { 117910015Speter SUCHECK; 118010015Speter xpp->sp_debug = dp->tc_dbglvl; 118110015Speter } 118210015Speter break; 118310015Speter#else 118410015Speter error = ENODEV; 118510015Speter goto out; 118610015Speter#endif 118710015Speter case TCSIGRXIT: 118810015Speter dp->tc_int = regp->rx_int_count; 118910015Speter break; 119010015Speter case TCSIRXIT: 119110015Speter SUCHECK; 119210015Speter regp->rx_int_count = dp->tc_int; 119310015Speter break; 119410015Speter case TCSIGIT: 119510015Speter dp->tc_int = regp->int_count; 119610015Speter break; 119710015Speter case TCSIIT: 119810015Speter SUCHECK; 119910015Speter regp->int_count = dp->tc_int; 120010015Speter break; 120110044Speter case TCSISTATE: 120210044Speter dp->tc_int = xpp->sp_ccb->hi_ip; 120310015Speter break; 120410044Speter /* these next three use a different structure */ 120510044Speter case TCSI_PORT: 120610015Speter SUCHECK; 120734832Speter si_bcopy(xpp, &sps->tc_siport, sizeof(sps->tc_siport)); 120810015Speter break; 120910044Speter case TCSI_CCB: 121010044Speter SUCHECK; 121150442Speter si_vbcopy(xpp->sp_ccb, &sps->tc_ccb, sizeof(sps->tc_ccb)); 121210015Speter break; 121310044Speter case TCSI_TTY: 121410044Speter SUCHECK; 121534832Speter si_bcopy(xpp->sp_tty, &sps->tc_tty, sizeof(sps->tc_tty)); 121610015Speter break; 121710015Speter default: 121810015Speter error = EINVAL; 121910015Speter goto out; 122010015Speter } 122110015Speterout: 122210015Speter splx(oldspl); 122310015Speter return(error); /* success */ 122410015Speter} 122510015Speter 122610015Speter/* 122710015Speter * siparam() : Configure line params 122810015Speter * called at spltty(); 122910015Speter * this may sleep, does not flush, nor wait for drain, nor block writes 123010015Speter * caller must arrange this if it's important.. 123110015Speter */ 123212724Sphkstatic int 123356498Spetersiparam(struct tty *tp, struct termios *t) 123410015Speter{ 123556498Speter struct si_port *pp = TP2PP(tp); 123610015Speter volatile struct si_channel *ccbp; 123710015Speter int oldspl, cflag, iflag, oflag, lflag; 123810015Speter int error = 0; /* shutup gcc */ 123910015Speter int ispeed = 0; /* shutup gcc */ 124010015Speter int ospeed = 0; /* shutup gcc */ 124110161Speter BYTE val; 124210015Speter 124310015Speter DPRINT((pp, DBG_ENTRY|DBG_PARAM, "siparam(%x,%x)\n", tp, t)); 124410015Speter cflag = t->c_cflag; 124510015Speter iflag = t->c_iflag; 124610015Speter oflag = t->c_oflag; 124710015Speter lflag = t->c_lflag; 124810044Speter DPRINT((pp, DBG_PARAM, "OFLAG 0x%x CFLAG 0x%x IFLAG 0x%x LFLAG 0x%x\n", 124910044Speter oflag, cflag, iflag, lflag)); 125010015Speter 125134832Speter /* XXX - if Jet host and SXDC module, use extended baud rates */ 125210015Speter 125310015Speter /* if not hung up.. */ 125410015Speter if (t->c_ospeed != 0) { 125510015Speter /* translate baud rate to firmware values */ 125610015Speter ospeed = ttspeedtab(t->c_ospeed, bdrates); 125710015Speter ispeed = t->c_ispeed ? 125810015Speter ttspeedtab(t->c_ispeed, bdrates) : ospeed; 125910015Speter 126010015Speter /* enforce legit baud rate */ 126110015Speter if (ospeed < 0 || ispeed < 0) 126210015Speter return (EINVAL); 126310015Speter } 126410015Speter 126510015Speter oldspl = spltty(); 126610015Speter 126710015Speter ccbp = pp->sp_ccb; 126810015Speter 126910161Speter /* ========== set hi_break ========== */ 127010161Speter val = 0; 127110161Speter if (iflag & IGNBRK) /* Breaks */ 127210161Speter val |= BR_IGN; 127310161Speter if (iflag & BRKINT) /* Interrupt on break? */ 127410161Speter val |= BR_INT; 127510161Speter if (iflag & PARMRK) /* Parity mark? */ 127610161Speter val |= BR_PARMRK; 127710161Speter if (iflag & IGNPAR) /* Ignore chars with parity errors? */ 127810161Speter val |= BR_PARIGN; 127910161Speter ccbp->hi_break = val; 128010161Speter 128110161Speter /* ========== set hi_csr ========== */ 128210015Speter /* if not hung up.. */ 128310015Speter if (t->c_ospeed != 0) { 128410015Speter /* Set I/O speeds */ 128510161Speter val = (ispeed << 4) | ospeed; 128610015Speter } 128710161Speter ccbp->hi_csr = val; 128810015Speter 128910161Speter /* ========== set hi_mr2 ========== */ 129010161Speter val = 0; 129110015Speter if (cflag & CSTOPB) /* Stop bits */ 129210161Speter val |= MR2_2_STOP; 129310015Speter else 129410161Speter val |= MR2_1_STOP; 129510161Speter /* 129610161Speter * Enable H/W RTS/CTS handshaking. The default TA/MTA is 129710161Speter * a DCE, hence the reverse sense of RTS and CTS 129810161Speter */ 129910161Speter /* Output Flow - RTS must be raised before data can be sent */ 130010161Speter if (cflag & CCTS_OFLOW) 130110161Speter val |= MR2_RTSCONT; 130210161Speter 130316575Speter ccbp->hi_mr2 = val; 130410161Speter 130510161Speter /* ========== set hi_mr1 ========== */ 130610161Speter val = 0; 130710015Speter if (!(cflag & PARENB)) /* Parity */ 130810161Speter val |= MR1_NONE; 130910015Speter else 131010161Speter val |= MR1_WITH; 131110015Speter if (cflag & PARODD) 131210161Speter val |= MR1_ODD; 131310015Speter 131410015Speter if ((cflag & CS8) == CS8) { /* 8 data bits? */ 131510161Speter val |= MR1_8_BITS; 131610015Speter } else if ((cflag & CS7) == CS7) { /* 7 data bits? */ 131710161Speter val |= MR1_7_BITS; 131810015Speter } else if ((cflag & CS6) == CS6) { /* 6 data bits? */ 131910161Speter val |= MR1_6_BITS; 132010015Speter } else { /* Must be 5 */ 132110161Speter val |= MR1_5_BITS; 132210015Speter } 132310161Speter /* 132410161Speter * Enable H/W RTS/CTS handshaking. The default TA/MTA is 132510161Speter * a DCE, hence the reverse sense of RTS and CTS 132610161Speter */ 132710161Speter /* Input Flow - CTS is raised when port is ready to receive data */ 132810161Speter if (cflag & CRTS_IFLOW) 132910161Speter val |= MR1_CTSCONT; 133010015Speter 133110161Speter ccbp->hi_mr1 = val; 133210161Speter 133310161Speter /* ========== set hi_mask ========== */ 133410161Speter val = 0xff; 133510161Speter if ((cflag & CS8) == CS8) { /* 8 data bits? */ 133610161Speter val &= 0xFF; 133710161Speter } else if ((cflag & CS7) == CS7) { /* 7 data bits? */ 133810161Speter val &= 0x7F; 133910161Speter } else if ((cflag & CS6) == CS6) { /* 6 data bits? */ 134010161Speter val &= 0x3F; 134110161Speter } else { /* Must be 5 */ 134210161Speter val &= 0x1F; 134310161Speter } 134410015Speter if (iflag & ISTRIP) 134510161Speter val &= 0x7F; 134610015Speter 134710161Speter ccbp->hi_mask = val; 134810161Speter 134910161Speter /* ========== set hi_prtcl ========== */ 135056592Speter val = SP_DCEN; /* Monitor DCD always, or TIOCMGET misses it */ 135110161Speter if (iflag & IXANY) 135210161Speter val |= SP_TANY; 135310161Speter if (iflag & IXON) 135410161Speter val |= SP_TXEN; 135510161Speter if (iflag & IXOFF) 135610161Speter val |= SP_RXEN; 135710161Speter if (iflag & INPCK) 135810161Speter val |= SP_PAEN; 135910015Speter 136010161Speter ccbp->hi_prtcl = val; 136110161Speter 136210161Speter 136310161Speter /* ========== set hi_{rx|tx}{on|off} ========== */ 136410161Speter /* XXX: the card TOTALLY shields us from the flow control... */ 136510015Speter ccbp->hi_txon = t->c_cc[VSTART]; 136610015Speter ccbp->hi_txoff = t->c_cc[VSTOP]; 136710015Speter 136810015Speter ccbp->hi_rxon = t->c_cc[VSTART]; 136910015Speter ccbp->hi_rxoff = t->c_cc[VSTOP]; 137010015Speter 137110161Speter /* ========== send settings to the card ========== */ 137210015Speter /* potential sleep here */ 137310015Speter if (ccbp->hi_stat == IDLE_CLOSE) /* Not yet open */ 137410015Speter si_command(pp, LOPEN, SI_WAIT); /* open it */ 137510015Speter else 137610015Speter si_command(pp, CONFIG, SI_WAIT); /* change params */ 137710015Speter 137810161Speter /* ========== set DTR etc ========== */ 137910015Speter /* Hangup if ospeed == 0 */ 138010015Speter if (t->c_ospeed == 0) { 138110015Speter (void) si_modem(pp, BIC, TIOCM_DTR|TIOCM_RTS); 138210015Speter } else { 138310015Speter /* 138410015Speter * If the previous speed was 0, may need to re-enable 138534832Speter * the modem signals 138634832Speter */ 138710015Speter (void) si_modem(pp, SET, TIOCM_DTR|TIOCM_RTS); 138810015Speter } 138910015Speter 139010044Speter DPRINT((pp, DBG_PARAM, "siparam, complete: MR1 %x MR2 %x HI_MASK %x PRTCL %x HI_BREAK %x\n", 139110044Speter ccbp->hi_mr1, ccbp->hi_mr2, ccbp->hi_mask, ccbp->hi_prtcl, ccbp->hi_break)); 139210015Speter 139310015Speter splx(oldspl); 139410015Speter return(error); 139510015Speter} 139610015Speter 139710015Speter/* 139810015Speter * Enable or Disable the writes to this channel... 139910015Speter * "state" -> enabled = 1; disabled = 0; 140010015Speter */ 140110015Speterstatic void 140256498Spetersi_write_enable(struct si_port *pp, int state) 140310015Speter{ 140410015Speter int oldspl; 140510015Speter 140610015Speter oldspl = spltty(); 140710015Speter 140810015Speter if (state) { 140910015Speter pp->sp_state &= ~SS_BLOCKWRITE; 141010015Speter if (pp->sp_state & SS_WAITWRITE) { 141110015Speter pp->sp_state &= ~SS_WAITWRITE; 141210015Speter /* thunder away! */ 1413111748Sdes wakeup(pp); 141410015Speter } 141510015Speter } else { 141610015Speter pp->sp_state |= SS_BLOCKWRITE; 141710015Speter } 141810015Speter 141910015Speter splx(oldspl); 142010015Speter} 142110015Speter 142210015Speter/* 142310015Speter * Set/Get state of modem control lines. 142410015Speter * Due to DCE-like behaviour of the adapter, some signals need translation: 142510015Speter * TIOCM_DTR DSR 142610015Speter * TIOCM_RTS CTS 142710015Speter */ 142810015Speterstatic int 142956498Spetersi_modem(struct si_port *pp, enum si_mctl cmd, int bits) 143010015Speter{ 143110015Speter volatile struct si_channel *ccbp; 143210015Speter int x; 143310015Speter 143410015Speter DPRINT((pp, DBG_ENTRY|DBG_MODEM, "si_modem(%x,%s,%x)\n", pp, si_mctl2str(cmd), bits)); 143510015Speter ccbp = pp->sp_ccb; /* Find channel address */ 143610015Speter switch (cmd) { 143710015Speter case GET: 143810015Speter x = ccbp->hi_ip; 143910015Speter bits = TIOCM_LE; 144010015Speter if (x & IP_DCD) bits |= TIOCM_CAR; 144110015Speter if (x & IP_DTR) bits |= TIOCM_DTR; 144210015Speter if (x & IP_RTS) bits |= TIOCM_RTS; 144310015Speter if (x & IP_RI) bits |= TIOCM_RI; 144410015Speter return(bits); 144510015Speter case SET: 144610015Speter ccbp->hi_op &= ~(OP_DSR|OP_CTS); 144710015Speter /* fall through */ 144810015Speter case BIS: 144910015Speter x = 0; 145010015Speter if (bits & TIOCM_DTR) 145110015Speter x |= OP_DSR; 145210015Speter if (bits & TIOCM_RTS) 145310015Speter x |= OP_CTS; 145410015Speter ccbp->hi_op |= x; 145510015Speter break; 145610015Speter case BIC: 145710015Speter if (bits & TIOCM_DTR) 145810015Speter ccbp->hi_op &= ~OP_DSR; 145910015Speter if (bits & TIOCM_RTS) 146010015Speter ccbp->hi_op &= ~OP_CTS; 146110015Speter } 146210015Speter return 0; 146310015Speter} 146410015Speter 146510015Speter/* 146610015Speter * Handle change of modem state 146710015Speter */ 146810015Speterstatic void 146956498Spetersi_modem_state(struct si_port *pp, struct tty *tp, int hi_ip) 147010015Speter{ 147110015Speter /* if a modem dev */ 147210015Speter if (hi_ip & IP_DCD) { 147350442Speter if (!(pp->sp_last_hi_ip & IP_DCD)) { 147410015Speter DPRINT((pp, DBG_INTR, "modem carr on t_line %d\n", 147510015Speter tp->t_line)); 1476130077Sphk (void)ttyld_modem(tp, 1); 147710015Speter } 147810015Speter } else { 147910015Speter if (pp->sp_last_hi_ip & IP_DCD) { 148010015Speter DPRINT((pp, DBG_INTR, "modem carr off\n")); 1481130077Sphk if (ttyld_modem(tp, 0)) 148210015Speter (void) si_modem(pp, SET, 0); 148310015Speter } 148410015Speter } 148510015Speter pp->sp_last_hi_ip = hi_ip; 148610015Speter 148710015Speter} 148810015Speter 148910015Speter/* 149010015Speter * Poller to catch missed interrupts. 149112174Speter * 149212496Speter * Note that the SYSV Specialix drivers poll at 100 times per second to get 149312496Speter * better response. We could really use a "periodic" version timeout(). :-) 149410015Speter */ 149510015Speter#ifdef POLL 149610708Speterstatic void 149710015Spetersi_poll(void *nothing) 149810015Speter{ 149956498Speter struct si_softc *sc; 150056498Speter int i; 150110015Speter volatile struct si_reg *regp; 150256498Speter struct si_port *pp; 150312174Speter int lost, oldspl, port; 150410015Speter 150510015Speter DPRINT((0, DBG_POLL, "si_poll()\n")); 150611609Speter oldspl = spltty(); 150710015Speter if (in_intr) 150810015Speter goto out; 150910015Speter lost = 0; 151056498Speter for (i = 0; i < si_numunits; i++) { 151156498Speter sc = devclass_get_softc(si_devclass, i); 151256498Speter if (sc == NULL || sc->sc_type == SIEMPTY) 151310015Speter continue; 151410015Speter regp = (struct si_reg *)sc->sc_maddr; 151534832Speter 151610015Speter /* 151710015Speter * See if there has been a pending interrupt for 2 seconds 151833395Speter * or so. The test (int_scounter >= 200) won't correspond 151910015Speter * to 2 seconds if int_count gets changed. 152010015Speter */ 152110015Speter if (regp->int_pending != 0) { 152210015Speter if (regp->int_scounter >= 200 && 152310015Speter regp->initstat == 1) { 152412174Speter printf("si%d: lost intr\n", i); 152510015Speter lost++; 152610015Speter } 152710015Speter } else { 152810015Speter regp->int_scounter = 0; 152910015Speter } 153010015Speter 153112174Speter /* 153212174Speter * gripe about no input flow control.. 153312174Speter */ 153412174Speter pp = sc->sc_ports; 153512174Speter for (port = 0; port < sc->sc_nport; pp++, port++) { 153612174Speter if (pp->sp_delta_overflows > 0) { 153712174Speter printf("si%d: %d tty level buffer overflows\n", 153812174Speter i, pp->sp_delta_overflows); 153912174Speter pp->sp_delta_overflows = 0; 154012174Speter } 154112174Speter } 154210015Speter } 154317547Speter if (lost || si_realpoll) 154456498Speter si_intr(NULL); /* call intr with fake vector */ 154511609Speterout: 154610015Speter splx(oldspl); 154710015Speter 154815639Speter timeout(si_poll, (caddr_t)0L, si_pollrate); 154910015Speter} 155010015Speter#endif /* ifdef POLL */ 155110015Speter 155210015Speter/* 155310015Speter * The interrupt handler polls ALL ports on ALL adapters each time 155410015Speter * it is called. 155510015Speter */ 155610015Speter 155712496Speterstatic BYTE si_rxbuf[SI_BUFFERSIZE]; /* input staging area */ 155834832Speterstatic BYTE si_txbuf[SI_BUFFERSIZE]; /* output staging area */ 155910015Speter 156056505Spetervoid 156156498Spetersi_intr(void *arg) 156210015Speter{ 156356498Speter struct si_softc *sc; 156456498Speter struct si_port *pp; 156510015Speter volatile struct si_channel *ccbp; 156656498Speter struct tty *tp; 156710015Speter volatile caddr_t maddr; 156811872Sphk BYTE op, ip; 156912174Speter int x, card, port, n, i, isopen; 157010015Speter volatile BYTE *z; 157110015Speter BYTE c; 157210015Speter 157356498Speter sc = arg; 157456498Speter 157556498Speter DPRINT((0, arg == NULL ? DBG_POLL:DBG_INTR, "si_intr\n")); 157656498Speter if (in_intr) 157710708Speter return; 157810015Speter in_intr = 1; 157910015Speter 158010015Speter /* 158110015Speter * When we get an int we poll all the channels and do ALL pending 158210015Speter * work, not just the first one we find. This allows all cards to 158310015Speter * share the same vector. 158434832Speter * 158534832Speter * XXX - But if we're sharing the vector with something that's NOT 158634832Speter * a SI/XIO/SX card, we may be making more work for ourselves. 158710015Speter */ 158856498Speter for (card = 0; card < si_numunits; card++) { 158956498Speter sc = devclass_get_softc(si_devclass, card); 159056498Speter if (sc == NULL || sc->sc_type == SIEMPTY) 159110015Speter continue; 159212174Speter 159312174Speter /* 159412174Speter * First, clear the interrupt 159512174Speter */ 159610015Speter switch(sc->sc_type) { 159734832Speter case SIHOST: 159810015Speter maddr = sc->sc_maddr; 159910015Speter ((volatile struct si_reg *)maddr)->int_pending = 0; 160010015Speter /* flag nothing pending */ 160110015Speter *(maddr+SIINTCL) = 0x00; /* Set IRQ clear */ 160210015Speter *(maddr+SIINTCL_CL) = 0x00; /* Clear IRQ clear */ 160310015Speter break; 160410015Speter case SIHOST2: 160510015Speter maddr = sc->sc_maddr; 160610015Speter ((volatile struct si_reg *)maddr)->int_pending = 0; 160710015Speter *(maddr+SIPLIRQCLR) = 0x00; 160810015Speter *(maddr+SIPLIRQCLR) = 0x10; 160910015Speter break; 161033395Speter case SIPCI: 161133395Speter maddr = sc->sc_maddr; 161233395Speter ((volatile struct si_reg *)maddr)->int_pending = 0; 161333395Speter *(maddr+SIPCIINTCL) = 0x0; 161433395Speter break; 161534832Speter case SIJETPCI: /* fall through to JETISA case */ 161633395Speter case SIJETISA: 161733395Speter maddr = sc->sc_maddr; 161833395Speter ((volatile struct si_reg *)maddr)->int_pending = 0; 161933395Speter *(maddr+SIJETINTCL) = 0x0; 162033395Speter break; 162110015Speter case SIEISA: 162210015Speter maddr = sc->sc_maddr; 162310015Speter ((volatile struct si_reg *)maddr)->int_pending = 0; 162456498Speter (void)inb(sc->sc_iobase + 3); 162510015Speter break; 162610015Speter case SIEMPTY: 162710015Speter default: 162810015Speter continue; 162910015Speter } 163010015Speter ((volatile struct si_reg *)maddr)->int_scounter = 0; 163110015Speter 163212174Speter /* 163312174Speter * check each port 163412174Speter */ 163550442Speter for (pp = sc->sc_ports, port = 0; port < sc->sc_nport; 163634832Speter pp++, port++) { 163710015Speter ccbp = pp->sp_ccb; 163810015Speter tp = pp->sp_tty; 163910015Speter 164010015Speter /* 164110015Speter * See if a command has completed ? 164210015Speter */ 164310015Speter if (ccbp->hi_stat != pp->sp_pend) { 164410015Speter DPRINT((pp, DBG_INTR, 164534735Speter "si_intr hi_stat = 0x%x, pend = %d\n", 164610015Speter ccbp->hi_stat, pp->sp_pend)); 164710015Speter switch(pp->sp_pend) { 164810015Speter case LOPEN: 164910015Speter case MPEND: 165010015Speter case MOPEN: 165110015Speter case CONFIG: 165216575Speter case SBREAK: 165316575Speter case EBREAK: 165410015Speter pp->sp_pend = ccbp->hi_stat; 165510015Speter /* sleeping in si_command */ 165610015Speter wakeup(&pp->sp_state); 165710015Speter break; 165810015Speter default: 165910015Speter pp->sp_pend = ccbp->hi_stat; 166010015Speter } 166134832Speter } 166210015Speter 166310015Speter /* 166410015Speter * Continue on if it's closed 166510015Speter */ 166610015Speter if (ccbp->hi_stat == IDLE_CLOSE) { 166710015Speter continue; 166810015Speter } 166910015Speter 167010015Speter /* 167110015Speter * Do modem state change if not a local device 167210015Speter */ 167310015Speter si_modem_state(pp, tp, ccbp->hi_ip); 167410015Speter 167510015Speter /* 167634832Speter * Check to see if we should 'receive' characters. 167712174Speter */ 167812174Speter if (tp->t_state & TS_CONNECTED && 167912174Speter tp->t_state & TS_ISOPEN) 168012174Speter isopen = 1; 168112174Speter else 168212174Speter isopen = 0; 168312174Speter 168412174Speter /* 168516575Speter * Do input break processing 168610015Speter */ 168710015Speter if (ccbp->hi_state & ST_BREAK) { 168812174Speter if (isopen) { 1689130077Sphk ttyld_rint(tp, TTY_BI); 169010015Speter } 169110015Speter ccbp->hi_state &= ~ST_BREAK; /* A Bit iffy this */ 169210015Speter DPRINT((pp, DBG_INTR, "si_intr break\n")); 169310015Speter } 169410015Speter 169510015Speter /* 169612174Speter * Do RX stuff - if not open then dump any characters. 169712174Speter * XXX: This is VERY messy and needs to be cleaned up. 169812174Speter * 169912174Speter * XXX: can we leave data in the host adapter buffer 170012174Speter * when the clists are full? That may be dangerous 170112174Speter * if the user cannot get an interrupt signal through. 170210015Speter */ 170310015Speter 170412174Speter more_rx: /* XXX Sorry. the nesting was driving me bats! :-( */ 170512174Speter 170612174Speter if (!isopen) { 170710015Speter ccbp->hi_rxopos = ccbp->hi_rxipos; 170812174Speter goto end_rx; 170912174Speter } 171010015Speter 171112174Speter /* 171215640Speter * If the tty input buffers are blocked, stop emptying 171315640Speter * the incoming buffers and let the auto flow control 171415640Speter * assert.. 171515640Speter */ 171615640Speter if (tp->t_state & TS_TBLOCK) { 171715640Speter goto end_rx; 171815640Speter } 171915640Speter 172015640Speter /* 172112174Speter * Process read characters if not skipped above 172212174Speter */ 172315640Speter op = ccbp->hi_rxopos; 172415640Speter ip = ccbp->hi_rxipos; 172515640Speter c = ip - op; 172612174Speter if (c == 0) { 172712174Speter goto end_rx; 172812174Speter } 172910015Speter 173012174Speter n = c & 0xff; 173115640Speter if (n > 250) 173215640Speter n = 250; 173312174Speter 173412174Speter DPRINT((pp, DBG_INTR, "n = %d, op = %d, ip = %d\n", 173510015Speter n, op, ip)); 173610015Speter 173712174Speter /* 173812174Speter * Suck characters out of host card buffer into the 173912174Speter * "input staging buffer" - so that we dont leave the 174012174Speter * host card in limbo while we're possibly echoing 174112174Speter * characters and possibly flushing input inside the 174212174Speter * ldisc l_rint() routine. 174312174Speter */ 174412496Speter if (n <= SI_BUFFERSIZE - op) { 174510015Speter 174612174Speter DPRINT((pp, DBG_INTR, "\tsingle copy\n")); 174712174Speter z = ccbp->hi_rxbuf + op; 174850442Speter si_vbcopy(z, si_rxbuf, n); 174910015Speter 175012174Speter op += n; 175112174Speter } else { 175212496Speter x = SI_BUFFERSIZE - op; 175310015Speter 175412174Speter DPRINT((pp, DBG_INTR, "\tdouble part 1 %d\n", x)); 175512174Speter z = ccbp->hi_rxbuf + op; 175650442Speter si_vbcopy(z, si_rxbuf, x); 175710015Speter 175834832Speter DPRINT((pp, DBG_INTR, "\tdouble part 2 %d\n", 175934832Speter n - x)); 176012174Speter z = ccbp->hi_rxbuf; 176150442Speter si_vbcopy(z, si_rxbuf + x, n - x); 176210015Speter 176312174Speter op += n; 176412174Speter } 176510015Speter 176612174Speter /* clear collected characters from buffer */ 176712174Speter ccbp->hi_rxopos = op; 176812174Speter 176912174Speter DPRINT((pp, DBG_INTR, "n = %d, op = %d, ip = %d\n", 177010015Speter n, op, ip)); 177110015Speter 177212174Speter /* 177312174Speter * at this point... 177412174Speter * n = number of chars placed in si_rxbuf 177512174Speter */ 177610015Speter 177712174Speter /* 177812174Speter * Avoid the grotesquely inefficient lineswitch 177912174Speter * routine (ttyinput) in "raw" mode. It usually 178012174Speter * takes about 450 instructions (that's without 178112174Speter * canonical processing or echo!). slinput is 178212174Speter * reasonably fast (usually 40 instructions 178312174Speter * plus call overhead). 178412174Speter */ 178512174Speter if (tp->t_state & TS_CAN_BYPASS_L_RINT) { 178610015Speter 178712174Speter /* block if the driver supports it */ 178850442Speter if (tp->t_rawq.c_cc + n >= SI_I_HIGH_WATER && 178950442Speter (tp->t_cflag & CRTS_IFLOW || 179050442Speter tp->t_iflag & IXOFF) && 179150442Speter !(tp->t_state & TS_TBLOCK)) 179212174Speter ttyblock(tp); 179310015Speter 179412174Speter tk_nin += n; 179512174Speter tk_rawcc += n; 179612174Speter tp->t_rawcc += n; 179712174Speter 179812174Speter pp->sp_delta_overflows += 179912174Speter b_to_q((char *)si_rxbuf, n, &tp->t_rawq); 180012174Speter 180112174Speter ttwakeup(tp); 180250442Speter if (tp->t_state & TS_TTSTOP && 180350442Speter (tp->t_iflag & IXANY || 180450442Speter tp->t_cc[VSTART] == tp->t_cc[VSTOP])) { 180512174Speter tp->t_state &= ~TS_TTSTOP; 180612174Speter tp->t_lflag &= ~FLUSHO; 180712174Speter si_start(tp); 180812174Speter } 180912174Speter } else { 181012174Speter /* 181112174Speter * It'd be nice to not have to go through the 181212174Speter * function call overhead for each char here. 181312174Speter * It'd be nice to block input it, saving a 181412174Speter * loop here and the call/return overhead. 181512174Speter */ 181612174Speter for(x = 0; x < n; x++) { 181712174Speter i = si_rxbuf[x]; 1818130077Sphk if (ttyld_rint(tp, i) 181912174Speter == -1) { 182012174Speter pp->sp_delta_overflows++; 182110015Speter } 182212174Speter } 182312174Speter } 182412174Speter goto more_rx; /* try for more until RXbuf is empty */ 182510015Speter 182612174Speter end_rx: /* XXX: Again, sorry about the gotos.. :-) */ 182710015Speter 182810015Speter /* 182910015Speter * Do TX stuff 183010015Speter */ 1831130077Sphk ttyld_start(tp); 183210015Speter 183310015Speter } /* end of for (all ports on this controller) */ 183410015Speter } /* end of for (all controllers) */ 183510015Speter 183611609Speter in_intr = 0; 183756498Speter DPRINT((0, arg == NULL ? DBG_POLL:DBG_INTR, "end si_intr\n")); 183810015Speter} 183910015Speter 184010015Speter/* 184110015Speter * Nudge the transmitter... 184212174Speter * 184312174Speter * XXX: I inherited some funny code here. It implies the host card only 184412174Speter * interrupts when the transmit buffer reaches the low-water-mark, and does 184512174Speter * not interrupt when it's actually hits empty. In some cases, we have 184612174Speter * processes waiting for complete drain, and we need to simulate an interrupt 184712174Speter * about when we think the buffer is going to be empty (and retry if not). 184812174Speter * I really am not certain about this... I *need* the hardware manuals. 184910015Speter */ 185010015Speterstatic void 185156498Spetersi_start(struct tty *tp) 185210015Speter{ 185310015Speter struct si_port *pp; 185410015Speter volatile struct si_channel *ccbp; 185556498Speter struct clist *qp; 185610015Speter BYTE ipos; 185710015Speter int nchar; 185810015Speter int oldspl, count, n, amount, buffer_full; 185910015Speter 186010015Speter oldspl = spltty(); 186110015Speter 186210015Speter qp = &tp->t_outq; 186310015Speter pp = TP2PP(tp); 186410015Speter 186510015Speter DPRINT((pp, DBG_ENTRY|DBG_START, 186610015Speter "si_start(%x) t_state %x sp_state %x t_outq.c_cc %d\n", 186710015Speter tp, tp->t_state, pp->sp_state, qp->c_cc)); 186810015Speter 186910015Speter if (tp->t_state & (TS_TIMEOUT|TS_TTSTOP)) 187010015Speter goto out; 187110015Speter 187210015Speter buffer_full = 0; 187310015Speter ccbp = pp->sp_ccb; 187410015Speter 187510015Speter count = (int)ccbp->hi_txipos - (int)ccbp->hi_txopos; 187610015Speter DPRINT((pp, DBG_START, "count %d\n", (BYTE)count)); 187710015Speter 187810015Speter while ((nchar = qp->c_cc) > 0) { 187910015Speter if ((BYTE)count >= 255) { 188010015Speter buffer_full++; 188110015Speter break; 188210015Speter } 188310015Speter amount = min(nchar, (255 - (BYTE)count)); 188410015Speter ipos = (unsigned int)ccbp->hi_txipos; 188534832Speter n = q_to_b(&tp->t_outq, si_txbuf, amount); 188610015Speter /* will it fit in one lump? */ 188734832Speter if ((SI_BUFFERSIZE - ipos) >= n) { 188850442Speter si_bcopyv(si_txbuf, &ccbp->hi_txbuf[ipos], n); 188910015Speter } else { 189050442Speter si_bcopyv(si_txbuf, &ccbp->hi_txbuf[ipos], 189134832Speter SI_BUFFERSIZE - ipos); 189250442Speter si_bcopyv(si_txbuf + (SI_BUFFERSIZE - ipos), 189350442Speter &ccbp->hi_txbuf[0], n - (SI_BUFFERSIZE - ipos)); 189410015Speter } 189510015Speter ccbp->hi_txipos += n; 189610015Speter count = (int)ccbp->hi_txipos - (int)ccbp->hi_txopos; 189710015Speter } 189810015Speter 189910015Speter if (count != 0 && nchar == 0) { 190010015Speter tp->t_state |= TS_BUSY; 190110015Speter } else { 190210015Speter tp->t_state &= ~TS_BUSY; 190310015Speter } 190410015Speter 190510015Speter /* wakeup time? */ 190610015Speter ttwwakeup(tp); 190710015Speter 190810015Speter DPRINT((pp, DBG_START, "count %d, nchar %d, tp->t_state 0x%x\n", 190910015Speter (BYTE)count, nchar, tp->t_state)); 191010015Speter 191134735Speter if (tp->t_state & TS_BUSY) 191210015Speter { 191310015Speter int time; 191410015Speter 191534735Speter time = ttspeedtab(tp->t_ospeed, chartimes); 191634735Speter 191734735Speter if (time > 0) { 191834735Speter if (time < nchar) 191934735Speter time = nchar / time; 192034735Speter else 192134735Speter time = 2; 192210015Speter } else { 192334735Speter DPRINT((pp, DBG_START, 192434735Speter "bad char time value! %d\n", time)); 192534735Speter time = hz/10; 192610015Speter } 192710015Speter 192810015Speter if ((pp->sp_state & (SS_LSTART|SS_INLSTART)) == SS_LSTART) { 192929677Sgibbs untimeout(si_lstart, (caddr_t)pp, pp->lstart_ch); 193010015Speter } else { 193110015Speter pp->sp_state |= SS_LSTART; 193210015Speter } 193310015Speter DPRINT((pp, DBG_START, "arming lstart, time=%d\n", time)); 193429677Sgibbs pp->lstart_ch = timeout(si_lstart, (caddr_t)pp, time); 193510015Speter } 193610015Speter 193710015Speterout: 193810015Speter splx(oldspl); 193910015Speter DPRINT((pp, DBG_EXIT|DBG_START, "leave si_start()\n")); 194010015Speter} 194110015Speter 194210015Speter/* 194310015Speter * Note: called at splsoftclock from the timeout code 194410015Speter * This has to deal with two things... cause wakeups while waiting for 194510015Speter * tty drains on last process exit, and call l_start at about the right 194610015Speter * time for protocols like ppp. 194710015Speter */ 194810015Speterstatic void 194925047Sbdesi_lstart(void *arg) 195010015Speter{ 195156498Speter struct si_port *pp = arg; 195256498Speter struct tty *tp; 195310015Speter int oldspl; 195410015Speter 195510015Speter DPRINT((pp, DBG_ENTRY|DBG_LSTART, "si_lstart(%x) sp_state %x\n", 195610015Speter pp, pp->sp_state)); 195710015Speter 195810015Speter oldspl = spltty(); 195910015Speter 196010015Speter if ((pp->sp_state & SS_OPEN) == 0 || (pp->sp_state & SS_LSTART) == 0) { 196110015Speter splx(oldspl); 196210015Speter return; 196310015Speter } 196410015Speter pp->sp_state &= ~SS_LSTART; 196510015Speter pp->sp_state |= SS_INLSTART; 196610015Speter 196710015Speter tp = pp->sp_tty; 196810015Speter 196910015Speter /* deal with the process exit case */ 197010015Speter ttwwakeup(tp); 197110015Speter 197212174Speter /* nudge protocols - eg: ppp */ 1973130077Sphk ttyld_start(tp); 197410015Speter 197510015Speter pp->sp_state &= ~SS_INLSTART; 197610015Speter splx(oldspl); 197710015Speter} 197810015Speter 197910015Speter/* 198010015Speter * Stop output on a line. called at spltty(); 198110015Speter */ 1982105215Sphkstatic void 198356498Spetersi_stop(struct tty *tp, int rw) 198410015Speter{ 198510015Speter volatile struct si_channel *ccbp; 198610015Speter struct si_port *pp; 198710015Speter 198810015Speter pp = TP2PP(tp); 198910015Speter ccbp = pp->sp_ccb; 199010015Speter 199151654Sphk DPRINT((TP2PP(tp), DBG_ENTRY|DBG_STOP, "si_stop(%x,%x)\n", tp, rw)); 199210015Speter 199310015Speter /* XXX: must check (rw & FWRITE | FREAD) etc flushing... */ 199410015Speter if (rw & FWRITE) { 199510015Speter /* what level are we meant to be flushing anyway? */ 199610015Speter if (tp->t_state & TS_BUSY) { 199710015Speter si_command(TP2PP(tp), WFLUSH, SI_NOWAIT); 199810015Speter tp->t_state &= ~TS_BUSY; 199910015Speter ttwwakeup(tp); /* Bruce???? */ 200010015Speter } 200110015Speter } 200212174Speter#if 1 /* XXX: this doesn't work right yet.. */ 200312174Speter /* XXX: this may have been failing because we used to call l_rint() 200412174Speter * while we were looping based on these two counters. Now, we collect 200512174Speter * the data and then loop stuffing it into l_rint(), making this 200612174Speter * useless. Should we cause this to blow away the staging buffer? 200712174Speter */ 200810015Speter if (rw & FREAD) { 200910015Speter ccbp->hi_rxopos = ccbp->hi_rxipos; 201010015Speter } 201110015Speter#endif 201210015Speter} 201310015Speter 201410015Speter/* 201534832Speter * Issue a command to the host card CPU. 201610015Speter */ 201710015Speter 201810015Speterstatic void 201956498Spetersi_command(struct si_port *pp, int cmd, int waitflag) 202010015Speter{ 202110015Speter int oldspl; 202210015Speter volatile struct si_channel *ccbp = pp->sp_ccb; 202310015Speter int x; 202410015Speter 202510015Speter DPRINT((pp, DBG_ENTRY|DBG_PARAM, "si_command(%x,%x,%d): hi_stat 0x%x\n", 202610015Speter pp, cmd, waitflag, ccbp->hi_stat)); 202710015Speter 202810015Speter oldspl = spltty(); /* Keep others out */ 202910015Speter 203010015Speter /* wait until it's finished what it was doing.. */ 203116575Speter /* XXX: sits in IDLE_BREAK until something disturbs it or break 203216575Speter * is turned off. */ 203310015Speter while((x = ccbp->hi_stat) != IDLE_OPEN && 203410015Speter x != IDLE_CLOSE && 203516575Speter x != IDLE_BREAK && 203610015Speter x != cmd) { 203710015Speter if (in_intr) { /* Prevent sleep in intr */ 203810015Speter DPRINT((pp, DBG_PARAM, 203910015Speter "cmd intr collision - completing %d\trequested %d\n", 204010015Speter x, cmd)); 204110015Speter splx(oldspl); 204210015Speter return; 204310015Speter } else if (ttysleep(pp->sp_tty, (caddr_t)&pp->sp_state, TTIPRI|PCATCH, 204410015Speter "sicmd1", 1)) { 204510015Speter splx(oldspl); 204610015Speter return; 204710015Speter } 204810015Speter } 204916575Speter /* it should now be in IDLE_{OPEN|CLOSE|BREAK}, or "cmd" */ 205010015Speter 205110015Speter /* if there was a pending command, cause a state-change wakeup */ 205216575Speter switch(pp->sp_pend) { 205316575Speter case LOPEN: 205416575Speter case MPEND: 205516575Speter case MOPEN: 205616575Speter case CONFIG: 205716575Speter case SBREAK: 205816575Speter case EBREAK: 205916575Speter wakeup(&pp->sp_state); 206016575Speter break; 206116575Speter default: 206216575Speter break; 206310015Speter } 206410015Speter 206510015Speter pp->sp_pend = cmd; /* New command pending */ 206610015Speter ccbp->hi_stat = cmd; /* Post it */ 206710015Speter 206810015Speter if (waitflag) { 206910015Speter if (in_intr) { /* If in interrupt handler */ 207010015Speter DPRINT((pp, DBG_PARAM, 207110015Speter "attempt to sleep in si_intr - cmd req %d\n", 207210015Speter cmd)); 207310015Speter splx(oldspl); 207410015Speter return; 207516575Speter } else while(ccbp->hi_stat != IDLE_OPEN && 207616575Speter ccbp->hi_stat != IDLE_BREAK) { 207710015Speter if (ttysleep(pp->sp_tty, (caddr_t)&pp->sp_state, TTIPRI|PCATCH, 207810015Speter "sicmd2", 0)) 207910015Speter break; 208010015Speter } 208110015Speter } 208210015Speter splx(oldspl); 208310015Speter} 208410015Speter 208510015Speter 208610015Speter#ifdef SI_DEBUG 208713353Speter 208856505Spetervoid 208913353Spetersi_dprintf(struct si_port *pp, int flags, const char *fmt, ...) 209010015Speter{ 209113353Speter va_list ap; 209213630Sphk 209310015Speter if ((pp == NULL && (si_debug&flags)) || 209410015Speter (pp != NULL && ((pp->sp_debug&flags) || (si_debug&flags)))) { 209534832Speter if (pp != NULL) 209634832Speter printf("%ci%d(%d): ", 's', 209746679Sphk (int)SI_CARD(minor(pp->sp_tty->t_dev)), 209846679Sphk (int)SI_PORT(minor(pp->sp_tty->t_dev))); 209913630Sphk va_start(ap, fmt); 210013630Sphk vprintf(fmt, ap); 210113353Speter va_end(ap); 210210015Speter } 210310015Speter} 210410015Speter 210510015Speterstatic char * 210656498Spetersi_mctl2str(enum si_mctl cmd) 210710015Speter{ 210810015Speter switch (cmd) { 210934832Speter case GET: 211034832Speter return("GET"); 211134832Speter case SET: 211234832Speter return("SET"); 211334832Speter case BIS: 211434832Speter return("BIS"); 211534832Speter case BIC: 211634832Speter return("BIC"); 211710015Speter } 211810015Speter return("BAD"); 211910015Speter} 212012502Sjulian 212112624Speter#endif /* DEBUG */ 212212502Sjulian 212334832Speterstatic char * 212456498Spetersi_modulename(int host_type, int uart_type) 212534832Speter{ 212634832Speter switch (host_type) { 212734832Speter /* Z280 based cards */ 212850442Speter case SIEISA: 212950442Speter case SIHOST2: 213034832Speter case SIHOST: 213134832Speter case SIPCI: 213234832Speter switch (uart_type) { 213334832Speter case 0: 213434832Speter return(" (XIO)"); 213534832Speter case 1: 213634832Speter return(" (SI)"); 213734832Speter } 213834832Speter break; 213934832Speter /* T225 based hosts */ 214034832Speter case SIJETPCI: 214134832Speter case SIJETISA: 214234832Speter switch (uart_type) { 214334832Speter case 0: 214434832Speter return(" (SI)"); 214534832Speter case 40: 214634832Speter return(" (XIO)"); 214736856Sphk case 72: 214836856Sphk return(" (SXDC)"); 214934832Speter } 215034832Speter break; 215134832Speter } 215234832Speter return(""); 215334832Speter} 2154