📄 dgb.c
字号:
/*- * dgb.c $FreeBSD: src/sys/gnu/i386/isa/dgb.c,v 1.20.2.2 1999/09/05 08:10:03 peter Exp $ * * Digiboard driver. * * Stage 1. "Better than nothing". * Stage 2. "Gee, it works!". * * Based on sio driver by Bruce Evans and on Linux driver by Troy * De Jongh <troyd@digibd.com> or <troyd@skypoint.com> * which is under GNU General Public License version 2 so this driver * is forced to be under GPL 2 too. * * Written by Serge Babkin, * Joint Stock Commercial Bank "Chelindbank" * (Chelyabinsk, Russia) * babkin@hq.icb.chel.su * * Assorted hacks to make it more functional and working under 3.0-current. * Fixed broken routines to prevent processes hanging on closed (thanks * to Bruce for his patience and assistance). Thanks also to Maxim Bolotin * <max@run.net> for his patches which did most of the work to get this * running under 2.2/3.0-current. * Implemented ioctls: TIOCMSDTRWAIT, TIOCMGDTRWAIT, TIOCTIMESTAMP & * TIOCDCDTIMESTAMP. * Sysctl debug flag is now a bitflag, to filter noise during debugging. * David L. Nugent <davidn@blaze.net.au> */#include "dgb.h"#if NDGB > 0 /* Helg: i.e.25 times per sec board will be polled */#define POLLSPERSEC 25/* How many charactes can we write to input tty rawq */#define DGB_IBUFSIZE (TTYHOG-100)/* the overall number of ports controlled by this driver */#ifndef NDGBPORTS# define NDGBPORTS (NDGB*16)#endif#include <sys/param.h>#include <sys/systm.h>#include <sys/reboot.h>#include <sys/ioctl.h>#include <sys/tty.h>#include <sys/proc.h>#include <sys/conf.h>#include <sys/dkstat.h>#include <sys/file.h>#include <sys/uio.h>#include <sys/kernel.h>#include <sys/sysctl.h>#include <sys/malloc.h>#include <sys/syslog.h>#ifdef DEVFS#include <sys/devfsext.h>#endif /*DEVFS*/#include <machine/clock.h>#include <vm/vm.h>#include <vm/vm_param.h>#include <vm/pmap.h>#include <i386/isa/isa_device.h>#include <gnu/i386/isa/dgbios.h>#include <gnu/i386/isa/dgfep.h>#define DEBUG#include <gnu/i386/isa/dgreg.h>/* This avoids warnings: we're an isa device only * so it does not matter... */#undef outb#define outb outbv#define CALLOUT_MASK 0x80#define CONTROL_MASK 0x60#define CONTROL_INIT_STATE 0x20#define CONTROL_LOCK_STATE 0x40#define UNIT_MASK 0x30000#define PORT_MASK 0x1F#define DEV_TO_UNIT(dev) (MINOR_TO_UNIT(minor(dev)))#define MINOR_MAGIC_MASK (CALLOUT_MASK | CONTROL_MASK)#define MINOR_TO_UNIT(mynor) (((mynor) & UNIT_MASK)>>16)#define MINOR_TO_PORT(mynor) ((mynor) & PORT_MASK)/* types. XXX - should be elsewhere */typedef u_char bool_t; /* boolean *//* digiboard port structure */struct dgb_p { bool_t status; u_char unit; /* board unit number */ u_char pnum; /* port number */ u_char omodem; /* FEP output modem status */ u_char imodem; /* FEP input modem status */ u_char modemfake; /* Modem values to be forced */ u_char modem; /* Force values */ u_char hflow; u_char dsr; u_char dcd; u_char stopc; u_char startc; u_char stopca; u_char startca; u_char fepstopc; u_char fepstartc; u_char fepstopca; u_char fepstartca; u_char txwin; u_char rxwin; ushort fepiflag; ushort fepcflag; ushort fepoflag; ushort txbufhead; ushort txbufsize; ushort rxbufhead; ushort rxbufsize; int close_delay; int count; int blocked_open; int event; int asyncflags; u_long statusflags; u_char *txptr; u_char *rxptr; volatile struct board_chan *brdchan; struct tty *tty; bool_t active_out; /* nonzero if the callout device is open */ u_int wopeners; /* # processes waiting for DCD in open() */ /* Initial state. */ struct termios it_in; /* should be in struct tty */ struct termios it_out; /* Lock state. */ struct termios lt_in; /* should be in struct tty */ struct termios lt_out; bool_t do_timestamp; bool_t do_dcd_timestamp; struct timeval timestamp; struct timeval dcd_timestamp; /* flags of state, are used in sleep() too */ u_char closing; /* port is being closed now */ u_char draining; /* port is being drained now */ u_char used; /* port is being used now */ u_char mustdrain; /* data must be waited to drain in dgbparam() */#ifdef DEVFS struct { void *tty; void *init; void *lock; void *cua; } devfs_token;#endif};/* Digiboard per-board structure */struct dgb_softc { /* struct board_info */ u_char status; /* status: DISABLED/ENABLED */ u_char unit; /* unit number */ u_char type; /* type of card: PCXE, PCXI, PCXEVE */ u_char altpin; /* do we need alternate pin setting ? */ ushort numports; /* number of ports on card */ ushort port; /* I/O port */ u_char *vmem; /* virtual memory address */ long pmem; /* physical memory address */ int mem_seg; /* internal memory segment */ struct dgb_p *ports; /* pointer to array of port descriptors */ struct tty *ttys; /* pointer to array of TTY structures */ volatile struct global_data *mailbox; }; static struct dgb_softc dgb_softc[NDGB];static struct dgb_p dgb_ports[NDGBPORTS];static struct tty dgb_tty[NDGBPORTS];/* * The public functions in the com module ought to be declared in a com-driver * system header. *//* Interrupt handling entry points. */static void dgbpoll __P((void *unit_c));/*static void dgbintr __P((int unit));*//* Device switch entry points. */#define dgbreset noreset#define dgbmmap nommap#define dgbstrategy nostrategystatic int dgbattach __P((struct isa_device *dev));static int dgbprobe __P((struct isa_device *dev));static void fepcmd(struct dgb_p *port, unsigned cmd, unsigned op1, unsigned op2, unsigned ncmds, unsigned bytecmd);static void dgbstart __P((struct tty *tp));static int dgbparam __P((struct tty *tp, struct termios *t));static void dgbhardclose __P((struct dgb_p *port));static void dgb_drain_or_flush __P((struct dgb_p *port));static int dgbdrain __P((struct dgb_p *port));static void dgb_pause __P((void *chan));static void wakeflush __P((void *p));static void disc_optim __P((struct tty *tp, struct termios *t));struct isa_driver dgbdriver = { dgbprobe, dgbattach, "dgb",0};static d_open_t dgbopen;static d_close_t dgbclose;static d_read_t dgbread;static d_write_t dgbwrite;static d_ioctl_t dgbioctl;static d_stop_t dgbstop;static d_devtotty_t dgbdevtotty;#define CDEV_MAJOR 58static struct cdevsw dgb_cdevsw = { dgbopen, dgbclose, dgbread, dgbwrite, /*58*/ dgbioctl, dgbstop, noreset, dgbdevtotty, /* dgb */ ttselect, nommap, NULL, "dgb", NULL, -1 };static speed_t dgbdefaultrate = TTYDEF_SPEED;static struct speedtab dgbspeedtab[] = { 0, FEP_B0, /* old (sysV-like) Bx codes */ 50, FEP_B50, 75, FEP_B75, 110, FEP_B110, 134, FEP_B134, 150, FEP_B150, 200, FEP_B200, 300, FEP_B300, 600, FEP_B600, 1200, FEP_B1200, 1800, FEP_B1800, 2400, FEP_B2400, 4800, FEP_B4800, 9600, FEP_B9600, 19200, FEP_B19200, 38400, FEP_B38400, 57600, (FEP_FASTBAUD|FEP_B50), /* B50 & fast baud table */ 115200, (FEP_FASTBAUD|FEP_B110), /* B100 & fast baud table */ -1, -1};static struct dbgflagtbl{ tcflag_t in_mask; tcflag_t in_val; tcflag_t out_val;} dgb_cflags[] ={ { PARODD, PARODD, FEP_PARODD }, { PARENB, PARENB, FEP_PARENB }, { CSTOPB, CSTOPB, FEP_CSTOPB }, { CSIZE, CS5, FEP_CS6 }, { CSIZE, CS6, FEP_CS6 }, { CSIZE, CS7, FEP_CS7 }, { CSIZE, CS8, FEP_CS8 }, { CLOCAL, CLOCAL, FEP_CLOCAL }, { (tcflag_t)-1 }}, dgb_iflags[] ={ { IGNBRK, IGNBRK, FEP_IGNBRK }, { BRKINT, BRKINT, FEP_BRKINT }, { IGNPAR, IGNPAR, FEP_IGNPAR }, { PARMRK, PARMRK, FEP_PARMRK }, { INPCK, INPCK, FEP_INPCK }, { ISTRIP, ISTRIP, FEP_ISTRIP }, { IXON, IXON, FEP_IXON }, { IXOFF, IXOFF, FEP_IXOFF }, { IXANY, IXANY, FEP_IXANY }, { (tcflag_t)-1 }}, dgb_flow[] ={ { CRTSCTS, CRTSCTS, CTS|RTS }, { CRTSCTS, CCTS_OFLOW, CTS }, { CRTSCTS, CRTS_IFLOW, RTS }, { (tcflag_t)-1 }};/* xlat bsd termios flags to dgb sys-v style */static tcflag_tdgbflags(struct dbgflagtbl *tbl, tcflag_t input){ tcflag_t output = 0; int i; for (i=0; tbl[i].in_mask != (tcflag_t)-1; i++) { if ((input & tbl[i].in_mask) == tbl[i].in_val) output |= tbl[i].out_val; } return output;}static int dgbdebug=0;SYSCTL_INT(_debug, OID_AUTO, dgb_debug, CTLFLAG_RW, &dgbdebug, 0, "");static int setwin __P((struct dgb_softc *sc, unsigned addr));static int setinitwin __P((struct dgb_softc *sc, unsigned addr));static void hidewin __P((struct dgb_softc *sc));static void towin __P((struct dgb_softc *sc, int win));/*Helg: to allow recursive dgb...() calls */typedef struct { /* If we were called and don't want to disturb we need: */ short port, /* write to this port */ data; /* this data on exit */ /* or DATA_WINOFF to close memory window on entry */ } BoardMemWinState; /* so several channels and even boards can coexist */#define DATA_WINOFF 0static BoardMemWinState bmws;/* return current memory window state and close window */static BoardMemWinStatebmws_get(void){ BoardMemWinState bmwsRet=bmws; if(bmws.data!=DATA_WINOFF) outb(bmws.port, bmws.data=DATA_WINOFF); return bmwsRet;}/* restore memory window state */static voidbmws_set(BoardMemWinState ws){ if(ws.data != bmws.data || ws.port!=bmws.port ) { if(bmws.data!=DATA_WINOFF) outb(bmws.port,DATA_WINOFF); if(ws.data!=DATA_WINOFF) outb(ws.port, ws.data); bmws=ws; }}static inline int setwin(sc,addr) struct dgb_softc *sc; unsigned int addr;{ if(sc->type==PCXEVE) { outb(bmws.port=sc->port+1, bmws.data=FEPWIN|(addr>>13)); DPRINT3(DB_WIN,"dgb%d: switched to window 0x%x\n",sc->unit,addr>>13); return (addr & 0x1FFF); } else { outb(bmws.port=sc->port,bmws.data=FEPMEM); return addr; }}static inline int setinitwin(sc,addr) struct dgb_softc *sc; unsigned int addr;{ if(sc->type==PCXEVE) { outb(bmws.port=sc->port+1, bmws.data=FEPWIN|(addr>>13)); DPRINT3(DB_WIN,"dgb%d: switched to window 0x%x\n",sc->unit,addr>>13); return (addr & 0x1FFF); } else { outb(bmws.port=sc->port,bmws.data=inb(sc->port)|FEPMEM); return addr; }}static inline voidhidewin(sc) struct dgb_softc *sc;{ bmws.data=0; if(sc->type==PCXEVE) outb(bmws.port=sc->port+1, bmws.data); else outb(bmws.port=sc->port, bmws.data);}static inline voidtowin(sc,win) struct dgb_softc *sc; int win;{ if(sc->type==PCXEVE) { outb(bmws.port=sc->port+1, bmws.data=win); } else { outb(bmws.port=sc->port,bmws.data=FEPMEM); }}static intdgbprobe(dev) struct isa_device *dev;{ struct dgb_softc *sc= &dgb_softc[dev->id_unit]; int i, v, t; u_long win_size; /* size of vizible memory window */ u_char *mem; int addr; int unit=dev->id_unit; sc->unit=dev->id_unit; sc->port=dev->id_iobase; if(dev->id_flags & DGBFLAG_ALTPIN) sc->altpin=1; else sc->altpin=0; /* left 24 bits only (ISA address) */ sc->pmem=((long)dev->id_maddr & 0xFFFFFF); DPRINT4(DB_INFO,"dgb%d: port 0x%x mem 0x%x\n",unit,sc->port,sc->pmem); outb(sc->port, FEPRST); sc->status=DISABLED; for(i=0; i< 1000; i++) { DELAY(1); if( (inb(sc->port) & FEPMASK) == FEPRST ) { sc->status=ENABLED; DPRINT3(DB_EXCEPT,"dgb%d: got reset after %d us\n",unit,i); break; } } if(sc->status!=ENABLED) { DPRINT2(DB_EXCEPT,"dgb%d: failed to respond\n",dev->id_unit); return 0; } /* check type of card and get internal memory characteristics */ v=inb(sc->port); if( v & 0x1 ) { switch( v&0x30 ) { case 0: sc->mem_seg=0xF000; win_size=0x10000; printf("dgb%d: PC/Xi 64K\n",dev->id_unit); break; case 0x10: sc->mem_seg=0xE000; win_size=0x20000; printf("dgb%d: PC/Xi 128K\n",dev->id_unit); break; case 0x20: sc->mem_seg=0xC000; win_size=0x40000; printf("dgb%d: PC/Xi 256K\n",dev->id_unit); break; default: /* case 0x30: */ sc->mem_seg=0x8000; win_size=0x80000; printf("dgb%d: PC/Xi 512K\n",dev->id_unit); break; } sc->type=PCXI; } else { outb(sc->port, 1); v=inb(sc->port); if( v & 0x1 ) { printf("dgb%d: PC/Xm isn't supported\n",dev->id_unit); sc->status=DISABLED; return 0; } sc->mem_seg=0xF000; if(dev->id_flags==DGBFLAG_NOWIN || ( v&0xC0 )==0) { win_size=0x10000; printf("dgb%d: PC/Xe 64K\n",dev->id_unit); sc->type=PCXE; } else { win_size=0x2000; printf("dgb%d: PC/Xe 64/8K (windowed)\n",dev->id_unit); sc->type=PCXEVE; if((u_long)sc->pmem & ~0xFFE000) { printf("dgb%d: warning: address 0x%x truncated to 0x%x\n", dev->id_unit, sc->pmem, (long)sc->pmem & 0xFFE000); dev->id_maddr= (u_char *)( (long)sc->pmem & 0xFFE000 ); } } } /* save size of vizible memory segment */ dev->id_msize=win_size; /* map memory */ dev->id_maddr=sc->vmem=pmap_mapdev(sc->pmem,dev->id_msize); outb(sc->port, FEPCLR); /* drop RESET */ hidewin(sc); /* Helg: to set initial bmws state */ return 4; /* we need I/O space of 4 ports */}static intdgbattach(dev) struct isa_device *dev;{ int unit=dev->id_unit; struct dgb_softc *sc= &dgb_softc[dev->id_unit]; int i, t; u_char *mem; u_char *ptr; int addr; struct dgb_p *port; volatile struct board_chan *bc; struct global_data *gd; int shrinkmem; int nfails; ushort *pstat; int lowwater; static int nports=0; if(sc->status!=ENABLED) { DPRINT2(DB_EXCEPT,"dbg%d: try to attach a disabled card\n",unit); return 0; } mem=sc->vmem; DPRINT3(DB_INFO,"dgb%d: internal memory segment 0x%x\n",unit,sc->mem_seg); outb(sc->port, FEPRST); DELAY(1); for(i=0; (inb(sc->port) & FEPMASK) != FEPRST ; i++) { if(i>10000) { printf("dgb%d: 1st reset failed\n",dev->id_unit); sc->status=DISABLED; hidewin(sc); return 0; } DELAY(1); } DPRINT3(DB_INFO,"dgb%d: got reset after %d us\n",unit,i); /* for PCXEVE set up interrupt and base address */ if(sc->type==PCXEVE) { t=(((u_long)sc->pmem>>8) & 0xFFE0) | 0x10 /* enable windowing */; /* IRQ isn't used */#if 0 switch(dev->id_irq) { case IRQ3: t|=0x1; break; case IRQ5: t|=2; break; case IRQ7: t|=3; break; case IRQ10: t|=4; break; case IRQ11: t|=5; break; case IRQ12: t|=6; break; case IRQ15: t|=7; break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -