si.c revision 132771
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 132771 2004-07-28 06:21:53Z kan $"); 37119419Sobrien 3810015Speter#ifndef lint 3934832Speterstatic const char si_copyright1[] = "@(#) Copyright (C) Specialix International, 1990,1992,1998", 4034832Speter si_copyright2[] = "@(#) Copyright (C) Andy Rutter 1993", 4156505Speter si_copyright3[] = "@(#) Copyright (C) Peter Wemm 2000"; 4210015Speter#endif /* not lint */ 4310015Speter 4431778Seivind#include "opt_compat.h" 4532929Seivind#include "opt_debug_si.h" 46111899Sdas#include "opt_tty.h" 4731778Seivind 4810015Speter#include <sys/param.h> 4910015Speter#include <sys/systm.h> 50130892Sphk#ifndef BURN_BRIDGES 51130344Sphk#if defined(COMPAT_43) 5224207Sbde#include <sys/ioctl_compat.h> 5324207Sbde#endif 54130892Sphk#endif 5510015Speter#include <sys/tty.h> 5610015Speter#include <sys/conf.h> 5724131Sbde#include <sys/fcntl.h> 5810015Speter#include <sys/kernel.h> 5910015Speter#include <sys/malloc.h> 6015683Speter#include <sys/sysctl.h> 6156498Speter#include <sys/bus.h> 6256498Speter#include <machine/bus.h> 6356498Speter#include <sys/rman.h> 6456498Speter#include <machine/resource.h> 6510015Speter 6610015Speter 6712659Sbde#include <vm/vm.h> 6812662Sdg#include <vm/pmap.h> 6912659Sbde 7013353Speter#include <machine/stdarg.h> 7110015Speter 7256498Speter#include <dev/si/sireg.h> 7356505Speter#include <dev/si/sivar.h> 7456498Speter#include <dev/si/si.h> 7556498Speter 7610015Speter/* 7710015Speter * This device driver is designed to interface the Specialix International 7834832Speter * SI, XIO and SX range of serial multiplexor cards to FreeBSD on an ISA, 7934832Speter * EISA or PCI bus machine. 8010015Speter * 8134832Speter * The controller is interfaced to the host via dual port RAM 8234832Speter * and an interrupt. 8333395Speter * 8434832Speter * The code for the Host 1 (very old ISA cards) has not been tested. 8510015Speter */ 8610015Speter 8717547Speter#define POLL /* turn on poller to scan for lost interrupts */ 8817547Speter#define REALPOLL /* on each poll, scan for work regardless */ 8917547Speter#define POLLHZ (hz/10) /* 10 times per second */ 9012496Speter#define SI_I_HIGH_WATER (TTYHOG - 2 * SI_BUFFERSIZE) 9134832Speter#define INT_COUNT 25000 /* max of 125 ints per second */ 9234832Speter#define JET_INT_COUNT 100 /* max of 100 ints per second */ 9315639Speter#define RXINT_COUNT 1 /* one rxint per 10 milliseconds */ 9410015Speter 9510015Speterenum si_mctl { GET, SET, BIS, BIC }; 9610015Speter 9756498Speterstatic void si_command(struct si_port *, int, int); 9856498Speterstatic int si_modem(struct si_port *, enum si_mctl, int); 9956498Speterstatic void si_write_enable(struct si_port *, int); 100130585Sphkstatic int si_Sioctl(struct cdev *, u_long, caddr_t, int, struct thread *); 10156498Speterstatic void si_start(struct tty *); 10256498Speterstatic void si_stop(struct tty *, int); 10325047Sbdestatic timeout_t si_lstart; 10456498Speterstatic void sihardclose(struct si_port *pp); 10510015Speter 10656505Speter#ifdef SI_DEBUG 10756505Speterstatic char *si_mctl2str(enum si_mctl cmd); 10856505Speter#endif 10956505Speter 11056498Speterstatic int siparam(struct tty *, struct termios *); 11110015Speter 11256498Speterstatic void si_modem_state(struct si_port *pp, struct tty *tp, int hi_ip); 11356498Speterstatic char * si_modulename(int host_type, int uart_type); 11410708Speter 11512675Sjulianstatic d_open_t siopen; 11612675Sjulianstatic d_close_t siclose; 11712675Sjulianstatic d_write_t siwrite; 11812675Sjulianstatic d_ioctl_t siioctl; 11912675Sjulian 12047625Sphkstatic struct cdevsw si_cdevsw = { 121126080Sphk .d_version = D_VERSION, 122111815Sphk .d_open = siopen, 123111815Sphk .d_close = siclose, 124111815Sphk .d_write = siwrite, 125111815Sphk .d_ioctl = siioctl, 126111815Sphk .d_name = "si", 127126080Sphk .d_flags = D_TTY | D_NEEDGIANT, 12838485Sbde}; 12912675Sjulian 13010962Speterstatic int si_Nports; 13110962Speterstatic int si_Nmodules; 13210962Speterstatic int si_debug = 0; /* data, not bss, so it's patchable */ 13310015Speter 13434832SpeterSYSCTL_INT(_machdep, OID_AUTO, si_debug, CTLFLAG_RW, &si_debug, 0, ""); 135100743SpeterTUNABLE_INT("machdep.si_debug", &si_debug); 13634832Speter 13756498Speterstatic int si_numunits; 13810044Speter 13956505Speterdevclass_t si_devclass; 14056498Speter 14112174Speter#ifndef B2000 /* not standard, but the hardware knows it. */ 14210015Speter# define B2000 2000 14310015Speter#endif 14410015Speterstatic struct speedtab bdrates[] = { 14550442Speter { B75, CLK75, }, /* 0x0 */ 14650442Speter { B110, CLK110, }, /* 0x1 */ 14750442Speter { B150, CLK150, }, /* 0x3 */ 14850442Speter { B300, CLK300, }, /* 0x4 */ 14950442Speter { B600, CLK600, }, /* 0x5 */ 15050442Speter { B1200, CLK1200, }, /* 0x6 */ 15150442Speter { B2000, CLK2000, }, /* 0x7 */ 15250442Speter { B2400, CLK2400, }, /* 0x8 */ 15350442Speter { B4800, CLK4800, }, /* 0x9 */ 15450442Speter { B9600, CLK9600, }, /* 0xb */ 15550442Speter { B19200, CLK19200, }, /* 0xc */ 15650442Speter { B38400, CLK38400, }, /* 0x2 (out of order!) */ 15750442Speter { B57600, CLK57600, }, /* 0xd */ 15850442Speter { B115200, CLK110, }, /* 0x1 (dupe!, 110 baud on "si") */ 15950442Speter { -1, -1 }, 16010015Speter}; 16110015Speter 16210015Speter 16310015Speter/* populated with approx character/sec rates - translated at card 16410015Speter * initialisation time to chars per tick of the clock */ 16510015Speterstatic int done_chartimes = 0; 16610015Speterstatic struct speedtab chartimes[] = { 16750442Speter { B75, 8, }, 16850442Speter { B110, 11, }, 16950442Speter { B150, 15, }, 17050442Speter { B300, 30, }, 17150442Speter { B600, 60, }, 17250442Speter { B1200, 120, }, 17350442Speter { B2000, 200, }, 17450442Speter { B2400, 240, }, 17550442Speter { B4800, 480, }, 17650442Speter { B9600, 960, }, 17750442Speter { B19200, 1920, }, 17850442Speter { B38400, 3840, }, 17950442Speter { B57600, 5760, }, 18050442Speter { B115200, 11520, }, 18150442Speter { -1, -1 }, 18210015Speter}; 18310015Speterstatic volatile int in_intr = 0; /* Inside interrupt handler? */ 18410015Speter 18510015Speter#ifdef POLL 18615683Speterstatic int si_pollrate; /* in addition to irq */ 18756498Speterstatic int si_realpoll = 0; /* poll HW on timer */ 18815639Speter 18916403SpeterSYSCTL_INT(_machdep, OID_AUTO, si_pollrate, CTLFLAG_RW, &si_pollrate, 0, ""); 19017547SpeterSYSCTL_INT(_machdep, OID_AUTO, si_realpoll, CTLFLAG_RW, &si_realpoll, 0, ""); 19150442Speter 19210015Speterstatic int init_finished = 0; 19356498Speterstatic void si_poll(void *); 19410015Speter#endif 19510015Speter 19610015Speter/* 19710015Speter * Array of adapter types and the corresponding RAM size. The order of 19810015Speter * entries here MUST match the ordinal of the adapter type. 19910015Speter */ 20010015Speterstatic char *si_type[] = { 20110015Speter "EMPTY", 20210015Speter "SIHOST", 20334832Speter "SIMCA", /* FreeBSD does not support Microchannel */ 20410015Speter "SIHOST2", 20510015Speter "SIEISA", 20633395Speter "SIPCI", 20733395Speter "SXPCI", 20833395Speter "SXISA", 20910015Speter}; 21010015Speter 21156498Speter/* 21256498Speter * We have to make an 8 bit version of bcopy, since some cards can't 21356498Speter * deal with 32 bit I/O 21456498Speter */ 21556498Speterstatic void __inline 21656498Spetersi_bcopy(const void *src, void *dst, size_t len) 21756498Speter{ 218132771Skan u_char *d; 219132771Skan const u_char *s; 220132771Skan 221132771Skan d = dst; 222132771Skan s = src; 22356498Speter while (len--) 224132771Skan *d++ = *s++; 22556498Speter} 22656498Speterstatic void __inline 22756498Spetersi_vbcopy(const volatile void *src, void *dst, size_t len) 22856498Speter{ 229132771Skan u_char *d; 230132771Skan const volatile u_char *s; 231132771Skan 232132771Skan d = dst; 233132771Skan s = src; 23456498Speter while (len--) 235132771Skan *d++ = *s++; 23656498Speter} 23756498Speterstatic void __inline 23856498Spetersi_bcopyv(const void *src, volatile void *dst, size_t len) 23956498Speter{ 240132771Skan volatile u_char *d; 241132771Skan const u_char *s; 242132771Skan 243132771Skan d = dst; 244132771Skan s = src; 24556498Speter while (len--) 246132771Skan *d++ = *s++; 24756498Speter} 24856498Speter 24934832Speter/* 25010015Speter * Attach the device. Initialize the card. 25110015Speter */ 25256505Speterint 25356498Spetersiattach(device_t dev) 25410015Speter{ 25556498Speter int unit; 25656498Speter struct si_softc *sc; 25710015Speter struct si_port *pp; 25810015Speter volatile struct si_channel *ccbp; 25910015Speter volatile struct si_reg *regp; 26010015Speter volatile caddr_t maddr; 26110015Speter struct si_module *modp; 26210015Speter struct speedtab *spt; 26310015Speter int nmodule, nport, x, y; 26412174Speter int uart_type; 26510015Speter 26656498Speter sc = device_get_softc(dev); 26756498Speter unit = device_get_unit(dev); 26810015Speter 26956505Speter sc->sc_typename = si_type[sc->sc_type]; 27056505Speter if (si_numunits < unit + 1) 27156505Speter si_numunits = unit + 1; 27256505Speter 27356498Speter DPRINT((0, DBG_AUTOBOOT, "si%d: siattach\n", unit)); 27410015Speter 27556498Speter#ifdef POLL 27656498Speter if (si_pollrate == 0) { 27756498Speter si_pollrate = POLLHZ; /* in addition to irq */ 27856498Speter#ifdef REALPOLL 27956498Speter si_realpoll = 1; /* scan always */ 28056498Speter#endif 28156498Speter } 28256498Speter#endif 28356498Speter 28433395Speter DPRINT((0, DBG_AUTOBOOT, "si%d: type: %s paddr: %x maddr: %x\n", unit, 28533395Speter sc->sc_typename, sc->sc_paddr, sc->sc_maddr)); 28633395Speter 28710015Speter sc->sc_ports = NULL; /* mark as uninitialised */ 28810015Speter 28910015Speter maddr = sc->sc_maddr; 29010015Speter 29134832Speter /* Stop the CPU first so it won't stomp around while we load */ 29234832Speter 29334832Speter switch (sc->sc_type) { 29434832Speter case SIEISA: 29556498Speter outb(sc->sc_iobase + 2, sc->sc_irq << 4); 29634832Speter break; 29734832Speter case SIPCI: 29834832Speter *(maddr+SIPCIRESET) = 0; 29934832Speter break; 30034832Speter case SIJETPCI: /* fall through to JET ISA */ 30134832Speter case SIJETISA: 30234832Speter *(maddr+SIJETCONFIG) = 0; 30334832Speter break; 30434832Speter case SIHOST2: 30534832Speter *(maddr+SIPLRESET) = 0; 30634832Speter break; 30734832Speter case SIHOST: 30834832Speter *(maddr+SIRESET) = 0; 30934832Speter break; 31034832Speter default: /* this should never happen */ 31134832Speter printf("si%d: unsupported configuration\n", unit); 31256498Speter return EINVAL; 31334832Speter break; 31434832Speter } 31534832Speter 31634832Speter /* OK, now lets download the download code */ 31734832Speter 31836956Ssteve if (SI_ISJET(sc->sc_type)) { 31933395Speter DPRINT((0, DBG_DOWNLOAD, "si%d: jet_download: nbytes %d\n", 32056498Speter unit, si3_t225_dsize)); 32134832Speter si_bcopy(si3_t225_download, maddr + si3_t225_downloadaddr, 32234832Speter si3_t225_dsize); 32334832Speter DPRINT((0, DBG_DOWNLOAD, 32434832Speter "si%d: jet_bootstrap: nbytes %d -> %x\n", 32556498Speter unit, si3_t225_bsize, si3_t225_bootloadaddr)); 32634832Speter si_bcopy(si3_t225_bootstrap, maddr + si3_t225_bootloadaddr, 32734832Speter si3_t225_bsize); 32834832Speter } else { 32933395Speter DPRINT((0, DBG_DOWNLOAD, "si%d: si_download: nbytes %d\n", 33056498Speter unit, si2_z280_dsize)); 33134832Speter si_bcopy(si2_z280_download, maddr + si2_z280_downloadaddr, 33234832Speter si2_z280_dsize); 33333395Speter } 33410015Speter 33534832Speter /* Now start the CPU */ 33634832Speter 33710015Speter switch (sc->sc_type) { 33810015Speter case SIEISA: 33934832Speter /* modify the download code to tell it that it's on an EISA */ 34034832Speter *(maddr + 0x42) = 1; 34156498Speter outb(sc->sc_iobase + 2, (sc->sc_irq << 4) | 4); 34256498Speter (void)inb(sc->sc_iobase + 3); /* reset interrupt */ 34310015Speter break; 34433395Speter case SIPCI: 34534832Speter /* modify the download code to tell it that it's on a PCI */ 34633395Speter *(maddr+0x42) = 1; 34733395Speter *(maddr+SIPCIRESET) = 1; 34833395Speter *(maddr+SIPCIINTCL) = 0; 34933395Speter break; 35033395Speter case SIJETPCI: 35133395Speter *(maddr+SIJETRESET) = 0; 35233395Speter *(maddr+SIJETCONFIG) = SIJETBUSEN|SIJETIRQEN; 35333395Speter break; 35433395Speter case SIJETISA: 35533395Speter *(maddr+SIJETRESET) = 0; 35634832Speter switch (sc->sc_irq) { 35756498Speter case 9: 35834832Speter *(maddr+SIJETCONFIG) = SIJETBUSEN|SIJETIRQEN|0x90; 35934832Speter break; 36056498Speter case 10: 36134832Speter *(maddr+SIJETCONFIG) = SIJETBUSEN|SIJETIRQEN|0xa0; 36234832Speter break; 36356498Speter case 11: 36434832Speter *(maddr+SIJETCONFIG) = SIJETBUSEN|SIJETIRQEN|0xb0; 36534832Speter break; 36656498Speter case 12: 36734832Speter *(maddr+SIJETCONFIG) = SIJETBUSEN|SIJETIRQEN|0xc0; 36834832Speter break; 36956498Speter case 15: 37034832Speter *(maddr+SIJETCONFIG) = SIJETBUSEN|SIJETIRQEN|0xf0; 37134832Speter break; 37234832Speter } 37333395Speter break; 37410015Speter case SIHOST: 37510015Speter *(maddr+SIRESET_CL) = 0; 37610015Speter *(maddr+SIINTCL_CL) = 0; 37710015Speter break; 37810015Speter case SIHOST2: 37910015Speter *(maddr+SIPLRESET) = 0x10; 38010015Speter switch (sc->sc_irq) { 38156498Speter case 11: 38210015Speter *(maddr+SIPLIRQ11) = 0x10; 38310015Speter break; 38456498Speter case 12: 38510015Speter *(maddr+SIPLIRQ12) = 0x10; 38610015Speter break; 38756498Speter case 15: 38810015Speter *(maddr+SIPLIRQ15) = 0x10; 38910015Speter break; 39010015Speter } 39110015Speter *(maddr+SIPLIRQCLR) = 0x10; 39210015Speter break; 39334832Speter default: /* this should _REALLY_ never happen */ 39434832Speter printf("si%d: Uh, it was supported a second ago...\n", unit); 39556498Speter return EINVAL; 39610015Speter } 39710015Speter 39810015Speter DELAY(1000000); /* wait around for a second */ 39910015Speter 40010015Speter regp = (struct si_reg *)maddr; 40110015Speter y = 0; 40210015Speter /* wait max of 5 sec for init OK */ 40310015Speter while (regp->initstat == 0 && y++ < 10) { 40410015Speter DELAY(500000); 40510015Speter } 40610015Speter switch (regp->initstat) { 40710015Speter case 0: 40810015Speter printf("si%d: startup timeout - aborting\n", unit); 40912174Speter sc->sc_type = SIEMPTY; 41056498Speter return EINVAL; 41110015Speter case 1: 41236956Ssteve if (SI_ISJET(sc->sc_type)) { 41334832Speter /* set throttle to 100 times per second */ 41434832Speter regp->int_count = JET_INT_COUNT; 41534832Speter /* rx_intr_count is a NOP in Jet */ 41634832Speter } else { 41734832Speter /* set throttle to 125 times per second */ 41834832Speter regp->int_count = INT_COUNT; 41934832Speter /* rx intr max of 25 times per second */ 42034832Speter regp->rx_int_count = RXINT_COUNT; 42134832Speter } 42210015Speter regp->int_pending = 0; /* no intr pending */ 42310015Speter regp->int_scounter = 0; /* reset counter */ 42410015Speter break; 42510015Speter case 0xff: 42610015Speter /* 42710015Speter * No modules found, so give up on this one. 42810015Speter */ 42910015Speter printf("si%d: %s - no ports found\n", unit, 43010015Speter si_type[sc->sc_type]); 43110015Speter return 0; 43210015Speter default: 43334832Speter printf("si%d: download code version error - initstat %x\n", 43410015Speter unit, regp->initstat); 43556498Speter return EINVAL; 43610015Speter } 43710015Speter 43810015Speter /* 43910015Speter * First time around the ports just count them in order 44010015Speter * to allocate some memory. 44110015Speter */ 44210015Speter nport = 0; 44310015Speter modp = (struct si_module *)(maddr + 0x80); 44410015Speter for (;;) { 44512174Speter DPRINT((0, DBG_DOWNLOAD, "si%d: ccb addr 0x%x\n", unit, modp)); 44634832Speter switch (modp->sm_type) { 44734832Speter case TA4: 44810015Speter DPRINT((0, DBG_DOWNLOAD, 44934832Speter "si%d: Found old TA4 module, 4 ports\n", 45034832Speter unit)); 45134832Speter x = 4; 45210015Speter break; 45334832Speter case TA8: 45434832Speter DPRINT((0, DBG_DOWNLOAD, 45534832Speter "si%d: Found old TA8 module, 8 ports\n", 45634832Speter unit)); 45734832Speter x = 8; 45834832Speter break; 45934832Speter case TA4_ASIC: 46034832Speter DPRINT((0, DBG_DOWNLOAD, 46134832Speter "si%d: Found ASIC TA4 module, 4 ports\n", 46234832Speter unit)); 46334832Speter x = 4; 46434832Speter break; 46534832Speter case TA8_ASIC: 46634832Speter DPRINT((0, DBG_DOWNLOAD, 46734832Speter "si%d: Found ASIC TA8 module, 8 ports\n", 46834832Speter unit)); 46934832Speter x = 8; 47034832Speter break; 47134832Speter case MTA: 47234832Speter DPRINT((0, DBG_DOWNLOAD, 47334832Speter "si%d: Found CD1400 module, 8 ports\n", 47434832Speter unit)); 47534832Speter x = 8; 47634832Speter break; 47734832Speter case SXDC: 47834832Speter DPRINT((0, DBG_DOWNLOAD, 47934832Speter "si%d: Found SXDC module, 8 ports\n", 48034832Speter unit)); 48134832Speter x = 8; 48234832Speter break; 48310015Speter default: 48410015Speter printf("si%d: unknown module type %d\n", 48510015Speter unit, modp->sm_type); 48634832Speter goto try_next; 48710015Speter } 48834832Speter 48934832Speter /* this was limited in firmware and is also a driver issue */ 49034832Speter if ((nport + x) > SI_MAXPORTPERCARD) { 49134832Speter printf("si%d: extra ports ignored\n", unit); 49234832Speter goto try_next; 49334832Speter } 49434832Speter 49534832Speter nport += x; 49634832Speter si_Nports += x; 49734832Speter si_Nmodules++; 49834832Speter 49934832Spetertry_next: 50010015Speter if (modp->sm_next == 0) 50110015Speter break; 50210015Speter modp = (struct si_module *) 50310015Speter (maddr + (unsigned)(modp->sm_next & 0x7fff)); 50410015Speter } 50510015Speter sc->sc_ports = (struct si_port *)malloc(sizeof(struct si_port) * nport, 50669781Sdwmalone M_DEVBUF, M_NOWAIT | M_ZERO); 50710015Speter if (sc->sc_ports == 0) { 50810015Speter printf("si%d: fail to malloc memory for port structs\n", 50910015Speter unit); 51056498Speter return EINVAL; 51110015Speter } 51210015Speter sc->sc_nport = nport; 51310015Speter 51410015Speter /* 51510015Speter * Scan round the ports again, this time initialising. 51610015Speter */ 51710015Speter pp = sc->sc_ports; 51810015Speter nmodule = 0; 51910015Speter modp = (struct si_module *)(maddr + 0x80); 52034832Speter uart_type = 1000; /* arbitary, > uchar_max */ 52110015Speter for (;;) { 52234832Speter switch (modp->sm_type) { 52334832Speter case TA4: 52434832Speter nport = 4; 52510015Speter break; 52634832Speter case TA8: 52734832Speter nport = 8; 52834832Speter break; 52934832Speter case TA4_ASIC: 53034832Speter nport = 4; 53134832Speter break; 53234832Speter case TA8_ASIC: 53334832Speter nport = 8; 53434832Speter break; 53534832Speter case MTA: 53634832Speter nport = 8; 53734832Speter break; 53834832Speter case SXDC: 53934832Speter nport = 8; 54034832Speter break; 54110015Speter default: 54234832Speter goto try_next2; 54310015Speter } 54434832Speter nmodule++; 54534832Speter ccbp = (struct si_channel *)((char *)modp + 0x100); 54634832Speter if (uart_type == 1000) 54734832Speter uart_type = ccbp->type; 54834832Speter else if (uart_type != ccbp->type) 54934832Speter printf("si%d: Warning: module %d mismatch! (%d%s != %d%s)\n", 55034832Speter unit, nmodule, 55134832Speter ccbp->type, si_modulename(sc->sc_type, ccbp->type), 55234832Speter uart_type, si_modulename(sc->sc_type, uart_type)); 55334832Speter 55434832Speter for (x = 0; x < nport; x++, pp++, ccbp++) { 55534832Speter pp->sp_ccb = ccbp; /* save the address */ 55672685Speter pp->sp_tty = ttymalloc(NULL); 55734832Speter pp->sp_pend = IDLE_CLOSE; 55834832Speter pp->sp_state = 0; /* internal flag */ 55934832Speter pp->sp_iin.c_iflag = TTYDEF_IFLAG; 56034832Speter pp->sp_iin.c_oflag = TTYDEF_OFLAG; 56134832Speter pp->sp_iin.c_cflag = TTYDEF_CFLAG; 56234832Speter pp->sp_iin.c_lflag = TTYDEF_LFLAG; 56334832Speter termioschars(&pp->sp_iin); 56434832Speter pp->sp_iin.c_ispeed = pp->sp_iin.c_ospeed = 56534832Speter TTYDEF_SPEED;; 56634832Speter pp->sp_iout = pp->sp_iin; 56734832Speter } 56834832Spetertry_next2: 56910015Speter if (modp->sm_next == 0) { 57034832Speter printf("si%d: card: %s, ports: %d, modules: %d, type: %d%s\n", 57110015Speter unit, 57210015Speter sc->sc_typename, 57310015Speter sc->sc_nport, 57412174Speter nmodule, 57534832Speter uart_type, 57634832Speter si_modulename(sc->sc_type, uart_type)); 57710015Speter break; 57810015Speter } 57910015Speter modp = (struct si_module *) 58010015Speter (maddr + (unsigned)(modp->sm_next & 0x7fff)); 58110015Speter } 58210015Speter if (done_chartimes == 0) { 58310015Speter for (spt = chartimes ; spt->sp_speed != -1; spt++) { 58410015Speter if ((spt->sp_code /= hz) == 0) 58510015Speter spt->sp_code = 1; 58610015Speter } 58710015Speter done_chartimes = 1; 58810015Speter } 58912502Sjulian 59012675Sjulian/* path name devsw minor type uid gid perm*/ 59150442Speter for (x = 0; x < sc->sc_nport; x++) { 59234735Speter /* sync with the manuals that start at 1 */ 59356498Speter y = x + 1 + unit * (1 << SI_CARDSHIFT); 59450442Speter make_dev(&si_cdevsw, x, 0, 0, 0600, "ttyA%02d", y); 59550442Speter make_dev(&si_cdevsw, x + 0x00080, 0, 0, 0600, "cuaA%02d", y); 59650442Speter make_dev(&si_cdevsw, x + 0x10000, 0, 0, 0600, "ttyiA%02d", y); 59750442Speter make_dev(&si_cdevsw, x + 0x10080, 0, 0, 0600, "cuaiA%02d", y); 59850442Speter make_dev(&si_cdevsw, x + 0x20000, 0, 0, 0600, "ttylA%02d", y); 59950442Speter make_dev(&si_cdevsw, x + 0x20080, 0, 0, 0600, "cualA%02d", y); 60012675Sjulian } 60150254Sphk make_dev(&si_cdevsw, 0x40000, 0, 0, 0600, "si_control"); 60256498Speter return (0); 60310015Speter} 60410015Speter 60512675Sjulianstatic int 606130585Sphksiopen(struct cdev *dev, int flag, int mode, struct thread *td) 60710015Speter{ 60810015Speter int oldspl, error; 60910015Speter int card, port; 61056498Speter struct si_softc *sc; 61156498Speter struct tty *tp; 61210015Speter volatile struct si_channel *ccbp; 61310015Speter struct si_port *pp; 61410015Speter int mynor = minor(dev); 61510015Speter 61610015Speter /* quickly let in /dev/si_control */ 61710015Speter if (IS_CONTROLDEV(mynor)) { 61893593Sjhb if ((error = suser(td))) 61910015Speter return(error); 62010015Speter return(0); 62110015Speter } 62210015Speter 62310015Speter card = SI_CARD(mynor); 62456498Speter sc = devclass_get_softc(si_devclass, card); 62556498Speter if (sc == NULL) 62610015Speter return (ENXIO); 62710015Speter 62812174Speter if (sc->sc_type == SIEMPTY) { 62912174Speter DPRINT((0, DBG_OPEN|DBG_FAIL, "si%d: type %s??\n", 63010015Speter card, sc->sc_typename)); 63110015Speter return(ENXIO); 63210015Speter } 63310015Speter 63410015Speter port = SI_PORT(mynor); 63510015Speter if (port >= sc->sc_nport) { 63612174Speter DPRINT((0, DBG_OPEN|DBG_FAIL, "si%d: nports %d\n", 63710015Speter card, sc->sc_nport)); 63810015Speter return(ENXIO); 63910015Speter } 64010015Speter 64110015Speter#ifdef POLL 64210015Speter /* 64310015Speter * We've now got a device, so start the poller. 64410015Speter */ 64510015Speter if (init_finished == 0) { 64615639Speter timeout(si_poll, (caddr_t)0L, si_pollrate); 64710015Speter init_finished = 1; 64810015Speter } 64910015Speter#endif 65010015Speter 65110015Speter /* initial/lock device */ 65210015Speter if (IS_STATE(mynor)) { 65310015Speter return(0); 65410015Speter } 65510015Speter 65610015Speter pp = sc->sc_ports + port; 65710015Speter tp = pp->sp_tty; /* the "real" tty */ 65851654Sphk dev->si_tty = tp; 65910015Speter ccbp = pp->sp_ccb; /* Find control block */ 66050016Snsayer DPRINT((pp, DBG_ENTRY|DBG_OPEN, "siopen(%s,%x,%x,%x)\n", 66183366Sjulian devtoname(dev), flag, mode, td)); 66210015Speter 66310015Speter oldspl = spltty(); /* Keep others out */ 66410015Speter error = 0; 66510015Speter 66610015Speteropen_top: 667131981Sphk error = ttydtrwaitsleep(tp); 668131981Sphk if (error != 0) 669131981Sphk goto out; 67010015Speter 67110015Speter if (tp->t_state & TS_ISOPEN) { 67210015Speter /* 67310015Speter * The device is open, so everything has been initialised. 67410015Speter * handle conflicts. 67510015Speter */ 67610015Speter if (IS_CALLOUT(mynor)) { 67710015Speter if (!pp->sp_active_out) { 67810015Speter error = EBUSY; 67910015Speter goto out; 68010015Speter } 68110015Speter } else { 68210015Speter if (pp->sp_active_out) { 68310015Speter if (flag & O_NONBLOCK) { 68410015Speter error = EBUSY; 68510015Speter goto out; 68610015Speter } 68710015Speter error = tsleep(&pp->sp_active_out, 68810015Speter TTIPRI|PCATCH, "sibi", 0); 68910015Speter if (error != 0) 69010015Speter goto out; 69110015Speter goto open_top; 69210015Speter } 69310015Speter } 69443425Sphk if (tp->t_state & TS_XCLUDE && 69593593Sjhb suser(td)) { 69610015Speter DPRINT((pp, DBG_OPEN|DBG_FAIL, 69710015Speter "already open and EXCLUSIVE set\n")); 69810015Speter error = EBUSY; 69910015Speter goto out; 70010015Speter } 70110015Speter } else { 70210015Speter /* 70310015Speter * The device isn't open, so there are no conflicts. 70410015Speter * Initialize it. Avoid sleep... :-) 70510015Speter */ 70610015Speter DPRINT((pp, DBG_OPEN, "first open\n")); 70710015Speter tp->t_oproc = si_start; 70851654Sphk tp->t_stop = si_stop; 70910015Speter tp->t_param = siparam; 71010015Speter tp->t_dev = dev; 71110015Speter tp->t_termios = mynor & SI_CALLOUT_MASK 71210015Speter ? pp->sp_iout : pp->sp_iin; 71310015Speter 71410015Speter (void) si_modem(pp, SET, TIOCM_DTR|TIOCM_RTS); 71510015Speter 71610015Speter ++pp->sp_wopeners; /* in case of sleep in siparam */ 71710015Speter 71810015Speter error = siparam(tp, &tp->t_termios); 71910015Speter 72010015Speter --pp->sp_wopeners; 72110015Speter if (error != 0) 72210015Speter goto out; 72310015Speter /* XXX: we should goto_top if siparam slept */ 72410015Speter 72510015Speter /* set initial DCD state */ 72610015Speter pp->sp_last_hi_ip = ccbp->hi_ip; 72710015Speter if ((pp->sp_last_hi_ip & IP_DCD) || IS_CALLOUT(mynor)) { 728130077Sphk ttyld_modem(tp, 1); 72910015Speter } 73010015Speter } 73110015Speter 73210015Speter /* whoops! we beat the close! */ 73310015Speter if (pp->sp_state & SS_CLOSING) { 73410015Speter /* try and stop it from proceeding to bash the hardware */ 73510015Speter pp->sp_state &= ~SS_CLOSING; 73610015Speter } 73710015Speter 73810015Speter /* 73910015Speter * Wait for DCD if necessary 74010015Speter */ 74150442Speter if (!(tp->t_state & TS_CARR_ON) && 74250442Speter !IS_CALLOUT(mynor) && 74350442Speter !(tp->t_cflag & CLOCAL) && 74450442Speter !(flag & O_NONBLOCK)) { 74510015Speter ++pp->sp_wopeners; 74610015Speter DPRINT((pp, DBG_OPEN, "sleeping for carrier\n")); 74710015Speter error = tsleep(TSA_CARR_ON(tp), TTIPRI|PCATCH, "sidcd", 0); 74810015Speter --pp->sp_wopeners; 74910015Speter if (error != 0) 75010015Speter goto out; 75110015Speter goto open_top; 75210015Speter } 75310015Speter 754130077Sphk error = ttyld_open(tp, dev); 755131134Sphk ttyldoptim(tp); 75610015Speter if (tp->t_state & TS_ISOPEN && IS_CALLOUT(mynor)) 75710015Speter pp->sp_active_out = TRUE; 75810015Speter 75910015Speter pp->sp_state |= SS_OPEN; /* made it! */ 76010015Speter 76110015Speterout: 76210015Speter splx(oldspl); 76310015Speter 76410015Speter DPRINT((pp, DBG_OPEN, "leaving siopen\n")); 76510015Speter 76610015Speter if (!(tp->t_state & TS_ISOPEN) && pp->sp_wopeners == 0) 76710015Speter sihardclose(pp); 76810015Speter 76910015Speter return(error); 77010015Speter} 77110015Speter 77212675Sjulianstatic int 773130585Sphksiclose(struct cdev *dev, int flag, int mode, struct thread *td) 77410015Speter{ 77556498Speter struct si_port *pp; 77656498Speter struct tty *tp; 77710015Speter int oldspl; 77810015Speter int error = 0; 77910015Speter int mynor = minor(dev); 78010015Speter 78110015Speter if (IS_SPECIAL(mynor)) 78210015Speter return(0); 78310015Speter 78410015Speter oldspl = spltty(); 78510015Speter 78610015Speter pp = MINOR2PP(mynor); 78710015Speter tp = pp->sp_tty; 78810015Speter 78950016Snsayer DPRINT((pp, DBG_ENTRY|DBG_CLOSE, "siclose(%s,%x,%x,%x) sp_state:%x\n", 79083366Sjulian devtoname(dev), flag, mode, td, pp->sp_state)); 79110015Speter 79210015Speter /* did we sleep and loose a race? */ 79310015Speter if (pp->sp_state & SS_CLOSING) { 79410015Speter /* error = ESOMETING? */ 79510015Speter goto out; 79610015Speter } 79710015Speter 79810015Speter /* begin race detection.. */ 79910015Speter pp->sp_state |= SS_CLOSING; 80010015Speter 80110015Speter si_write_enable(pp, 0); /* block writes for ttywait() */ 80210015Speter 80310015Speter /* THIS MAY SLEEP IN TTYWAIT!!! */ 804130077Sphk ttyld_close(tp, flag); 80510015Speter 80610015Speter si_write_enable(pp, 1); 80710015Speter 80810015Speter /* did we sleep and somebody started another open? */ 80910015Speter if (!(pp->sp_state & SS_CLOSING)) { 81010015Speter /* error = ESOMETING? */ 81110015Speter goto out; 81210015Speter } 81310015Speter /* ok. we are now still on the right track.. nuke the hardware */ 81410015Speter 81510015Speter if (pp->sp_state & SS_LSTART) { 81629677Sgibbs untimeout(si_lstart, (caddr_t)pp, pp->lstart_ch); 81710015Speter pp->sp_state &= ~SS_LSTART; 81810015Speter } 81910015Speter 82010015Speter sihardclose(pp); 821132226Sphk tty_close(tp); 82210015Speter pp->sp_state &= ~SS_OPEN; 82310015Speter 82410015Speterout: 82510015Speter DPRINT((pp, DBG_CLOSE|DBG_EXIT, "close done, returning\n")); 82610015Speter splx(oldspl); 82710015Speter return(error); 82810015Speter} 82910015Speter 83010015Speterstatic void 83156498Spetersihardclose(struct si_port *pp) 83210015Speter{ 83310015Speter int oldspl; 83410015Speter struct tty *tp; 83510015Speter volatile struct si_channel *ccbp; 83610015Speter 83710015Speter oldspl = spltty(); 83810015Speter 83910015Speter tp = pp->sp_tty; 84010015Speter ccbp = pp->sp_ccb; /* Find control block */ 84150442Speter if (tp->t_cflag & HUPCL || 84250442Speter (!pp->sp_active_out && 84350442Speter !(ccbp->hi_ip & IP_DCD) && 84450442Speter !(pp->sp_iin.c_cflag && CLOCAL)) || 84550442Speter !(tp->t_state & TS_ISOPEN)) { 84610015Speter 84710015Speter (void) si_modem(pp, BIC, TIOCM_DTR|TIOCM_RTS); 84810015Speter (void) si_command(pp, FCLOSE, SI_NOWAIT); 84910015Speter 850131981Sphk ttydtrwaitstart(tp); 85110015Speter } 85210015Speter pp->sp_active_out = FALSE; 853111748Sdes wakeup(&pp->sp_active_out); 85410015Speter wakeup(TSA_CARR_ON(tp)); 85510015Speter 85610015Speter splx(oldspl); 85710015Speter} 85810015Speter 85912675Sjulianstatic int 860130585Sphksiwrite(struct cdev *dev, struct uio *uio, int flag) 86110015Speter{ 86256498Speter struct si_port *pp; 86356498Speter struct tty *tp; 86410015Speter int error = 0; 86510015Speter int mynor = minor(dev); 86610015Speter int oldspl; 86710015Speter 86810015Speter if (IS_SPECIAL(mynor)) { 86910015Speter DPRINT((0, DBG_ENTRY|DBG_FAIL|DBG_WRITE, "siwrite(CONTROLDEV!!)\n")); 87010015Speter return(ENODEV); 87110015Speter } 87210015Speter pp = MINOR2PP(mynor); 87310015Speter tp = pp->sp_tty; 87450016Snsayer DPRINT((pp, DBG_WRITE, "siwrite(%s,%x,%x)\n", devtoname(dev), uio, flag)); 87510015Speter 87610015Speter oldspl = spltty(); 87710015Speter /* 87810015Speter * If writes are currently blocked, wait on the "real" tty 87910015Speter */ 88010015Speter while (pp->sp_state & SS_BLOCKWRITE) { 88110015Speter pp->sp_state |= SS_WAITWRITE; 88210015Speter DPRINT((pp, DBG_WRITE, "in siwrite, wait for SS_BLOCKWRITE to clear\n")); 88318515Speter if ((error = ttysleep(tp, (caddr_t)pp, TTOPRI|PCATCH, 88418515Speter "siwrite", tp->t_timeout))) { 88517291Speter if (error == EWOULDBLOCK) 88617290Speter error = EIO; 88710015Speter goto out; 88817290Speter } 88910015Speter } 89010015Speter 891130077Sphk error = ttyld_write(tp, uio, flag); 89210015Speterout: 89310015Speter splx(oldspl); 89410015Speter return (error); 89510015Speter} 89610015Speter 89710015Speter 89812675Sjulianstatic int 899130585Sphksiioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) 90010015Speter{ 90110015Speter struct si_port *pp; 90256498Speter struct tty *tp; 90310015Speter int error; 90410015Speter int mynor = minor(dev); 90510015Speter int oldspl; 90610015Speter int blocked = 0; 907130892Sphk#ifndef BURN_BRIDGES 90810015Speter#if defined(COMPAT_43) 90938351Sbde u_long oldcmd; 91010015Speter struct termios term; 91110015Speter#endif 912130892Sphk#endif 91310015Speter 91410015Speter if (IS_SI_IOCTL(cmd)) 91583366Sjulian return(si_Sioctl(dev, cmd, data, flag, td)); 91610015Speter 91710015Speter pp = MINOR2PP(mynor); 91810015Speter tp = pp->sp_tty; 91910015Speter 92050016Snsayer DPRINT((pp, DBG_ENTRY|DBG_IOCTL, "siioctl(%s,%lx,%x,%x)\n", 92150016Snsayer devtoname(dev), cmd, data, flag)); 92210015Speter if (IS_STATE(mynor)) { 92310015Speter struct termios *ct; 92410015Speter 92510015Speter switch (mynor & SI_STATE_MASK) { 92610015Speter case SI_INIT_STATE_MASK: 92710015Speter ct = IS_CALLOUT(mynor) ? &pp->sp_iout : &pp->sp_iin; 92810015Speter break; 92910015Speter case SI_LOCK_STATE_MASK: 93016839Speter ct = IS_CALLOUT(mynor) ? &pp->sp_lout : &pp->sp_lin; 93110015Speter break; 93210015Speter default: 93310015Speter return (ENODEV); 93410015Speter } 93510015Speter switch (cmd) { 93610015Speter case TIOCSETA: 93793593Sjhb error = suser(td); 93810015Speter if (error != 0) 93910015Speter return (error); 94010015Speter *ct = *(struct termios *)data; 94110015Speter return (0); 94210015Speter case TIOCGETA: 94310015Speter *(struct termios *)data = *ct; 94410015Speter return (0); 94510015Speter case TIOCGETD: 94610015Speter *(int *)data = TTYDISC; 94710015Speter return (0); 94810015Speter case TIOCGWINSZ: 94910015Speter bzero(data, sizeof(struct winsize)); 95010015Speter return (0); 95110015Speter default: 95210015Speter return (ENOTTY); 95310015Speter } 95410015Speter } 95510015Speter /* 95610015Speter * Do the old-style ioctl compat routines... 95710015Speter */ 958130892Sphk#ifndef BURN_BRIDGES 95910015Speter#if defined(COMPAT_43) 96010015Speter term = tp->t_termios; 96110015Speter oldcmd = cmd; 96210015Speter error = ttsetcompat(tp, &cmd, data, &term); 96310015Speter if (error != 0) 96410015Speter return (error); 96510015Speter if (cmd != oldcmd) 96610015Speter data = (caddr_t)&term; 96710015Speter#endif 968130892Sphk#endif 96910015Speter /* 97010015Speter * Do the initial / lock state business 97110015Speter */ 97210015Speter if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { 97310015Speter int cc; 97410015Speter struct termios *dt = (struct termios *)data; 97510015Speter struct termios *lt = mynor & SI_CALLOUT_MASK 97610015Speter ? &pp->sp_lout : &pp->sp_lin; 97710015Speter 97850442Speter dt->c_iflag = (tp->t_iflag & lt->c_iflag) | 97950442Speter (dt->c_iflag & ~lt->c_iflag); 98050442Speter dt->c_oflag = (tp->t_oflag & lt->c_oflag) | 98150442Speter (dt->c_oflag & ~lt->c_oflag); 98250442Speter dt->c_cflag = (tp->t_cflag & lt->c_cflag) | 98350442Speter (dt->c_cflag & ~lt->c_cflag); 98450442Speter dt->c_lflag = (tp->t_lflag & lt->c_lflag) | 98550442Speter (dt->c_lflag & ~lt->c_lflag); 98610015Speter for (cc = 0; cc < NCCS; ++cc) 98710015Speter if (lt->c_cc[cc] != 0) 98810015Speter dt->c_cc[cc] = tp->t_cc[cc]; 98910015Speter if (lt->c_ispeed != 0) 99010015Speter dt->c_ispeed = tp->t_ispeed; 99110015Speter if (lt->c_ospeed != 0) 99210015Speter dt->c_ospeed = tp->t_ospeed; 99310015Speter } 99410015Speter 99510015Speter /* 99610015Speter * Block user-level writes to give the ttywait() 99710015Speter * a chance to completely drain for commands 99810015Speter * that require the port to be in a quiescent state. 99910015Speter */ 100010015Speter switch (cmd) { 100134832Speter case TIOCSETAW: 100234832Speter case TIOCSETAF: 100317396Speter case TIOCDRAIN: 1004130892Sphk#ifndef BURN_BRIDGES 100517396Speter#ifdef COMPAT_43 100617396Speter case TIOCSETP: 100717396Speter#endif 1008130892Sphk#endif 100910015Speter blocked++; /* block writes for ttywait() and siparam() */ 101010015Speter si_write_enable(pp, 0); 101110015Speter } 101210015Speter 1013130057Sphk error = ttyioctl(dev, cmd, data, flag, td); 1014131134Sphk ttyldoptim(tp); 1015130057Sphk if (error != ENOTTY) 101610015Speter goto out; 101710015Speter 101810015Speter oldspl = spltty(); 101910015Speter 102050435Speter error = 0; 102110015Speter switch (cmd) { 102210015Speter case TIOCSBRK: 102316575Speter si_command(pp, SBREAK, SI_WAIT); 102410015Speter break; 102510015Speter case TIOCCBRK: 102616575Speter si_command(pp, EBREAK, SI_WAIT); 102710015Speter break; 102810015Speter case TIOCSDTR: 102910015Speter (void) si_modem(pp, SET, TIOCM_DTR|TIOCM_RTS); 103010015Speter break; 103110015Speter case TIOCCDTR: 103210015Speter (void) si_modem(pp, SET, 0); 103310015Speter break; 103410015Speter case TIOCMSET: 103510015Speter (void) si_modem(pp, SET, *(int *)data); 103610015Speter break; 103710015Speter case TIOCMBIS: 103810015Speter (void) si_modem(pp, BIS, *(int *)data); 103910015Speter break; 104010015Speter case TIOCMBIC: 104110015Speter (void) si_modem(pp, BIC, *(int *)data); 104210015Speter break; 104310015Speter case TIOCMGET: 104410015Speter *(int *)data = si_modem(pp, GET, 0); 104510015Speter break; 104610015Speter default: 104710015Speter error = ENOTTY; 104810015Speter } 104910015Speter splx(oldspl); 105050435Speter 105110015Speterout: 105210015Speter DPRINT((pp, DBG_IOCTL|DBG_EXIT, "siioctl ret %d\n", error)); 105310015Speter if (blocked) 105410015Speter si_write_enable(pp, 1); 105510015Speter return(error); 105610015Speter} 105710015Speter 105810015Speter/* 105910015Speter * Handle the Specialix ioctls. All MUST be called via the CONTROL device 106010015Speter */ 106110015Speterstatic int 1062130585Sphksi_Sioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) 106310015Speter{ 106410015Speter struct si_softc *xsc; 106556498Speter struct si_port *xpp; 106610015Speter volatile struct si_reg *regp; 106710015Speter struct si_tcsi *dp; 106810044Speter struct si_pstat *sps; 106911872Sphk int *ip, error = 0; 107010015Speter int oldspl; 107110015Speter int card, port; 107210015Speter int mynor = minor(dev); 107310015Speter 107450016Snsayer DPRINT((0, DBG_ENTRY|DBG_IOCTL, "si_Sioctl(%s,%lx,%x,%x)\n", 107550016Snsayer devtoname(dev), cmd, data, flag)); 107610015Speter 107710044Speter#if 1 107810044Speter DPRINT((0, DBG_IOCTL, "TCSI_PORT=%x\n", TCSI_PORT)); 107910044Speter DPRINT((0, DBG_IOCTL, "TCSI_CCB=%x\n", TCSI_CCB)); 108010044Speter DPRINT((0, DBG_IOCTL, "TCSI_TTY=%x\n", TCSI_TTY)); 108110044Speter#endif 108210044Speter 108310015Speter if (!IS_CONTROLDEV(mynor)) { 108410015Speter DPRINT((0, DBG_IOCTL|DBG_FAIL, "not called from control device!\n")); 108510015Speter return(ENODEV); 108610015Speter } 108710015Speter 108810015Speter oldspl = spltty(); /* better safe than sorry */ 108910015Speter 109010015Speter ip = (int *)data; 109110015Speter 109293593Sjhb#define SUCHECK if ((error = suser(td))) goto out 109310015Speter 109410015Speter switch (cmd) { 109510015Speter case TCSIPORTS: 109610015Speter *ip = si_Nports; 109710015Speter goto out; 109810015Speter case TCSIMODULES: 109910015Speter *ip = si_Nmodules; 110010015Speter goto out; 110110015Speter case TCSISDBG_ALL: 110210015Speter SUCHECK; 110310015Speter si_debug = *ip; 110410015Speter goto out; 110510015Speter case TCSIGDBG_ALL: 110610015Speter *ip = si_debug; 110710015Speter goto out; 110810015Speter default: 110910015Speter /* 111010015Speter * Check that a controller for this port exists 111110015Speter */ 111210044Speter 111310044Speter /* may also be a struct si_pstat, a superset of si_tcsi */ 111410044Speter 111510015Speter dp = (struct si_tcsi *)data; 111610044Speter sps = (struct si_pstat *)data; 111710015Speter card = dp->tc_card; 111856498Speter xsc = devclass_get_softc(si_devclass, card); /* check.. */ 111956498Speter if (xsc == NULL || xsc->sc_type == SIEMPTY) { 112010015Speter error = ENOENT; 112110015Speter goto out; 112210015Speter } 112310015Speter /* 112410015Speter * And check that a port exists 112510015Speter */ 112610015Speter port = dp->tc_port; 112710015Speter if (port < 0 || port >= xsc->sc_nport) { 112810015Speter error = ENOENT; 112910015Speter goto out; 113010015Speter } 113110015Speter xpp = xsc->sc_ports + port; 113210015Speter regp = (struct si_reg *)xsc->sc_maddr; 113310015Speter } 113410015Speter 113510015Speter switch (cmd) { 113610015Speter case TCSIDEBUG: 113710015Speter#ifdef SI_DEBUG 113810015Speter SUCHECK; 113910015Speter if (xpp->sp_debug) 114010015Speter xpp->sp_debug = 0; 114110015Speter else { 114210015Speter xpp->sp_debug = DBG_ALL; 114310015Speter DPRINT((xpp, DBG_IOCTL, "debug toggled %s\n", 114410015Speter (xpp->sp_debug&DBG_ALL)?"ON":"OFF")); 114510015Speter } 114610015Speter break; 114710015Speter#else 114810015Speter error = ENODEV; 114910015Speter goto out; 115010015Speter#endif 115110015Speter case TCSISDBG_LEVEL: 115210015Speter case TCSIGDBG_LEVEL: 115310015Speter#ifdef SI_DEBUG 115410015Speter if (cmd == TCSIGDBG_LEVEL) { 115510015Speter dp->tc_dbglvl = xpp->sp_debug; 115610015Speter } else { 115710015Speter SUCHECK; 115810015Speter xpp->sp_debug = dp->tc_dbglvl; 115910015Speter } 116010015Speter break; 116110015Speter#else 116210015Speter error = ENODEV; 116310015Speter goto out; 116410015Speter#endif 116510015Speter case TCSIGRXIT: 116610015Speter dp->tc_int = regp->rx_int_count; 116710015Speter break; 116810015Speter case TCSIRXIT: 116910015Speter SUCHECK; 117010015Speter regp->rx_int_count = dp->tc_int; 117110015Speter break; 117210015Speter case TCSIGIT: 117310015Speter dp->tc_int = regp->int_count; 117410015Speter break; 117510015Speter case TCSIIT: 117610015Speter SUCHECK; 117710015Speter regp->int_count = dp->tc_int; 117810015Speter break; 117910044Speter case TCSISTATE: 118010044Speter dp->tc_int = xpp->sp_ccb->hi_ip; 118110015Speter break; 118210044Speter /* these next three use a different structure */ 118310044Speter case TCSI_PORT: 118410015Speter SUCHECK; 118534832Speter si_bcopy(xpp, &sps->tc_siport, sizeof(sps->tc_siport)); 118610015Speter break; 118710044Speter case TCSI_CCB: 118810044Speter SUCHECK; 118950442Speter si_vbcopy(xpp->sp_ccb, &sps->tc_ccb, sizeof(sps->tc_ccb)); 119010015Speter break; 119110044Speter case TCSI_TTY: 119210044Speter SUCHECK; 119334832Speter si_bcopy(xpp->sp_tty, &sps->tc_tty, sizeof(sps->tc_tty)); 119410015Speter break; 119510015Speter default: 119610015Speter error = EINVAL; 119710015Speter goto out; 119810015Speter } 119910015Speterout: 120010015Speter splx(oldspl); 120110015Speter return(error); /* success */ 120210015Speter} 120310015Speter 120410015Speter/* 120510015Speter * siparam() : Configure line params 120610015Speter * called at spltty(); 120710015Speter * this may sleep, does not flush, nor wait for drain, nor block writes 120810015Speter * caller must arrange this if it's important.. 120910015Speter */ 121012724Sphkstatic int 121156498Spetersiparam(struct tty *tp, struct termios *t) 121210015Speter{ 121356498Speter struct si_port *pp = TP2PP(tp); 121410015Speter volatile struct si_channel *ccbp; 121510015Speter int oldspl, cflag, iflag, oflag, lflag; 121610015Speter int error = 0; /* shutup gcc */ 121710015Speter int ispeed = 0; /* shutup gcc */ 121810015Speter int ospeed = 0; /* shutup gcc */ 121910161Speter BYTE val; 122010015Speter 122110015Speter DPRINT((pp, DBG_ENTRY|DBG_PARAM, "siparam(%x,%x)\n", tp, t)); 122210015Speter cflag = t->c_cflag; 122310015Speter iflag = t->c_iflag; 122410015Speter oflag = t->c_oflag; 122510015Speter lflag = t->c_lflag; 122610044Speter DPRINT((pp, DBG_PARAM, "OFLAG 0x%x CFLAG 0x%x IFLAG 0x%x LFLAG 0x%x\n", 122710044Speter oflag, cflag, iflag, lflag)); 122810015Speter 122934832Speter /* XXX - if Jet host and SXDC module, use extended baud rates */ 123010015Speter 123110015Speter /* if not hung up.. */ 123210015Speter if (t->c_ospeed != 0) { 123310015Speter /* translate baud rate to firmware values */ 123410015Speter ospeed = ttspeedtab(t->c_ospeed, bdrates); 123510015Speter ispeed = t->c_ispeed ? 123610015Speter ttspeedtab(t->c_ispeed, bdrates) : ospeed; 123710015Speter 123810015Speter /* enforce legit baud rate */ 123910015Speter if (ospeed < 0 || ispeed < 0) 124010015Speter return (EINVAL); 124110015Speter } 124210015Speter 124310015Speter oldspl = spltty(); 124410015Speter 124510015Speter ccbp = pp->sp_ccb; 124610015Speter 124710161Speter /* ========== set hi_break ========== */ 124810161Speter val = 0; 124910161Speter if (iflag & IGNBRK) /* Breaks */ 125010161Speter val |= BR_IGN; 125110161Speter if (iflag & BRKINT) /* Interrupt on break? */ 125210161Speter val |= BR_INT; 125310161Speter if (iflag & PARMRK) /* Parity mark? */ 125410161Speter val |= BR_PARMRK; 125510161Speter if (iflag & IGNPAR) /* Ignore chars with parity errors? */ 125610161Speter val |= BR_PARIGN; 125710161Speter ccbp->hi_break = val; 125810161Speter 125910161Speter /* ========== set hi_csr ========== */ 126010015Speter /* if not hung up.. */ 126110015Speter if (t->c_ospeed != 0) { 126210015Speter /* Set I/O speeds */ 126310161Speter val = (ispeed << 4) | ospeed; 126410015Speter } 126510161Speter ccbp->hi_csr = val; 126610015Speter 126710161Speter /* ========== set hi_mr2 ========== */ 126810161Speter val = 0; 126910015Speter if (cflag & CSTOPB) /* Stop bits */ 127010161Speter val |= MR2_2_STOP; 127110015Speter else 127210161Speter val |= MR2_1_STOP; 127310161Speter /* 127410161Speter * Enable H/W RTS/CTS handshaking. The default TA/MTA is 127510161Speter * a DCE, hence the reverse sense of RTS and CTS 127610161Speter */ 127710161Speter /* Output Flow - RTS must be raised before data can be sent */ 127810161Speter if (cflag & CCTS_OFLOW) 127910161Speter val |= MR2_RTSCONT; 128010161Speter 128116575Speter ccbp->hi_mr2 = val; 128210161Speter 128310161Speter /* ========== set hi_mr1 ========== */ 128410161Speter val = 0; 128510015Speter if (!(cflag & PARENB)) /* Parity */ 128610161Speter val |= MR1_NONE; 128710015Speter else 128810161Speter val |= MR1_WITH; 128910015Speter if (cflag & PARODD) 129010161Speter val |= MR1_ODD; 129110015Speter 129210015Speter if ((cflag & CS8) == CS8) { /* 8 data bits? */ 129310161Speter val |= MR1_8_BITS; 129410015Speter } else if ((cflag & CS7) == CS7) { /* 7 data bits? */ 129510161Speter val |= MR1_7_BITS; 129610015Speter } else if ((cflag & CS6) == CS6) { /* 6 data bits? */ 129710161Speter val |= MR1_6_BITS; 129810015Speter } else { /* Must be 5 */ 129910161Speter val |= MR1_5_BITS; 130010015Speter } 130110161Speter /* 130210161Speter * Enable H/W RTS/CTS handshaking. The default TA/MTA is 130310161Speter * a DCE, hence the reverse sense of RTS and CTS 130410161Speter */ 130510161Speter /* Input Flow - CTS is raised when port is ready to receive data */ 130610161Speter if (cflag & CRTS_IFLOW) 130710161Speter val |= MR1_CTSCONT; 130810015Speter 130910161Speter ccbp->hi_mr1 = val; 131010161Speter 131110161Speter /* ========== set hi_mask ========== */ 131210161Speter val = 0xff; 131310161Speter if ((cflag & CS8) == CS8) { /* 8 data bits? */ 131410161Speter val &= 0xFF; 131510161Speter } else if ((cflag & CS7) == CS7) { /* 7 data bits? */ 131610161Speter val &= 0x7F; 131710161Speter } else if ((cflag & CS6) == CS6) { /* 6 data bits? */ 131810161Speter val &= 0x3F; 131910161Speter } else { /* Must be 5 */ 132010161Speter val &= 0x1F; 132110161Speter } 132210015Speter if (iflag & ISTRIP) 132310161Speter val &= 0x7F; 132410015Speter 132510161Speter ccbp->hi_mask = val; 132610161Speter 132710161Speter /* ========== set hi_prtcl ========== */ 132856592Speter val = SP_DCEN; /* Monitor DCD always, or TIOCMGET misses it */ 132910161Speter if (iflag & IXANY) 133010161Speter val |= SP_TANY; 133110161Speter if (iflag & IXON) 133210161Speter val |= SP_TXEN; 133310161Speter if (iflag & IXOFF) 133410161Speter val |= SP_RXEN; 133510161Speter if (iflag & INPCK) 133610161Speter val |= SP_PAEN; 133710015Speter 133810161Speter ccbp->hi_prtcl = val; 133910161Speter 134010161Speter 134110161Speter /* ========== set hi_{rx|tx}{on|off} ========== */ 134210161Speter /* XXX: the card TOTALLY shields us from the flow control... */ 134310015Speter ccbp->hi_txon = t->c_cc[VSTART]; 134410015Speter ccbp->hi_txoff = t->c_cc[VSTOP]; 134510015Speter 134610015Speter ccbp->hi_rxon = t->c_cc[VSTART]; 134710015Speter ccbp->hi_rxoff = t->c_cc[VSTOP]; 134810015Speter 134910161Speter /* ========== send settings to the card ========== */ 135010015Speter /* potential sleep here */ 135110015Speter if (ccbp->hi_stat == IDLE_CLOSE) /* Not yet open */ 135210015Speter si_command(pp, LOPEN, SI_WAIT); /* open it */ 135310015Speter else 135410015Speter si_command(pp, CONFIG, SI_WAIT); /* change params */ 135510015Speter 135610161Speter /* ========== set DTR etc ========== */ 135710015Speter /* Hangup if ospeed == 0 */ 135810015Speter if (t->c_ospeed == 0) { 135910015Speter (void) si_modem(pp, BIC, TIOCM_DTR|TIOCM_RTS); 136010015Speter } else { 136110015Speter /* 136210015Speter * If the previous speed was 0, may need to re-enable 136334832Speter * the modem signals 136434832Speter */ 136510015Speter (void) si_modem(pp, SET, TIOCM_DTR|TIOCM_RTS); 136610015Speter } 136710015Speter 136810044Speter DPRINT((pp, DBG_PARAM, "siparam, complete: MR1 %x MR2 %x HI_MASK %x PRTCL %x HI_BREAK %x\n", 136910044Speter ccbp->hi_mr1, ccbp->hi_mr2, ccbp->hi_mask, ccbp->hi_prtcl, ccbp->hi_break)); 137010015Speter 137110015Speter splx(oldspl); 137210015Speter return(error); 137310015Speter} 137410015Speter 137510015Speter/* 137610015Speter * Enable or Disable the writes to this channel... 137710015Speter * "state" -> enabled = 1; disabled = 0; 137810015Speter */ 137910015Speterstatic void 138056498Spetersi_write_enable(struct si_port *pp, int state) 138110015Speter{ 138210015Speter int oldspl; 138310015Speter 138410015Speter oldspl = spltty(); 138510015Speter 138610015Speter if (state) { 138710015Speter pp->sp_state &= ~SS_BLOCKWRITE; 138810015Speter if (pp->sp_state & SS_WAITWRITE) { 138910015Speter pp->sp_state &= ~SS_WAITWRITE; 139010015Speter /* thunder away! */ 1391111748Sdes wakeup(pp); 139210015Speter } 139310015Speter } else { 139410015Speter pp->sp_state |= SS_BLOCKWRITE; 139510015Speter } 139610015Speter 139710015Speter splx(oldspl); 139810015Speter} 139910015Speter 140010015Speter/* 140110015Speter * Set/Get state of modem control lines. 140210015Speter * Due to DCE-like behaviour of the adapter, some signals need translation: 140310015Speter * TIOCM_DTR DSR 140410015Speter * TIOCM_RTS CTS 140510015Speter */ 140610015Speterstatic int 140756498Spetersi_modem(struct si_port *pp, enum si_mctl cmd, int bits) 140810015Speter{ 140910015Speter volatile struct si_channel *ccbp; 141010015Speter int x; 141110015Speter 141210015Speter DPRINT((pp, DBG_ENTRY|DBG_MODEM, "si_modem(%x,%s,%x)\n", pp, si_mctl2str(cmd), bits)); 141310015Speter ccbp = pp->sp_ccb; /* Find channel address */ 141410015Speter switch (cmd) { 141510015Speter case GET: 141610015Speter x = ccbp->hi_ip; 141710015Speter bits = TIOCM_LE; 141810015Speter if (x & IP_DCD) bits |= TIOCM_CAR; 141910015Speter if (x & IP_DTR) bits |= TIOCM_DTR; 142010015Speter if (x & IP_RTS) bits |= TIOCM_RTS; 142110015Speter if (x & IP_RI) bits |= TIOCM_RI; 142210015Speter return(bits); 142310015Speter case SET: 142410015Speter ccbp->hi_op &= ~(OP_DSR|OP_CTS); 142510015Speter /* fall through */ 142610015Speter case BIS: 142710015Speter x = 0; 142810015Speter if (bits & TIOCM_DTR) 142910015Speter x |= OP_DSR; 143010015Speter if (bits & TIOCM_RTS) 143110015Speter x |= OP_CTS; 143210015Speter ccbp->hi_op |= x; 143310015Speter break; 143410015Speter case BIC: 143510015Speter if (bits & TIOCM_DTR) 143610015Speter ccbp->hi_op &= ~OP_DSR; 143710015Speter if (bits & TIOCM_RTS) 143810015Speter ccbp->hi_op &= ~OP_CTS; 143910015Speter } 144010015Speter return 0; 144110015Speter} 144210015Speter 144310015Speter/* 144410015Speter * Handle change of modem state 144510015Speter */ 144610015Speterstatic void 144756498Spetersi_modem_state(struct si_port *pp, struct tty *tp, int hi_ip) 144810015Speter{ 144910015Speter /* if a modem dev */ 145010015Speter if (hi_ip & IP_DCD) { 145150442Speter if (!(pp->sp_last_hi_ip & IP_DCD)) { 145210015Speter DPRINT((pp, DBG_INTR, "modem carr on t_line %d\n", 145310015Speter tp->t_line)); 1454130077Sphk (void)ttyld_modem(tp, 1); 145510015Speter } 145610015Speter } else { 145710015Speter if (pp->sp_last_hi_ip & IP_DCD) { 145810015Speter DPRINT((pp, DBG_INTR, "modem carr off\n")); 1459130077Sphk if (ttyld_modem(tp, 0)) 146010015Speter (void) si_modem(pp, SET, 0); 146110015Speter } 146210015Speter } 146310015Speter pp->sp_last_hi_ip = hi_ip; 146410015Speter 146510015Speter} 146610015Speter 146710015Speter/* 146810015Speter * Poller to catch missed interrupts. 146912174Speter * 147012496Speter * Note that the SYSV Specialix drivers poll at 100 times per second to get 147112496Speter * better response. We could really use a "periodic" version timeout(). :-) 147210015Speter */ 147310015Speter#ifdef POLL 147410708Speterstatic void 147510015Spetersi_poll(void *nothing) 147610015Speter{ 147756498Speter struct si_softc *sc; 147856498Speter int i; 147910015Speter volatile struct si_reg *regp; 148056498Speter struct si_port *pp; 148112174Speter int lost, oldspl, port; 148210015Speter 148310015Speter DPRINT((0, DBG_POLL, "si_poll()\n")); 148411609Speter oldspl = spltty(); 148510015Speter if (in_intr) 148610015Speter goto out; 148710015Speter lost = 0; 148856498Speter for (i = 0; i < si_numunits; i++) { 148956498Speter sc = devclass_get_softc(si_devclass, i); 149056498Speter if (sc == NULL || sc->sc_type == SIEMPTY) 149110015Speter continue; 149210015Speter regp = (struct si_reg *)sc->sc_maddr; 149334832Speter 149410015Speter /* 149510015Speter * See if there has been a pending interrupt for 2 seconds 149633395Speter * or so. The test (int_scounter >= 200) won't correspond 149710015Speter * to 2 seconds if int_count gets changed. 149810015Speter */ 149910015Speter if (regp->int_pending != 0) { 150010015Speter if (regp->int_scounter >= 200 && 150110015Speter regp->initstat == 1) { 150212174Speter printf("si%d: lost intr\n", i); 150310015Speter lost++; 150410015Speter } 150510015Speter } else { 150610015Speter regp->int_scounter = 0; 150710015Speter } 150810015Speter 150912174Speter /* 151012174Speter * gripe about no input flow control.. 151112174Speter */ 151212174Speter pp = sc->sc_ports; 151312174Speter for (port = 0; port < sc->sc_nport; pp++, port++) { 151412174Speter if (pp->sp_delta_overflows > 0) { 151512174Speter printf("si%d: %d tty level buffer overflows\n", 151612174Speter i, pp->sp_delta_overflows); 151712174Speter pp->sp_delta_overflows = 0; 151812174Speter } 151912174Speter } 152010015Speter } 152117547Speter if (lost || si_realpoll) 152256498Speter si_intr(NULL); /* call intr with fake vector */ 152311609Speterout: 152410015Speter splx(oldspl); 152510015Speter 152615639Speter timeout(si_poll, (caddr_t)0L, si_pollrate); 152710015Speter} 152810015Speter#endif /* ifdef POLL */ 152910015Speter 153010015Speter/* 153110015Speter * The interrupt handler polls ALL ports on ALL adapters each time 153210015Speter * it is called. 153310015Speter */ 153410015Speter 153512496Speterstatic BYTE si_rxbuf[SI_BUFFERSIZE]; /* input staging area */ 153634832Speterstatic BYTE si_txbuf[SI_BUFFERSIZE]; /* output staging area */ 153710015Speter 153856505Spetervoid 153956498Spetersi_intr(void *arg) 154010015Speter{ 154156498Speter struct si_softc *sc; 154256498Speter struct si_port *pp; 154310015Speter volatile struct si_channel *ccbp; 154456498Speter struct tty *tp; 154510015Speter volatile caddr_t maddr; 154611872Sphk BYTE op, ip; 154712174Speter int x, card, port, n, i, isopen; 154810015Speter volatile BYTE *z; 154910015Speter BYTE c; 155010015Speter 155156498Speter sc = arg; 155256498Speter 155356498Speter DPRINT((0, arg == NULL ? DBG_POLL:DBG_INTR, "si_intr\n")); 155456498Speter if (in_intr) 155510708Speter return; 155610015Speter in_intr = 1; 155710015Speter 155810015Speter /* 155910015Speter * When we get an int we poll all the channels and do ALL pending 156010015Speter * work, not just the first one we find. This allows all cards to 156110015Speter * share the same vector. 156234832Speter * 156334832Speter * XXX - But if we're sharing the vector with something that's NOT 156434832Speter * a SI/XIO/SX card, we may be making more work for ourselves. 156510015Speter */ 156656498Speter for (card = 0; card < si_numunits; card++) { 156756498Speter sc = devclass_get_softc(si_devclass, card); 156856498Speter if (sc == NULL || sc->sc_type == SIEMPTY) 156910015Speter continue; 157012174Speter 157112174Speter /* 157212174Speter * First, clear the interrupt 157312174Speter */ 157410015Speter switch(sc->sc_type) { 157534832Speter case SIHOST: 157610015Speter maddr = sc->sc_maddr; 157710015Speter ((volatile struct si_reg *)maddr)->int_pending = 0; 157810015Speter /* flag nothing pending */ 157910015Speter *(maddr+SIINTCL) = 0x00; /* Set IRQ clear */ 158010015Speter *(maddr+SIINTCL_CL) = 0x00; /* Clear IRQ clear */ 158110015Speter break; 158210015Speter case SIHOST2: 158310015Speter maddr = sc->sc_maddr; 158410015Speter ((volatile struct si_reg *)maddr)->int_pending = 0; 158510015Speter *(maddr+SIPLIRQCLR) = 0x00; 158610015Speter *(maddr+SIPLIRQCLR) = 0x10; 158710015Speter break; 158833395Speter case SIPCI: 158933395Speter maddr = sc->sc_maddr; 159033395Speter ((volatile struct si_reg *)maddr)->int_pending = 0; 159133395Speter *(maddr+SIPCIINTCL) = 0x0; 159233395Speter break; 159334832Speter case SIJETPCI: /* fall through to JETISA case */ 159433395Speter case SIJETISA: 159533395Speter maddr = sc->sc_maddr; 159633395Speter ((volatile struct si_reg *)maddr)->int_pending = 0; 159733395Speter *(maddr+SIJETINTCL) = 0x0; 159833395Speter break; 159910015Speter case SIEISA: 160010015Speter maddr = sc->sc_maddr; 160110015Speter ((volatile struct si_reg *)maddr)->int_pending = 0; 160256498Speter (void)inb(sc->sc_iobase + 3); 160310015Speter break; 160410015Speter case SIEMPTY: 160510015Speter default: 160610015Speter continue; 160710015Speter } 160810015Speter ((volatile struct si_reg *)maddr)->int_scounter = 0; 160910015Speter 161012174Speter /* 161112174Speter * check each port 161212174Speter */ 161350442Speter for (pp = sc->sc_ports, port = 0; port < sc->sc_nport; 161434832Speter pp++, port++) { 161510015Speter ccbp = pp->sp_ccb; 161610015Speter tp = pp->sp_tty; 161710015Speter 161810015Speter /* 161910015Speter * See if a command has completed ? 162010015Speter */ 162110015Speter if (ccbp->hi_stat != pp->sp_pend) { 162210015Speter DPRINT((pp, DBG_INTR, 162334735Speter "si_intr hi_stat = 0x%x, pend = %d\n", 162410015Speter ccbp->hi_stat, pp->sp_pend)); 162510015Speter switch(pp->sp_pend) { 162610015Speter case LOPEN: 162710015Speter case MPEND: 162810015Speter case MOPEN: 162910015Speter case CONFIG: 163016575Speter case SBREAK: 163116575Speter case EBREAK: 163210015Speter pp->sp_pend = ccbp->hi_stat; 163310015Speter /* sleeping in si_command */ 163410015Speter wakeup(&pp->sp_state); 163510015Speter break; 163610015Speter default: 163710015Speter pp->sp_pend = ccbp->hi_stat; 163810015Speter } 163934832Speter } 164010015Speter 164110015Speter /* 164210015Speter * Continue on if it's closed 164310015Speter */ 164410015Speter if (ccbp->hi_stat == IDLE_CLOSE) { 164510015Speter continue; 164610015Speter } 164710015Speter 164810015Speter /* 164910015Speter * Do modem state change if not a local device 165010015Speter */ 165110015Speter si_modem_state(pp, tp, ccbp->hi_ip); 165210015Speter 165310015Speter /* 165434832Speter * Check to see if we should 'receive' characters. 165512174Speter */ 165612174Speter if (tp->t_state & TS_CONNECTED && 165712174Speter tp->t_state & TS_ISOPEN) 165812174Speter isopen = 1; 165912174Speter else 166012174Speter isopen = 0; 166112174Speter 166212174Speter /* 166316575Speter * Do input break processing 166410015Speter */ 166510015Speter if (ccbp->hi_state & ST_BREAK) { 166612174Speter if (isopen) { 1667130077Sphk ttyld_rint(tp, TTY_BI); 166810015Speter } 166910015Speter ccbp->hi_state &= ~ST_BREAK; /* A Bit iffy this */ 167010015Speter DPRINT((pp, DBG_INTR, "si_intr break\n")); 167110015Speter } 167210015Speter 167310015Speter /* 167412174Speter * Do RX stuff - if not open then dump any characters. 167512174Speter * XXX: This is VERY messy and needs to be cleaned up. 167612174Speter * 167712174Speter * XXX: can we leave data in the host adapter buffer 167812174Speter * when the clists are full? That may be dangerous 167912174Speter * if the user cannot get an interrupt signal through. 168010015Speter */ 168110015Speter 168212174Speter more_rx: /* XXX Sorry. the nesting was driving me bats! :-( */ 168312174Speter 168412174Speter if (!isopen) { 168510015Speter ccbp->hi_rxopos = ccbp->hi_rxipos; 168612174Speter goto end_rx; 168712174Speter } 168810015Speter 168912174Speter /* 169015640Speter * If the tty input buffers are blocked, stop emptying 169115640Speter * the incoming buffers and let the auto flow control 169215640Speter * assert.. 169315640Speter */ 169415640Speter if (tp->t_state & TS_TBLOCK) { 169515640Speter goto end_rx; 169615640Speter } 169715640Speter 169815640Speter /* 169912174Speter * Process read characters if not skipped above 170012174Speter */ 170115640Speter op = ccbp->hi_rxopos; 170215640Speter ip = ccbp->hi_rxipos; 170315640Speter c = ip - op; 170412174Speter if (c == 0) { 170512174Speter goto end_rx; 170612174Speter } 170710015Speter 170812174Speter n = c & 0xff; 170915640Speter if (n > 250) 171015640Speter n = 250; 171112174Speter 171212174Speter DPRINT((pp, DBG_INTR, "n = %d, op = %d, ip = %d\n", 171310015Speter n, op, ip)); 171410015Speter 171512174Speter /* 171612174Speter * Suck characters out of host card buffer into the 171712174Speter * "input staging buffer" - so that we dont leave the 171812174Speter * host card in limbo while we're possibly echoing 171912174Speter * characters and possibly flushing input inside the 172012174Speter * ldisc l_rint() routine. 172112174Speter */ 172212496Speter if (n <= SI_BUFFERSIZE - op) { 172310015Speter 172412174Speter DPRINT((pp, DBG_INTR, "\tsingle copy\n")); 172512174Speter z = ccbp->hi_rxbuf + op; 172650442Speter si_vbcopy(z, si_rxbuf, n); 172710015Speter 172812174Speter op += n; 172912174Speter } else { 173012496Speter x = SI_BUFFERSIZE - op; 173110015Speter 173212174Speter DPRINT((pp, DBG_INTR, "\tdouble part 1 %d\n", x)); 173312174Speter z = ccbp->hi_rxbuf + op; 173450442Speter si_vbcopy(z, si_rxbuf, x); 173510015Speter 173634832Speter DPRINT((pp, DBG_INTR, "\tdouble part 2 %d\n", 173734832Speter n - x)); 173812174Speter z = ccbp->hi_rxbuf; 173950442Speter si_vbcopy(z, si_rxbuf + x, n - x); 174010015Speter 174112174Speter op += n; 174212174Speter } 174310015Speter 174412174Speter /* clear collected characters from buffer */ 174512174Speter ccbp->hi_rxopos = op; 174612174Speter 174712174Speter DPRINT((pp, DBG_INTR, "n = %d, op = %d, ip = %d\n", 174810015Speter n, op, ip)); 174910015Speter 175012174Speter /* 175112174Speter * at this point... 175212174Speter * n = number of chars placed in si_rxbuf 175312174Speter */ 175410015Speter 175512174Speter /* 175612174Speter * Avoid the grotesquely inefficient lineswitch 175712174Speter * routine (ttyinput) in "raw" mode. It usually 175812174Speter * takes about 450 instructions (that's without 175912174Speter * canonical processing or echo!). slinput is 176012174Speter * reasonably fast (usually 40 instructions 176112174Speter * plus call overhead). 176212174Speter */ 176312174Speter if (tp->t_state & TS_CAN_BYPASS_L_RINT) { 176410015Speter 176512174Speter /* block if the driver supports it */ 176650442Speter if (tp->t_rawq.c_cc + n >= SI_I_HIGH_WATER && 176750442Speter (tp->t_cflag & CRTS_IFLOW || 176850442Speter tp->t_iflag & IXOFF) && 176950442Speter !(tp->t_state & TS_TBLOCK)) 177012174Speter ttyblock(tp); 177110015Speter 177212174Speter tk_nin += n; 177312174Speter tk_rawcc += n; 177412174Speter tp->t_rawcc += n; 177512174Speter 177612174Speter pp->sp_delta_overflows += 177712174Speter b_to_q((char *)si_rxbuf, n, &tp->t_rawq); 177812174Speter 177912174Speter ttwakeup(tp); 178050442Speter if (tp->t_state & TS_TTSTOP && 178150442Speter (tp->t_iflag & IXANY || 178250442Speter tp->t_cc[VSTART] == tp->t_cc[VSTOP])) { 178312174Speter tp->t_state &= ~TS_TTSTOP; 178412174Speter tp->t_lflag &= ~FLUSHO; 178512174Speter si_start(tp); 178612174Speter } 178712174Speter } else { 178812174Speter /* 178912174Speter * It'd be nice to not have to go through the 179012174Speter * function call overhead for each char here. 179112174Speter * It'd be nice to block input it, saving a 179212174Speter * loop here and the call/return overhead. 179312174Speter */ 179412174Speter for(x = 0; x < n; x++) { 179512174Speter i = si_rxbuf[x]; 1796130077Sphk if (ttyld_rint(tp, i) 179712174Speter == -1) { 179812174Speter pp->sp_delta_overflows++; 179910015Speter } 180012174Speter } 180112174Speter } 180212174Speter goto more_rx; /* try for more until RXbuf is empty */ 180310015Speter 180412174Speter end_rx: /* XXX: Again, sorry about the gotos.. :-) */ 180510015Speter 180610015Speter /* 180710015Speter * Do TX stuff 180810015Speter */ 1809130077Sphk ttyld_start(tp); 181010015Speter 181110015Speter } /* end of for (all ports on this controller) */ 181210015Speter } /* end of for (all controllers) */ 181310015Speter 181411609Speter in_intr = 0; 181556498Speter DPRINT((0, arg == NULL ? DBG_POLL:DBG_INTR, "end si_intr\n")); 181610015Speter} 181710015Speter 181810015Speter/* 181910015Speter * Nudge the transmitter... 182012174Speter * 182112174Speter * XXX: I inherited some funny code here. It implies the host card only 182212174Speter * interrupts when the transmit buffer reaches the low-water-mark, and does 182312174Speter * not interrupt when it's actually hits empty. In some cases, we have 182412174Speter * processes waiting for complete drain, and we need to simulate an interrupt 182512174Speter * about when we think the buffer is going to be empty (and retry if not). 182612174Speter * I really am not certain about this... I *need* the hardware manuals. 182710015Speter */ 182810015Speterstatic void 182956498Spetersi_start(struct tty *tp) 183010015Speter{ 183110015Speter struct si_port *pp; 183210015Speter volatile struct si_channel *ccbp; 183356498Speter struct clist *qp; 183410015Speter BYTE ipos; 183510015Speter int nchar; 183610015Speter int oldspl, count, n, amount, buffer_full; 183710015Speter 183810015Speter oldspl = spltty(); 183910015Speter 184010015Speter qp = &tp->t_outq; 184110015Speter pp = TP2PP(tp); 184210015Speter 184310015Speter DPRINT((pp, DBG_ENTRY|DBG_START, 184410015Speter "si_start(%x) t_state %x sp_state %x t_outq.c_cc %d\n", 184510015Speter tp, tp->t_state, pp->sp_state, qp->c_cc)); 184610015Speter 184710015Speter if (tp->t_state & (TS_TIMEOUT|TS_TTSTOP)) 184810015Speter goto out; 184910015Speter 185010015Speter buffer_full = 0; 185110015Speter ccbp = pp->sp_ccb; 185210015Speter 185310015Speter count = (int)ccbp->hi_txipos - (int)ccbp->hi_txopos; 185410015Speter DPRINT((pp, DBG_START, "count %d\n", (BYTE)count)); 185510015Speter 185610015Speter while ((nchar = qp->c_cc) > 0) { 185710015Speter if ((BYTE)count >= 255) { 185810015Speter buffer_full++; 185910015Speter break; 186010015Speter } 186110015Speter amount = min(nchar, (255 - (BYTE)count)); 186210015Speter ipos = (unsigned int)ccbp->hi_txipos; 186334832Speter n = q_to_b(&tp->t_outq, si_txbuf, amount); 186410015Speter /* will it fit in one lump? */ 186534832Speter if ((SI_BUFFERSIZE - ipos) >= n) { 186650442Speter si_bcopyv(si_txbuf, &ccbp->hi_txbuf[ipos], n); 186710015Speter } else { 186850442Speter si_bcopyv(si_txbuf, &ccbp->hi_txbuf[ipos], 186934832Speter SI_BUFFERSIZE - ipos); 187050442Speter si_bcopyv(si_txbuf + (SI_BUFFERSIZE - ipos), 187150442Speter &ccbp->hi_txbuf[0], n - (SI_BUFFERSIZE - ipos)); 187210015Speter } 187310015Speter ccbp->hi_txipos += n; 187410015Speter count = (int)ccbp->hi_txipos - (int)ccbp->hi_txopos; 187510015Speter } 187610015Speter 187710015Speter if (count != 0 && nchar == 0) { 187810015Speter tp->t_state |= TS_BUSY; 187910015Speter } else { 188010015Speter tp->t_state &= ~TS_BUSY; 188110015Speter } 188210015Speter 188310015Speter /* wakeup time? */ 188410015Speter ttwwakeup(tp); 188510015Speter 188610015Speter DPRINT((pp, DBG_START, "count %d, nchar %d, tp->t_state 0x%x\n", 188710015Speter (BYTE)count, nchar, tp->t_state)); 188810015Speter 188934735Speter if (tp->t_state & TS_BUSY) 189010015Speter { 189110015Speter int time; 189210015Speter 189334735Speter time = ttspeedtab(tp->t_ospeed, chartimes); 189434735Speter 189534735Speter if (time > 0) { 189634735Speter if (time < nchar) 189734735Speter time = nchar / time; 189834735Speter else 189934735Speter time = 2; 190010015Speter } else { 190134735Speter DPRINT((pp, DBG_START, 190234735Speter "bad char time value! %d\n", time)); 190334735Speter time = hz/10; 190410015Speter } 190510015Speter 190610015Speter if ((pp->sp_state & (SS_LSTART|SS_INLSTART)) == SS_LSTART) { 190729677Sgibbs untimeout(si_lstart, (caddr_t)pp, pp->lstart_ch); 190810015Speter } else { 190910015Speter pp->sp_state |= SS_LSTART; 191010015Speter } 191110015Speter DPRINT((pp, DBG_START, "arming lstart, time=%d\n", time)); 191229677Sgibbs pp->lstart_ch = timeout(si_lstart, (caddr_t)pp, time); 191310015Speter } 191410015Speter 191510015Speterout: 191610015Speter splx(oldspl); 191710015Speter DPRINT((pp, DBG_EXIT|DBG_START, "leave si_start()\n")); 191810015Speter} 191910015Speter 192010015Speter/* 192110015Speter * Note: called at splsoftclock from the timeout code 192210015Speter * This has to deal with two things... cause wakeups while waiting for 192310015Speter * tty drains on last process exit, and call l_start at about the right 192410015Speter * time for protocols like ppp. 192510015Speter */ 192610015Speterstatic void 192725047Sbdesi_lstart(void *arg) 192810015Speter{ 192956498Speter struct si_port *pp = arg; 193056498Speter struct tty *tp; 193110015Speter int oldspl; 193210015Speter 193310015Speter DPRINT((pp, DBG_ENTRY|DBG_LSTART, "si_lstart(%x) sp_state %x\n", 193410015Speter pp, pp->sp_state)); 193510015Speter 193610015Speter oldspl = spltty(); 193710015Speter 193810015Speter if ((pp->sp_state & SS_OPEN) == 0 || (pp->sp_state & SS_LSTART) == 0) { 193910015Speter splx(oldspl); 194010015Speter return; 194110015Speter } 194210015Speter pp->sp_state &= ~SS_LSTART; 194310015Speter pp->sp_state |= SS_INLSTART; 194410015Speter 194510015Speter tp = pp->sp_tty; 194610015Speter 194710015Speter /* deal with the process exit case */ 194810015Speter ttwwakeup(tp); 194910015Speter 195012174Speter /* nudge protocols - eg: ppp */ 1951130077Sphk ttyld_start(tp); 195210015Speter 195310015Speter pp->sp_state &= ~SS_INLSTART; 195410015Speter splx(oldspl); 195510015Speter} 195610015Speter 195710015Speter/* 195810015Speter * Stop output on a line. called at spltty(); 195910015Speter */ 1960105215Sphkstatic void 196156498Spetersi_stop(struct tty *tp, int rw) 196210015Speter{ 196310015Speter volatile struct si_channel *ccbp; 196410015Speter struct si_port *pp; 196510015Speter 196610015Speter pp = TP2PP(tp); 196710015Speter ccbp = pp->sp_ccb; 196810015Speter 196951654Sphk DPRINT((TP2PP(tp), DBG_ENTRY|DBG_STOP, "si_stop(%x,%x)\n", tp, rw)); 197010015Speter 197110015Speter /* XXX: must check (rw & FWRITE | FREAD) etc flushing... */ 197210015Speter if (rw & FWRITE) { 197310015Speter /* what level are we meant to be flushing anyway? */ 197410015Speter if (tp->t_state & TS_BUSY) { 197510015Speter si_command(TP2PP(tp), WFLUSH, SI_NOWAIT); 197610015Speter tp->t_state &= ~TS_BUSY; 197710015Speter ttwwakeup(tp); /* Bruce???? */ 197810015Speter } 197910015Speter } 198012174Speter#if 1 /* XXX: this doesn't work right yet.. */ 198112174Speter /* XXX: this may have been failing because we used to call l_rint() 198212174Speter * while we were looping based on these two counters. Now, we collect 198312174Speter * the data and then loop stuffing it into l_rint(), making this 198412174Speter * useless. Should we cause this to blow away the staging buffer? 198512174Speter */ 198610015Speter if (rw & FREAD) { 198710015Speter ccbp->hi_rxopos = ccbp->hi_rxipos; 198810015Speter } 198910015Speter#endif 199010015Speter} 199110015Speter 199210015Speter/* 199334832Speter * Issue a command to the host card CPU. 199410015Speter */ 199510015Speter 199610015Speterstatic void 199756498Spetersi_command(struct si_port *pp, int cmd, int waitflag) 199810015Speter{ 199910015Speter int oldspl; 200010015Speter volatile struct si_channel *ccbp = pp->sp_ccb; 200110015Speter int x; 200210015Speter 200310015Speter DPRINT((pp, DBG_ENTRY|DBG_PARAM, "si_command(%x,%x,%d): hi_stat 0x%x\n", 200410015Speter pp, cmd, waitflag, ccbp->hi_stat)); 200510015Speter 200610015Speter oldspl = spltty(); /* Keep others out */ 200710015Speter 200810015Speter /* wait until it's finished what it was doing.. */ 200916575Speter /* XXX: sits in IDLE_BREAK until something disturbs it or break 201016575Speter * is turned off. */ 201110015Speter while((x = ccbp->hi_stat) != IDLE_OPEN && 201210015Speter x != IDLE_CLOSE && 201316575Speter x != IDLE_BREAK && 201410015Speter x != cmd) { 201510015Speter if (in_intr) { /* Prevent sleep in intr */ 201610015Speter DPRINT((pp, DBG_PARAM, 201710015Speter "cmd intr collision - completing %d\trequested %d\n", 201810015Speter x, cmd)); 201910015Speter splx(oldspl); 202010015Speter return; 202110015Speter } else if (ttysleep(pp->sp_tty, (caddr_t)&pp->sp_state, TTIPRI|PCATCH, 202210015Speter "sicmd1", 1)) { 202310015Speter splx(oldspl); 202410015Speter return; 202510015Speter } 202610015Speter } 202716575Speter /* it should now be in IDLE_{OPEN|CLOSE|BREAK}, or "cmd" */ 202810015Speter 202910015Speter /* if there was a pending command, cause a state-change wakeup */ 203016575Speter switch(pp->sp_pend) { 203116575Speter case LOPEN: 203216575Speter case MPEND: 203316575Speter case MOPEN: 203416575Speter case CONFIG: 203516575Speter case SBREAK: 203616575Speter case EBREAK: 203716575Speter wakeup(&pp->sp_state); 203816575Speter break; 203916575Speter default: 204016575Speter break; 204110015Speter } 204210015Speter 204310015Speter pp->sp_pend = cmd; /* New command pending */ 204410015Speter ccbp->hi_stat = cmd; /* Post it */ 204510015Speter 204610015Speter if (waitflag) { 204710015Speter if (in_intr) { /* If in interrupt handler */ 204810015Speter DPRINT((pp, DBG_PARAM, 204910015Speter "attempt to sleep in si_intr - cmd req %d\n", 205010015Speter cmd)); 205110015Speter splx(oldspl); 205210015Speter return; 205316575Speter } else while(ccbp->hi_stat != IDLE_OPEN && 205416575Speter ccbp->hi_stat != IDLE_BREAK) { 205510015Speter if (ttysleep(pp->sp_tty, (caddr_t)&pp->sp_state, TTIPRI|PCATCH, 205610015Speter "sicmd2", 0)) 205710015Speter break; 205810015Speter } 205910015Speter } 206010015Speter splx(oldspl); 206110015Speter} 206210015Speter 206310015Speter 206410015Speter#ifdef SI_DEBUG 206513353Speter 206656505Spetervoid 206713353Spetersi_dprintf(struct si_port *pp, int flags, const char *fmt, ...) 206810015Speter{ 206913353Speter va_list ap; 207013630Sphk 207110015Speter if ((pp == NULL && (si_debug&flags)) || 207210015Speter (pp != NULL && ((pp->sp_debug&flags) || (si_debug&flags)))) { 207334832Speter if (pp != NULL) 207434832Speter printf("%ci%d(%d): ", 's', 207546679Sphk (int)SI_CARD(minor(pp->sp_tty->t_dev)), 207646679Sphk (int)SI_PORT(minor(pp->sp_tty->t_dev))); 207713630Sphk va_start(ap, fmt); 207813630Sphk vprintf(fmt, ap); 207913353Speter va_end(ap); 208010015Speter } 208110015Speter} 208210015Speter 208310015Speterstatic char * 208456498Spetersi_mctl2str(enum si_mctl cmd) 208510015Speter{ 208610015Speter switch (cmd) { 208734832Speter case GET: 208834832Speter return("GET"); 208934832Speter case SET: 209034832Speter return("SET"); 209134832Speter case BIS: 209234832Speter return("BIS"); 209334832Speter case BIC: 209434832Speter return("BIC"); 209510015Speter } 209610015Speter return("BAD"); 209710015Speter} 209812502Sjulian 209912624Speter#endif /* DEBUG */ 210012502Sjulian 210134832Speterstatic char * 210256498Spetersi_modulename(int host_type, int uart_type) 210334832Speter{ 210434832Speter switch (host_type) { 210534832Speter /* Z280 based cards */ 210650442Speter case SIEISA: 210750442Speter case SIHOST2: 210834832Speter case SIHOST: 210934832Speter case SIPCI: 211034832Speter switch (uart_type) { 211134832Speter case 0: 211234832Speter return(" (XIO)"); 211334832Speter case 1: 211434832Speter return(" (SI)"); 211534832Speter } 211634832Speter break; 211734832Speter /* T225 based hosts */ 211834832Speter case SIJETPCI: 211934832Speter case SIJETISA: 212034832Speter switch (uart_type) { 212134832Speter case 0: 212234832Speter return(" (SI)"); 212334832Speter case 40: 212434832Speter return(" (XIO)"); 212536856Sphk case 72: 212636856Sphk return(" (SXDC)"); 212734832Speter } 212834832Speter break; 212934832Speter } 213034832Speter return(""); 213134832Speter} 2132