📄 si.c
字号:
/* * Device driver for Specialix range (SI/XIO) of serial line multiplexors. * * Copyright (C) 1990, 1992, 1998 Specialix International, * Copyright (C) 1993, Andy Rutter <andy@acronym.co.uk> * Copyright (C) 1995, Peter Wemm <peter@netplex.com.au> * * 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.78 1999/01/12 00:36:35 eivind Exp $ */#ifndef lintstatic const char si_copyright1[] = "@(#) Copyright (C) Specialix International, 1990,1992,1998", si_copyright2[] = "@(#) Copyright (C) Andy Rutter 1993", si_copyright3[] = "@(#) Copyright (C) Peter Wemm 1995";#endif /* not lint */#include "opt_compat.h"#include "opt_debug_si.h"#include "opt_devfs.h"#include <sys/param.h>#include <sys/systm.h>#if defined(COMPAT_43) || defined(COMPAT_SUNOS)#include <sys/ioctl_compat.h>#endif#include <sys/tty.h>#include <sys/proc.h>#include <sys/conf.h>#include <sys/fcntl.h>#include <sys/dkstat.h>#include <sys/kernel.h>#include <sys/malloc.h>#include <sys/sysctl.h>#ifdef DEVFS#include <sys/devfsext.h>#endif /*DEVFS*/#include <machine/clock.h>#include <vm/vm.h>#include <vm/pmap.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 <machine/stdarg.h>#include "pci.h"#if NPCI > 0#include <pci/pcivar.h>#endif#include "eisa.h"#if NEISA > 0#include <i386/eisa/eisaconf.h>#include <i386/isa/icu.h>#endif#include "si.h"/* * This device driver is designed to interface the Specialix International * SI, XIO and SX range of serial multiplexor cards to FreeBSD on an ISA, * EISA or PCI bus machine. * * The controller is interfaced to the host via dual port RAM * and an interrupt. * * The code for the Host 1 (very old ISA cards) has not been tested. */#define POLL /* turn on poller to scan for lost interrupts */#define REALPOLL /* on each poll, scan for work regardless */#define POLLHZ (hz/10) /* 10 times per second */#define SI_I_HIGH_WATER (TTYHOG - 2 * SI_BUFFERSIZE)#define INT_COUNT 25000 /* max of 125 ints per second */#define JET_INT_COUNT 100 /* max of 100 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, u_long, caddr_t, int, struct proc *));static void si_start __P((struct tty *));static timeout_t si_lstart;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));static int siparam __P((struct tty *, struct termios *));static int siprobe __P((struct isa_device *id));static int siattach __P((struct isa_device *id));static void si_modem_state __P((struct si_port *pp, struct tty *tp, int hi_ip));static void si_intr __P((int unit));static char * si_modulename __P((int host_type, int uart_type));struct isa_driver sidriver = { siprobe, siattach, "si" };static u_long sipcieisacount = 0;#if NPCI > 0static const char *sipciprobe __P((pcici_t, pcidi_t));static void sipciattach __P((pcici_t, int));static struct pci_device sipcidev = { "si", sipciprobe, sipciattach, &sipcieisacount, NULL,};DATA_SET (pcidevice_set, sipcidev);#endif#if NEISA > 0static int si_eisa_probe __P((void));static int si_eisa_attach __P((struct eisa_device *ed));static struct eisa_driver si_eisa_driver = { "si", si_eisa_probe, si_eisa_attach, NULL, &sipcieisacount,};DATA_SET(eisadriver_set, si_eisa_driver);#endifstatic d_open_t siopen;static d_close_t siclose;static d_read_t siread;static d_write_t siwrite;static d_ioctl_t siioctl;static d_stop_t sistop;static d_devtotty_t sidevtotty;#define CDEV_MAJOR 68static struct cdevsw si_cdevsw = { siopen, siclose, siread, siwrite, siioctl, sistop, noreset, sidevtotty, ttpoll, nommap, NULL, "si", NULL, -1, nodump, nopsize, D_TTY,};#ifdef SI_DEBUG /* use: ``options "SI_DEBUG"'' in your config file */static void si_dprintf __P((struct si_port *pp, int flags, const char *fmt, ...));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 */SYSCTL_INT(_machdep, OID_AUTO, si_debug, CTLFLAG_RW, &si_debug, 0, "");static struct tty *si_tty;/* where the firmware lives; defined in si2_z280.c and si3_t225.c *//* old: si2_z280.c */extern unsigned char si2_z280_download[];extern unsigned short si2_z280_downloadaddr;extern int si2_z280_dsize;/* new: si3_t225.c */extern unsigned char si3_t225_download[];extern unsigned short si3_t225_downloadaddr;extern int si3_t225_dsize;extern unsigned char si3_t225_bootstrap[];extern unsigned short si3_t225_bootloadaddr;extern int si3_t225_bsize;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 */#if NEISA > 0 int sc_eisa_iobase; /* EISA io port address */ int sc_eisa_irq; /* EISA irq number */#endif#ifdef DEVFS struct { void *ttya; void *cuaa; void *ttyl; void *cual; void *ttyi; void *cuai; } devfs_token[32]; /* what is the max per card? */ void *control_token;#endif};static 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? */#ifdef POLLstatic int si_pollrate; /* in addition to irq */static int si_realpoll; /* poll HW on timer */SYSCTL_INT(_machdep, OID_AUTO, si_pollrate, CTLFLAG_RW, &si_pollrate, 0, "");SYSCTL_INT(_machdep, OID_AUTO, si_realpoll, CTLFLAG_RW, &si_realpoll, 0, ""); static int init_finished = 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", "SIMCA", /* FreeBSD does not support Microchannel */ "SIHOST2", "SIEISA", "SIPCI", "SXPCI", "SXISA",};#if NPCI > 0static const char *sipciprobe(configid, deviceid)pcici_t configid;pcidi_t deviceid;{ switch (deviceid) { case 0x400011cb: return("Specialix SI/XIO PCI host card"); break; case 0x200011cb: if (pci_conf_read(configid, SIJETSSIDREG) == 0x020011cb) return("Specialix SX PCI host card"); else return NULL; break; default: return NULL; } /*NOTREACHED*/}voidsipciattach(configid, unit)pcici_t configid;int unit;{ struct isa_device id; vm_offset_t vaddr,paddr; u_long mapval = 0; /* shut up gcc, should not be needed */ switch ( pci_conf_read(configid, 0) >> 16 ) { case 0x4000: si_softc[unit].sc_type = SIPCI; mapval = SIPCIBADR; break; case 0x2000: si_softc[unit].sc_type = SIJETPCI; mapval = SIJETBADR; break; } if (!pci_map_mem(configid, mapval, &vaddr, &paddr)) { printf("si%d: couldn't map memory\n", unit); } /* * We're cheating here a little bit. The argument to an ISA * interrupt routine is the unit number. The argument to a * PCI interrupt handler is a void *, but we're simply going * to be lazy and hand it the unit number. */ if (!pci_map_int(configid, (pci_inthand_t *) si_intr, (void *)unit, &tty_imask)) { printf("si%d: couldn't map interrupt\n", unit); } si_softc[unit].sc_typename = si_type[si_softc[unit].sc_type]; /* * More cheating: We're going to dummy up a struct isa_device * and call the other attach routine. We don't really have to * fill in very much of the structure, since we filled in a * little of the soft state already. */ id.id_unit = unit; id.id_maddr = (caddr_t) vaddr; siattach(&id);}#endif#if NEISA > 0static const char *si_eisa_match __P((eisa_id_t id));static const char *si_eisa_match(id) eisa_id_t id;{ if (id == SIEISADEVID) return ("Specialix SI/XIO EISA host card"); return (NULL);}static intsi_eisa_probe(void){ struct eisa_device *ed = NULL; int count, irq; for (count=0; (ed = eisa_match_dev(ed, si_eisa_match)) != NULL; count++) { u_long port,maddr; port = (ed->ioconf.slot * EISA_SLOT_SIZE) + SIEISABASE; eisa_add_iospace(ed, port, SIEISAIOSIZE, RESVADDR_NONE); maddr = (inb(port+1) << 24) | (inb(port) << 16); irq = ((inb(port+2) >> 4) & 0xf); eisa_add_mspace(ed, maddr, SIEISA_MEMSIZE, RESVADDR_NONE); eisa_add_intr(ed, irq); eisa_registerdev(ed, &si_eisa_driver); count++; } return count;}static intsi_eisa_attach(ed) struct eisa_device *ed;{ struct isa_device id; resvaddr_t *maddr,*iospace; u_int irq; struct si_softc *sc; sc = &si_softc[ed->unit]; sc->sc_type = SIEISA; sc->sc_typename = si_type[sc->sc_type]; if ((iospace = ed->ioconf.ioaddrs.lh_first) == NULL) { printf("si%lu: no iospace??\n", ed->unit); return -1; } sc->sc_eisa_iobase = iospace->addr; irq = ((inb(iospace->addr + 2) >> 4) & 0xf); sc->sc_eisa_irq = irq; if ((maddr = ed->ioconf.maddrs.lh_first) == NULL) { printf("si%lu: where am I??\n", ed->unit); return -1; } eisa_reg_start(ed); if (eisa_reg_iospace(ed, iospace)) { printf("si%lu: failed to register iospace %p\n", ed->unit, (void *)iospace); return -1; } if (eisa_reg_mspace(ed, maddr)) { printf("si%lu: failed to register memspace %p\n", ed->unit, (void *)maddr); return -1; } /* * We're cheating here a little bit. The argument to an ISA * interrupt routine is the unit number. The argument to a * EISA interrupt handler is a void *, but we're simply going * to be lazy and hand it the unit number. */ if (eisa_reg_intr(ed, irq, (void (*)(void *)) si_intr, (void *)(intptr_t)(ed->unit), &tty_imask, 1)) { printf("si%lu: failed to register interrupt %d\n", ed->unit, irq); return -1; } eisa_reg_end(ed); if (eisa_enable_intr(ed, irq)) { return -1; } /* * More cheating: We're going to dummy up a struct isa_device * and call the other attach routine. We don't really have to * fill in very much of the structure, since we filled in a * little of the soft state already. */ id.id_unit = ed->unit; id.id_maddr = (caddr_t) pmap_mapdev(maddr->addr, SIEISA_MEMSIZE); return (siattach(&id));}#endif/* Look for a valid board at the given mem addr */static 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_pollrate = POLLHZ; /* default 10 per second */#ifdef REALPOLL si_realpoll = 1; /* scan always */#endif 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 (%p) out of range\n", id->id_unit, (void *)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); } if (si_softc[id->id_unit].sc_typename) { /* EISA or PCI has taken this unit, choose another */ for (i=0; i < NSI; i++) { if (si_softc[i].sc_typename == NULL) { id->id_unit = i; break; } } if (i >= NSI) { DPRINT((0, DBG_AUTOBOOT|DBG_FAIL, "si%d: cannot realloc unit\n", id->id_unit)); return (0); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -