📄 fas.c
字号:
/* FAS Final Async Solution driver for 286/386 versions of system V UNIX *//* FAS was developed byUwe Doering INET : gemini@geminix.in-berlin.deBillstedter Pfad 17 b UUCP : ...!unido!fub!geminix.in-berlin.de!gemini1000 Berlin 20Germany*//*+:EDITS:*//*:09-10-1992-13:59-wht@n4hgf-ECU release 3.20 *//*:08-22-1992-15:38-wht@n4hgf-ECU release 3.20 BETA *//*:07-25-1991-12:57-wht@n4hgf-ECU release 3.10 *//*:06-04-1991-19:41-wht@n4hgf-add FASIC_SIP_CHANGE *//*:02-05-1991-12:13-wht@n4hgf-apply 2.08b2->2.08.0 diffs *//*:01-20-1991-05:01-wht@n4hgf-changed buffer sizes */#if defined(FASI)char *fasi_driver_ident = "FAS/i 2.08.01";#endif /* FASI */#if !defined (M_I286) && !defined(__STDC__)#ident "@(#)fas.c 2.08"#endif/* Note: This source code was quite heavily optimized for speed. You may wonder that register variables aren't used everywhere. This is because there is an overhead in memory accesses when using register variables. As you may know data accesses usually need much more wait states on the memory bus than code accesses (because of page or cache misses). Therefor, saving some data accesses has higher priority than saving code accesses. You may also note some not very elegant constructions that may be intentional because they are faster. If you want to make style improvements you should check the assembler output whether this wouldn't slow things down. Decisions for speed optimization were based on assembler listings produced by the standard UNIX V 3.X/386 C compiler.*/#include <sys/param.h>#include <sys/types.h>#include <sys/signal.h>#include <sys/buf.h>#include <sys/dir.h>#if defined (XENIX)#include <sys/page.h>#include <sys/seg.h>#endif#include <sys/user.h>#include <sys/errno.h>#include <sys/tty.h>#include <sys/conf.h>#include <sys/sysinfo.h>#include <sys/file.h>#if !defined (XENIX) && !defined(CBAUD)#include <sys/termio.h>#endif#include <sys/ioctl.h>#if !defined(FASI)#include <macros.h>#endif#if defined (HAVE_VPIX)#if !defined (XENIX)#include <sys/tss.h>#include <sys/immu.h>#include <sys/region.h>#endif#include <sys/proc.h>#include <sys/v86.h>#endif#if defined (XENIX)#include "fas.h"#else#include <local/fas.h>#if !defined (NO_ASM)#include <sys/inline.h>#endif#endif#if defined (SCO) || defined (XENIX)#define asyputchar sioputchar#define asygetchar siogetchar#endif#if defined (XENIX) || defined (NO_ASM)#define intr_disable() old_level = SPLINT ()#define intr_restore() (void) splx (old_level)#define REGVAR#else/* This is a terrible ugly kludge to speed up the `inb' and `outb' functions. I.e., originally, the `outb' inline function had an overhead of four data memory accesses for parameter passing. This parameter passing actually consumed more clock cycles than the assembler `outb' command itself. Although this solution can't prevent unnessessary register moves it limits them at least to register to register moves that are much faster. You need a line like the following in the declaration part of every function that uses `inb' or `outb' calls: REGVAR; This hack should work with every compiler that knows about the UNIX V 3.X/386 standard compiler's inline assembler directives.*/asm void loadal (val){%reg val; movl val,%eax%mem val; movb val,%al}asm void loaddx (val){%reg val; movl val,%edx%mem val; movw val,%dx}asm int outbyte (){ outb (%dx)}asm int inbyte (){ xorl %eax,%eax inb (%dx)}/* The port parameter of the `outb' macro must be one of the predefined port macros from `fas.h' or a simple uint variable (no indirection is allowed). Additionally, `fip' must be a register variable in the functions where `outb' is used. This prevents the destruction of the `eax' CPU register while loading the `edx' register with the port address. This is highly compiler implementation specific.*/#define outb(port,val) (regvar = (val), loadal (regvar), regvar = (port), loaddx (regvar), outbyte ())#define inb(port) (regvar = (port), loaddx (regvar), inbyte ())#define REGVAR register uint regvar/* This function inserts the address optimization assembler pseudo-op wherever called.*/asm void optim (){ .optim}/* This dummy function has nothing to do but to call optim so that the `.optim' assembler pseudo-op will be included in the assembler file. This must be the first of all functions.*/#if defined (OPTIM) /* Define for uPort, ISC doesn't know about */static void /* `.optim', but has turned on optimization by */dummy () /* default, so we don't need it there anyway. */{ optim ();}#endif#endif /* XENIX || NO_ASM *//* functions provided by this driver */int fasinit ();int fasopen ();int fasclose ();int fasread ();int faswrite ();int fasioctl ();int fasintr ();#if defined (NEED_PUT_GETCHAR)int asyputchar ();int asygetchar ();#endif#if defined (NEED_INIT8250)int init8250 ();#endifstatic int fas_proc ();static void fas_param ();static void fas_fproc ();static void fas_mproc ();static uint fas_rproc ();static void fas_xproc ();static void fas_event ();#if defined (HAVE_VPIX)static int fas_vpix_sr ();#endifstatic void fas_rxfer ();static void fas_xxfer ();static void fas_ihlw_check ();static void fas_hdx_check ();static void fas_hangup ();static void fas_timeout ();static void fas_cmd ();static void fas_open_device ();static void fas_close_device ();static uint fas_make_ctl_val ();static int fas_test_device ();/* external functions used by this driver */extern int ttinit ();extern int ttiocom ();extern int ttyflush ();extern int SPLINT ();extern int SPLWRK ();extern int splx ();extern int sleep ();extern int wakeup ();extern void longjmp ();extern int signal ();extern int timeout ();extern int untimeout ();extern int printf ();#if defined (SCO) || defined (XENIX)extern int printcfg ();#endif#if defined (HAVE_VPIX)extern int fubyte ();extern int subyte ();extern int v86setint ();#endif#if defined (XENIX)extern int inb ();extern int outb ();#endif/* external data objects used by this driver */extern int tthiwat [];/* the following stuff is defined in space.c */extern uint fas_physical_units;extern ulong fas_port [];extern uint fas_vec [];extern uint fas_init_seq [];extern uint fas_mcb [];extern ulong fas_modem [];extern ulong fas_flow [];extern uint fas_ctl_port [];extern uint fas_ctl_val [];extern uint fas_int_ack_port [];extern uint fas_int_ack [];extern struct fas_info fas_info [];extern struct tty fas_tty [];extern struct fas_info *fas_info_ptr [];extern struct tty *fas_tty_ptr [];/* end of space.c references */#if defined(FASI)int fasiintr_entries = 0;extern char *fasi_space_ident;#endif /* FASI *//* fas_is_initted Flag to indicate that we have been thru init. This is realy only necessary for systems that use asyputchar and asygetchar but it doesn't hurt to have it anyway.*/static int fas_is_initted = FALSE;/* event_scheduled Flag to indicate that the event handler has been scheduled via the timeout() function.*/static int event_scheduled = FALSE;/* array of pointers to the first fas_info structure for each interrupt vector*/static struct fas_info *fas_first_int_user [NUM_INT_VECTORS];/* the values for the various baud rates */static uint fas_speeds [CBAUD + 1] ={ 1, BAUD_BASE/50, BAUD_BASE/75, BAUD_BASE/110, (2*BAUD_BASE+134)/269, BAUD_BASE/150, BAUD_BASE/200, BAUD_BASE/300, BAUD_BASE/600, BAUD_BASE/1200, BAUD_BASE/1800, BAUD_BASE/2400, BAUD_BASE/4800, BAUD_BASE/9600, BAUD_BASE/19200, BAUD_BASE/38400};/* time for one character to completely leave the transmitter shift register */static uint fas_ctimes [CBAUD + 1] ={ 1, HZ*15/50+2, HZ*15/75+2, HZ*15/110+2, HZ*30/269+2, HZ*15/150+2, HZ*15/200+2, HZ*15/300+2, HZ*15/600+2, HZ*15/1200+2, HZ*15/1800+2, HZ*15/2400+2, HZ*15/4800+2, HZ*15/9600+2, HZ*15/19200+2, HZ*15/38400+2};/* dynamically adapt xmit buffer size to baud rate to prevent long buffer drains at low speeds These values are checked against boundaries and will be modified if necessary before use. Checking is done in fas_param (). Drain time is about 5 seconds with continuous character flow.*/static uint fas_xbuf_size [CBAUD + 1] ={ 1, 50/2, 75/2, 110/2, 269/4, 150/2, 200/2, 300/2, 600/2, 1200/2, 1800/2, 2400/2, 4800/2, 9600/2, 19200/2, 38400/2};/* lookup table for minor device number -> open mode flags translation */static uint fas_open_modes [16] ={ OS_OPEN_FOR_DIALOUT | OS_FAKE_CARR_ON | OS_CLOCAL, OS_OPEN_FOR_DIALOUT | OS_FAKE_CARR_ON | OS_CLOCAL | OS_HWO_HANDSHAKE | OS_HWI_HANDSHAKE, OS_OPEN_FOR_DIALOUT | OS_FAKE_CARR_ON | OS_CLOCAL | OS_HWO_HANDSHAKE, OS_OPEN_FOR_DIALOUT | OS_FAKE_CARR_ON | OS_CLOCAL | OS_HWO_HANDSHAKE | OS_HDX_HANDSHAKE, OS_OPEN_FOR_DIALOUT | OS_FAKE_CARR_ON, OS_OPEN_FOR_DIALOUT | OS_FAKE_CARR_ON | OS_HWO_HANDSHAKE | OS_HWI_HANDSHAKE, OS_OPEN_FOR_DIALOUT | OS_FAKE_CARR_ON | OS_HWO_HANDSHAKE, OS_OPEN_FOR_DIALOUT | OS_FAKE_CARR_ON | OS_HWO_HANDSHAKE | OS_HDX_HANDSHAKE, OS_OPEN_FOR_GETTY | OS_WAIT_OPEN | OS_NO_DIALOUT, OS_OPEN_FOR_GETTY | OS_WAIT_OPEN | OS_NO_DIALOUT | OS_HWO_HANDSHAKE | OS_HWI_HANDSHAKE, OS_OPEN_FOR_GETTY | OS_WAIT_OPEN | OS_NO_DIALOUT | OS_HWO_HANDSHAKE, OS_OPEN_FOR_GETTY | OS_WAIT_OPEN | OS_NO_DIALOUT | OS_HWO_HANDSHAKE | OS_HDX_HANDSHAKE, OS_OPEN_FOR_GETTY | OS_WAIT_OPEN, OS_OPEN_FOR_GETTY | OS_WAIT_OPEN | OS_HWO_HANDSHAKE | OS_HWI_HANDSHAKE, OS_OPEN_FOR_GETTY | OS_WAIT_OPEN | OS_HWO_HANDSHAKE, OS_OPEN_FOR_GETTY | OS_WAIT_OPEN | OS_HWO_HANDSHAKE | OS_HDX_HANDSHAKE};/* The following defines are used to access multiplexed ports. */#define GET_PORT(port,num) \ ((fip->device_flags.i & DF_CTL_EVERY)\ ? (port)\ : (port) + (num))#define fas_first_ctl(fip,port) \ ((void) (((fip)->device_flags.i & DF_CTL_FIRST)\ ? outb (CTL_PORT, (port).p.ctl)\ : 0))#define fas_ctl(fip,port) \ ((void) (((fip)->device_flags.i & (DF_CTL_FIRST | DF_CTL_EVERY))\ ? outb (CTL_PORT, (port).p.ctl)\ : 0))#define fas_first_outb(fip,port,val) \ ((void) (((fip)->device_flags.i & (DF_CTL_FIRST | DF_CTL_EVERY))\ ? outb (CTL_PORT, (port).p.ctl)\ : 0),\ (void) outb ((port).addr, (val)))#define fas_outb(fip,port,val) \ ((void) (((fip)->device_flags.i & DF_CTL_EVERY)\ ? outb (CTL_PORT, (port).p.ctl)\ : 0),\ (void) outb ((port).addr, (val)))#define fas_first_inb(fip,port) \ ((void) (((fip)->device_flags.i & (DF_CTL_FIRST | DF_CTL_EVERY))\ ? outb (CTL_PORT, (port).p.ctl)\ : 0),\ inb ((port).addr))#define fas_inb(fip,port) \ ((void) (((fip)->device_flags.i & DF_CTL_EVERY)\ ? outb (CTL_PORT, (port).p.ctl)\ : 0),\ inb ((port).addr))/* The following defines are used to take apart the minor device numbers. */#define GET_UNIT(dev) ((dev) & 0x0f)#define GET_OPEN_MODE(dev) (fas_open_modes [((dev) >> 4) & 0x0f])/* lock device against concurrent use */#define get_device_lock(fip,prio) \{\ /* sleep while device is used by an other process */\ while ((fip)->device_flags.i & DF_DEVICE_LOCKED)\ (void) sleep ((caddr_t) &(fip)->device_flags.i, (prio));\ (fip)->device_flags.s |= DF_DEVICE_LOCKED;\}/* release device */#define release_device_lock(fip) \{\ (fip)->device_flags.s &= ~DF_DEVICE_LOCKED;\ /* wakeup the process that may wait for this device */\ (void) wakeup ((caddr_t) &(fip)->device_flags.i);\}/* schedule event */#define event_sched(fip,event) \{\ (fip)->event_flags.s |= (event);\ if (!event_scheduled)\ {\ event_scheduled = TRUE;\ (void) timeout (fas_event, (void *) NULL,\ (EVENT_TIME) * (HZ) / 1000);\ }\}/* fasinit This routine checks for the presense of the devices in the fas_port array and if the device is present tests and initializes it. During the initialization if the device is determined to be an NS16550A chip the DF_DEVICE_IS_NS16550A flag is set and the FIFOs will be used. If the device is an i82510 chip the DF_DEVICE_IS_I82510 flag is set and the device will be handled accordingly.*/intfasinit (){ register struct fas_info *fip; register uint unit; uint logical_units, port, *seq_ptr; char port_stat [MAX_UNITS + 1]; REGVAR; if (fas_is_initted) return (0); fas_is_initted = TRUE; /* execute the init sequence for the serial card */ for (seq_ptr = fas_init_seq; *seq_ptr; seq_ptr++) { port = *seq_ptr; seq_ptr++; if (*seq_ptr & READ_PORT) (void) inb (port); else (void) outb (port, *seq_ptr); } /* setup the list of pointers to the tty structures */ for (unit = 0, logical_units = fas_physical_units * 2; unit < logical_units; unit++) fas_tty_ptr [unit] = &fas_tty [unit]; /* setup and initialize all serial ports */ for (unit = 0; unit < fas_physical_units; unit++) { fas_info_ptr [unit] = fip = &fas_info [unit]; port_stat [unit] = '-'; if (port = (uint) ((ushort) (fas_port [unit]))) { /* check the int vector */ if (fas_vec [unit] >= NUM_INT_VECTORS) { port_stat [unit] = '>'; continue; } /* init all of its ports */ if (fas_ctl_port [unit]) { fip->ctl_port = fas_ctl_port [unit]; if (fas_ctl_val [unit] & 0xff00) fip->device_flags.s |= DF_CTL_EVERY; else fip->device_flags.s |= DF_CTL_FIRST; } fip->port_0.p.addr = GET_PORT (port, 0); fip->port_1.p.addr = GET_PORT (port, 1); fip->port_2.p.addr = GET_PORT (port, 2); fip->port_3.p.addr = GET_PORT (port, 3); fip->port_4.p.addr = GET_PORT (port, 4); fip->port_5.p.addr = GET_PORT (port, 5); fip->port_6.p.addr = GET_PORT (port, 6); fip->port_0.p.ctl = fas_make_ctl_val (fip, unit, 0); fip->port_1.p.ctl = fas_make_ctl_val (fip, unit, 1); fip->port_2.p.ctl = fas_make_ctl_val (fip, unit, 2); fip->port_3.p.ctl = fas_make_ctl_val (fip, unit, 3); fip->port_4.p.ctl = fas_make_ctl_val (fip, unit, 4); fip->port_5.p.ctl = fas_make_ctl_val (fip, unit, 5); fip->port_6.p.ctl = fas_make_ctl_val (fip, unit, 6); fip->vec = fas_vec [unit]; fip->modem.l = fas_modem [unit]; fip->flow.l = fas_flow [unit]; /* mask off invalid bits */ fip->modem.m.di &= MC_ANY_CONTROL; fip->modem.m.eo &= MC_ANY_CONTROL; fip->modem.m.ei &= MC_ANY_CONTROL; fip->modem.m.ca &= MS_ANY_PRESENT; fip->flow.m.ic &= MC_ANY_CONTROL; fip->flow.m.oc &= MS_ANY_PRESENT; fip->flow.m.oe &= MS_ANY_PRESENT; fip->flow.m.hc &= MC_ANY_CONTROL; fip->recv_ring_put_ptr = fip->recv_buffer; fip->recv_ring_take_ptr = fip->recv_buffer; fip->xmit_ring_put_ptr = fip->xmit_buffer; fip->xmit_ring_take_ptr = fip->xmit_buffer; fip->xmit_fifo_size = 1; fip->ier = IE_NONE; /* disable all ints */ fas_first_outb (fip, INT_ENABLE_PORT, fip->ier); /* is there a serial chip ? */ if (fas_inb (fip, INT_ENABLE_PORT) != fip->ier)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -