📄 si.c
字号:
/* * Device driver for Specialix range (SI/XIO) of serial line multiplexors. * * Copyright (C) 1990, 1992 Specialix International, * Copyright (C) 1993, Andy Rutter <andy@acronym.co.uk> * Copyright (C) 1995, Peter Wemm <peter@haywire.dialix.com> * * Originally derived from: SunOS 4.x version * Ported from BSDI version to FreeBSD by Peter Wemm. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notices, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notices, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Andy Rutter of * Advanced Methods and Tools Ltd. based on original information * from Specialix International. * 4. Neither the name of Advanced Methods and Tools, nor Specialix * International may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN * NO EVENT SHALL THE AUTHORS BE LIABLE. * * $\Id: si.c,v 1.9.2.5 1996/07/01 11:01:58 peter Exp $ */#ifndef lintstatic char si_copyright1[] = "@(#) (C) Specialix International, 1990,1992", si_copyright2[] = "@(#) (C) Andy Rutter 1993", si_copyright3[] = "@(#) (C) Peter Wemm 1995";#endif /* not lint */#include <sys/param.h>#include <sys/systm.h>#include <sys/ioctl.h>#include <sys/tty.h>#include <sys/ttydefaults.h>#include <sys/proc.h>#include <sys/user.h>#include <sys/conf.h>#include <sys/file.h>#include <sys/uio.h>#include <sys/dkstat.h>#include <sys/kernel.h>#include <sys/syslog.h>#include <sys/malloc.h>#include <sys/devconf.h>#include <machine/clock.h>#include <i386/isa/icu.h>#include <i386/isa/isa.h>#include <i386/isa/isa_device.h>#include <i386/isa/sireg.h>#include <machine/si.h>#include "si.h"/* * This device driver is designed to interface the Specialix International * range of serial multiplexor cards (SI/XIO) to BSDI/386 on an ISA bus machine. * * The controller is interfaced to the host via dual port ram * and a (programmable - SIHOST2) interrupt at IRQ 11,12 or 15. */#define POLL /* turn on poller to generate buffer empty interrupt */#undef FASTPOLL /* turn on 100Hz poller, (XXX: NOTYET!) */#define SI_DEF_HWFLOW /* turn on default CRTSCTS flow control */#define SI_I_HIGH_WATER (TTYHOG - 2 * SI_BUFFERSIZE)#define INT_COUNT 25000 /* max of 125 ints per second */#define RXINT_COUNT 1 /* one rxint per 10 milliseconds */enum si_mctl { GET, SET, BIS, BIC };static void si_command __P((struct si_port *, int, int));static int si_modem __P((struct si_port *, enum si_mctl, int));static void si_write_enable __P((struct si_port *, int));static int si_Sioctl __P((dev_t, int, caddr_t, int, struct proc *));static void si_start __P((struct tty *));static void si_lstart __P((struct si_port *));static void si_disc_optim __P((struct tty *tp, struct termios *t, struct si_port *pp));static void sihardclose __P((struct si_port *pp));static void sidtrwakeup __P((void *chan));void sistop __P((struct tty *tp, int rw));void siintr __P((int unit));int siparam __P((struct tty *, struct termios *));extern void si_registerdev __P((struct isa_device *id));extern int siprobe __P((struct isa_device *id));extern int siattach __P((struct isa_device *id));static void si_modem_state __P((struct si_port *pp, struct tty *tp, int hi_ip));#ifdef SI_DEBUG /* use: ``options "SI_DEBUG"'' in your config file *//* XXX: should be varargs, I know.. but where's vprintf()? */static void si_dprintf __P((/* struct si_port *pp, int flags, char *str, int a1, int a2, int a3, int a4, int a5, int a6 */));static char *si_mctl2str __P((enum si_mctl cmd));#define DPRINT(x) si_dprintf x#else#define DPRINT(x) /* void */#endifstatic int si_Nports;static int si_Nmodules;static int si_debug = 0; /* data, not bss, so it's patchable */static struct tty *si_tty;/* where the firmware lives; defined in si_code.c */extern int si_dsize;extern unsigned char si_download[];struct si_softc { int sc_type; /* adapter type */ char *sc_typename; /* adapter type string */ struct si_port *sc_ports; /* port structures for this card */ caddr_t sc_paddr; /* physical addr of iomem */ caddr_t sc_maddr; /* kvaddr of iomem */ int sc_nport; /* # ports on this card */ int sc_irq; /* copy of attach irq */ int sc_eisa_iobase; /* EISA io port address */ int sc_eisa_irqbits; struct kern_devconf sc_kdc;};struct si_softc si_softc[NSI]; /* up to 4 elements */#ifndef B2000 /* not standard, but the hardware knows it. */# define B2000 2000#endifstatic struct speedtab bdrates[] = { B75, CLK75, /* 0x0 */ B110, CLK110, /* 0x1 */ B150, CLK150, /* 0x3 */ B300, CLK300, /* 0x4 */ B600, CLK600, /* 0x5 */ B1200, CLK1200, /* 0x6 */ B2000, CLK2000, /* 0x7 */ B2400, CLK2400, /* 0x8 */ B4800, CLK4800, /* 0x9 */ B9600, CLK9600, /* 0xb */ B19200, CLK19200, /* 0xc */ B38400, CLK38400, /* 0x2 (out of order!) */ B57600, CLK57600, /* 0xd */ B115200, CLK110, /* 0x1 (dupe!, 110 baud on "si") */ -1, -1};/* populated with approx character/sec rates - translated at card * initialisation time to chars per tick of the clock */static int done_chartimes = 0;static struct speedtab chartimes[] = { B75, 8, B110, 11, B150, 15, B300, 30, B600, 60, B1200, 120, B2000, 200, B2400, 240, B4800, 480, B9600, 960, B19200, 1920, B38400, 3840, B57600, 5760, B115200, 11520, -1, -1};static volatile int in_intr = 0; /* Inside interrupt handler? */static int si_default_rate = TTYDEF_SPEED;static int si_default_iflag = 0;static int si_default_oflag = 0;static int si_default_lflag = 0;#ifdef SI_DEF_HWFLOWstatic int si_default_cflag = TTYDEF_CFLAG | CRTSCTS;#elsestatic int si_default_cflag = TTYDEF_CFLAG;#endif#ifdef POLL#define POLL_INTERVAL (hz/2)static int init_finished = 0;static int fastpoll = 0;static void si_poll __P((void *));#endif/* * Array of adapter types and the corresponding RAM size. The order of * entries here MUST match the ordinal of the adapter type. */static char *si_type[] = { "EMPTY", "SIHOST", "SI2", /* MCA */ "SIHOST2", "SIEISA",};static struct kern_devconf si_kdc[NSI] = { { 0, 0, 0, /* filled in by dev_attach */ "si", 0, { MDDT_ISA, 0, "tty" }, isa_generic_externalize, 0, 0, ISA_EXTERNALLEN, &kdc_isa0, /* parent */ 0, /* parent data */ DC_UNCONFIGURED, /* state */ "Specialix SI/XIO Host adapter", DC_CLS_SERIAL, /* class */} };voidsi_registerdev(id) struct isa_device *id;{ if (id->id_unit != 0) { si_kdc[id->id_unit] = si_kdc[0]; /* struct copy */ } si_kdc[id->id_unit].kdc_unit = id->id_unit; si_kdc[id->id_unit].kdc_isa = id; si_kdc[id->id_unit].kdc_state = DC_UNCONFIGURED; dev_attach(&si_kdc[id->id_unit]);}/* Look for a valid board at the given mem addr */intsiprobe(id) struct isa_device *id;{ struct si_softc *sc; int type; u_int i, ramsize; volatile BYTE was, *ux; volatile unsigned char *maddr; unsigned char *paddr; si_registerdev(id); maddr = id->id_maddr; /* virtual address... */ paddr = (caddr_t)vtophys(id->id_maddr); /* physical address... */ DPRINT((0, DBG_AUTOBOOT, "si%d: probe at virtual=0x%x physical=0x%x\n", id->id_unit, id->id_maddr, paddr)); /* * this is a lie, but it's easier than trying to handle caching * and ram conflicts in the >1M and <16M region. */ if ((caddr_t)paddr < (caddr_t)IOM_BEGIN || (caddr_t)paddr >= (caddr_t)IOM_END) { printf("si%d: iomem (%lx) out of range\n", id->id_unit, (long)paddr); return(0); } if (id->id_unit >= NSI) { /* THIS IS IMPOSSIBLE */ return(0); } if (((u_int)paddr & 0x7fff) != 0) { DPRINT((0, DBG_AUTOBOOT|DBG_FAIL, "si%d: iomem (%x) not on 32k boundary\n", id->id_unit, paddr)); return(0); } for (i=0; i < NSI; i++) { if ((sc = &si_softc[i]) == NULL) continue; if ((caddr_t)sc->sc_paddr == (caddr_t)paddr) { DPRINT((0, DBG_AUTOBOOT|DBG_FAIL, "si%d: iomem (%x) already configured to si%d\n", id->id_unit, sc->sc_paddr, i)); return(0); } }#if NEISA > 0 if (id->id_iobase > 0x0fff) { /* EISA card */ int irq, port; unsigned long base; int eisa_irqs[] = { 0,IRQ1,IRQ2,IRQ3,IRQ4,IRQ5,IRQ6,IRQ7, IRQ8,IRQ9,IRQ10,IRQ11,IRQ12,IRQ13,IRQ14,IRQ15 }; port = id->id_iobase; base = (inb(port+1) << 24) | (inb(port) << 16); irq = ((inb(port+2) >> 4) & 0xf); id->id_irq = eisa_irqs[irq]; DPRINT((0, DBG_AUTOBOOT, "si%d: EISA base %x, irq %x, id_irq %x, port %x\n", id->id_unit, base, irq, id->id_irq, port)); if ((id->id_irq&(IRQ1|IRQ2|IRQ8|IRQ13)) != 0) goto bad_irq; id->id_iobase &= 0xf000; id->id_iosize = 0x0fff; type = EISA; outb(p+2, (BYTE)irq << 4); sc->sc_eisa_iobase = p; sc->sc_eisa_irqbits = irq << 4; ramsize = SIEISA_RAMSIZE; goto got_card; }#endif /* Is there anything out there? (0x17 is just an arbitrary number) */ *maddr = 0x17; if (*maddr != 0x17) { DPRINT((0, DBG_AUTOBOOT|DBG_FAIL, "si%d: 0x17 check fail at phys 0x%x\n", id->id_unit, paddr));fail: return(0); } /* * OK, now to see if whatever responded is really an SI card. * Try for a MK II first (SIHOST2) */ for (i=SIPLSIG; i<SIPLSIG+8; i++) if ((*(maddr+i) & 7) != (~(BYTE)i & 7)) goto try_mk1; /* It must be an SIHOST2 */ *(maddr + SIPLRESET) = 0; *(maddr + SIPLIRQCLR) = 0; *(maddr + SIPLIRQSET) = 0x10; type = SIHOST2; ramsize = SIHOST2_RAMSIZE; goto got_card; /* * Its not a MK II, so try for a MK I (SIHOST) */try_mk1: *(maddr+SIRESET) = 0x0; /* reset the card */ *(maddr+SIINTCL) = 0x0; /* clear int */ *(maddr+SIRAM) = 0x17; if (*(maddr+SIRAM) != (BYTE)0x17) goto fail; *(maddr+0x7ff8) = 0x17; if (*(maddr+0x7ff8) != (BYTE)0x17) { DPRINT((0, DBG_AUTOBOOT|DBG_FAIL, "si%d: 0x17 check fail at phys 0x%x = 0x%x\n", id->id_unit, paddr+0x77f8, *(maddr+0x77f8))); goto fail; } /* It must be an SIHOST (maybe?) - there must be a better way XXXX */ type = SIHOST; ramsize = SIHOST_RAMSIZE;got_card: DPRINT((0, DBG_AUTOBOOT, "si%d: found type %d card, try memory test\n", id->id_unit, type)); /* Try the acid test */ ux = (BYTE *)(maddr + SIRAM); for (i=0; i<ramsize; i++, ux++) *ux = (BYTE)(i&0xff); ux = (BYTE *)(maddr + SIRAM); for (i=0; i<ramsize; i++, ux++) { if ((was = *ux) != (BYTE)(i&0xff)) { DPRINT((0, DBG_AUTOBOOT|DBG_FAIL, "si%d: match fail at phys 0x%x, was %x should be %x\n", id->id_unit, paddr+i, was, i&0xff)); goto fail; } } /* clear out the RAM */ ux = (BYTE *)(maddr + SIRAM); for (i=0; i<ramsize; i++) *ux++ = 0; ux = (BYTE *)(maddr + SIRAM); for (i=0; i<ramsize; i++) { if ((was = *ux++) != 0) { DPRINT((0, DBG_AUTOBOOT|DBG_FAIL, "si%d: clear fail at phys 0x%x, was %x\n", id->id_unit, paddr+i, was)); goto fail; } } /* * Success, we've found a valid board, now fill in * the adapter structure. */ switch (type) { case SIHOST2: if ((id->id_irq&(IRQ11|IRQ12|IRQ15)) == 0) {bad_irq: DPRINT((0, DBG_AUTOBOOT|DBG_FAIL, "si%d: bad IRQ value - %d\n", id->id_unit, id->id_irq)); return(0); } id->id_msize = SIHOST2_MEMSIZE; break; case SIHOST: if ((id->id_irq&(IRQ11|IRQ12|IRQ15)) == 0) { goto bad_irq; } id->id_msize = SIHOST_MEMSIZE; break; case SIEISA: id->id_msize = SIEISA_MEMSIZE; break; case SI2: /* MCA */ default: printf("si%d: %s not supported\n", id->id_unit, si_type[type]); return(0); } si_softc[id->id_unit].sc_type = type; si_softc[id->id_unit].sc_typename = si_type[type]; return(-1); /* -1 == found */}/* * Attach the device. Initialize the card. */intsiattach(id) struct isa_device *id;{ int unit = id->id_unit; struct si_softc *sc = &si_softc[unit]; struct si_port *pp; volatile struct si_channel *ccbp; volatile struct si_reg *regp; volatile caddr_t maddr; struct si_module *modp; struct tty *tp; struct speedtab *spt; int nmodule, nport, x, y; int uart_type; DPRINT((0, DBG_AUTOBOOT, "si%d: siattach\n", id->id_unit)); sc->sc_paddr = (caddr_t)vtophys(id->id_maddr); sc->sc_maddr = id->id_maddr; sc->sc_irq = id->id_irq; sc->sc_ports = NULL; /* mark as uninitialised */ maddr = sc->sc_maddr; /* * OK, now lets download the firmware and try and boot the CPU.. */ DPRINT((0, DBG_DOWNLOAD, "si%d: si_download: nbytes %d\n", id->id_unit, si_dsize)); bcopy(si_download, maddr, si_dsize); switch (sc->sc_type) { case SIEISA:#if NEISA > 0 /* modify the Z280 firmware to tell it that it's on an EISA */ *(maddr+0x42) = 1; outb(sc->sc_eisa_iobase+2, sc->sc_eisa_irqbits | 4); (void)inb(sc->sc_eisa_iobase+3); /* reset interrupt */ break;#endif /* fall-through if not EISA */ case SI2: /* * must get around to converting the code for * these one day, if FreeBSD ever supports it. */ return 0; case SIHOST: *(maddr+SIRESET_CL) = 0; *(maddr+SIINTCL_CL) = 0; break; case SIHOST2: *(maddr+SIPLRESET) = 0x10; switch (sc->sc_irq) { case IRQ11: *(maddr+SIPLIRQ11) = 0x10; break; case IRQ12: *(maddr+SIPLIRQ12) = 0x10; break; case IRQ15: *(maddr+SIPLIRQ15) = 0x10; break; } *(maddr+SIPLIRQCLR) = 0x10; break; } DELAY(1000000); /* wait around for a second */ regp = (struct si_reg *)maddr; y = 0; /* wait max of 5 sec for init OK */ while (regp->initstat == 0 && y++ < 10) { DELAY(500000); } switch (regp->initstat) { case 0: printf("si%d: startup timeout - aborting\n", unit); sc->sc_type = SIEMPTY; return 0; case 1: /* set throttle to 125 intr per second */ regp->int_count = INT_COUNT; /* rx intr max of 25 timer per second */ regp->rx_int_count = RXINT_COUNT; regp->int_pending = 0; /* no intr pending */ regp->int_scounter = 0; /* reset counter */ break; case 0xff: /* * No modules found, so give up on this one. */ printf("si%d: %s - no ports found\n", unit, si_type[sc->sc_type]); return 0; default: printf("si%d: Z280 version error - initstat %x\n", unit, regp->initstat); return 0; } /* * First time around the ports just count them in order * to allocate some memory. */ nport = 0; modp = (struct si_module *)(maddr + 0x80); for (;;) { DPRINT((0, DBG_DOWNLOAD, "si%d: ccb addr 0x%x\n", unit, modp)); switch (modp->sm_type & (~MMASK)) { case M232: case M422: DPRINT((0, DBG_DOWNLOAD, "si%d: Found 232/422 module, %d ports\n", unit, (int)(modp->sm_type & MMASK))); /* this is a firmware issue */ if (si_Nports == SI_MAXPORTPERCARD) { printf("si%d: extra ports ignored\n", unit); continue; } x = modp->sm_type & MMASK; nport += x; si_Nports += x; si_Nmodules++; break; default: printf("si%d: unknown module type %d\n", unit, modp->sm_type); break; } if (modp->sm_next == 0) break; modp = (struct si_module *) (maddr + (unsigned)(modp->sm_next & 0x7fff));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -