si.c revision 166091
1139749Simp/*- 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 166091 2007-01-18 13:33:36Z marius $"); 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" 46166091Smarius#include "opt_eisa.h" 47111899Sdas#include "opt_tty.h" 4831778Seivind 4910015Speter#include <sys/param.h> 5010015Speter#include <sys/systm.h> 51136058Sphk#include <sys/serial.h> 5210015Speter#include <sys/tty.h> 5310015Speter#include <sys/conf.h> 5424131Sbde#include <sys/fcntl.h> 5510015Speter#include <sys/kernel.h> 5610015Speter#include <sys/malloc.h> 57164033Srwatson#include <sys/priv.h> 5815683Speter#include <sys/sysctl.h> 5956498Speter#include <sys/bus.h> 6056498Speter#include <machine/bus.h> 6156498Speter#include <sys/rman.h> 6256498Speter#include <machine/resource.h> 6310015Speter 6410015Speter 6512659Sbde#include <vm/vm.h> 6612662Sdg#include <vm/pmap.h> 6712659Sbde 6813353Speter#include <machine/stdarg.h> 6910015Speter 7056498Speter#include <dev/si/sireg.h> 7156505Speter#include <dev/si/sivar.h> 7256498Speter#include <dev/si/si.h> 7356498Speter 7410015Speter/* 7510015Speter * This device driver is designed to interface the Specialix International 7634832Speter * SI, XIO and SX range of serial multiplexor cards to FreeBSD on an ISA, 7734832Speter * EISA or PCI bus machine. 7810015Speter * 7934832Speter * The controller is interfaced to the host via dual port RAM 8034832Speter * and an interrupt. 8133395Speter * 8234832Speter * The code for the Host 1 (very old ISA cards) has not been tested. 8310015Speter */ 8410015Speter 8517547Speter#define POLL /* turn on poller to scan for lost interrupts */ 8617547Speter#define REALPOLL /* on each poll, scan for work regardless */ 8717547Speter#define POLLHZ (hz/10) /* 10 times per second */ 8812496Speter#define SI_I_HIGH_WATER (TTYHOG - 2 * SI_BUFFERSIZE) 8934832Speter#define INT_COUNT 25000 /* max of 125 ints per second */ 9034832Speter#define JET_INT_COUNT 100 /* max of 100 ints per second */ 9115639Speter#define RXINT_COUNT 1 /* one rxint per 10 milliseconds */ 9210015Speter 9356498Speterstatic void si_command(struct si_port *, int, int); 94130585Sphkstatic int si_Sioctl(struct cdev *, u_long, caddr_t, int, struct thread *); 9556498Speterstatic void si_start(struct tty *); 9656498Speterstatic void si_stop(struct tty *, int); 9725047Sbdestatic timeout_t si_lstart; 9810015Speter 99136058Sphkstatic t_break_t sibreak; 100136058Sphkstatic t_close_t siclose; 101136058Sphkstatic t_modem_t simodem; 102136058Sphkstatic t_open_t siopen; 10356505Speter 10456498Speterstatic int siparam(struct tty *, struct termios *); 10510015Speter 10656498Speterstatic void si_modem_state(struct si_port *pp, struct tty *tp, int hi_ip); 10756498Speterstatic char * si_modulename(int host_type, int uart_type); 10810708Speter 109136058Sphkstatic struct cdevsw si_Scdevsw = { 110126080Sphk .d_version = D_VERSION, 111136058Sphk .d_ioctl = si_Sioctl, 112111815Sphk .d_name = "si", 113126080Sphk .d_flags = D_TTY | D_NEEDGIANT, 11438485Sbde}; 11512675Sjulian 11610962Speterstatic int si_Nports; 11710962Speterstatic int si_Nmodules; 11810962Speterstatic int si_debug = 0; /* data, not bss, so it's patchable */ 11910015Speter 12034832SpeterSYSCTL_INT(_machdep, OID_AUTO, si_debug, CTLFLAG_RW, &si_debug, 0, ""); 121100743SpeterTUNABLE_INT("machdep.si_debug", &si_debug); 12234832Speter 12356498Speterstatic int si_numunits; 12410044Speter 12556505Speterdevclass_t si_devclass; 12656498Speter 12712174Speter#ifndef B2000 /* not standard, but the hardware knows it. */ 12810015Speter# define B2000 2000 12910015Speter#endif 13010015Speterstatic struct speedtab bdrates[] = { 13150442Speter { B75, CLK75, }, /* 0x0 */ 13250442Speter { B110, CLK110, }, /* 0x1 */ 13350442Speter { B150, CLK150, }, /* 0x3 */ 13450442Speter { B300, CLK300, }, /* 0x4 */ 13550442Speter { B600, CLK600, }, /* 0x5 */ 13650442Speter { B1200, CLK1200, }, /* 0x6 */ 13750442Speter { B2000, CLK2000, }, /* 0x7 */ 13850442Speter { B2400, CLK2400, }, /* 0x8 */ 13950442Speter { B4800, CLK4800, }, /* 0x9 */ 14050442Speter { B9600, CLK9600, }, /* 0xb */ 14150442Speter { B19200, CLK19200, }, /* 0xc */ 14250442Speter { B38400, CLK38400, }, /* 0x2 (out of order!) */ 14350442Speter { B57600, CLK57600, }, /* 0xd */ 14450442Speter { B115200, CLK110, }, /* 0x1 (dupe!, 110 baud on "si") */ 14550442Speter { -1, -1 }, 14610015Speter}; 14710015Speter 14810015Speter 14910015Speter/* populated with approx character/sec rates - translated at card 15010015Speter * initialisation time to chars per tick of the clock */ 15110015Speterstatic int done_chartimes = 0; 15210015Speterstatic struct speedtab chartimes[] = { 15350442Speter { B75, 8, }, 15450442Speter { B110, 11, }, 15550442Speter { B150, 15, }, 15650442Speter { B300, 30, }, 15750442Speter { B600, 60, }, 15850442Speter { B1200, 120, }, 15950442Speter { B2000, 200, }, 16050442Speter { B2400, 240, }, 16150442Speter { B4800, 480, }, 16250442Speter { B9600, 960, }, 16350442Speter { B19200, 1920, }, 16450442Speter { B38400, 3840, }, 16550442Speter { B57600, 5760, }, 16650442Speter { B115200, 11520, }, 16750442Speter { -1, -1 }, 16810015Speter}; 16910015Speterstatic volatile int in_intr = 0; /* Inside interrupt handler? */ 17010015Speter 17110015Speter#ifdef POLL 17215683Speterstatic int si_pollrate; /* in addition to irq */ 17356498Speterstatic int si_realpoll = 0; /* poll HW on timer */ 17415639Speter 17516403SpeterSYSCTL_INT(_machdep, OID_AUTO, si_pollrate, CTLFLAG_RW, &si_pollrate, 0, ""); 17617547SpeterSYSCTL_INT(_machdep, OID_AUTO, si_realpoll, CTLFLAG_RW, &si_realpoll, 0, ""); 17750442Speter 17810015Speterstatic int init_finished = 0; 17956498Speterstatic void si_poll(void *); 18010015Speter#endif 18110015Speter 18210015Speter/* 18310015Speter * Array of adapter types and the corresponding RAM size. The order of 18410015Speter * entries here MUST match the ordinal of the adapter type. 18510015Speter */ 186136058Sphkstatic const char *si_type[] = { 18710015Speter "EMPTY", 18810015Speter "SIHOST", 18934832Speter "SIMCA", /* FreeBSD does not support Microchannel */ 19010015Speter "SIHOST2", 19110015Speter "SIEISA", 19233395Speter "SIPCI", 19333395Speter "SXPCI", 19433395Speter "SXISA", 19510015Speter}; 19610015Speter 19756498Speter/* 19856498Speter * We have to make an 8 bit version of bcopy, since some cards can't 19956498Speter * deal with 32 bit I/O 20056498Speter */ 20156498Speterstatic void __inline 20256498Spetersi_bcopy(const void *src, void *dst, size_t len) 20356498Speter{ 204132771Skan u_char *d; 205132771Skan const u_char *s; 206132771Skan 207132771Skan d = dst; 208132771Skan s = src; 20956498Speter while (len--) 210132771Skan *d++ = *s++; 21156498Speter} 21256498Speterstatic void __inline 21356498Spetersi_vbcopy(const volatile void *src, void *dst, size_t len) 21456498Speter{ 215132771Skan u_char *d; 216132771Skan const volatile u_char *s; 217132771Skan 218132771Skan d = dst; 219132771Skan s = src; 22056498Speter while (len--) 221132771Skan *d++ = *s++; 22256498Speter} 22356498Speterstatic void __inline 22456498Spetersi_bcopyv(const void *src, volatile void *dst, size_t len) 22556498Speter{ 226132771Skan volatile u_char *d; 227132771Skan const u_char *s; 228132771Skan 229132771Skan d = dst; 230132771Skan s = src; 23156498Speter while (len--) 232132771Skan *d++ = *s++; 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; 244136058Sphk struct tty *tp; 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) { 281166091Smarius#ifdef DEV_EISA 28234832Speter case SIEISA: 28356498Speter outb(sc->sc_iobase + 2, sc->sc_irq << 4); 284166091Smarius#endif 28534832Speter break; 28634832Speter case SIPCI: 28734832Speter *(maddr+SIPCIRESET) = 0; 28834832Speter break; 28934832Speter case SIJETPCI: /* fall through to JET ISA */ 29034832Speter case SIJETISA: 29134832Speter *(maddr+SIJETCONFIG) = 0; 29234832Speter break; 29334832Speter case SIHOST2: 29434832Speter *(maddr+SIPLRESET) = 0; 29534832Speter break; 29634832Speter case SIHOST: 29734832Speter *(maddr+SIRESET) = 0; 29834832Speter break; 29934832Speter default: /* this should never happen */ 30034832Speter printf("si%d: unsupported configuration\n", unit); 30156498Speter return EINVAL; 30234832Speter break; 30334832Speter } 30434832Speter 30534832Speter /* OK, now lets download the download code */ 30634832Speter 30736956Ssteve if (SI_ISJET(sc->sc_type)) { 30833395Speter DPRINT((0, DBG_DOWNLOAD, "si%d: jet_download: nbytes %d\n", 30956498Speter unit, si3_t225_dsize)); 31034832Speter si_bcopy(si3_t225_download, maddr + si3_t225_downloadaddr, 31134832Speter si3_t225_dsize); 31234832Speter DPRINT((0, DBG_DOWNLOAD, 31334832Speter "si%d: jet_bootstrap: nbytes %d -> %x\n", 31456498Speter unit, si3_t225_bsize, si3_t225_bootloadaddr)); 31534832Speter si_bcopy(si3_t225_bootstrap, maddr + si3_t225_bootloadaddr, 31634832Speter si3_t225_bsize); 31734832Speter } else { 31833395Speter DPRINT((0, DBG_DOWNLOAD, "si%d: si_download: nbytes %d\n", 31956498Speter unit, si2_z280_dsize)); 32034832Speter si_bcopy(si2_z280_download, maddr + si2_z280_downloadaddr, 32134832Speter si2_z280_dsize); 32233395Speter } 32310015Speter 32434832Speter /* Now start the CPU */ 32534832Speter 32610015Speter switch (sc->sc_type) { 327166091Smarius#ifdef DEV_EISA 32810015Speter case SIEISA: 32934832Speter /* modify the download code to tell it that it's on an EISA */ 33034832Speter *(maddr + 0x42) = 1; 33156498Speter outb(sc->sc_iobase + 2, (sc->sc_irq << 4) | 4); 33256498Speter (void)inb(sc->sc_iobase + 3); /* reset interrupt */ 33310015Speter break; 334166091Smarius#endif 33533395Speter case SIPCI: 33634832Speter /* modify the download code to tell it that it's on a PCI */ 33733395Speter *(maddr+0x42) = 1; 33833395Speter *(maddr+SIPCIRESET) = 1; 33933395Speter *(maddr+SIPCIINTCL) = 0; 34033395Speter break; 34133395Speter case SIJETPCI: 34233395Speter *(maddr+SIJETRESET) = 0; 34333395Speter *(maddr+SIJETCONFIG) = SIJETBUSEN|SIJETIRQEN; 34433395Speter break; 34533395Speter case SIJETISA: 34633395Speter *(maddr+SIJETRESET) = 0; 34734832Speter switch (sc->sc_irq) { 34856498Speter case 9: 34934832Speter *(maddr+SIJETCONFIG) = SIJETBUSEN|SIJETIRQEN|0x90; 35034832Speter break; 35156498Speter case 10: 35234832Speter *(maddr+SIJETCONFIG) = SIJETBUSEN|SIJETIRQEN|0xa0; 35334832Speter break; 35456498Speter case 11: 35534832Speter *(maddr+SIJETCONFIG) = SIJETBUSEN|SIJETIRQEN|0xb0; 35634832Speter break; 35756498Speter case 12: 35834832Speter *(maddr+SIJETCONFIG) = SIJETBUSEN|SIJETIRQEN|0xc0; 35934832Speter break; 36056498Speter case 15: 36134832Speter *(maddr+SIJETCONFIG) = SIJETBUSEN|SIJETIRQEN|0xf0; 36234832Speter break; 36334832Speter } 36433395Speter break; 36510015Speter case SIHOST: 36610015Speter *(maddr+SIRESET_CL) = 0; 36710015Speter *(maddr+SIINTCL_CL) = 0; 36810015Speter break; 36910015Speter case SIHOST2: 37010015Speter *(maddr+SIPLRESET) = 0x10; 37110015Speter switch (sc->sc_irq) { 37256498Speter case 11: 37310015Speter *(maddr+SIPLIRQ11) = 0x10; 37410015Speter break; 37556498Speter case 12: 37610015Speter *(maddr+SIPLIRQ12) = 0x10; 37710015Speter break; 37856498Speter case 15: 37910015Speter *(maddr+SIPLIRQ15) = 0x10; 38010015Speter break; 38110015Speter } 38210015Speter *(maddr+SIPLIRQCLR) = 0x10; 38310015Speter break; 38434832Speter default: /* this should _REALLY_ never happen */ 38534832Speter printf("si%d: Uh, it was supported a second ago...\n", unit); 38656498Speter return EINVAL; 38710015Speter } 38810015Speter 38910015Speter DELAY(1000000); /* wait around for a second */ 39010015Speter 39110015Speter regp = (struct si_reg *)maddr; 39210015Speter y = 0; 39310015Speter /* wait max of 5 sec for init OK */ 39410015Speter while (regp->initstat == 0 && y++ < 10) { 39510015Speter DELAY(500000); 39610015Speter } 39710015Speter switch (regp->initstat) { 39810015Speter case 0: 39910015Speter printf("si%d: startup timeout - aborting\n", unit); 40012174Speter sc->sc_type = SIEMPTY; 40156498Speter return EINVAL; 40210015Speter case 1: 40336956Ssteve if (SI_ISJET(sc->sc_type)) { 40434832Speter /* set throttle to 100 times per second */ 40534832Speter regp->int_count = JET_INT_COUNT; 40634832Speter /* rx_intr_count is a NOP in Jet */ 40734832Speter } else { 40834832Speter /* set throttle to 125 times per second */ 40934832Speter regp->int_count = INT_COUNT; 41034832Speter /* rx intr max of 25 times per second */ 41134832Speter regp->rx_int_count = RXINT_COUNT; 41234832Speter } 41310015Speter regp->int_pending = 0; /* no intr pending */ 41410015Speter regp->int_scounter = 0; /* reset counter */ 41510015Speter break; 41610015Speter case 0xff: 41710015Speter /* 41810015Speter * No modules found, so give up on this one. 41910015Speter */ 42010015Speter printf("si%d: %s - no ports found\n", unit, 42110015Speter si_type[sc->sc_type]); 42210015Speter return 0; 42310015Speter default: 42434832Speter printf("si%d: download code version error - initstat %x\n", 42510015Speter unit, regp->initstat); 42656498Speter return EINVAL; 42710015Speter } 42810015Speter 42910015Speter /* 43010015Speter * First time around the ports just count them in order 43110015Speter * to allocate some memory. 43210015Speter */ 43310015Speter nport = 0; 43410015Speter modp = (struct si_module *)(maddr + 0x80); 43510015Speter for (;;) { 43612174Speter DPRINT((0, DBG_DOWNLOAD, "si%d: ccb addr 0x%x\n", unit, modp)); 43734832Speter switch (modp->sm_type) { 43834832Speter case TA4: 43910015Speter DPRINT((0, DBG_DOWNLOAD, 44034832Speter "si%d: Found old TA4 module, 4 ports\n", 44134832Speter unit)); 44234832Speter x = 4; 44310015Speter break; 44434832Speter case TA8: 44534832Speter DPRINT((0, DBG_DOWNLOAD, 44634832Speter "si%d: Found old TA8 module, 8 ports\n", 44734832Speter unit)); 44834832Speter x = 8; 44934832Speter break; 45034832Speter case TA4_ASIC: 45134832Speter DPRINT((0, DBG_DOWNLOAD, 45234832Speter "si%d: Found ASIC TA4 module, 4 ports\n", 45334832Speter unit)); 45434832Speter x = 4; 45534832Speter break; 45634832Speter case TA8_ASIC: 45734832Speter DPRINT((0, DBG_DOWNLOAD, 45834832Speter "si%d: Found ASIC TA8 module, 8 ports\n", 45934832Speter unit)); 46034832Speter x = 8; 46134832Speter break; 46234832Speter case MTA: 46334832Speter DPRINT((0, DBG_DOWNLOAD, 46434832Speter "si%d: Found CD1400 module, 8 ports\n", 46534832Speter unit)); 46634832Speter x = 8; 46734832Speter break; 46834832Speter case SXDC: 46934832Speter DPRINT((0, DBG_DOWNLOAD, 47034832Speter "si%d: Found SXDC module, 8 ports\n", 47134832Speter unit)); 47234832Speter x = 8; 47334832Speter break; 47410015Speter default: 47510015Speter printf("si%d: unknown module type %d\n", 47610015Speter unit, modp->sm_type); 47734832Speter goto try_next; 47810015Speter } 47934832Speter 48034832Speter /* this was limited in firmware and is also a driver issue */ 48134832Speter if ((nport + x) > SI_MAXPORTPERCARD) { 48234832Speter printf("si%d: extra ports ignored\n", unit); 48334832Speter goto try_next; 48434832Speter } 48534832Speter 48634832Speter nport += x; 48734832Speter si_Nports += x; 48834832Speter si_Nmodules++; 48934832Speter 49034832Spetertry_next: 49110015Speter if (modp->sm_next == 0) 49210015Speter break; 49310015Speter modp = (struct si_module *) 49410015Speter (maddr + (unsigned)(modp->sm_next & 0x7fff)); 49510015Speter } 49610015Speter sc->sc_ports = (struct si_port *)malloc(sizeof(struct si_port) * nport, 49769781Sdwmalone M_DEVBUF, M_NOWAIT | M_ZERO); 49810015Speter if (sc->sc_ports == 0) { 49910015Speter printf("si%d: fail to malloc memory for port structs\n", 50010015Speter unit); 50156498Speter return EINVAL; 50210015Speter } 50310015Speter sc->sc_nport = nport; 50410015Speter 50510015Speter /* 50610015Speter * Scan round the ports again, this time initialising. 50710015Speter */ 50810015Speter pp = sc->sc_ports; 50910015Speter nmodule = 0; 51010015Speter modp = (struct si_module *)(maddr + 0x80); 51134832Speter uart_type = 1000; /* arbitary, > uchar_max */ 51210015Speter for (;;) { 51334832Speter switch (modp->sm_type) { 51434832Speter case TA4: 51534832Speter nport = 4; 51610015Speter break; 51734832Speter case TA8: 51834832Speter nport = 8; 51934832Speter break; 52034832Speter case TA4_ASIC: 52134832Speter nport = 4; 52234832Speter break; 52334832Speter case TA8_ASIC: 52434832Speter nport = 8; 52534832Speter break; 52634832Speter case MTA: 52734832Speter nport = 8; 52834832Speter break; 52934832Speter case SXDC: 53034832Speter nport = 8; 53134832Speter break; 53210015Speter default: 53334832Speter goto try_next2; 53410015Speter } 53534832Speter nmodule++; 53634832Speter ccbp = (struct si_channel *)((char *)modp + 0x100); 53734832Speter if (uart_type == 1000) 53834832Speter uart_type = ccbp->type; 53934832Speter else if (uart_type != ccbp->type) 54034832Speter printf("si%d: Warning: module %d mismatch! (%d%s != %d%s)\n", 54134832Speter unit, nmodule, 54234832Speter ccbp->type, si_modulename(sc->sc_type, ccbp->type), 54334832Speter uart_type, si_modulename(sc->sc_type, uart_type)); 54434832Speter 54534832Speter for (x = 0; x < nport; x++, pp++, ccbp++) { 54634832Speter pp->sp_ccb = ccbp; /* save the address */ 54734832Speter pp->sp_pend = IDLE_CLOSE; 54834832Speter pp->sp_state = 0; /* internal flag */ 549136058Sphk#ifdef SI_DEBUG 550154081Sjhb sprintf(pp->sp_name, "si%r%r", unit, 551154081Sjhb (int)(pp - sc->sc_ports)); 552136058Sphk#endif 553136058Sphk tp = pp->sp_tty = ttyalloc(); 554136058Sphk tp->t_sc = pp; 555136058Sphk tp->t_break = sibreak; 556136058Sphk tp->t_close = siclose; 557136058Sphk tp->t_modem = simodem; 558136058Sphk tp->t_open = siopen; 559136058Sphk tp->t_oproc = si_start; 560136058Sphk tp->t_param = siparam; 561136058Sphk tp->t_stop = si_stop; 562154081Sjhb ttycreate(tp, TS_CALLOUT, "A%r%r", unit, 563154081Sjhb (int)(pp - sc->sc_ports)); 56434832Speter } 56534832Spetertry_next2: 56610015Speter if (modp->sm_next == 0) { 56734832Speter printf("si%d: card: %s, ports: %d, modules: %d, type: %d%s\n", 56810015Speter unit, 56910015Speter sc->sc_typename, 57010015Speter sc->sc_nport, 57112174Speter nmodule, 57234832Speter uart_type, 57334832Speter si_modulename(sc->sc_type, uart_type)); 57410015Speter break; 57510015Speter } 57610015Speter modp = (struct si_module *) 57710015Speter (maddr + (unsigned)(modp->sm_next & 0x7fff)); 57810015Speter } 57910015Speter if (done_chartimes == 0) { 58010015Speter for (spt = chartimes ; spt->sp_speed != -1; spt++) { 58110015Speter if ((spt->sp_code /= hz) == 0) 58210015Speter spt->sp_code = 1; 58310015Speter } 58410015Speter done_chartimes = 1; 58510015Speter } 58612502Sjulian 587154081Sjhb if (unit == 0) 588154081Sjhb make_dev(&si_Scdevsw, 0, UID_ROOT, GID_WHEEL, 0600, 589154081Sjhb "si_control"); 59056498Speter return (0); 59110015Speter} 59210015Speter 59312675Sjulianstatic int 594136058Sphksiopen(struct tty *tp, struct cdev *dev) 59510015Speter{ 59610015Speter 59710015Speter#ifdef POLL 59810015Speter /* 59910015Speter * We've now got a device, so start the poller. 60010015Speter */ 60110015Speter if (init_finished == 0) { 60215639Speter timeout(si_poll, (caddr_t)0L, si_pollrate); 60310015Speter init_finished = 1; 60410015Speter } 60510015Speter#endif 606136058Sphk return(0); 60710015Speter} 60810015Speter 609136058Sphkstatic void 610136058Sphksiclose(struct tty *tp) 61110015Speter{ 61256498Speter struct si_port *pp; 61310015Speter 614136058Sphk pp = tp->t_sc; 615136058Sphk (void) si_command(pp, FCLOSE, SI_NOWAIT); 61610015Speter} 61710015Speter 61810015Speterstatic void 619136058Sphksibreak(struct tty *tp, int sig) 62010015Speter{ 62156498Speter struct si_port *pp; 62210015Speter 623136058Sphk pp = tp->t_sc; 624136058Sphk if (sig) 62516575Speter si_command(pp, SBREAK, SI_WAIT); 626136058Sphk else 62716575Speter si_command(pp, EBREAK, SI_WAIT); 62810015Speter} 62910015Speter 630136058Sphk 63110015Speter/* 632136058Sphk * Handle the Specialix ioctls on the control dev. 63310015Speter */ 63410015Speterstatic int 635130585Sphksi_Sioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) 63610015Speter{ 63710015Speter struct si_softc *xsc; 63856498Speter struct si_port *xpp; 63910015Speter volatile struct si_reg *regp; 64010015Speter struct si_tcsi *dp; 64110044Speter struct si_pstat *sps; 64211872Sphk int *ip, error = 0; 64310015Speter int oldspl; 64410015Speter int card, port; 64510015Speter 64650016Snsayer DPRINT((0, DBG_ENTRY|DBG_IOCTL, "si_Sioctl(%s,%lx,%x,%x)\n", 64750016Snsayer devtoname(dev), cmd, data, flag)); 64810015Speter 64910044Speter#if 1 65010044Speter DPRINT((0, DBG_IOCTL, "TCSI_PORT=%x\n", TCSI_PORT)); 65110044Speter DPRINT((0, DBG_IOCTL, "TCSI_CCB=%x\n", TCSI_CCB)); 65210044Speter DPRINT((0, DBG_IOCTL, "TCSI_TTY=%x\n", TCSI_TTY)); 65310044Speter#endif 65410044Speter 65510015Speter oldspl = spltty(); /* better safe than sorry */ 65610015Speter 65710015Speter ip = (int *)data; 65810015Speter 659164033Srwatson#define SUCHECK if ((error = priv_check(td, PRIV_DRIVER))) goto out 66010015Speter 66110015Speter switch (cmd) { 66210015Speter case TCSIPORTS: 66310015Speter *ip = si_Nports; 66410015Speter goto out; 66510015Speter case TCSIMODULES: 66610015Speter *ip = si_Nmodules; 66710015Speter goto out; 66810015Speter case TCSISDBG_ALL: 66910015Speter SUCHECK; 67010015Speter si_debug = *ip; 67110015Speter goto out; 67210015Speter case TCSIGDBG_ALL: 67310015Speter *ip = si_debug; 67410015Speter goto out; 67510015Speter default: 67610015Speter /* 67710015Speter * Check that a controller for this port exists 67810015Speter */ 67910044Speter 68010044Speter /* may also be a struct si_pstat, a superset of si_tcsi */ 68110044Speter 68210015Speter dp = (struct si_tcsi *)data; 68310044Speter sps = (struct si_pstat *)data; 68410015Speter card = dp->tc_card; 68556498Speter xsc = devclass_get_softc(si_devclass, card); /* check.. */ 68656498Speter if (xsc == NULL || xsc->sc_type == SIEMPTY) { 68710015Speter error = ENOENT; 68810015Speter goto out; 68910015Speter } 69010015Speter /* 69110015Speter * And check that a port exists 69210015Speter */ 69310015Speter port = dp->tc_port; 69410015Speter if (port < 0 || port >= xsc->sc_nport) { 69510015Speter error = ENOENT; 69610015Speter goto out; 69710015Speter } 69810015Speter xpp = xsc->sc_ports + port; 69910015Speter regp = (struct si_reg *)xsc->sc_maddr; 70010015Speter } 70110015Speter 70210015Speter switch (cmd) { 70310015Speter case TCSIDEBUG: 70410015Speter#ifdef SI_DEBUG 70510015Speter SUCHECK; 70610015Speter if (xpp->sp_debug) 70710015Speter xpp->sp_debug = 0; 70810015Speter else { 70910015Speter xpp->sp_debug = DBG_ALL; 71010015Speter DPRINT((xpp, DBG_IOCTL, "debug toggled %s\n", 71110015Speter (xpp->sp_debug&DBG_ALL)?"ON":"OFF")); 71210015Speter } 71310015Speter break; 71410015Speter#else 71510015Speter error = ENODEV; 71610015Speter goto out; 71710015Speter#endif 71810015Speter case TCSISDBG_LEVEL: 71910015Speter case TCSIGDBG_LEVEL: 72010015Speter#ifdef SI_DEBUG 72110015Speter if (cmd == TCSIGDBG_LEVEL) { 72210015Speter dp->tc_dbglvl = xpp->sp_debug; 72310015Speter } else { 72410015Speter SUCHECK; 72510015Speter xpp->sp_debug = dp->tc_dbglvl; 72610015Speter } 72710015Speter break; 72810015Speter#else 72910015Speter error = ENODEV; 73010015Speter goto out; 73110015Speter#endif 73210015Speter case TCSIGRXIT: 73310015Speter dp->tc_int = regp->rx_int_count; 73410015Speter break; 73510015Speter case TCSIRXIT: 73610015Speter SUCHECK; 73710015Speter regp->rx_int_count = dp->tc_int; 73810015Speter break; 73910015Speter case TCSIGIT: 74010015Speter dp->tc_int = regp->int_count; 74110015Speter break; 74210015Speter case TCSIIT: 74310015Speter SUCHECK; 74410015Speter regp->int_count = dp->tc_int; 74510015Speter break; 74610044Speter case TCSISTATE: 74710044Speter dp->tc_int = xpp->sp_ccb->hi_ip; 74810015Speter break; 74910044Speter /* these next three use a different structure */ 75010044Speter case TCSI_PORT: 75110015Speter SUCHECK; 75234832Speter si_bcopy(xpp, &sps->tc_siport, sizeof(sps->tc_siport)); 75310015Speter break; 75410044Speter case TCSI_CCB: 75510044Speter SUCHECK; 75650442Speter si_vbcopy(xpp->sp_ccb, &sps->tc_ccb, sizeof(sps->tc_ccb)); 75710015Speter break; 75810044Speter case TCSI_TTY: 75910044Speter SUCHECK; 76034832Speter si_bcopy(xpp->sp_tty, &sps->tc_tty, sizeof(sps->tc_tty)); 76110015Speter break; 76210015Speter default: 76310015Speter error = EINVAL; 76410015Speter goto out; 76510015Speter } 76610015Speterout: 76710015Speter splx(oldspl); 76810015Speter return(error); /* success */ 76910015Speter} 77010015Speter 77110015Speter/* 77210015Speter * siparam() : Configure line params 77310015Speter * called at spltty(); 77410015Speter * this may sleep, does not flush, nor wait for drain, nor block writes 77510015Speter * caller must arrange this if it's important.. 77610015Speter */ 77712724Sphkstatic int 77856498Spetersiparam(struct tty *tp, struct termios *t) 77910015Speter{ 780136058Sphk struct si_port *pp = tp->t_sc; 78110015Speter volatile struct si_channel *ccbp; 78210015Speter int oldspl, cflag, iflag, oflag, lflag; 78310015Speter int error = 0; /* shutup gcc */ 78410015Speter int ispeed = 0; /* shutup gcc */ 78510015Speter int ospeed = 0; /* shutup gcc */ 78610161Speter BYTE val; 78710015Speter 78810015Speter DPRINT((pp, DBG_ENTRY|DBG_PARAM, "siparam(%x,%x)\n", tp, t)); 78910015Speter cflag = t->c_cflag; 79010015Speter iflag = t->c_iflag; 79110015Speter oflag = t->c_oflag; 79210015Speter lflag = t->c_lflag; 79310044Speter DPRINT((pp, DBG_PARAM, "OFLAG 0x%x CFLAG 0x%x IFLAG 0x%x LFLAG 0x%x\n", 79410044Speter oflag, cflag, iflag, lflag)); 79510015Speter 79634832Speter /* XXX - if Jet host and SXDC module, use extended baud rates */ 79710015Speter 79810015Speter /* if not hung up.. */ 79910015Speter if (t->c_ospeed != 0) { 80010015Speter /* translate baud rate to firmware values */ 80110015Speter ospeed = ttspeedtab(t->c_ospeed, bdrates); 80210015Speter ispeed = t->c_ispeed ? 80310015Speter ttspeedtab(t->c_ispeed, bdrates) : ospeed; 80410015Speter 80510015Speter /* enforce legit baud rate */ 80610015Speter if (ospeed < 0 || ispeed < 0) 80710015Speter return (EINVAL); 80810015Speter } 80910015Speter 81010015Speter oldspl = spltty(); 81110015Speter 81210015Speter ccbp = pp->sp_ccb; 81310015Speter 81410161Speter /* ========== set hi_break ========== */ 81510161Speter val = 0; 81610161Speter if (iflag & IGNBRK) /* Breaks */ 81710161Speter val |= BR_IGN; 81810161Speter if (iflag & BRKINT) /* Interrupt on break? */ 81910161Speter val |= BR_INT; 82010161Speter if (iflag & PARMRK) /* Parity mark? */ 82110161Speter val |= BR_PARMRK; 82210161Speter if (iflag & IGNPAR) /* Ignore chars with parity errors? */ 82310161Speter val |= BR_PARIGN; 82410161Speter ccbp->hi_break = val; 82510161Speter 82610161Speter /* ========== set hi_csr ========== */ 82710015Speter /* if not hung up.. */ 82810015Speter if (t->c_ospeed != 0) { 82910015Speter /* Set I/O speeds */ 83010161Speter val = (ispeed << 4) | ospeed; 83110015Speter } 83210161Speter ccbp->hi_csr = val; 83310015Speter 83410161Speter /* ========== set hi_mr2 ========== */ 83510161Speter val = 0; 83610015Speter if (cflag & CSTOPB) /* Stop bits */ 83710161Speter val |= MR2_2_STOP; 83810015Speter else 83910161Speter val |= MR2_1_STOP; 84010161Speter /* 84110161Speter * Enable H/W RTS/CTS handshaking. The default TA/MTA is 84210161Speter * a DCE, hence the reverse sense of RTS and CTS 84310161Speter */ 84410161Speter /* Output Flow - RTS must be raised before data can be sent */ 84510161Speter if (cflag & CCTS_OFLOW) 84610161Speter val |= MR2_RTSCONT; 84710161Speter 84816575Speter ccbp->hi_mr2 = val; 84910161Speter 85010161Speter /* ========== set hi_mr1 ========== */ 85110161Speter val = 0; 85210015Speter if (!(cflag & PARENB)) /* Parity */ 85310161Speter val |= MR1_NONE; 85410015Speter else 85510161Speter val |= MR1_WITH; 85610015Speter if (cflag & PARODD) 85710161Speter val |= MR1_ODD; 85810015Speter 85910015Speter if ((cflag & CS8) == CS8) { /* 8 data bits? */ 86010161Speter val |= MR1_8_BITS; 86110015Speter } else if ((cflag & CS7) == CS7) { /* 7 data bits? */ 86210161Speter val |= MR1_7_BITS; 86310015Speter } else if ((cflag & CS6) == CS6) { /* 6 data bits? */ 86410161Speter val |= MR1_6_BITS; 86510015Speter } else { /* Must be 5 */ 86610161Speter val |= MR1_5_BITS; 86710015Speter } 86810161Speter /* 86910161Speter * Enable H/W RTS/CTS handshaking. The default TA/MTA is 87010161Speter * a DCE, hence the reverse sense of RTS and CTS 87110161Speter */ 87210161Speter /* Input Flow - CTS is raised when port is ready to receive data */ 87310161Speter if (cflag & CRTS_IFLOW) 87410161Speter val |= MR1_CTSCONT; 87510015Speter 87610161Speter ccbp->hi_mr1 = val; 87710161Speter 87810161Speter /* ========== set hi_mask ========== */ 87910161Speter val = 0xff; 88010161Speter if ((cflag & CS8) == CS8) { /* 8 data bits? */ 88110161Speter val &= 0xFF; 88210161Speter } else if ((cflag & CS7) == CS7) { /* 7 data bits? */ 88310161Speter val &= 0x7F; 88410161Speter } else if ((cflag & CS6) == CS6) { /* 6 data bits? */ 88510161Speter val &= 0x3F; 88610161Speter } else { /* Must be 5 */ 88710161Speter val &= 0x1F; 88810161Speter } 88910015Speter if (iflag & ISTRIP) 89010161Speter val &= 0x7F; 89110015Speter 89210161Speter ccbp->hi_mask = val; 89310161Speter 89410161Speter /* ========== set hi_prtcl ========== */ 89556592Speter val = SP_DCEN; /* Monitor DCD always, or TIOCMGET misses it */ 89610161Speter if (iflag & IXANY) 89710161Speter val |= SP_TANY; 89810161Speter if (iflag & IXON) 89910161Speter val |= SP_TXEN; 90010161Speter if (iflag & IXOFF) 90110161Speter val |= SP_RXEN; 90210161Speter if (iflag & INPCK) 90310161Speter val |= SP_PAEN; 90410015Speter 90510161Speter ccbp->hi_prtcl = val; 90610161Speter 90710161Speter 90810161Speter /* ========== set hi_{rx|tx}{on|off} ========== */ 90910161Speter /* XXX: the card TOTALLY shields us from the flow control... */ 91010015Speter ccbp->hi_txon = t->c_cc[VSTART]; 91110015Speter ccbp->hi_txoff = t->c_cc[VSTOP]; 91210015Speter 91310015Speter ccbp->hi_rxon = t->c_cc[VSTART]; 91410015Speter ccbp->hi_rxoff = t->c_cc[VSTOP]; 91510015Speter 91610161Speter /* ========== send settings to the card ========== */ 91710015Speter /* potential sleep here */ 91810015Speter if (ccbp->hi_stat == IDLE_CLOSE) /* Not yet open */ 91910015Speter si_command(pp, LOPEN, SI_WAIT); /* open it */ 92010015Speter else 92110015Speter si_command(pp, CONFIG, SI_WAIT); /* change params */ 92210015Speter 92310161Speter /* ========== set DTR etc ========== */ 92410015Speter /* Hangup if ospeed == 0 */ 92510015Speter if (t->c_ospeed == 0) { 926136058Sphk (void) simodem(tp, 0, SER_DTR | SER_RTS); 92710015Speter } else { 92810015Speter /* 92910015Speter * If the previous speed was 0, may need to re-enable 93034832Speter * the modem signals 93134832Speter */ 932136058Sphk (void) simodem(tp, SER_DTR | SER_RTS, 0); 93310015Speter } 93410015Speter 93510044Speter DPRINT((pp, DBG_PARAM, "siparam, complete: MR1 %x MR2 %x HI_MASK %x PRTCL %x HI_BREAK %x\n", 93610044Speter ccbp->hi_mr1, ccbp->hi_mr2, ccbp->hi_mask, ccbp->hi_prtcl, ccbp->hi_break)); 93710015Speter 93810015Speter splx(oldspl); 93910015Speter return(error); 94010015Speter} 94110015Speter 94210015Speter/* 94310015Speter * Set/Get state of modem control lines. 94410015Speter * Due to DCE-like behaviour of the adapter, some signals need translation: 94510015Speter * TIOCM_DTR DSR 94610015Speter * TIOCM_RTS CTS 94710015Speter */ 94810015Speterstatic int 949136058Sphksimodem(struct tty *tp, int sigon, int sigoff) 95010015Speter{ 951136058Sphk struct si_port *pp; 95210015Speter volatile struct si_channel *ccbp; 95310015Speter int x; 95410015Speter 955136058Sphk pp = tp->t_sc; 956136058Sphk DPRINT((pp, DBG_ENTRY|DBG_MODEM, "simodem(%x,%x)\n", sigon, sigoff)); 95710015Speter ccbp = pp->sp_ccb; /* Find channel address */ 958136058Sphk if (sigon == 0 && sigoff == 0) { 95910015Speter x = ccbp->hi_ip; 960136058Sphk /* 961136058Sphk * XXX: not sure this is correct, should it be CTS&DSR ? 962136058Sphk * XXX: or do we (just) miss CTS & DSR ? 963136058Sphk */ 964136058Sphk if (x & IP_DCD) sigon |= SER_DCD; 965136058Sphk if (x & IP_DTR) sigon |= SER_DTR; 966136058Sphk if (x & IP_RTS) sigon |= SER_RTS; 967136058Sphk if (x & IP_RI) sigon |= SER_RI; 968136058Sphk return (sigon); 96910015Speter } 970136058Sphk 971136058Sphk x = ccbp->hi_op; 972136058Sphk if (sigon & SER_DTR) 973136058Sphk x |= OP_DSR; 974136058Sphk if (sigoff & SER_DTR) 975136058Sphk x &= ~OP_DSR; 976136058Sphk if (sigon & SER_RTS) 977136058Sphk x |= OP_CTS; 978136058Sphk if (sigoff & SER_RTS) 979136058Sphk x &= ~OP_CTS; 980136058Sphk ccbp->hi_op = x; 98110015Speter return 0; 98210015Speter} 98310015Speter 98410015Speter/* 98510015Speter * Handle change of modem state 98610015Speter */ 98710015Speterstatic void 98856498Spetersi_modem_state(struct si_port *pp, struct tty *tp, int hi_ip) 98910015Speter{ 99010015Speter /* if a modem dev */ 99110015Speter if (hi_ip & IP_DCD) { 99250442Speter if (!(pp->sp_last_hi_ip & IP_DCD)) { 99310015Speter DPRINT((pp, DBG_INTR, "modem carr on t_line %d\n", 99410015Speter tp->t_line)); 995130077Sphk (void)ttyld_modem(tp, 1); 99610015Speter } 99710015Speter } else { 99810015Speter if (pp->sp_last_hi_ip & IP_DCD) { 99910015Speter DPRINT((pp, DBG_INTR, "modem carr off\n")); 1000130077Sphk if (ttyld_modem(tp, 0)) 1001136058Sphk (void) simodem(tp, 0, SER_DTR | SER_RTS); 100210015Speter } 100310015Speter } 100410015Speter pp->sp_last_hi_ip = hi_ip; 100510015Speter 100610015Speter} 100710015Speter 100810015Speter/* 100910015Speter * Poller to catch missed interrupts. 101012174Speter * 101112496Speter * Note that the SYSV Specialix drivers poll at 100 times per second to get 101212496Speter * better response. We could really use a "periodic" version timeout(). :-) 101310015Speter */ 101410015Speter#ifdef POLL 101510708Speterstatic void 101610015Spetersi_poll(void *nothing) 101710015Speter{ 101856498Speter struct si_softc *sc; 101956498Speter int i; 102010015Speter volatile struct si_reg *regp; 102156498Speter struct si_port *pp; 102212174Speter int lost, oldspl, port; 102310015Speter 102410015Speter DPRINT((0, DBG_POLL, "si_poll()\n")); 102511609Speter oldspl = spltty(); 102610015Speter if (in_intr) 102710015Speter goto out; 102810015Speter lost = 0; 102956498Speter for (i = 0; i < si_numunits; i++) { 103056498Speter sc = devclass_get_softc(si_devclass, i); 103156498Speter if (sc == NULL || sc->sc_type == SIEMPTY) 103210015Speter continue; 103310015Speter regp = (struct si_reg *)sc->sc_maddr; 103434832Speter 103510015Speter /* 103610015Speter * See if there has been a pending interrupt for 2 seconds 103733395Speter * or so. The test (int_scounter >= 200) won't correspond 103810015Speter * to 2 seconds if int_count gets changed. 103910015Speter */ 104010015Speter if (regp->int_pending != 0) { 104110015Speter if (regp->int_scounter >= 200 && 104210015Speter regp->initstat == 1) { 104312174Speter printf("si%d: lost intr\n", i); 104410015Speter lost++; 104510015Speter } 104610015Speter } else { 104710015Speter regp->int_scounter = 0; 104810015Speter } 104910015Speter 105012174Speter /* 105112174Speter * gripe about no input flow control.. 105212174Speter */ 105312174Speter pp = sc->sc_ports; 105412174Speter for (port = 0; port < sc->sc_nport; pp++, port++) { 105512174Speter if (pp->sp_delta_overflows > 0) { 105612174Speter printf("si%d: %d tty level buffer overflows\n", 105712174Speter i, pp->sp_delta_overflows); 105812174Speter pp->sp_delta_overflows = 0; 105912174Speter } 106012174Speter } 106110015Speter } 106217547Speter if (lost || si_realpoll) 106356498Speter si_intr(NULL); /* call intr with fake vector */ 106411609Speterout: 106510015Speter splx(oldspl); 106610015Speter 106715639Speter timeout(si_poll, (caddr_t)0L, si_pollrate); 106810015Speter} 106910015Speter#endif /* ifdef POLL */ 107010015Speter 107110015Speter/* 107210015Speter * The interrupt handler polls ALL ports on ALL adapters each time 107310015Speter * it is called. 107410015Speter */ 107510015Speter 107612496Speterstatic BYTE si_rxbuf[SI_BUFFERSIZE]; /* input staging area */ 107734832Speterstatic BYTE si_txbuf[SI_BUFFERSIZE]; /* output staging area */ 107810015Speter 107956505Spetervoid 108056498Spetersi_intr(void *arg) 108110015Speter{ 108256498Speter struct si_softc *sc; 108356498Speter struct si_port *pp; 108410015Speter volatile struct si_channel *ccbp; 108556498Speter struct tty *tp; 108610015Speter volatile caddr_t maddr; 108711872Sphk BYTE op, ip; 108812174Speter int x, card, port, n, i, isopen; 108910015Speter volatile BYTE *z; 109010015Speter BYTE c; 109110015Speter 109256498Speter sc = arg; 109356498Speter 109456498Speter DPRINT((0, arg == NULL ? DBG_POLL:DBG_INTR, "si_intr\n")); 109556498Speter if (in_intr) 109610708Speter return; 109710015Speter in_intr = 1; 109810015Speter 109910015Speter /* 110010015Speter * When we get an int we poll all the channels and do ALL pending 110110015Speter * work, not just the first one we find. This allows all cards to 110210015Speter * share the same vector. 110334832Speter * 110434832Speter * XXX - But if we're sharing the vector with something that's NOT 110534832Speter * a SI/XIO/SX card, we may be making more work for ourselves. 110610015Speter */ 110756498Speter for (card = 0; card < si_numunits; card++) { 110856498Speter sc = devclass_get_softc(si_devclass, card); 110956498Speter if (sc == NULL || sc->sc_type == SIEMPTY) 111010015Speter continue; 111112174Speter 111212174Speter /* 111312174Speter * First, clear the interrupt 111412174Speter */ 111510015Speter switch(sc->sc_type) { 111634832Speter case SIHOST: 111710015Speter maddr = sc->sc_maddr; 111810015Speter ((volatile struct si_reg *)maddr)->int_pending = 0; 111910015Speter /* flag nothing pending */ 112010015Speter *(maddr+SIINTCL) = 0x00; /* Set IRQ clear */ 112110015Speter *(maddr+SIINTCL_CL) = 0x00; /* Clear IRQ clear */ 112210015Speter break; 112310015Speter case SIHOST2: 112410015Speter maddr = sc->sc_maddr; 112510015Speter ((volatile struct si_reg *)maddr)->int_pending = 0; 112610015Speter *(maddr+SIPLIRQCLR) = 0x00; 112710015Speter *(maddr+SIPLIRQCLR) = 0x10; 112810015Speter break; 112933395Speter case SIPCI: 113033395Speter maddr = sc->sc_maddr; 113133395Speter ((volatile struct si_reg *)maddr)->int_pending = 0; 113233395Speter *(maddr+SIPCIINTCL) = 0x0; 113333395Speter break; 113434832Speter case SIJETPCI: /* fall through to JETISA case */ 113533395Speter case SIJETISA: 113633395Speter maddr = sc->sc_maddr; 113733395Speter ((volatile struct si_reg *)maddr)->int_pending = 0; 113833395Speter *(maddr+SIJETINTCL) = 0x0; 113933395Speter break; 1140166091Smarius#ifdef DEV_EISA 114110015Speter case SIEISA: 114210015Speter maddr = sc->sc_maddr; 114310015Speter ((volatile struct si_reg *)maddr)->int_pending = 0; 114456498Speter (void)inb(sc->sc_iobase + 3); 114510015Speter break; 1146166091Smarius#endif 114710015Speter case SIEMPTY: 114810015Speter default: 114910015Speter continue; 115010015Speter } 115110015Speter ((volatile struct si_reg *)maddr)->int_scounter = 0; 115210015Speter 115312174Speter /* 115412174Speter * check each port 115512174Speter */ 115650442Speter for (pp = sc->sc_ports, port = 0; port < sc->sc_nport; 115734832Speter pp++, port++) { 115810015Speter ccbp = pp->sp_ccb; 115910015Speter tp = pp->sp_tty; 116010015Speter 116110015Speter /* 116210015Speter * See if a command has completed ? 116310015Speter */ 116410015Speter if (ccbp->hi_stat != pp->sp_pend) { 116510015Speter DPRINT((pp, DBG_INTR, 116634735Speter "si_intr hi_stat = 0x%x, pend = %d\n", 116710015Speter ccbp->hi_stat, pp->sp_pend)); 116810015Speter switch(pp->sp_pend) { 116910015Speter case LOPEN: 117010015Speter case MPEND: 117110015Speter case MOPEN: 117210015Speter case CONFIG: 117316575Speter case SBREAK: 117416575Speter case EBREAK: 117510015Speter pp->sp_pend = ccbp->hi_stat; 117610015Speter /* sleeping in si_command */ 117710015Speter wakeup(&pp->sp_state); 117810015Speter break; 117910015Speter default: 118010015Speter pp->sp_pend = ccbp->hi_stat; 118110015Speter } 118234832Speter } 118310015Speter 118410015Speter /* 118510015Speter * Continue on if it's closed 118610015Speter */ 118710015Speter if (ccbp->hi_stat == IDLE_CLOSE) { 118810015Speter continue; 118910015Speter } 119010015Speter 119110015Speter /* 119210015Speter * Do modem state change if not a local device 119310015Speter */ 119410015Speter si_modem_state(pp, tp, ccbp->hi_ip); 119510015Speter 119610015Speter /* 119734832Speter * Check to see if we should 'receive' characters. 119812174Speter */ 119912174Speter if (tp->t_state & TS_CONNECTED && 120012174Speter tp->t_state & TS_ISOPEN) 120112174Speter isopen = 1; 120212174Speter else 120312174Speter isopen = 0; 120412174Speter 120512174Speter /* 120616575Speter * Do input break processing 120710015Speter */ 120810015Speter if (ccbp->hi_state & ST_BREAK) { 120912174Speter if (isopen) { 1210130077Sphk ttyld_rint(tp, TTY_BI); 121110015Speter } 121210015Speter ccbp->hi_state &= ~ST_BREAK; /* A Bit iffy this */ 121310015Speter DPRINT((pp, DBG_INTR, "si_intr break\n")); 121410015Speter } 121510015Speter 121610015Speter /* 121712174Speter * Do RX stuff - if not open then dump any characters. 121812174Speter * XXX: This is VERY messy and needs to be cleaned up. 121912174Speter * 122012174Speter * XXX: can we leave data in the host adapter buffer 122112174Speter * when the clists are full? That may be dangerous 122212174Speter * if the user cannot get an interrupt signal through. 122310015Speter */ 122410015Speter 122512174Speter more_rx: /* XXX Sorry. the nesting was driving me bats! :-( */ 122612174Speter 122712174Speter if (!isopen) { 122810015Speter ccbp->hi_rxopos = ccbp->hi_rxipos; 122912174Speter goto end_rx; 123012174Speter } 123110015Speter 123212174Speter /* 123315640Speter * If the tty input buffers are blocked, stop emptying 123415640Speter * the incoming buffers and let the auto flow control 123515640Speter * assert.. 123615640Speter */ 123715640Speter if (tp->t_state & TS_TBLOCK) { 123815640Speter goto end_rx; 123915640Speter } 124015640Speter 124115640Speter /* 124212174Speter * Process read characters if not skipped above 124312174Speter */ 124415640Speter op = ccbp->hi_rxopos; 124515640Speter ip = ccbp->hi_rxipos; 124615640Speter c = ip - op; 124712174Speter if (c == 0) { 124812174Speter goto end_rx; 124912174Speter } 125010015Speter 125112174Speter n = c & 0xff; 125215640Speter if (n > 250) 125315640Speter n = 250; 125412174Speter 125512174Speter DPRINT((pp, DBG_INTR, "n = %d, op = %d, ip = %d\n", 125610015Speter n, op, ip)); 125710015Speter 125812174Speter /* 125912174Speter * Suck characters out of host card buffer into the 126012174Speter * "input staging buffer" - so that we dont leave the 126112174Speter * host card in limbo while we're possibly echoing 126212174Speter * characters and possibly flushing input inside the 126312174Speter * ldisc l_rint() routine. 126412174Speter */ 126512496Speter if (n <= SI_BUFFERSIZE - op) { 126610015Speter 126712174Speter DPRINT((pp, DBG_INTR, "\tsingle copy\n")); 126812174Speter z = ccbp->hi_rxbuf + op; 126950442Speter si_vbcopy(z, si_rxbuf, n); 127010015Speter 127112174Speter op += n; 127212174Speter } else { 127312496Speter x = SI_BUFFERSIZE - op; 127410015Speter 127512174Speter DPRINT((pp, DBG_INTR, "\tdouble part 1 %d\n", x)); 127612174Speter z = ccbp->hi_rxbuf + op; 127750442Speter si_vbcopy(z, si_rxbuf, x); 127810015Speter 127934832Speter DPRINT((pp, DBG_INTR, "\tdouble part 2 %d\n", 128034832Speter n - x)); 128112174Speter z = ccbp->hi_rxbuf; 128250442Speter si_vbcopy(z, si_rxbuf + x, n - x); 128310015Speter 128412174Speter op += n; 128512174Speter } 128610015Speter 128712174Speter /* clear collected characters from buffer */ 128812174Speter ccbp->hi_rxopos = op; 128912174Speter 129012174Speter DPRINT((pp, DBG_INTR, "n = %d, op = %d, ip = %d\n", 129110015Speter n, op, ip)); 129210015Speter 129312174Speter /* 129412174Speter * at this point... 129512174Speter * n = number of chars placed in si_rxbuf 129612174Speter */ 129710015Speter 129812174Speter /* 129912174Speter * Avoid the grotesquely inefficient lineswitch 130012174Speter * routine (ttyinput) in "raw" mode. It usually 130112174Speter * takes about 450 instructions (that's without 130212174Speter * canonical processing or echo!). slinput is 130312174Speter * reasonably fast (usually 40 instructions 130412174Speter * plus call overhead). 130512174Speter */ 130612174Speter if (tp->t_state & TS_CAN_BYPASS_L_RINT) { 130710015Speter 130812174Speter /* block if the driver supports it */ 130950442Speter if (tp->t_rawq.c_cc + n >= SI_I_HIGH_WATER && 131050442Speter (tp->t_cflag & CRTS_IFLOW || 131150442Speter tp->t_iflag & IXOFF) && 131250442Speter !(tp->t_state & TS_TBLOCK)) 131312174Speter ttyblock(tp); 131410015Speter 131512174Speter tk_nin += n; 131612174Speter tk_rawcc += n; 131712174Speter tp->t_rawcc += n; 131812174Speter 131912174Speter pp->sp_delta_overflows += 132012174Speter b_to_q((char *)si_rxbuf, n, &tp->t_rawq); 132112174Speter 132212174Speter ttwakeup(tp); 132350442Speter if (tp->t_state & TS_TTSTOP && 132450442Speter (tp->t_iflag & IXANY || 132550442Speter tp->t_cc[VSTART] == tp->t_cc[VSTOP])) { 132612174Speter tp->t_state &= ~TS_TTSTOP; 132712174Speter tp->t_lflag &= ~FLUSHO; 132812174Speter si_start(tp); 132912174Speter } 133012174Speter } else { 133112174Speter /* 133212174Speter * It'd be nice to not have to go through the 133312174Speter * function call overhead for each char here. 133412174Speter * It'd be nice to block input it, saving a 133512174Speter * loop here and the call/return overhead. 133612174Speter */ 133712174Speter for(x = 0; x < n; x++) { 133812174Speter i = si_rxbuf[x]; 1339130077Sphk if (ttyld_rint(tp, i) 134012174Speter == -1) { 134112174Speter pp->sp_delta_overflows++; 134210015Speter } 134312174Speter } 134412174Speter } 134512174Speter goto more_rx; /* try for more until RXbuf is empty */ 134610015Speter 134712174Speter end_rx: /* XXX: Again, sorry about the gotos.. :-) */ 134810015Speter 134910015Speter /* 135010015Speter * Do TX stuff 135110015Speter */ 1352130077Sphk ttyld_start(tp); 135310015Speter 135410015Speter } /* end of for (all ports on this controller) */ 135510015Speter } /* end of for (all controllers) */ 135610015Speter 135711609Speter in_intr = 0; 135856498Speter DPRINT((0, arg == NULL ? DBG_POLL:DBG_INTR, "end si_intr\n")); 135910015Speter} 136010015Speter 136110015Speter/* 136210015Speter * Nudge the transmitter... 136312174Speter * 136412174Speter * XXX: I inherited some funny code here. It implies the host card only 136512174Speter * interrupts when the transmit buffer reaches the low-water-mark, and does 136612174Speter * not interrupt when it's actually hits empty. In some cases, we have 136712174Speter * processes waiting for complete drain, and we need to simulate an interrupt 136812174Speter * about when we think the buffer is going to be empty (and retry if not). 136912174Speter * I really am not certain about this... I *need* the hardware manuals. 137010015Speter */ 137110015Speterstatic void 137256498Spetersi_start(struct tty *tp) 137310015Speter{ 137410015Speter struct si_port *pp; 137510015Speter volatile struct si_channel *ccbp; 137656498Speter struct clist *qp; 137710015Speter BYTE ipos; 137810015Speter int nchar; 137910015Speter int oldspl, count, n, amount, buffer_full; 138010015Speter 138110015Speter oldspl = spltty(); 138210015Speter 138310015Speter qp = &tp->t_outq; 1384136058Sphk pp = tp->t_sc; 138510015Speter 138610015Speter DPRINT((pp, DBG_ENTRY|DBG_START, 138710015Speter "si_start(%x) t_state %x sp_state %x t_outq.c_cc %d\n", 138810015Speter tp, tp->t_state, pp->sp_state, qp->c_cc)); 138910015Speter 139010015Speter if (tp->t_state & (TS_TIMEOUT|TS_TTSTOP)) 139110015Speter goto out; 139210015Speter 139310015Speter buffer_full = 0; 139410015Speter ccbp = pp->sp_ccb; 139510015Speter 139610015Speter count = (int)ccbp->hi_txipos - (int)ccbp->hi_txopos; 139710015Speter DPRINT((pp, DBG_START, "count %d\n", (BYTE)count)); 139810015Speter 139910015Speter while ((nchar = qp->c_cc) > 0) { 140010015Speter if ((BYTE)count >= 255) { 140110015Speter buffer_full++; 140210015Speter break; 140310015Speter } 140410015Speter amount = min(nchar, (255 - (BYTE)count)); 140510015Speter ipos = (unsigned int)ccbp->hi_txipos; 140634832Speter n = q_to_b(&tp->t_outq, si_txbuf, amount); 140710015Speter /* will it fit in one lump? */ 140834832Speter if ((SI_BUFFERSIZE - ipos) >= n) { 140950442Speter si_bcopyv(si_txbuf, &ccbp->hi_txbuf[ipos], n); 141010015Speter } else { 141150442Speter si_bcopyv(si_txbuf, &ccbp->hi_txbuf[ipos], 141234832Speter SI_BUFFERSIZE - ipos); 141350442Speter si_bcopyv(si_txbuf + (SI_BUFFERSIZE - ipos), 141450442Speter &ccbp->hi_txbuf[0], n - (SI_BUFFERSIZE - ipos)); 141510015Speter } 141610015Speter ccbp->hi_txipos += n; 141710015Speter count = (int)ccbp->hi_txipos - (int)ccbp->hi_txopos; 141810015Speter } 141910015Speter 142010015Speter if (count != 0 && nchar == 0) { 142110015Speter tp->t_state |= TS_BUSY; 142210015Speter } else { 142310015Speter tp->t_state &= ~TS_BUSY; 142410015Speter } 142510015Speter 142610015Speter /* wakeup time? */ 142710015Speter ttwwakeup(tp); 142810015Speter 142910015Speter DPRINT((pp, DBG_START, "count %d, nchar %d, tp->t_state 0x%x\n", 143010015Speter (BYTE)count, nchar, tp->t_state)); 143110015Speter 143234735Speter if (tp->t_state & TS_BUSY) 143310015Speter { 143410015Speter int time; 143510015Speter 143634735Speter time = ttspeedtab(tp->t_ospeed, chartimes); 143734735Speter 143834735Speter if (time > 0) { 143934735Speter if (time < nchar) 144034735Speter time = nchar / time; 144134735Speter else 144234735Speter time = 2; 144310015Speter } else { 144434735Speter DPRINT((pp, DBG_START, 144534735Speter "bad char time value! %d\n", time)); 144634735Speter time = hz/10; 144710015Speter } 144810015Speter 144910015Speter if ((pp->sp_state & (SS_LSTART|SS_INLSTART)) == SS_LSTART) { 145029677Sgibbs untimeout(si_lstart, (caddr_t)pp, pp->lstart_ch); 145110015Speter } else { 145210015Speter pp->sp_state |= SS_LSTART; 145310015Speter } 145410015Speter DPRINT((pp, DBG_START, "arming lstart, time=%d\n", time)); 145529677Sgibbs pp->lstart_ch = timeout(si_lstart, (caddr_t)pp, time); 145610015Speter } 145710015Speter 145810015Speterout: 145910015Speter splx(oldspl); 146010015Speter DPRINT((pp, DBG_EXIT|DBG_START, "leave si_start()\n")); 146110015Speter} 146210015Speter 146310015Speter/* 146410015Speter * Note: called at splsoftclock from the timeout code 146510015Speter * This has to deal with two things... cause wakeups while waiting for 146610015Speter * tty drains on last process exit, and call l_start at about the right 146710015Speter * time for protocols like ppp. 146810015Speter */ 146910015Speterstatic void 147025047Sbdesi_lstart(void *arg) 147110015Speter{ 147256498Speter struct si_port *pp = arg; 147356498Speter struct tty *tp; 147410015Speter int oldspl; 147510015Speter 147610015Speter DPRINT((pp, DBG_ENTRY|DBG_LSTART, "si_lstart(%x) sp_state %x\n", 147710015Speter pp, pp->sp_state)); 147810015Speter 147910015Speter oldspl = spltty(); 1480136058Sphk tp = pp->sp_tty; 148110015Speter 1482136058Sphk if ((tp->t_state & TS_ISOPEN) == 0 || 1483136058Sphk (pp->sp_state & SS_LSTART) == 0) { 148410015Speter splx(oldspl); 148510015Speter return; 148610015Speter } 148710015Speter pp->sp_state &= ~SS_LSTART; 148810015Speter pp->sp_state |= SS_INLSTART; 148910015Speter 149010015Speter 149110015Speter /* deal with the process exit case */ 149210015Speter ttwwakeup(tp); 149310015Speter 149412174Speter /* nudge protocols - eg: ppp */ 1495130077Sphk ttyld_start(tp); 149610015Speter 149710015Speter pp->sp_state &= ~SS_INLSTART; 149810015Speter splx(oldspl); 149910015Speter} 150010015Speter 150110015Speter/* 150210015Speter * Stop output on a line. called at spltty(); 150310015Speter */ 1504105215Sphkstatic void 150556498Spetersi_stop(struct tty *tp, int rw) 150610015Speter{ 150710015Speter volatile struct si_channel *ccbp; 150810015Speter struct si_port *pp; 150910015Speter 1510136058Sphk pp = tp->t_sc; 151110015Speter ccbp = pp->sp_ccb; 151210015Speter 1513136058Sphk DPRINT((pp, DBG_ENTRY|DBG_STOP, "si_stop(%x,%x)\n", tp, rw)); 151410015Speter 151510015Speter /* XXX: must check (rw & FWRITE | FREAD) etc flushing... */ 151610015Speter if (rw & FWRITE) { 151710015Speter /* what level are we meant to be flushing anyway? */ 151810015Speter if (tp->t_state & TS_BUSY) { 1519136058Sphk si_command(pp, WFLUSH, SI_NOWAIT); 152010015Speter tp->t_state &= ~TS_BUSY; 152110015Speter ttwwakeup(tp); /* Bruce???? */ 152210015Speter } 152310015Speter } 152412174Speter#if 1 /* XXX: this doesn't work right yet.. */ 152512174Speter /* XXX: this may have been failing because we used to call l_rint() 152612174Speter * while we were looping based on these two counters. Now, we collect 152712174Speter * the data and then loop stuffing it into l_rint(), making this 152812174Speter * useless. Should we cause this to blow away the staging buffer? 152912174Speter */ 153010015Speter if (rw & FREAD) { 153110015Speter ccbp->hi_rxopos = ccbp->hi_rxipos; 153210015Speter } 153310015Speter#endif 153410015Speter} 153510015Speter 153610015Speter/* 153734832Speter * Issue a command to the host card CPU. 153810015Speter */ 153910015Speter 154010015Speterstatic void 154156498Spetersi_command(struct si_port *pp, int cmd, int waitflag) 154210015Speter{ 154310015Speter int oldspl; 154410015Speter volatile struct si_channel *ccbp = pp->sp_ccb; 154510015Speter int x; 154610015Speter 154710015Speter DPRINT((pp, DBG_ENTRY|DBG_PARAM, "si_command(%x,%x,%d): hi_stat 0x%x\n", 154810015Speter pp, cmd, waitflag, ccbp->hi_stat)); 154910015Speter 155010015Speter oldspl = spltty(); /* Keep others out */ 155110015Speter 155210015Speter /* wait until it's finished what it was doing.. */ 155316575Speter /* XXX: sits in IDLE_BREAK until something disturbs it or break 155416575Speter * is turned off. */ 155510015Speter while((x = ccbp->hi_stat) != IDLE_OPEN && 155610015Speter x != IDLE_CLOSE && 155716575Speter x != IDLE_BREAK && 155810015Speter x != cmd) { 155910015Speter if (in_intr) { /* Prevent sleep in intr */ 156010015Speter DPRINT((pp, DBG_PARAM, 156110015Speter "cmd intr collision - completing %d\trequested %d\n", 156210015Speter x, cmd)); 156310015Speter splx(oldspl); 156410015Speter return; 156510015Speter } else if (ttysleep(pp->sp_tty, (caddr_t)&pp->sp_state, TTIPRI|PCATCH, 156610015Speter "sicmd1", 1)) { 156710015Speter splx(oldspl); 156810015Speter return; 156910015Speter } 157010015Speter } 157116575Speter /* it should now be in IDLE_{OPEN|CLOSE|BREAK}, or "cmd" */ 157210015Speter 157310015Speter /* if there was a pending command, cause a state-change wakeup */ 157416575Speter switch(pp->sp_pend) { 157516575Speter case LOPEN: 157616575Speter case MPEND: 157716575Speter case MOPEN: 157816575Speter case CONFIG: 157916575Speter case SBREAK: 158016575Speter case EBREAK: 158116575Speter wakeup(&pp->sp_state); 158216575Speter break; 158316575Speter default: 158416575Speter break; 158510015Speter } 158610015Speter 158710015Speter pp->sp_pend = cmd; /* New command pending */ 158810015Speter ccbp->hi_stat = cmd; /* Post it */ 158910015Speter 159010015Speter if (waitflag) { 159110015Speter if (in_intr) { /* If in interrupt handler */ 159210015Speter DPRINT((pp, DBG_PARAM, 159310015Speter "attempt to sleep in si_intr - cmd req %d\n", 159410015Speter cmd)); 159510015Speter splx(oldspl); 159610015Speter return; 159716575Speter } else while(ccbp->hi_stat != IDLE_OPEN && 159816575Speter ccbp->hi_stat != IDLE_BREAK) { 159910015Speter if (ttysleep(pp->sp_tty, (caddr_t)&pp->sp_state, TTIPRI|PCATCH, 160010015Speter "sicmd2", 0)) 160110015Speter break; 160210015Speter } 160310015Speter } 160410015Speter splx(oldspl); 160510015Speter} 160610015Speter 160710015Speter 160810015Speter#ifdef SI_DEBUG 160913353Speter 161056505Spetervoid 161113353Spetersi_dprintf(struct si_port *pp, int flags, const char *fmt, ...) 161210015Speter{ 161313353Speter va_list ap; 161413630Sphk 161510015Speter if ((pp == NULL && (si_debug&flags)) || 161610015Speter (pp != NULL && ((pp->sp_debug&flags) || (si_debug&flags)))) { 161734832Speter if (pp != NULL) 1618136058Sphk printf("%s: ", pp->sp_name); 161913630Sphk va_start(ap, fmt); 162013630Sphk vprintf(fmt, ap); 162113353Speter va_end(ap); 162210015Speter } 162310015Speter} 162410015Speter 162512624Speter#endif /* DEBUG */ 162612502Sjulian 162734832Speterstatic char * 162856498Spetersi_modulename(int host_type, int uart_type) 162934832Speter{ 163034832Speter switch (host_type) { 163134832Speter /* Z280 based cards */ 1632166091Smarius#ifdef DEV_EISA 163350442Speter case SIEISA: 1634166091Smarius#endif 163550442Speter case SIHOST2: 163634832Speter case SIHOST: 163734832Speter case SIPCI: 163834832Speter switch (uart_type) { 163934832Speter case 0: 164034832Speter return(" (XIO)"); 164134832Speter case 1: 164234832Speter return(" (SI)"); 164334832Speter } 164434832Speter break; 164534832Speter /* T225 based hosts */ 164634832Speter case SIJETPCI: 164734832Speter case SIJETISA: 164834832Speter switch (uart_type) { 164934832Speter case 0: 165034832Speter return(" (SI)"); 165134832Speter case 40: 165234832Speter return(" (XIO)"); 165336856Sphk case 72: 165436856Sphk return(" (SXDC)"); 165534832Speter } 165634832Speter break; 165734832Speter } 165834832Speter return(""); 165934832Speter} 1660