diff --git a/sys/amd64/amd64/fpu.c b/sys/amd64/amd64/fpu.c index c9d92f4bbfa8..30914d017355 100644 --- a/sys/amd64/amd64/fpu.c +++ b/sys/amd64/amd64/fpu.c @@ -1,933 +1,928 @@ /*- * Copyright (c) 1990 William Jolitz. * Copyright (c) 1991 The Regents of the University of California. * All rights reserved. * * 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 * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, 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 the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)npx.c 7.2 (Berkeley) 5/12/91 * $FreeBSD$ */ -#include "npx.h" -#if NNPX > 0 - #include "opt_debug_npx.h" #include "opt_math_emulate.h" #include #include #include #include #include #include #include #include #include #include #ifdef NPX_DEBUG #include #endif #include #ifndef SMP #include #endif #include #include #include #include #include #include #ifndef SMP #include #endif #include #include #include #ifndef SMP #include #include #include #endif /* * 387 and 287 Numeric Coprocessor Extension (NPX) Driver. */ /* Configuration flags. */ #define NPX_DISABLE_I586_OPTIMIZED_BCOPY (1 << 0) #define NPX_DISABLE_I586_OPTIMIZED_BZERO (1 << 1) #define NPX_DISABLE_I586_OPTIMIZED_COPYIO (1 << 2) #define NPX_PREFER_EMULATOR (1 << 3) #ifdef __GNUC__ #define fldcw(addr) __asm("fldcw %0" : : "m" (*(addr))) #define fnclex() __asm("fnclex") #define fninit() __asm("fninit") #define fnop() __asm("fnop") #define fnsave(addr) __asm __volatile("fnsave %0" : "=m" (*(addr))) #define fnstcw(addr) __asm __volatile("fnstcw %0" : "=m" (*(addr))) #define fnstsw(addr) __asm __volatile("fnstsw %0" : "=m" (*(addr))) #define fp_divide_by_0() __asm("fldz; fld1; fdiv %st,%st(1); fnop") #define frstor(addr) __asm("frstor %0" : : "m" (*(addr))) #define start_emulating() __asm("smsw %%ax; orb %0,%%al; lmsw %%ax" \ : : "n" (CR0_TS) : "ax") #define stop_emulating() __asm("clts") #else /* not __GNUC__ */ void fldcw __P((caddr_t addr)); void fnclex __P((void)); void fninit __P((void)); void fnop __P((void)); void fnsave __P((caddr_t addr)); void fnstcw __P((caddr_t addr)); void fnstsw __P((caddr_t addr)); void fp_divide_by_0 __P((void)); void frstor __P((caddr_t addr)); void start_emulating __P((void)); void stop_emulating __P((void)); #endif /* __GNUC__ */ typedef u_char bool_t; static int npx_attach __P((device_t dev)); void npx_intr __P((void *)); static void npx_identify __P((driver_t *driver, device_t parent)); static int npx_probe __P((device_t dev)); static int npx_probe1 __P((device_t dev)); #ifdef I586_CPU static long timezero __P((const char *funcname, void (*func)(void *buf, size_t len))); #endif /* I586_CPU */ int hw_float; /* XXX currently just alias for npx_exists */ SYSCTL_INT(_hw,HW_FLOATINGPT, floatingpoint, CTLFLAG_RD, &hw_float, 0, "Floatingpoint instructions executed in hardware"); #ifndef SMP static u_int npx0_imask = SWI_CLOCK_MASK; static struct gate_descriptor npx_idt_probeintr; static int npx_intrno; static volatile u_int npx_intrs_while_probing; static volatile u_int npx_traps_while_probing; #endif static bool_t npx_ex16; static bool_t npx_exists; static bool_t npx_irq13; static int npx_irq; /* irq number */ #ifndef SMP /* * Special interrupt handlers. Someday intr0-intr15 will be used to count * interrupts. We'll still need a special exception 16 handler. The busy * latch stuff in probeintr() can be moved to npxprobe(). */ inthand_t probeintr; __asm(" \n\ .text \n\ .p2align 2,0x90 \n\ .type " __XSTRING(CNAME(probeintr)) ",@function \n\ " __XSTRING(CNAME(probeintr)) ": \n\ ss \n\ incl " __XSTRING(CNAME(npx_intrs_while_probing)) " \n\ pushl %eax \n\ movb $0x20,%al # EOI (asm in strings loses cpp features) \n\ outb %al,$0xa0 # IO_ICU2 \n\ outb %al,$0x20 # IO_ICU1 \n\ movb $0,%al \n\ outb %al,$0xf0 # clear BUSY# latch \n\ popl %eax \n\ iret \n\ "); inthand_t probetrap; __asm(" \n\ .text \n\ .p2align 2,0x90 \n\ .type " __XSTRING(CNAME(probetrap)) ",@function \n\ " __XSTRING(CNAME(probetrap)) ": \n\ ss \n\ incl " __XSTRING(CNAME(npx_traps_while_probing)) " \n\ fnclex \n\ iret \n\ "); #endif /* SMP */ /* * Identify routine. Create a connection point on our parent for probing. */ static void npx_identify(driver, parent) driver_t *driver; device_t parent; { device_t child; child = BUS_ADD_CHILD(parent, 0, "npx", 0); if (child == NULL) panic("npx_identify"); } /* * Probe routine. Initialize cr0 to give correct behaviour for [f]wait * whether the device exists or not (XXX should be elsewhere). Set flags * to tell npxattach() what to do. Modify device struct if npx doesn't * need to use interrupts. Return 1 if device exists. */ static int npx_probe(dev) device_t dev; { #ifdef SMP if (resource_int_value("npx", 0, "irq", &npx_irq) != 0) npx_irq = 13; return npx_probe1(dev); #else /* SMP */ int result; u_long save_eflags; u_char save_icu1_mask; u_char save_icu2_mask; struct gate_descriptor save_idt_npxintr; struct gate_descriptor save_idt_npxtrap; /* * This routine is now just a wrapper for npxprobe1(), to install * special npx interrupt and trap handlers, to enable npx interrupts * and to disable other interrupts. Someday isa_configure() will * install suitable handlers and run with interrupts enabled so we * won't need to do so much here. */ if (resource_int_value("npx", 0, "irq", &npx_irq) != 0) npx_irq = 13; npx_intrno = NRSVIDT + npx_irq; save_eflags = read_eflags(); disable_intr(); save_icu1_mask = inb(IO_ICU1 + 1); save_icu2_mask = inb(IO_ICU2 + 1); save_idt_npxintr = idt[npx_intrno]; save_idt_npxtrap = idt[16]; outb(IO_ICU1 + 1, ~IRQ_SLAVE); outb(IO_ICU2 + 1, ~(1 << (npx_irq - 8))); setidt(16, probetrap, SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(npx_intrno, probeintr, SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); npx_idt_probeintr = idt[npx_intrno]; enable_intr(); result = npx_probe1(dev); disable_intr(); outb(IO_ICU1 + 1, save_icu1_mask); outb(IO_ICU2 + 1, save_icu2_mask); idt[npx_intrno] = save_idt_npxintr; idt[16] = save_idt_npxtrap; write_eflags(save_eflags); return (result); #endif /* SMP */ } static int npx_probe1(dev) device_t dev; { #ifndef SMP u_short control; u_short status; #endif /* * Partially reset the coprocessor, if any. Some BIOS's don't reset * it after a warm boot. */ outb(0xf1, 0); /* full reset on some systems, NOP on others */ outb(0xf0, 0); /* clear BUSY# latch */ /* * Prepare to trap all ESC (i.e., NPX) instructions and all WAIT * instructions. We must set the CR0_MP bit and use the CR0_TS * bit to control the trap, because setting the CR0_EM bit does * not cause WAIT instructions to trap. It's important to trap * WAIT instructions - otherwise the "wait" variants of no-wait * control instructions would degenerate to the "no-wait" variants * after FP context switches but work correctly otherwise. It's * particularly important to trap WAITs when there is no NPX - * otherwise the "wait" variants would always degenerate. * * Try setting CR0_NE to get correct error reporting on 486DX's. * Setting it should fail or do nothing on lesser processors. */ load_cr0(rcr0() | CR0_MP | CR0_NE); /* * But don't trap while we're probing. */ stop_emulating(); /* * Finish resetting the coprocessor, if any. If there is an error * pending, then we may get a bogus IRQ13, but probeintr() will handle * it OK. Bogus halts have never been observed, but we enabled * IRQ13 and cleared the BUSY# latch early to handle them anyway. */ fninit(); #ifdef SMP /* * Exception 16 MUST work for SMP. */ npx_irq13 = 0; npx_ex16 = hw_float = npx_exists = 1; device_set_desc(dev, "math processor"); return (0); #else /* !SMP */ device_set_desc(dev, "math processor"); /* * Don't use fwait here because it might hang. * Don't use fnop here because it usually hangs if there is no FPU. */ DELAY(1000); /* wait for any IRQ13 */ #ifdef DIAGNOSTIC if (npx_intrs_while_probing != 0) printf("fninit caused %u bogus npx interrupt(s)\n", npx_intrs_while_probing); if (npx_traps_while_probing != 0) printf("fninit caused %u bogus npx trap(s)\n", npx_traps_while_probing); #endif /* * Check for a status of mostly zero. */ status = 0x5a5a; fnstsw(&status); if ((status & 0xb8ff) == 0) { /* * Good, now check for a proper control word. */ control = 0x5a5a; fnstcw(&control); if ((control & 0x1f3f) == 0x033f) { hw_float = npx_exists = 1; /* * We have an npx, now divide by 0 to see if exception * 16 works. */ control &= ~(1 << 2); /* enable divide by 0 trap */ fldcw(&control); npx_traps_while_probing = npx_intrs_while_probing = 0; fp_divide_by_0(); if (npx_traps_while_probing != 0) { /* * Good, exception 16 works. */ npx_ex16 = 1; return (0); } if (npx_intrs_while_probing != 0) { int rid; struct resource *r; void *intr; /* * Bad, we are stuck with IRQ13. */ npx_irq13 = 1; /* * npxattach would be too late to set npx0_imask */ npx0_imask |= (1 << npx_irq); /* * We allocate these resources permanently, * so there is no need to keep track of them. */ rid = 0; r = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, IO_NPX, IO_NPX, IO_NPXSIZE, RF_ACTIVE); if (r == 0) panic("npx: can't get ports"); rid = 0; r = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, npx_irq, npx_irq, 1, RF_ACTIVE); if (r == 0) panic("npx: can't get IRQ"); BUS_SETUP_INTR(device_get_parent(dev), dev, r, INTR_TYPE_MISC, npx_intr, 0, &intr); if (intr == 0) panic("npx: can't create intr"); return (0); } /* * Worse, even IRQ13 is broken. Use emulator. */ } } /* * Probe failed, but we want to get to npxattach to initialize the * emulator and say that it has been installed. XXX handle devices * that aren't really devices better. */ return (0); #endif /* SMP */ } /* * Attach routine - announce which it is, and wire into system */ int npx_attach(dev) device_t dev; { int flags; if (resource_int_value("npx", 0, "flags", &flags) != 0) flags = 0; if (flags) device_printf(dev, "flags 0x%x ", flags); if (npx_irq13) { device_printf(dev, "using IRQ 13 interface\n"); } else { #if defined(MATH_EMULATE) || defined(GPL_MATH_EMULATE) if (npx_ex16) { if (!(flags & NPX_PREFER_EMULATOR)) device_printf(dev, "INT 16 interface\n"); else { device_printf(dev, "FPU exists, but flags request " "emulator\n"); hw_float = npx_exists = 0; } } else if (npx_exists) { device_printf(dev, "error reporting broken; using 387 emulator\n"); hw_float = npx_exists = 0; } else device_printf(dev, "387 emulator\n"); #else if (npx_ex16) { device_printf(dev, "INT 16 interface\n"); if (flags & NPX_PREFER_EMULATOR) { device_printf(dev, "emulator requested, but none compiled " "into kernel, using FPU\n"); } } else device_printf(dev, "no 387 emulator in kernel and no FPU!\n"); #endif } npxinit(__INITIAL_NPXCW__); #ifdef I586_CPU if (cpu_class == CPUCLASS_586 && npx_ex16 && npx_exists && timezero("i586_bzero()", i586_bzero) < timezero("bzero()", bzero) * 4 / 5) { if (!(flags & NPX_DISABLE_I586_OPTIMIZED_BCOPY)) { bcopy_vector = i586_bcopy; ovbcopy_vector = i586_bcopy; } if (!(flags & NPX_DISABLE_I586_OPTIMIZED_BZERO)) bzero = i586_bzero; if (!(flags & NPX_DISABLE_I586_OPTIMIZED_COPYIO)) { copyin_vector = i586_copyin; copyout_vector = i586_copyout; } } #endif return (0); /* XXX unused */ } /* * Initialize floating point unit. */ void npxinit(control) u_short control; { struct save87 dummy; if (!npx_exists) return; /* * fninit has the same h/w bugs as fnsave. Use the detoxified * fnsave to throw away any junk in the fpu. npxsave() initializes * the fpu and sets npxproc = NULL as important side effects. */ npxsave(&dummy); stop_emulating(); fldcw(&control); if (curpcb != NULL) fnsave(&curpcb->pcb_savefpu); start_emulating(); } /* * Free coprocessor (if we have it). */ void npxexit(p) struct proc *p; { if (p == npxproc) npxsave(&curpcb->pcb_savefpu); #ifdef NPX_DEBUG if (npx_exists) { u_int masked_exceptions; masked_exceptions = curpcb->pcb_savefpu.sv_env.en_cw & curpcb->pcb_savefpu.sv_env.en_sw & 0x7f; /* * Log exceptions that would have trapped with the old * control word (overflow, divide by 0, and invalid operand). */ if (masked_exceptions & 0x0d) log(LOG_ERR, "pid %d (%s) exited with masked floating point exceptions 0x%02x\n", p->p_pid, p->p_comm, masked_exceptions); } #endif } /* * The following mechanism is used to ensure that the FPE_... value * that is passed as a trapcode to the signal handler of the user * process does not have more than one bit set. * * Multiple bits may be set if the user process modifies the control * word while a status word bit is already set. While this is a sign * of bad coding, we have no choise than to narrow them down to one * bit, since we must not send a trapcode that is not exactly one of * the FPE_ macros. * * The mechanism has a static table with 127 entries. Each combination * of the 7 FPU status word exception bits directly translates to a * position in this table, where a single FPE_... value is stored. * This FPE_... value stored there is considered the "most important" * of the exception bits and will be sent as the signal code. The * precedence of the bits is based upon Intel Document "Numerical * Applications", Chapter "Special Computational Situations". * * The macro to choose one of these values does these steps: 1) Throw * away status word bits that cannot be masked. 2) Throw away the bits * currently masked in the control word, assuming the user isn't * interested in them anymore. 3) Reinsert status word bit 7 (stack * fault) if it is set, which cannot be masked but must be presered. * 4) Use the remaining bits to point into the trapcode table. * * The 6 maskable bits in order of their preference, as stated in the * above referenced Intel manual: * 1 Invalid operation (FP_X_INV) * 1a Stack underflow * 1b Stack overflow * 1c Operand of unsupported format * 1d SNaN operand. * 2 QNaN operand (not an exception, irrelavant here) * 3 Any other invalid-operation not mentioned above or zero divide * (FP_X_INV, FP_X_DZ) * 4 Denormal operand (FP_X_DNML) * 5 Numeric over/underflow (FP_X_OFL, FP_X_UFL) * 6 Inexact result (FP_X_IMP) */ static char fpetable[128] = { 0, FPE_FLTINV, /* 1 - INV */ FPE_FLTUND, /* 2 - DNML */ FPE_FLTINV, /* 3 - INV | DNML */ FPE_FLTDIV, /* 4 - DZ */ FPE_FLTINV, /* 5 - INV | DZ */ FPE_FLTDIV, /* 6 - DNML | DZ */ FPE_FLTINV, /* 7 - INV | DNML | DZ */ FPE_FLTOVF, /* 8 - OFL */ FPE_FLTINV, /* 9 - INV | OFL */ FPE_FLTUND, /* A - DNML | OFL */ FPE_FLTINV, /* B - INV | DNML | OFL */ FPE_FLTDIV, /* C - DZ | OFL */ FPE_FLTINV, /* D - INV | DZ | OFL */ FPE_FLTDIV, /* E - DNML | DZ | OFL */ FPE_FLTINV, /* F - INV | DNML | DZ | OFL */ FPE_FLTUND, /* 10 - UFL */ FPE_FLTINV, /* 11 - INV | UFL */ FPE_FLTUND, /* 12 - DNML | UFL */ FPE_FLTINV, /* 13 - INV | DNML | UFL */ FPE_FLTDIV, /* 14 - DZ | UFL */ FPE_FLTINV, /* 15 - INV | DZ | UFL */ FPE_FLTDIV, /* 16 - DNML | DZ | UFL */ FPE_FLTINV, /* 17 - INV | DNML | DZ | UFL */ FPE_FLTOVF, /* 18 - OFL | UFL */ FPE_FLTINV, /* 19 - INV | OFL | UFL */ FPE_FLTUND, /* 1A - DNML | OFL | UFL */ FPE_FLTINV, /* 1B - INV | DNML | OFL | UFL */ FPE_FLTDIV, /* 1C - DZ | OFL | UFL */ FPE_FLTINV, /* 1D - INV | DZ | OFL | UFL */ FPE_FLTDIV, /* 1E - DNML | DZ | OFL | UFL */ FPE_FLTINV, /* 1F - INV | DNML | DZ | OFL | UFL */ FPE_FLTRES, /* 20 - IMP */ FPE_FLTINV, /* 21 - INV | IMP */ FPE_FLTUND, /* 22 - DNML | IMP */ FPE_FLTINV, /* 23 - INV | DNML | IMP */ FPE_FLTDIV, /* 24 - DZ | IMP */ FPE_FLTINV, /* 25 - INV | DZ | IMP */ FPE_FLTDIV, /* 26 - DNML | DZ | IMP */ FPE_FLTINV, /* 27 - INV | DNML | DZ | IMP */ FPE_FLTOVF, /* 28 - OFL | IMP */ FPE_FLTINV, /* 29 - INV | OFL | IMP */ FPE_FLTUND, /* 2A - DNML | OFL | IMP */ FPE_FLTINV, /* 2B - INV | DNML | OFL | IMP */ FPE_FLTDIV, /* 2C - DZ | OFL | IMP */ FPE_FLTINV, /* 2D - INV | DZ | OFL | IMP */ FPE_FLTDIV, /* 2E - DNML | DZ | OFL | IMP */ FPE_FLTINV, /* 2F - INV | DNML | DZ | OFL | IMP */ FPE_FLTUND, /* 30 - UFL | IMP */ FPE_FLTINV, /* 31 - INV | UFL | IMP */ FPE_FLTUND, /* 32 - DNML | UFL | IMP */ FPE_FLTINV, /* 33 - INV | DNML | UFL | IMP */ FPE_FLTDIV, /* 34 - DZ | UFL | IMP */ FPE_FLTINV, /* 35 - INV | DZ | UFL | IMP */ FPE_FLTDIV, /* 36 - DNML | DZ | UFL | IMP */ FPE_FLTINV, /* 37 - INV | DNML | DZ | UFL | IMP */ FPE_FLTOVF, /* 38 - OFL | UFL | IMP */ FPE_FLTINV, /* 39 - INV | OFL | UFL | IMP */ FPE_FLTUND, /* 3A - DNML | OFL | UFL | IMP */ FPE_FLTINV, /* 3B - INV | DNML | OFL | UFL | IMP */ FPE_FLTDIV, /* 3C - DZ | OFL | UFL | IMP */ FPE_FLTINV, /* 3D - INV | DZ | OFL | UFL | IMP */ FPE_FLTDIV, /* 3E - DNML | DZ | OFL | UFL | IMP */ FPE_FLTINV, /* 3F - INV | DNML | DZ | OFL | UFL | IMP */ FPE_FLTSUB, /* 40 - STK */ FPE_FLTSUB, /* 41 - INV | STK */ FPE_FLTUND, /* 42 - DNML | STK */ FPE_FLTSUB, /* 43 - INV | DNML | STK */ FPE_FLTDIV, /* 44 - DZ | STK */ FPE_FLTSUB, /* 45 - INV | DZ | STK */ FPE_FLTDIV, /* 46 - DNML | DZ | STK */ FPE_FLTSUB, /* 47 - INV | DNML | DZ | STK */ FPE_FLTOVF, /* 48 - OFL | STK */ FPE_FLTSUB, /* 49 - INV | OFL | STK */ FPE_FLTUND, /* 4A - DNML | OFL | STK */ FPE_FLTSUB, /* 4B - INV | DNML | OFL | STK */ FPE_FLTDIV, /* 4C - DZ | OFL | STK */ FPE_FLTSUB, /* 4D - INV | DZ | OFL | STK */ FPE_FLTDIV, /* 4E - DNML | DZ | OFL | STK */ FPE_FLTSUB, /* 4F - INV | DNML | DZ | OFL | STK */ FPE_FLTUND, /* 50 - UFL | STK */ FPE_FLTSUB, /* 51 - INV | UFL | STK */ FPE_FLTUND, /* 52 - DNML | UFL | STK */ FPE_FLTSUB, /* 53 - INV | DNML | UFL | STK */ FPE_FLTDIV, /* 54 - DZ | UFL | STK */ FPE_FLTSUB, /* 55 - INV | DZ | UFL | STK */ FPE_FLTDIV, /* 56 - DNML | DZ | UFL | STK */ FPE_FLTSUB, /* 57 - INV | DNML | DZ | UFL | STK */ FPE_FLTOVF, /* 58 - OFL | UFL | STK */ FPE_FLTSUB, /* 59 - INV | OFL | UFL | STK */ FPE_FLTUND, /* 5A - DNML | OFL | UFL | STK */ FPE_FLTSUB, /* 5B - INV | DNML | OFL | UFL | STK */ FPE_FLTDIV, /* 5C - DZ | OFL | UFL | STK */ FPE_FLTSUB, /* 5D - INV | DZ | OFL | UFL | STK */ FPE_FLTDIV, /* 5E - DNML | DZ | OFL | UFL | STK */ FPE_FLTSUB, /* 5F - INV | DNML | DZ | OFL | UFL | STK */ FPE_FLTRES, /* 60 - IMP | STK */ FPE_FLTSUB, /* 61 - INV | IMP | STK */ FPE_FLTUND, /* 62 - DNML | IMP | STK */ FPE_FLTSUB, /* 63 - INV | DNML | IMP | STK */ FPE_FLTDIV, /* 64 - DZ | IMP | STK */ FPE_FLTSUB, /* 65 - INV | DZ | IMP | STK */ FPE_FLTDIV, /* 66 - DNML | DZ | IMP | STK */ FPE_FLTSUB, /* 67 - INV | DNML | DZ | IMP | STK */ FPE_FLTOVF, /* 68 - OFL | IMP | STK */ FPE_FLTSUB, /* 69 - INV | OFL | IMP | STK */ FPE_FLTUND, /* 6A - DNML | OFL | IMP | STK */ FPE_FLTSUB, /* 6B - INV | DNML | OFL | IMP | STK */ FPE_FLTDIV, /* 6C - DZ | OFL | IMP | STK */ FPE_FLTSUB, /* 6D - INV | DZ | OFL | IMP | STK */ FPE_FLTDIV, /* 6E - DNML | DZ | OFL | IMP | STK */ FPE_FLTSUB, /* 6F - INV | DNML | DZ | OFL | IMP | STK */ FPE_FLTUND, /* 70 - UFL | IMP | STK */ FPE_FLTSUB, /* 71 - INV | UFL | IMP | STK */ FPE_FLTUND, /* 72 - DNML | UFL | IMP | STK */ FPE_FLTSUB, /* 73 - INV | DNML | UFL | IMP | STK */ FPE_FLTDIV, /* 74 - DZ | UFL | IMP | STK */ FPE_FLTSUB, /* 75 - INV | DZ | UFL | IMP | STK */ FPE_FLTDIV, /* 76 - DNML | DZ | UFL | IMP | STK */ FPE_FLTSUB, /* 77 - INV | DNML | DZ | UFL | IMP | STK */ FPE_FLTOVF, /* 78 - OFL | UFL | IMP | STK */ FPE_FLTSUB, /* 79 - INV | OFL | UFL | IMP | STK */ FPE_FLTUND, /* 7A - DNML | OFL | UFL | IMP | STK */ FPE_FLTSUB, /* 7B - INV | DNML | OFL | UFL | IMP | STK */ FPE_FLTDIV, /* 7C - DZ | OFL | UFL | IMP | STK */ FPE_FLTSUB, /* 7D - INV | DZ | OFL | UFL | IMP | STK */ FPE_FLTDIV, /* 7E - DNML | DZ | OFL | UFL | IMP | STK */ FPE_FLTSUB, /* 7F - INV | DNML | DZ | OFL | UFL | IMP | STK */ }; /* * Preserve the FP status word, clear FP exceptions, then generate a SIGFPE. * * Clearing exceptions is necessary mainly to avoid IRQ13 bugs. We now * depend on longjmp() restoring a usable state. Restoring the state * or examining it might fail if we didn't clear exceptions. * * The error code chosen will be one of the FPE_... macros. It will be * sent as the second argument to old BSD-style signal handlers and as * "siginfo_t->si_code" (second argument) to SA_SIGINFO signal handlers. * * XXX the FP state is not preserved across signal handlers. So signal * handlers cannot afford to do FP unless they preserve the state or * longjmp() out. Both preserving the state and longjmp()ing may be * destroyed by IRQ13 bugs. Clearing FP exceptions is not an acceptable * solution for signals other than SIGFPE. */ void npx_intr(dummy) void *dummy; { int code; u_short control; struct intrframe *frame; if (npxproc == NULL || !npx_exists) { printf("npxintr: npxproc = %p, curproc = %p, npx_exists = %d\n", npxproc, curproc, npx_exists); panic("npxintr from nowhere"); } if (npxproc != curproc) { printf("npxintr: npxproc = %p, curproc = %p, npx_exists = %d\n", npxproc, curproc, npx_exists); panic("npxintr from non-current process"); } outb(0xf0, 0); fnstsw(&curpcb->pcb_savefpu.sv_ex_sw); fnstcw(&control); fnclex(); /* * Pass exception to process. */ frame = (struct intrframe *)&dummy; /* XXX */ if ((ISPL(frame->if_cs) == SEL_UPL) || (frame->if_eflags & PSL_VM)) { /* * Interrupt is essentially a trap, so we can afford to call * the SIGFPE handler (if any) as soon as the interrupt * returns. * * XXX little or nothing is gained from this, and plenty is * lost - the interrupt frame has to contain the trap frame * (this is otherwise only necessary for the rescheduling trap * in doreti, and the frame for that could easily be set up * just before it is used). */ curproc->p_md.md_regs = INTR_TO_TRAPFRAME(frame); /* * Encode the appropriate code for detailed information on * this exception. */ code = fpetable[(curpcb->pcb_savefpu.sv_ex_sw & ~control & 0x3f) | (curpcb->pcb_savefpu.sv_ex_sw & 0x40)]; trapsignal(curproc, SIGFPE, code); } else { /* * Nested interrupt. These losers occur when: * o an IRQ13 is bogusly generated at a bogus time, e.g.: * o immediately after an fnsave or frstor of an * error state. * o a couple of 386 instructions after * "fstpl _memvar" causes a stack overflow. * These are especially nasty when combined with a * trace trap. * o an IRQ13 occurs at the same time as another higher- * priority interrupt. * * Treat them like a true async interrupt. */ psignal(curproc, SIGFPE); } } /* * Implement device not available (DNA) exception * * It would be better to switch FP context here (if curproc != npxproc) * and not necessarily for every context switch, but it is too hard to * access foreign pcb's. */ int npxdna() { if (!npx_exists) return (0); if (npxproc != NULL) { printf("npxdna: npxproc = %p, curproc = %p\n", npxproc, curproc); panic("npxdna"); } stop_emulating(); /* * Record new context early in case frstor causes an IRQ13. */ npxproc = curproc; curpcb->pcb_savefpu.sv_ex_sw = 0; /* * The following frstor may cause an IRQ13 when the state being * restored has a pending error. The error will appear to have been * triggered by the current (npx) user instruction even when that * instruction is a no-wait instruction that should not trigger an * error (e.g., fnclex). On at least one 486 system all of the * no-wait instructions are broken the same as frstor, so our * treatment does not amplify the breakage. On at least one * 386/Cyrix 387 system, fnclex works correctly while frstor and * fnsave are broken, so our treatment breaks fnclex if it is the * first FPU instruction after a context switch. */ frstor(&curpcb->pcb_savefpu); return (1); } /* * Wrapper for fnsave instruction to handle h/w bugs. If there is an error * pending, then fnsave generates a bogus IRQ13 on some systems. Force * any IRQ13 to be handled immediately, and then ignore it. This routine is * often called at splhigh so it must not use many system services. In * particular, it's much easier to install a special handler than to * guarantee that it's safe to use npxintr() and its supporting code. */ void npxsave(addr) struct save87 *addr; { #ifdef SMP stop_emulating(); fnsave(addr); /* fnop(); */ start_emulating(); npxproc = NULL; #else /* SMP */ u_char icu1_mask; u_char icu2_mask; u_char old_icu1_mask; u_char old_icu2_mask; struct gate_descriptor save_idt_npxintr; disable_intr(); old_icu1_mask = inb(IO_ICU1 + 1); old_icu2_mask = inb(IO_ICU2 + 1); save_idt_npxintr = idt[npx_intrno]; outb(IO_ICU1 + 1, old_icu1_mask & ~(IRQ_SLAVE | npx0_imask)); outb(IO_ICU2 + 1, old_icu2_mask & ~(npx0_imask >> 8)); idt[npx_intrno] = npx_idt_probeintr; enable_intr(); stop_emulating(); fnsave(addr); fnop(); start_emulating(); npxproc = NULL; disable_intr(); icu1_mask = inb(IO_ICU1 + 1); /* masks may have changed */ icu2_mask = inb(IO_ICU2 + 1); outb(IO_ICU1 + 1, (icu1_mask & ~npx0_imask) | (old_icu1_mask & npx0_imask)); outb(IO_ICU2 + 1, (icu2_mask & ~(npx0_imask >> 8)) | (old_icu2_mask & (npx0_imask >> 8))); idt[npx_intrno] = save_idt_npxintr; enable_intr(); /* back to usual state */ #endif /* SMP */ } #ifdef I586_CPU static long timezero(funcname, func) const char *funcname; void (*func) __P((void *buf, size_t len)); { void *buf; #define BUFSIZE 1000000 long usec; struct timeval finish, start; buf = malloc(BUFSIZE, M_TEMP, M_NOWAIT); if (buf == NULL) return (BUFSIZE); microtime(&start); (*func)(buf, BUFSIZE); microtime(&finish); usec = 1000000 * (finish.tv_sec - start.tv_sec) + finish.tv_usec - start.tv_usec; if (usec <= 0) usec = 1; if (bootverbose) printf("%s bandwidth = %ld bytes/sec\n", funcname, (long)(BUFSIZE * (int64_t)1000000 / usec)); free(buf, M_TEMP); return (usec); } #endif /* I586_CPU */ static device_method_t npx_methods[] = { /* Device interface */ DEVMETHOD(device_identify, npx_identify), DEVMETHOD(device_probe, npx_probe), DEVMETHOD(device_attach, npx_attach), DEVMETHOD(device_detach, bus_generic_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), { 0, 0 } }; static driver_t npx_driver = { "npx", npx_methods, 1, /* no softc */ }; static devclass_t npx_devclass; /* * We prefer to attach to the root nexus so that the usual case (exception 16) * doesn't describe the processor as being `on isa'. */ DRIVER_MODULE(npx, nexus, npx_driver, npx_devclass, 0, 0); - -#endif /* NNPX > 0 */ diff --git a/sys/amd64/isa/npx.c b/sys/amd64/isa/npx.c index c9d92f4bbfa8..30914d017355 100644 --- a/sys/amd64/isa/npx.c +++ b/sys/amd64/isa/npx.c @@ -1,933 +1,928 @@ /*- * Copyright (c) 1990 William Jolitz. * Copyright (c) 1991 The Regents of the University of California. * All rights reserved. * * 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 * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, 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 the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)npx.c 7.2 (Berkeley) 5/12/91 * $FreeBSD$ */ -#include "npx.h" -#if NNPX > 0 - #include "opt_debug_npx.h" #include "opt_math_emulate.h" #include #include #include #include #include #include #include #include #include #include #ifdef NPX_DEBUG #include #endif #include #ifndef SMP #include #endif #include #include #include #include #include #include #ifndef SMP #include #endif #include #include #include #ifndef SMP #include #include #include #endif /* * 387 and 287 Numeric Coprocessor Extension (NPX) Driver. */ /* Configuration flags. */ #define NPX_DISABLE_I586_OPTIMIZED_BCOPY (1 << 0) #define NPX_DISABLE_I586_OPTIMIZED_BZERO (1 << 1) #define NPX_DISABLE_I586_OPTIMIZED_COPYIO (1 << 2) #define NPX_PREFER_EMULATOR (1 << 3) #ifdef __GNUC__ #define fldcw(addr) __asm("fldcw %0" : : "m" (*(addr))) #define fnclex() __asm("fnclex") #define fninit() __asm("fninit") #define fnop() __asm("fnop") #define fnsave(addr) __asm __volatile("fnsave %0" : "=m" (*(addr))) #define fnstcw(addr) __asm __volatile("fnstcw %0" : "=m" (*(addr))) #define fnstsw(addr) __asm __volatile("fnstsw %0" : "=m" (*(addr))) #define fp_divide_by_0() __asm("fldz; fld1; fdiv %st,%st(1); fnop") #define frstor(addr) __asm("frstor %0" : : "m" (*(addr))) #define start_emulating() __asm("smsw %%ax; orb %0,%%al; lmsw %%ax" \ : : "n" (CR0_TS) : "ax") #define stop_emulating() __asm("clts") #else /* not __GNUC__ */ void fldcw __P((caddr_t addr)); void fnclex __P((void)); void fninit __P((void)); void fnop __P((void)); void fnsave __P((caddr_t addr)); void fnstcw __P((caddr_t addr)); void fnstsw __P((caddr_t addr)); void fp_divide_by_0 __P((void)); void frstor __P((caddr_t addr)); void start_emulating __P((void)); void stop_emulating __P((void)); #endif /* __GNUC__ */ typedef u_char bool_t; static int npx_attach __P((device_t dev)); void npx_intr __P((void *)); static void npx_identify __P((driver_t *driver, device_t parent)); static int npx_probe __P((device_t dev)); static int npx_probe1 __P((device_t dev)); #ifdef I586_CPU static long timezero __P((const char *funcname, void (*func)(void *buf, size_t len))); #endif /* I586_CPU */ int hw_float; /* XXX currently just alias for npx_exists */ SYSCTL_INT(_hw,HW_FLOATINGPT, floatingpoint, CTLFLAG_RD, &hw_float, 0, "Floatingpoint instructions executed in hardware"); #ifndef SMP static u_int npx0_imask = SWI_CLOCK_MASK; static struct gate_descriptor npx_idt_probeintr; static int npx_intrno; static volatile u_int npx_intrs_while_probing; static volatile u_int npx_traps_while_probing; #endif static bool_t npx_ex16; static bool_t npx_exists; static bool_t npx_irq13; static int npx_irq; /* irq number */ #ifndef SMP /* * Special interrupt handlers. Someday intr0-intr15 will be used to count * interrupts. We'll still need a special exception 16 handler. The busy * latch stuff in probeintr() can be moved to npxprobe(). */ inthand_t probeintr; __asm(" \n\ .text \n\ .p2align 2,0x90 \n\ .type " __XSTRING(CNAME(probeintr)) ",@function \n\ " __XSTRING(CNAME(probeintr)) ": \n\ ss \n\ incl " __XSTRING(CNAME(npx_intrs_while_probing)) " \n\ pushl %eax \n\ movb $0x20,%al # EOI (asm in strings loses cpp features) \n\ outb %al,$0xa0 # IO_ICU2 \n\ outb %al,$0x20 # IO_ICU1 \n\ movb $0,%al \n\ outb %al,$0xf0 # clear BUSY# latch \n\ popl %eax \n\ iret \n\ "); inthand_t probetrap; __asm(" \n\ .text \n\ .p2align 2,0x90 \n\ .type " __XSTRING(CNAME(probetrap)) ",@function \n\ " __XSTRING(CNAME(probetrap)) ": \n\ ss \n\ incl " __XSTRING(CNAME(npx_traps_while_probing)) " \n\ fnclex \n\ iret \n\ "); #endif /* SMP */ /* * Identify routine. Create a connection point on our parent for probing. */ static void npx_identify(driver, parent) driver_t *driver; device_t parent; { device_t child; child = BUS_ADD_CHILD(parent, 0, "npx", 0); if (child == NULL) panic("npx_identify"); } /* * Probe routine. Initialize cr0 to give correct behaviour for [f]wait * whether the device exists or not (XXX should be elsewhere). Set flags * to tell npxattach() what to do. Modify device struct if npx doesn't * need to use interrupts. Return 1 if device exists. */ static int npx_probe(dev) device_t dev; { #ifdef SMP if (resource_int_value("npx", 0, "irq", &npx_irq) != 0) npx_irq = 13; return npx_probe1(dev); #else /* SMP */ int result; u_long save_eflags; u_char save_icu1_mask; u_char save_icu2_mask; struct gate_descriptor save_idt_npxintr; struct gate_descriptor save_idt_npxtrap; /* * This routine is now just a wrapper for npxprobe1(), to install * special npx interrupt and trap handlers, to enable npx interrupts * and to disable other interrupts. Someday isa_configure() will * install suitable handlers and run with interrupts enabled so we * won't need to do so much here. */ if (resource_int_value("npx", 0, "irq", &npx_irq) != 0) npx_irq = 13; npx_intrno = NRSVIDT + npx_irq; save_eflags = read_eflags(); disable_intr(); save_icu1_mask = inb(IO_ICU1 + 1); save_icu2_mask = inb(IO_ICU2 + 1); save_idt_npxintr = idt[npx_intrno]; save_idt_npxtrap = idt[16]; outb(IO_ICU1 + 1, ~IRQ_SLAVE); outb(IO_ICU2 + 1, ~(1 << (npx_irq - 8))); setidt(16, probetrap, SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(npx_intrno, probeintr, SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); npx_idt_probeintr = idt[npx_intrno]; enable_intr(); result = npx_probe1(dev); disable_intr(); outb(IO_ICU1 + 1, save_icu1_mask); outb(IO_ICU2 + 1, save_icu2_mask); idt[npx_intrno] = save_idt_npxintr; idt[16] = save_idt_npxtrap; write_eflags(save_eflags); return (result); #endif /* SMP */ } static int npx_probe1(dev) device_t dev; { #ifndef SMP u_short control; u_short status; #endif /* * Partially reset the coprocessor, if any. Some BIOS's don't reset * it after a warm boot. */ outb(0xf1, 0); /* full reset on some systems, NOP on others */ outb(0xf0, 0); /* clear BUSY# latch */ /* * Prepare to trap all ESC (i.e., NPX) instructions and all WAIT * instructions. We must set the CR0_MP bit and use the CR0_TS * bit to control the trap, because setting the CR0_EM bit does * not cause WAIT instructions to trap. It's important to trap * WAIT instructions - otherwise the "wait" variants of no-wait * control instructions would degenerate to the "no-wait" variants * after FP context switches but work correctly otherwise. It's * particularly important to trap WAITs when there is no NPX - * otherwise the "wait" variants would always degenerate. * * Try setting CR0_NE to get correct error reporting on 486DX's. * Setting it should fail or do nothing on lesser processors. */ load_cr0(rcr0() | CR0_MP | CR0_NE); /* * But don't trap while we're probing. */ stop_emulating(); /* * Finish resetting the coprocessor, if any. If there is an error * pending, then we may get a bogus IRQ13, but probeintr() will handle * it OK. Bogus halts have never been observed, but we enabled * IRQ13 and cleared the BUSY# latch early to handle them anyway. */ fninit(); #ifdef SMP /* * Exception 16 MUST work for SMP. */ npx_irq13 = 0; npx_ex16 = hw_float = npx_exists = 1; device_set_desc(dev, "math processor"); return (0); #else /* !SMP */ device_set_desc(dev, "math processor"); /* * Don't use fwait here because it might hang. * Don't use fnop here because it usually hangs if there is no FPU. */ DELAY(1000); /* wait for any IRQ13 */ #ifdef DIAGNOSTIC if (npx_intrs_while_probing != 0) printf("fninit caused %u bogus npx interrupt(s)\n", npx_intrs_while_probing); if (npx_traps_while_probing != 0) printf("fninit caused %u bogus npx trap(s)\n", npx_traps_while_probing); #endif /* * Check for a status of mostly zero. */ status = 0x5a5a; fnstsw(&status); if ((status & 0xb8ff) == 0) { /* * Good, now check for a proper control word. */ control = 0x5a5a; fnstcw(&control); if ((control & 0x1f3f) == 0x033f) { hw_float = npx_exists = 1; /* * We have an npx, now divide by 0 to see if exception * 16 works. */ control &= ~(1 << 2); /* enable divide by 0 trap */ fldcw(&control); npx_traps_while_probing = npx_intrs_while_probing = 0; fp_divide_by_0(); if (npx_traps_while_probing != 0) { /* * Good, exception 16 works. */ npx_ex16 = 1; return (0); } if (npx_intrs_while_probing != 0) { int rid; struct resource *r; void *intr; /* * Bad, we are stuck with IRQ13. */ npx_irq13 = 1; /* * npxattach would be too late to set npx0_imask */ npx0_imask |= (1 << npx_irq); /* * We allocate these resources permanently, * so there is no need to keep track of them. */ rid = 0; r = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, IO_NPX, IO_NPX, IO_NPXSIZE, RF_ACTIVE); if (r == 0) panic("npx: can't get ports"); rid = 0; r = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, npx_irq, npx_irq, 1, RF_ACTIVE); if (r == 0) panic("npx: can't get IRQ"); BUS_SETUP_INTR(device_get_parent(dev), dev, r, INTR_TYPE_MISC, npx_intr, 0, &intr); if (intr == 0) panic("npx: can't create intr"); return (0); } /* * Worse, even IRQ13 is broken. Use emulator. */ } } /* * Probe failed, but we want to get to npxattach to initialize the * emulator and say that it has been installed. XXX handle devices * that aren't really devices better. */ return (0); #endif /* SMP */ } /* * Attach routine - announce which it is, and wire into system */ int npx_attach(dev) device_t dev; { int flags; if (resource_int_value("npx", 0, "flags", &flags) != 0) flags = 0; if (flags) device_printf(dev, "flags 0x%x ", flags); if (npx_irq13) { device_printf(dev, "using IRQ 13 interface\n"); } else { #if defined(MATH_EMULATE) || defined(GPL_MATH_EMULATE) if (npx_ex16) { if (!(flags & NPX_PREFER_EMULATOR)) device_printf(dev, "INT 16 interface\n"); else { device_printf(dev, "FPU exists, but flags request " "emulator\n"); hw_float = npx_exists = 0; } } else if (npx_exists) { device_printf(dev, "error reporting broken; using 387 emulator\n"); hw_float = npx_exists = 0; } else device_printf(dev, "387 emulator\n"); #else if (npx_ex16) { device_printf(dev, "INT 16 interface\n"); if (flags & NPX_PREFER_EMULATOR) { device_printf(dev, "emulator requested, but none compiled " "into kernel, using FPU\n"); } } else device_printf(dev, "no 387 emulator in kernel and no FPU!\n"); #endif } npxinit(__INITIAL_NPXCW__); #ifdef I586_CPU if (cpu_class == CPUCLASS_586 && npx_ex16 && npx_exists && timezero("i586_bzero()", i586_bzero) < timezero("bzero()", bzero) * 4 / 5) { if (!(flags & NPX_DISABLE_I586_OPTIMIZED_BCOPY)) { bcopy_vector = i586_bcopy; ovbcopy_vector = i586_bcopy; } if (!(flags & NPX_DISABLE_I586_OPTIMIZED_BZERO)) bzero = i586_bzero; if (!(flags & NPX_DISABLE_I586_OPTIMIZED_COPYIO)) { copyin_vector = i586_copyin; copyout_vector = i586_copyout; } } #endif return (0); /* XXX unused */ } /* * Initialize floating point unit. */ void npxinit(control) u_short control; { struct save87 dummy; if (!npx_exists) return; /* * fninit has the same h/w bugs as fnsave. Use the detoxified * fnsave to throw away any junk in the fpu. npxsave() initializes * the fpu and sets npxproc = NULL as important side effects. */ npxsave(&dummy); stop_emulating(); fldcw(&control); if (curpcb != NULL) fnsave(&curpcb->pcb_savefpu); start_emulating(); } /* * Free coprocessor (if we have it). */ void npxexit(p) struct proc *p; { if (p == npxproc) npxsave(&curpcb->pcb_savefpu); #ifdef NPX_DEBUG if (npx_exists) { u_int masked_exceptions; masked_exceptions = curpcb->pcb_savefpu.sv_env.en_cw & curpcb->pcb_savefpu.sv_env.en_sw & 0x7f; /* * Log exceptions that would have trapped with the old * control word (overflow, divide by 0, and invalid operand). */ if (masked_exceptions & 0x0d) log(LOG_ERR, "pid %d (%s) exited with masked floating point exceptions 0x%02x\n", p->p_pid, p->p_comm, masked_exceptions); } #endif } /* * The following mechanism is used to ensure that the FPE_... value * that is passed as a trapcode to the signal handler of the user * process does not have more than one bit set. * * Multiple bits may be set if the user process modifies the control * word while a status word bit is already set. While this is a sign * of bad coding, we have no choise than to narrow them down to one * bit, since we must not send a trapcode that is not exactly one of * the FPE_ macros. * * The mechanism has a static table with 127 entries. Each combination * of the 7 FPU status word exception bits directly translates to a * position in this table, where a single FPE_... value is stored. * This FPE_... value stored there is considered the "most important" * of the exception bits and will be sent as the signal code. The * precedence of the bits is based upon Intel Document "Numerical * Applications", Chapter "Special Computational Situations". * * The macro to choose one of these values does these steps: 1) Throw * away status word bits that cannot be masked. 2) Throw away the bits * currently masked in the control word, assuming the user isn't * interested in them anymore. 3) Reinsert status word bit 7 (stack * fault) if it is set, which cannot be masked but must be presered. * 4) Use the remaining bits to point into the trapcode table. * * The 6 maskable bits in order of their preference, as stated in the * above referenced Intel manual: * 1 Invalid operation (FP_X_INV) * 1a Stack underflow * 1b Stack overflow * 1c Operand of unsupported format * 1d SNaN operand. * 2 QNaN operand (not an exception, irrelavant here) * 3 Any other invalid-operation not mentioned above or zero divide * (FP_X_INV, FP_X_DZ) * 4 Denormal operand (FP_X_DNML) * 5 Numeric over/underflow (FP_X_OFL, FP_X_UFL) * 6 Inexact result (FP_X_IMP) */ static char fpetable[128] = { 0, FPE_FLTINV, /* 1 - INV */ FPE_FLTUND, /* 2 - DNML */ FPE_FLTINV, /* 3 - INV | DNML */ FPE_FLTDIV, /* 4 - DZ */ FPE_FLTINV, /* 5 - INV | DZ */ FPE_FLTDIV, /* 6 - DNML | DZ */ FPE_FLTINV, /* 7 - INV | DNML | DZ */ FPE_FLTOVF, /* 8 - OFL */ FPE_FLTINV, /* 9 - INV | OFL */ FPE_FLTUND, /* A - DNML | OFL */ FPE_FLTINV, /* B - INV | DNML | OFL */ FPE_FLTDIV, /* C - DZ | OFL */ FPE_FLTINV, /* D - INV | DZ | OFL */ FPE_FLTDIV, /* E - DNML | DZ | OFL */ FPE_FLTINV, /* F - INV | DNML | DZ | OFL */ FPE_FLTUND, /* 10 - UFL */ FPE_FLTINV, /* 11 - INV | UFL */ FPE_FLTUND, /* 12 - DNML | UFL */ FPE_FLTINV, /* 13 - INV | DNML | UFL */ FPE_FLTDIV, /* 14 - DZ | UFL */ FPE_FLTINV, /* 15 - INV | DZ | UFL */ FPE_FLTDIV, /* 16 - DNML | DZ | UFL */ FPE_FLTINV, /* 17 - INV | DNML | DZ | UFL */ FPE_FLTOVF, /* 18 - OFL | UFL */ FPE_FLTINV, /* 19 - INV | OFL | UFL */ FPE_FLTUND, /* 1A - DNML | OFL | UFL */ FPE_FLTINV, /* 1B - INV | DNML | OFL | UFL */ FPE_FLTDIV, /* 1C - DZ | OFL | UFL */ FPE_FLTINV, /* 1D - INV | DZ | OFL | UFL */ FPE_FLTDIV, /* 1E - DNML | DZ | OFL | UFL */ FPE_FLTINV, /* 1F - INV | DNML | DZ | OFL | UFL */ FPE_FLTRES, /* 20 - IMP */ FPE_FLTINV, /* 21 - INV | IMP */ FPE_FLTUND, /* 22 - DNML | IMP */ FPE_FLTINV, /* 23 - INV | DNML | IMP */ FPE_FLTDIV, /* 24 - DZ | IMP */ FPE_FLTINV, /* 25 - INV | DZ | IMP */ FPE_FLTDIV, /* 26 - DNML | DZ | IMP */ FPE_FLTINV, /* 27 - INV | DNML | DZ | IMP */ FPE_FLTOVF, /* 28 - OFL | IMP */ FPE_FLTINV, /* 29 - INV | OFL | IMP */ FPE_FLTUND, /* 2A - DNML | OFL | IMP */ FPE_FLTINV, /* 2B - INV | DNML | OFL | IMP */ FPE_FLTDIV, /* 2C - DZ | OFL | IMP */ FPE_FLTINV, /* 2D - INV | DZ | OFL | IMP */ FPE_FLTDIV, /* 2E - DNML | DZ | OFL | IMP */ FPE_FLTINV, /* 2F - INV | DNML | DZ | OFL | IMP */ FPE_FLTUND, /* 30 - UFL | IMP */ FPE_FLTINV, /* 31 - INV | UFL | IMP */ FPE_FLTUND, /* 32 - DNML | UFL | IMP */ FPE_FLTINV, /* 33 - INV | DNML | UFL | IMP */ FPE_FLTDIV, /* 34 - DZ | UFL | IMP */ FPE_FLTINV, /* 35 - INV | DZ | UFL | IMP */ FPE_FLTDIV, /* 36 - DNML | DZ | UFL | IMP */ FPE_FLTINV, /* 37 - INV | DNML | DZ | UFL | IMP */ FPE_FLTOVF, /* 38 - OFL | UFL | IMP */ FPE_FLTINV, /* 39 - INV | OFL | UFL | IMP */ FPE_FLTUND, /* 3A - DNML | OFL | UFL | IMP */ FPE_FLTINV, /* 3B - INV | DNML | OFL | UFL | IMP */ FPE_FLTDIV, /* 3C - DZ | OFL | UFL | IMP */ FPE_FLTINV, /* 3D - INV | DZ | OFL | UFL | IMP */ FPE_FLTDIV, /* 3E - DNML | DZ | OFL | UFL | IMP */ FPE_FLTINV, /* 3F - INV | DNML | DZ | OFL | UFL | IMP */ FPE_FLTSUB, /* 40 - STK */ FPE_FLTSUB, /* 41 - INV | STK */ FPE_FLTUND, /* 42 - DNML | STK */ FPE_FLTSUB, /* 43 - INV | DNML | STK */ FPE_FLTDIV, /* 44 - DZ | STK */ FPE_FLTSUB, /* 45 - INV | DZ | STK */ FPE_FLTDIV, /* 46 - DNML | DZ | STK */ FPE_FLTSUB, /* 47 - INV | DNML | DZ | STK */ FPE_FLTOVF, /* 48 - OFL | STK */ FPE_FLTSUB, /* 49 - INV | OFL | STK */ FPE_FLTUND, /* 4A - DNML | OFL | STK */ FPE_FLTSUB, /* 4B - INV | DNML | OFL | STK */ FPE_FLTDIV, /* 4C - DZ | OFL | STK */ FPE_FLTSUB, /* 4D - INV | DZ | OFL | STK */ FPE_FLTDIV, /* 4E - DNML | DZ | OFL | STK */ FPE_FLTSUB, /* 4F - INV | DNML | DZ | OFL | STK */ FPE_FLTUND, /* 50 - UFL | STK */ FPE_FLTSUB, /* 51 - INV | UFL | STK */ FPE_FLTUND, /* 52 - DNML | UFL | STK */ FPE_FLTSUB, /* 53 - INV | DNML | UFL | STK */ FPE_FLTDIV, /* 54 - DZ | UFL | STK */ FPE_FLTSUB, /* 55 - INV | DZ | UFL | STK */ FPE_FLTDIV, /* 56 - DNML | DZ | UFL | STK */ FPE_FLTSUB, /* 57 - INV | DNML | DZ | UFL | STK */ FPE_FLTOVF, /* 58 - OFL | UFL | STK */ FPE_FLTSUB, /* 59 - INV | OFL | UFL | STK */ FPE_FLTUND, /* 5A - DNML | OFL | UFL | STK */ FPE_FLTSUB, /* 5B - INV | DNML | OFL | UFL | STK */ FPE_FLTDIV, /* 5C - DZ | OFL | UFL | STK */ FPE_FLTSUB, /* 5D - INV | DZ | OFL | UFL | STK */ FPE_FLTDIV, /* 5E - DNML | DZ | OFL | UFL | STK */ FPE_FLTSUB, /* 5F - INV | DNML | DZ | OFL | UFL | STK */ FPE_FLTRES, /* 60 - IMP | STK */ FPE_FLTSUB, /* 61 - INV | IMP | STK */ FPE_FLTUND, /* 62 - DNML | IMP | STK */ FPE_FLTSUB, /* 63 - INV | DNML | IMP | STK */ FPE_FLTDIV, /* 64 - DZ | IMP | STK */ FPE_FLTSUB, /* 65 - INV | DZ | IMP | STK */ FPE_FLTDIV, /* 66 - DNML | DZ | IMP | STK */ FPE_FLTSUB, /* 67 - INV | DNML | DZ | IMP | STK */ FPE_FLTOVF, /* 68 - OFL | IMP | STK */ FPE_FLTSUB, /* 69 - INV | OFL | IMP | STK */ FPE_FLTUND, /* 6A - DNML | OFL | IMP | STK */ FPE_FLTSUB, /* 6B - INV | DNML | OFL | IMP | STK */ FPE_FLTDIV, /* 6C - DZ | OFL | IMP | STK */ FPE_FLTSUB, /* 6D - INV | DZ | OFL | IMP | STK */ FPE_FLTDIV, /* 6E - DNML | DZ | OFL | IMP | STK */ FPE_FLTSUB, /* 6F - INV | DNML | DZ | OFL | IMP | STK */ FPE_FLTUND, /* 70 - UFL | IMP | STK */ FPE_FLTSUB, /* 71 - INV | UFL | IMP | STK */ FPE_FLTUND, /* 72 - DNML | UFL | IMP | STK */ FPE_FLTSUB, /* 73 - INV | DNML | UFL | IMP | STK */ FPE_FLTDIV, /* 74 - DZ | UFL | IMP | STK */ FPE_FLTSUB, /* 75 - INV | DZ | UFL | IMP | STK */ FPE_FLTDIV, /* 76 - DNML | DZ | UFL | IMP | STK */ FPE_FLTSUB, /* 77 - INV | DNML | DZ | UFL | IMP | STK */ FPE_FLTOVF, /* 78 - OFL | UFL | IMP | STK */ FPE_FLTSUB, /* 79 - INV | OFL | UFL | IMP | STK */ FPE_FLTUND, /* 7A - DNML | OFL | UFL | IMP | STK */ FPE_FLTSUB, /* 7B - INV | DNML | OFL | UFL | IMP | STK */ FPE_FLTDIV, /* 7C - DZ | OFL | UFL | IMP | STK */ FPE_FLTSUB, /* 7D - INV | DZ | OFL | UFL | IMP | STK */ FPE_FLTDIV, /* 7E - DNML | DZ | OFL | UFL | IMP | STK */ FPE_FLTSUB, /* 7F - INV | DNML | DZ | OFL | UFL | IMP | STK */ }; /* * Preserve the FP status word, clear FP exceptions, then generate a SIGFPE. * * Clearing exceptions is necessary mainly to avoid IRQ13 bugs. We now * depend on longjmp() restoring a usable state. Restoring the state * or examining it might fail if we didn't clear exceptions. * * The error code chosen will be one of the FPE_... macros. It will be * sent as the second argument to old BSD-style signal handlers and as * "siginfo_t->si_code" (second argument) to SA_SIGINFO signal handlers. * * XXX the FP state is not preserved across signal handlers. So signal * handlers cannot afford to do FP unless they preserve the state or * longjmp() out. Both preserving the state and longjmp()ing may be * destroyed by IRQ13 bugs. Clearing FP exceptions is not an acceptable * solution for signals other than SIGFPE. */ void npx_intr(dummy) void *dummy; { int code; u_short control; struct intrframe *frame; if (npxproc == NULL || !npx_exists) { printf("npxintr: npxproc = %p, curproc = %p, npx_exists = %d\n", npxproc, curproc, npx_exists); panic("npxintr from nowhere"); } if (npxproc != curproc) { printf("npxintr: npxproc = %p, curproc = %p, npx_exists = %d\n", npxproc, curproc, npx_exists); panic("npxintr from non-current process"); } outb(0xf0, 0); fnstsw(&curpcb->pcb_savefpu.sv_ex_sw); fnstcw(&control); fnclex(); /* * Pass exception to process. */ frame = (struct intrframe *)&dummy; /* XXX */ if ((ISPL(frame->if_cs) == SEL_UPL) || (frame->if_eflags & PSL_VM)) { /* * Interrupt is essentially a trap, so we can afford to call * the SIGFPE handler (if any) as soon as the interrupt * returns. * * XXX little or nothing is gained from this, and plenty is * lost - the interrupt frame has to contain the trap frame * (this is otherwise only necessary for the rescheduling trap * in doreti, and the frame for that could easily be set up * just before it is used). */ curproc->p_md.md_regs = INTR_TO_TRAPFRAME(frame); /* * Encode the appropriate code for detailed information on * this exception. */ code = fpetable[(curpcb->pcb_savefpu.sv_ex_sw & ~control & 0x3f) | (curpcb->pcb_savefpu.sv_ex_sw & 0x40)]; trapsignal(curproc, SIGFPE, code); } else { /* * Nested interrupt. These losers occur when: * o an IRQ13 is bogusly generated at a bogus time, e.g.: * o immediately after an fnsave or frstor of an * error state. * o a couple of 386 instructions after * "fstpl _memvar" causes a stack overflow. * These are especially nasty when combined with a * trace trap. * o an IRQ13 occurs at the same time as another higher- * priority interrupt. * * Treat them like a true async interrupt. */ psignal(curproc, SIGFPE); } } /* * Implement device not available (DNA) exception * * It would be better to switch FP context here (if curproc != npxproc) * and not necessarily for every context switch, but it is too hard to * access foreign pcb's. */ int npxdna() { if (!npx_exists) return (0); if (npxproc != NULL) { printf("npxdna: npxproc = %p, curproc = %p\n", npxproc, curproc); panic("npxdna"); } stop_emulating(); /* * Record new context early in case frstor causes an IRQ13. */ npxproc = curproc; curpcb->pcb_savefpu.sv_ex_sw = 0; /* * The following frstor may cause an IRQ13 when the state being * restored has a pending error. The error will appear to have been * triggered by the current (npx) user instruction even when that * instruction is a no-wait instruction that should not trigger an * error (e.g., fnclex). On at least one 486 system all of the * no-wait instructions are broken the same as frstor, so our * treatment does not amplify the breakage. On at least one * 386/Cyrix 387 system, fnclex works correctly while frstor and * fnsave are broken, so our treatment breaks fnclex if it is the * first FPU instruction after a context switch. */ frstor(&curpcb->pcb_savefpu); return (1); } /* * Wrapper for fnsave instruction to handle h/w bugs. If there is an error * pending, then fnsave generates a bogus IRQ13 on some systems. Force * any IRQ13 to be handled immediately, and then ignore it. This routine is * often called at splhigh so it must not use many system services. In * particular, it's much easier to install a special handler than to * guarantee that it's safe to use npxintr() and its supporting code. */ void npxsave(addr) struct save87 *addr; { #ifdef SMP stop_emulating(); fnsave(addr); /* fnop(); */ start_emulating(); npxproc = NULL; #else /* SMP */ u_char icu1_mask; u_char icu2_mask; u_char old_icu1_mask; u_char old_icu2_mask; struct gate_descriptor save_idt_npxintr; disable_intr(); old_icu1_mask = inb(IO_ICU1 + 1); old_icu2_mask = inb(IO_ICU2 + 1); save_idt_npxintr = idt[npx_intrno]; outb(IO_ICU1 + 1, old_icu1_mask & ~(IRQ_SLAVE | npx0_imask)); outb(IO_ICU2 + 1, old_icu2_mask & ~(npx0_imask >> 8)); idt[npx_intrno] = npx_idt_probeintr; enable_intr(); stop_emulating(); fnsave(addr); fnop(); start_emulating(); npxproc = NULL; disable_intr(); icu1_mask = inb(IO_ICU1 + 1); /* masks may have changed */ icu2_mask = inb(IO_ICU2 + 1); outb(IO_ICU1 + 1, (icu1_mask & ~npx0_imask) | (old_icu1_mask & npx0_imask)); outb(IO_ICU2 + 1, (icu2_mask & ~(npx0_imask >> 8)) | (old_icu2_mask & (npx0_imask >> 8))); idt[npx_intrno] = save_idt_npxintr; enable_intr(); /* back to usual state */ #endif /* SMP */ } #ifdef I586_CPU static long timezero(funcname, func) const char *funcname; void (*func) __P((void *buf, size_t len)); { void *buf; #define BUFSIZE 1000000 long usec; struct timeval finish, start; buf = malloc(BUFSIZE, M_TEMP, M_NOWAIT); if (buf == NULL) return (BUFSIZE); microtime(&start); (*func)(buf, BUFSIZE); microtime(&finish); usec = 1000000 * (finish.tv_sec - start.tv_sec) + finish.tv_usec - start.tv_usec; if (usec <= 0) usec = 1; if (bootverbose) printf("%s bandwidth = %ld bytes/sec\n", funcname, (long)(BUFSIZE * (int64_t)1000000 / usec)); free(buf, M_TEMP); return (usec); } #endif /* I586_CPU */ static device_method_t npx_methods[] = { /* Device interface */ DEVMETHOD(device_identify, npx_identify), DEVMETHOD(device_probe, npx_probe), DEVMETHOD(device_attach, npx_attach), DEVMETHOD(device_detach, bus_generic_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), { 0, 0 } }; static driver_t npx_driver = { "npx", npx_methods, 1, /* no softc */ }; static devclass_t npx_devclass; /* * We prefer to attach to the root nexus so that the usual case (exception 16) * doesn't describe the processor as being `on isa'. */ DRIVER_MODULE(npx, nexus, npx_driver, npx_devclass, 0, 0); - -#endif /* NNPX > 0 */ diff --git a/sys/dev/lnc/if_lnc.c b/sys/dev/lnc/if_lnc.c index 1c14f8e4084b..9acef11066c0 100644 --- a/sys/dev/lnc/if_lnc.c +++ b/sys/dev/lnc/if_lnc.c @@ -1,2025 +1,2022 @@ /*- * Copyright (c) 1994-1998 * Paul Richards. All rights reserved. * * 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 * notice, this list of conditions and the following disclaimer, * verbatim and that no modifications are made prior to this * point in the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, 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 Paul Richards. * 4. The name Paul Richards may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY PAUL RICHARDS ``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 PAUL RICHARDS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* #define DIAGNOSTIC #define DEBUG * * TODO ---- * * This driver will need bounce buffer support when dma'ing to mbufs above the * 16Mb mark. * * Check all the XXX comments -- some of them are just things I've left * unfinished rather than "difficult" problems that were hacked around. * * Check log settings. * * Check how all the arpcom flags get set and used. * * Re-inline and re-static all routines after debugging. * * Remember to assign iobase in SHMEM probe routines. * * Replace all occurences of LANCE-controller-card etc in prints by the name * strings of the appropriate type -- nifty window dressing * * Add DEPCA support -- mostly done. * */ #include "pci.h" #include "lnc.h" -#if NLNC > 0 #include "opt_inet.h" /* Some defines that should really be in generic locations */ #define FCS_LEN 4 #define MULTICAST_FILTER_LEN 8 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "opt_bdg.h" #ifdef BRIDGE #include #endif #ifdef PC98 #include #endif #include #include #include struct lnc_softc { struct arpcom arpcom; /* see ../../net/if_arp.h */ struct nic_info nic; /* NIC specific info */ int nrdre; struct host_ring_entry *recv_ring; /* start of alloc'd mem */ int recv_next; int ntdre; struct host_ring_entry *trans_ring; int trans_next; struct init_block *init_block; /* Initialisation block */ int pending_transmits; /* No. of transmit descriptors in use */ int next_to_send; struct mbuf *mbufs; int mbuf_count; int flags; int rap; int rdp; int bdp; #ifdef DEBUG int lnc_debug; #endif LNCSTATS_STRUCT }; static struct lnc_softc lnc_softc[NLNC]; static char const * const nic_ident[] = { "Unknown", "BICC", "NE2100", "DEPCA", "CNET98S", /* PC-98 */ }; static char const * const ic_ident[] = { "Unknown", "LANCE", "C-LANCE", "PCnet-ISA", "PCnet-ISA+", "PCnet-ISA II", "PCnet-32 VL-Bus", "PCnet-PCI", "PCnet-PCI II", "PCnet-FAST", "PCnet-FAST+", "PCnet-Home", }; static void lnc_setladrf __P((struct lnc_softc *sc)); static void lnc_stop __P((struct lnc_softc *sc)); static void lnc_reset __P((struct lnc_softc *sc)); static void lnc_free_mbufs __P((struct lnc_softc *sc)); static __inline int alloc_mbuf_cluster __P((struct lnc_softc *sc, struct host_ring_entry *desc)); static __inline struct mbuf *chain_mbufs __P((struct lnc_softc *sc, int start_of_packet, int pkt_len)); static __inline struct mbuf *mbuf_packet __P((struct lnc_softc *sc, int start_of_packet, int pkt_len)); static __inline void lnc_rint __P((struct lnc_softc *sc)); static __inline void lnc_tint __P((struct lnc_softc *sc)); static int lnc_probe __P((struct isa_device *isa_dev)); #ifdef PC98 static int cnet98s_probe __P((struct lnc_softc *sc, unsigned iobase)); #endif static int ne2100_probe __P((struct lnc_softc *sc, unsigned iobase)); static int bicc_probe __P((struct lnc_softc *sc, unsigned iobase)); static int dec_macaddr_extract __P((u_char ring[], struct lnc_softc *sc)); static int depca_probe __P((struct lnc_softc *sc, unsigned iobase)); static int lance_probe __P((struct lnc_softc *sc)); static int pcnet_probe __P((struct lnc_softc *sc)); static int lnc_attach_sc __P((struct lnc_softc *sc, int unit)); static int lnc_attach __P((struct isa_device *isa_dev)); static void lnc_init __P((void *)); static ointhand2_t lncintr; static __inline int mbuf_to_buffer __P((struct mbuf *m, char *buffer)); static __inline struct mbuf *chain_to_cluster __P((struct mbuf *m)); static void lnc_start __P((struct ifnet *ifp)); static int lnc_ioctl __P((struct ifnet *ifp, u_long command, caddr_t data)); static void lnc_watchdog __P((struct ifnet *ifp)); #ifdef DEBUG void lnc_dump_state __P((struct lnc_softc *sc)); void mbuf_dump_chain __P((struct mbuf *m)); #endif #if NPCI > 0 void *lnc_attach_ne2100_pci __P((int unit, unsigned iobase)); #endif void lncintr_sc __P((struct lnc_softc *sc)); struct isa_driver lncdriver = {lnc_probe, lnc_attach, "lnc"}; static __inline void write_csr(struct lnc_softc *sc, u_short port, u_short val) { outw(sc->rap, port); outw(sc->rdp, val); } static __inline u_short read_csr(struct lnc_softc *sc, u_short port) { outw(sc->rap, port); return (inw(sc->rdp)); } static __inline void write_bcr(struct lnc_softc *sc, u_short port, u_short val) { outw(sc->rap, port); outw(sc->bdp, val); } static __inline u_short read_bcr(struct lnc_softc *sc, u_short port) { outw(sc->rap, port); return (inw(sc->bdp)); } static __inline u_long ether_crc(const u_char *ether_addr) { #define POLYNOMIAL 0xEDB88320UL u_char i, j, addr; u_int crc = 0xFFFFFFFFUL; for (i = 0; i < ETHER_ADDR_LEN; i++) { addr = *ether_addr++; for (j = 0; j < MULTICAST_FILTER_LEN; j++) { crc = (crc >> 1) ^ (((crc ^ addr) & 1) ? POLYNOMIAL : 0); addr >>= 1; } } return crc; #undef POLYNOMIAL } /* * Set up the logical address filter for multicast packets */ static __inline void lnc_setladrf(struct lnc_softc *sc) { struct ifnet *ifp = &sc->arpcom.ac_if; struct ifmultiaddr *ifma; u_long index; int i; if (sc->flags & IFF_ALLMULTI) { for (i=0; i < MULTICAST_FILTER_LEN; i++) sc->init_block->ladrf[i] = 0xFF; return; } /* * For each multicast address, calculate a crc for that address and * then use the high order 6 bits of the crc as a hash code where * bits 3-5 select the byte of the address filter and bits 0-2 select * the bit within that byte. */ bzero(sc->init_block->ladrf, MULTICAST_FILTER_LEN); for (ifma = ifp->if_multiaddrs.lh_first; ifma; ifma = ifma->ifma_link.le_next) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; index = ether_crc(LLADDR((struct sockaddr_dl *)ifma->ifma_addr)) >> 26; sc->init_block->ladrf[index >> 3] |= 1 << (index & 7); } } static void lnc_stop(struct lnc_softc *sc) { write_csr(sc, CSR0, STOP); } static void lnc_reset(struct lnc_softc *sc) { lnc_init(sc); } static void lnc_free_mbufs(struct lnc_softc *sc) { int i; /* * We rely on other routines to keep the buff.mbuf field valid. If * it's not NULL then we assume it points to an allocated mbuf. */ for (i = 0; i < NDESC(sc->nrdre); i++) if ((sc->recv_ring + i)->buff.mbuf) m_free((sc->recv_ring + i)->buff.mbuf); for (i = 0; i < NDESC(sc->ntdre); i++) if ((sc->trans_ring + i)->buff.mbuf) m_free((sc->trans_ring + i)->buff.mbuf); if (sc->mbuf_count) m_freem(sc->mbufs); } static __inline int alloc_mbuf_cluster(struct lnc_softc *sc, struct host_ring_entry *desc) { register struct mds *md = desc->md; struct mbuf *m=0; int addr; /* Try and get cluster off local cache */ if (sc->mbuf_count) { sc->mbuf_count--; m = sc->mbufs; sc->mbufs = m->m_next; /* XXX m->m_data = m->m_ext.ext_buf;*/ } else { MGET(m, M_DONTWAIT, MT_DATA); if (!m) return(1); MCLGET(m, M_DONTWAIT); if (!m->m_ext.ext_buf) { m_free(m); return(1); } } desc->buff.mbuf = m; addr = kvtop(m->m_data); md->md0 = addr; md->md1= ((addr >> 16) & 0xff) | OWN; md->md2 = -(short)(MCLBYTES - sizeof(struct pkthdr)); md->md3 = 0; return(0); } static __inline struct mbuf * chain_mbufs(struct lnc_softc *sc, int start_of_packet, int pkt_len) { struct mbuf *head, *m; struct host_ring_entry *desc; /* * Turn head into a pkthdr mbuf -- * assumes a pkthdr type mbuf was * allocated to the descriptor * originally. */ desc = sc->recv_ring + start_of_packet; head = desc->buff.mbuf; head->m_flags |= M_PKTHDR; m = head; do { m = desc->buff.mbuf; m->m_len = min((MCLBYTES - sizeof(struct pkthdr)), pkt_len); pkt_len -= m->m_len; if (alloc_mbuf_cluster(sc, desc)) return((struct mbuf *)NULL); INC_MD_PTR(start_of_packet, sc->nrdre) desc = sc->recv_ring + start_of_packet; m->m_next = desc->buff.mbuf; } while (start_of_packet != sc->recv_next); m->m_next = 0; return(head); } static __inline struct mbuf * mbuf_packet(struct lnc_softc *sc, int start_of_packet, int pkt_len) { struct host_ring_entry *start; struct mbuf *head,*m,*m_prev; char *data,*mbuf_data; short blen; int amount; /* Get a pkthdr mbuf for the start of packet */ MGETHDR(head, M_DONTWAIT, MT_DATA); if (!head) { LNCSTATS(drop_packet) return(0); } m = head; m->m_len = 0; start = sc->recv_ring + start_of_packet; /*blen = -(start->md->md2);*/ blen = RECVBUFSIZE; /* XXX More PCnet-32 crap */ data = start->buff.data; mbuf_data = m->m_data; while (start_of_packet != sc->recv_next) { /* * If the data left fits in a single buffer then set * blen to the size of the data left. */ if (pkt_len < blen) blen = pkt_len; /* * amount is least of data in current ring buffer and * amount of space left in current mbuf. */ amount = min(blen, M_TRAILINGSPACE(m)); if (amount == 0) { /* mbuf must be empty */ m_prev = m; MGET(m, M_DONTWAIT, MT_DATA); if (!m) { m_freem(head); return(0); } if (pkt_len >= MINCLSIZE) MCLGET(m, M_DONTWAIT); m->m_len = 0; m_prev->m_next = m; amount = min(blen, M_TRAILINGSPACE(m)); mbuf_data = m->m_data; } bcopy(data, mbuf_data, amount); blen -= amount; pkt_len -= amount; m->m_len += amount; data += amount; mbuf_data += amount; if (blen == 0) { start->md->md1 &= HADR; start->md->md1 |= OWN; start->md->md2 = -RECVBUFSIZE; /* XXX - shouldn't be necessary */ INC_MD_PTR(start_of_packet, sc->nrdre) start = sc->recv_ring + start_of_packet; data = start->buff.data; /*blen = -(start->md->md2);*/ blen = RECVBUFSIZE; /* XXX More PCnet-32 crap */ } } return(head); } static __inline void lnc_rint(struct lnc_softc *sc) { struct host_ring_entry *next, *start; int start_of_packet; struct mbuf *head; struct ether_header *eh; int lookahead; int flags; int pkt_len; /* * The LANCE will issue a RINT interrupt when the ownership of the * last buffer of a receive packet has been relinquished by the LANCE. * Therefore, it can be assumed that a complete packet can be found * before hitting buffers that are still owned by the LANCE, if not * then there is a bug in the driver that is causing the descriptors * to get out of sync. */ #ifdef DIAGNOSTIC if ((sc->recv_ring + sc->recv_next)->md->md1 & OWN) { int unit = sc->arpcom.ac_if.if_unit; log(LOG_ERR, "lnc%d: Receive interrupt with buffer still owned by controller -- Resetting\n", unit); lnc_reset(sc); return; } if (!((sc->recv_ring + sc->recv_next)->md->md1 & STP)) { int unit = sc->arpcom.ac_if.if_unit; log(LOG_ERR, "lnc%d: Receive interrupt but not start of packet -- Resetting\n", unit); lnc_reset(sc); return; } #endif lookahead = 0; next = sc->recv_ring + sc->recv_next; while ((flags = next->md->md1) & STP) { /* Make a note of the start of the packet */ start_of_packet = sc->recv_next; /* * Find the end of the packet. Even if not data chaining, * jabber packets can overrun into a second descriptor. * If there is no error, then the ENP flag is set in the last * descriptor of the packet. If there is an error then the ERR * flag will be set in the descriptor where the error occured. * Therefore, to find the last buffer of a packet we search for * either ERR or ENP. */ if (!(flags & (ENP | MDERR))) { do { INC_MD_PTR(sc->recv_next, sc->nrdre) next = sc->recv_ring + sc->recv_next; flags = next->md->md1; } while (!(flags & (STP | OWN | ENP | MDERR))); if (flags & STP) { int unit = sc->arpcom.ac_if.if_unit; log(LOG_ERR, "lnc%d: Start of packet found before end of previous in receive ring -- Resetting\n", unit); lnc_reset(sc); return; } if (flags & OWN) { if (lookahead) { /* * Looked ahead into a packet still * being received */ sc->recv_next = start_of_packet; break; } else { int unit = sc->arpcom.ac_if.if_unit; log(LOG_ERR, "lnc%d: End of received packet not found-- Resetting\n", unit); lnc_reset(sc); return; } } } pkt_len = (next->md->md3 & MCNT) - FCS_LEN; /* Move pointer onto start of next packet */ INC_MD_PTR(sc->recv_next, sc->nrdre) next = sc->recv_ring + sc->recv_next; if (flags & MDERR) { int unit = sc->arpcom.ac_if.if_unit; if (flags & RBUFF) { LNCSTATS(rbuff) log(LOG_ERR, "lnc%d: Receive buffer error\n", unit); } if (flags & OFLO) { /* OFLO only valid if ENP is not set */ if (!(flags & ENP)) { LNCSTATS(oflo) log(LOG_ERR, "lnc%d: Receive overflow error \n", unit); } } else if (flags & ENP) { if ((sc->arpcom.ac_if.if_flags & IFF_PROMISC)==0) { /* * FRAM and CRC are valid only if ENP * is set and OFLO is not. */ if (flags & FRAM) { LNCSTATS(fram) log(LOG_ERR, "lnc%d: Framing error\n", unit); /* * FRAM is only set if there's a CRC * error so avoid multiple messages */ } else if (flags & CRC) { LNCSTATS(crc) log(LOG_ERR, "lnc%d: Receive CRC error\n", unit); } } } /* Drop packet */ LNCSTATS(rerr) sc->arpcom.ac_if.if_ierrors++; while (start_of_packet != sc->recv_next) { start = sc->recv_ring + start_of_packet; start->md->md2 = -RECVBUFSIZE; /* XXX - shouldn't be necessary */ start->md->md1 &= HADR; start->md->md1 |= OWN; INC_MD_PTR(start_of_packet, sc->nrdre) } } else { /* Valid packet */ sc->arpcom.ac_if.if_ipackets++; if (sc->nic.mem_mode == DMA_MBUF) head = chain_mbufs(sc, start_of_packet, pkt_len); else head = mbuf_packet(sc, start_of_packet, pkt_len); if (head) { /* * First mbuf in packet holds the * ethernet and packet headers */ head->m_pkthdr.rcvif = &sc->arpcom.ac_if; head->m_pkthdr.len = pkt_len ; /* * BPF expects the ether header to be in the first * mbuf of the chain so point eh at the right place * but don't increment the mbuf pointers before * the bpf tap. */ eh = (struct ether_header *) head->m_data; if (sc->arpcom.ac_if.if_bpf) bpf_mtap(&sc->arpcom.ac_if, head); #ifdef BRIDGE if (do_bridge) { struct ifnet *bdg_ifp ; bdg_ifp = bridge_in(head); if (bdg_ifp == BDG_DROP) m_freem(head); else { if (bdg_ifp != BDG_LOCAL) bdg_forward(&head, bdg_ifp); if ( bdg_ifp == BDG_LOCAL || bdg_ifp == BDG_BCAST || bdg_ifp == BDG_MCAST ) goto getit; else if (head) m_freem(head); } } else #endif /* Check this packet is really for us */ if ((sc->arpcom.ac_if.if_flags & IFF_PROMISC) && !(eh->ether_dhost[0] & 1) && /* Broadcast and multicast */ (bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr, sizeof(eh->ether_dhost)))) m_freem(head); else { #ifdef BRIDGE getit: #endif /* Skip over the ether header */ head->m_data += sizeof *eh; head->m_len -= sizeof *eh; head->m_pkthdr.len -= sizeof *eh; ether_input(&sc->arpcom.ac_if, eh, head); } } else { int unit = sc->arpcom.ac_if.if_unit; log(LOG_ERR,"lnc%d: Packet dropped, no mbufs\n",unit); LNCSTATS(drop_packet) } } lookahead++; } /* * At this point all completely received packets have been processed * so clear RINT since any packets that have arrived while we were in * here have been dealt with. */ outw(sc->rdp, RINT | INEA); } static __inline void lnc_tint(struct lnc_softc *sc) { struct host_ring_entry *next, *start; int start_of_packet; int lookahead; /* * If the driver is reset in this routine then we return immediately to * the interrupt driver routine. Any interrupts that have occured * since the reset will be dealt with there. sc->trans_next * should point to the start of the first packet that was awaiting * transmission after the last transmit interrupt was dealt with. The * LANCE should have relinquished ownership of that descriptor before * the interrupt. Therefore, sc->trans_next should point to a * descriptor with STP set and OWN cleared. If not then the driver's * pointers are out of sync with the LANCE, which signifies a bug in * the driver. Therefore, the following two checks are really * diagnostic, since if the driver is working correctly they should * never happen. */ #ifdef DIAGNOSTIC if ((sc->trans_ring + sc->trans_next)->md->md1 & OWN) { int unit = sc->arpcom.ac_if.if_unit; log(LOG_ERR, "lnc%d: Transmit interrupt with buffer still owned by controller -- Resetting\n", unit); lnc_reset(sc); return; } #endif /* * The LANCE will write the status information for the packet it just * tried to transmit in one of two places. If the packet was * transmitted successfully then the status will be written into the * last descriptor of the packet. If the transmit failed then the * status will be written into the descriptor that was being accessed * when the error occured and all subsequent descriptors in that * packet will have been relinquished by the LANCE. * * At this point we know that sc->trans_next points to the start * of a packet that the LANCE has just finished trying to transmit. * We now search for a buffer with either ENP or ERR set. */ lookahead = 0; do { start_of_packet = sc->trans_next; next = sc->trans_ring + sc->trans_next; #ifdef DIAGNOSTIC if (!(next->md->md1 & STP)) { int unit = sc->arpcom.ac_if.if_unit; log(LOG_ERR, "lnc%d: Transmit interrupt but not start of packet -- Resetting\n", unit); lnc_reset(sc); return; } #endif /* * Find end of packet. */ if (!(next->md->md1 & (ENP | MDERR))) { do { INC_MD_PTR(sc->trans_next, sc->ntdre) next = sc->trans_ring + sc->trans_next; } while (!(next->md->md1 & (STP | OWN | ENP | MDERR))); if (next->md->md1 & STP) { int unit = sc->arpcom.ac_if.if_unit; log(LOG_ERR, "lnc%d: Start of packet found before end of previous in transmit ring -- Resetting\n", unit); lnc_reset(sc); return; } if (next->md->md1 & OWN) { if (lookahead) { /* * Looked ahead into a packet still * being transmitted */ sc->trans_next = start_of_packet; break; } else { int unit = sc->arpcom.ac_if.if_unit; log(LOG_ERR, "lnc%d: End of transmitted packet not found -- Resetting\n", unit); lnc_reset(sc); return; } } } /* * Check for ERR first since other flags are irrelevant if an * error occurred. */ if (next->md->md1 & MDERR) { int unit = sc->arpcom.ac_if.if_unit; LNCSTATS(terr) sc->arpcom.ac_if.if_oerrors++; if (next->md->md3 & LCOL) { LNCSTATS(lcol) log(LOG_ERR, "lnc%d: Transmit late collision -- Net error?\n", unit); sc->arpcom.ac_if.if_collisions++; /* * Clear TBUFF since it's not valid when LCOL * set */ next->md->md3 &= ~TBUFF; } if (next->md->md3 & LCAR) { LNCSTATS(lcar) log(LOG_ERR, "lnc%d: Loss of carrier during transmit -- Net error?\n", unit); } if (next->md->md3 & RTRY) { LNCSTATS(rtry) log(LOG_ERR, "lnc%d: Transmit of packet failed after 16 attempts -- TDR = %d\n", unit, ((sc->trans_ring + sc->trans_next)->md->md3 & TDR)); sc->arpcom.ac_if.if_collisions += 16; /* * Clear TBUFF since it's not valid when RTRY * set */ next->md->md3 &= ~TBUFF; } /* * TBUFF is only valid if neither LCOL nor RTRY are set. * We need to check UFLO after LCOL and RTRY so that we * know whether or not TBUFF is valid. If either are * set then TBUFF will have been cleared above. A * UFLO error will turn off the transmitter so we * have to reset. * */ if (next->md->md3 & UFLO) { LNCSTATS(uflo) /* * If an UFLO has occured it's possibly due * to a TBUFF error */ if (next->md->md3 & TBUFF) { LNCSTATS(tbuff) log(LOG_ERR, "lnc%d: Transmit buffer error -- Resetting\n", unit); } else log(LOG_ERR, "lnc%d: Transmit underflow error -- Resetting\n", unit); lnc_reset(sc); return; } do { INC_MD_PTR(sc->trans_next, sc->ntdre) next = sc->trans_ring + sc->trans_next; } while (!(next->md->md1 & STP) && (sc->trans_next != sc->next_to_send)); } else { /* * Since we check for ERR first then if we get here * the packet was transmitted correctly. There may * still have been non-fatal errors though. * Don't bother checking for DEF, waste of time. */ sc->arpcom.ac_if.if_opackets++; if (next->md->md1 & MORE) { LNCSTATS(more) sc->arpcom.ac_if.if_collisions += 2; } /* * ONE is invalid if LCOL is set. If LCOL was set then * ERR would have also been set and we would have * returned from lnc_tint above. Therefore we can * assume if we arrive here that ONE is valid. * */ if (next->md->md1 & ONE) { LNCSTATS(one) sc->arpcom.ac_if.if_collisions++; } INC_MD_PTR(sc->trans_next, sc->ntdre) next = sc->trans_ring + sc->trans_next; } /* * Clear descriptors and free any mbufs. */ do { start = sc->trans_ring + start_of_packet; start->md->md1 &= HADR; if (sc->nic.mem_mode == DMA_MBUF) { /* Cache clusters on a local queue */ if ((start->buff.mbuf->m_flags & M_EXT) && (sc->mbuf_count < MBUF_CACHE_LIMIT)) { if (sc->mbuf_count) { start->buff.mbuf->m_next = sc->mbufs; sc->mbufs = start->buff.mbuf; } else sc->mbufs = start->buff.mbuf; sc->mbuf_count++; start->buff.mbuf = 0; } else { struct mbuf *junk; MFREE(start->buff.mbuf, junk); start->buff.mbuf = 0; } } sc->pending_transmits--; INC_MD_PTR(start_of_packet, sc->ntdre) }while (start_of_packet != sc->trans_next); /* * There's now at least one free descriptor * in the ring so indicate that we can accept * more packets again. */ sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE; lookahead++; } while (sc->pending_transmits && !(next->md->md1 & OWN)); /* * Clear TINT since we've dealt with all * the completed transmissions. */ outw(sc->rdp, TINT | INEA); /* XXX only while doing if_is comparisons */ if (!(sc->arpcom.ac_if.if_flags & IFF_OACTIVE)) lnc_start(&sc->arpcom.ac_if); } static int lnc_probe(struct isa_device * isa_dev) { int nports; int unit = isa_dev->id_unit; struct lnc_softc *sc = &lnc_softc[unit]; unsigned iobase = isa_dev->id_iobase; #ifdef DIAGNOSTIC int vsw; vsw = inw(isa_dev->id_iobase + PCNET_VSW); printf("Vendor Specific Word = %x\n", vsw); #endif nports = bicc_probe(sc, iobase); if (nports == 0) nports = ne2100_probe(sc, iobase); if (nports == 0) nports = depca_probe(sc, iobase); #ifdef PC98 if (nports == 0) nports = cnet98s_probe(sc, iobase); #endif return (nports); } #ifdef PC98 /* ISA Bus Configuration Registers */ /* XXX - Should be in ic/Am7990.h */ #define MSRDA 0x0000 /* ISACSR0: Master Mode Read Activity */ #define MSWRA 0x0001 /* ISACSR1: Master Mode Write Activity */ #define MC 0x0002 /* ISACSR2: Miscellaneous Configuration */ #define LED1 0x0005 /* ISACSR5: LED1 Status */ #define LED2 0x0006 /* ISACSR6: LED2 Status */ #define LED3 0x0007 /* ISACSR7: LED3 Status */ #define LED_PSE 0x0080 /* Pulse Stretcher */ #define LED_XMTE 0x0010 /* Transmit Status */ #define LED_RVPOLE 0x0008 /* Receive Polarity */ #define LED_RCVE 0x0004 /* Receive Status */ #define LED_JABE 0x0002 /* Jabber */ #define LED_COLE 0x0001 /* Collision */ static int cnet98s_probe(struct lnc_softc *sc, unsigned iobase) { int i; ushort tmp; sc->rap = iobase + CNET98S_RAP; sc->rdp = iobase + CNET98S_RDP; /* Reset */ tmp = inw(iobase + CNET98S_RESET); outw(iobase + CNET98S_RESET, tmp); DELAY(500); sc->nic.ic = pcnet_probe(sc); if ((sc->nic.ic == UNKNOWN) || (sc->nic.ic > PCnet_32)) { return (0); } sc->nic.ident = CNET98S; sc->nic.mem_mode = DMA_FIXED; /* XXX - For now just use the defines */ sc->nrdre = NRDRE; sc->ntdre = NTDRE; /* Extract MAC address from PROM */ for (i = 0; i < ETHER_ADDR_LEN; i++) { sc->arpcom.ac_enaddr[i] = inb(iobase + (i * 2)); } /* * ISA Configuration * * XXX - Following parameters are Contec C-NET(98)S only. * So, check the Ethernet address here. * * Contec uses 00 80 4c ?? ?? ?? */ if (sc->arpcom.ac_enaddr[0] == (u_char)0x00 && sc->arpcom.ac_enaddr[1] == (u_char)0x80 && sc->arpcom.ac_enaddr[2] == (u_char)0x4c) { outw(sc->rap, MSRDA); outw(iobase + CNET98S_IDP, 0x0006); outw(sc->rap, MSWRA); outw(iobase + CNET98S_IDP, 0x0006); #ifdef DIAGNOSTIC outw(sc->rap, MC); printf("ISACSR2 = %x\n", inw(iobase + CNET98S_IDP)); #endif outw(sc->rap, LED1); outw(iobase + CNET98S_IDP, LED_PSE | LED_XMTE); outw(sc->rap, LED2); outw(iobase + CNET98S_IDP, LED_PSE | LED_RCVE); outw(sc->rap, LED3); outw(iobase + CNET98S_IDP, LED_PSE | LED_COLE); } return (CNET98S_IOSIZE); } #endif static int ne2100_probe(struct lnc_softc *sc, unsigned iobase) { int i; sc->rap = iobase + PCNET_RAP; sc->rdp = iobase + PCNET_RDP; sc->nic.ic = pcnet_probe(sc); if ((sc->nic.ic > 0) && (sc->nic.ic < PCnet_PCI)) { sc->nic.ident = NE2100; sc->nic.mem_mode = DMA_FIXED; /* XXX - For now just use the defines */ sc->nrdre = NRDRE; sc->ntdre = NTDRE; /* Extract MAC address from PROM */ for (i = 0; i < ETHER_ADDR_LEN; i++) sc->arpcom.ac_enaddr[i] = inb(iobase + i); return (NE2100_IOSIZE); } else { return (0); } } static int bicc_probe(struct lnc_softc *sc, unsigned iobase) { int i; /* * There isn't any way to determine if a NIC is a BICC. Basically, if * the lance probe succeeds using the i/o addresses of the BICC then * we assume it's a BICC. * */ sc->rap = iobase + BICC_RAP; sc->rdp = iobase + BICC_RDP; /* I think all these cards us the Am7990 */ if ((sc->nic.ic = lance_probe(sc))) { sc->nic.ident = BICC; sc->nic.mem_mode = DMA_FIXED; /* XXX - For now just use the defines */ sc->nrdre = NRDRE; sc->ntdre = NTDRE; /* Extract MAC address from PROM */ for (i = 0; i < ETHER_ADDR_LEN; i++) sc->arpcom.ac_enaddr[i] = inb(iobase + (i * 2)); return (BICC_IOSIZE); } else { return (0); } } /* * I don't have data sheets for the dec cards but it looks like the mac * address is contained in a 32 byte ring. Each time you read from the port * you get the next byte in the ring. The mac address is stored after a * signature so keep searching for the signature first. */ static int dec_macaddr_extract(u_char ring[], struct lnc_softc * sc) { const unsigned char signature[] = {0xff, 0x00, 0x55, 0xaa, 0xff, 0x00, 0x55, 0xaa}; int i, j, rindex; for (i = 0; i < sizeof ring; i++) { for (j = 0, rindex = i; j < sizeof signature; j++) { if (ring[rindex] != signature[j]) break; if (++rindex > sizeof ring) rindex = 0; } if (j == sizeof signature) { for (j = 0, rindex = i; j < ETHER_ADDR_LEN; j++) { sc->arpcom.ac_enaddr[j] = ring[rindex]; if (++rindex > sizeof ring) rindex = 0; } return (1); } } return (0); } static int depca_probe(struct lnc_softc *sc, unsigned iobase) { int i; unsigned char maddr_ring[DEPCA_ADDR_ROM_SIZE]; sc->rap = iobase + DEPCA_RAP; sc->rdp = iobase + DEPCA_RDP; if ((sc->nic.ic = lance_probe(sc))) { sc->nic.ident = DEPCA; sc->nic.mem_mode = SHMEM; /* Extract MAC address from PROM */ for (i = 0; i < DEPCA_ADDR_ROM_SIZE; i++) maddr_ring[i] = inb(iobase + DEPCA_ADP); if (dec_macaddr_extract(maddr_ring, sc)) { return (DEPCA_IOSIZE); } } return (0); } static int lance_probe(struct lnc_softc *sc) { write_csr(sc, CSR0, STOP); if ((inw(sc->rdp) & STOP) && !(read_csr(sc, CSR3))) { /* * Check to see if it's a C-LANCE. For the LANCE the INEA bit * cannot be set while the STOP bit is. This restriction is * removed for the C-LANCE. */ write_csr(sc, CSR0, INEA); if (read_csr(sc, CSR0) & INEA) return (C_LANCE); else return (LANCE); } else return (UNKNOWN); } static int pcnet_probe(struct lnc_softc *sc) { u_long chip_id; int type; /* * The PCnet family don't reset the RAP register on reset so we'll * have to write during the probe :-) It does have an ID register * though so the probe is just a matter of reading it. */ if ((type = lance_probe(sc))) { chip_id = read_csr(sc, CSR89); chip_id <<= 16; chip_id |= read_csr(sc, CSR88); if (chip_id & AMD_MASK) { chip_id >>= 12; switch (chip_id & PART_MASK) { case Am79C960: return (PCnet_ISA); case Am79C961: return (PCnet_ISAplus); case Am79C961A: return (PCnet_ISA_II); case Am79C965: return (PCnet_32); case Am79C970: return (PCnet_PCI); case Am79C970A: return (PCnet_PCI_II); case Am79C971: return (PCnet_FAST); case Am79C972: return (PCnet_FASTplus); case Am79C978: return (PCnet_Home); default: break; } } } return (type); } static int lnc_attach_sc(struct lnc_softc *sc, int unit) { int lnc_mem_size; /* * Allocate memory for use by the controller. * * XXX -- the Am7990 and Am79C960 only have 24 address lines and so can * only access the lower 16Mb of physical memory. For the moment we * assume that malloc will allocate memory within the lower 16Mb * range. This is not a very valid assumption but there's nothing * that can be done about it yet. For shared memory NICs this isn't * relevant. * */ lnc_mem_size = ((NDESC(sc->nrdre) + NDESC(sc->ntdre)) * sizeof(struct host_ring_entry)); if (sc->nic.mem_mode != SHMEM) lnc_mem_size += sizeof(struct init_block) + (sizeof(struct mds) * (NDESC(sc->nrdre) + NDESC(sc->ntdre))) + MEM_SLEW; /* If using DMA to fixed host buffers then allocate memory for them */ if (sc->nic.mem_mode == DMA_FIXED) lnc_mem_size += (NDESC(sc->nrdre) * RECVBUFSIZE) + (NDESC(sc->ntdre) * TRANSBUFSIZE); if (sc->nic.mem_mode != SHMEM) { if (sc->nic.ic < PCnet_32) { /* ISA based cards */ sc->recv_ring = contigmalloc(lnc_mem_size, M_DEVBUF, M_NOWAIT, 0ul, 0xfffffful, 4ul, 0x1000000); } else { /* Non-ISA based cards, 32 bit capable */ #ifdef notyet /* * For the 32 bit driver we're not fussed where we DMA to * though it'll still need to be contiguous */ sc->recv_ring = malloc(lnc_mem_size, M_DEVBUF, M_NOWAIT); #else /* * For now it still needs to be below 16MB because the * descriptor's can only hold 16 bit addresses. */ sc->recv_ring = contigmalloc(lnc_mem_size, M_DEVBUF, M_NOWAIT, 0ul, 0xfffffful, 4ul, 0x1000000); #endif } } if (!sc->recv_ring) { log(LOG_ERR, "lnc%d: Couldn't allocate memory for NIC\n", unit); return (0); /* XXX -- attach failed -- not tested in * calling routines */ } /* Set default mode */ sc->nic.mode = NORMAL; /* Fill in arpcom structure entries */ sc->arpcom.ac_if.if_softc = sc; sc->arpcom.ac_if.if_name = lncdriver.name; sc->arpcom.ac_if.if_unit = unit; sc->arpcom.ac_if.if_mtu = ETHERMTU; sc->arpcom.ac_if.if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; sc->arpcom.ac_if.if_timer = 0; sc->arpcom.ac_if.if_output = ether_output; sc->arpcom.ac_if.if_start = lnc_start; sc->arpcom.ac_if.if_ioctl = lnc_ioctl; sc->arpcom.ac_if.if_watchdog = lnc_watchdog; sc->arpcom.ac_if.if_init = lnc_init; sc->arpcom.ac_if.if_type = IFT_ETHER; sc->arpcom.ac_if.if_addrlen = ETHER_ADDR_LEN; sc->arpcom.ac_if.if_hdrlen = ETHER_HDR_LEN; sc->arpcom.ac_if.if_snd.ifq_maxlen = IFQ_MAXLEN; /* * XXX -- should check return status of if_attach */ if_attach(&sc->arpcom.ac_if); ether_ifattach(&sc->arpcom.ac_if); printf("lnc%d: ", unit); if (sc->nic.ic == LANCE || sc->nic.ic == C_LANCE) printf("%s (%s)", nic_ident[sc->nic.ident], ic_ident[sc->nic.ic]); else printf("%s", ic_ident[sc->nic.ic]); printf(" address %6D\n", sc->arpcom.ac_enaddr, ":"); bpfattach(&sc->arpcom.ac_if, DLT_EN10MB, sizeof(struct ether_header)); return (1); } static int lnc_attach(struct isa_device * isa_dev) { int unit = isa_dev->id_unit; struct lnc_softc *sc = &lnc_softc[unit]; int result; isa_dev->id_ointr = lncintr; result = lnc_attach_sc (sc, unit); if (result == 0) return (0); #ifndef PC98 /* * XXX - is it safe to call isa_dmacascade() after if_attach() * and ether_ifattach() have been called in lnc_attach() ??? */ if ((sc->nic.mem_mode != SHMEM) && (sc->nic.ic < PCnet_32)) isa_dmacascade(isa_dev->id_drq); #endif return result; } #if NPCI > 0 void * lnc_attach_ne2100_pci(int unit, unsigned iobase) { int i; struct lnc_softc *sc = malloc(sizeof *sc, M_DEVBUF, M_NOWAIT); if (sc) { bzero (sc, sizeof *sc); sc->rap = iobase + PCNET_RAP; sc->rdp = iobase + PCNET_RDP; sc->bdp = iobase + PCNET_BDP; sc->nic.ic = pcnet_probe(sc); if (sc->nic.ic >= PCnet_32) { sc->nic.ident = NE2100; sc->nic.mem_mode = DMA_FIXED; /* XXX - For now just use the defines */ sc->nrdre = NRDRE; sc->ntdre = NTDRE; /* Extract MAC address from PROM */ for (i = 0; i < ETHER_ADDR_LEN; i++) sc->arpcom.ac_enaddr[i] = inb(iobase + i); if (lnc_attach_sc(sc, unit) == 0) { free(sc, M_DEVBUF); sc = NULL; } } else { free(sc, M_DEVBUF); sc = NULL; } } return sc; } #endif static void lnc_init(xsc) void *xsc; { struct lnc_softc *sc = xsc; int s, i; char *lnc_mem; /* Check that interface has valid address */ if (TAILQ_EMPTY(&sc->arpcom.ac_if.if_addrhead)) /* XXX unlikely */ return; /* Shut down interface */ s = splimp(); lnc_stop(sc); sc->arpcom.ac_if.if_flags |= IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; /* XXX??? */ /* * This sets up the memory area for the controller. Memory is set up for * the initialisation block (12 words of contiguous memory starting * on a word boundary),the transmit and receive ring structures (each * entry is 4 words long and must start on a quadword boundary) and * the data buffers. * * The alignment tests are particularly paranoid. */ sc->recv_next = 0; sc->trans_ring = sc->recv_ring + NDESC(sc->nrdre); sc->trans_next = 0; if (sc->nic.mem_mode == SHMEM) lnc_mem = (char *) sc->nic.iobase; else lnc_mem = (char *) (sc->trans_ring + NDESC(sc->ntdre)); lnc_mem = (char *)(((int)lnc_mem + 1) & ~1); sc->init_block = (struct init_block *) ((int) lnc_mem & ~1); lnc_mem = (char *) (sc->init_block + 1); lnc_mem = (char *)(((int)lnc_mem + 7) & ~7); /* Initialise pointers to descriptor entries */ for (i = 0; i < NDESC(sc->nrdre); i++) { (sc->recv_ring + i)->md = (struct mds *) lnc_mem; lnc_mem += sizeof(struct mds); } for (i = 0; i < NDESC(sc->ntdre); i++) { (sc->trans_ring + i)->md = (struct mds *) lnc_mem; lnc_mem += sizeof(struct mds); } /* Initialise the remaining ring entries */ if (sc->nic.mem_mode == DMA_MBUF) { sc->mbufs = 0; sc->mbuf_count = 0; /* Free previously allocated mbufs */ if (sc->flags & LNC_INITIALISED) lnc_free_mbufs(sc); for (i = 0; i < NDESC(sc->nrdre); i++) { if (alloc_mbuf_cluster(sc, sc->recv_ring+i)) { log(LOG_ERR, "Initialisation failed -- no mbufs\n"); splx(s); return; } } for (i = 0; i < NDESC(sc->ntdre); i++) { (sc->trans_ring + i)->buff.mbuf = 0; (sc->trans_ring + i)->md->md0 = 0; (sc->trans_ring + i)->md->md1 = 0; (sc->trans_ring + i)->md->md2 = 0; (sc->trans_ring + i)->md->md3 = 0; } } else { for (i = 0; i < NDESC(sc->nrdre); i++) { (sc->recv_ring + i)->md->md0 = kvtop(lnc_mem); (sc->recv_ring + i)->md->md1 = ((kvtop(lnc_mem) >> 16) & 0xff) | OWN; (sc->recv_ring + i)->md->md2 = -RECVBUFSIZE; (sc->recv_ring + i)->md->md3 = 0; (sc->recv_ring + i)->buff.data = lnc_mem; lnc_mem += RECVBUFSIZE; } for (i = 0; i < NDESC(sc->ntdre); i++) { (sc->trans_ring + i)->md->md0 = kvtop(lnc_mem); (sc->trans_ring + i)->md->md1 = ((kvtop(lnc_mem) >> 16) & 0xff); (sc->trans_ring + i)->md->md2 = 0; (sc->trans_ring + i)->md->md3 = 0; (sc->trans_ring + i)->buff.data = lnc_mem; lnc_mem += TRANSBUFSIZE; } } sc->next_to_send = 0; /* Set up initialisation block */ sc->init_block->mode = sc->nic.mode; for (i = 0; i < ETHER_ADDR_LEN; i++) sc->init_block->padr[i] = sc->arpcom.ac_enaddr[i]; lnc_setladrf(sc); sc->init_block->rdra = kvtop(sc->recv_ring->md); sc->init_block->rlen = ((kvtop(sc->recv_ring->md) >> 16) & 0xff) | (sc->nrdre << 13); sc->init_block->tdra = kvtop(sc->trans_ring->md); sc->init_block->tlen = ((kvtop(sc->trans_ring->md) >> 16) & 0xff) | (sc->ntdre << 13); /* Set flags to show that the memory area is valid */ sc->flags |= LNC_INITIALISED; sc->pending_transmits = 0; /* Give the LANCE the physical address of the initialisation block */ if (sc->nic.ic == PCnet_Home) { u_short media; /* Set PHY_SEL to HomeRun */ media = read_bcr(sc, BCR49); media &= ~3; media |= 1; write_bcr(sc, BCR49, media); } write_csr(sc, CSR1, kvtop(sc->init_block)); write_csr(sc, CSR2, (kvtop(sc->init_block) >> 16) & 0xff); /* * Depending on which controller this is, CSR3 has different meanings. * For the Am7990 it controls DMA operations, for the Am79C960 it * controls interrupt masks and transmitter algorithms. In either * case, none of the flags are set. * */ write_csr(sc, CSR3, 0); /* Let's see if it starts */ write_csr(sc, CSR0, INIT); for (i = 0; i < 1000; i++) if (read_csr(sc, CSR0) & IDON) break; /* * Now that the initialisation is complete there's no reason to * access anything except CSR0, so we leave RAP pointing there * so we can just access RDP from now on, saving an outw each * time. */ if (read_csr(sc, CSR0) & IDON) { /* * Enable interrupts, start the LANCE, mark the interface as * running and transmit any pending packets. */ write_csr(sc, CSR0, STRT | INEA); sc->arpcom.ac_if.if_flags |= IFF_RUNNING; sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE; lnc_start(&sc->arpcom.ac_if); } else log(LOG_ERR, "lnc%d: Initialisation failed\n", sc->arpcom.ac_if.if_unit); splx(s); } /* * The interrupt flag (INTR) will be set and provided that the interrupt enable * flag (INEA) is also set, the interrupt pin will be driven low when any of * the following occur: * * 1) Completion of the initialisation routine (IDON). 2) The reception of a * packet (RINT). 3) The transmission of a packet (TINT). 4) A transmitter * timeout error (BABL). 5) A missed packet (MISS). 6) A memory error (MERR). * * The interrupt flag is cleared when all of the above conditions are cleared. * * If the driver is reset from this routine then it first checks to see if any * interrupts have ocurred since the reset and handles them before returning. * This is because the NIC may signify a pending interrupt in CSR0 using the * INTR flag even if a hardware interrupt is currently inhibited (at least I * think it does from reading the data sheets). We may as well deal with * these pending interrupts now rather than get the overhead of another * hardware interrupt immediately upon returning from the interrupt handler. * */ void lncintr_sc(struct lnc_softc *sc) { int unit = sc->arpcom.ac_if.if_unit; u_short csr0; /* * INEA is the only bit that can be cleared by writing a 0 to it so * we have to include it in any writes that clear other flags. */ while ((csr0 = inw(sc->rdp)) & INTR) { /* * Clear interrupt flags early to avoid race conditions. The * controller can still set these flags even while we're in * this interrupt routine. If the flag is still set from the * event that caused this interrupt any new events will * be missed. */ /* outw(sc->rdp, IDON | CERR | BABL | MISS | MERR | RINT | TINT | INEA); */ outw(sc->rdp, csr0); /* We don't do anything with the IDON flag */ if (csr0 & ERR) { if (csr0 & CERR) { log(LOG_ERR, "lnc%d: Heartbeat error -- SQE test failed\n", unit); LNCSTATS(cerr) } if (csr0 & BABL) { log(LOG_ERR, "lnc%d: Babble error - more than 1519 bytes transmitted\n", unit); LNCSTATS(babl) sc->arpcom.ac_if.if_oerrors++; } if (csr0 & MISS) { log(LOG_ERR, "lnc%d: Missed packet -- no receive buffer\n", unit); LNCSTATS(miss) sc->arpcom.ac_if.if_ierrors++; } if (csr0 & MERR) { log(LOG_ERR, "lnc%d: Memory error -- Resetting\n", unit); LNCSTATS(merr) lnc_reset(sc); continue; } } if (csr0 & RINT) { LNCSTATS(rint) lnc_rint(sc); } if (csr0 & TINT) { LNCSTATS(tint) sc->arpcom.ac_if.if_timer = 0; lnc_tint(sc); } /* * If there's room in the transmit descriptor ring then queue * some more transmit packets. */ if (!(sc->arpcom.ac_if.if_flags & IFF_OACTIVE)) lnc_start(&sc->arpcom.ac_if); } } static void lncintr(int unit) { struct lnc_softc *sc = &lnc_softc[unit]; lncintr_sc (sc); } static __inline int mbuf_to_buffer(struct mbuf *m, char *buffer) { int len=0; for( ; m; m = m->m_next) { bcopy(mtod(m, caddr_t), buffer, m->m_len); buffer += m->m_len; len += m->m_len; } return(len); } static __inline struct mbuf * chain_to_cluster(struct mbuf *m) { struct mbuf *new; MGET(new, M_DONTWAIT, MT_DATA); if (new) { MCLGET(new, M_DONTWAIT); if (new->m_ext.ext_buf) { new->m_len = mbuf_to_buffer(m, new->m_data); m_freem(m); return(new); } else m_free(new); } return(0); } /* * IFF_OACTIVE and IFF_RUNNING are checked in ether_output so it's redundant * to check them again since we wouldn't have got here if they were not * appropriately set. This is also called from lnc_init and lncintr but the * flags should be ok at those points too. */ static void lnc_start(struct ifnet *ifp) { struct lnc_softc *sc = ifp->if_softc; struct host_ring_entry *desc; int tmp; int end_of_packet; struct mbuf *head, *m; int len, chunk; int addr; int no_entries_needed; do { IF_DEQUEUE(&sc->arpcom.ac_if.if_snd, head); if (!head) return; if (sc->nic.mem_mode == DMA_MBUF) { no_entries_needed = 0; for (m=head; m; m = m->m_next) no_entries_needed++; /* * We try and avoid bcopy as much as possible * but there are two cases when we use it. * * 1) If there are not enough free entries in the ring * to hold each mbuf in the chain then compact the * chain into a single cluster. * * 2) The Am7990 and Am79C90 must not have less than * 100 bytes in the first descriptor of a chained * packet so it's necessary to shuffle the mbuf * contents to ensure this. */ if (no_entries_needed > (NDESC(sc->ntdre) - sc->pending_transmits)) { if (!(head = chain_to_cluster(head))) { log(LOG_ERR, "lnc%d: Couldn't get mbuf for transmit packet -- Resetting \n ",ifp->if_unit); lnc_reset(sc); return; } } else if ((sc->nic.ic == LANCE) || (sc->nic.ic == C_LANCE)) { if ((head->m_len < 100) && (head->m_next)) { len = 100 - head->m_len; if (M_TRAILINGSPACE(head) < len) { /* * Move data to start of data * area. We assume the first * mbuf has a packet header * and is not a cluster. */ bcopy((caddr_t)head->m_data, (caddr_t)head->m_pktdat, head->m_len); head->m_data = head->m_pktdat; } m = head->m_next; while (m && (len > 0)) { chunk = min(len, m->m_len); bcopy(mtod(m, caddr_t), mtod(head, caddr_t) + head->m_len, chunk); len -= chunk; head->m_len += chunk; m->m_len -= chunk; m->m_data += chunk; if (m->m_len <= 0) { MFREE(m, head->m_next); m = head->m_next; } } } } tmp = sc->next_to_send; /* * On entering this loop we know that tmp points to a * descriptor with a clear OWN bit. */ desc = sc->trans_ring + tmp; len = ETHER_MIN_LEN; for (m = head; m; m = m->m_next) { desc->buff.mbuf = m; addr = kvtop(m->m_data); desc->md->md0 = addr; desc->md->md1 = ((addr >> 16) & 0xff); desc->md->md3 = 0; desc->md->md2 = -m->m_len; sc->pending_transmits++; len -= m->m_len; INC_MD_PTR(tmp, sc->ntdre) desc = sc->trans_ring + tmp; } end_of_packet = tmp; DEC_MD_PTR(tmp, sc->ntdre) desc = sc->trans_ring + tmp; desc->md->md1 |= ENP; if (len > 0) desc->md->md2 -= len; /* * Set OWN bits in reverse order, otherwise the Lance * could start sending the packet before all the * buffers have been relinquished by the host. */ while (tmp != sc->next_to_send) { desc->md->md1 |= OWN; DEC_MD_PTR(tmp, sc->ntdre) desc = sc->trans_ring + tmp; } sc->next_to_send = end_of_packet; desc->md->md1 |= STP | OWN; } else { sc->pending_transmits++; desc = sc->trans_ring + sc->next_to_send; len = mbuf_to_buffer(head, desc->buff.data); desc->md->md3 = 0; desc->md->md2 = -max(len, ETHER_MIN_LEN - ETHER_CRC_LEN); desc->md->md1 |= OWN | STP | ENP; INC_MD_PTR(sc->next_to_send, sc->ntdre) } /* Force an immediate poll of the transmit ring */ outw(sc->rdp, TDMD | INEA); /* * Set a timer so if the buggy Am7990.h shuts * down we can wake it up. */ ifp->if_timer = 2; if (sc->arpcom.ac_if.if_bpf) bpf_mtap(&sc->arpcom.ac_if, head); if (sc->nic.mem_mode != DMA_MBUF) m_freem(head); } while (sc->pending_transmits < NDESC(sc->ntdre)); /* * Transmit ring is full so set IFF_OACTIVE * since we can't buffer any more packets. */ sc->arpcom.ac_if.if_flags |= IFF_OACTIVE; LNCSTATS(trans_ring_full) } static int lnc_ioctl(struct ifnet * ifp, u_long command, caddr_t data) { struct lnc_softc *sc = ifp->if_softc; int s, error = 0; s = splimp(); switch (command) { case SIOCSIFADDR: case SIOCGIFADDR: case SIOCSIFMTU: error = ether_ioctl(ifp, command, data); break; case SIOCSIFFLAGS: #ifdef DEBUG if (ifp->if_flags & IFF_DEBUG) sc->lnc_debug = 1; else sc->lnc_debug = 0; #endif if (ifp->if_flags & IFF_PROMISC) { if (!(sc->nic.mode & PROM)) { sc->nic.mode |= PROM; lnc_init(sc); } } else if (sc->nic.mode & PROM) { sc->nic.mode &= ~PROM; lnc_init(sc); } if ((ifp->if_flags & IFF_ALLMULTI) && !(sc->flags & LNC_ALLMULTI)) { sc->flags |= LNC_ALLMULTI; lnc_init(sc); } else if (!(ifp->if_flags & IFF_ALLMULTI) && (sc->flags & LNC_ALLMULTI)) { sc->flags &= ~LNC_ALLMULTI; lnc_init(sc); } if ((ifp->if_flags & IFF_UP) == 0 && (ifp->if_flags & IFF_RUNNING) != 0) { /* * If interface is marked down and it is running, * then stop it. */ lnc_stop(sc); ifp->if_flags &= ~IFF_RUNNING; } else if ((ifp->if_flags & IFF_UP) != 0 && (ifp->if_flags & IFF_RUNNING) == 0) { /* * If interface is marked up and it is stopped, then * start it. */ lnc_init(sc); } break; case SIOCADDMULTI: case SIOCDELMULTI: lnc_init(sc); error = 0; break; default: error = EINVAL; } (void) splx(s); return error; } static void lnc_watchdog(struct ifnet *ifp) { log(LOG_ERR, "lnc%d: Device timeout -- Resetting\n", ifp->if_unit); ifp->if_oerrors++; lnc_reset(ifp->if_softc); } #ifdef DEBUG void lnc_dump_state(struct lnc_softc *sc) { int i; printf("\nDriver/NIC [%d] state dump\n", sc->arpcom.ac_if.if_unit); printf("Memory access mode: %b\n", sc->nic.mem_mode, MEM_MODES); printf("Host memory\n"); printf("-----------\n"); printf("Receive ring: base = %p, next = %p\n", (void *)sc->recv_ring, (void *)(sc->recv_ring + sc->recv_next)); for (i = 0; i < NDESC(sc->nrdre); i++) printf("\t%d:%p md = %p buff = %p\n", i, (void *)(sc->recv_ring + i), (void *)(sc->recv_ring + i)->md, (void *)(sc->recv_ring + i)->buff.data); printf("Transmit ring: base = %p, next = %p\n", (void *)sc->trans_ring, (void *)(sc->trans_ring + sc->trans_next)); for (i = 0; i < NDESC(sc->ntdre); i++) printf("\t%d:%p md = %p buff = %p\n", i, (void *)(sc->trans_ring + i), (void *)(sc->trans_ring + i)->md, (void *)(sc->trans_ring + i)->buff.data); printf("Lance memory (may be on host(DMA) or card(SHMEM))\n"); printf("Init block = %p\n", (void *)sc->init_block); printf("\tmode = %b rlen:rdra = %x:%x tlen:tdra = %x:%x\n", sc->init_block->mode, INIT_MODE, sc->init_block->rlen, sc->init_block->rdra, sc->init_block->tlen, sc->init_block->tdra); printf("Receive descriptor ring\n"); for (i = 0; i < NDESC(sc->nrdre); i++) printf("\t%d buffer = 0x%x%x, BCNT = %d,\tMCNT = %u,\tflags = %b\n", i, ((sc->recv_ring + i)->md->md1 & HADR), (sc->recv_ring + i)->md->md0, -(short) (sc->recv_ring + i)->md->md2, (sc->recv_ring + i)->md->md3, (((sc->recv_ring + i)->md->md1 & ~HADR) >> 8), RECV_MD1); printf("Transmit descriptor ring\n"); for (i = 0; i < NDESC(sc->ntdre); i++) printf("\t%d buffer = 0x%x%x, BCNT = %d,\tflags = %b %b\n", i, ((sc->trans_ring + i)->md->md1 & HADR), (sc->trans_ring + i)->md->md0, -(short) (sc->trans_ring + i)->md->md2, ((sc->trans_ring + i)->md->md1 >> 8), TRANS_MD1, ((sc->trans_ring + i)->md->md3 >> 10), TRANS_MD3); printf("\nnext_to_send = %x\n", sc->next_to_send); printf("\n CSR0 = %b CSR1 = %x CSR2 = %x CSR3 = %x\n\n", read_csr(sc, CSR0), CSR0_FLAGS, read_csr(sc, CSR1), read_csr(sc, CSR2), read_csr(sc, CSR3)); /* Set RAP back to CSR0 */ outw(sc->rap, CSR0); } void mbuf_dump_chain(struct mbuf * m) { #define MBUF_FLAGS \ "\20\1M_EXT\2M_PKTHDR\3M_EOR\4UNKNOWN\5M_BCAST\6M_MCAST" if (!m) log(LOG_DEBUG, "m == NULL\n"); do { log(LOG_DEBUG, "m = %p\n", (void *)m); log(LOG_DEBUG, "m_hdr.mh_next = %p\n", (void *)m->m_hdr.mh_next); log(LOG_DEBUG, "m_hdr.mh_nextpkt = %p\n", (void *)m->m_hdr.mh_nextpkt); log(LOG_DEBUG, "m_hdr.mh_len = %d\n", m->m_hdr.mh_len); log(LOG_DEBUG, "m_hdr.mh_data = %p\n", (void *)m->m_hdr.mh_data); log(LOG_DEBUG, "m_hdr.mh_type = %d\n", m->m_hdr.mh_type); log(LOG_DEBUG, "m_hdr.mh_flags = %b\n", m->m_hdr.mh_flags, MBUF_FLAGS); if (!(m->m_hdr.mh_flags & (M_PKTHDR | M_EXT))) log(LOG_DEBUG, "M_dat.M_databuf = %p\n", (void *)m->M_dat.M_databuf); else { if (m->m_hdr.mh_flags & M_PKTHDR) { log(LOG_DEBUG, "M_dat.MH.MH_pkthdr.len = %d\n", m->M_dat.MH.MH_pkthdr.len); log(LOG_DEBUG, "M_dat.MH.MH_pkthdr.rcvif = %p\n", (void *)m->M_dat.MH.MH_pkthdr.rcvif); if (!(m->m_hdr.mh_flags & M_EXT)) log(LOG_DEBUG, "M_dat.MH.MH_dat.MH_databuf = %p\n", (void *)m->M_dat.MH.MH_dat.MH_databuf); } if (m->m_hdr.mh_flags & M_EXT) { log(LOG_DEBUG, "M_dat.MH.MH_dat.MH_ext.ext_buff %p\n", (void *)m->M_dat.MH.MH_dat.MH_ext.ext_buf); log(LOG_DEBUG, "M_dat.MH.MH_dat.MH_ext.ext_free %p\n", (void *)m->M_dat.MH.MH_dat.MH_ext.ext_free); log(LOG_DEBUG, "M_dat.MH.MH_dat.MH_ext.ext_size %d\n", m->M_dat.MH.MH_dat.MH_ext.ext_size); } } } while ((m = m->m_next) != NULL); } #endif - -#endif diff --git a/sys/dev/mcd/mcd.c b/sys/dev/mcd/mcd.c index e4da168e3807..923ec0f8fc74 100644 --- a/sys/dev/mcd/mcd.c +++ b/sys/dev/mcd/mcd.c @@ -1,1836 +1,1833 @@ /* * Copyright 1993 by Holger Veit (data part) * Copyright 1993 by Brian Moore (audio part) * Changes Copyright 1993 by Gary Clark II * Changes Copyright (C) 1994-1995 by Andrey A. Chernov, Moscow, Russia * * Rewrote probe routine to work on newer Mitsumi drives. * Additional changes (C) 1994 by Jordan K. Hubbard * * All rights reserved. * * 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 * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, 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 software was developed by Holger Veit and Brian Moore * for use with "386BSD" and similar operating systems. * "Similar operating systems" includes mainly non-profit oriented * systems for research and education, including but not restricted to * "NetBSD", "FreeBSD", "Mach" (by CMU). * 4. Neither the name of the developer(s) nor the name "386BSD" * may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER(S) ``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 DEVELOPER(S) BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ static const char COPYRIGHT[] = "mcd-driver (C)1993 by H.Veit & B.Moore"; #include "mcd.h" -#if NMCD > 0 #include #include #include #include #include #include #include #include #include #include #include #define MCD_TRACE(format, args...) \ { \ if (mcd_data[unit].debug) { \ printf("mcd%d: status=0x%02x: ", \ unit, mcd_data[unit].status); \ printf(format, ## args); \ } \ } #define mcd_part(dev) ((minor(dev)) & 7) #define mcd_unit(dev) (((minor(dev)) & 0x38) >> 3) #define mcd_phys(dev) (((minor(dev)) & 0x40) >> 6) #define RAW_PART 2 /* flags */ #define MCDVALID 0x0001 /* parameters loaded */ #define MCDINIT 0x0002 /* device is init'd */ #define MCDNEWMODEL 0x0004 /* device is new model */ #define MCDLABEL 0x0008 /* label is read */ #define MCDPROBING 0x0010 /* probing */ #define MCDREADRAW 0x0020 /* read raw mode (2352 bytes) */ #define MCDVOLINFO 0x0040 /* already read volinfo */ #define MCDTOC 0x0080 /* already read toc */ #define MCDMBXBSY 0x0100 /* local mbx is busy */ /* status */ #define MCDAUDIOBSY MCD_ST_AUDIOBSY /* playing audio */ #define MCDDSKCHNG MCD_ST_DSKCHNG /* sensed change of disk */ #define MCDDSKIN MCD_ST_DSKIN /* sensed disk in drive */ #define MCDDOOROPEN MCD_ST_DOOROPEN /* sensed door open */ /* These are apparently the different states a mitsumi can get up to */ #define MCDCDABSENT 0x0030 #define MCDCDPRESENT 0x0020 #define MCDSCLOSED 0x0080 #define MCDSOPEN 0x00a0 #define MCD_MD_UNKNOWN (-1) /* toc */ #define MCD_MAXTOCS 104 /* from the Linux driver */ #define MCD_LASTPLUS1 170 /* special toc entry */ #define MCD_TYPE_UNKNOWN 0 #define MCD_TYPE_LU002S 1 #define MCD_TYPE_LU005S 2 #define MCD_TYPE_LU006S 3 #define MCD_TYPE_FX001 4 #define MCD_TYPE_FX001D 5 struct mcd_mbx { short unit; short port; short retry; short nblk; int sz; u_long skip; struct buf *bp; int p_offset; short count; short mode; }; static struct mcd_data { short type; char *name; short config; short flags; u_char read_command; short status; int blksize; u_long disksize; int iobase; struct disklabel dlabel; int partflags[MAXPARTITIONS]; int openflags; struct mcd_volinfo volinfo; struct mcd_qchninfo toc[MCD_MAXTOCS]; short audio_status; short curr_mode; struct mcd_read2 lastpb; short debug; struct buf_queue_head head; /* head of buf queue */ struct mcd_mbx mbx; } mcd_data[NMCD]; /* reader state machine */ #define MCD_S_BEGIN 0 #define MCD_S_BEGIN1 1 #define MCD_S_WAITSTAT 2 #define MCD_S_WAITMODE 3 #define MCD_S_WAITREAD 4 /* prototypes */ static void mcd_start(int unit); static int mcd_getdisklabel(int unit); #ifdef NOTYET static void mcd_configure(struct mcd_data *cd); #endif static int mcd_get(int unit, char *buf, int nmax); static int mcd_setflags(int unit,struct mcd_data *cd); static int mcd_getstat(int unit,int sflg); static int mcd_send(int unit, int cmd,int nretrys); static void hsg2msf(int hsg, bcd_t *msf); static int msf2hsg(bcd_t *msf, int relative); static int mcd_volinfo(int unit); static ointhand2_t mcdintr; static int mcd_waitrdy(int port,int dly); static timeout_t mcd_timeout; static void mcd_doread(int state, struct mcd_mbx *mbxin); static void mcd_soft_reset(int unit); static int mcd_hard_reset(int unit); static int mcd_setmode(int unit, int mode); static int mcd_getqchan(int unit, struct mcd_qchninfo *q); static int mcd_subchan(int unit, struct ioc_read_subchannel *sc); static int mcd_toc_header(int unit, struct ioc_toc_header *th); static int mcd_read_toc(int unit); static int mcd_toc_entrys(int unit, struct ioc_read_toc_entry *te); #if 0 static int mcd_toc_entry(int unit, struct ioc_read_toc_single_entry *te); #endif static int mcd_stop(int unit); static int mcd_eject(int unit); static int mcd_inject(int unit); static int mcd_playtracks(int unit, struct ioc_play_track *pt); static int mcd_play(int unit, struct mcd_read2 *pb); static int mcd_playmsf(int unit, struct ioc_play_msf *pt); static int mcd_playblocks(int unit, struct ioc_play_blocks *); static int mcd_pause(int unit); static int mcd_resume(int unit); static int mcd_lock_door(int unit, int lock); static int mcd_close_tray(int unit); static int mcd_probe(struct isa_device *dev); static int mcd_attach(struct isa_device *dev); struct isa_driver mcddriver = { mcd_probe, mcd_attach, "mcd" }; static d_open_t mcdopen; static d_close_t mcdclose; static d_ioctl_t mcdioctl; static d_psize_t mcdsize; static d_strategy_t mcdstrategy; #define CDEV_MAJOR 29 #define BDEV_MAJOR 7 static struct cdevsw mcd_cdevsw = { /* open */ mcdopen, /* close */ mcdclose, /* read */ physread, /* write */ nowrite, /* ioctl */ mcdioctl, /* poll */ nopoll, /* mmap */ nommap, /* strategy */ mcdstrategy, /* name */ "mcd", /* maj */ CDEV_MAJOR, /* dump */ nodump, /* psize */ nopsize, /* flags */ D_DISK, /* bmaj */ BDEV_MAJOR }; #define mcd_put(port,byte) outb(port,byte) #define MCD_RETRYS 5 #define MCD_RDRETRYS 8 #define CLOSE_TRAY_SECS 8 #define DISK_SENSE_SECS 3 #define WAIT_FRAC 4 /* several delays */ #define RDELAY_WAITSTAT 300 #define RDELAY_WAITMODE 300 #define RDELAY_WAITREAD 800 #define MIN_DELAY 15 #define DELAY_GETREPLY 5000000 int mcd_attach(struct isa_device *dev) { int unit = dev->id_unit; struct mcd_data *cd = mcd_data + unit; dev->id_ointr = mcdintr; cd->iobase = dev->id_iobase; cd->flags |= MCDINIT; mcd_soft_reset(unit); bufq_init(&cd->head); #ifdef NOTYET /* wire controller for interrupts and dma */ mcd_configure(cd); #endif /* name filled in probe */ make_dev(&mcd_cdevsw, dkmakeminor(unit, 0, 0), UID_ROOT, GID_OPERATOR, 0640, "rmcd%da", unit); make_dev(&mcd_cdevsw, dkmakeminor(unit, 0, RAW_PART), UID_ROOT, GID_OPERATOR, 0640, "rmcd%dc", unit); make_dev(&mcd_cdevsw, dkmakeminor(unit, 0, 0), UID_ROOT, GID_OPERATOR, 0640, "mcd%da", unit); make_dev(&mcd_cdevsw, dkmakeminor(unit, 0, RAW_PART), UID_ROOT, GID_OPERATOR, 0640, "mcd%dc", unit); return 1; } int mcdopen(dev_t dev, int flags, int fmt, struct proc *p) { int unit,part,phys,r,retry; struct mcd_data *cd; unit = mcd_unit(dev); if (unit >= NMCD) return ENXIO; cd = mcd_data + unit; part = mcd_part(dev); phys = mcd_phys(dev); /* not initialized*/ if (!(cd->flags & MCDINIT)) return ENXIO; /* invalidated in the meantime? mark all open part's invalid */ if (!(cd->flags & MCDVALID) && cd->openflags) return ENXIO; if (mcd_getstat(unit,1) == -1) return EIO; if ( (cd->status & (MCDDSKCHNG|MCDDOOROPEN)) || !(cd->status & MCDDSKIN)) for (retry = 0; retry < DISK_SENSE_SECS * WAIT_FRAC; retry++) { (void) tsleep((caddr_t)cd, PSOCK | PCATCH, "mcdsn1", hz/WAIT_FRAC); if ((r = mcd_getstat(unit,1)) == -1) return EIO; if (r != -2) break; } if (( (cd->status & (MCDDOOROPEN|MCDDSKCHNG)) || !(cd->status & MCDDSKIN) ) && major(dev) == CDEV_MAJOR && part == RAW_PART ) { cd->openflags |= (1<partflags[part] |= MCDREADRAW; return 0; } if (cd->status & MCDDOOROPEN) { printf("mcd%d: door is open\n", unit); return ENXIO; } if (!(cd->status & MCDDSKIN)) { printf("mcd%d: no CD inside\n", unit); return ENXIO; } if (cd->status & MCDDSKCHNG) { printf("mcd%d: CD not sensed\n", unit); return ENXIO; } if (mcdsize(dev) < 0) { if (major(dev) == CDEV_MAJOR && part == RAW_PART) { cd->openflags |= (1<partflags[part] |= MCDREADRAW; return 0; } printf("mcd%d: failed to get disk size\n",unit); return ENXIO; } else cd->flags |= MCDVALID; /* XXX get a default disklabel */ mcd_getdisklabel(unit); MCD_TRACE("open: partition=%d, disksize = %ld, blksize=%d\n", part, cd->disksize, cd->blksize); dev->si_bsize_phys = cd->blksize; if (part == RAW_PART || (part < cd->dlabel.d_npartitions && cd->dlabel.d_partitions[part].p_fstype != FS_UNUSED)) { cd->openflags |= (1<partflags[part] |= MCDREADRAW; (void) mcd_lock_door(unit, MCD_LK_LOCK); if (!(cd->flags & MCDVALID)) return ENXIO; return 0; } return ENXIO; } int mcdclose(dev_t dev, int flags, int fmt, struct proc *p) { int unit,part; struct mcd_data *cd; unit = mcd_unit(dev); if (unit >= NMCD) return ENXIO; cd = mcd_data + unit; part = mcd_part(dev); if (!(cd->flags & MCDINIT) || !(cd->openflags & (1<openflags &= ~(1<partflags[part] &= ~MCDREADRAW; return 0; } void mcdstrategy(struct buf *bp) { struct mcd_data *cd; int s; int unit = mcd_unit(bp->b_dev); cd = mcd_data + unit; /* test validity */ /*MCD_TRACE("strategy: buf=0x%lx, unit=%ld, block#=%ld bcount=%ld\n", bp,unit,bp->b_blkno,bp->b_bcount);*/ if (unit >= NMCD || bp->b_blkno < 0) { printf("mcdstrategy: unit = %d, blkno = %ld, bcount = %ld\n", unit, (long)bp->b_blkno, bp->b_bcount); printf("mcd: mcdstratregy failure"); bp->b_error = EINVAL; bp->b_flags |= B_ERROR; goto bad; } /* if device invalidated (e.g. media change, door open), error */ if (!(cd->flags & MCDVALID)) { MCD_TRACE("strategy: drive not valid\n"); bp->b_error = EIO; goto bad; } /* read only */ if (!(bp->b_flags & B_READ)) { bp->b_error = EROFS; goto bad; } /* no data to read */ if (bp->b_bcount == 0) goto done; /* for non raw access, check partition limits */ if (mcd_part(bp->b_dev) != RAW_PART) { if (!(cd->flags & MCDLABEL)) { bp->b_error = EIO; goto bad; } /* adjust transfer if necessary */ if (bounds_check_with_label(bp,&cd->dlabel,1) <= 0) { goto done; } } else { bp->b_pblkno = bp->b_blkno; bp->b_resid = 0; } /* queue it */ s = splbio(); bufqdisksort(&cd->head, bp); splx(s); /* now check whether we can perform processing */ mcd_start(unit); return; bad: bp->b_flags |= B_ERROR; done: bp->b_resid = bp->b_bcount; biodone(bp); return; } static void mcd_start(int unit) { struct mcd_data *cd = mcd_data + unit; struct partition *p; struct buf *bp; int s = splbio(); if (cd->flags & MCDMBXBSY) { splx(s); return; } bp = bufq_first(&cd->head); if (bp != 0) { /* block found to process, dequeue */ /*MCD_TRACE("mcd_start: found block bp=0x%x\n",bp,0,0,0);*/ bufq_remove(&cd->head, bp); splx(s); } else { /* nothing to do */ splx(s); return; } /* changed media? */ if (!(cd->flags & MCDVALID)) { MCD_TRACE("mcd_start: drive not valid\n"); return; } p = cd->dlabel.d_partitions + mcd_part(bp->b_dev); cd->flags |= MCDMBXBSY; if (cd->partflags[mcd_part(bp->b_dev)] & MCDREADRAW) cd->flags |= MCDREADRAW; cd->mbx.unit = unit; cd->mbx.port = cd->iobase; cd->mbx.retry = MCD_RETRYS; cd->mbx.bp = bp; cd->mbx.p_offset = p->p_offset; /* calling the read routine */ mcd_doread(MCD_S_BEGIN,&(cd->mbx)); /* triggers mcd_start, when successful finished */ return; } int mcdioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) { struct mcd_data *cd; int unit,part,retry,r; unit = mcd_unit(dev); part = mcd_part(dev); cd = mcd_data + unit; if (mcd_getstat(unit, 1) == -1) /* detect disk change too */ return EIO; MCD_TRACE("ioctl called 0x%lx\n", cmd); switch (cmd) { case CDIOCSETPATCH: case CDIOCGETVOL: case CDIOCSETVOL: case CDIOCSETMONO: case CDIOCSETSTERIO: case CDIOCSETMUTE: case CDIOCSETLEFT: case CDIOCSETRIGHT: return EINVAL; case CDIOCEJECT: return mcd_eject(unit); case CDIOCSETDEBUG: cd->debug = 1; return 0; case CDIOCCLRDEBUG: cd->debug = 0; return 0; case CDIOCRESET: return mcd_hard_reset(unit); case CDIOCALLOW: return mcd_lock_door(unit, MCD_LK_UNLOCK); case CDIOCPREVENT: return mcd_lock_door(unit, MCD_LK_LOCK); case CDIOCCLOSE: return mcd_inject(unit); } if (!(cd->flags & MCDVALID)) { if ( major(dev) != CDEV_MAJOR || part != RAW_PART || !(cd->openflags & (1<status & (MCDDSKCHNG|MCDDOOROPEN)) || !(cd->status & MCDDSKIN)) for (retry = 0; retry < DISK_SENSE_SECS * WAIT_FRAC; retry++) { (void) tsleep((caddr_t)cd, PSOCK | PCATCH, "mcdsn2", hz/WAIT_FRAC); if ((r = mcd_getstat(unit,1)) == -1) return EIO; if (r != -2) break; } if ( (cd->status & (MCDDOOROPEN|MCDDSKCHNG)) || !(cd->status & MCDDSKIN) || mcdsize(dev) < 0 ) return ENXIO; cd->flags |= MCDVALID; mcd_getdisklabel(unit); if (mcd_phys(dev)) cd->partflags[part] |= MCDREADRAW; (void) mcd_lock_door(unit, MCD_LK_LOCK); if (!(cd->flags & MCDVALID)) return ENXIO; } switch (cmd) { case DIOCGDINFO: *(struct disklabel *) addr = cd->dlabel; return 0; case DIOCGPART: ((struct partinfo *) addr)->disklab = &cd->dlabel; ((struct partinfo *) addr)->part = &cd->dlabel.d_partitions[mcd_part(dev)]; return 0; /* * a bit silly, but someone might want to test something on a * section of cdrom. */ case DIOCWDINFO: case DIOCSDINFO: if ((flags & FWRITE) == 0) return EBADF; else { return setdisklabel(&cd->dlabel, (struct disklabel *) addr, 0); } case DIOCWLABEL: return EBADF; case CDIOCPLAYTRACKS: return mcd_playtracks(unit, (struct ioc_play_track *) addr); case CDIOCPLAYBLOCKS: return mcd_playblocks(unit, (struct ioc_play_blocks *) addr); case CDIOCPLAYMSF: return mcd_playmsf(unit, (struct ioc_play_msf *) addr); case CDIOCREADSUBCHANNEL: return mcd_subchan(unit, (struct ioc_read_subchannel *) addr); case CDIOREADTOCHEADER: return mcd_toc_header(unit, (struct ioc_toc_header *) addr); case CDIOREADTOCENTRYS: return mcd_toc_entrys(unit, (struct ioc_read_toc_entry *) addr); case CDIOCRESUME: return mcd_resume(unit); case CDIOCPAUSE: return mcd_pause(unit); case CDIOCSTART: if (mcd_setmode(unit, MCD_MD_COOKED) != 0) return EIO; return 0; case CDIOCSTOP: return mcd_stop(unit); default: return ENOTTY; } /*NOTREACHED*/ } /* this could have been taken from scsi/cd.c, but it is not clear * whether the scsi cd driver is linked in */ static int mcd_getdisklabel(int unit) { struct mcd_data *cd = mcd_data + unit; if (cd->flags & MCDLABEL) return -1; bzero(&cd->dlabel,sizeof(struct disklabel)); /* filled with spaces first */ strncpy(cd->dlabel.d_typename," ", sizeof(cd->dlabel.d_typename)); strncpy(cd->dlabel.d_typename, cd->name, min(strlen(cd->name), sizeof(cd->dlabel.d_typename) - 1)); strncpy(cd->dlabel.d_packname,"unknown ", sizeof(cd->dlabel.d_packname)); cd->dlabel.d_secsize = cd->blksize; cd->dlabel.d_nsectors = 100; cd->dlabel.d_ntracks = 1; cd->dlabel.d_ncylinders = (cd->disksize/100)+1; cd->dlabel.d_secpercyl = 100; cd->dlabel.d_secperunit = cd->disksize; cd->dlabel.d_rpm = 300; cd->dlabel.d_interleave = 1; cd->dlabel.d_flags = D_REMOVABLE; cd->dlabel.d_npartitions= 1; cd->dlabel.d_partitions[0].p_offset = 0; cd->dlabel.d_partitions[0].p_size = cd->disksize; cd->dlabel.d_partitions[0].p_fstype = 9; cd->dlabel.d_magic = DISKMAGIC; cd->dlabel.d_magic2 = DISKMAGIC; cd->dlabel.d_checksum = dkcksum(&cd->dlabel); cd->flags |= MCDLABEL; return 0; } int mcdsize(dev_t dev) { int size; int unit = mcd_unit(dev); struct mcd_data *cd = mcd_data + unit; if (mcd_volinfo(unit) == 0) { cd->blksize = MCDBLK; size = msf2hsg(cd->volinfo.vol_msf, 0); cd->disksize = size * (MCDBLK/DEV_BSIZE); return 0; } return -1; } /*************************************************************** * lower level of driver starts here **************************************************************/ #ifdef NOTDEF static char irqs[] = { 0x00,0x00,0x10,0x20,0x00,0x30,0x00,0x00, 0x00,0x10,0x40,0x50,0x00,0x00,0x00,0x00 }; static char drqs[] = { 0x00,0x01,0x00,0x03,0x00,0x05,0x06,0x07, }; #endif #ifdef NOT_YET static void mcd_configure(struct mcd_data *cd) { outb(cd->iobase+mcd_config,cd->config); } #endif /* Wait for non-busy - return 0 on timeout */ static int twiddle_thumbs(int port, int unit, int count, char *whine) { int i; for (i = 0; i < count; i++) { if (!(inb(port+MCD_FLAGS) & MFL_STATUS_NOT_AVAIL)) return 1; } if (bootverbose) printf("mcd%d: timeout %s\n", unit, whine); return 0; } /* check to see if a Mitsumi CD-ROM is attached to the ISA bus */ int mcd_probe(struct isa_device *dev) { int port = dev->id_iobase; int unit = dev->id_unit; int i, j; unsigned char stbytes[3]; static int once; if (!once++) cdevsw_add(&mcd_cdevsw); mcd_data[unit].flags = MCDPROBING; #ifdef NOTDEF /* get irq/drq configuration word */ mcd_data[unit].config = irqs[dev->id_irq]; /* | drqs[dev->id_drq];*/ #else mcd_data[unit].config = 0; #endif /* send a reset */ outb(port+MCD_FLAGS, M_RESET); /* * delay awhile by getting any pending garbage (old data) and * throwing it away. */ for (i = 1000000; i != 0; i--) inb(port+MCD_FLAGS); /* Get status */ outb(port+MCD_DATA, MCD_CMDGETSTAT); if (!twiddle_thumbs(port, unit, 1000000, "getting status")) return 0; /* Timeout */ /* Get version information */ outb(port+MCD_DATA, MCD_CMDCONTINFO); for (j = 0; j < 3; j++) { if (!twiddle_thumbs(port, unit, 3000, "getting version info")) return 0; stbytes[j] = (inb(port+MCD_DATA) & 0xFF); } if (stbytes[1] == stbytes[2]) return 0; if (stbytes[2] >= 4 || stbytes[1] != 'M') { outb(port+MCD_CTRL, M_PICKLE); mcd_data[unit].flags |= MCDNEWMODEL; } mcd_data[unit].read_command = MCD_CMDSINGLESPEEDREAD; switch (stbytes[1]) { case 'M': if (stbytes[2] <= 2) { mcd_data[unit].type = MCD_TYPE_LU002S; mcd_data[unit].name = "Mitsumi LU002S"; } else if (stbytes[2] <= 5) { mcd_data[unit].type = MCD_TYPE_LU005S; mcd_data[unit].name = "Mitsumi LU005S"; } else { mcd_data[unit].type = MCD_TYPE_LU006S; mcd_data[unit].name = "Mitsumi LU006S"; } break; case 'F': mcd_data[unit].type = MCD_TYPE_FX001; mcd_data[unit].name = "Mitsumi FX001"; break; case 'D': mcd_data[unit].type = MCD_TYPE_FX001D; mcd_data[unit].name = "Mitsumi FX001D"; mcd_data[unit].read_command = MCD_CMDDOUBLESPEEDREAD; break; default: mcd_data[unit].type = MCD_TYPE_UNKNOWN; mcd_data[unit].name = "Mitsumi ???"; break; } printf("mcd%d: type %s, version info: %c %x\n", unit, mcd_data[unit].name, stbytes[1], stbytes[2]); return 4; } static int mcd_waitrdy(int port,int dly) { int i; /* wait until flag port senses status ready */ for (i=0; iiobase; /* wait data to become ready */ if (mcd_waitrdy(port,dly)<0) { printf("mcd%d: timeout getreply\n",unit); return -1; } /* get the data */ return inb(port+mcd_status) & 0xFF; } static int mcd_getstat(int unit,int sflg) { int i; struct mcd_data *cd = mcd_data + unit; int port = cd->iobase; /* get the status */ if (sflg) outb(port+mcd_command, MCD_CMDGETSTAT); i = mcd_getreply(unit,DELAY_GETREPLY); if (i<0 || (i & MCD_ST_CMDCHECK)) { cd->curr_mode = MCD_MD_UNKNOWN; return -1; } cd->status = i; if (mcd_setflags(unit,cd) < 0) return -2; return cd->status; } static int mcd_setflags(int unit, struct mcd_data *cd) { /* check flags */ if ( (cd->status & (MCDDSKCHNG|MCDDOOROPEN)) || !(cd->status & MCDDSKIN)) { MCD_TRACE("setflags: sensed DSKCHNG or DOOROPEN or !DSKIN\n"); mcd_soft_reset(unit); return -1; } if (cd->status & MCDAUDIOBSY) cd->audio_status = CD_AS_PLAY_IN_PROGRESS; else if (cd->audio_status == CD_AS_PLAY_IN_PROGRESS) cd->audio_status = CD_AS_PLAY_COMPLETED; return 0; } static int mcd_get(int unit, char *buf, int nmax) { int i,k; for (i=0; iflags & MCDVOLINFO) return 0; /*MCD_TRACE("mcd_volinfo: enter\n",0,0,0,0);*/ /* send volume info command */ if (mcd_send(unit,MCD_CMDGETVOLINFO,MCD_RETRYS) < 0) return EIO; /* get data */ if (mcd_get(unit,(char*) &cd->volinfo,sizeof(struct mcd_volinfo)) < 0) { printf("mcd%d: mcd_volinfo: error read data\n",unit); return EIO; } if (cd->volinfo.trk_low > 0 && cd->volinfo.trk_high >= cd->volinfo.trk_low ) { cd->flags |= MCDVOLINFO; /* volinfo is OK */ return 0; } return EINVAL; } static void mcdintr(unit) int unit; { MCD_TRACE("stray interrupt\n"); } /* state machine to process read requests * initialize with MCD_S_BEGIN: calculate sizes, and read status * MCD_S_WAITSTAT: wait for status reply, set mode * MCD_S_WAITMODE: waits for status reply from set mode, set read command * MCD_S_WAITREAD: wait for read ready, read data */ static struct mcd_mbx *mbxsave; static struct callout_handle tohandle = CALLOUT_HANDLE_INITIALIZER(&tohandle); static void mcd_timeout(void *arg) { mcd_doread((int)arg, mbxsave); } static void mcd_doread(int state, struct mcd_mbx *mbxin) { struct mcd_mbx *mbx = (state!=MCD_S_BEGIN) ? mbxsave : mbxin; int unit = mbx->unit; int port = mbx->port; int com_port = mbx->port + mcd_command; int data_port = mbx->port + mcd_rdata; struct buf *bp = mbx->bp; struct mcd_data *cd = mcd_data + unit; int rm,i,k; struct mcd_read2 rbuf; int blknum; caddr_t addr; loop: switch (state) { case MCD_S_BEGIN: mbx = mbxsave = mbxin; case MCD_S_BEGIN1: retry_status: /* get status */ outb(com_port, MCD_CMDGETSTAT); mbx->count = RDELAY_WAITSTAT; tohandle = timeout(mcd_timeout, (caddr_t)MCD_S_WAITSTAT,hz/100); /* XXX */ return; case MCD_S_WAITSTAT: untimeout(mcd_timeout,(caddr_t)MCD_S_WAITSTAT, tohandle); if (mbx->count-- >= 0) { if (inb(port+MCD_FLAGS) & MFL_STATUS_NOT_AVAIL) { timeout(mcd_timeout, (caddr_t)MCD_S_WAITSTAT,hz/100); /* XXX */ return; } cd->status = inb(port+mcd_status) & 0xFF; if (cd->status & MCD_ST_CMDCHECK) goto retry_status; if (mcd_setflags(unit,cd) < 0) goto changed; MCD_TRACE("got WAITSTAT delay=%d\n", RDELAY_WAITSTAT-mbx->count); /* reject, if audio active */ if (cd->status & MCDAUDIOBSY) { printf("mcd%d: audio is active\n",unit); goto readerr; } retry_mode: /* to check for raw/cooked mode */ if (cd->flags & MCDREADRAW) { rm = MCD_MD_RAW; mbx->sz = MCDRBLK; } else { rm = MCD_MD_COOKED; mbx->sz = cd->blksize; } if (rm == cd->curr_mode) goto modedone; mbx->count = RDELAY_WAITMODE; cd->curr_mode = MCD_MD_UNKNOWN; mbx->mode = rm; mcd_put(com_port, MCD_CMDSETMODE); mcd_put(com_port, rm); tohandle = timeout(mcd_timeout, (caddr_t)MCD_S_WAITMODE,hz/100); /* XXX */ return; } else { printf("mcd%d: timeout getstatus\n",unit); goto readerr; } case MCD_S_WAITMODE: untimeout(mcd_timeout,(caddr_t)MCD_S_WAITMODE, tohandle); if (mbx->count-- < 0) { printf("mcd%d: timeout set mode\n",unit); goto readerr; } if (inb(port+MCD_FLAGS) & MFL_STATUS_NOT_AVAIL) { tohandle = timeout(mcd_timeout, (caddr_t)MCD_S_WAITMODE,hz/100); return; } cd->status = inb(port+mcd_status) & 0xFF; if (cd->status & MCD_ST_CMDCHECK) { cd->curr_mode = MCD_MD_UNKNOWN; goto retry_mode; } if (mcd_setflags(unit,cd) < 0) goto changed; cd->curr_mode = mbx->mode; MCD_TRACE("got WAITMODE delay=%d\n", RDELAY_WAITMODE-mbx->count); modedone: /* for first block */ mbx->nblk = (bp->b_bcount + (mbx->sz-1)) / mbx->sz; mbx->skip = 0; nextblock: blknum = (bp->b_blkno / (mbx->sz/DEV_BSIZE)) + mbx->p_offset + mbx->skip/mbx->sz; MCD_TRACE("mcd_doread: read blknum=%d for bp=%p\n", blknum, bp); /* build parameter block */ hsg2msf(blknum,rbuf.start_msf); retry_read: /* send the read command */ disable_intr(); mcd_put(com_port,cd->read_command); mcd_put(com_port,rbuf.start_msf[0]); mcd_put(com_port,rbuf.start_msf[1]); mcd_put(com_port,rbuf.start_msf[2]); mcd_put(com_port,0); mcd_put(com_port,0); mcd_put(com_port,1); enable_intr(); /* Spin briefly (<= 2ms) to avoid missing next block */ for (i = 0; i < 20; i++) { k = inb(port+MCD_FLAGS); if (!(k & MFL_DATA_NOT_AVAIL)) goto got_it; DELAY(100); } mbx->count = RDELAY_WAITREAD; tohandle = timeout(mcd_timeout, (caddr_t)MCD_S_WAITREAD,hz/100); /* XXX */ return; case MCD_S_WAITREAD: untimeout(mcd_timeout,(caddr_t)MCD_S_WAITREAD, tohandle); if (mbx->count-- > 0) { k = inb(port+MCD_FLAGS); if (!(k & MFL_DATA_NOT_AVAIL)) { /* XXX */ MCD_TRACE("got data delay=%d\n", RDELAY_WAITREAD-mbx->count); got_it: /* data is ready */ addr = bp->b_data + mbx->skip; outb(port+mcd_ctl2,0x04); /* XXX */ for (i=0; isz; i++) *addr++ = inb(data_port); outb(port+mcd_ctl2,0x0c); /* XXX */ k = inb(port+MCD_FLAGS); /* If we still have some junk, read it too */ if (!(k & MFL_DATA_NOT_AVAIL)) { outb(port+mcd_ctl2,0x04); /* XXX */ (void)inb(data_port); (void)inb(data_port); outb(port+mcd_ctl2,0x0c); /* XXX */ } if (--mbx->nblk > 0) { mbx->skip += mbx->sz; goto nextblock; } /* return buffer */ bp->b_resid = 0; biodone(bp); cd->flags &= ~(MCDMBXBSY|MCDREADRAW); mcd_start(mbx->unit); return; } if (!(k & MFL_STATUS_NOT_AVAIL)) { cd->status = inb(port+mcd_status) & 0xFF; if (cd->status & MCD_ST_CMDCHECK) goto retry_read; if (mcd_setflags(unit,cd) < 0) goto changed; } tohandle = timeout(mcd_timeout, (caddr_t)MCD_S_WAITREAD,hz/100); /* XXX */ return; } else { printf("mcd%d: timeout read data\n",unit); goto readerr; } } readerr: if (mbx->retry-- > 0) { printf("mcd%d: retrying\n",unit); state = MCD_S_BEGIN1; goto loop; } harderr: /* invalidate the buffer */ bp->b_flags |= B_ERROR; bp->b_resid = bp->b_bcount; biodone(bp); cd->flags &= ~(MCDMBXBSY|MCDREADRAW); mcd_start(mbx->unit); return; changed: printf("mcd%d: media changed\n", unit); goto harderr; #ifdef NOTDEF printf("mcd%d: unit timeout, resetting\n",mbx->unit); outb(mbx->port+mcd_reset,MCD_CMDRESET); DELAY(300000); (void)mcd_getstat(mbx->unit,1); (void)mcd_getstat(mbx->unit,1); /*cd->status &= ~MCDDSKCHNG; */ cd->debug = 1; /* preventive set debug mode */ #endif } static int mcd_lock_door(int unit, int lock) { struct mcd_data *cd = mcd_data + unit; int port = cd->iobase; outb(port+mcd_command, MCD_CMDLOCKDRV); outb(port+mcd_command, lock); if (mcd_getstat(unit,0) == -1) return EIO; return 0; } static int mcd_close_tray(int unit) { struct mcd_data *cd = mcd_data + unit; int port = cd->iobase; int retry, r; if (mcd_getstat(unit,1) == -1) return EIO; if (cd->status & MCDDOOROPEN) { outb(port+mcd_command, MCD_CMDCLOSETRAY); for (retry = 0; retry < CLOSE_TRAY_SECS * WAIT_FRAC; retry++) { if (inb(port+MCD_FLAGS) & MFL_STATUS_NOT_AVAIL) (void) tsleep((caddr_t)cd, PSOCK | PCATCH, "mcdcls", hz/WAIT_FRAC); else { if ((r = mcd_getstat(unit,0)) == -1) return EIO; return 0; } } return ENXIO; } return 0; } static int mcd_eject(int unit) { struct mcd_data *cd = mcd_data + unit; int port = cd->iobase, r; if (mcd_getstat(unit,1) == -1) /* detect disk change too */ return EIO; if (cd->status & MCDDOOROPEN) return 0; if ((r = mcd_stop(unit)) == EIO) return r; outb(port+mcd_command, MCD_CMDEJECTDISK); if (mcd_getstat(unit,0) == -1) return EIO; return 0; } static int mcd_inject(int unit) { struct mcd_data *cd = mcd_data + unit; if (mcd_getstat(unit,1) == -1) /* detect disk change too */ return EIO; if (cd->status & MCDDOOROPEN) return mcd_close_tray(unit); return 0; } static int mcd_hard_reset(int unit) { struct mcd_data *cd = mcd_data + unit; int port = cd->iobase; outb(port+mcd_reset,MCD_CMDRESET); cd->curr_mode = MCD_MD_UNKNOWN; cd->audio_status = CD_AS_AUDIO_INVALID; return 0; } static void mcd_soft_reset(int unit) { struct mcd_data *cd = mcd_data + unit; int i; cd->flags &= (MCDINIT|MCDPROBING|MCDNEWMODEL); cd->curr_mode = MCD_MD_UNKNOWN; for (i=0; ipartflags[i] = 0; cd->audio_status = CD_AS_AUDIO_INVALID; } static int mcd_setmode(int unit, int mode) { struct mcd_data *cd = mcd_data + unit; int port = cd->iobase; int retry, st; if (cd->curr_mode == mode) return 0; if (cd->debug) printf("mcd%d: setting mode to %d\n", unit, mode); for(retry=0; retrycurr_mode = MCD_MD_UNKNOWN; outb(port+mcd_command, MCD_CMDSETMODE); outb(port+mcd_command, mode); if ((st = mcd_getstat(unit, 0)) >= 0) { cd->curr_mode = mode; return 0; } if (st == -2) { printf("mcd%d: media changed\n", unit); break; } } return -1; } static int mcd_toc_header(int unit, struct ioc_toc_header *th) { struct mcd_data *cd = mcd_data + unit; int r; if ((r = mcd_volinfo(unit)) != 0) return r; th->starting_track = bcd2bin(cd->volinfo.trk_low); th->ending_track = bcd2bin(cd->volinfo.trk_high); th->len = 2 * sizeof(u_char) /* start & end tracks */ + (th->ending_track + 1 - th->starting_track + 1) * sizeof(struct cd_toc_entry); return 0; } static int mcd_read_toc(int unit) { struct mcd_data *cd = mcd_data + unit; struct ioc_toc_header th; struct mcd_qchninfo q; int rc, trk, idx, retry; /* Only read TOC if needed */ if (cd->flags & MCDTOC) return 0; if (cd->debug) printf("mcd%d: reading toc header\n", unit); if ((rc = mcd_toc_header(unit, &th)) != 0) return rc; if (mcd_send(unit, MCD_CMDSTOPAUDIO, MCD_RETRYS) < 0) return EIO; if (mcd_setmode(unit, MCD_MD_TOC) != 0) return EIO; if (cd->debug) printf("mcd%d: get_toc reading qchannel info\n",unit); for(trk=th.starting_track; trk<=th.ending_track; trk++) cd->toc[trk].idx_no = 0; trk = th.ending_track - th.starting_track + 1; for(retry=0; retry<600 && trk>0; retry++) { if (mcd_getqchan(unit, &q) < 0) break; idx = bcd2bin(q.idx_no); if (idx>=th.starting_track && idx<=th.ending_track && q.trk_no==0) { if (cd->toc[idx].idx_no == 0) { cd->toc[idx] = q; trk--; } } } if (mcd_setmode(unit, MCD_MD_COOKED) != 0) return EIO; if (trk != 0) return ENXIO; /* add a fake last+1 */ idx = th.ending_track + 1; cd->toc[idx].control = cd->toc[idx-1].control; cd->toc[idx].addr_type = cd->toc[idx-1].addr_type; cd->toc[idx].trk_no = 0; cd->toc[idx].idx_no = MCD_LASTPLUS1; cd->toc[idx].hd_pos_msf[0] = cd->volinfo.vol_msf[0]; cd->toc[idx].hd_pos_msf[1] = cd->volinfo.vol_msf[1]; cd->toc[idx].hd_pos_msf[2] = cd->volinfo.vol_msf[2]; if (cd->debug) { int i; for (i = th.starting_track; i <= idx; i++) printf("mcd%d: trk %d idx %d pos %d %d %d\n", unit, i, cd->toc[i].idx_no > 0x99 ? cd->toc[i].idx_no : bcd2bin(cd->toc[i].idx_no), bcd2bin(cd->toc[i].hd_pos_msf[0]), bcd2bin(cd->toc[i].hd_pos_msf[1]), bcd2bin(cd->toc[i].hd_pos_msf[2])); } cd->flags |= MCDTOC; return 0; } #if 0 static int mcd_toc_entry(int unit, struct ioc_read_toc_single_entry *te) { struct mcd_data *cd = mcd_data + unit; struct ioc_toc_header th; int rc, trk; if (te->address_format != CD_MSF_FORMAT && te->address_format != CD_LBA_FORMAT) return EINVAL; /* Copy the toc header */ if ((rc = mcd_toc_header(unit, &th)) != 0) return rc; /* verify starting track */ trk = te->track; if (trk == 0) trk = th.starting_track; else if (trk == MCD_LASTPLUS1) trk = th.ending_track + 1; else if (trk < th.starting_track || trk > th.ending_track + 1) return EINVAL; /* Make sure we have a valid toc */ if ((rc=mcd_read_toc(unit)) != 0) return rc; /* Copy the TOC data. */ if (cd->toc[trk].idx_no == 0) return EIO; te->entry.control = cd->toc[trk].control; te->entry.addr_type = cd->toc[trk].addr_type; te->entry.track = cd->toc[trk].idx_no > 0x99 ? cd->toc[trk].idx_no : bcd2bin(cd->toc[trk].idx_no); switch (te->address_format) { case CD_MSF_FORMAT: te->entry.addr.msf.unused = 0; te->entry.addr.msf.minute = bcd2bin(cd->toc[trk].hd_pos_msf[0]); te->entry.addr.msf.second = bcd2bin(cd->toc[trk].hd_pos_msf[1]); te->entry.addr.msf.frame = bcd2bin(cd->toc[trk].hd_pos_msf[2]); break; case CD_LBA_FORMAT: te->entry.addr.lba = htonl(msf2hsg(cd->toc[trk].hd_pos_msf, 0)); break; } return 0; } #endif static int mcd_toc_entrys(int unit, struct ioc_read_toc_entry *te) { struct mcd_data *cd = mcd_data + unit; struct cd_toc_entry entries[MCD_MAXTOCS]; struct ioc_toc_header th; int rc, n, trk, len; if ( te->data_len < sizeof(entries[0]) || (te->data_len % sizeof(entries[0])) != 0 || (te->address_format != CD_MSF_FORMAT && te->address_format != CD_LBA_FORMAT) ) return EINVAL; /* Copy the toc header */ if ((rc = mcd_toc_header(unit, &th)) != 0) return rc; /* verify starting track */ trk = te->starting_track; if (trk == 0) trk = th.starting_track; else if (trk == MCD_LASTPLUS1) trk = th.ending_track + 1; else if (trk < th.starting_track || trk > th.ending_track + 1) return EINVAL; len = ((th.ending_track + 1 - trk) + 1) * sizeof(entries[0]); if (te->data_len < len) len = te->data_len; if (len > sizeof(entries)) return EINVAL; /* Make sure we have a valid toc */ if ((rc=mcd_read_toc(unit)) != 0) return rc; /* Copy the TOC data. */ for (n = 0; len > 0 && trk <= th.ending_track + 1; trk++) { if (cd->toc[trk].idx_no == 0) continue; entries[n].control = cd->toc[trk].control; entries[n].addr_type = cd->toc[trk].addr_type; entries[n].track = cd->toc[trk].idx_no > 0x99 ? cd->toc[trk].idx_no : bcd2bin(cd->toc[trk].idx_no); switch (te->address_format) { case CD_MSF_FORMAT: entries[n].addr.msf.unused = 0; entries[n].addr.msf.minute = bcd2bin(cd->toc[trk].hd_pos_msf[0]); entries[n].addr.msf.second = bcd2bin(cd->toc[trk].hd_pos_msf[1]); entries[n].addr.msf.frame = bcd2bin(cd->toc[trk].hd_pos_msf[2]); break; case CD_LBA_FORMAT: entries[n].addr.lba = htonl(msf2hsg(cd->toc[trk].hd_pos_msf, 0)); break; } len -= sizeof(struct cd_toc_entry); n++; } /* copy the data back */ return copyout(entries, te->data, n * sizeof(struct cd_toc_entry)); } static int mcd_stop(int unit) { struct mcd_data *cd = mcd_data + unit; /* Verify current status */ if (cd->audio_status != CD_AS_PLAY_IN_PROGRESS && cd->audio_status != CD_AS_PLAY_PAUSED && cd->audio_status != CD_AS_PLAY_COMPLETED) { if (cd->debug) printf("mcd%d: stop attempted when not playing, audio status %d\n", unit, cd->audio_status); return EINVAL; } if (cd->audio_status == CD_AS_PLAY_IN_PROGRESS) if (mcd_send(unit, MCD_CMDSTOPAUDIO, MCD_RETRYS) < 0) return EIO; cd->audio_status = CD_AS_PLAY_COMPLETED; return 0; } static int mcd_getqchan(int unit, struct mcd_qchninfo *q) { struct mcd_data *cd = mcd_data + unit; if (mcd_send(unit, MCD_CMDGETQCHN, MCD_RETRYS) < 0) return -1; if (mcd_get(unit, (char *) q, sizeof(struct mcd_qchninfo)) < 0) return -1; if (cd->debug) { printf("mcd%d: getqchan control=0x%x addr_type=0x%x trk=%d ind=%d ttm=%d:%d.%d dtm=%d:%d.%d\n", unit, q->control, q->addr_type, bcd2bin(q->trk_no), bcd2bin(q->idx_no), bcd2bin(q->trk_size_msf[0]), bcd2bin(q->trk_size_msf[1]), bcd2bin(q->trk_size_msf[2]), bcd2bin(q->hd_pos_msf[0]), bcd2bin(q->hd_pos_msf[1]), bcd2bin(q->hd_pos_msf[2])); } return 0; } static int mcd_subchan(int unit, struct ioc_read_subchannel *sc) { struct mcd_data *cd = mcd_data + unit; struct mcd_qchninfo q; struct cd_sub_channel_info data; int lba; if (cd->debug) printf("mcd%d: subchan af=%d, df=%d\n", unit, sc->address_format, sc->data_format); if (sc->address_format != CD_MSF_FORMAT && sc->address_format != CD_LBA_FORMAT) return EINVAL; if (sc->data_format != CD_CURRENT_POSITION && sc->data_format != CD_MEDIA_CATALOG) return EINVAL; if (mcd_setmode(unit, MCD_MD_COOKED) != 0) return EIO; if (mcd_getqchan(unit, &q) < 0) return EIO; data.header.audio_status = cd->audio_status; data.what.position.data_format = sc->data_format; switch (sc->data_format) { case CD_MEDIA_CATALOG: data.what.media_catalog.mc_valid = 1; data.what.media_catalog.mc_number[0] = '\0'; break; case CD_CURRENT_POSITION: data.what.position.control = q.control; data.what.position.addr_type = q.addr_type; data.what.position.track_number = bcd2bin(q.trk_no); data.what.position.index_number = bcd2bin(q.idx_no); switch (sc->address_format) { case CD_MSF_FORMAT: data.what.position.reladdr.msf.unused = 0; data.what.position.reladdr.msf.minute = bcd2bin(q.trk_size_msf[0]); data.what.position.reladdr.msf.second = bcd2bin(q.trk_size_msf[1]); data.what.position.reladdr.msf.frame = bcd2bin(q.trk_size_msf[2]); data.what.position.absaddr.msf.unused = 0; data.what.position.absaddr.msf.minute = bcd2bin(q.hd_pos_msf[0]); data.what.position.absaddr.msf.second = bcd2bin(q.hd_pos_msf[1]); data.what.position.absaddr.msf.frame = bcd2bin(q.hd_pos_msf[2]); break; case CD_LBA_FORMAT: lba = msf2hsg(q.trk_size_msf, 1); /* * Pre-gap has index number of 0, and decreasing MSF * address. Must be converted to negative LBA, per * SCSI spec. */ if (data.what.position.index_number == 0) lba = -lba; data.what.position.reladdr.lba = htonl(lba); data.what.position.absaddr.lba = htonl(msf2hsg(q.hd_pos_msf, 0)); break; } break; } return copyout(&data, sc->data, min(sizeof(struct cd_sub_channel_info), sc->data_len)); } static int mcd_playmsf(int unit, struct ioc_play_msf *p) { struct mcd_data *cd = mcd_data + unit; struct mcd_read2 pb; if (cd->debug) printf("mcd%d: playmsf: from %d:%d.%d to %d:%d.%d\n", unit, p->start_m, p->start_s, p->start_f, p->end_m, p->end_s, p->end_f); if ((p->start_m * 60 * 75 + p->start_s * 75 + p->start_f) >= (p->end_m * 60 * 75 + p->end_s * 75 + p->end_f) || (p->end_m * 60 * 75 + p->end_s * 75 + p->end_f) > M_msf(cd->volinfo.vol_msf) * 60 * 75 + S_msf(cd->volinfo.vol_msf) * 75 + F_msf(cd->volinfo.vol_msf)) return EINVAL; pb.start_msf[0] = bin2bcd(p->start_m); pb.start_msf[1] = bin2bcd(p->start_s); pb.start_msf[2] = bin2bcd(p->start_f); pb.end_msf[0] = bin2bcd(p->end_m); pb.end_msf[1] = bin2bcd(p->end_s); pb.end_msf[2] = bin2bcd(p->end_f); if (mcd_setmode(unit, MCD_MD_COOKED) != 0) return EIO; return mcd_play(unit, &pb); } static int mcd_playtracks(int unit, struct ioc_play_track *pt) { struct mcd_data *cd = mcd_data + unit; struct mcd_read2 pb; int a = pt->start_track; int z = pt->end_track; int rc, i; if ((rc = mcd_read_toc(unit)) != 0) return rc; if (cd->debug) printf("mcd%d: playtracks from %d:%d to %d:%d\n", unit, a, pt->start_index, z, pt->end_index); if ( a < bcd2bin(cd->volinfo.trk_low) || a > bcd2bin(cd->volinfo.trk_high) || a > z || z < bcd2bin(cd->volinfo.trk_low) || z > bcd2bin(cd->volinfo.trk_high)) return EINVAL; for (i = 0; i < 3; i++) { pb.start_msf[i] = cd->toc[a].hd_pos_msf[i]; pb.end_msf[i] = cd->toc[z+1].hd_pos_msf[i]; } if (mcd_setmode(unit, MCD_MD_COOKED) != 0) return EIO; return mcd_play(unit, &pb); } static int mcd_playblocks(int unit, struct ioc_play_blocks *p) { struct mcd_data *cd = mcd_data + unit; struct mcd_read2 pb; if (cd->debug) printf("mcd%d: playblocks: blkno %d length %d\n", unit, p->blk, p->len); if (p->blk > cd->disksize || p->len > cd->disksize || p->blk < 0 || p->len < 0 || (p->blk + p->len) > cd->disksize) return EINVAL; hsg2msf(p->blk, pb.start_msf); hsg2msf(p->blk + p->len, pb.end_msf); if (mcd_setmode(unit, MCD_MD_COOKED) != 0) return EIO; return mcd_play(unit, &pb); } static int mcd_play(int unit, struct mcd_read2 *pb) { struct mcd_data *cd = mcd_data + unit; int com_port = cd->iobase + mcd_command; int retry, st = -1, status; cd->lastpb = *pb; for(retry=0; retrystart_msf[0]); outb(com_port, pb->start_msf[1]); outb(com_port, pb->start_msf[2]); outb(com_port, pb->end_msf[0]); outb(com_port, pb->end_msf[1]); outb(com_port, pb->end_msf[2]); enable_intr(); status=mcd_getstat(unit, 0); if (status == -1) continue; else if (status != -2) st = 0; break; } if (status == -2) { printf("mcd%d: media changed\n", unit); return ENXIO; } if (cd->debug) printf("mcd%d: mcd_play retry=%d, status=0x%02x\n", unit, retry, status); if (st < 0) return ENXIO; cd->audio_status = CD_AS_PLAY_IN_PROGRESS; return 0; } static int mcd_pause(int unit) { struct mcd_data *cd = mcd_data + unit; struct mcd_qchninfo q; int rc; /* Verify current status */ if (cd->audio_status != CD_AS_PLAY_IN_PROGRESS && cd->audio_status != CD_AS_PLAY_PAUSED) { if (cd->debug) printf("mcd%d: pause attempted when not playing, audio status %d\n", unit, cd->audio_status); return EINVAL; } /* Get the current position */ if (mcd_getqchan(unit, &q) < 0) return EIO; /* Copy it into lastpb */ cd->lastpb.start_msf[0] = q.hd_pos_msf[0]; cd->lastpb.start_msf[1] = q.hd_pos_msf[1]; cd->lastpb.start_msf[2] = q.hd_pos_msf[2]; /* Stop playing */ if ((rc=mcd_stop(unit)) != 0) return rc; /* Set the proper status and exit */ cd->audio_status = CD_AS_PLAY_PAUSED; return 0; } static int mcd_resume(int unit) { struct mcd_data *cd = mcd_data + unit; if (cd->audio_status != CD_AS_PLAY_PAUSED) return EINVAL; return mcd_play(unit, &cd->lastpb); } - -#endif /* NMCD > 0 */ diff --git a/sys/dev/mse/mse.c b/sys/dev/mse/mse.c index 332e020333d8..5dd8376702c8 100644 --- a/sys/dev/mse/mse.c +++ b/sys/dev/mse/mse.c @@ -1,771 +1,768 @@ /* * Copyright 1992 by the University of Guelph * * Permission to use, copy and modify this * software and its documentation for any purpose and without * fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright * notice and this permission notice appear in supporting * documentation. * University of Guelph makes no representations about the suitability of * this software for any purpose. It is provided "as is" * without express or implied warranty. * * $FreeBSD$ */ /* * Driver for the Logitech and ATI Inport Bus mice for use with 386bsd and * the X386 port, courtesy of * Rick Macklem, rick@snowhite.cis.uoguelph.ca * Caveats: The driver currently uses spltty(), but doesn't use any * generic tty code. It could use splmse() (that only masks off the * bus mouse interrupt, but that would require hacking in i386/isa/icu.s. * (This may be worth the effort, since the Logitech generates 30/60 * interrupts/sec continuously while it is open.) * NB: The ATI has NOT been tested yet! */ /* * Modification history: * Sep 6, 1994 -- Lars Fredriksen(fredriks@mcs.com) * improved probe based on input from Logitech. * * Oct 19, 1992 -- E. Stark (stark@cs.sunysb.edu) * fixes to make it work with Microsoft InPort busmouse * * Jan, 1993 -- E. Stark (stark@cs.sunysb.edu) * added patches for new "select" interface * * May 4, 1993 -- E. Stark (stark@cs.sunysb.edu) * changed position of some spl()'s in mseread * * October 8, 1993 -- E. Stark (stark@cs.sunysb.edu) * limit maximum negative x/y value to -127 to work around XFree problem * that causes spurious button pushes. */ #include "mse.h" -#if NMSE > 0 #include #include #include #include #include #include #include #include #include #include /* driver configuration flags (config) */ #define MSE_CONFIG_ACCEL 0x00f0 /* acceleration factor */ #define MSE_CONFIG_FLAGS (MSE_CONFIG_ACCEL) static int mseprobe(struct isa_device *); static int mseattach(struct isa_device *); struct isa_driver msedriver = { mseprobe, mseattach, "mse" }; static d_open_t mseopen; static d_close_t mseclose; static d_read_t mseread; static d_ioctl_t mseioctl; static d_poll_t msepoll; #define CDEV_MAJOR 27 static struct cdevsw mse_cdevsw = { /* open */ mseopen, /* close */ mseclose, /* read */ mseread, /* write */ nowrite, /* ioctl */ mseioctl, /* poll */ msepoll, /* mmap */ nommap, /* strategy */ nostrategy, /* name */ "mse", /* maj */ CDEV_MAJOR, /* dump */ nodump, /* psize */ nopsize, /* flags */ 0, /* bmaj */ -1 }; static ointhand2_t mseintr; /* * Software control structure for mouse. The sc_enablemouse(), * sc_disablemouse() and sc_getmouse() routines must be called spl'd(). */ static struct mse_softc { int sc_flags; int sc_mousetype; struct selinfo sc_selp; u_int sc_port; void (*sc_enablemouse) __P((u_int port)); void (*sc_disablemouse) __P((u_int port)); void (*sc_getmouse) __P((u_int port, int *dx, int *dy, int *but)); int sc_deltax; int sc_deltay; int sc_obuttons; int sc_buttons; int sc_bytesread; u_char sc_bytes[MOUSE_SYS_PACKETSIZE]; mousehw_t hw; mousemode_t mode; mousestatus_t status; } mse_sc[NMSE]; /* Flags */ #define MSESC_OPEN 0x1 #define MSESC_WANT 0x2 /* and Mouse Types */ #define MSE_NONE 0 /* don't move this! */ #define MSE_LOGITECH 0x1 #define MSE_ATIINPORT 0x2 #define MSE_LOGI_SIG 0xA5 #define MSE_PORTA 0 #define MSE_PORTB 1 #define MSE_PORTC 2 #define MSE_PORTD 3 #define MSE_UNIT(dev) (minor(dev) >> 1) #define MSE_NBLOCKIO(dev) (minor(dev) & 0x1) /* * Logitech bus mouse definitions */ #define MSE_SETUP 0x91 /* What does this mean? */ /* The definition for the control port */ /* is as follows: */ /* D7 = Mode set flag (1 = active) */ /* D6,D5 = Mode selection (port A) */ /* 00 = Mode 0 = Basic I/O */ /* 01 = Mode 1 = Strobed I/O */ /* 10 = Mode 2 = Bi-dir bus */ /* D4 = Port A direction (1 = input)*/ /* D3 = Port C (upper 4 bits) */ /* direction. (1 = input) */ /* D2 = Mode selection (port B & C) */ /* 0 = Mode 0 = Basic I/O */ /* 1 = Mode 1 = Strobed I/O */ /* D1 = Port B direction (1 = input)*/ /* D0 = Port C (lower 4 bits) */ /* direction. (1 = input) */ /* So 91 means Basic I/O on all 3 ports,*/ /* Port A is an input port, B is an */ /* output port, C is split with upper */ /* 4 bits being an output port and lower*/ /* 4 bits an input port, and enable the */ /* sucker. */ /* Courtesy Intel 8255 databook. Lars */ #define MSE_HOLD 0x80 #define MSE_RXLOW 0x00 #define MSE_RXHIGH 0x20 #define MSE_RYLOW 0x40 #define MSE_RYHIGH 0x60 #define MSE_DISINTR 0x10 #define MSE_INTREN 0x00 static int mse_probelogi __P((struct isa_device *idp)); static void mse_disablelogi __P((u_int port)); static void mse_getlogi __P((u_int port, int *dx, int *dy, int *but)); static void mse_enablelogi __P((u_int port)); /* * ATI Inport mouse definitions */ #define MSE_INPORT_RESET 0x80 #define MSE_INPORT_STATUS 0x00 #define MSE_INPORT_DX 0x01 #define MSE_INPORT_DY 0x02 #define MSE_INPORT_MODE 0x07 #define MSE_INPORT_HOLD 0x20 #define MSE_INPORT_INTREN 0x09 static int mse_probeati __P((struct isa_device *idp)); static void mse_enableati __P((u_int port)); static void mse_disableati __P((u_int port)); static void mse_getati __P((u_int port, int *dx, int *dy, int *but)); #define MSEPRI (PZERO + 3) /* * Table of mouse types. * Keep the Logitech last, since I haven't figured out how to probe it * properly yet. (Someday I'll have the documentation.) */ static struct mse_types { int m_type; /* Type of bus mouse */ int (*m_probe) __P((struct isa_device *idp)); /* Probe routine to test for it */ void (*m_enable) __P((u_int port)); /* Start routine */ void (*m_disable) __P((u_int port)); /* Disable interrupts routine */ void (*m_get) __P((u_int port, int *dx, int *dy, int *but)); /* and get mouse status */ mousehw_t m_hw; /* buttons iftype type model hwid */ mousemode_t m_mode; /* proto rate res accel level size mask */ } mse_types[] = { { MSE_ATIINPORT, mse_probeati, mse_enableati, mse_disableati, mse_getati, { 2, MOUSE_IF_INPORT, MOUSE_MOUSE, MOUSE_MODEL_GENERIC, 0, }, { MOUSE_PROTO_INPORT, -1, -1, 0, 0, MOUSE_MSC_PACKETSIZE, { MOUSE_MSC_SYNCMASK, MOUSE_MSC_SYNC, }, }, }, { MSE_LOGITECH, mse_probelogi, mse_enablelogi, mse_disablelogi, mse_getlogi, { 2, MOUSE_IF_BUS, MOUSE_MOUSE, MOUSE_MODEL_GENERIC, 0, }, { MOUSE_PROTO_BUS, -1, -1, 0, 0, MOUSE_MSC_PACKETSIZE, { MOUSE_MSC_SYNCMASK, MOUSE_MSC_SYNC, }, }, }, { 0, }, }; int mseprobe(idp) register struct isa_device *idp; { register struct mse_softc *sc = &mse_sc[idp->id_unit]; register int i; /* * Check for each mouse type in the table. */ i = 0; while (mse_types[i].m_type) { if ((*mse_types[i].m_probe)(idp)) { sc->sc_mousetype = mse_types[i].m_type; sc->sc_enablemouse = mse_types[i].m_enable; sc->sc_disablemouse = mse_types[i].m_disable; sc->sc_getmouse = mse_types[i].m_get; sc->hw = mse_types[i].m_hw; sc->mode = mse_types[i].m_mode; return (1); } i++; } return (0); } int mseattach(idp) struct isa_device *idp; { int unit = idp->id_unit; struct mse_softc *sc = &mse_sc[unit]; idp->id_ointr = mseintr; sc->sc_port = idp->id_iobase; sc->mode.accelfactor = (idp->id_flags & MSE_CONFIG_ACCEL) >> 4; make_dev(&mse_cdevsw, unit << 1, 0, 0, 0600, "mse%d", unit); make_dev(&mse_cdevsw, (unit<<1)+1, 0, 0, 0600, "nmse%d", unit); return (1); } /* * Exclusive open the mouse, initialize it and enable interrupts. */ static int mseopen(dev, flags, fmt, p) dev_t dev; int flags; int fmt; struct proc *p; { register struct mse_softc *sc; int s; if (MSE_UNIT(dev) >= NMSE) return (ENXIO); sc = &mse_sc[MSE_UNIT(dev)]; if (sc->sc_mousetype == MSE_NONE) return (ENXIO); if (sc->sc_flags & MSESC_OPEN) return (EBUSY); sc->sc_flags |= MSESC_OPEN; sc->sc_obuttons = sc->sc_buttons = MOUSE_MSC_BUTTONS; sc->sc_deltax = sc->sc_deltay = 0; sc->sc_bytesread = sc->mode.packetsize = MOUSE_MSC_PACKETSIZE; sc->mode.level = 0; sc->status.flags = 0; sc->status.button = sc->status.obutton = 0; sc->status.dx = sc->status.dy = sc->status.dz = 0; /* * Initialize mouse interface and enable interrupts. */ s = spltty(); (*sc->sc_enablemouse)(sc->sc_port); splx(s); return (0); } /* * mseclose: just turn off mouse innterrupts. */ static int mseclose(dev, flags, fmt, p) dev_t dev; int flags; int fmt; struct proc *p; { struct mse_softc *sc = &mse_sc[MSE_UNIT(dev)]; int s; s = spltty(); (*sc->sc_disablemouse)(sc->sc_port); sc->sc_flags &= ~MSESC_OPEN; splx(s); return(0); } /* * mseread: return mouse info using the MSC serial protocol, but without * using bytes 4 and 5. * (Yes this is cheesy, but it makes the X386 server happy, so...) */ static int mseread(dev, uio, ioflag) dev_t dev; struct uio *uio; int ioflag; { register struct mse_softc *sc = &mse_sc[MSE_UNIT(dev)]; int xfer, s, error; /* * If there are no protocol bytes to be read, set up a new protocol * packet. */ s = spltty(); /* XXX Should be its own spl, but where is imlXX() */ if (sc->sc_bytesread >= sc->mode.packetsize) { while (sc->sc_deltax == 0 && sc->sc_deltay == 0 && (sc->sc_obuttons ^ sc->sc_buttons) == 0) { if (MSE_NBLOCKIO(dev)) { splx(s); return (0); } sc->sc_flags |= MSESC_WANT; error = tsleep((caddr_t)sc, MSEPRI | PCATCH, "mseread", 0); if (error) { splx(s); return (error); } } /* * Generate protocol bytes. * For some reason X386 expects 5 bytes but never uses * the fourth or fifth? */ sc->sc_bytes[0] = sc->mode.syncmask[1] | (sc->sc_buttons & ~sc->mode.syncmask[0]); if (sc->sc_deltax > 127) sc->sc_deltax = 127; if (sc->sc_deltax < -127) sc->sc_deltax = -127; sc->sc_deltay = -sc->sc_deltay; /* Otherwise mousey goes wrong way */ if (sc->sc_deltay > 127) sc->sc_deltay = 127; if (sc->sc_deltay < -127) sc->sc_deltay = -127; sc->sc_bytes[1] = sc->sc_deltax; sc->sc_bytes[2] = sc->sc_deltay; sc->sc_bytes[3] = sc->sc_bytes[4] = 0; sc->sc_bytes[5] = sc->sc_bytes[6] = 0; sc->sc_bytes[7] = MOUSE_SYS_EXTBUTTONS; sc->sc_obuttons = sc->sc_buttons; sc->sc_deltax = sc->sc_deltay = 0; sc->sc_bytesread = 0; } splx(s); xfer = min(uio->uio_resid, sc->mode.packetsize - sc->sc_bytesread); error = uiomove(&sc->sc_bytes[sc->sc_bytesread], xfer, uio); if (error) return (error); sc->sc_bytesread += xfer; return(0); } /* * mseioctl: process ioctl commands. */ static int mseioctl(dev, cmd, addr, flag, p) dev_t dev; u_long cmd; caddr_t addr; int flag; struct proc *p; { register struct mse_softc *sc = &mse_sc[MSE_UNIT(dev)]; mousestatus_t status; int err = 0; int s; switch (cmd) { case MOUSE_GETHWINFO: s = spltty(); *(mousehw_t *)addr = sc->hw; if (sc->mode.level == 0) ((mousehw_t *)addr)->model = MOUSE_MODEL_GENERIC; splx(s); break; case MOUSE_GETMODE: s = spltty(); *(mousemode_t *)addr = sc->mode; switch (sc->mode.level) { case 0: break; case 1: ((mousemode_t *)addr)->protocol = MOUSE_PROTO_SYSMOUSE; ((mousemode_t *)addr)->syncmask[0] = MOUSE_SYS_SYNCMASK; ((mousemode_t *)addr)->syncmask[1] = MOUSE_SYS_SYNC; break; } splx(s); break; case MOUSE_SETMODE: switch (((mousemode_t *)addr)->level) { case 0: case 1: break; default: return (EINVAL); } if (((mousemode_t *)addr)->accelfactor < -1) return (EINVAL); else if (((mousemode_t *)addr)->accelfactor >= 0) sc->mode.accelfactor = ((mousemode_t *)addr)->accelfactor; sc->mode.level = ((mousemode_t *)addr)->level; switch (sc->mode.level) { case 0: sc->sc_bytesread = sc->mode.packetsize = MOUSE_MSC_PACKETSIZE; break; case 1: sc->sc_bytesread = sc->mode.packetsize = MOUSE_SYS_PACKETSIZE; break; } break; case MOUSE_GETLEVEL: *(int *)addr = sc->mode.level; break; case MOUSE_SETLEVEL: switch (*(int *)addr) { case 0: sc->mode.level = *(int *)addr; sc->sc_bytesread = sc->mode.packetsize = MOUSE_MSC_PACKETSIZE; break; case 1: sc->mode.level = *(int *)addr; sc->sc_bytesread = sc->mode.packetsize = MOUSE_SYS_PACKETSIZE; break; default: return (EINVAL); } break; case MOUSE_GETSTATUS: s = spltty(); status = sc->status; sc->status.flags = 0; sc->status.obutton = sc->status.button; sc->status.button = 0; sc->status.dx = 0; sc->status.dy = 0; sc->status.dz = 0; splx(s); *(mousestatus_t *)addr = status; break; case MOUSE_READSTATE: case MOUSE_READDATA: return (ENODEV); #if (defined(MOUSE_GETVARS)) case MOUSE_GETVARS: case MOUSE_SETVARS: return (ENODEV); #endif default: return (ENOTTY); } return (err); } /* * msepoll: check for mouse input to be processed. */ static int msepoll(dev, events, p) dev_t dev; int events; struct proc *p; { register struct mse_softc *sc = &mse_sc[MSE_UNIT(dev)]; int s; int revents = 0; s = spltty(); if (events & (POLLIN | POLLRDNORM)) { if (sc->sc_bytesread != sc->mode.packetsize || sc->sc_deltax != 0 || sc->sc_deltay != 0 || (sc->sc_obuttons ^ sc->sc_buttons) != 0) revents |= events & (POLLIN | POLLRDNORM); else { /* * Since this is an exclusive open device, any previous * proc pointer is trash now, so we can just assign it. */ selrecord(p, &sc->sc_selp); } } splx(s); return (revents); } /* * mseintr: update mouse status. sc_deltax and sc_deltay are accumulative. */ static void mseintr(unit) int unit; { /* * the table to turn MouseSystem button bits (MOUSE_MSC_BUTTON?UP) * into `mousestatus' button bits (MOUSE_BUTTON?DOWN). */ static int butmap[8] = { 0, MOUSE_BUTTON3DOWN, MOUSE_BUTTON2DOWN, MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN, MOUSE_BUTTON1DOWN, MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN, MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN, MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN }; register struct mse_softc *sc = &mse_sc[unit]; int dx, dy, but; int sign; #ifdef DEBUG static int mse_intrcnt = 0; if((mse_intrcnt++ % 10000) == 0) printf("mseintr\n"); #endif /* DEBUG */ if ((sc->sc_flags & MSESC_OPEN) == 0) return; (*sc->sc_getmouse)(sc->sc_port, &dx, &dy, &but); if (sc->mode.accelfactor > 0) { sign = (dx < 0); dx = dx * dx / sc->mode.accelfactor; if (dx == 0) dx = 1; if (sign) dx = -dx; sign = (dy < 0); dy = dy * dy / sc->mode.accelfactor; if (dy == 0) dy = 1; if (sign) dy = -dy; } sc->sc_deltax += dx; sc->sc_deltay += dy; sc->sc_buttons = but; but = butmap[~but & MOUSE_MSC_BUTTONS]; sc->status.dx += dx; sc->status.dy += dy; sc->status.flags |= ((dx || dy) ? MOUSE_POSCHANGED : 0) | (sc->status.button ^ but); sc->status.button = but; /* * If mouse state has changed, wake up anyone wanting to know. */ if (sc->sc_deltax != 0 || sc->sc_deltay != 0 || (sc->sc_obuttons ^ sc->sc_buttons) != 0) { if (sc->sc_flags & MSESC_WANT) { sc->sc_flags &= ~MSESC_WANT; wakeup((caddr_t)sc); } selwakeup(&sc->sc_selp); } } /* * Routines for the Logitech mouse. */ /* * Test for a Logitech bus mouse and return 1 if it is. * (until I know how to use the signature port properly, just disable * interrupts and return 1) */ static int mse_probelogi(idp) register struct isa_device *idp; { int sig; outb(idp->id_iobase + MSE_PORTD, MSE_SETUP); /* set the signature port */ outb(idp->id_iobase + MSE_PORTB, MSE_LOGI_SIG); DELAY(30000); /* 30 ms delay */ sig = inb(idp->id_iobase + MSE_PORTB) & 0xFF; if (sig == MSE_LOGI_SIG) { outb(idp->id_iobase + MSE_PORTC, MSE_DISINTR); return(1); } else { if (bootverbose) printf("mse%d: wrong signature %x\n",idp->id_unit,sig); return(0); } } /* * Initialize Logitech mouse and enable interrupts. */ static void mse_enablelogi(port) register u_int port; { int dx, dy, but; outb(port + MSE_PORTD, MSE_SETUP); mse_getlogi(port, &dx, &dy, &but); } /* * Disable interrupts for Logitech mouse. */ static void mse_disablelogi(port) register u_int port; { outb(port + MSE_PORTC, MSE_DISINTR); } /* * Get the current dx, dy and button up/down state. */ static void mse_getlogi(port, dx, dy, but) register u_int port; int *dx; int *dy; int *but; { register char x, y; outb(port + MSE_PORTC, MSE_HOLD | MSE_RXLOW); x = inb(port + MSE_PORTA); *but = (x >> 5) & MOUSE_MSC_BUTTONS; x &= 0xf; outb(port + MSE_PORTC, MSE_HOLD | MSE_RXHIGH); x |= (inb(port + MSE_PORTA) << 4); outb(port + MSE_PORTC, MSE_HOLD | MSE_RYLOW); y = (inb(port + MSE_PORTA) & 0xf); outb(port + MSE_PORTC, MSE_HOLD | MSE_RYHIGH); y |= (inb(port + MSE_PORTA) << 4); *dx = x; *dy = y; outb(port + MSE_PORTC, MSE_INTREN); } /* * Routines for the ATI Inport bus mouse. */ /* * Test for a ATI Inport bus mouse and return 1 if it is. * (do not enable interrupts) */ static int mse_probeati(idp) register struct isa_device *idp; { int i; for (i = 0; i < 2; i++) if (inb(idp->id_iobase + MSE_PORTC) == 0xde) return (1); return (0); } /* * Initialize ATI Inport mouse and enable interrupts. */ static void mse_enableati(port) register u_int port; { outb(port + MSE_PORTA, MSE_INPORT_RESET); outb(port + MSE_PORTA, MSE_INPORT_MODE); outb(port + MSE_PORTB, MSE_INPORT_INTREN); } /* * Disable interrupts for ATI Inport mouse. */ static void mse_disableati(port) register u_int port; { outb(port + MSE_PORTA, MSE_INPORT_MODE); outb(port + MSE_PORTB, 0); } /* * Get current dx, dy and up/down button state. */ static void mse_getati(port, dx, dy, but) register u_int port; int *dx; int *dy; int *but; { register char byte; outb(port + MSE_PORTA, MSE_INPORT_MODE); outb(port + MSE_PORTB, MSE_INPORT_HOLD); outb(port + MSE_PORTA, MSE_INPORT_STATUS); *but = ~inb(port + MSE_PORTB) & MOUSE_MSC_BUTTONS; outb(port + MSE_PORTA, MSE_INPORT_DX); byte = inb(port + MSE_PORTB); *dx = byte; outb(port + MSE_PORTA, MSE_INPORT_DY); byte = inb(port + MSE_PORTB); *dy = byte; outb(port + MSE_PORTA, MSE_INPORT_MODE); outb(port + MSE_PORTB, MSE_INPORT_INTREN); } - -#endif /* NMSE */ diff --git a/sys/i386/bios/mca_machdep.c b/sys/i386/bios/mca_machdep.c index 8c416cf43917..6b71206394ff 100644 --- a/sys/i386/bios/mca_machdep.c +++ b/sys/i386/bios/mca_machdep.c @@ -1,162 +1,158 @@ /*- * Copyright (c) 1999 Matthew N. Dodd * All rights reserved. * * 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 * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ -#include "mca.h" -#if NMCA > 0 - #include #include #include #include #include #include #include #include #include #include #include #include /* Global MCA bus flag */ int MCA_system = 0; /* System Configuration Block */ struct sys_config { u_int16_t count; u_int8_t model; u_int8_t submodel; u_int8_t bios_rev; u_int8_t feature; #define FEATURE_RESV 0x01 /* Reserved */ #define FEATURE_MCABUS 0x02 /* MicroChannel Architecture */ #define FEATURE_EBDA 0x04 /* Extended BIOS data area allocated */ #define FEATURE_WAITEV 0x08 /* Wait for external event is supported */ #define FEATURE_KBDINT 0x10 /* Keyboard intercept called by Int 09h */ #define FEATURE_RTC 0x20 /* Real-time clock present */ #define FEATURE_IC2 0x40 /* Second interrupt chip present */ #define FEATURE_DMA3 0x80 /* DMA channel 3 used by hard disk BIOS */ u_int8_t pad[3]; } __attribute__ ((packed)); /* Function Prototypes */ static void bios_mcabus_present (void *); SYSINIT(mca_present, SI_SUB_CPU, SI_ORDER_ANY, bios_mcabus_present, NULL); /* Functions */ static void bios_mcabus_present(void * dummy) { struct vm86frame vmf; struct sys_config * scp; vm_offset_t paddr; bzero(&vmf, sizeof(struct vm86frame)); vmf.vmf_ah = 0xc0; if (vm86_intcall(0x15, &vmf)) { if (bootverbose) { printf("BIOS SDT: INT call failed.\n"); } return; } if ((vmf.vmf_ah != 0) && (vmf.vmf_flags & 0x01)) { if (bootverbose) { printf("BIOS SDT: Not supported. Not PS/2?\n"); printf("BIOS SDT: AH 0x%02x, Flags 0x%04x\n", vmf.vmf_ah, vmf.vmf_flags); } return; } paddr = vmf.vmf_es; paddr = (paddr << 4) + vmf.vmf_bx; scp = (struct sys_config *)BIOS_PADDRTOVADDR(paddr); if (bootverbose) { printf("BIOS SDT: model 0x%02x, submodel 0x%02x, bios_rev 0x%02x\n", scp->model, scp->submodel, scp->bios_rev); printf("BIOS SDT: features 0x%b\n", scp->feature, "\20" "\01RESV" "\02MCABUS" "\03EBDA" "\04WAITEV" "\05KBDINT" "\06RTC" "\07IC2" "\08DMA3\n"); } MCA_system = ((scp->feature & FEATURE_MCABUS) ? 1 : 0); if (MCA_system) printf("MicroChannel Architecture System detected.\n"); return; } int mca_bus_nmi (void) { int slot; int retval = 0; int pos5 = 0; /* Disable motherboard setup */ outb(MCA_MB_SETUP_REG, MCA_MB_SETUP_DIS); /* For each slot */ for (slot = 0; slot < MCA_MAX_SLOTS; slot++) { /* Select the slot */ outb(MCA_ADAP_SETUP_REG, slot | MCA_ADAP_SET); pos5 = inb(MCA_POS_REG(MCA_POS5)); /* If Adapter Check is low */ if ((pos5 & MCA_POS5_CHCK) == 0) { retval++; /* If Adapter Check Status is available */ if ((pos5 & MCA_POS5_CHCK_STAT) == 0) { printf("MCA NMI: slot %d, POS6=0x%02x, POS7=0x%02x\n", slot+1, inb( MCA_POS_REG(MCA_POS6) ), inb( MCA_POS_REG(MCA_POS7) )); } else { printf("MCA NMI: slot %d\n", slot+1); } } /* Disable adapter setup */ outb(MCA_ADAP_SETUP_REG, MCA_ADAP_SETUP_DIS); } return (retval); } -#endif diff --git a/sys/i386/isa/asc.c b/sys/i386/isa/asc.c index bcb344a7abc5..a819cddbbd6a 100644 --- a/sys/i386/isa/asc.c +++ b/sys/i386/isa/asc.c @@ -1,881 +1,878 @@ /* asc.c - device driver for hand scanners * * Current version supports: * * - AmiScan (Mustek) Color and BW hand scanners (GI1904 chipset) * * Copyright (c) 1995 Gunther Schadow. All rights reserved. * Copyright (c) 1995,1996,1997 Luigi Rizzo. All rights reserved. * * 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 * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, 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 Gunther Schadow * and Luigi Rizzo. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * $FreeBSD$ */ #include "asc.h" -#if NASC > 0 #include #include #include #include #include #include #include #include #include #include #include #include #include #include /*** *** CONSTANTS & DEFINES *** ***/ #define PROBE_FAIL 0 #define PROBE_SUCCESS IO_ASCSIZE #define ATTACH_FAIL 0 #define ATTACH_SUCCESS 1 #define SUCCESS 0 #define FAIL -1 #define INVALID FAIL #define DMA1_READY 0x08 #define ASCDEBUG #ifdef ASCDEBUG # define lprintf if(scu->flags & FLAG_DEBUG) printf #else # define lprintf (void) #endif #define TIMEOUT (hz*15) /* timeout while reading a buffer - default value */ #define ASCPRI PRIBIO /* priority while reading a buffer */ /*** *** LAYOUT OF THE MINOR NUMBER ***/ #define UNIT_MASK 0xc0 /* unit asc0 .. asc3 */ #define UNIT(x) (x >> 6) #define DBUG_MASK 0x20 #define FRMT_MASK 0x18 /* output format */ #define FRMT_RAW 0x00 /* output bits as read from scanner */ #define FRMT_GRAY 0x1 /* output gray mode for color scanner */ #define FRMT_PBM 0x08 /* output pbm format */ #define FRMT_PGM 0x18 /*** *** THE GEMOMETRY TABLE ***/ #define GREY_LINE 826 /* 825, or 826 , or 550 ??? */ static const struct asc_geom { int dpi; /* dots per inch */ int dpl; /* dots per line */ int bpl; /* bytes per line */ int g_res; /* get resolution value (ASC_STAT) */ } geomtab[] = { { 800, 3312, 414, ASC_RES_800}, { 700, 2896, 362, ASC_RES_700}, { 600, 2480, 310, ASC_RES_600}, { 500, 1656, 258, ASC_RES_500}, { 400, 1656, 207, ASC_RES_400}, { 300, 1240, 155, ASC_RES_300}, { 200, 832, 104, ASC_RES_200}, { 100, 416, 52, ASC_RES_100}, { 200, 3*GREY_LINE, 3*GREY_LINE, 0 /* returned by color scanner */}, { 200, GREY_LINE, GREY_LINE, 0 /* color scanner, grey mode */}, { INVALID, 416, 52, INVALID } /* terminator */ }; /*** *** THE TABLE OF UNITS ***/ struct _sbuf { size_t size; size_t rptr; size_t wptr; /* only changed in ascintr */ size_t count; char *base; }; struct asc_unit { long thedev; /* XXX */ int base; /* base address */ int dma_num; /* dma number */ char dma_byte; /* mask of byte for setting DMA value */ char int_byte; /* mask of byte for setting int value */ char cfg_byte; /* mirror of byte written to config reg (ASC_CFG). */ char cmd_byte; /* mirror of byte written to cmd port (ASC_CMD)*/ char portf_byte; int flags; #define ATTACHED 0x01 #define OPEN 0x02 #define READING 0x04 #define DMA_ACTIVE 0x08 #define SLEEPING 0x10 #define SEL_COLL 0x20 #define PBM_MODE 0x40 #define FLAG_DEBUG 0x80 int geometry; /* resolution as geomtab index */ int linesize; /* length of one scan line (from geom.table) */ int blen; /* length of buffer in lines */ int btime; /* timeout of buffer in seconds/hz */ struct _sbuf sbuf; long icnt; /* interrupt count XXX for debugging */ struct selinfo selp; int height; /* height, for pnm modes */ size_t bcount; /* bytes to read, for pnm modes */ }; static struct asc_unit unittab[NASC]; /*** I could not find a reasonable buffer size limit other than by *** experiments. MAXPHYS is obviously too much, while DEV_BSIZE and *** PAGE_SIZE are really too small. There must be something wrong *** with isa_dmastart/isa_dmarangecheck HELP!!! *** *** Note, must be DEFAULT_BLEN * samples_per_line <= MAX_BUFSIZE ***/ #define MAX_BUFSIZE 0xb000 /* XXX was 0x3000 */ #define DEFAULT_BLEN 16 /*** *** THE PER-DRIVER RECORD FOR ISA.C ***/ static int ascprobe (struct isa_device *isdp); static int ascattach(struct isa_device *isdp); struct isa_driver ascdriver = { ascprobe, ascattach, "asc" }; static ointhand2_t ascintr; static d_open_t ascopen; static d_close_t ascclose; static d_read_t ascread; static d_ioctl_t ascioctl; static d_poll_t ascpoll; #define CDEV_MAJOR 71 static struct cdevsw asc_cdevsw = { /* open */ ascopen, /* close */ ascclose, /* read */ ascread, /* write */ nowrite, /* ioctl */ ascioctl, /* poll */ ascpoll, /* mmap */ nommap, /* strategy */ nostrategy, /* name */ "asc", /* maj */ CDEV_MAJOR, /* dump */ nodump, /* psize */ nopsize, /* flags */ 0, /* bmaj */ -1 }; #define STATIC static /*** *** LOCALLY USED SUBROUTINES *** ***/ /*** *** get_resolution *** read resolution from the scanner ***/ static void get_resolution(struct asc_unit *scu) { int res, i, delay; res=0; scu->cmd_byte = ASC_STANDBY; outb(ASC_CMD, scu->cmd_byte); tsleep((caddr_t)scu, ASCPRI | PCATCH, "ascres", hz/10); for(delay= 100; (res=inb(ASC_STAT)) & ASC_RDY_FLAG; delay--) { i = tsleep((caddr_t)scu, ASCPRI | PCATCH, "ascres0", 1); if ( ( i == 0 ) || ( i == EWOULDBLOCK ) ) i = SUCCESS; else break; } if (delay==0) { lprintf("asc.get_resolution: timeout completing command\n"); return /* -1 */; } /* ... actual read resolution... */ res &= ASC_RES_MASK; for (i=0; geomtab[i].dpi != INVALID; i++) { if (geomtab[i].g_res == res) break; } if (geomtab[i].dpi==INVALID) { scu->geometry= i; /* INVALID; */ lprintf("asc.get_resolution: wrong resolution\n"); } else { lprintf("asc.get_resolution: %d dpi\n",geomtab[i].dpi); scu->geometry = i; } scu->portf_byte=0; /* default */ if (geomtab[scu->geometry].g_res==0 && !(scu->thedev&FRMT_GRAY)) { /* color scanner seems to require this */ scu->portf_byte=2; /* scu->geometry++; */ } scu->linesize = geomtab[scu->geometry].bpl; scu->height = geomtab[scu->geometry].dpl; /* default... */ } /*** *** buffer_allocate *** allocate/reallocate a buffer *** Now just checks that the preallocated buffer is large enough. ***/ static int buffer_allocate(struct asc_unit *scu) { size_t size, size1; size = scu->blen * scu->linesize; lprintf("asc.buffer_allocate: need 0x%x bytes\n", size); if ( size > MAX_BUFSIZE ) { size1=size; size= ( (MAX_BUFSIZE+scu->linesize-1) / scu->linesize)*scu->linesize; lprintf("asc.buffer_allocate: 0x%x bytes are too much, try 0x%x\n", size1, size); return ENOMEM; } scu->sbuf.size = size; scu->sbuf.rptr = 0; scu->sbuf.wptr = 0; scu->sbuf.count = 0; /* available data for reading */ lprintf("asc.buffer_allocate: ok\n"); return SUCCESS; } /*** dma_restart *** invoked locally to start dma. Must run in a critical section ***/ static void dma_restart(struct asc_unit *scu) { unsigned char al=scu->cmd_byte; if (geomtab[scu->geometry].g_res==0) {/* color */ isa_dmastart(B_READ, scu->sbuf.base+scu->sbuf.wptr, scu->linesize + 90 /* XXX */ , scu->dma_num); /* * looks like we have to set and then clear this * bit to enable the scanner to send interrupts */ outb( ASC_CMD, al |= 4 ); /* seems to disable interrupts */ #if 0 outb( ASC_CMD, al |= 8 ); /* ??? seems useless */ #endif outb( ASC_CMD, al &= 0xfb ); scu->cmd_byte = al; } else { /* normal */ isa_dmastart(B_READ, scu->sbuf.base+scu->sbuf.wptr, scu->linesize, scu->dma_num); /*** this is done in sub_20, after dmastart ? ***/ #if 0 outb( ASC_CMD, al |= 4 ); outb( ASC_CMD, al |= 8 ); /* ??? seems useless */ outb( ASC_CMD, al &= 0xfb ); scu->cmd_byte = al; #else outb( ASC_CMD, ASC_OPERATE); #endif } scu->flags |= DMA_ACTIVE; } /*** *** the main functions ***/ /*** asc_reset *** resets the scanner and the config bytes... ***/ static void asc_reset(struct asc_unit *scu) { scu->cfg_byte = 0 ; /* clear... */ scu->cmd_byte = 0 ; /* clear... */ outb(ASC_CFG,scu->cfg_byte); /* for safety, do this here */ outb(ASC_CMD,scu->cmd_byte); /* probably not needed */ tsleep((caddr_t)scu, ASCPRI | PCATCH, "ascres", hz/10); /* sleep .1 sec */ scu->blen = DEFAULT_BLEN; scu->btime = TIMEOUT; scu->height = 0 ; /* don't know better... */ } /************************************************************************** *** *** ascprobe *** read status port and check for proper configuration: *** - if address group matches (status byte has reasonable value) *** cannot check interrupt/dma, only clear the config byte. ***/ static int ascprobe (struct isa_device *isdp) { int unit = isdp->id_unit; struct asc_unit *scu = unittab + unit; int stb; static int once; if (!once++) cdevsw_add(&asc_cdevsw); scu->base = isdp->id_iobase; /*** needed by the following macros ***/ scu->flags = FLAG_DEBUG; if ( isdp->id_iobase < 0 ) { lprintf("asc%d.probe: no iobase given\n", unit); return PROBE_FAIL; } if ((stb=inb(ASC_PROBE)) != ASC_PROBE_VALUE) { lprintf("asc%d.probe: failed, got 0x%02x instead of 0x%02x\n", unit, stb, ASC_PROBE_VALUE); return PROBE_FAIL; } /* * NOTE NOTE NOTE * the new AmiScan Color board uses int 10,11,12 instead of 3,5,10 * respectively. This means that the driver must act accordingly. * Unfortunately there is no easy way of telling which board one has, * other than trying to get an interrupt and noticing that it is * missing. use "option ASC_NEW_BOARD" if you have a new board. * */ #if ASC_NEW_BOARD #define ASC_IRQ_A 10 #define ASC_IRQ_B 11 #define ASC_IRQ_C 12 #else #define ASC_IRQ_A 3 #define ASC_IRQ_B 5 #define ASC_IRQ_C 10 #endif switch(ffs(isdp->id_irq) - 1) { case ASC_IRQ_A : scu->int_byte = ASC_CNF_IRQ3; break; case ASC_IRQ_B : scu->int_byte = ASC_CNF_IRQ5; break; case ASC_IRQ_C : scu->int_byte = ASC_CNF_IRQ10; break; #if 0 case -1: scu->int_byte = 0; lprintf("asc%d.probe: warning - going interruptless\n", unit); break; #endif default: lprintf("asc%d.probe: unsupported INT %d (only 3, 5, 10)\n", unit, ffs(isdp->id_irq) - 1 ); return PROBE_FAIL; } scu->dma_num = isdp->id_drq; switch(scu->dma_num) { case 1: scu->dma_byte = ASC_CNF_DMA1; break; case 3: scu->dma_byte = ASC_CNF_DMA3; break; default: lprintf("asc%d.probe: unsupported DMA %d (only 1 or 3)\n", unit, scu->dma_num); return PROBE_FAIL; } asc_reset(scu); /* lprintf("asc%d.probe: ok\n", unit); */ scu->flags &= ~FLAG_DEBUG; scu->icnt = 0; return PROBE_SUCCESS; } /************************************************************************** *** *** ascattach *** finish initialization of unit structure, get geometry value (?) ***/ static int ascattach(struct isa_device *isdp) { int unit = isdp->id_unit; struct asc_unit *scu = unittab + unit; isdp->id_ointr = ascintr; scu->flags |= FLAG_DEBUG; printf("asc%d: [GI1904/Trust Ami-Scan Grey/Color]\n", unit); /* * Initialize buffer structure. * XXX this must be done early to give a good chance of getting a * contiguous buffer. This wastes memory. */ scu->sbuf.base = contigmalloc((unsigned long)MAX_BUFSIZE, M_DEVBUF, M_NOWAIT, 0ul, 0xfffffful, 1ul, 0x10000ul); if ( scu->sbuf.base == NULL ) { lprintf("asc%d.attach: buffer allocation failed\n", unit); return ATTACH_FAIL; /* XXX attach must not fail */ } scu->sbuf.size = INVALID; scu->sbuf.rptr = INVALID; scu->flags |= ATTACHED; /* lprintf("asc%d.attach: ok\n", unit); */ scu->flags &= ~FLAG_DEBUG; scu->selp.si_flags=0; scu->selp.si_pid=(pid_t)0; #define ASC_UID 0 #define ASC_GID 13 make_dev(&asc_cdevsw, unit<<6, ASC_UID, ASC_GID, 0666, "asc%d", unit); make_dev(&asc_cdevsw, ((unit<<6) + FRMT_PBM), ASC_UID, ASC_GID, 0666, "asc%dp", unit); make_dev(&asc_cdevsw, ((unit<<6) + DBUG_MASK), ASC_UID, ASC_GID, 0666, "asc%dd", unit); make_dev(&asc_cdevsw, ((unit<<6) + DBUG_MASK+FRMT_PBM), ASC_UID, ASC_GID, 0666, "asc%dpd", unit); return ATTACH_SUCCESS; } /************************************************************************** *** *** ascintr *** the interrupt routine, at the end of DMA... ***/ static void ascintr(int unit) { struct asc_unit *scu = unittab + unit; int chan_bit = 0x01 << scu->dma_num; scu->icnt++; /* ignore stray interrupts... */ if ((scu->flags & (OPEN |READING)) != (OPEN | READING) ) { /* must be after closing... */ scu->flags &= ~(OPEN | READING | DMA_ACTIVE | SLEEPING | SEL_COLL); return; } if ( (scu->flags & DMA_ACTIVE) && (inb(DMA1_READY) & chan_bit) != 0) { outb( ASC_CMD, ASC_STANDBY); scu->flags &= ~DMA_ACTIVE; /* bounce buffers... */ isa_dmadone(B_READ, scu->sbuf.base+scu->sbuf.wptr, scu->linesize, scu->dma_num); scu->sbuf.wptr += scu->linesize; if (scu->sbuf.wptr >= scu->sbuf.size) scu->sbuf.wptr=0; scu->sbuf.count += scu->linesize; if (scu->flags & SLEEPING) { scu->flags &= ~SLEEPING; wakeup((caddr_t)scu); } if (scu->sbuf.size - scu->sbuf.count >= scu->linesize) { dma_restart(scu); } if (scu->selp.si_pid) { selwakeup(&scu->selp); scu->selp.si_pid=(pid_t)0; scu->selp.si_flags = 0; } } } /************************************************************************** *** *** ascopen *** set open flag, set modes according to minor number *** FOR RELEASE: *** don't switch scanner on, wait until first read or ioctls go before ***/ STATIC int ascopen(dev_t dev, int flags, int fmt, struct proc *p) { struct asc_unit *scu; int unit; unit = UNIT(minor(dev)) & UNIT_MASK; if ( unit >= NASC ) { #ifdef ASCDEBUG /* XXX lprintf isn't valid here since there is no scu. */ printf("asc%d.open: unconfigured unit number (max %d)\n", unit, NASC); #endif return ENXIO; } scu = unittab + unit; if ( !( scu->flags & ATTACHED ) ) { lprintf("asc%d.open: unit was not attached successfully 0x%04x\n", unit, scu->flags); return ENXIO; } if ( minor(dev) & DBUG_MASK ) scu->flags |= FLAG_DEBUG; else scu->flags &= ~FLAG_DEBUG; switch(minor(dev) & FRMT_MASK) { case FRMT_PBM: scu->flags |= PBM_MODE; lprintf("asc%d.open: pbm mode\n", unit); break; case FRMT_RAW: lprintf("asc%d.open: raw mode\n", unit); scu->flags &= ~PBM_MODE; break; default: lprintf("asc%d.open: gray maps are not yet supported", unit); return ENXIO; } lprintf("asc%d.open: minor %d icnt %ld\n", unit, minor(dev), scu->icnt); if ( scu->flags & OPEN ) { lprintf("asc%d.open: already open", unit); return EBUSY; } if (isa_dma_acquire(scu->dma_num)) return(EBUSY); scu->flags = ATTACHED | OPEN; asc_reset(scu); get_resolution(scu); return SUCCESS; } static int asc_startread(struct asc_unit *scu) { /*** from here on, things can be delayed to the first read/ioctl ***/ /*** this was done in sub_12... ***/ scu->cfg_byte= scu->cmd_byte=0; /* init scanner */ outb(ASC_CMD, scu->cmd_byte); /*** this was done in sub_16, set scan len... ***/ outb(ASC_BOH, scu->portf_byte ); if (geomtab[scu->geometry].g_res==0) { /* color */ scu->cmd_byte = 0x00 ; } else { scu->cmd_byte = 0x90 ; } outb(ASC_CMD, scu->cmd_byte); outb(ASC_LEN_L, scu->linesize & 0xff /* len_low */); outb(ASC_LEN_H, (scu->linesize >>8) & 0xff /* len_high */); /*** this was done in sub_21, config DMA ... ***/ scu->cfg_byte |= scu->dma_byte; outb(ASC_CFG, scu->cfg_byte); /*** sub_22: enable int on the scanner ***/ scu->cfg_byte |= scu->int_byte; outb(ASC_CFG, scu->cfg_byte); /*** sub_28: light on etc...***/ scu->cmd_byte = ASC_STANDBY; outb(ASC_CMD, scu->cmd_byte); tsleep((caddr_t)scu, ASCPRI | PCATCH, "ascstrd", hz/10); /* sleep .1 sec */ return SUCCESS; } /************************************************************************** *** *** ascclose *** turn off scanner, release the buffer *** should probably terminate dma ops, release int and dma. lr 12mar95 ***/ STATIC int ascclose(dev_t dev, int flags, int fmt, struct proc *p) { int unit = UNIT(minor(dev)); struct asc_unit *scu = unittab + unit; lprintf("asc%d.close: minor %d\n", unit, minor(dev)); if ( unit >= NASC || !( scu->flags & ATTACHED ) ) { lprintf("asc%d.close: unit was not attached successfully 0x%04x\n", unit, scu->flags); return ENXIO; } /* all this is in sub_29... */ /* cli(); */ outb(ASC_CFG, 0 ); /* don't save in CFG byte!!! */ scu->cmd_byte &= ~ASC_LIGHT_ON; outb(ASC_CMD, scu->cmd_byte);/* light off */ tsleep((caddr_t)scu, ASCPRI | PCATCH, "ascclo", hz/2); /* sleep 1/2 sec */ scu->cfg_byte &= ~ scu->dma_byte ; /* disable scanner dma */ scu->cfg_byte &= ~ scu->int_byte ; /* disable scanner int */ outb(ASC_CFG, scu->cfg_byte); /* --- disable dma controller ? --- */ isa_dma_release(scu->dma_num); /* --- disable interrupts on the controller (sub_24) --- */ scu->sbuf.size = INVALID; scu->sbuf.rptr = INVALID; scu->flags &= ~(FLAG_DEBUG | OPEN | READING); return SUCCESS; } static void pbm_init(struct asc_unit *scu) { int width = geomtab[scu->geometry].dpl; int l= sprintf(scu->sbuf.base,"P4 %d %d\n", width, scu->height); char *p; scu->bcount = scu->height * width / 8 + l; /* move header to end of sbuf */ scu->sbuf.rptr=scu->sbuf.size-l; bcopy(scu->sbuf.base, scu->sbuf.base+scu->sbuf.rptr,l); scu->sbuf.count = l; if (geomtab[scu->geometry].g_res!=0) { /* BW scanner */ for(p = scu->sbuf.base + scu->sbuf.rptr; l; p++, l--) *p = ~*p; } } /************************************************************************** *** *** ascread ***/ STATIC int ascread(dev_t dev, struct uio *uio, int ioflag) { int unit = UNIT(minor(dev)); struct asc_unit *scu = unittab + unit; size_t nbytes; int sps, res; unsigned char *p; lprintf("asc%d.read: minor %d icnt %ld\n", unit, minor(dev), scu->icnt); if ( unit >= NASC || !( scu->flags & ATTACHED ) ) { lprintf("asc%d.read: unit was not attached successfully 0x%04x\n", unit, scu->flags); return ENXIO; } if ( !(scu->flags & READING) ) { /*** first read... ***/ /* allocate a buffer for reading data and init things */ if ( (res = buffer_allocate(scu)) == SUCCESS ) scu->flags |= READING; else return res; asc_startread(scu); if ( scu->flags & PBM_MODE ) { /* initialize for pbm mode */ pbm_init(scu); } } lprintf("asc%d.read(before): " "sz 0x%x, rptr 0x%x, wptr 0x%x, cnt 0x%x bcnt 0x%x flags 0x%x icnt %ld\n", unit, scu->sbuf.size, scu->sbuf.rptr, scu->sbuf.wptr, scu->sbuf.count, scu->bcount,scu->flags, scu->icnt); sps=spltty(); if ( scu->sbuf.count == 0 ) { /* no data avail., must wait */ if (!(scu->flags & DMA_ACTIVE)) dma_restart(scu); scu->flags |= SLEEPING; res = tsleep((caddr_t)scu, ASCPRI | PCATCH, "ascread", 0); scu->flags &= ~SLEEPING; if ( res == 0 ) res = SUCCESS; } splx(sps); /* lower priority... */ if (scu->flags & FLAG_DEBUG) tsleep((caddr_t)scu, ASCPRI | PCATCH, "ascdly",hz); lprintf("asc%d.read(after): " "sz 0x%x, rptr 0x%x, wptr 0x%x, cnt 0x%x bcnt 0x%x flags 0x%x icnt %ld\n", unit, scu->sbuf.size, scu->sbuf.rptr, scu->sbuf.wptr, scu->sbuf.count, scu->bcount,scu->flags,scu->icnt); /* first, not more than available... */ nbytes = min( uio->uio_resid, scu->sbuf.count ); /* second, contiguous data... */ nbytes = min( nbytes, (scu->sbuf.size - scu->sbuf.rptr) ); /* third, one line (will remove this later, XXX) */ nbytes = min( nbytes, scu->linesize ); if ( (scu->flags & PBM_MODE) ) nbytes = min( nbytes, scu->bcount ); lprintf("asc%d.read: transferring 0x%x bytes\n", unit, nbytes); if (geomtab[scu->geometry].g_res!=0) { /* BW scanner */ lprintf("asc%d.read: invert buffer\n",unit); for(p = scu->sbuf.base + scu->sbuf.rptr, res=nbytes; res; p++, res--) *p = ~*p; } res = uiomove(scu->sbuf.base + scu->sbuf.rptr, nbytes, uio); if ( res != SUCCESS ) { lprintf("asc%d.read: uiomove failed %d", unit, res); return res; } sps=spltty(); scu->sbuf.rptr += nbytes; if (scu->sbuf.rptr >= scu->sbuf.size) scu->sbuf.rptr=0; scu->sbuf.count -= nbytes; /* having moved some data, can read mode */ if (!(scu->flags & DMA_ACTIVE)) dma_restart(scu); splx(sps); /* lower priority... */ if ( scu->flags & PBM_MODE ) scu->bcount -= nbytes; lprintf("asc%d.read: size 0x%x, pointer 0x%x, bcount 0x%x, ok\n", unit, scu->sbuf.size, scu->sbuf.rptr, scu->bcount); return SUCCESS; } /************************************************************************** *** *** ascioctl ***/ STATIC int ascioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p) { int unit = UNIT(minor(dev)); struct asc_unit *scu = unittab + unit; lprintf("asc%d.ioctl: minor %d\n", unit, minor(dev)); if ( unit >= NASC || !( scu->flags & ATTACHED ) ) { lprintf("asc%d.ioctl: unit was not attached successfully 0x%04x\n", unit, scu->flags); return ENXIO; } switch(cmd) { case ASC_GRES: asc_reset(scu); get_resolution(scu); *(int *)data=geomtab[scu->geometry].dpi; lprintf("asc%d.ioctl:ASC_GRES %ddpi\n", unit, *(int *)data); return SUCCESS; case ASC_GWIDTH: *(int *)data=geomtab[scu->geometry].dpl; lprintf("asc%d.ioctl:ASC_GWIDTH %d\n", unit, *(int *)data); return SUCCESS; case ASC_GHEIGHT: *(int *)data=scu->height; lprintf("asc%d.ioctl:ASC_GHEIGHT %d\n", unit, *(int *)data); return SUCCESS; case ASC_SHEIGHT: lprintf("asc%d.ioctl:ASC_SHEIGHT %d\n", unit, *(int *)data); if ( scu->flags & READING ) { lprintf("asc%d:ioctl on already reading unit\n", unit); return EBUSY; } scu->height=*(int *)data; return SUCCESS; #if 0 case ASC_GBLEN: *(int *)data=scu->blen; lprintf("asc%d.ioctl:ASC_GBLEN %d\n", unit, *(int *)data); return SUCCESS; case ASC_SBLEN: lprintf("asc%d.ioctl:ASC_SBLEN %d\n", unit, *(int *)data); if (*(int *)data * geomtab[scu->geometry].dpl / 8 > MAX_BUFSIZE) { lprintf("asc%d:ioctl buffer size too high\n", unit); return ENOMEM; } scu->blen=*(int *)data; return SUCCESS; case ASC_GBTIME: *(int *)data = scu->btime / hz; lprintf("asc%d.ioctl:ASC_GBTIME %d\n", unit, *(int *)data); return SUCCESS; case ASC_SBTIME: scu->btime = *(int *)data * hz; lprintf("asc%d.ioctl:ASC_SBTIME %d\n", unit, *(int *)data); return SUCCESS; #endif default: return ENOTTY; } return SUCCESS; } STATIC int ascpoll(dev_t dev, int events, struct proc *p) { int unit = UNIT(minor(dev)); struct asc_unit *scu = unittab + unit; int sps; struct proc *p1; int revents = 0; sps=spltty(); if (events & (POLLIN | POLLRDNORM)) { if (scu->sbuf.count >0) revents |= events & (POLLIN | POLLRDNORM); else { if (!(scu->flags & DMA_ACTIVE)) dma_restart(scu); if (scu->selp.si_pid && (p1=pfind(scu->selp.si_pid)) && p1->p_wchan == (caddr_t)&selwait) scu->selp.si_flags = SI_COLL; else scu->selp.si_pid = p->p_pid; } } splx(sps); return 0; } - -#endif /* NASC > 0 */ diff --git a/sys/i386/isa/ctx.c b/sys/i386/isa/ctx.c index 462ca2883a57..2aaa5aa6a2e5 100644 --- a/sys/i386/isa/ctx.c +++ b/sys/i386/isa/ctx.c @@ -1,453 +1,450 @@ /* * CORTEX-I Frame Grabber driver V1.0 * * Copyright (C) 1994, Paul S. LaFollette, Jr. This software may be used, * modified, copied, distributed, and sold, in both source and binary form * provided that the above copyright and these terms are retained. Under * no circumstances is the author responsible for the proper functioning * of this software, nor does the author assume any responsibility * for damages incurred with its use. * * $FreeBSD$ */ /* * * * * Device Driver for CORTEX-I Frame Grabber * Made by ImageNation Corporation * 1200 N.E. Keyues Road * Vancouver, WA 98684 (206) 944-9131 * (I have no ties to this company, just thought you might want * to know how to get in touch with them.) * * In order to understand this device, you really need to consult the * manual which ImageNation provides when you buy the board. (And * what a pleasure it is to buy something for a PC and actually get * programming information along with it.) I will limit myself here to * a few comments which are specific to this driver. See also the file * ctxreg.h for definitions of registers and control bits. * * 1. Although the hardware supports low resolution (256 x 256) * acqusition and display, I have not implemented access to * these modes in this driver. There are some fairly quirky * aspects to the way this board works in low resolution mode, * and I don't want to deal with them. Maybe later. * * 2. Choosing the base address for the video memory: This is set * using a combination of hardware and software, using the left * most dip switch on the board, and the AB_SELECT bit of control * port 1, according to the chart below: * * Left DIP switch || DOWN | UP | * ================================================= * AB_SELECT = 0 || 0xA0000 | 0xB0000 | * ------------------------------------------------- * AB_SELECT = 1 || 0xD0000 | 0xE0000 | * ------------------------------------------------ * * When the RAM_ENABLE bit of control port 1 is clear (0), the * video ram is disconnected from the computer bus. This makes * it possible, in principle, to share memory space with other * devices (such as VGA) which can also disconnect themselves * from the bus. It also means that multiple CORTEX-I boards * can share the same video memory space. Disconnecting from the * bus does not affect the video display of the video ram contents, * so that one needs only set the RAM_ENABLE bit when actually * reading or writing to memory. The cost of this is low, * the benefits to me are great (I need more than one board * in my machine, and 0xE0000 is the only address choice that * doesn't conflict with anything) so I adopt this strategy here. * * XXX-Note... this driver has only been tested for the * XXX base = 0xE0000 case! * * 3) There is a deficiency in the documentation from ImageNation, I * think. In order to successfully load the lookup table, it is * necessary to clear SEE_STORED_VIDEO in control port 0 as well as * setting LUT_LOAD_ENABLE in control port 1. * * 4) This driver accesses video memory through read or write operations. * Other functionality is provided through ioctl's, manifest * constants for which are defined in ioctl_ctx.h. The ioctl's * include: * CTX_LIVE Display live video * CTX_GRAB Grab a frame of video data * CTX_H_ORGANIZE Set things up so that sequential read * operations access horizontal lines of * pixels. * CTX_V_ORGANIZE Set things up so that sequential read * operations access vertical lines of * pixels. * CTX_SET_LUT Set the lookup table from an array * of 256 unsigned chars passed as the * third parameter to ioctl. * CTX_GET_LUT Return the current lookup table to * the application as an array of 256 * unsigned chars. Again the third * parameter to the ioctl call. * * Thus, * ioctl(fi, CTX_H_ORGANIZE, 0); * lseek(fi, y*512, SEEK_SET); * read(fi, buffer, 512); * * will fill buffer with 512 pixels (unsigned chars) which represent * the y-th horizontal line of the image. * Similarly, * ioctl(fi, CTX_V_ORGANIZE, 0: * lseek(fi, x*512+y, SEEK_SET); * read(fi, buffer, 10); * * will read 10 a vertical line of 10 pixels starting at (x,y). * * Obviously, this sort of ugliness needs to be hidden away from * the casual user, with an appropriate set of higher level * functions. * */ #include "ctx.h" -#if NCTX > 0 #include #include #include #include #include #include #include #include #include #include static int waitvb(int port); /* state flags */ #define OPEN (0x01) /* device is open */ #define UNIT(x) ((x) & 0x07) static int ctxprobe __P((struct isa_device *devp)); static int ctxattach __P((struct isa_device *devp)); struct isa_driver ctxdriver = {ctxprobe, ctxattach, "ctx"}; static d_open_t ctxopen; static d_close_t ctxclose; static d_read_t ctxread; static d_write_t ctxwrite; static d_ioctl_t ctxioctl; #define CDEV_MAJOR 40 static struct cdevsw ctx_cdevsw = { /* open */ ctxopen, /* close */ ctxclose, /* read */ ctxread, /* write */ ctxwrite, /* ioctl */ ctxioctl, /* poll */ nopoll, /* mmap */ nommap, /* strategy */ nostrategy, /* name */ "ctx", /* maj */ CDEV_MAJOR, /* dump */ nodump, /* psize */ nopsize, /* flags */ 0, /* bmaj */ -1 }; #define LUTSIZE 256 /* buffer size for Look Up Table (LUT) */ #define PAGESIZE 65536 /* size of one video page, 1/4 of the screen */ /* * Per unit shadow registers (because the dumb hardware is RO) */ static struct ctx_soft_registers { u_char *lutp; u_char cp0; u_char cp1; u_char flag; int iobase; caddr_t maddr; int msize; } ctx_sr[NCTX]; static int ctxprobe(struct isa_device * devp) { int status; static int once; if (!once++) cdevsw_add(&ctx_cdevsw); if (inb(devp->id_iobase) == 0xff) /* 0xff only if board absent */ status = 0; else { status = 1; /*XXX uses only one port? */ } return (status); } static int ctxattach(struct isa_device * devp) { struct ctx_soft_registers *sr; sr = &(ctx_sr[devp->id_unit]); sr->cp0 = 0; /* zero out the shadow registers */ sr->cp1 = 0; /* and the open flag. wait for */ sr->flag = 0; /* open to malloc the LUT space */ sr->iobase = devp->id_iobase; sr->maddr = devp->id_maddr; sr->msize = devp->id_msize; make_dev(&ctx_cdevsw, 0, 0, 0, 0600, "ctx%d", devp->id_unit); return (1); } static int ctxopen(dev_t dev, int flags, int fmt, struct proc *p) { struct ctx_soft_registers *sr; u_char unit; int i; unit = UNIT(minor(dev)); /* minor number out of range? */ if (unit >= NCTX) return (ENXIO); sr = &(ctx_sr[unit]); if (sr->flag != 0) /* someone has already opened us */ return (EBUSY); /* get space for the LUT buffer */ sr->lutp = malloc(LUTSIZE, M_DEVBUF, M_WAITOK); if (sr->lutp == NULL) return (ENOMEM); sr->flag = OPEN; /* Set up the shadow registers. We don't actually write these values to the control ports until after we finish loading the lookup table. */ sr->cp0 |= SEE_STORED_VIDEO; if ((kvtop(sr->maddr) == 0xB0000) || (kvtop(sr->maddr) == 0xE0000)) sr->cp1 |= AB_SELECT; /* map to B or E if necessary */ /* but don't enable RAM */ /* Set up the lookup table initially so that it is transparent. */ outb(sr->iobase + ctx_cp0, (u_char) 0); outb(sr->iobase + ctx_cp1, (u_char) (LUT_LOAD_ENABLE | BLANK_DISPLAY)); for (i = 0; i < LUTSIZE; i++) { outb(sr->iobase + ctx_lutaddr, (u_char) i); sr->lutp[i] = (u_char) i; outb(sr->iobase + ctx_lutdata, (u_char) sr->lutp[i]); } /* Disable LUT loading, and push the data in the shadow registers into the control ports. */ outb(sr->iobase + ctx_cp0, sr->cp0); outb(sr->iobase + ctx_cp1, sr->cp1); return (0); /* successful open. All ready to go. */ } static int ctxclose(dev_t dev, int flags, int fmt, struct proc *p) { int unit; unit = UNIT(minor(dev)); ctx_sr[unit].flag = 0; free(ctx_sr[unit].lutp, M_DEVBUF); ctx_sr[unit].lutp = NULL; return (0); } static int ctxwrite(dev_t dev, struct uio * uio, int ioflag) { int unit, status = 0; int page, count, offset; struct ctx_soft_registers *sr; unit = UNIT(minor(dev)); sr = &(ctx_sr[unit]); if (uio->uio_offset < 0) return (EINVAL); if (uio->uio_offset >= 4 * PAGESIZE) page = 4; /* EOF */ else page = (u_int)uio->uio_offset / PAGESIZE; offset = (u_int)uio->uio_offset % PAGESIZE; count = min(uio->uio_resid, PAGESIZE - offset); while ((page >= 0) && (page <= 3) && (count > 0)) { sr->cp0 &= ~3; sr->cp0 |= page; outb(sr->iobase + ctx_cp0, sr->cp0); /* Before doing the uiomove, we need to "connect" the frame buffer ram to the machine bus. This is done here so that we can have several different boards installed, all sharing the same memory space... each board is only "connected" to the bus when its memory is actually being read or written. All my instincts tell me that I should disable interrupts here, so I have done so. */ disable_intr(); sr->cp1 |= RAM_ENABLE; outb(sr->iobase + ctx_cp1, sr->cp1); status = uiomove(sr->maddr + offset, count, uio); sr->cp1 &= ~RAM_ENABLE; outb(sr->iobase + ctx_cp1, sr->cp1); enable_intr(); page = (u_int)uio->uio_offset / PAGESIZE; offset = (u_int)uio->uio_offset % PAGESIZE; count = min(uio->uio_resid, PAGESIZE - offset); } if (uio->uio_resid > 0) return (ENOSPC); else return (status); } static int ctxread(dev_t dev, struct uio * uio, int ioflag) { int unit, status = 0; int page, count, offset; struct ctx_soft_registers *sr; unit = UNIT(minor(dev)); sr = &(ctx_sr[unit]); if (uio->uio_offset < 0) return (EINVAL); if (uio->uio_offset >= 4 * PAGESIZE) page = 4; /* EOF */ else page = (u_int)uio->uio_offset / PAGESIZE; offset = (u_int)uio->uio_offset % PAGESIZE; count = min(uio->uio_resid, PAGESIZE - offset); while ((page >= 0) && (page <= 3) && (count > 0)) { sr->cp0 &= ~3; sr->cp0 |= page; outb(sr->iobase + ctx_cp0, sr->cp0); /* Before doing the uiomove, we need to "connect" the frame buffer ram to the machine bus. This is done here so that we can have several different boards installed, all sharing the same memory space... each board is only "connected" to the bus when its memory is actually being read or written. All my instincts tell me that I should disable interrupts here, so I have done so. */ disable_intr(); sr->cp1 |= RAM_ENABLE; outb(sr->iobase + ctx_cp1, sr->cp1); status = uiomove(sr->maddr + offset, count, uio); sr->cp1 &= ~RAM_ENABLE; outb(sr->iobase + ctx_cp1, sr->cp1); enable_intr(); page = (u_int)uio->uio_offset / PAGESIZE; offset = (u_int)uio->uio_offset % PAGESIZE; count = min(uio->uio_resid, PAGESIZE - offset); } if (uio->uio_resid > 0) return (ENOSPC); else return (status); } static int ctxioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p) { int error; int unit, i; struct ctx_soft_registers *sr; error = 0; unit = UNIT(minor(dev)); sr = &(ctx_sr[unit]); switch (cmd) { case CTX_LIVE: sr->cp0 &= ~SEE_STORED_VIDEO; outb(sr->iobase + ctx_cp0, sr->cp0); break; case CTX_GRAB: sr->cp0 &= ~SEE_STORED_VIDEO; outb(sr->iobase + ctx_cp0, sr->cp0); sr->cp0 |= ACQUIRE; if (waitvb(sr->iobase)) /* wait for vert blank to start * acquire */ error = ENODEV; outb(sr->iobase + ctx_cp0, sr->cp0); if (waitvb(sr->iobase)) /* wait for two more to finish acquire */ error = ENODEV; if (waitvb(sr->iobase)) error = ENODEV; sr->cp0 &= ~ACQUIRE; /* turn off acquire and turn on * display */ sr->cp0 |= SEE_STORED_VIDEO; outb(sr->iobase + ctx_cp0, sr->cp0); break; case CTX_H_ORGANIZE: sr->cp0 &= ~PAGE_ROTATE; outb(sr->iobase + ctx_cp0, sr->cp0); break; case CTX_V_ORGANIZE: sr->cp0 |= PAGE_ROTATE; outb(sr->iobase + ctx_cp0, sr->cp0); break; case CTX_SET_LUT: bcopy((u_char *) data, sr->lutp, LUTSIZE); outb(sr->iobase + ctx_cp0, (u_char) 0); outb(sr->iobase + ctx_cp1, (u_char) (LUT_LOAD_ENABLE | BLANK_DISPLAY)); for (i = 0; i < LUTSIZE; i++) { outb(sr->iobase + ctx_lutaddr, i); outb(sr->iobase + ctx_lutdata, sr->lutp[i]); } outb(sr->iobase + ctx_cp0, sr->cp0); /* restore control * registers */ outb(sr->iobase + ctx_cp1, sr->cp1); break; case CTX_GET_LUT: bcopy(sr->lutp, (u_char *) data, LUTSIZE); break; default: error = ENODEV; } return (error); } static int waitvb(int port) { /* wait for a vertical blank, */ if (inb(port) == 0xff) /* 0xff means no board present */ return (1); while ((inb(port) & VERTICAL_BLANK) != 0) { } while ((inb(port) & VERTICAL_BLANK) == 0) { } return (0); } - -#endif /* NCTX > 0 */ diff --git a/sys/i386/isa/cx.c b/sys/i386/isa/cx.c index 9240be577e23..3bca982abb51 100644 --- a/sys/i386/isa/cx.c +++ b/sys/i386/isa/cx.c @@ -1,957 +1,955 @@ /* * Cronyx-Sigma adapter driver for FreeBSD. * Supports PPP/HDLC protocol in synchronous mode, * and asyncronous channels with full modem control. * * Copyright (C) 1994 Cronyx Ltd. * Author: Serge Vakulenko, * * This software is distributed with NO WARRANTIES, not even the implied * warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * Authors grant any other persons or organisations permission to use * or modify this software as long as this message is kept with the software, * all derivative works or modified versions. * * Version 1.9, Wed Oct 4 18:58:15 MSK 1995 * * $FreeBSD$ * */ #undef DEBUG #include "cx.h" -#if NCX > 0 #include #include #include #include #include #include #include #include #include #ifdef __FreeBSD__ # if __FreeBSD__ < 2 # include # define RB_GETC(q) getc(q) # endif #endif #ifdef __bsdi__ # include # include # define tsleep(tp,pri,msg,x) ((tp)->t_state |= TS_WOPEN,\ ttysleep (tp, (caddr_t)&tp->t_rawq, pri, msg, x)) #endif #if !defined (__FreeBSD__) || __FreeBSD__ >= 2 # define t_out t_outq # define RB_LEN(q) ((q).c_cc) # define RB_GETC(q) getc(&q) #ifndef TSA_CARR_ON /* FreeBSD 2.x before not long after 2.0.5 */ # define TSA_CARR_ON(tp) tp # define TSA_OLOWAT(q) ((caddr_t)&(q)->t_out) #endif #endif #include #include /* XXX imported from if_cx.c. */ void cxswitch (cx_chan_t *c, cx_soft_opt_t new); /* XXX exported. */ void cxmint (cx_chan_t *c); int cxrinta (cx_chan_t *c); void cxtinta (cx_chan_t *c); timeout_t cxtimeout; #ifdef DEBUG # define print(s) printf s #else # define print(s) {/*void*/} #endif #define DMABUFSZ (6*256) /* buffer size */ #define BYTE *(unsigned char*)& #define UNIT(u) (minor(u) & 077) #define UNIT_CTL 077 extern cx_board_t cxboard [NCX]; /* adapter state structures */ extern cx_chan_t *cxchan [NCX*NCHAN]; /* unit to channel struct pointer */ #if __FreeBSD__ >= 2 static struct tty cx_tty [NCX*NCHAN]; /* tty data */ static d_open_t cxopen; static d_close_t cxclose; static d_ioctl_t cxioctl; #define CDEV_MAJOR 42 /* Don't make this static, since if_cx.c uses it. */ struct cdevsw cx_cdevsw = { /* open */ cxopen, /* close */ cxclose, /* read */ ttyread, /* write */ ttywrite, /* ioctl */ cxioctl, /* poll */ ttypoll, /* mmap */ nommap, /* strategy */ nostrategy, /* name */ "cx", /* maj */ CDEV_MAJOR, /* dump */ nodump, /* psize */ nopsize, /* flags */ D_TTY, /* bmaj */ -1 }; #else struct tty *cx_tty [NCX*NCHAN]; /* tty data */ #endif static void cxoproc (struct tty *tp); static void cxstop (struct tty *tp, int flag); static int cxparam (struct tty *tp, struct termios *t); int cxopen (dev_t dev, int flag, int mode, struct proc *p) { int unit = UNIT (dev); cx_chan_t *c = cxchan[unit]; unsigned short port; struct tty *tp; int error = 0; if (unit == UNIT_CTL) { print (("cx: cxopen /dev/cronyx\n")); return (0); } if (unit >= NCX*NCHAN || !c || c->type==T_NONE) return (ENXIO); port = c->chip->port; print (("cx%d.%d: cxopen unit=%d\n", c->board->num, c->num, unit)); if (c->mode != M_ASYNC) return (EBUSY); if (! c->ttyp) { #ifdef __FreeBSD__ #if __FreeBSD__ >= 2 c->ttyp = &cx_tty[unit]; #else c->ttyp = cx_tty[unit] = ttymalloc (cx_tty[unit]); #endif #else MALLOC (cx_tty[unit], struct tty*, sizeof (struct tty), M_DEVBUF, M_WAITOK); bzero (cx_tty[unit], sizeof (*cx_tty[unit])); c->ttyp = cx_tty[unit]; #endif c->ttyp->t_oproc = cxoproc; c->ttyp->t_stop = cxstop; c->ttyp->t_param = cxparam; } dev->si_tty = c->ttyp; #ifdef __bsdi__ if (! c->ttydev) { MALLOC (c->ttydev, struct ttydevice_tmp*, sizeof (struct ttydevice_tmp), M_DEVBUF, M_WAITOK); bzero (c->ttydev, sizeof (*c->ttydev)); strcpy (c->ttydev->tty_name, "cx"); c->ttydev->tty_unit = unit; c->ttydev->tty_base = unit; c->ttydev->tty_count = 1; c->ttydev->tty_ttys = c->ttyp; tty_attach (c->ttydev); } #endif tp = c->ttyp; tp->t_dev = dev; if ((tp->t_state & TS_ISOPEN) && (tp->t_state & TS_XCLUDE) && suser(p)) return (EBUSY); if (! (tp->t_state & TS_ISOPEN)) { ttychars (tp); if (tp->t_ispeed == 0) { #ifdef __bsdi__ tp->t_termios = deftermios; #else tp->t_iflag = 0; tp->t_oflag = 0; tp->t_lflag = 0; tp->t_cflag = CREAD | CS8 | HUPCL; tp->t_ispeed = c->rxbaud; tp->t_ospeed = c->txbaud; #endif } cxparam (tp, &tp->t_termios); ttsetwater (tp); } spltty (); if (! (tp->t_state & TS_ISOPEN)) { /* * Compute optimal receiver buffer length. * The best choice is rxbaud/400. * Make it even, to avoid byte-wide DMA transfers. * -------------------------- * Baud rate Buffer length * -------------------------- * 300 4 * 1200 4 * 9600 24 * 19200 48 * 38400 96 * 57600 192 * 115200 288 * -------------------------- */ int rbsz = (c->rxbaud + 800 - 1) / 800 * 2; if (rbsz < 4) rbsz = 4; else if (rbsz > DMABUFSZ) rbsz = DMABUFSZ; /* Initialize channel, enable receiver. */ cx_cmd (port, CCR_INITCH | CCR_ENRX); cx_cmd (port, CCR_INITCH | CCR_ENRX); /* Start receiver. */ outw (ARBCNT(port), rbsz); outw (BRBCNT(port), rbsz); outw (ARBSTS(port), BSTS_OWN24); outw (BRBSTS(port), BSTS_OWN24); /* Enable interrupts. */ outb (IER(port), IER_RXD | IER_RET | IER_TXD | IER_MDM); cx_chan_dtr (c, 1); cx_chan_rts (c, 1); } if (cx_chan_cd (c)) (*linesw[tp->t_line].l_modem)(tp, 1); if (! (flag & O_NONBLOCK)) { /* Lock the channel against cxconfig while we are * waiting for carrier. */ c->sopt.lock = 1; while (!(tp->t_cflag & CLOCAL) && !(tp->t_state & TS_CARR_ON)) if ((error = tsleep (TSA_CARR_ON(tp), TTIPRI | PCATCH, "cxdcd", 0))) break; c->sopt.lock = 0; /* Unlock the channel. */ } print (("cx%d.%d: cxopen done csr=%b\n", c->board->num, c->num, inb(CSR(c->chip->port)), CSRA_BITS)); spl0 (); if (error) return (error); #if __FreeBSD__ >= 2 error = (*linesw[tp->t_line].l_open) (dev, tp); #else error = (*linesw[tp->t_line].l_open) (dev, tp, 0); #endif return (error); } int cxclose (dev_t dev, int flag, int mode, struct proc *p) { int unit = UNIT (dev); cx_chan_t *c = cxchan[unit]; struct tty *tp; int s; if (unit == UNIT_CTL) return (0); tp = c->ttyp; (*linesw[tp->t_line].l_close) (tp, flag); /* Disable receiver. * Transmitter continues sending the queued data. */ s = spltty (); outb (CAR(c->chip->port), c->num & 3); outb (IER(c->chip->port), IER_TXD | IER_MDM); cx_cmd (c->chip->port, CCR_DISRX); /* Clear DTR and RTS. */ if ((tp->t_cflag & HUPCL) || ! (tp->t_state & TS_ISOPEN)) { cx_chan_dtr (c, 0); cx_chan_rts (c, 0); } /* Stop sending break. */ if (c->brk == BRK_SEND) { c->brk = BRK_STOP; if (! (tp->t_state & TS_BUSY)) cxoproc (tp); } splx (s); ttyclose (tp); return (0); } int cxioctl (dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) { int unit = UNIT (dev); cx_chan_t *c, *m; cx_stat_t *st; struct tty *tp; int error, s; unsigned char msv; struct ifnet *master; if (unit == UNIT_CTL) { /* Process an ioctl request on /dev/cronyx */ cx_options_t *o = (cx_options_t*) data; if (o->board >= NCX || o->channel >= NCHAN) return (EINVAL); c = &cxboard[o->board].chan[o->channel]; if (c->type == T_NONE) return (ENXIO); switch (cmd) { default: return (EINVAL); case CXIOCSETMODE: print (("cx%d.%d: CXIOCSETMODE\n", o->board, o->channel)); if (c->type == T_NONE) return (EINVAL); if (c->type == T_ASYNC && o->mode != M_ASYNC) return (EINVAL); if (o->mode == M_ASYNC) switch (c->type) { case T_SYNC_RS232: case T_SYNC_V35: case T_SYNC_RS449: return (EINVAL); } /* Somebody is waiting for carrier? */ if (c->sopt.lock) return (EBUSY); /* /dev/ttyXX is already opened by someone? */ if (c->mode == M_ASYNC && c->ttyp && (c->ttyp->t_state & TS_ISOPEN)) return (EBUSY); /* Network interface is up? */ if (c->mode != M_ASYNC && (c->ifp->if_flags & IFF_UP)) return (EBUSY); /* Find the master interface. */ master = *o->master ? ifunit (o->master) : c->ifp; if (! master) return (EINVAL); m = cxchan[master->if_unit]; /* Leave the previous master queue. */ if (c->master != c->ifp) { cx_chan_t *p = cxchan[c->master->if_unit]; for (; p; p=p->slaveq) if (p->slaveq == c) p->slaveq = c->slaveq; } /* Set up new master. */ c->master = master; c->slaveq = 0; /* Join the new master queue. */ if (c->master != c->ifp) { c->slaveq = m->slaveq; m->slaveq = c; } c->mode = o->mode; c->rxbaud = o->rxbaud; c->txbaud = o->txbaud; c->opt = o->opt; c->aopt = o->aopt; c->hopt = o->hopt; c->bopt = o->bopt; c->xopt = o->xopt; switch (c->num) { case 0: c->board->if0type = o->iftype; break; case 8: c->board->if8type = o->iftype; break; } s = spltty (); cxswitch (c, o->sopt); cx_setup_chan (c); outb (IER(c->chip->port), 0); splx (s); break; case CXIOCGETSTAT: st = (cx_stat_t*) data; st->rintr = c->stat->rintr; st->tintr = c->stat->tintr; st->mintr = c->stat->mintr; st->ibytes = c->stat->ibytes; st->ipkts = c->stat->ipkts; st->ierrs = c->stat->ierrs; st->obytes = c->stat->obytes; st->opkts = c->stat->opkts; st->oerrs = c->stat->oerrs; break; case CXIOCGETMODE: print (("cx%d.%d: CXIOCGETMODE\n", o->board, o->channel)); o->type = c->type; o->mode = c->mode; o->rxbaud = c->rxbaud; o->txbaud = c->txbaud; o->opt = c->opt; o->aopt = c->aopt; o->hopt = c->hopt; o->bopt = c->bopt; o->xopt = c->xopt; o->sopt = c->sopt; switch (c->num) { case 0: o->iftype = c->board->if0type; break; case 8: o->iftype = c->board->if8type; break; } if (c->master != c->ifp) snprintf (o->master, sizeof(o->master), "%s%d", c->master->if_name, c->master->if_unit); else *o->master = 0; break; } return (0); } c = cxchan[unit]; tp = c->ttyp; if (! tp) return (EINVAL); #if __FreeBSD__ >= 2 error = (*linesw[tp->t_line].l_ioctl) (tp, cmd, data, flag, p); #else error = (*linesw[tp->t_line].l_ioctl) (tp, cmd, data, flag); #endif if (error != ENOIOCTL) return (error); error = ttioctl (tp, cmd, data, flag); if (error != ENOIOCTL) return (error); s = spltty (); switch (cmd) { default: splx (s); return (ENOTTY); case TIOCSBRK: /* Start sending line break */ c->brk = BRK_SEND; if (! (tp->t_state & TS_BUSY)) cxoproc (tp); break; case TIOCCBRK: /* Stop sending line break */ c->brk = BRK_STOP; if (! (tp->t_state & TS_BUSY)) cxoproc (tp); break; case TIOCSDTR: /* Set DTR */ cx_chan_dtr (c, 1); break; case TIOCCDTR: /* Clear DTR */ cx_chan_dtr (c, 0); break; case TIOCMSET: /* Set DTR/RTS */ cx_chan_dtr (c, (*(int*)data & TIOCM_DTR) ? 1 : 0); cx_chan_rts (c, (*(int*)data & TIOCM_RTS) ? 1 : 0); break; case TIOCMBIS: /* Add DTR/RTS */ if (*(int*)data & TIOCM_DTR) cx_chan_dtr (c, 1); if (*(int*)data & TIOCM_RTS) cx_chan_rts (c, 1); break; case TIOCMBIC: /* Clear DTR/RTS */ if (*(int*)data & TIOCM_DTR) cx_chan_dtr (c, 0); if (*(int*)data & TIOCM_RTS) cx_chan_rts (c, 0); break; case TIOCMGET: /* Get modem status */ msv = inb (MSVR(c->chip->port)); *(int*)data = TIOCM_LE; /* always enabled while open */ if (msv & MSV_DSR) *(int*)data |= TIOCM_DSR; if (msv & MSV_CTS) *(int*)data |= TIOCM_CTS; if (msv & MSV_CD) *(int*)data |= TIOCM_CD; if (c->dtr) *(int*)data |= TIOCM_DTR; if (c->rts) *(int*)data |= TIOCM_RTS; break; } splx (s); return (0); } /* * Fill transmitter buffer with data. */ static void cxout (cx_chan_t *c, char b) { unsigned char *buf, *p, sym; unsigned short port = c->chip->port, len = 0, cnt_port, sts_port; struct tty *tp = c->ttyp; if (! tp) return; /* Choose the buffer. */ if (b == 'A') { buf = c->atbuf; cnt_port = ATBCNT(port); sts_port = ATBSTS(port); } else { buf = c->btbuf; cnt_port = BTBCNT(port); sts_port = BTBSTS(port); } /* Is it busy? */ if (inb (sts_port) & BSTS_OWN24) { tp->t_state |= TS_BUSY; return; } switch (c->brk) { case BRK_SEND: *buf++ = 0; /* extended transmit command */ *buf++ = 0x81; /* send break */ *buf++ = 0; /* extended transmit command */ *buf++ = 0x82; /* insert delay */ *buf++ = 250; /* 1/4 of second */ *buf++ = 0; /* extended transmit command */ *buf++ = 0x82; /* insert delay */ *buf++ = 250; /* + 1/4 of second */ len = 8; c->brk = BRK_IDLE; break; case BRK_STOP: *buf++ = 0; /* extended transmit command */ *buf++ = 0x83; /* stop break */ len = 2; c->brk = BRK_IDLE; break; case BRK_IDLE: p = buf; if (tp->t_iflag & IXOFF) while (RB_LEN (tp->t_out) && pt_out); /* Send XON/XOFF out of band. */ if (sym == tp->t_cc[VSTOP]) { outb (STCR(port), STC_SNDSPC|STC_SSPC_2); continue; } if (sym == tp->t_cc[VSTART]) { outb (STCR(port), STC_SNDSPC|STC_SSPC_1); continue; } /* Duplicate NULLs in ETC mode. */ if (! sym) *p++ = 0; *p++ = sym; } else while (RB_LEN (tp->t_out) && pt_out); /* Duplicate NULLs in ETC mode. */ if (! sym) *p++ = 0; *p++ = sym; } len = p - buf; break; } /* Start transmitter. */ if (len) { outw (cnt_port, len); outb (sts_port, BSTS_INTR | BSTS_OWN24); c->stat->obytes += len; tp->t_state |= TS_BUSY; print (("cx%d.%d: out %d bytes to %c\n", c->board->num, c->num, len, b)); } } void cxoproc (struct tty *tp) { int unit = UNIT (tp->t_dev); cx_chan_t *c = cxchan[unit]; unsigned short port = c->chip->port; int s = spltty (); /* Set current channel number */ outb (CAR(port), c->num & 3); if (! (tp->t_state & (TS_TIMEOUT | TS_TTSTOP))) { /* Start transmitter. */ if (! (inb (CSR(port)) & CSRA_TXEN)) cx_cmd (port, CCR_ENTX); /* Determine the buffer order. */ if (inb (DMABSTS(port)) & DMABSTS_NTBUF) { cxout (c, 'B'); cxout (c, 'A'); } else { cxout (c, 'A'); cxout (c, 'B'); } } #ifndef TS_ASLEEP /* FreeBSD some time after 2.0.5 */ ttwwakeup(tp); #else if (RB_LEN (tp->t_out) <= tp->t_lowat) { if (tp->t_state & TS_ASLEEP) { tp->t_state &= ~TS_ASLEEP; wakeup(TSA_OLOWAT(tp)); } selwakeup(&tp->t_wsel); } #endif splx (s); } static int cxparam (struct tty *tp, struct termios *t) { int unit = UNIT (tp->t_dev); cx_chan_t *c = cxchan[unit]; unsigned short port = c->chip->port; int clock, period, s; cx_cor1_async_t cor1; if (t->c_ospeed == 0) { /* Clear DTR and RTS. */ s = spltty (); cx_chan_dtr (c, 0); cx_chan_rts (c, 0); splx (s); print (("cx%d.%d: cxparam (hangup)\n", c->board->num, c->num)); return (0); } print (("cx%d.%d: cxparam\n", c->board->num, c->num)); /* Check requested parameters. */ if (t->c_ospeed < 300 || t->c_ospeed > 256*1024) return(EINVAL); if (t->c_ispeed && (t->c_ispeed < 300 || t->c_ispeed > 256*1024)) return(EINVAL); #ifdef __bsdi__ /* CLOCAL flag set -- wakeup everybody who waits for CD. */ /* FreeBSD does this themselves. */ if (! (tp->t_cflag & CLOCAL) && (t->c_cflag & CLOCAL)) wakeup ((caddr_t) &tp->t_rawq); #endif /* And copy them to tty and channel structures. */ c->rxbaud = tp->t_ispeed = t->c_ispeed; c->txbaud = tp->t_ospeed = t->c_ospeed; tp->t_cflag = t->c_cflag; /* Set character length and parity mode. */ BYTE cor1 = 0; switch (t->c_cflag & CSIZE) { default: case CS8: cor1.charlen = 7; break; case CS7: cor1.charlen = 6; break; case CS6: cor1.charlen = 5; break; case CS5: cor1.charlen = 4; break; } if (t->c_cflag & PARENB) { cor1.parmode = PARM_NORMAL; cor1.ignpar = 0; cor1.parity = (t->c_cflag & PARODD) ? PAR_ODD : PAR_EVEN; } else { cor1.parmode = PARM_NOPAR; cor1.ignpar = 1; } /* Enable/disable hardware CTS. */ c->aopt.cor2.ctsae = (t->c_cflag & CRTSCTS) ? 1 : 0; /* Handle DSR as CTS. */ c->aopt.cor2.dsrae = (t->c_cflag & CRTSCTS) ? 1 : 0; /* Enable extended transmit command mode. * Unfortunately, there is no other method for sending break. */ c->aopt.cor2.etc = 1; /* Enable/disable hardware XON/XOFF. */ c->aopt.cor2.ixon = (t->c_iflag & IXON) ? 1 : 0; c->aopt.cor2.ixany = (t->c_iflag & IXANY) ? 1 : 0; /* Set the number of stop bits. */ if (t->c_cflag & CSTOPB) c->aopt.cor3.stopb = STOPB_2; else c->aopt.cor3.stopb = STOPB_1; /* Disable/enable passing XON/XOFF chars to the host. */ c->aopt.cor3.scde = (t->c_iflag & IXON) ? 1 : 0; c->aopt.cor3.flowct = (t->c_iflag & IXON) ? FLOWCC_NOTPASS : FLOWCC_PASS; c->aopt.schr1 = t->c_cc[VSTART]; /* XON */ c->aopt.schr2 = t->c_cc[VSTOP]; /* XOFF */ /* Set current channel number. */ s = spltty (); outb (CAR(port), c->num & 3); /* Set up receiver clock values. */ cx_clock (c->chip->oscfreq, c->rxbaud, &clock, &period); c->opt.rcor.clk = clock; outb (RCOR(port), BYTE c->opt.rcor); outb (RBPR(port), period); /* Set up transmitter clock values. */ cx_clock (c->chip->oscfreq, c->txbaud, &clock, &period); c->opt.tcor.clk = clock; c->opt.tcor.ext1x = 0; outb (TCOR(port), BYTE c->opt.tcor); outb (TBPR(port), period); outb (COR2(port), BYTE c->aopt.cor2); outb (COR3(port), BYTE c->aopt.cor3); outb (SCHR1(port), c->aopt.schr1); outb (SCHR2(port), c->aopt.schr2); if (BYTE c->aopt.cor1 != BYTE cor1) { BYTE c->aopt.cor1 = BYTE cor1; outb (COR1(port), BYTE c->aopt.cor1); /* Any change to COR1 require reinitialization. */ /* Unfortunately, it may cause transmitter glitches... */ cx_cmd (port, CCR_INITCH); } splx (s); return (0); } /* * Stop output on a line */ void cxstop (struct tty *tp, int flag) { cx_chan_t *c = cxchan[UNIT(tp->t_dev)]; unsigned short port = c->chip->port; int s = spltty (); if (tp->t_state & TS_BUSY) { print (("cx%d.%d: cxstop\n", c->board->num, c->num)); /* Set current channel number */ outb (CAR(port), c->num & 3); /* Stop transmitter */ cx_cmd (port, CCR_DISTX); } splx (s); } /* * Handle receive interrupts, including receive errors and * receive timeout interrupt. */ int cxrinta (cx_chan_t *c) { unsigned short port = c->chip->port; unsigned short len = 0, risr = inw (RISR(port)), reoir = 0; struct tty *tp = c->ttyp; /* Compute optimal receiver buffer length. */ int rbsz = (c->rxbaud + 800 - 1) / 800 * 2; if (rbsz < 4) rbsz = 4; else if (rbsz > DMABUFSZ) rbsz = DMABUFSZ; if (risr & RISA_TIMEOUT) { unsigned long rcbadr = (unsigned short) inw (RCBADRL(port)) | (long) inw (RCBADRU(port)) << 16; unsigned char *buf = 0; unsigned short cnt_port = 0, sts_port = 0; if (rcbadr >= c->brphys && rcbadr < c->brphys+DMABUFSZ) { buf = c->brbuf; len = rcbadr - c->brphys; cnt_port = BRBCNT(port); sts_port = BRBSTS(port); } else if (rcbadr >= c->arphys && rcbadr < c->arphys+DMABUFSZ) { buf = c->arbuf; len = rcbadr - c->arphys; cnt_port = ARBCNT(port); sts_port = ARBSTS(port); } else printf ("cx%d.%d: timeout: invalid buffer address\n", c->board->num, c->num); if (len) { print (("cx%d.%d: async receive timeout (%d bytes), risr=%b, arbsts=%b, brbsts=%b\n", c->board->num, c->num, len, risr, RISA_BITS, inb (ARBSTS(port)), BSTS_BITS, inb (BRBSTS(port)), BSTS_BITS)); c->stat->ibytes += len; if (tp && (tp->t_state & TS_ISOPEN)) { int i; int (*rint)(int, struct tty *) = linesw[tp->t_line].l_rint; for (i=0; iboard->num, c->num, risr, RISA_BITS, inb (ARBSTS(port)), BSTS_BITS, inb (BRBSTS(port)), BSTS_BITS)); if (risr & RIS_BUSERR) { printf ("cx%d.%d: receive bus error\n", c->board->num, c->num); ++c->stat->ierrs; } if (risr & (RIS_OVERRUN | RISA_PARERR | RISA_FRERR | RISA_BREAK)) { int err = 0; if (risr & RISA_PARERR) err |= TTY_PE; if (risr & RISA_FRERR) err |= TTY_FE; #ifdef TTY_OE if (risr & RIS_OVERRUN) err |= TTY_OE; #endif #ifdef TTY_BI if (risr & RISA_BREAK) err |= TTY_BI; #endif print (("cx%d.%d: receive error %x\n", c->board->num, c->num, err)); if (tp && (tp->t_state & TS_ISOPEN)) (*linesw[tp->t_line].l_rint) (err, tp); ++c->stat->ierrs; } /* Discard exception characters. */ if ((risr & RISA_SCMASK) && tp && (tp->t_iflag & IXON)) reoir |= REOI_DISCEXC; /* Handle received data. */ if ((risr & RIS_EOBUF) && tp && (tp->t_state & TS_ISOPEN)) { int (*rint)(int, struct tty *) = linesw[tp->t_line].l_rint; unsigned char *buf; int i; len = (risr & RIS_BB) ? inw(BRBCNT(port)) : inw(ARBCNT(port)); print (("cx%d.%d: async: %d bytes received\n", c->board->num, c->num, len)); c->stat->ibytes += len; buf = (risr & RIS_BB) ? c->brbuf : c->arbuf; for (i=0; ittyp; unsigned short port = c->chip->port; unsigned char tisr = inb (TISR(port)); print (("cx%d.%d: async transmit interrupt, tisr=%b, atbsts=%b, btbsts=%b\n", c->board->num, c->num, tisr, TIS_BITS, inb (ATBSTS(port)), BSTS_BITS, inb (BTBSTS(port)), BSTS_BITS)); if (tisr & TIS_BUSERR) { printf ("cx%d.%d: transmit bus error\n", c->board->num, c->num); ++c->stat->oerrs; } else if (tisr & TIS_UNDERRUN) { printf ("cx%d.%d: transmit underrun error\n", c->board->num, c->num); ++c->stat->oerrs; } if (tp) { tp->t_state &= ~(TS_BUSY | TS_FLUSH); if (tp->t_line) (*linesw[tp->t_line].l_start) (tp); else cxoproc (tp); } } /* * Handle modem interrupt. */ void cxmint (cx_chan_t *c) { unsigned short port = c->chip->port; unsigned char misr = inb (MISR(port)); unsigned char msvr = inb (MSVR(port)); struct tty *tp = c->ttyp; if (c->mode != M_ASYNC) { printf ("cx%d.%d: unexpected modem interrupt, misr=%b, msvr=%b\n", c->board->num, c->num, misr, MIS_BITS, msvr, MSV_BITS); return; } print (("cx%d.%d: modem interrupt, misr=%b, msvr=%b\n", c->board->num, c->num, misr, MIS_BITS, msvr, MSV_BITS)); /* Ignore DSR events. */ /* Ignore RTC/CTS events, handled by hardware. */ /* Handle carrier detect/loss. */ if (tp && (misr & MIS_CCD)) (*linesw[tp->t_line].l_modem) (tp, (msvr & MSV_CD) != 0); } /* * Recover after lost transmit interrupts. */ void cxtimeout (void *a) { cx_board_t *b; cx_chan_t *c; struct tty *tp; int s; for (b=cxboard; bchan; cchan+NCHAN; ++c) { tp = c->ttyp; if (c->type==T_NONE || c->mode!=M_ASYNC || !tp) continue; s = spltty (); if (tp->t_state & TS_BUSY) { tp->t_state &= ~TS_BUSY; if (tp->t_line) (*linesw[tp->t_line].l_start) (tp); else cxoproc (tp); } splx (s); } timeout (cxtimeout, 0, hz*5); } #if defined(__FreeBSD__) && (__FreeBSD__ > 1 ) static void cx_drvinit(void *unused) { cdevsw_add(&cx_cdevsw); } SYSINIT(cxdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,cx_drvinit,NULL) #endif -#endif /* NCX */ diff --git a/sys/i386/isa/gpib.c b/sys/i386/isa/gpib.c index f7b8253e7a23..f78ddd69eb2a 100644 --- a/sys/i386/isa/gpib.c +++ b/sys/i386/isa/gpib.c @@ -1,1164 +1,1158 @@ /* * GPIB driver for FreeBSD. * Version 0.1 (No interrupts, no DMA) * Supports National Instruments AT-GPIB and AT-GPIB/TNT boards. * (AT-GPIB not tested, but it should work) * * Written by Fred Cawthorne (fcawth@delphi.umd.edu) * Some sections were based partly on the lpt driver. * (some remnants may remain) * * This software is distributed with NO WARRANTIES, not even the implied * warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * The author grants any other persons or organizations permission to use * or modify this software as long as this message is kept with the software, * all derivative works or modified versions. * * $FreeBSD$ * */ /*Please read the README file for usage information*/ -#include "gp.h" - -#if NGP > 0 - #include #include #include #include #include #include #include #include #define MIN(a,b) ((a < b) ? a : b) #define GPIBPRI (PZERO+8)|PCATCH #define SLEEP_MAX 1000 #define SLEEP_MIN 4 static int initgpib(void); static void closegpib(void); static int sendgpibfifo(unsigned char device,char *data,int count); static int sendrawgpibfifo(unsigned char device,char *data,int count); static int readgpibfifo(unsigned char device,char *data,int count); #if 0 static void showregs(void); #endif static void enableremote(unsigned char device); static void gotolocal(unsigned char device); static void menableremote(unsigned char *device); static void mgotolocal(unsigned char *device); static void mtrigger(unsigned char *device); static void trigger(unsigned char device); static char spoll(unsigned char device); static int gpprobe(struct isa_device *dvp); static int gpattach(struct isa_device *dvp); struct isa_driver gpdriver = {gpprobe, gpattach, "gp"}; static d_open_t gpopen; static d_close_t gpclose; static d_write_t gpwrite; static d_ioctl_t gpioctl; #define CDEV_MAJOR 44 static struct cdevsw gp_cdevsw = { /* open */ gpopen, /* close */ gpclose, /* read */ noread, /* write */ gpwrite, /* ioctl */ gpioctl, /* poll */ nopoll, /* mmap */ nommap, /* strategy */ nostrategy, /* name */ "gp", /* maj */ CDEV_MAJOR, /* dump */ nodump, /* psize */ nopsize, /* flags */ 0, /* bmaj */ -1 }; #define BUFSIZE 1024 #define ATTACHED 0x08 #define OPEN 0x04 #define INIT 0x02 static struct gpib_softc { char *sc_cp; /* current data to send */ int sc_count; /* bytes queued in sc_inbuf */ int sc_type; /* Type of gpib controller */ u_char sc_flags; /* flags (open and internal) */ char sc_unit; /* gpib device number */ char *sc_inbuf; /* buffer for data */ } gpib_sc; /* only support one of these? */ static int oldcount; static char oldbytes[2]; /*Probe routine*/ /*This needs to be changed to be a bit more robust*/ static int gpprobe(struct isa_device *dvp) { int status; struct gpib_softc *sc = &gpib_sc; static int once; if (!once++) cdevsw_add(&gp_cdevsw); gpib_port = dvp->id_iobase; status=1; sc->sc_type=3; if ((inb(KSR)&0xF7)==0x34) sc->sc_type=3; else if ((inb(KSR)&0xF7)==0x24) sc->sc_type=2; else if ((inb(KSR)&0xF7)==0x14) sc->sc_type=1; else status=0; return (status); } /* * gpattach() * Attach device and print the type of card to the screen. */ static int gpattach(isdp) struct isa_device *isdp; { struct gpib_softc *sc = &gpib_sc; sc->sc_unit = isdp->id_unit; if (sc->sc_type==3) printf ("gp%d: type AT-GPIB/TNT\n",sc->sc_unit); if (sc->sc_type==2) printf ("gp%d: type AT-GPIB chip NAT4882B\n",sc->sc_unit); if (sc->sc_type==1) printf ("gp%d: type AT-GPIB chip NAT4882A\n",sc->sc_unit); sc->sc_flags |=ATTACHED; make_dev(&gp_cdevsw, 0, 0, 0, 0600, "gp"); return (1); } /* * gpopen() * New open on device. * * More than 1 open is not allowed on the entire device. * i.e. even if gpib5 is open, we can't open another minor device */ static int gpopen(dev, flags, fmt, p) dev_t dev; int flags; int fmt; struct proc *p; { struct gpib_softc *sc = &gpib_sc; u_char unit; int status; unit= minor(dev); /* minor number out of limits ? */ if (unit >= 32) return (ENXIO); /* Attached ? */ if (!(sc->sc_flags&ATTACHED)) { /* not attached */ return(ENXIO); } /* Already open */ if (sc->sc_flags&OPEN) { /* too late .. */ return(EBUSY); } /* Have memory for buffer? */ sc->sc_inbuf = malloc(BUFSIZE, M_DEVBUF, M_WAITOK); if (sc->sc_inbuf == 0) return(ENOMEM); if (initgpib()) return(EBUSY); sc->sc_flags |= OPEN; sc->sc_count = 0; oldcount=0; if (unit!=0) { /*Someone is trying to access an actual device*/ /*So.. we'll address it to listen*/ enableremote(unit); do { status=inb(ISR2); } while (!(status&8)&&tsleep((caddr_t)&gpib_sc, GPIBPRI,"gpibpoll",1)==EWOULDBLOCK); outb(CDOR,(unit&31)+32);/*address device to listen*/ do status=inb(ISR2); while (!(status&8)&&tsleep((caddr_t)&gpib_sc, GPIBPRI,"gpibpoll",1)==EWOULDBLOCK); outb (CDOR,64); /*Address controller (me) to talk*/ do status=inb(ISR2); while (!(status&8)&&tsleep((caddr_t)&gpib_sc, GPIBPRI,"gpibpoll",1)==EWOULDBLOCK); outb(AUXMR,gts); /*Set to Standby (Controller)*/ do status=inb(ISR1); while (!(status&2)&&tsleep((caddr_t)&gpib_sc, GPIBPRI,"gpibpoll",1)==EWOULDBLOCK); /*Set up the TURBO488 registers*/ outb(IMR2,0x30); /*we have to enable DMA (0x30) for turbo488 to work*/ outb(CNT0,0); /*NOTE this does not enable DMA to the host computer!!*/ outb(CNT1,0); outb(CNT2,0); outb(CNT3,0); outb(CMDR,0x20); outb(CFG,0x47); /* 16 bit, write, fifo B first, TMOE TIM */ outb(CMDR,0x10); /*RESET fifos*/ outb(CMDR,0x04); /*Tell TURBO488 to GO*/ } return(0); } /* * gpclose() * Close gpib device. */ static int gpclose(dev, flags, fmt, p) dev_t dev; int flags; int fmt; struct proc *p; { struct gpib_softc *sc = &gpib_sc; unsigned char unit; unsigned char status; unit=minor(dev); if (unit!=0) { /*Here we need to send the last character with EOS*/ /*and unaddress the listening device*/ status=EWOULDBLOCK; /*Wait for fifo to become empty*/ do { status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",1); } while ((inb(ISR3)&0x04)&&status==EWOULDBLOCK); /*Fifo is not empty*/ outb(CMDR,0x08); /*Issue STOP to TURBO488*/ /*Wait for DONE and STOP*/ if (status==EWOULDBLOCK) do { status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",1); } while (!(inb(ISR3)&0x11)&&status==EWOULDBLOCK); /*not done and stop*/ /*Shut down TURBO488 */ outb(IMR2,0x00); /*DISABLE DMA to turbo488*/ outb(CMDR,0x20); /*soft reset turbo488*/ outb(CMDR,0x10); /*reset fifos*/ /*Send last byte with EOI set*/ /*Send second to last byte if there are 2 bytes left*/ if (status==EWOULDBLOCK) { do if (!(inb(ISR1)&2)) status=tsleep((caddr_t)&gpib_sc, GPIBPRI,"gpibpoll",1); while (!(inb(ISR1)&2)&&(status==EWOULDBLOCK)); if (oldcount==2){ outb(CDOR,oldbytes[0]); /*Send second to last byte*/ while (!(inb(ISR1)&2)&&(status==EWOULDBLOCK)); status=tsleep((caddr_t)&gpib_sc, GPIBPRI,"gpibpoll",1); } outb(AUXMR,seoi); /*Set EOI for the last byte*/ outb(AUXMR,0x5E); /*Clear SYNC*/ if (oldcount==1) outb(CDOR,oldbytes[0]); else if (oldcount==2) outb(CDOR,oldbytes[1]); else { outb (CDOR,13); /*Send a CR.. we've got trouble*/ printf("gpib: Warning: gpclose called with nothing left in buffer\n"); } } do if (!(inb(ISR1)&2)) status=tsleep((caddr_t)&gpib_sc, GPIBPRI,"gpibpoll",1); while (!(inb(ISR1)&2)&&(status==EWOULDBLOCK)); if (!(inb(ISR1)&2)&&status==EWOULDBLOCK) do status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",1); while (!(inb(ISR1)&2)&&status==EWOULDBLOCK); outb(AUXMR,tca); /* Regain full control of the bus*/ do status=inb(ISR2); while (!(status&8)&&tsleep((caddr_t)&gpib_sc, GPIBPRI,"gpibpoll",1)==EWOULDBLOCK); outb(CDOR,63); /*unlisten*/ do status=inb(ISR2); while (!(status&8)&&tsleep((caddr_t)&gpib_sc, GPIBPRI,"gpibpoll",1)==EWOULDBLOCK); outb(AUXMR,0x5E); /*Clear SYNC*/ outb (CDOR,95);/*untalk*/ do status=inb(ISR2); while (!(status&8)&&tsleep((caddr_t)&gpib_sc, GPIBPRI,"gpibpoll",1)==EWOULDBLOCK); /*gotolocal(minor(dev));*/ } closegpib(); sc->sc_flags = ATTACHED; free(sc->sc_inbuf, M_DEVBUF); sc->sc_inbuf = 0; /* Sanity */ return(0); } /* * gpwrite() * Copy from user's buffer, then write to GPIB device referenced * by minor(dev). */ static int gpwrite(dev, uio, ioflag) dev_t dev; struct uio *uio; int ioflag; { int err,count; /* main loop */ while ((gpib_sc.sc_count = MIN(BUFSIZE-1, uio->uio_resid)) > 0) { /* If there were >1 bytes left over, send them */ if (oldcount==2) sendrawgpibfifo(minor(dev),oldbytes,2); /*If there was 1 character left, put it at the beginning of the new buffer*/ if (oldcount==1){ (gpib_sc.sc_inbuf)[0]=oldbytes[0]; gpib_sc.sc_cp = gpib_sc.sc_inbuf; /* get from user-space */ uiomove(gpib_sc.sc_inbuf+1, gpib_sc.sc_count, uio); gpib_sc.sc_count++; } else { gpib_sc.sc_cp = gpib_sc.sc_inbuf; /* get from user-space */ uiomove(gpib_sc.sc_inbuf, gpib_sc.sc_count, uio); } /*NOTE we always leave one byte in case this is the last write so close can send EOI with the last byte There may be 2 bytes since we are doing 16 bit transfers.(note the -1 in the count below)*/ /*If count<=2 we'll either pick it up on the next write or on close*/ if (gpib_sc.sc_count>2) { count = sendrawgpibfifo(minor(dev),gpib_sc.sc_cp,gpib_sc.sc_count-1); err=!count; if (err) return(1); oldcount=gpib_sc.sc_count-count; /*Set # of remaining bytes*/ gpib_sc.sc_count-=count; gpib_sc.sc_cp+=count; /*point char pointer to remaining bytes*/ } else oldcount=gpib_sc.sc_count; oldbytes[0]=gpib_sc.sc_cp[0]; if (oldcount==2) oldbytes[1]=gpib_sc.sc_cp[1]; } return(0); } /* Here is how you would usually access a GPIB device An exception would be a plotter or printer that you can just write to using a minor device = its GPIB address */ static int gpioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p) { struct gpibdata *gd = (struct gpibdata *)data; int error,result; error = 0; switch (cmd) { case GPIBWRITE: sendgpibfifo(gd->address,gd->data,*(gd->count)); error=0; break; case GPIBREAD: result=readgpibfifo(gd->address,gd->data,*(gd->count)); *(gd->count)=result; error=0; break; case GPIBINIT: initgpib(); error=0; break; case GPIBTRIGGER: trigger(gd->address); error=0; break; case GPIBREMOTE: enableremote(gd->address); error=0; break; case GPIBLOCAL: gotolocal(gd->address); error=0; break; case GPIBMTRIGGER: mtrigger(gd->data); error=0; break; case GPIBMREMOTE: menableremote(gd->data); error=0; break; case GPIBMLOCAL: mgotolocal(gd->data); error=0; break; case GPIBSPOLL: *(gd->data)=spoll(gd->address); error=0; break; default: error = ENODEV; } return(error); } #if 0 /*Just in case you want a dump of the registers...*/ static void showregs() { printf ("NAT4882:\n"); printf ("ISR1=%X\t",inb(ISR1)); printf ("ISR2=%X\t",inb(ISR2)); printf ("SPSR=%X\t",inb(SPSR)); printf ("KSR =%X\t",inb(KSR)); printf ("ADSR=%X\t",inb(ADSR)); printf ("CPTR=%X\t",inb(CPTR)); printf ("SASR=%X\t",inb(SASR)); printf ("ADR0=%X\t",inb(ADR0)); printf ("ISR0=%X\t",inb(ISR0)); printf ("ADR1=%X\t",inb(ADR1)); printf ("BSR =%X\n",inb(BSR)); printf ("Turbo488\n"); printf ("STS1=%X ",inb(STS1)); printf ("STS2=%X ",inb(STS2)); printf ("ISR3=%X ",inb(ISR3)); printf ("CNT0=%X ",inb(CNT0)); printf ("CNT1=%X ",inb(CNT1)); printf ("CNT2=%X ",inb(CNT2)); printf ("CNT3=%X ",inb(CNT3)); printf ("IMR3=%X ",inb(IMR3)); printf ("TIMER=%X\n",inb(TIMER)); } #endif /*Set up the NAT4882 and TURBO488 registers */ /*This will be nonsense to you unless you have a data sheet from National Instruments. They should give you one if you call them*/ static int initgpib() { outb(CMDR,0x20); outb(CFG,0x16); outb(IMR3,0); outb(CMDR,0x10); outb(CNT0,0); outb(CNT1,0); outb(CNT2,0); outb(CNT3,0); outb(INTR,0); /* Put interrupt line in tri-state mode??*/ outb(AUXMR,chip_reset); outb(IMR1,0x10); /* send interrupt to TURBO488 when END received*/ outb(IMR2,0); outb(IMR0,0x90); /* Do we want nba here too??? */ outb(ADMR,1); outb(ADR,0); outb(ADR,128); outb(AUXMR,0xE9); outb(AUXMR,0x49); outb(AUXMR,0x70); outb(AUXMR,0xD0); outb(AUXMR,0xA0); outb(EOSR,10); /*set EOS message to newline*/ /*should I make the default to interpret END as EOS?*/ /*It isn't now. The following changes this*/ outb(AUXMR,0x80); /*No special EOS handling*/ /*outb(AUXMR,0x88) */ /* Transmit END with EOS*/ /*outb(AUXMR,0x84) */ /* Set END on EOS received*/ /*outb(AUXMR,0x8C) */ /* Do both of the above*/ /* outb(AUXMR,hldi); */ /*Perform RFD Holdoff for all data in*/ /*Not currently supported*/ outb(AUXMR,pon); outb(AUXMR,sic_rsc); tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",1); outb(AUXMR,sic_rsc_off); return(0); } /*This is kind of Brute force.. But it works*/ static void closegpib() { outb(AUXMR,chip_reset); } /*GPIB ROUTINES: These will also make little sense unless you have a data sheet. Note that the routines with an "m" in the beginning are for accessing multiple devices in one call*/ /*This is one thing I could not figure out how to do correctly. I tried to use the auxilary command to enable remote, but it never worked. Here, I bypass everything and write to the BSR to enable the remote line. NOTE that these lines are effectively "OR'ed" with the actual lines, so writing a 1 to the bit in the BSR forces the GPIB line true, no matter what the fancy circuitry of the NAT4882 wants to do with it*/ static void enableremote(unsigned char device) { int status; status=EWOULDBLOCK; if (status==EWOULDBLOCK) do { status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",2); } while (!(inb(ISR2)&0x08)&&status==EWOULDBLOCK); /*Wait to send next cmd*/ outb(BSR,1); /*Set REN bit on GPIB*/ if (status==EWOULDBLOCK) do { status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",2); } while (!(inb(ISR2)&0x08)&&status==EWOULDBLOCK); /*Wait to send next cmd*/ outb(CDOR,(device&31)+32); /*address device to listen*/ if (status==EWOULDBLOCK) do { status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",2); } while (!(inb(ISR2)&0x08)&&status==EWOULDBLOCK); /*Wait to send next cmd*/ outb (CDOR,63); /*Unaddress device*/ if (status==EWOULDBLOCK) do { status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",2); } while (!(inb(ISR2)&0x08)&&status==EWOULDBLOCK); /*Wait to send next cmd*/ } /*This does not release the REM line on the gpib port, because if it did, all the remote devices would go to local mode. This only sends the gotolocal message to one device. Currently, REM is always held true after enableremote is called, and is reset only on a close of the gpib device */ static void gotolocal(unsigned char device) { int status; status=EWOULDBLOCK; if (status==EWOULDBLOCK) do { status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",2); } while (!(inb(ISR2)&0x08)&&status==EWOULDBLOCK); /*Wait to send next cmd*/ outb(CDOR,(device&31)+32); if (status==EWOULDBLOCK) do { status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",2); } while (!(inb(ISR2)&0x08)&&status==EWOULDBLOCK); /*Wait to send next cmd*/ outb(AUXMR,0x5E); /*Clear SYNC*/ outb (CDOR,1); if (status==EWOULDBLOCK) do { status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",2); } while (!(inb(ISR2)&0x08)&&status==EWOULDBLOCK); /*Wait to send next cmd*/ outb(AUXMR,0x5E); outb (CDOR,63);/*unaddress device*/ if (status==EWOULDBLOCK) do { status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",2); } while (!(inb(ISR2)&0x08)&&status==EWOULDBLOCK); /*Wait to send next cmd*/ } static void menableremote(unsigned char *device) { int status, counter = 0; status=EWOULDBLOCK; if (status==EWOULDBLOCK) do { status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",2); } while (!(inb(ISR2)&0x08)&&status==EWOULDBLOCK); /*Wait to send next cmd*/ outb(BSR,1); /*Set REN bit on GPIB*/ do { if (status==EWOULDBLOCK) do { status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",2); } while (!(inb(ISR2)&0x08)&&status==EWOULDBLOCK); /*Wait to send next cmd*/ outb(CDOR,(device[counter]&31)+32); /*address device to listen*/ counter++; } while (device[counter]<32); if (status==EWOULDBLOCK) do { status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",2); } while (!(inb(ISR2)&0x08)&&status==EWOULDBLOCK); /*Wait to send next cmd*/ outb (CDOR,63); /*Unaddress device*/ if (status==EWOULDBLOCK) do { status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",2); } while (!(inb(ISR2)&0x08)&&status==EWOULDBLOCK); /*Wait to send next cmd*/ } static void mgotolocal(unsigned char *device) { int status; int counter=0; status=EWOULDBLOCK; if (device[counter]<32) do { if (status==EWOULDBLOCK) do { status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",2); } while (!(inb(ISR2)&0x08)&&status==EWOULDBLOCK); /*Wait to send next cmd*/ outb(CDOR,(device[counter]&31)+32); counter++; } while (device[counter]<32); if (status==EWOULDBLOCK) do { status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",2); } while (!(inb(ISR2)&0x08)&&status==EWOULDBLOCK); /*Wait to send next cmd*/ outb(AUXMR,0x5E); /*Clear SYNC*/ outb (CDOR,1); if (status==EWOULDBLOCK) do { status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",2); } while (!(inb(ISR2)&0x08)&&status==EWOULDBLOCK); /*Wait to send next cmd*/ outb(AUXMR,0x5E); outb (CDOR,63);/*unaddress device*/ if (status==EWOULDBLOCK) do { status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",2); } while (!(inb(ISR2)&0x08)&&status==EWOULDBLOCK); /*Wait to send next cmd*/ } /*Trigger a device. What happens depends on how the device is configured. */ static void trigger(unsigned char device) { int status; status=EWOULDBLOCK; if (device<32) { if (!(inb(ISR2)&0x08)) do { status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",1); } while (!(inb(ISR2)&0x08)&&status==EWOULDBLOCK); /*Wait to send next cmd*/ outb(CDOR,(device&31)+32); /*address device to listen*/ if (!(inb(ISR2)&0x08)&&status==EWOULDBLOCK) do { status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",1); } while (!(inb(ISR2)&0x08)&&status==EWOULDBLOCK); /*Wait to send next cmd*/ outb (CDOR,8); /*send GET*/ if (!(inb(ISR2)&0x08)&&status==EWOULDBLOCK) do { status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",1); } while (!(inb(ISR2)&0x08)&&status==EWOULDBLOCK); /*Wait to send next cmd*/ outb (AUXMR,0x5E); outb (CDOR,63);/*unaddress device*/ if (!(inb(ISR2)&0x08)&&status==EWOULDBLOCK) do { status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",1); } while (!(inb(ISR2)&0x08)&&status==EWOULDBLOCK); /*Wait to send next cmd*/ } } /*Trigger multiple devices by addressing them all to listen, and then sending GET*/ static void mtrigger(unsigned char *device) { int status=EWOULDBLOCK; int counter=0; if(device[0]<32){ do { if (device[counter]<32) if (!(inb(ISR2)&0x08)&&status==EWOULDBLOCK) do { status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",1); } while (!(inb(ISR2)&0x08)&&status==EWOULDBLOCK); /*Wait to send next cmd*/ outb(CDOR,(device[counter]&31)+32); /*address device to listen*/ counter++; } while (device[counter]<32); if (!(inb(ISR2)&0x08)&&status==EWOULDBLOCK) do { status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",1); } while (!(inb(ISR2)&0x08)&&status==EWOULDBLOCK); /*Wait to send next cmd*/ outb (CDOR,8); /*send GET*/ if (!(inb(ISR2)&0x08)&&status==EWOULDBLOCK) do { status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",1); } while (!(inb(ISR2)&0x08)&&status==EWOULDBLOCK); /*Wait to send next cmd*/ outb (AUXMR,0x5E); outb (CDOR,63);/*unaddress device*/ if (!(inb(ISR2)&0x08)&&status==EWOULDBLOCK) do { status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",1); } while (!(inb(ISR2)&0x08)&&status==EWOULDBLOCK); /*Wait to send next cmd*/ } } /*This is not used now, but it should work with NI's 8 bit gpib board since it does not use the TURBO488 registers at all */ /*Send data through the TURBO488 FIFOS to a device that is already addressed to listen. This is used by the write call when someone is writing to a printer or plotter, etc... */ /*The last byte of each write is held off until either the next write or close, so it can be sent with EOI set*/ static int sendrawgpibfifo(unsigned char device,char *data,int count) { int status; int counter; int fifopos; int sleeptime; sleeptime=SLEEP_MIN; counter=0; fifopos=0; status=EWOULDBLOCK; do { /*Wait for fifo to become not full if it is full */ sleeptime=SLEEP_MIN; if (!(inb(ISR3)&0x08)) do { status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",sleeptime); if (sleeptime1)&&(inb(ISR3)&0x08)){ outw(FIFOB,*(unsigned*)(data+counter)); /* printf ("gpib: sent:%c,%c\n",data[counter],data[counter+1]);*/ counter+=2; count-=2; } } while ((count>1)&&(status==EWOULDBLOCK)); /*The write routine and close routine must check if there is 1 byte left and handle it accordingly*/ /*Return the number of bytes written to the device*/ return(counter); } static int sendgpibfifo(unsigned char device,char *data,int count) { int status; int counter; int fifopos; int sleeptime; outb(IMR2,0x30); /*we have to enable DMA (0x30) for turbo488 to work*/ outb(CNT0,0); outb(CNT1,0); outb(CNT2,0); outb(CNT3,0); status=EWOULDBLOCK; if (!(inb(ISR2)&8)) do status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",1); while (!(inb(ISR2)&8)&&status==EWOULDBLOCK); outb(CDOR,(device&31)+32);/*address device to listen*/ if (!(inb(ISR2)&8)&&status==EWOULDBLOCK) do status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",1); while (!(inb(ISR2)&8)&&status==EWOULDBLOCK); outb (CDOR,64); /*Address controller (me) to talk*/ if (!(inb(ISR2)&8)&&status==EWOULDBLOCK) do status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",1); while (!(inb(ISR2)&8)&&status==EWOULDBLOCK); outb(AUXMR,gts); /*Set to Standby (Controller)*/ fifopos=0; sleeptime=SLEEP_MIN; counter=0; fifopos=0; outb(CMDR,0x20); outb(CFG,0x47); /* 16 bit, write, fifo B first, TMOE TIM */ outb(CMDR,0x10); /*RESET fifos*/ outb(CCRG,seoi); /*program to send EOI at end*/ outb(CMDR,0x04); /*Tell TURBO488 to GO*/ status=EWOULDBLOCK; do { /*Wait for fifo to become not full if it is full */ sleeptime=SLEEP_MIN; if (!(inb(ISR3)&0x08)) do { status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",sleeptime); if (sleeptime1)&&(inb(ISR3)&0x08)){ /*if(count==2) outb(CFG,15+0x40); *//*send eoi when done*/ outw(FIFOB,*(unsigned*)(data+counter)); counter+=2; count-=2; } } while ((count>2)&&(status==EWOULDBLOCK)); if (count==2&&status==EWOULDBLOCK) { /*Wait for fifo to become not full*/ if(status==EWOULDBLOCK&&!(inb(ISR3)&0x08)) do { status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",SLEEP_MIN); } while (!(inb(ISR3)&0x08)&&status==EWOULDBLOCK); /*Fifo is full*/ /*outb(CFG,0x40+15);*//*send eoi when done*/ outb(FIFOB,data[counter]); counter++; count--; } /*outb(CMDR,0x04);*/ /*Wait for fifo to become empty*/ if (status==EWOULDBLOCK) do { status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",1); } while ((inb(ISR3)&0x04)&&status==EWOULDBLOCK); /*Fifo is not empty*/ outb(CMDR,0x08); /*Issue STOP to TURBO488*/ /*Wait for DONE and STOP*/ if (status==EWOULDBLOCK) do { status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",1); } while (!(inb(ISR3)&0x11)&&status==EWOULDBLOCK); /*not done and stop*/ outb(IMR2,0x00); /*we have to enable DMA (0x30) for turbo488 to work*/ outb(CMDR,0x20); /*soft reset turbo488*/ outb(CMDR,0x10); /*reset fifos*/ /*Send last byte with EOI set*/ /*Here EOI is handled correctly since the string to be sent */ /*is actually all sent during the ioctl. (See above)*/ if (count==1&&status==EWOULDBLOCK) { /*Count should always=1 here*/ do if (!(inb(ISR1)&2)) status=tsleep((caddr_t)&gpib_sc, GPIBPRI,"gpibpoll",1); while (!(inb(ISR1)&2)&&(status==EWOULDBLOCK)); outb(AUXMR,seoi); /*Set EOI for the last byte*/ outb(AUXMR,0x5E); /*Clear SYNC*/ outb(CDOR,data[counter]); counter++; count--; } do if (!(inb(ISR1)&2)) status=tsleep((caddr_t)&gpib_sc, GPIBPRI,"gpibpoll",1); while (!(inb(ISR1)&2)&&(status==EWOULDBLOCK)); if (!(inb(ISR1)&2)&&status==EWOULDBLOCK) do status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",1); while (!(inb(ISR1)&2)&&status==EWOULDBLOCK); outb(AUXMR,tca); /* Regain full control of the bus*/ if (!(inb(ISR2)&8)&&status==EWOULDBLOCK) do status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",1); while (!(inb(ISR2)&8)&&status==EWOULDBLOCK); outb(CDOR,63); /*unlisten*/ if (!(inb(ISR2)&8)&&status==EWOULDBLOCK) do status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",1); while (!(inb(ISR2)&8)&&status==EWOULDBLOCK); outb(AUXMR,0x5E); /*Clear SYNC*/ outb (CDOR,95);/*untalk*/ if (!(inb(ISR2)&8)&&status==EWOULDBLOCK) do status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",1); while (!(inb(ISR2)&8)&&status==EWOULDBLOCK); return(counter); } static int readgpibfifo(unsigned char device,char *data,int count) { int status; int status2 = 0; int status1; int counter; int fifopos; unsigned inword; outb(IMR2,0x30); /*we have to enable DMA (0x30) for turbo488 to work*/ /*outb(IMR3,0x1F); outb(INTR,1); */ outb(CMDR,0x20); outb(CFG,14+0x60+1); /* Halt on int,read, fifo B first, CCEN TMOE TIM */ outb(CMDR,0x10); /*RESET fifos*/ outb(CCRG,tcs); /*program to tcs at end*/ outb(CMDR,0x08);/*STOP??*/ status=EWOULDBLOCK; do status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",1); while (!(inb(ISR2)&8)&&status==EWOULDBLOCK); outb (CDOR,32); /*Address controller (me) to listen*/ do status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",1); while (!(inb(ISR2)&8)&&status==EWOULDBLOCK); outb(CDOR,(device&31)+64);/*address device to talk*/ do status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",1); while (!(inb(ISR2)&8)&&status==EWOULDBLOCK); outb(AUXMR,gts); /*Set to Standby (Controller)*/ counter=0; fifopos=0; outb(CMDR,0x04); /*Tell TURBO488 to GO*/ do { status1=inb(ISR3); if (!(status1&0x01)&&(status1&0x04)){ status2=inb(STS2); inword=inw(FIFOB); *(unsigned*)(data+counter)=inword; /* printf ("Read:%c,%c\n",data[counter],data[counter+1]);*/ counter+=2; } else { status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",4); } } while (!(status1&0x01)&&status==EWOULDBLOCK); if(!(status2 & 0x04)){ /*Only 1 byte came in on last 16 bit transfer*/ data[counter-1]=0; counter--; } else data[counter]=0; outb(CMDR,0x08); /*send STOP*/ do{ status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",1); } while(!(inb(ISR3)&0x11)&&status==EWOULDBLOCK); /*wait for DONE and STOP*/ outb(AUXMR,0x55); outb(IMR2,0x00); /*we have to enable DMA (0x30) for turbo488 to work*/ outb(CMDR,0x20); /*soft reset turbo488*/ outb(CMDR,0x10); /*reset fifos*/ /* do status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",1); while (!(inb(ISR1)&2));*/ outb(AUXMR,tca); /* Regain full control of the bus*/ do status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",1); while (!(inb(ISR2)&8)&&status==EWOULDBLOCK); outb(CDOR,63); /*unlisten*/ do status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",1); while (!(inb(ISR2)&8)&&status==EWOULDBLOCK); outb(AUXMR,0x5E); /*Clear SYNC*/ outb (CDOR,95);/*untalk*/ do status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",1); while (!(inb(ISR2)&8)&&status==EWOULDBLOCK); return(counter); } /* Return the status byte from device */ static char spoll(unsigned char device) { int status=EWOULDBLOCK; unsigned int statusbyte; if (!(inb(ISR2)&8)) do status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",1); while (!(inb(ISR2)&8)&&status==EWOULDBLOCK); outb(CDOR,(device&31)+64);/*address device to talk*/ if (!(inb(ISR2)&8)&&status==EWOULDBLOCK) do status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",1); while (!(inb(ISR2)&8)&&status==EWOULDBLOCK); outb (CDOR,32); /*Address controller (me) to listen*/ if (!(inb(ISR2)&8)&&status==EWOULDBLOCK) do status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",1); while (!(inb(ISR2)&8)&&status==EWOULDBLOCK); outb(AUXMR,0x5E); outb (CDOR,0x18); /*Send SPE (serial poll enable)*/ if (!(inb(ISR2)&8)&&status==EWOULDBLOCK) do status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",1); while (!(inb(ISR2)&8)&&status==EWOULDBLOCK); /*wait for bus to be synced*/ if (!(inb(ISR0)&1)&&status==EWOULDBLOCK) do status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",1); while (!(inb(ISR0)&1)&&status==EWOULDBLOCK); outb(AUXMR,gts); /*Set to Standby (Controller)*/ if (!(inb(ISR1)&1)&&status==EWOULDBLOCK) do status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",1); while (!(inb(ISR1)&1)&&status==EWOULDBLOCK); outb(AUXMR,0x5E); outb(AUXMR,tcs); /* Take control after next read*/ statusbyte=inb(DIR); if (!(inb(ISR2)&8)&&status==EWOULDBLOCK) do status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",1); while (!(inb(ISR2)&8)&&status==EWOULDBLOCK); outb(CDOR,0x19); /*SPD (serial poll disable)*/ /*wait for bus to be synced*/ if (!(inb(ISR0)&1)&&status==EWOULDBLOCK) do status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",1); while (!(inb(ISR0)&1)&&status==EWOULDBLOCK); if (!(inb(ISR2)&8)&&status==EWOULDBLOCK) do status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",1); while (!(inb(ISR2)&8)&&status==EWOULDBLOCK); outb(CDOR,95); /*untalk*/ if (!(inb(ISR2)&8)&&status==EWOULDBLOCK) do status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",1); while (!(inb(ISR2)&8)&&status==EWOULDBLOCK); outb(AUXMR,0x5E); outb (CDOR,63);/*unlisten*/ if (!(inb(ISR2)&8)&&status==EWOULDBLOCK) do status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",1); while (!(inb(ISR2)&8)&&status==EWOULDBLOCK); /*wait for bus to be synced*/ if (!(inb(ISR0)&1)&&status==EWOULDBLOCK) do status=tsleep((caddr_t)&gpib_sc,GPIBPRI,"gpibpoll",1); while (!(inb(ISR0)&1)&&status==EWOULDBLOCK); return(statusbyte); } - -#endif /* NGPIB > 0 */ diff --git a/sys/i386/isa/gsc.c b/sys/i386/isa/gsc.c index 3b3f0f3c8a43..77626767d644 100644 --- a/sys/i386/isa/gsc.c +++ b/sys/i386/isa/gsc.c @@ -1,834 +1,831 @@ /* gsc.c - device driver for handy scanners * * Current version supports: * * - Genius GS-4500 * * Copyright (c) 1995 Gunther Schadow. All rights reserved. * * 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 * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, 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 Gunther Schadow. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ * */ #include "gsc.h" -#if NGSC > 0 #include #include #include #include #include #include #include #include #include #include #include /*********************************************************************** * * CONSTANTS & DEFINES * ***********************************************************************/ #define PROBE_FAIL 0 #define PROBE_SUCCESS IO_GSCSIZE #define ATTACH_FAIL 0 #define ATTACH_SUCCESS 1 #define SUCCESS 0 #define FAIL -1 #define INVALID FAIL #define DMA1_READY 0x08 #ifdef GSCDEBUG #define lprintf(args) \ do { \ if (scu->flags & FLAG_DEBUG) \ printf args; \ } while (0) #else #define lprintf(args) #endif #define MIN(a, b) (((a) < (b)) ? (a) : (b)) #define TIMEOUT (hz*15) /* timeout while reading a buffer - default value */ #define LONG (hz/60) /* timesteps while reading a buffer */ #define GSCPRI PRIBIO /* priority while reading a buffer */ /*********************************************************************** * * LAYOUT OF THE MINOR NUMBER * ***********************************************************************/ #define UNIT_MASK 0xc0 /* unit gsc0 .. gsc3 */ #define UNIT(x) (x >> 6) #define DBUG_MASK 0x20 #define FRMT_MASK 0x18 /* output format */ #define FRMT_RAW 0x00 /* output bits as read from scanner */ #define FRMT_GRAY 0x10 /* output graymap (not implemented yet) */ #define FRMT_PBM 0x08 /* output pbm format */ #define FRMT_PGM 0x18 /*********************************************************************** * * THE GEMOMETRY TABLE * ***********************************************************************/ #define GEOMTAB_SIZE 7 static const struct gsc_geom { int dpi; /* dots per inch */ int dpl; /* dots per line */ int g_res; /* get resolution value (status flag) */ int s_res; /* set resolution value (control register) */ } geomtab[GEOMTAB_SIZE] = { { 100, 424, GSC_RES_100, GSC_CNT_424}, { 200, 840, GSC_RES_200, GSC_CNT_840}, { 300, 1264, GSC_RES_300, GSC_CNT_1264}, { 400, 1648, GSC_RES_400, GSC_CNT_1648}, { -1, 1696, -1, GSC_CNT_1696}, { -2, 2644, -2, GSC_CNT_2544}, { -3, 3648, -3, GSC_CNT_3648}, }; #define NEW_GEOM { INVALID, INVALID, INVALID, INVALID } /*********************************************************************** * * THE TABLE OF UNITS * ***********************************************************************/ struct _sbuf { size_t size; size_t poi; char *base; }; struct gsc_unit { int channel; /* DMA channel */ int data; /* - video port */ int stat; /* - status port */ int ctrl; /* - control port */ int clrp; /* - clear port */ int flags; #define ATTACHED 0x01 #define OPEN 0x02 #define READING 0x04 #define EOF 0x08 #define FLAG_DEBUG 0x10 #define PBM_MODE 0x20 int geometry; /* resolution as geomtab index */ int blen; /* length of buffer in lines */ int btime; /* timeout of buffer in seconds/hz */ struct _sbuf sbuf; char ctrl_byte; /* the byte actually written to ctrl port */ int height; /* height, for pnm modes */ size_t bcount; /* bytes to read, for pnm modes */ struct _sbuf hbuf; /* buffer for pnm header data */ }; static struct gsc_unit unittab[NGSC]; /* I could not find a reasonable buffer size limit other than by * experiments. MAXPHYS is obviously too much, while DEV_BSIZE and * PAGE_SIZE are really too small. There must be something wrong * with isa_dmastart/isa_dmarangecheck HELP!!! */ #define MAX_BUFSIZE 0x3000 #define DEFAULT_BLEN 59 /*********************************************************************** * * THE PER-DRIVER RECORD FOR ISA.C * ***********************************************************************/ static int gscprobe (struct isa_device *isdp); static int gscattach(struct isa_device *isdp); struct isa_driver gscdriver = { gscprobe, gscattach, "gsc" }; static d_open_t gscopen; static d_close_t gscclose; static d_read_t gscread; static d_ioctl_t gscioctl; #define CDEV_MAJOR 47 static struct cdevsw gsc_cdevsw = { /* open */ gscopen, /* close */ gscclose, /* read */ gscread, /* write */ nowrite, /* ioctl */ gscioctl, /* poll */ nopoll, /* mmap */ nommap, /* strategy */ nostrategy, /* name */ "gsc", /* maj */ CDEV_MAJOR, /* dump */ nodump, /* psize */ nopsize, /* flags */ 0, /* bmaj */ -1 }; /*********************************************************************** * * LOCALLY USED SUBROUTINES * ***********************************************************************/ /*********************************************************************** * * lookup_geometry -- lookup a record in the geometry table by pattern * * The caller supplies a geometry record pattern, where INVALID * matches anything. Returns the index in the table or INVALID if * lookup fails. */ static int lookup_geometry(struct gsc_geom geom, const struct gsc_unit *scu) { struct gsc_geom tab; int i; for(i=0; istat)); if ( ( geom.g_res = inb(scu->stat) ) == FAIL ) return INVALID; geom.g_res &= GSC_RES_MASK; return lookup_geometry(geom, scu); } /*********************************************************************** * * buffer_allocate -- allocate/reallocate a buffer * Now just checks that the preallocated buffer is large enough. */ static int buffer_allocate(struct gsc_unit *scu) { size_t size; size = scu->blen * geomtab[scu->geometry].dpl / 8; lprintf(("gsc.buffer_allocate: need 0x%x bytes\n", size)); if ( size > MAX_BUFSIZE ) { lprintf(("gsc.buffer_allocate: 0x%x bytes are too much\n", size)); return ENOMEM; } scu->sbuf.size = size; scu->sbuf.poi = size; lprintf(("gsc.buffer_allocate: ok\n")); return SUCCESS; } /*********************************************************************** * * buffer_read -- scan a buffer */ static int buffer_read(struct gsc_unit *scu) { int stb; int res = SUCCESS; int chan_bit; char *p; int sps; int delay; lprintf(("gsc.buffer_read: begin\n")); if (scu->ctrl_byte == INVALID) { lprintf(("gsc.buffer_read: invalid ctrl_byte\n")); return EIO; } sps=splbio(); outb( scu->ctrl, scu->ctrl_byte | GSC_POWER_ON ); outb( scu->clrp, 0 ); stb = inb( scu->stat ); isa_dmastart(B_READ, scu->sbuf.base, scu->sbuf.size, scu->channel); chan_bit = 0x01 << scu->channel; for(delay=0; !(inb(DMA1_READY) & 0x01 << scu->channel); delay += LONG) { if(delay >= scu->btime) { splx(sps); lprintf(("gsc.buffer_read: timeout\n")); res = EWOULDBLOCK; break; } res = tsleep((caddr_t)scu, GSCPRI | PCATCH, "gscread", LONG); if ( ( res == 0 ) || ( res == EWOULDBLOCK ) ) res = SUCCESS; else break; } splx(sps); isa_dmadone(B_READ, scu->sbuf.base, scu->sbuf.size, scu->channel); outb( scu->clrp, 0 ); if(res != SUCCESS) { lprintf(("gsc.buffer_read: aborted with %d\n", res)); return res; } lprintf(("gsc.buffer_read: invert buffer\n")); for(p = scu->sbuf.base + scu->sbuf.size - 1; p >= scu->sbuf.base; p--) *p = ~*p; scu->sbuf.poi = 0; lprintf(("gsc.buffer_read: ok\n")); return SUCCESS; } /*********************************************************************** * * the main functions * ***********************************************************************/ /*********************************************************************** * * gscprobe * * read status port and check for proper configuration: * - if address group matches (status byte has reasonable value) * - if DMA channel matches (status byte has correct value) */ static int gscprobe (struct isa_device *isdp) { int unit = isdp->id_unit; struct gsc_unit *scu = unittab + unit; int stb; struct gsc_geom geom = NEW_GEOM; static int once; if (!once++) cdevsw_add(&gsc_cdevsw); scu->flags = FLAG_DEBUG; lprintf(("gsc%d.probe " "on iobase 0x%03x, irq %d, drq %d, addr %p, size %d\n", unit, isdp->id_iobase, isdp->id_irq, isdp->id_drq, isdp->id_maddr, isdp->id_msize)); if ( isdp->id_iobase < 0 ) { lprintf(("gsc%d.probe: no iobase given\n", unit)); return PROBE_FAIL; } stb = inb( GSC_STAT(isdp->id_iobase) ); if (stb == FAIL) { lprintf(("gsc%d.probe: get status byte failed\n", unit)); return PROBE_FAIL; } scu->data = GSC_DATA(isdp->id_iobase); scu->stat = GSC_STAT(isdp->id_iobase); scu->ctrl = GSC_CTRL(isdp->id_iobase); scu->clrp = GSC_CLRP(isdp->id_iobase); outb(scu->clrp,stb); stb = inb(scu->stat); switch(stb & GSC_CNF_MASK) { case GSC_CNF_DMA1: lprintf(("gsc%d.probe: DMA 1\n", unit)); scu->channel = 1; break; case GSC_CNF_DMA3: lprintf(("gsc%d.probe: DMA 3\n", unit)); scu->channel = 3; break; case GSC_CNF_IRQ3: lprintf(("gsc%d.probe: IRQ 3\n", unit)); goto probe_noirq; case GSC_CNF_IRQ5: lprintf(("gsc%d.probe: IRQ 5\n", unit)); probe_noirq: lprintf(("gsc%d.probe: sorry, can't use IRQ yet\n", unit)); return PROBE_FAIL; default: lprintf(("gsc%d.probe: invalid status byte 0x%02x\n", unit, (u_char) stb)); return PROBE_FAIL; } if (isdp->id_drq < 0) isdp->id_drq = scu->channel; if (scu->channel != isdp->id_drq) { lprintf(("gsc%d.probe: drq mismatch: config: %d; hardware: %d\n", unit, isdp->id_drq, scu->channel)); return PROBE_FAIL; } geom.g_res = stb & GSC_RES_MASK; scu->geometry = lookup_geometry(geom, scu); if (scu->geometry == INVALID) { lprintf(("gsc%d.probe: geometry lookup failed\n", unit)); return PROBE_FAIL; } else { scu->ctrl_byte = geomtab[scu->geometry].s_res; outb(scu->ctrl, scu->ctrl_byte | GSC_POWER_ON); lprintf(("gsc%d.probe: status 0x%02x, %ddpi\n", unit, stb, geomtab[scu->geometry].dpi)); outb(scu->ctrl, scu->ctrl_byte & ~GSC_POWER_ON); } lprintf(("gsc%d.probe: ok\n", unit)); scu->flags &= ~FLAG_DEBUG; return PROBE_SUCCESS; } /*********************************************************************** * * gscattach * * finish initialization of unit structure * get geometry value */ static int gscattach(struct isa_device *isdp) { int unit = isdp->id_unit; struct gsc_unit *scu = unittab + unit; scu->flags |= FLAG_DEBUG; lprintf(("gsc%d.attach: " "iobase 0x%03x, irq %d, drq %d, addr %p, size %d\n", unit, isdp->id_iobase, isdp->id_irq, isdp->id_drq, isdp->id_maddr, isdp->id_msize)); printf("gsc%d: GeniScan GS-4500 at %ddpi\n", unit, geomtab[scu->geometry].dpi); /* * Initialize buffer structure. * XXX this must be done early to give a good chance of getting a * contiguous buffer. This wastes memory. */ scu->sbuf.base = contigmalloc((unsigned long)MAX_BUFSIZE, M_DEVBUF, M_NOWAIT, 0ul, 0xfffffful, 1ul, 0x10000ul); if ( scu->sbuf.base == NULL ) { lprintf(("gsc%d.attach: buffer allocation failed\n", unit)); return ATTACH_FAIL; /* XXX attach must not fail */ } scu->sbuf.size = INVALID; scu->sbuf.poi = INVALID; scu->blen = DEFAULT_BLEN; scu->btime = TIMEOUT; scu->flags |= ATTACHED; lprintf(("gsc%d.attach: ok\n", unit)); scu->flags &= ~FLAG_DEBUG; #define GSC_UID 0 #define GSC_GID 13 make_dev(&gsc_cdevsw, unit<<6, GSC_UID, GSC_GID, 0666, "gsc%d", unit); make_dev(&gsc_cdevsw, ((unit<<6) + FRMT_PBM), GSC_UID, GSC_GID, 0666, "gsc%dp", unit); make_dev(&gsc_cdevsw, ((unit<<6) + DBUG_MASK), GSC_UID, GSC_GID, 0666, "gsc%dd", unit); make_dev(&gsc_cdevsw, ((unit<<6) + DBUG_MASK+FRMT_PBM), GSC_UID, GSC_GID, 0666, "gsc%dpd", unit); return ATTACH_SUCCESS; } /*********************************************************************** * * gscopen * * set open flag * set modes according to minor number * don't switch scanner on, wait until first read ioctls go before */ static int gscopen (dev_t dev, int flags, int fmt, struct proc *p) { struct gsc_unit *scu; int unit; unit = UNIT(minor(dev)) & UNIT_MASK; if ( unit >= NGSC ) { #ifdef GSCDEBUG /* XXX lprintf isn't valid here since there is no scu. */ printf("gsc%d.open: unconfigured unit number (max %d)\n", unit, NGSC); #endif return ENXIO; } scu = unittab + unit; if ( !( scu->flags & ATTACHED ) ) { lprintf(("gsc%d.open: unit was not attached successfully 0x%04x\n", unit, scu->flags)); return ENXIO; } if ( minor(dev) & DBUG_MASK ) scu->flags |= FLAG_DEBUG; else scu->flags &= ~FLAG_DEBUG; switch(minor(dev) & FRMT_MASK) { case FRMT_PBM: scu->flags |= PBM_MODE; lprintf(("gsc%d.open: pbm mode\n", unit)); break; case FRMT_RAW: lprintf(("gsc%d.open: raw mode\n", unit)); scu->flags &= ~PBM_MODE; break; default: lprintf(("gsc%d.open: gray maps are not yet supported", unit)); return ENXIO; } lprintf(("gsc%d.open: minor %d\n", unit, minor(dev))); if ( scu->flags & OPEN ) { lprintf(("gsc%d.open: already open", unit)); return EBUSY; } if (isa_dma_acquire(scu->channel)) return(EBUSY); scu->flags |= OPEN; return SUCCESS; } /*********************************************************************** * * gscclose * * turn off scanner * release the buffer */ static int gscclose (dev_t dev, int flags, int fmt, struct proc *p) { int unit = UNIT(minor(dev)); struct gsc_unit *scu = unittab + unit; lprintf(("gsc%d.close: minor %d\n", unit, minor(dev))); if ( unit >= NGSC || !( scu->flags & ATTACHED ) ) { lprintf(("gsc%d.read: unit was not attached successfully 0x%04x\n", unit, scu->flags)); return ENXIO; } outb(scu->ctrl, scu->ctrl_byte & ~GSC_POWER_ON); scu->sbuf.size = INVALID; scu->sbuf.poi = INVALID; isa_dma_release(scu->channel); scu->flags &= ~(FLAG_DEBUG | OPEN | READING); return SUCCESS; } /*********************************************************************** * * gscread */ static int gscread (dev_t dev, struct uio *uio, int ioflag) { int unit = UNIT(minor(dev)); struct gsc_unit *scu = unittab + unit; size_t nbytes; int res; lprintf(("gsc%d.read: minor %d\n", unit, minor(dev))); if ( unit >= NGSC || !( scu->flags & ATTACHED ) ) { lprintf(("gsc%d.read: unit was not attached successfully 0x%04x\n", unit, scu->flags)); return ENXIO; } if ( !(scu->flags & READING) ) { res = buffer_allocate(scu); if ( res == SUCCESS ) scu->flags |= READING; else return res; scu->ctrl_byte = geomtab[scu->geometry].s_res; /* initialize for pbm mode */ if ( scu->flags & PBM_MODE ) { char *p; int width = geomtab[scu->geometry].dpl; sprintf(scu->sbuf.base,"P4 %d %d\n", width, scu->height); scu->bcount = scu->height * width / 8; lprintf(("gsc%d.read: initializing pbm mode: `%s', bcount: 0x%x\n", unit, scu->sbuf.base, scu->bcount)); /* move header to end of sbuf */ for(p=scu->sbuf.base; *p; p++); while(--p >= scu->sbuf.base) { *(char *)(scu->sbuf.base + --scu->sbuf.poi) = *p; scu->bcount++; } } } lprintf(("gsc%d.read(before buffer_read): " "size 0x%x, pointer 0x%x, bcount 0x%x, ok\n", unit, scu->sbuf.size, scu->sbuf.poi, scu->bcount)); if ( scu->sbuf.poi == scu->sbuf.size ) if ( (res = buffer_read(scu)) != SUCCESS ) return res; lprintf(("gsc%d.read(after buffer_read): " "size 0x%x, pointer 0x%x, bcount 0x%x, ok\n", unit, scu->sbuf.size, scu->sbuf.poi, scu->bcount)); nbytes = MIN( uio->uio_resid, scu->sbuf.size - scu->sbuf.poi ); if ( (scu->flags & PBM_MODE) ) nbytes = MIN( nbytes, scu->bcount ); lprintf(("gsc%d.read: transferring 0x%x bytes", unit, nbytes)); res = uiomove(scu->sbuf.base + scu->sbuf.poi, nbytes, uio); if ( res != SUCCESS ) { lprintf(("gsc%d.read: uiomove failed %d", unit, res)); return res; } scu->sbuf.poi += nbytes; if ( scu->flags & PBM_MODE ) scu->bcount -= nbytes; lprintf(("gsc%d.read: size 0x%x, pointer 0x%x, bcount 0x%x, ok\n", unit, scu->sbuf.size, scu->sbuf.poi, scu->bcount)); return SUCCESS; } /*********************************************************************** * * gscioctl * */ static int gscioctl (dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) { int unit = UNIT(minor(dev)); struct gsc_unit *scu = unittab + unit; lprintf(("gsc%d.ioctl: minor %d\n", unit, minor(dev))); if ( unit >= NGSC || !( scu->flags & ATTACHED ) ) { lprintf(("gsc%d.ioctl: unit was not attached successfully 0x%04x\n", unit, scu->flags)); return ENXIO; } switch(cmd) { case GSC_SRESSW: lprintf(("gsc%d.ioctl:GSC_SRESSW\n", unit)); if ( scu->flags & READING ) { lprintf(("gsc%d:ioctl on already reading unit\n", unit)); return EBUSY; } scu->geometry = get_geometry(scu); return SUCCESS; case GSC_GRES: *(int *)data=geomtab[scu->geometry].dpi; lprintf(("gsc%d.ioctl:GSC_GRES %ddpi\n", unit, *(int *)data)); return SUCCESS; case GSC_GWIDTH: *(int *)data=geomtab[scu->geometry].dpl; lprintf(("gsc%d.ioctl:GSC_GWIDTH %d\n", unit, *(int *)data)); return SUCCESS; case GSC_SRES: case GSC_SWIDTH: lprintf(("gsc%d.ioctl:GSC_SRES or GSC_SWIDTH %d\n", unit, *(int *)data)); { int g; struct gsc_geom geom = NEW_GEOM; if ( cmd == GSC_SRES ) geom.dpi = *(int *)data; else geom.dpl = *(int *)data; if ( ( g = lookup_geometry(geom, scu) ) == INVALID ) return EINVAL; scu->geometry = g; return SUCCESS; } case GSC_GHEIGHT: *(int *)data=scu->height; lprintf(("gsc%d.ioctl:GSC_GHEIGHT %d\n", unit, *(int *)data)); return SUCCESS; case GSC_SHEIGHT: lprintf(("gsc%d.ioctl:GSC_SHEIGHT %d\n", unit, *(int *)data)); if ( scu->flags & READING ) { lprintf(("gsc%d:ioctl on already reading unit\n", unit)); return EBUSY; } scu->height=*(int *)data; return SUCCESS; case GSC_GBLEN: *(int *)data=scu->blen; lprintf(("gsc%d.ioctl:GSC_GBLEN %d\n", unit, *(int *)data)); return SUCCESS; case GSC_SBLEN: lprintf(("gsc%d.ioctl:GSC_SBLEN %d\n", unit, *(int *)data)); if (*(int *)data * geomtab[scu->geometry].dpl / 8 > MAX_BUFSIZE) { lprintf(("gsc%d:ioctl buffer size too high\n", unit)); return ENOMEM; } scu->blen=*(int *)data; return SUCCESS; case GSC_GBTIME: *(int *)data = scu->btime / hz; lprintf(("gsc%d.ioctl:GSC_GBTIME %d\n", unit, *(int *)data)); return SUCCESS; case GSC_SBTIME: scu->btime = *(int *)data * hz; lprintf(("gsc%d.ioctl:GSC_SBTIME %d\n", unit, *(int *)data)); return SUCCESS; default: return ENOTTY; } } - -#endif /* NGSC > 0 */ diff --git a/sys/i386/isa/if_le.c b/sys/i386/isa/if_le.c index 620649545287..ff910dda1bab 100644 --- a/sys/i386/isa/if_le.c +++ b/sys/i386/isa/if_le.c @@ -1,2040 +1,2038 @@ /*- * Copyright (c) 1994 Matt Thomas (thomas@lkg.dec.com) * All rights reserved. * * 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 * notice, this list of conditions and the following disclaimer. * 2. The name of the author may not be used to endorse or promote products * derived from this software withough specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ /* * DEC EtherWORKS 2 Ethernet Controllers * DEC EtherWORKS 3 Ethernet Controllers * * Written by Matt Thomas * BPF support code stolen directly from if_ec.c * * This driver supports the DEPCA, DE100, DE101, DE200, DE201, * DE2002, DE203, DE204, DE205, and DE422 cards. */ #include "le.h" -#if NLE > 0 #include "opt_inet.h" #include "opt_ipx.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Forward declarations */ typedef struct le_softc le_softc_t; typedef struct le_board le_board_t; typedef u_short le_mcbits_t; #define LE_MC_NBPW_LOG2 4 #define LE_MC_NBPW (1 << LE_MC_NBPW_LOG2) #define IF_RESET_ARGS int unit #define LE_RESET(ifp) (((sc)->if_reset)((sc)->le_if.if_unit)) #if !defined(LE_NOLEMAC) /* * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * * Start of DEC EtherWORKS III (LEMAC) dependent structures * */ #include /* Include LEMAC definitions */ static int lemac_probe(le_softc_t *sc, const le_board_t *bd, int *msize); struct le_lemac_info { u_int lemac__lastpage; /* last 2K page */ u_int lemac__memmode; /* Are we in 2K, 32K, or 64K mode */ u_int lemac__membase; /* Physical address of start of RAM */ u_int lemac__txctl; /* Transmit Control Byte */ u_int lemac__txmax; /* Maximum # of outstanding transmits */ le_mcbits_t lemac__mctbl[LEMAC_MCTBL_SIZE/sizeof(le_mcbits_t)]; /* local copy of multicast table */ u_char lemac__eeprom[LEMAC_EEP_SIZE]; /* local copy eeprom */ char lemac__prodname[LEMAC_EEP_PRDNMSZ+1]; /* prodname name */ #define lemac_lastpage le_un.un_lemac.lemac__lastpage #define lemac_memmode le_un.un_lemac.lemac__memmode #define lemac_membase le_un.un_lemac.lemac__membase #define lemac_txctl le_un.un_lemac.lemac__txctl #define lemac_txmax le_un.un_lemac.lemac__txmax #define lemac_mctbl le_un.un_lemac.lemac__mctbl #define lemac_eeprom le_un.un_lemac.lemac__eeprom #define lemac_prodname le_un.un_lemac.lemac__prodname }; #endif /* !defined(LE_NOLEMAC) */ #if !defined(LE_NOLANCE) /* * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * * Start of DEC EtherWORKS II (LANCE) dependent structures * */ #include #ifndef LN_DOSTATS #define LN_DOSTATS 1 #endif static int depca_probe(le_softc_t *sc, const le_board_t *bd, int *msize); typedef struct lance_descinfo lance_descinfo_t; typedef struct lance_ring lance_ring_t; typedef unsigned lance_addr_t; struct lance_descinfo { caddr_t di_addr; /* address of descriptor */ lance_addr_t di_bufaddr; /* LANCE address of buffer owned by descriptor */ unsigned di_buflen; /* size of buffer owned by descriptor */ struct mbuf *di_mbuf; /* mbuf being transmitted/received */ }; struct lance_ring { lance_descinfo_t *ri_first; /* Pointer to first descriptor in ring */ lance_descinfo_t *ri_last; /* Pointer to last + 1 descriptor in ring */ lance_descinfo_t *ri_nextin; /* Pointer to next one to be given to HOST */ lance_descinfo_t *ri_nextout; /* Pointer to next one to be given to LANCE */ unsigned ri_max; /* Size of Ring - 1 */ unsigned ri_free; /* Number of free rings entires (owned by HOST) */ lance_addr_t ri_heap; /* Start of RAM for this ring */ lance_addr_t ri_heapend; /* End + 1 of RAM for this ring */ lance_addr_t ri_outptr; /* Pointer to first output byte */ unsigned ri_outsize; /* Space remaining for output */ }; struct le_lance_info { unsigned lance__csr1; /* LANCE Address of init block (low 16) */ unsigned lance__csr2; /* LANCE Address of init block (high 8) */ unsigned lance__csr3; /* Copy of CSR3 */ unsigned lance__rap; /* IO Port Offset of RAP */ unsigned lance__rdp; /* IO Port Offset of RDP */ unsigned lance__ramoffset; /* Offset to valid LANCE RAM */ unsigned lance__ramsize; /* Amount of RAM shared by LANCE */ unsigned lance__rxbufsize; /* Size of a receive buffer */ ln_initb_t lance__initb; /* local copy of LANCE initblock */ ln_initb_t *lance__raminitb; /* copy to board's LANCE initblock (debugging) */ ln_desc_t *lance__ramdesc; /* copy to board's LANCE descriptors (debugging) */ lance_ring_t lance__rxinfo; /* Receive ring information */ lance_ring_t lance__txinfo; /* Transmit ring information */ #define lance_csr1 le_un.un_lance.lance__csr1 #define lance_csr2 le_un.un_lance.lance__csr2 #define lance_csr3 le_un.un_lance.lance__csr3 #define lance_rap le_un.un_lance.lance__rap #define lance_rdp le_un.un_lance.lance__rdp #define lance_ramoffset le_un.un_lance.lance__ramoffset #define lance_ramsize le_un.un_lance.lance__ramsize #define lance_rxbufsize le_un.un_lance.lance__rxbufsize #define lance_initb le_un.un_lance.lance__initb #define lance_raminitb le_un.un_lance.lance__raminitb #define lance_ramdesc le_un.un_lance.lance__ramdesc #define lance_rxinfo le_un.un_lance.lance__rxinfo #define lance_txinfo le_un.un_lance.lance__txinfo }; #endif /* !defined(LE_NOLANCE) */ /* * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * * Start of Common Code * */ static void (*le_intrvec[NLE])(le_softc_t *sc); /* * Ethernet status, per interface. */ struct le_softc { struct arpcom le_ac; /* Common Ethernet/ARP Structure */ void (*if_init) __P((int)); /* Interface init routine */ void (*if_reset) __P((int)); /* Interface reset routine */ caddr_t le_membase; /* Starting memory address (virtual) */ unsigned le_iobase; /* Starting I/O base address */ unsigned le_irq; /* Interrupt Request Value */ unsigned le_flags; /* local copy of if_flags */ #define LE_BRDCSTONLY 0x01000000 /* If only broadcast is enabled */ u_int le_mcmask; /* bit mask for CRC-32 for multicast hash */ le_mcbits_t *le_mctbl; /* pointer to multicast table */ const char *le_prodname; /* product name DE20x-xx */ u_char le_hwaddr[6]; /* local copy of hwaddr */ unsigned le_scast_drops; /* singlecast drops */ unsigned le_mcast_drops; /* multicast drops */ unsigned le_bcast_drops; /* broadcast drops */ union { #if !defined(LE_NOLEMAC) struct le_lemac_info un_lemac; /* LEMAC specific information */ #endif #if !defined(LE_NOLANCE) struct le_lance_info un_lance; /* Am7990 specific information */ #endif } le_un; }; #define le_if le_ac.ac_if static int le_probe(struct isa_device *dvp); static int le_attach(struct isa_device *dvp); static ointhand2_t le_intr; static int le_ioctl(struct ifnet *ifp, u_long command, caddr_t data); static void le_input(le_softc_t *sc, caddr_t seg1, size_t total_len, size_t len2, caddr_t seg2); static void le_multi_filter(le_softc_t *sc); static void le_multi_op(le_softc_t *sc, const u_char *mca, int oper_flg); static int le_read_macaddr(le_softc_t *sc, int ioreg, int skippat); #define LE_CRC32_POLY 0xEDB88320UL /* CRC-32 Poly -- Little Endian */ struct le_board { int (*bd_probe)(le_softc_t *sc, const le_board_t *bd, int *msize); }; static le_softc_t le_softc[NLE]; static const le_board_t le_boards[] = { #if !defined(LE_NOLEMAC) { lemac_probe }, /* DE20[345] */ #endif #if !defined(LE_NOLANCE) { depca_probe }, /* DE{20[012],422} */ #endif { NULL } /* Must Be Last! */ }; /* * This tells the autoconf code how to set us up. */ struct isa_driver ledriver = { le_probe, le_attach, "le", }; static unsigned le_intrs[NLE]; #define LE_ADDREQUAL(a1, a2) \ (((u_short *)a1)[0] == ((u_short *)a2)[0] \ || ((u_short *)a1)[1] == ((u_short *)a2)[1] \ || ((u_short *)a1)[2] == ((u_short *)a2)[2]) #define LE_ADDRBRDCST(a1) \ (((u_short *)a1)[0] == 0xFFFFU \ || ((u_short *)a1)[1] == 0xFFFFU \ || ((u_short *)a1)[2] == 0xFFFFU) #define LE_INL(sc, reg) \ ({ u_int data; \ __asm __volatile("inl %1, %0": "=a" (data): "d" ((u_short)((sc)->le_iobase + (reg)))); \ data; }) #define LE_OUTL(sc, reg, data) \ ({__asm __volatile("outl %0, %1"::"a" ((u_int)(data)), "d" ((u_short)((sc)->le_iobase + (reg))));}) #define LE_INW(sc, reg) \ ({ u_short data; \ __asm __volatile("inw %1, %0": "=a" (data): "d" ((u_short)((sc)->le_iobase + (reg)))); \ data; }) #define LE_OUTW(sc, reg, data) \ ({__asm __volatile("outw %0, %1"::"a" ((u_short)(data)), "d" ((u_short)((sc)->le_iobase + (reg))));}) #define LE_INB(sc, reg) \ ({ u_char data; \ __asm __volatile("inb %1, %0": "=a" (data): "d" ((u_short)((sc)->le_iobase + (reg)))); \ data; }) #define LE_OUTB(sc, reg, data) \ ({__asm __volatile("outb %0, %1"::"a" ((u_char)(data)), "d" ((u_short)((sc)->le_iobase + (reg))));}) #define MEMCPY(to, from, len) bcopy(from, to, len) #define MEMSET(where, what, howmuch) bzero(where, howmuch) #define MEMCMP(l, r, len) bcmp(l, r, len) static int le_probe( struct isa_device *dvp) { le_softc_t *sc = &le_softc[dvp->id_unit]; const le_board_t *bd; int iospace; if (dvp->id_unit >= NLE) { printf("%s%d not configured -- too many devices\n", ledriver.name, dvp->id_unit); return 0; } sc->le_iobase = dvp->id_iobase; sc->le_membase = (u_char *) dvp->id_maddr; sc->le_irq = dvp->id_irq; sc->le_if.if_name = ledriver.name; sc->le_if.if_unit = dvp->id_unit; /* * Find and Initialize board.. */ sc->le_flags &= ~(IFF_UP|IFF_ALLMULTI); for (bd = le_boards; bd->bd_probe != NULL; bd++) { if ((iospace = (*bd->bd_probe)(sc, bd, &dvp->id_msize)) != 0) { return iospace; } } return 0; } static int le_attach( struct isa_device *dvp) { le_softc_t *sc = &le_softc[dvp->id_unit]; struct ifnet *ifp = &sc->le_if; dvp->id_ointr = le_intr; ifp->if_softc = sc; ifp->if_mtu = ETHERMTU; printf("%s%d: %s ethernet address %6D\n", ifp->if_name, ifp->if_unit, sc->le_prodname, sc->le_ac.ac_enaddr, ":"); ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_output = ether_output; ifp->if_ioctl = le_ioctl; ifp->if_type = IFT_ETHER; ifp->if_addrlen = 6; ifp->if_hdrlen = 14; bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); if_attach(ifp); ether_ifattach(ifp); return 1; } static void le_intr( int unit) { int s = splimp(); le_intrs[unit]++; (*le_intrvec[unit])(&le_softc[unit]); splx(s); } #define LE_XTRA 0 static void le_input( le_softc_t *sc, caddr_t seg1, size_t total_len, size_t len1, caddr_t seg2) { struct ether_header eh; struct mbuf *m; if (total_len - sizeof(eh) > ETHERMTU || total_len - sizeof(eh) < ETHERMIN) { sc->le_if.if_ierrors++; return; } MEMCPY(&eh, seg1, sizeof(eh)); if (sc->le_if.if_bpf != NULL && seg2 == NULL) { bpf_tap(&sc->le_if, seg1, total_len); /* * If this is single cast but not to us * drop it! */ if ((eh.ether_dhost[0] & 1) == 0) { if (!LE_ADDREQUAL(eh.ether_dhost, sc->le_ac.ac_enaddr)) { sc->le_scast_drops++; return; } } else if ((sc->le_flags & IFF_MULTICAST) == 0) { sc->le_mcast_drops++; return; } else if (sc->le_flags & LE_BRDCSTONLY) { if (!LE_ADDRBRDCST(eh.ether_dhost)) { sc->le_bcast_drops++; return; } } } seg1 += sizeof(eh); total_len -= sizeof(eh); len1 -= sizeof(eh); MGETHDR(m, M_DONTWAIT, MT_DATA); if (m == NULL) { sc->le_if.if_ierrors++; return; } m->m_pkthdr.len = total_len; m->m_pkthdr.rcvif = &sc->le_if; if (total_len + LE_XTRA > MHLEN /* >= MINCLSIZE */) { MCLGET(m, M_DONTWAIT); if ((m->m_flags & M_EXT) == 0) { m_free(m); sc->le_if.if_ierrors++; return; } } else if (total_len + LE_XTRA > MHLEN && MINCLSIZE == (MHLEN+MLEN)) { MGET(m->m_next, M_DONTWAIT, MT_DATA); if (m->m_next == NULL) { m_free(m); sc->le_if.if_ierrors++; return; } m->m_next->m_len = total_len - MHLEN - LE_XTRA; len1 = total_len = MHLEN - LE_XTRA; MEMCPY(mtod(m->m_next, caddr_t), &seg1[MHLEN-LE_XTRA], m->m_next->m_len); } else if (total_len + LE_XTRA > MHLEN) { panic("le_input: pkt of unknown length"); } m->m_data += LE_XTRA; m->m_len = total_len; MEMCPY(mtod(m, caddr_t), seg1, len1); if (seg2 != NULL) MEMCPY(mtod(m, caddr_t) + len1, seg2, total_len - len1); if (sc->le_if.if_bpf != NULL && seg2 != NULL) { bpf_mtap(&sc->le_if, m); /* * If this is single cast but not to us * drop it! */ if ((eh.ether_dhost[0] & 1) == 0) { if (!LE_ADDREQUAL(eh.ether_dhost, sc->le_ac.ac_enaddr)) { sc->le_scast_drops++; m_freem(m); return; } } else if ((sc->le_flags & IFF_MULTICAST) == 0) { sc->le_mcast_drops++; m_freem(m); return; } else if (sc->le_flags & LE_BRDCSTONLY) { if (!LE_ADDRBRDCST(eh.ether_dhost)) { sc->le_bcast_drops++; m_freem(m); return; } } } ether_input(&sc->le_if, &eh, m); } static int le_ioctl( struct ifnet *ifp, u_long cmd, caddr_t data) { le_softc_t *sc = ifp->if_softc; int s, error = 0; if ((sc->le_flags & IFF_UP) == 0) return EIO; s = splimp(); switch (cmd) { case SIOCSIFADDR: case SIOCGIFADDR: case SIOCSIFMTU: error = ether_ioctl(ifp, cmd, data); break; case SIOCSIFFLAGS: { (*sc->if_init)(ifp->if_unit); break; } case SIOCADDMULTI: case SIOCDELMULTI: /* * Update multicast listeners */ (*sc->if_init)(ifp->if_unit); error = 0; break; default: { error = EINVAL; } } splx(s); return error; } /* * This is the standard method of reading the DEC Address ROMS. * I don't understand it but it does work. */ static int le_read_macaddr( le_softc_t *sc, int ioreg, int skippat) { int cksum, rom_cksum; if (!skippat) { int idx, idx2, found, octet; static u_char testpat[] = { 0xFF, 0, 0x55, 0xAA, 0xFF, 0, 0x55, 0xAA }; idx2 = found = 0; for (idx = 0; idx < 32; idx++) { octet = LE_INB(sc, ioreg); if (octet == testpat[idx2]) { if (++idx2 == sizeof testpat) { ++found; break; } } else { idx2 = 0; } } if (!found) return -1; } cksum = 0; sc->le_hwaddr[0] = LE_INB(sc, ioreg); sc->le_hwaddr[1] = LE_INB(sc, ioreg); cksum = *(u_short *) &sc->le_hwaddr[0]; sc->le_hwaddr[2] = LE_INB(sc, ioreg); sc->le_hwaddr[3] = LE_INB(sc, ioreg); cksum *= 2; if (cksum > 65535) cksum -= 65535; cksum += *(u_short *) &sc->le_hwaddr[2]; if (cksum > 65535) cksum -= 65535; sc->le_hwaddr[4] = LE_INB(sc, ioreg); sc->le_hwaddr[5] = LE_INB(sc, ioreg); cksum *= 2; if (cksum > 65535) cksum -= 65535; cksum += *(u_short *) &sc->le_hwaddr[4]; if (cksum >= 65535) cksum -= 65535; rom_cksum = LE_INB(sc, ioreg); rom_cksum |= LE_INB(sc, ioreg) << 8; if (cksum != rom_cksum) return -1; return 0; } static void le_multi_filter( le_softc_t *sc) { struct ifmultiaddr *ifma; MEMSET(sc->le_mctbl, 0, (sc->le_mcmask + 1) / 8); if (sc->le_if.if_flags & IFF_ALLMULTI) { sc->le_flags |= IFF_MULTICAST|IFF_ALLMULTI; return; } sc->le_flags &= ~IFF_MULTICAST; /* if (interface has had an address assigned) { */ le_multi_op(sc, etherbroadcastaddr, TRUE); sc->le_flags |= LE_BRDCSTONLY|IFF_MULTICAST; /* } */ sc->le_flags |= IFF_MULTICAST; for (ifma = sc->le_ac.ac_if.if_multiaddrs.lh_first; ifma; ifma = ifma->ifma_link.le_next) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; le_multi_op(sc, LLADDR((struct sockaddr_dl *)ifma->ifma_addr), 1); sc->le_flags &= ~LE_BRDCSTONLY; } } static void le_multi_op( le_softc_t *sc, const u_char *mca, int enable) { u_int idx, bit, data, crc = 0xFFFFFFFFUL; #ifdef __alpha for (data = *(__unaligned u_long *) mca, bit = 0; bit < 48; bit++, data >>= 1) crc = (crc >> 1) ^ (((crc ^ data) & 1) ? LE_CRC32_POLY : 0); #else for (idx = 0; idx < 6; idx++) for (data = *mca++, bit = 0; bit < 8; bit++, data >>= 1) crc = (crc >> 1) ^ (((crc ^ data) & 1) ? LE_CRC32_POLY : 0); #endif /* * The following two line convert the N bit index into a longword index * and a longword mask. */ crc &= sc->le_mcmask; bit = 1 << (crc & (LE_MC_NBPW -1)); idx = crc >> (LE_MC_NBPW_LOG2); /* * Set or clear hash filter bit in our table. */ if (enable) { sc->le_mctbl[idx] |= bit; /* Set Bit */ } else { sc->le_mctbl[idx] &= ~bit; /* Clear Bit */ } } #if !defined(LE_NOLEMAC) /* * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * * Start of DEC EtherWORKS III (LEMAC) dependent code * */ #define LEMAC_INTR_ENABLE(sc) \ LE_OUTB(sc, LEMAC_REG_IC, LE_INB(sc, LEMAC_REG_IC) | LEMAC_IC_ALL) #define LEMAC_INTR_DISABLE(sc) \ LE_OUTB(sc, LEMAC_REG_IC, LE_INB(sc, LEMAC_REG_IC) & ~LEMAC_IC_ALL) #define LEMAC_64K_MODE(mbase) (((mbase) >= 0x0A) && ((mbase) <= 0x0F)) #define LEMAC_32K_MODE(mbase) (((mbase) >= 0x14) && ((mbase) <= 0x1F)) #define LEMAC_2K_MODE(mbase) ( (mbase) >= 0x40) static void lemac_init(int unit); static void lemac_start(struct ifnet *ifp); static void lemac_reset(IF_RESET_ARGS); static void lemac_intr(le_softc_t *sc); static void lemac_rne_intr(le_softc_t *sc); static void lemac_tne_intr(le_softc_t *sc); static void lemac_txd_intr(le_softc_t *sc, unsigned cs_value); static void lemac_rxd_intr(le_softc_t *sc, unsigned cs_value); static int lemac_read_eeprom(le_softc_t *sc); static void lemac_init_adapmem(le_softc_t *sc); #define LE_MCBITS_ALL_1S ((le_mcbits_t)~(le_mcbits_t)0) static const le_mcbits_t lemac_allmulti_mctbl[16] = { LE_MCBITS_ALL_1S, LE_MCBITS_ALL_1S, LE_MCBITS_ALL_1S, LE_MCBITS_ALL_1S, LE_MCBITS_ALL_1S, LE_MCBITS_ALL_1S, LE_MCBITS_ALL_1S, LE_MCBITS_ALL_1S, LE_MCBITS_ALL_1S, LE_MCBITS_ALL_1S, LE_MCBITS_ALL_1S, LE_MCBITS_ALL_1S, LE_MCBITS_ALL_1S, LE_MCBITS_ALL_1S, LE_MCBITS_ALL_1S, LE_MCBITS_ALL_1S, }; /* * An IRQ mapping table. Less space than switch statement. */ static const int lemac_irqs[] = { IRQ5, IRQ10, IRQ11, IRQ15 }; /* * Some tuning/monitoring variables. */ static unsigned lemac_deftxmax = 16; /* see lemac_max above */ static unsigned lemac_txnospc = 0; /* total # of tranmit starvations */ static unsigned lemac_tne_intrs = 0; /* total # of tranmit done intrs */ static unsigned lemac_rne_intrs = 0; /* total # of receive done intrs */ static unsigned lemac_txd_intrs = 0; /* total # of tranmit error intrs */ static unsigned lemac_rxd_intrs = 0; /* total # of receive error intrs */ static int lemac_probe( le_softc_t *sc, const le_board_t *bd, int *msize) { int irq, portval; LE_OUTB(sc, LEMAC_REG_IOP, LEMAC_IOP_EEINIT); DELAY(LEMAC_EEP_DELAY); /* * Read Ethernet address if card is present. */ if (le_read_macaddr(sc, LEMAC_REG_APD, 0) < 0) return 0; MEMCPY(sc->le_ac.ac_enaddr, sc->le_hwaddr, 6); /* * Clear interrupts and set IRQ. */ portval = LE_INB(sc, LEMAC_REG_IC) & LEMAC_IC_IRQMSK; irq = lemac_irqs[portval >> 5]; LE_OUTB(sc, LEMAC_REG_IC, portval); /* * Make sure settings match. */ if (irq != sc->le_irq) { printf("%s%d: lemac configuration error: expected IRQ 0x%x actual 0x%x\n", sc->le_if.if_name, sc->le_if.if_unit, sc->le_irq, irq); return 0; } /* * Try to reset the unit */ sc->if_init = lemac_init; sc->le_if.if_start = lemac_start; sc->if_reset = lemac_reset; sc->lemac_memmode = 2; LE_RESET(sc); if ((sc->le_flags & IFF_UP) == 0) return 0; /* * Check for correct memory base configuration. */ if (vtophys(sc->le_membase) != sc->lemac_membase) { printf("%s%d: lemac configuration error: expected iomem 0x%x actual 0x%x\n", sc->le_if.if_name, sc->le_if.if_unit, vtophys(sc->le_membase), sc->lemac_membase); return 0; } sc->le_prodname = sc->lemac_prodname; sc->le_mctbl = sc->lemac_mctbl; sc->le_mcmask = (1 << LEMAC_MCTBL_BITS) - 1; sc->lemac_txmax = lemac_deftxmax; *msize = 2048; le_intrvec[sc->le_if.if_unit] = lemac_intr; return LEMAC_IOSPACE; } /* * Do a hard reset of the board; */ static void lemac_reset( IF_RESET_ARGS) { le_softc_t *sc = &le_softc[unit]; int portval, cksum; /* * Initialize board.. */ sc->le_flags &= IFF_UP; sc->le_if.if_flags &= ~IFF_OACTIVE; LEMAC_INTR_DISABLE(sc); LE_OUTB(sc, LEMAC_REG_IOP, LEMAC_IOP_EEINIT); DELAY(LEMAC_EEP_DELAY); /* Disable Interrupts */ /* LE_OUTB(sc, LEMAC_REG_IC, LE_INB(sc, LEMAC_REG_IC) & ICR_IRQ_SEL); */ /* * Read EEPROM information. NOTE - the placement of this function * is important because functions hereafter may rely on information * read from the EEPROM. */ if ((cksum = lemac_read_eeprom(sc)) != LEMAC_EEP_CKSUM) { printf("%s%d: reset: EEPROM checksum failed (0x%x)\n", sc->le_if.if_name, sc->le_if.if_unit, cksum); return; } /* * Force to 2K mode if not already configured. */ portval = LE_INB(sc, LEMAC_REG_MBR); if (!LEMAC_2K_MODE(portval)) { if (LEMAC_64K_MODE(portval)) { portval = (((portval * 2) & 0xF) << 4); sc->lemac_memmode = 64; } else if (LEMAC_32K_MODE(portval)) { portval = ((portval & 0xF) << 4); sc->lemac_memmode = 32; } LE_OUTB(sc, LEMAC_REG_MBR, portval); } sc->lemac_membase = portval * (2 * 1024) + (512 * 1024); /* * Initialize Free Memory Queue, Init mcast table with broadcast. */ lemac_init_adapmem(sc); sc->le_flags |= IFF_UP; return; } static void lemac_init( int unit) { le_softc_t *sc = &le_softc[unit]; int s; if ((sc->le_flags & IFF_UP) == 0) return; s = splimp(); /* * If the interface has the up flag */ if (sc->le_if.if_flags & IFF_UP) { int saved_cs = LE_INB(sc, LEMAC_REG_CS); LE_OUTB(sc, LEMAC_REG_CS, saved_cs | (LEMAC_CS_TXD | LEMAC_CS_RXD)); LE_OUTB(sc, LEMAC_REG_PA0, sc->le_ac.ac_enaddr[0]); LE_OUTB(sc, LEMAC_REG_PA1, sc->le_ac.ac_enaddr[1]); LE_OUTB(sc, LEMAC_REG_PA2, sc->le_ac.ac_enaddr[2]); LE_OUTB(sc, LEMAC_REG_PA3, sc->le_ac.ac_enaddr[3]); LE_OUTB(sc, LEMAC_REG_PA4, sc->le_ac.ac_enaddr[4]); LE_OUTB(sc, LEMAC_REG_PA5, sc->le_ac.ac_enaddr[5]); LE_OUTB(sc, LEMAC_REG_IC, LE_INB(sc, LEMAC_REG_IC) | LEMAC_IC_IE); if (sc->le_if.if_flags & IFF_PROMISC) { LE_OUTB(sc, LEMAC_REG_CS, LEMAC_CS_MCE | LEMAC_CS_PME); } else { LEMAC_INTR_DISABLE(sc); le_multi_filter(sc); LE_OUTB(sc, LEMAC_REG_MPN, 0); if ((sc->le_flags | sc->le_if.if_flags) & IFF_ALLMULTI) { MEMCPY(&sc->le_membase[LEMAC_MCTBL_OFF], lemac_allmulti_mctbl, sizeof(lemac_allmulti_mctbl)); } else { MEMCPY(&sc->le_membase[LEMAC_MCTBL_OFF], sc->lemac_mctbl, sizeof(sc->lemac_mctbl)); } LE_OUTB(sc, LEMAC_REG_CS, LEMAC_CS_MCE); } LE_OUTB(sc, LEMAC_REG_CTL, LE_INB(sc, LEMAC_REG_CTL) ^ LEMAC_CTL_LED); LEMAC_INTR_ENABLE(sc); sc->le_if.if_flags |= IFF_RUNNING; } else { LE_OUTB(sc, LEMAC_REG_CS, LEMAC_CS_RXD|LEMAC_CS_TXD); LEMAC_INTR_DISABLE(sc); sc->le_if.if_flags &= ~IFF_RUNNING; } splx(s); } /* * What to do upon receipt of an interrupt. */ static void lemac_intr( le_softc_t *sc) { int cs_value; LEMAC_INTR_DISABLE(sc); /* Mask interrupts */ /* * Determine cause of interrupt. Receive events take * priority over Transmit. */ cs_value = LE_INB(sc, LEMAC_REG_CS); /* * Check for Receive Queue not being empty. * Check for Transmit Done Queue not being empty. */ if (cs_value & LEMAC_CS_RNE) lemac_rne_intr(sc); if (cs_value & LEMAC_CS_TNE) lemac_tne_intr(sc); /* * Check for Transmitter Disabled. * Check for Receiver Disabled. */ if (cs_value & LEMAC_CS_TXD) lemac_txd_intr(sc, cs_value); if (cs_value & LEMAC_CS_RXD) lemac_rxd_intr(sc, cs_value); /* * Toggle LED and unmask interrupts. */ LE_OUTB(sc, LEMAC_REG_CTL, LE_INB(sc, LEMAC_REG_CTL) ^ LEMAC_CTL_LED); LEMAC_INTR_ENABLE(sc); /* Unmask interrupts */ } static void lemac_rne_intr( le_softc_t *sc) { int rxcount, rxlen, rxpg; u_char *rxptr; lemac_rne_intrs++; rxcount = LE_INB(sc, LEMAC_REG_RQC); while (rxcount--) { rxpg = LE_INB(sc, LEMAC_REG_RQ); LE_OUTB(sc, LEMAC_REG_MPN, rxpg); rxptr = sc->le_membase; sc->le_if.if_ipackets++; if (*rxptr & LEMAC_RX_OK) { /* * Get receive length - subtract out checksum. */ rxlen = ((*(u_int *)rxptr >> 8) & 0x7FF) - 4; le_input(sc, rxptr + sizeof(u_int), rxlen, rxlen, NULL); } else { /* end if (*rxptr & LEMAC_RX_OK) */ sc->le_if.if_ierrors++; } LE_OUTB(sc, LEMAC_REG_FMQ, rxpg); /* Return this page to Free Memory Queue */ } /* end while (recv_count--) */ return; } static void lemac_rxd_intr( le_softc_t *sc, unsigned cs_value) { /* * Handle CS_RXD (Receiver disabled) here. * * Check Free Memory Queue Count. If not equal to zero * then just turn Receiver back on. If it is equal to * zero then check to see if transmitter is disabled. * Process transmit TXD loop once more. If all else * fails then do software init (0xC0 to EEPROM Init) * and rebuild Free Memory Queue. */ lemac_rxd_intrs++; /* * Re-enable Receiver. */ cs_value &= ~LEMAC_CS_RXD; LE_OUTB(sc, LEMAC_REG_CS, cs_value); if (LE_INB(sc, LEMAC_REG_FMC) > 0) return; if (cs_value & LEMAC_CS_TXD) lemac_txd_intr(sc, cs_value); if ((LE_INB(sc, LEMAC_REG_CS) & LEMAC_CS_RXD) == 0) return; printf("%s%d: fatal RXD error, attempting recovery\n", sc->le_if.if_name, sc->le_if.if_unit); LE_RESET(sc); if (sc->le_flags & IFF_UP) { lemac_init(sc->le_if.if_unit); return; } /* * Error during initializion. Mark card as disabled. */ printf("%s%d: recovery failed -- board disabled\n", sc->le_if.if_name, sc->le_if.if_unit); return; } static void lemac_start( struct ifnet *ifp) { le_softc_t *sc = (le_softc_t *) ifp; struct ifqueue *ifq = &ifp->if_snd; if ((ifp->if_flags & IFF_RUNNING) == 0) return; LEMAC_INTR_DISABLE(sc); while (ifq->ifq_head != NULL) { struct mbuf *m; int tx_pg; u_int txhdr, txoff; if (LE_INB(sc, LEMAC_REG_TQC) >= sc->lemac_txmax) { ifp->if_flags |= IFF_OACTIVE; break; } tx_pg = LE_INB(sc, LEMAC_REG_FMQ); /* get free memory page */ /* * Check for good transmit page. */ if (tx_pg == 0 || tx_pg > sc->lemac_lastpage) { lemac_txnospc++; ifp->if_flags |= IFF_OACTIVE; break; } IF_DEQUEUE(ifq, m); LE_OUTB(sc, LEMAC_REG_MPN, tx_pg); /* Shift 2K window. */ /* * The first four bytes of each transmit buffer are for * control information. The first byte is the control * byte, then the length (why not word aligned?), then * the off to the buffer. */ txoff = (mtod(m, u_int) & (sizeof(u_long) - 1)) + LEMAC_TX_HDRSZ; txhdr = sc->lemac_txctl | (m->m_pkthdr.len << 8) | (txoff << 24); *(u_int *) sc->le_membase = txhdr; /* * Copy the packet to the board */ m_copydata(m, 0, m->m_pkthdr.len, sc->le_membase + txoff); LE_OUTB(sc, LEMAC_REG_TQ, tx_pg); /* tell chip to transmit this packet */ if (sc->le_if.if_bpf) bpf_mtap(&sc->le_if, m); m_freem(m); /* free the mbuf */ } LEMAC_INTR_ENABLE(sc); } static void lemac_tne_intr( le_softc_t *sc) { int txsts, txcount = LE_INB(sc, LEMAC_REG_TDC); lemac_tne_intrs++; while (txcount--) { txsts = LE_INB(sc, LEMAC_REG_TDQ); sc->le_if.if_opackets++; /* another one done */ if ((txsts & LEMAC_TDQ_COL) != LEMAC_TDQ_NOCOL) sc->le_if.if_collisions++; } sc->le_if.if_flags &= ~IFF_OACTIVE; lemac_start(&sc->le_if); } static void lemac_txd_intr( le_softc_t *sc, unsigned cs_value) { /* * Read transmit status, remove transmit buffer from * transmit queue and place on free memory queue, * then reset transmitter. * Increment appropriate counters. */ lemac_txd_intrs++; sc->le_if.if_oerrors++; if (LE_INB(sc, LEMAC_REG_TS) & LEMAC_TS_ECL) sc->le_if.if_collisions++; sc->le_if.if_flags &= ~IFF_OACTIVE; LE_OUTB(sc, LEMAC_REG_FMQ, LE_INB(sc, LEMAC_REG_TQ)); /* Get Page number and write it back out */ LE_OUTB(sc, LEMAC_REG_CS, cs_value & ~LEMAC_CS_TXD); /* Turn back on transmitter */ return; } static int lemac_read_eeprom( le_softc_t *sc) { int word_off, cksum; u_char *ep; cksum = 0; ep = sc->lemac_eeprom; for (word_off = 0; word_off < LEMAC_EEP_SIZE / 2; word_off++) { LE_OUTB(sc, LEMAC_REG_PI1, word_off); LE_OUTB(sc, LEMAC_REG_IOP, LEMAC_IOP_EEREAD); DELAY(LEMAC_EEP_DELAY); *ep = LE_INB(sc, LEMAC_REG_EE1); cksum += *ep++; *ep = LE_INB(sc, LEMAC_REG_EE2); cksum += *ep++; } /* * Set up Transmit Control Byte for use later during transmit. */ sc->lemac_txctl |= LEMAC_TX_FLAGS; if ((sc->lemac_eeprom[LEMAC_EEP_SWFLAGS] & LEMAC_EEP_SW_SQE) == 0) sc->lemac_txctl &= ~LEMAC_TX_SQE; if (sc->lemac_eeprom[LEMAC_EEP_SWFLAGS] & LEMAC_EEP_SW_LAB) sc->lemac_txctl |= LEMAC_TX_LAB; MEMCPY(sc->lemac_prodname, &sc->lemac_eeprom[LEMAC_EEP_PRDNM], LEMAC_EEP_PRDNMSZ); sc->lemac_prodname[LEMAC_EEP_PRDNMSZ] = '\0'; return cksum % 256; } static void lemac_init_adapmem( le_softc_t *sc) { int pg, conf; conf = LE_INB(sc, LEMAC_REG_CNF); if ((sc->lemac_eeprom[LEMAC_EEP_SETUP] & LEMAC_EEP_ST_DRAM) == 0) { sc->lemac_lastpage = 63; conf &= ~LEMAC_CNF_DRAM; } else { sc->lemac_lastpage = 127; conf |= LEMAC_CNF_DRAM; } LE_OUTB(sc, LEMAC_REG_CNF, conf); for (pg = 1; pg <= sc->lemac_lastpage; pg++) LE_OUTB(sc, LEMAC_REG_FMQ, pg); return; } #endif /* !defined(LE_NOLEMAC) */ #if !defined(LE_NOLANCE) /* * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * * Start of DEPCA (DE200/DE201/DE202/DE422 etal) support. * */ static void depca_intr(le_softc_t *sc); static int lance_init_adapmem(le_softc_t *sc); static int lance_init_ring(le_softc_t *sc, ln_ring_t *rp, lance_ring_t *ri, unsigned ndescs, unsigned bufoffset, unsigned descoffset); static void lance_init(int unit); static void lance_reset(IF_RESET_ARGS); static void lance_intr(le_softc_t *sc); static int lance_rx_intr(le_softc_t *sc); static void lance_start(struct ifnet *ifp); static int lance_tx_intr(le_softc_t *sc); #define LN_BUFSIZE /* 380 */ 304 /* 1520 / 4 */ #define LN_TXDESC_RATIO 2048 #define LN_DESC_MAX 128 #if LN_DOSTATS static struct { unsigned lance_rx_misses; unsigned lance_rx_badcrc; unsigned lance_rx_badalign; unsigned lance_rx_badframe; unsigned lance_rx_buferror; unsigned lance_tx_deferred; unsigned lance_tx_single_collisions; unsigned lance_tx_multiple_collisions; unsigned lance_tx_excessive_collisions; unsigned lance_tx_late_collisions; unsigned lance_memory_errors; unsigned lance_inits; unsigned lance_tx_intrs; unsigned lance_tx_nospc[2]; unsigned lance_tx_drains[2]; unsigned lance_tx_orphaned; unsigned lance_tx_adoptions; unsigned lance_tx_emptied; unsigned lance_tx_deftxint; unsigned lance_tx_buferror; unsigned lance_high_txoutptr; unsigned lance_low_txheapsize; unsigned lance_low_txfree; unsigned lance_tx_intr_hidescs; /* unsigned lance_tx_intr_descs[LN_DESC_MAX]; */ unsigned lance_rx_intrs; unsigned lance_rx_badsop; unsigned lance_rx_contig; unsigned lance_rx_noncontig; unsigned lance_rx_intr_hidescs; unsigned lance_rx_ndescs[4096 / LN_BUFSIZE]; /* unsigned lance_rx_intr_descs[LN_DESC_MAX]; */ } lance_stats; #define LN_STAT(stat) (lance_stats.lance_ ## stat) #define LN_MINSTAT(stat, val) (LN_STAT(stat > (val)) ? LN_STAT(stat = (val)) : 0) #define LN_MAXSTAT(stat, val) (LN_STAT(stat < (val)) ? LN_STAT(stat = (val)) : 0) #else #define LN_STAT(stat) 0 #define LN_MINSTAT(stat, val) 0 #define LN_MAXSTAT(stat, val) 0 #endif #define LN_SELCSR(sc, csrno) (LE_OUTW(sc, sc->lance_rap, csrno)) #define LN_INQCSR(sc) (LE_INW(sc, sc->lance_rap)) #define LN_WRCSR(sc, val) (LE_OUTW(sc, sc->lance_rdp, val)) #define LN_RDCSR(sc) (LE_INW(sc, sc->lance_rdp)) #define LN_ZERO(sc, vaddr, len) bzero(vaddr, len) #define LN_COPYTO(sc, from, to, len) bcopy(from, to, len) #define LN_SETFLAG(sc, vaddr, val) \ (((volatile u_char *) vaddr)[3] = (val)) #define LN_PUTDESC(sc, desc, vaddr) \ (((volatile u_short *) vaddr)[0] = ((u_short *) desc)[0], \ ((volatile u_short *) vaddr)[2] = ((u_short *) desc)[2], \ ((volatile u_short *) vaddr)[1] = ((u_short *) desc)[1]) /* * Only get the descriptor flags and length/status. All else * read-only. */ #define LN_GETDESC(sc, desc, vaddr) \ (((u_short *) desc)[1] = ((volatile u_short *) vaddr)[1], \ ((u_short *) desc)[3] = ((volatile u_short *) vaddr)[3]) /* * These definitions are specific to the DEC "DEPCA-style" NICs. * (DEPCA, DE10x, DE20[012], DE422) * */ #define DEPCA_REG_NICSR 0 /* (RW;16) NI Control / Status */ #define DEPCA_REG_RDP 4 /* (RW:16) LANCE RDP (data) register */ #define DEPCA_REG_RAP 6 /* (RW:16) LANCE RAP (address) register */ #define DEPCA_REG_ADDRROM 12 /* (R : 8) DEPCA Ethernet Address ROM */ #define DEPCA_IOSPACE 16 /* DEPCAs use 16 bytes of IO space */ #define DEPCA_NICSR_LED 0x0001 /* Light the LED on the back of the DEPCA */ #define DEPCA_NICSR_ENABINTR 0x0002 /* Enable Interrupts */ #define DEPCA_NICSR_MASKINTR 0x0004 /* Mask Interrupts */ #define DEPCA_NICSR_AAC 0x0008 /* Address Counter Clear */ #define DEPCA_NICSR_REMOTEBOOT 0x0010 /* Remote Boot Enabled (ignored) */ #define DEPCA_NICSR_32KRAM 0x0020 /* DEPCA LANCE RAM size 64K (C) / 32K (S) */ #define DEPCA_NICSR_LOW32K 0x0040 /* Bank Select (A15 = !This Bit) */ #define DEPCA_NICSR_SHE 0x0080 /* Shared RAM Enabled (ie hide ROM) */ #define DEPCA_NICSR_BOOTTMO 0x0100 /* Remote Boot Timeout (ignored) */ #define DEPCA_RDNICSR(sc) (LE_INW(sc, DEPCA_REG_NICSR)) #define DEPCA_WRNICSR(sc, val) (LE_OUTW(sc, DEPCA_REG_NICSR, val)) #define DEPCA_IDSTR_OFFSET 0xC006 /* ID String Offset */ #define DEPCA_REG_EISAID 0x80 #define DEPCA_EISAID_MASK 0xf0ffffff #define DEPCA_EISAID_DE422 0x2042A310 typedef enum { DEPCA_CLASSIC, DEPCA_DE100, DEPCA_DE101, DEPCA_EE100, DEPCA_DE200, DEPCA_DE201, DEPCA_DE202, DEPCA_DE422, DEPCA_UNKNOWN } depca_t; static const char *depca_signatures[] = { "DEPCA", "DE100", "DE101", "EE100", "DE200", "DE201", "DE202", "DE422", NULL }; static int depca_probe( le_softc_t *sc, const le_board_t *bd, int *msize) { unsigned nicsr, idx, idstr_offset = DEPCA_IDSTR_OFFSET; /* * Find out how memory we are dealing with. Adjust * the ID string offset approriately if we are at * 32K. Make sure the ROM is enabled. */ nicsr = DEPCA_RDNICSR(sc); nicsr &= ~(DEPCA_NICSR_SHE|DEPCA_NICSR_LED|DEPCA_NICSR_ENABINTR); if (nicsr & DEPCA_NICSR_32KRAM) { /* * Make we are going to read the upper * 32K so we do read the ROM. */ sc->lance_ramsize = 32 * 1024; nicsr &= ~DEPCA_NICSR_LOW32K; sc->lance_ramoffset = 32 * 1024; idstr_offset -= sc->lance_ramsize; } else { sc->lance_ramsize = 64 * 1024; sc->lance_ramoffset = 0; } DEPCA_WRNICSR(sc, nicsr); sc->le_prodname = NULL; for (idx = 0; depca_signatures[idx] != NULL; idx++) { if (bcmp(depca_signatures[idx], sc->le_membase + idstr_offset, 5) == 0) { sc->le_prodname = depca_signatures[idx]; break; } } if (sc->le_prodname == NULL) { /* * Try to get the EISA device if it's a DE422. */ if (sc->le_iobase > 0x1000 && (sc->le_iobase & 0x0F00) == 0x0C00 && (LE_INL(sc, DEPCA_REG_EISAID) & DEPCA_EISAID_MASK) == DEPCA_EISAID_DE422) { sc->le_prodname = "DE422"; } else { return 0; } } if (idx == DEPCA_CLASSIC) sc->lance_ramsize -= 16384; /* Can't use the ROM area on a DEPCA */ /* * Try to read the address ROM. * Stop the LANCE, reset the Address ROM Counter (AAC), * read the NICSR to "clock" in the reset, and then * re-enable the Address ROM Counter. Now read the * address ROM. */ sc->lance_rdp = DEPCA_REG_RDP; sc->lance_rap = DEPCA_REG_RAP; sc->lance_csr3 = LN_CSR3_ALE; sc->le_mctbl = sc->lance_initb.ln_multi_mask; sc->le_mcmask = LN_MC_MASK; LN_SELCSR(sc, LN_CSR0); LN_WRCSR(sc, LN_CSR0_STOP); if (idx < DEPCA_DE200) { DEPCA_WRNICSR(sc, DEPCA_RDNICSR(sc) & ~DEPCA_NICSR_AAC); DEPCA_WRNICSR(sc, DEPCA_RDNICSR(sc) | DEPCA_NICSR_AAC); } if (le_read_macaddr(sc, DEPCA_REG_ADDRROM, idx == DEPCA_CLASSIC) < 0) return 0; MEMCPY(sc->le_ac.ac_enaddr, sc->le_hwaddr, 6); /* * Renable shared RAM. */ DEPCA_WRNICSR(sc, DEPCA_RDNICSR(sc) | DEPCA_NICSR_SHE); le_intrvec[sc->le_if.if_unit] = depca_intr; if (!lance_init_adapmem(sc)) return 0; sc->if_reset = lance_reset; sc->if_init = lance_init; sc->le_if.if_start = lance_start; DEPCA_WRNICSR(sc, DEPCA_NICSR_SHE | DEPCA_NICSR_ENABINTR); LE_RESET(sc); LN_STAT(low_txfree = sc->lance_txinfo.ri_max); LN_STAT(low_txheapsize = 0xFFFFFFFF); *msize = sc->lance_ramsize; return DEPCA_IOSPACE; } static void depca_intr( le_softc_t *sc) { DEPCA_WRNICSR(sc, DEPCA_RDNICSR(sc) ^ DEPCA_NICSR_LED); lance_intr(sc); } /* * Here's as good a place to describe our paritioning of the * LANCE shared RAM space. (NOTE: this driver does not yet support * the concept of a LANCE being able to DMA). * * First is the 24 (00:23) bytes for LANCE Initialization Block * Next are the recieve descriptors. The number is calculated from * how many LN_BUFSIZE buffers we can allocate (this number must * be a power of 2). Next are the transmit descriptors. The amount * of transmit descriptors is derived from the size of the RAM * divided by 1K. Now come the receive buffers (one for each receive * descriptor). Finally is the transmit heap. (no fixed buffers are * allocated so as to make the most use of the limited space). */ static int lance_init_adapmem( le_softc_t *sc) { lance_addr_t rxbufoffset; lance_addr_t rxdescoffset, txdescoffset; unsigned rxdescs, txdescs; /* * First calculate how many descriptors we heap. * Note this assumes the ramsize is a power of two. */ sc->lance_rxbufsize = LN_BUFSIZE; rxdescs = 1; while (rxdescs * sc->lance_rxbufsize < sc->lance_ramsize) rxdescs *= 2; rxdescs /= 2; if (rxdescs > LN_DESC_MAX) { sc->lance_rxbufsize *= rxdescs / LN_DESC_MAX; rxdescs = LN_DESC_MAX; } txdescs = sc->lance_ramsize / LN_TXDESC_RATIO; if (txdescs > LN_DESC_MAX) txdescs = LN_DESC_MAX; /* * Now calculate where everything goes in memory */ rxdescoffset = sizeof(ln_initb_t); txdescoffset = rxdescoffset + sizeof(ln_desc_t) * rxdescs; rxbufoffset = txdescoffset + sizeof(ln_desc_t) * txdescs; sc->le_mctbl = (le_mcbits_t *) sc->lance_initb.ln_multi_mask; /* * Remember these for debugging. */ sc->lance_raminitb = (ln_initb_t *) sc->le_membase; sc->lance_ramdesc = (ln_desc_t *) (sc->le_membase + rxdescoffset); /* * Initialize the rings. */ if (!lance_init_ring(sc, &sc->lance_initb.ln_rxring, &sc->lance_rxinfo, rxdescs, rxbufoffset, rxdescoffset)) return 0; sc->lance_rxinfo.ri_heap = rxbufoffset; sc->lance_rxinfo.ri_heapend = rxbufoffset + sc->lance_rxbufsize * rxdescs; if (!lance_init_ring(sc, &sc->lance_initb.ln_txring, &sc->lance_txinfo, txdescs, 0, txdescoffset)) return 0; sc->lance_txinfo.ri_heap = sc->lance_rxinfo.ri_heapend; sc->lance_txinfo.ri_heapend = sc->lance_ramsize; /* * Set CSR1 and CSR2 to the address of the init block (which * for us is always 0. */ sc->lance_csr1 = LN_ADDR_LO(0 + sc->lance_ramoffset); sc->lance_csr2 = LN_ADDR_HI(0 + sc->lance_ramoffset); return 1; } static int lance_init_ring( le_softc_t *sc, ln_ring_t *rp, lance_ring_t *ri, unsigned ndescs, lance_addr_t bufoffset, lance_addr_t descoffset) { lance_descinfo_t *di; /* * Initialize the ring pointer in the LANCE InitBlock */ rp->r_addr_lo = LN_ADDR_LO(descoffset + sc->lance_ramoffset); rp->r_addr_hi = LN_ADDR_HI(descoffset + sc->lance_ramoffset); rp->r_log2_size = ffs(ndescs) - 1; /* * Allocate the ring entry descriptors and initialize * our ring information data structure. All these are * our copies and do not live in the LANCE RAM. */ ri->ri_first = (lance_descinfo_t *) malloc(ndescs * sizeof(*di), M_DEVBUF, M_NOWAIT); if (ri->ri_first == NULL) { printf("lance_init_ring: malloc(%d) failed\n", ndescs * sizeof(*di)); return 0; } ri->ri_free = ri->ri_max = ndescs; ri->ri_last = ri->ri_first + ri->ri_max; for (di = ri->ri_first; di < ri->ri_last; di++) { di->di_addr = sc->le_membase + descoffset; di->di_mbuf = NULL; if (bufoffset) { di->di_bufaddr = bufoffset; di->di_buflen = sc->lance_rxbufsize; bufoffset += sc->lance_rxbufsize; } descoffset += sizeof(ln_desc_t); } return 1; } static void lance_dumpcsrs( le_softc_t *sc, const char *id) { printf("%s%d: %s: nicsr=%04x", sc->le_if.if_name, sc->le_if.if_unit, id, DEPCA_RDNICSR(sc)); LN_SELCSR(sc, LN_CSR0); printf(" csr0=%04x", LN_RDCSR(sc)); LN_SELCSR(sc, LN_CSR1); printf(" csr1=%04x", LN_RDCSR(sc)); LN_SELCSR(sc, LN_CSR2); printf(" csr2=%04x", LN_RDCSR(sc)); LN_SELCSR(sc, LN_CSR3); printf(" csr3=%04x\n", LN_RDCSR(sc)); LN_SELCSR(sc, LN_CSR0); } static void lance_reset( IF_RESET_ARGS) { le_softc_t *sc = &le_softc[unit]; register int cnt, csr; /* lance_dumpcsrs(sc, "lance_reset: start"); */ LN_WRCSR(sc, LN_RDCSR(sc) & ~LN_CSR0_ENABINTR); LN_WRCSR(sc, LN_CSR0_STOP); DELAY(100); sc->le_flags &= ~IFF_UP; sc->le_if.if_flags &= ~(IFF_UP|IFF_RUNNING); le_multi_filter(sc); /* initialize the multicast table */ if ((sc->le_flags | sc->le_if.if_flags) & IFF_ALLMULTI) { sc->lance_initb.ln_multi_mask[0] = 0xFFFFU; sc->lance_initb.ln_multi_mask[1] = 0xFFFFU; sc->lance_initb.ln_multi_mask[2] = 0xFFFFU; sc->lance_initb.ln_multi_mask[3] = 0xFFFFU; } sc->lance_initb.ln_physaddr[0] = ((u_short *) sc->le_ac.ac_enaddr)[0]; sc->lance_initb.ln_physaddr[1] = ((u_short *) sc->le_ac.ac_enaddr)[1]; sc->lance_initb.ln_physaddr[2] = ((u_short *) sc->le_ac.ac_enaddr)[2]; if (sc->le_if.if_flags & IFF_PROMISC) { sc->lance_initb.ln_mode |= LN_MODE_PROMISC; } else { sc->lance_initb.ln_mode &= ~LN_MODE_PROMISC; } /* * We force the init block to be at the start * of the LANCE's RAM buffer. */ LN_COPYTO(sc, &sc->lance_initb, sc->le_membase, sizeof(sc->lance_initb)); LN_SELCSR(sc, LN_CSR1); LN_WRCSR(sc, sc->lance_csr1); LN_SELCSR(sc, LN_CSR2); LN_WRCSR(sc, sc->lance_csr2); LN_SELCSR(sc, LN_CSR3); LN_WRCSR(sc, sc->lance_csr3); /* lance_dumpcsrs(sc, "lance_reset: preinit"); */ /* * clear INITDONE and INIT the chip */ LN_SELCSR(sc, LN_CSR0); LN_WRCSR(sc, LN_CSR0_INIT|LN_CSR0_INITDONE); csr = 0; cnt = 100; while (cnt-- > 0) { if (((csr = LN_RDCSR(sc)) & LN_CSR0_INITDONE) != 0) break; DELAY(10000); } if ((csr & LN_CSR0_INITDONE) == 0) { /* make sure we got out okay */ lance_dumpcsrs(sc, "lance_reset: reset failure"); } else { /* lance_dumpcsrs(sc, "lance_reset: end"); */ sc->le_if.if_flags |= IFF_UP; sc->le_flags |= IFF_UP; } } static void lance_init( int unit) { le_softc_t *sc = &le_softc[unit]; lance_ring_t *ri; lance_descinfo_t *di; ln_desc_t desc; LN_STAT(inits++); if (sc->le_if.if_flags & IFF_RUNNING) { LE_RESET(sc); lance_tx_intr(sc); /* * If we were running, requeue any pending transmits. */ ri = &sc->lance_txinfo; di = ri->ri_nextout; while (ri->ri_free < ri->ri_max) { if (--di == ri->ri_first) di = ri->ri_nextout - 1; if (di->di_mbuf == NULL) break; IF_PREPEND(&sc->le_if.if_snd, di->di_mbuf); di->di_mbuf = NULL; ri->ri_free++; } } else { LE_RESET(sc); } /* * Reset the transmit ring. Make sure we own all the buffers. * Also reset the transmit heap. */ sc->le_if.if_flags &= ~IFF_OACTIVE; ri = &sc->lance_txinfo; for (di = ri->ri_first; di < ri->ri_last; di++) { if (di->di_mbuf != NULL) { m_freem(di->di_mbuf); di->di_mbuf = NULL; } desc.d_flag = 0; desc.d_addr_lo = LN_ADDR_LO(ri->ri_heap + sc->lance_ramoffset); desc.d_addr_hi = LN_ADDR_HI(ri->ri_heap + sc->lance_ramoffset); desc.d_buflen = 0; LN_PUTDESC(sc, &desc, di->di_addr); } ri->ri_nextin = ri->ri_nextout = ri->ri_first; ri->ri_free = ri->ri_max; ri->ri_outptr = ri->ri_heap; ri->ri_outsize = ri->ri_heapend - ri->ri_heap; ri = &sc->lance_rxinfo; desc.d_flag = LN_DFLAG_OWNER; desc.d_buflen = 0 - sc->lance_rxbufsize; for (di = ri->ri_first; di < ri->ri_last; di++) { desc.d_addr_lo = LN_ADDR_LO(di->di_bufaddr + sc->lance_ramoffset); desc.d_addr_hi = LN_ADDR_HI(di->di_bufaddr + sc->lance_ramoffset); LN_PUTDESC(sc, &desc, di->di_addr); } ri->ri_nextin = ri->ri_nextout = ri->ri_first; ri->ri_outptr = ri->ri_heap; ri->ri_outsize = ri->ri_heapend - ri->ri_heap; ri->ri_free = 0; if (sc->le_if.if_flags & IFF_UP) { sc->le_if.if_flags |= IFF_RUNNING; LN_WRCSR(sc, LN_CSR0_START|LN_CSR0_INITDONE|LN_CSR0_ENABINTR); /* lance_dumpcsrs(sc, "lance_init: up"); */ lance_start(&sc->le_if); } else { /* lance_dumpcsrs(sc, "lance_init: down"); */ sc->le_if.if_flags &= ~IFF_RUNNING; } } static void lance_intr( le_softc_t *sc) { unsigned oldcsr; oldcsr = LN_RDCSR(sc); oldcsr &= ~LN_CSR0_ENABINTR; LN_WRCSR(sc, oldcsr); LN_WRCSR(sc, LN_CSR0_ENABINTR); if (oldcsr & LN_CSR0_ERRSUM) { if (oldcsr & LN_CSR0_MISS) { /* * LN_CSR0_MISS is signaled when the LANCE receiver * loses a packet because it doesn't own a receive * descriptor. Rev. D LANCE chips, which are no * longer used, require a chip reset as described * below. */ LN_STAT(rx_misses++); } if (oldcsr & LN_CSR0_MEMERROR) { LN_STAT(memory_errors++); if (oldcsr & (LN_CSR0_RXON|LN_CSR0_TXON)) { lance_init(sc->le_if.if_unit); return; } } } if ((oldcsr & LN_CSR0_RXINT) && lance_rx_intr(sc)) { lance_init(sc->le_if.if_unit); return; } if (oldcsr & LN_CSR0_TXINT) { if (lance_tx_intr(sc)) lance_start(&sc->le_if); } if (oldcsr == (LN_CSR0_PENDINTR|LN_CSR0_RXON|LN_CSR0_TXON)) printf("%s%d: lance_intr: stray interrupt\n", sc->le_if.if_name, sc->le_if.if_unit); } static int lance_rx_intr( le_softc_t *sc) { lance_ring_t *ri = &sc->lance_rxinfo; lance_descinfo_t *eop; ln_desc_t desc; int ndescs, total_len, rxdescs; LN_STAT(rx_intrs++); for (rxdescs = 0;;) { /* * Now to try to find the end of this packet chain. */ for (ndescs = 1, eop = ri->ri_nextin;; ndescs++) { /* * If we don't own this descriptor, the packet ain't * all here so return because we are done. */ LN_GETDESC(sc, &desc, eop->di_addr); if (desc.d_flag & LN_DFLAG_OWNER) return 0; /* * In case we have missed a packet and gotten the * LANCE confused, make sure we are pointing at the * start of a packet. If we aren't, something is really * strange so reinit the LANCE. */ if (desc.d_flag & LN_DFLAG_RxBUFERROR) { LN_STAT(rx_buferror++); return 1; } if ((desc.d_flag & LN_DFLAG_SOP) && eop != ri->ri_nextin) { LN_STAT(rx_badsop++); return 1; } if (desc.d_flag & LN_DFLAG_EOP) break; if (++eop == ri->ri_last) eop = ri->ri_first; } total_len = (desc.d_status & LN_DSTS_RxLENMASK) - 4; if ((desc.d_flag & LN_DFLAG_RxERRSUM) == 0) { /* * Valid Packet -- If the SOP is less than or equal to the EOP * or the length is less than the receive buffer size, then the * packet is contiguous in memory and can be copied in one shot. * Otherwise we need to copy two segments to get the entire * packet. */ if (ri->ri_nextin <= eop || total_len <= ri->ri_heapend - ri->ri_nextin->di_bufaddr) { le_input(sc, sc->le_membase + ri->ri_nextin->di_bufaddr, total_len, total_len, NULL); LN_STAT(rx_contig++); } else { le_input(sc, sc->le_membase + ri->ri_nextin->di_bufaddr, total_len, ri->ri_heapend - ri->ri_nextin->di_bufaddr, sc->le_membase + ri->ri_first->di_bufaddr); LN_STAT(rx_noncontig++); } } else { /* * If the packet is bad, increment the * counters. */ sc->le_if.if_ierrors++; if (desc.d_flag & LN_DFLAG_RxBADCRC) LN_STAT(rx_badcrc++); if (desc.d_flag & LN_DFLAG_RxOVERFLOW) LN_STAT(rx_badalign++); if (desc.d_flag & LN_DFLAG_RxFRAMING) LN_STAT(rx_badframe++); } sc->le_if.if_ipackets++; LN_STAT(rx_ndescs[ndescs-1]++); rxdescs += ndescs; while (ndescs-- > 0) { LN_SETFLAG(sc, ri->ri_nextin->di_addr, LN_DFLAG_OWNER); if (++ri->ri_nextin == ri->ri_last) ri->ri_nextin = ri->ri_first; } } /* LN_STAT(rx_intr_descs[rxdescs]++); */ LN_MAXSTAT(rx_intr_hidescs, rxdescs); return 0; } static void lance_start( struct ifnet *ifp) { le_softc_t *sc = (le_softc_t *) ifp; struct ifqueue *ifq = &ifp->if_snd; lance_ring_t *ri = &sc->lance_txinfo; lance_descinfo_t *di; ln_desc_t desc; unsigned len, slop; struct mbuf *m, *m0; caddr_t bp; if ((ifp->if_flags & IFF_RUNNING) == 0) return; for (;;) { IF_DEQUEUE(ifq, m); if (m == NULL) break; /* * Make the packet meets the minimum size for Ethernet. * The slop is so that we also use an even number of longwards. */ len = ETHERMIN + sizeof(struct ether_header); if (m->m_pkthdr.len > len) len = m->m_pkthdr.len; slop = (8 - len) & 3; /* * If there are no free ring entries (there must be always * one owned by the host), or there's not enough space for * this packet, or this packet would wrap around the end * of LANCE RAM then wait for the transmits to empty for * space and ring entries to become available. */ if (ri->ri_free == 1 || len + slop > ri->ri_outsize) { /* * Try to see if we can free up anything off the transit ring. */ if (lance_tx_intr(sc) > 0) { LN_STAT(tx_drains[0]++); IF_PREPEND(ifq, m); continue; } LN_STAT(tx_nospc[0]++); break; } if (len + slop > ri->ri_heapend - ri->ri_outptr) { /* * Since the packet won't fit in the end of the transmit * heap, see if there is space at the beginning of the transmit * heap. If not, try again when there is space. */ LN_STAT(tx_orphaned++); slop += ri->ri_heapend - ri->ri_outptr; if (len + slop > ri->ri_outsize) { LN_STAT(tx_nospc[1]++); break; } /* * Point to the beginning of the heap */ ri->ri_outptr = ri->ri_heap; LN_STAT(tx_adoptions++); } /* * Initialize the descriptor (saving the buffer address, * buffer length, and mbuf) and write the packet out * to the board. */ di = ri->ri_nextout; di->di_bufaddr = ri->ri_outptr; di->di_buflen = len + slop; di->di_mbuf = m; bp = sc->le_membase + di->di_bufaddr; for (m0 = m; m0 != NULL; m0 = m0->m_next) { LN_COPYTO(sc, mtod(m0, caddr_t), bp, m0->m_len); bp += m0->m_len; } /* * Zero out the remainder if needed (< ETHERMIN). */ if (m->m_pkthdr.len < len) LN_ZERO(sc, bp, len - m->m_pkthdr.len); /* * Finally, copy out the descriptor and tell the * LANCE to transmit!. */ desc.d_buflen = 0 - len; desc.d_addr_lo = LN_ADDR_LO(di->di_bufaddr + sc->lance_ramoffset); desc.d_addr_hi = LN_ADDR_HI(di->di_bufaddr + sc->lance_ramoffset); desc.d_flag = LN_DFLAG_SOP|LN_DFLAG_EOP|LN_DFLAG_OWNER; LN_PUTDESC(sc, &desc, di->di_addr); LN_WRCSR(sc, LN_CSR0_TXDEMAND|LN_CSR0_ENABINTR); /* * Do our bookkeeping with our transmit heap. * (if we wrap, point back to the beginning). */ ri->ri_outptr += di->di_buflen; ri->ri_outsize -= di->di_buflen; LN_MAXSTAT(high_txoutptr, ri->ri_outptr); LN_MINSTAT(low_txheapsize, ri->ri_outsize); if (ri->ri_outptr == ri->ri_heapend) ri->ri_outptr = ri->ri_heap; ri->ri_free--; if (++ri->ri_nextout == ri->ri_last) ri->ri_nextout = ri->ri_first; LN_MINSTAT(low_txfree, ri->ri_free); } if (m != NULL) { ifp->if_flags |= IFF_OACTIVE; IF_PREPEND(ifq, m); } } static int lance_tx_intr( le_softc_t *sc) { lance_ring_t *ri = &sc->lance_txinfo; unsigned xmits; LN_STAT(tx_intrs++); for (xmits = 0; ri->ri_free < ri->ri_max; ) { ln_desc_t desc; LN_GETDESC(sc, &desc, ri->ri_nextin->di_addr); if (desc.d_flag & LN_DFLAG_OWNER) break; if (desc.d_flag & (LN_DFLAG_TxONECOLL|LN_DFLAG_TxMULTCOLL)) sc->le_if.if_collisions++; if (desc.d_flag & LN_DFLAG_TxDEFERRED) LN_STAT(tx_deferred++); if (desc.d_flag & LN_DFLAG_TxONECOLL) LN_STAT(tx_single_collisions++); if (desc.d_flag & LN_DFLAG_TxMULTCOLL) LN_STAT(tx_multiple_collisions++); if (desc.d_flag & LN_DFLAG_TxERRSUM) { if (desc.d_status & (LN_DSTS_TxUNDERFLOW|LN_DSTS_TxBUFERROR| LN_DSTS_TxEXCCOLL|LN_DSTS_TxLATECOLL)) { if (desc.d_status & LN_DSTS_TxEXCCOLL) { unsigned tdr; LN_STAT(tx_excessive_collisions++); if ((tdr = (desc.d_status & LN_DSTS_TxTDRMASK)) > 0) { tdr *= 100; printf("%s%d: lance: warning: excessive collisions: TDR %dns (%d-%dm)\n", sc->le_if.if_name, sc->le_if.if_unit, tdr, (tdr*99)/1000, (tdr*117)/1000); } } if (desc.d_status & LN_DSTS_TxBUFERROR) LN_STAT(tx_buferror++); sc->le_if.if_oerrors++; if ((desc.d_status & LN_DSTS_TxLATECOLL) == 0) { lance_init(sc->le_if.if_unit); return 0; } else { LN_STAT(tx_late_collisions++); } } } m_freem(ri->ri_nextin->di_mbuf); ri->ri_nextin->di_mbuf = NULL; sc->le_if.if_opackets++; ri->ri_free++; ri->ri_outsize += ri->ri_nextin->di_buflen; if (++ri->ri_nextin == ri->ri_last) ri->ri_nextin = ri->ri_first; sc->le_if.if_flags &= ~IFF_OACTIVE; xmits++; } if (ri->ri_free == ri->ri_max) LN_STAT(tx_emptied++); /* LN_STAT(tx_intr_descs[xmits]++); */ LN_MAXSTAT(tx_intr_hidescs, xmits); return xmits; } #endif /* !defined(LE_NOLANCE) */ -#endif /* NLE > 0 */ diff --git a/sys/i386/isa/if_lnc.c b/sys/i386/isa/if_lnc.c index 1c14f8e4084b..9acef11066c0 100644 --- a/sys/i386/isa/if_lnc.c +++ b/sys/i386/isa/if_lnc.c @@ -1,2025 +1,2022 @@ /*- * Copyright (c) 1994-1998 * Paul Richards. All rights reserved. * * 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 * notice, this list of conditions and the following disclaimer, * verbatim and that no modifications are made prior to this * point in the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, 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 Paul Richards. * 4. The name Paul Richards may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY PAUL RICHARDS ``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 PAUL RICHARDS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* #define DIAGNOSTIC #define DEBUG * * TODO ---- * * This driver will need bounce buffer support when dma'ing to mbufs above the * 16Mb mark. * * Check all the XXX comments -- some of them are just things I've left * unfinished rather than "difficult" problems that were hacked around. * * Check log settings. * * Check how all the arpcom flags get set and used. * * Re-inline and re-static all routines after debugging. * * Remember to assign iobase in SHMEM probe routines. * * Replace all occurences of LANCE-controller-card etc in prints by the name * strings of the appropriate type -- nifty window dressing * * Add DEPCA support -- mostly done. * */ #include "pci.h" #include "lnc.h" -#if NLNC > 0 #include "opt_inet.h" /* Some defines that should really be in generic locations */ #define FCS_LEN 4 #define MULTICAST_FILTER_LEN 8 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "opt_bdg.h" #ifdef BRIDGE #include #endif #ifdef PC98 #include #endif #include #include #include struct lnc_softc { struct arpcom arpcom; /* see ../../net/if_arp.h */ struct nic_info nic; /* NIC specific info */ int nrdre; struct host_ring_entry *recv_ring; /* start of alloc'd mem */ int recv_next; int ntdre; struct host_ring_entry *trans_ring; int trans_next; struct init_block *init_block; /* Initialisation block */ int pending_transmits; /* No. of transmit descriptors in use */ int next_to_send; struct mbuf *mbufs; int mbuf_count; int flags; int rap; int rdp; int bdp; #ifdef DEBUG int lnc_debug; #endif LNCSTATS_STRUCT }; static struct lnc_softc lnc_softc[NLNC]; static char const * const nic_ident[] = { "Unknown", "BICC", "NE2100", "DEPCA", "CNET98S", /* PC-98 */ }; static char const * const ic_ident[] = { "Unknown", "LANCE", "C-LANCE", "PCnet-ISA", "PCnet-ISA+", "PCnet-ISA II", "PCnet-32 VL-Bus", "PCnet-PCI", "PCnet-PCI II", "PCnet-FAST", "PCnet-FAST+", "PCnet-Home", }; static void lnc_setladrf __P((struct lnc_softc *sc)); static void lnc_stop __P((struct lnc_softc *sc)); static void lnc_reset __P((struct lnc_softc *sc)); static void lnc_free_mbufs __P((struct lnc_softc *sc)); static __inline int alloc_mbuf_cluster __P((struct lnc_softc *sc, struct host_ring_entry *desc)); static __inline struct mbuf *chain_mbufs __P((struct lnc_softc *sc, int start_of_packet, int pkt_len)); static __inline struct mbuf *mbuf_packet __P((struct lnc_softc *sc, int start_of_packet, int pkt_len)); static __inline void lnc_rint __P((struct lnc_softc *sc)); static __inline void lnc_tint __P((struct lnc_softc *sc)); static int lnc_probe __P((struct isa_device *isa_dev)); #ifdef PC98 static int cnet98s_probe __P((struct lnc_softc *sc, unsigned iobase)); #endif static int ne2100_probe __P((struct lnc_softc *sc, unsigned iobase)); static int bicc_probe __P((struct lnc_softc *sc, unsigned iobase)); static int dec_macaddr_extract __P((u_char ring[], struct lnc_softc *sc)); static int depca_probe __P((struct lnc_softc *sc, unsigned iobase)); static int lance_probe __P((struct lnc_softc *sc)); static int pcnet_probe __P((struct lnc_softc *sc)); static int lnc_attach_sc __P((struct lnc_softc *sc, int unit)); static int lnc_attach __P((struct isa_device *isa_dev)); static void lnc_init __P((void *)); static ointhand2_t lncintr; static __inline int mbuf_to_buffer __P((struct mbuf *m, char *buffer)); static __inline struct mbuf *chain_to_cluster __P((struct mbuf *m)); static void lnc_start __P((struct ifnet *ifp)); static int lnc_ioctl __P((struct ifnet *ifp, u_long command, caddr_t data)); static void lnc_watchdog __P((struct ifnet *ifp)); #ifdef DEBUG void lnc_dump_state __P((struct lnc_softc *sc)); void mbuf_dump_chain __P((struct mbuf *m)); #endif #if NPCI > 0 void *lnc_attach_ne2100_pci __P((int unit, unsigned iobase)); #endif void lncintr_sc __P((struct lnc_softc *sc)); struct isa_driver lncdriver = {lnc_probe, lnc_attach, "lnc"}; static __inline void write_csr(struct lnc_softc *sc, u_short port, u_short val) { outw(sc->rap, port); outw(sc->rdp, val); } static __inline u_short read_csr(struct lnc_softc *sc, u_short port) { outw(sc->rap, port); return (inw(sc->rdp)); } static __inline void write_bcr(struct lnc_softc *sc, u_short port, u_short val) { outw(sc->rap, port); outw(sc->bdp, val); } static __inline u_short read_bcr(struct lnc_softc *sc, u_short port) { outw(sc->rap, port); return (inw(sc->bdp)); } static __inline u_long ether_crc(const u_char *ether_addr) { #define POLYNOMIAL 0xEDB88320UL u_char i, j, addr; u_int crc = 0xFFFFFFFFUL; for (i = 0; i < ETHER_ADDR_LEN; i++) { addr = *ether_addr++; for (j = 0; j < MULTICAST_FILTER_LEN; j++) { crc = (crc >> 1) ^ (((crc ^ addr) & 1) ? POLYNOMIAL : 0); addr >>= 1; } } return crc; #undef POLYNOMIAL } /* * Set up the logical address filter for multicast packets */ static __inline void lnc_setladrf(struct lnc_softc *sc) { struct ifnet *ifp = &sc->arpcom.ac_if; struct ifmultiaddr *ifma; u_long index; int i; if (sc->flags & IFF_ALLMULTI) { for (i=0; i < MULTICAST_FILTER_LEN; i++) sc->init_block->ladrf[i] = 0xFF; return; } /* * For each multicast address, calculate a crc for that address and * then use the high order 6 bits of the crc as a hash code where * bits 3-5 select the byte of the address filter and bits 0-2 select * the bit within that byte. */ bzero(sc->init_block->ladrf, MULTICAST_FILTER_LEN); for (ifma = ifp->if_multiaddrs.lh_first; ifma; ifma = ifma->ifma_link.le_next) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; index = ether_crc(LLADDR((struct sockaddr_dl *)ifma->ifma_addr)) >> 26; sc->init_block->ladrf[index >> 3] |= 1 << (index & 7); } } static void lnc_stop(struct lnc_softc *sc) { write_csr(sc, CSR0, STOP); } static void lnc_reset(struct lnc_softc *sc) { lnc_init(sc); } static void lnc_free_mbufs(struct lnc_softc *sc) { int i; /* * We rely on other routines to keep the buff.mbuf field valid. If * it's not NULL then we assume it points to an allocated mbuf. */ for (i = 0; i < NDESC(sc->nrdre); i++) if ((sc->recv_ring + i)->buff.mbuf) m_free((sc->recv_ring + i)->buff.mbuf); for (i = 0; i < NDESC(sc->ntdre); i++) if ((sc->trans_ring + i)->buff.mbuf) m_free((sc->trans_ring + i)->buff.mbuf); if (sc->mbuf_count) m_freem(sc->mbufs); } static __inline int alloc_mbuf_cluster(struct lnc_softc *sc, struct host_ring_entry *desc) { register struct mds *md = desc->md; struct mbuf *m=0; int addr; /* Try and get cluster off local cache */ if (sc->mbuf_count) { sc->mbuf_count--; m = sc->mbufs; sc->mbufs = m->m_next; /* XXX m->m_data = m->m_ext.ext_buf;*/ } else { MGET(m, M_DONTWAIT, MT_DATA); if (!m) return(1); MCLGET(m, M_DONTWAIT); if (!m->m_ext.ext_buf) { m_free(m); return(1); } } desc->buff.mbuf = m; addr = kvtop(m->m_data); md->md0 = addr; md->md1= ((addr >> 16) & 0xff) | OWN; md->md2 = -(short)(MCLBYTES - sizeof(struct pkthdr)); md->md3 = 0; return(0); } static __inline struct mbuf * chain_mbufs(struct lnc_softc *sc, int start_of_packet, int pkt_len) { struct mbuf *head, *m; struct host_ring_entry *desc; /* * Turn head into a pkthdr mbuf -- * assumes a pkthdr type mbuf was * allocated to the descriptor * originally. */ desc = sc->recv_ring + start_of_packet; head = desc->buff.mbuf; head->m_flags |= M_PKTHDR; m = head; do { m = desc->buff.mbuf; m->m_len = min((MCLBYTES - sizeof(struct pkthdr)), pkt_len); pkt_len -= m->m_len; if (alloc_mbuf_cluster(sc, desc)) return((struct mbuf *)NULL); INC_MD_PTR(start_of_packet, sc->nrdre) desc = sc->recv_ring + start_of_packet; m->m_next = desc->buff.mbuf; } while (start_of_packet != sc->recv_next); m->m_next = 0; return(head); } static __inline struct mbuf * mbuf_packet(struct lnc_softc *sc, int start_of_packet, int pkt_len) { struct host_ring_entry *start; struct mbuf *head,*m,*m_prev; char *data,*mbuf_data; short blen; int amount; /* Get a pkthdr mbuf for the start of packet */ MGETHDR(head, M_DONTWAIT, MT_DATA); if (!head) { LNCSTATS(drop_packet) return(0); } m = head; m->m_len = 0; start = sc->recv_ring + start_of_packet; /*blen = -(start->md->md2);*/ blen = RECVBUFSIZE; /* XXX More PCnet-32 crap */ data = start->buff.data; mbuf_data = m->m_data; while (start_of_packet != sc->recv_next) { /* * If the data left fits in a single buffer then set * blen to the size of the data left. */ if (pkt_len < blen) blen = pkt_len; /* * amount is least of data in current ring buffer and * amount of space left in current mbuf. */ amount = min(blen, M_TRAILINGSPACE(m)); if (amount == 0) { /* mbuf must be empty */ m_prev = m; MGET(m, M_DONTWAIT, MT_DATA); if (!m) { m_freem(head); return(0); } if (pkt_len >= MINCLSIZE) MCLGET(m, M_DONTWAIT); m->m_len = 0; m_prev->m_next = m; amount = min(blen, M_TRAILINGSPACE(m)); mbuf_data = m->m_data; } bcopy(data, mbuf_data, amount); blen -= amount; pkt_len -= amount; m->m_len += amount; data += amount; mbuf_data += amount; if (blen == 0) { start->md->md1 &= HADR; start->md->md1 |= OWN; start->md->md2 = -RECVBUFSIZE; /* XXX - shouldn't be necessary */ INC_MD_PTR(start_of_packet, sc->nrdre) start = sc->recv_ring + start_of_packet; data = start->buff.data; /*blen = -(start->md->md2);*/ blen = RECVBUFSIZE; /* XXX More PCnet-32 crap */ } } return(head); } static __inline void lnc_rint(struct lnc_softc *sc) { struct host_ring_entry *next, *start; int start_of_packet; struct mbuf *head; struct ether_header *eh; int lookahead; int flags; int pkt_len; /* * The LANCE will issue a RINT interrupt when the ownership of the * last buffer of a receive packet has been relinquished by the LANCE. * Therefore, it can be assumed that a complete packet can be found * before hitting buffers that are still owned by the LANCE, if not * then there is a bug in the driver that is causing the descriptors * to get out of sync. */ #ifdef DIAGNOSTIC if ((sc->recv_ring + sc->recv_next)->md->md1 & OWN) { int unit = sc->arpcom.ac_if.if_unit; log(LOG_ERR, "lnc%d: Receive interrupt with buffer still owned by controller -- Resetting\n", unit); lnc_reset(sc); return; } if (!((sc->recv_ring + sc->recv_next)->md->md1 & STP)) { int unit = sc->arpcom.ac_if.if_unit; log(LOG_ERR, "lnc%d: Receive interrupt but not start of packet -- Resetting\n", unit); lnc_reset(sc); return; } #endif lookahead = 0; next = sc->recv_ring + sc->recv_next; while ((flags = next->md->md1) & STP) { /* Make a note of the start of the packet */ start_of_packet = sc->recv_next; /* * Find the end of the packet. Even if not data chaining, * jabber packets can overrun into a second descriptor. * If there is no error, then the ENP flag is set in the last * descriptor of the packet. If there is an error then the ERR * flag will be set in the descriptor where the error occured. * Therefore, to find the last buffer of a packet we search for * either ERR or ENP. */ if (!(flags & (ENP | MDERR))) { do { INC_MD_PTR(sc->recv_next, sc->nrdre) next = sc->recv_ring + sc->recv_next; flags = next->md->md1; } while (!(flags & (STP | OWN | ENP | MDERR))); if (flags & STP) { int unit = sc->arpcom.ac_if.if_unit; log(LOG_ERR, "lnc%d: Start of packet found before end of previous in receive ring -- Resetting\n", unit); lnc_reset(sc); return; } if (flags & OWN) { if (lookahead) { /* * Looked ahead into a packet still * being received */ sc->recv_next = start_of_packet; break; } else { int unit = sc->arpcom.ac_if.if_unit; log(LOG_ERR, "lnc%d: End of received packet not found-- Resetting\n", unit); lnc_reset(sc); return; } } } pkt_len = (next->md->md3 & MCNT) - FCS_LEN; /* Move pointer onto start of next packet */ INC_MD_PTR(sc->recv_next, sc->nrdre) next = sc->recv_ring + sc->recv_next; if (flags & MDERR) { int unit = sc->arpcom.ac_if.if_unit; if (flags & RBUFF) { LNCSTATS(rbuff) log(LOG_ERR, "lnc%d: Receive buffer error\n", unit); } if (flags & OFLO) { /* OFLO only valid if ENP is not set */ if (!(flags & ENP)) { LNCSTATS(oflo) log(LOG_ERR, "lnc%d: Receive overflow error \n", unit); } } else if (flags & ENP) { if ((sc->arpcom.ac_if.if_flags & IFF_PROMISC)==0) { /* * FRAM and CRC are valid only if ENP * is set and OFLO is not. */ if (flags & FRAM) { LNCSTATS(fram) log(LOG_ERR, "lnc%d: Framing error\n", unit); /* * FRAM is only set if there's a CRC * error so avoid multiple messages */ } else if (flags & CRC) { LNCSTATS(crc) log(LOG_ERR, "lnc%d: Receive CRC error\n", unit); } } } /* Drop packet */ LNCSTATS(rerr) sc->arpcom.ac_if.if_ierrors++; while (start_of_packet != sc->recv_next) { start = sc->recv_ring + start_of_packet; start->md->md2 = -RECVBUFSIZE; /* XXX - shouldn't be necessary */ start->md->md1 &= HADR; start->md->md1 |= OWN; INC_MD_PTR(start_of_packet, sc->nrdre) } } else { /* Valid packet */ sc->arpcom.ac_if.if_ipackets++; if (sc->nic.mem_mode == DMA_MBUF) head = chain_mbufs(sc, start_of_packet, pkt_len); else head = mbuf_packet(sc, start_of_packet, pkt_len); if (head) { /* * First mbuf in packet holds the * ethernet and packet headers */ head->m_pkthdr.rcvif = &sc->arpcom.ac_if; head->m_pkthdr.len = pkt_len ; /* * BPF expects the ether header to be in the first * mbuf of the chain so point eh at the right place * but don't increment the mbuf pointers before * the bpf tap. */ eh = (struct ether_header *) head->m_data; if (sc->arpcom.ac_if.if_bpf) bpf_mtap(&sc->arpcom.ac_if, head); #ifdef BRIDGE if (do_bridge) { struct ifnet *bdg_ifp ; bdg_ifp = bridge_in(head); if (bdg_ifp == BDG_DROP) m_freem(head); else { if (bdg_ifp != BDG_LOCAL) bdg_forward(&head, bdg_ifp); if ( bdg_ifp == BDG_LOCAL || bdg_ifp == BDG_BCAST || bdg_ifp == BDG_MCAST ) goto getit; else if (head) m_freem(head); } } else #endif /* Check this packet is really for us */ if ((sc->arpcom.ac_if.if_flags & IFF_PROMISC) && !(eh->ether_dhost[0] & 1) && /* Broadcast and multicast */ (bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr, sizeof(eh->ether_dhost)))) m_freem(head); else { #ifdef BRIDGE getit: #endif /* Skip over the ether header */ head->m_data += sizeof *eh; head->m_len -= sizeof *eh; head->m_pkthdr.len -= sizeof *eh; ether_input(&sc->arpcom.ac_if, eh, head); } } else { int unit = sc->arpcom.ac_if.if_unit; log(LOG_ERR,"lnc%d: Packet dropped, no mbufs\n",unit); LNCSTATS(drop_packet) } } lookahead++; } /* * At this point all completely received packets have been processed * so clear RINT since any packets that have arrived while we were in * here have been dealt with. */ outw(sc->rdp, RINT | INEA); } static __inline void lnc_tint(struct lnc_softc *sc) { struct host_ring_entry *next, *start; int start_of_packet; int lookahead; /* * If the driver is reset in this routine then we return immediately to * the interrupt driver routine. Any interrupts that have occured * since the reset will be dealt with there. sc->trans_next * should point to the start of the first packet that was awaiting * transmission after the last transmit interrupt was dealt with. The * LANCE should have relinquished ownership of that descriptor before * the interrupt. Therefore, sc->trans_next should point to a * descriptor with STP set and OWN cleared. If not then the driver's * pointers are out of sync with the LANCE, which signifies a bug in * the driver. Therefore, the following two checks are really * diagnostic, since if the driver is working correctly they should * never happen. */ #ifdef DIAGNOSTIC if ((sc->trans_ring + sc->trans_next)->md->md1 & OWN) { int unit = sc->arpcom.ac_if.if_unit; log(LOG_ERR, "lnc%d: Transmit interrupt with buffer still owned by controller -- Resetting\n", unit); lnc_reset(sc); return; } #endif /* * The LANCE will write the status information for the packet it just * tried to transmit in one of two places. If the packet was * transmitted successfully then the status will be written into the * last descriptor of the packet. If the transmit failed then the * status will be written into the descriptor that was being accessed * when the error occured and all subsequent descriptors in that * packet will have been relinquished by the LANCE. * * At this point we know that sc->trans_next points to the start * of a packet that the LANCE has just finished trying to transmit. * We now search for a buffer with either ENP or ERR set. */ lookahead = 0; do { start_of_packet = sc->trans_next; next = sc->trans_ring + sc->trans_next; #ifdef DIAGNOSTIC if (!(next->md->md1 & STP)) { int unit = sc->arpcom.ac_if.if_unit; log(LOG_ERR, "lnc%d: Transmit interrupt but not start of packet -- Resetting\n", unit); lnc_reset(sc); return; } #endif /* * Find end of packet. */ if (!(next->md->md1 & (ENP | MDERR))) { do { INC_MD_PTR(sc->trans_next, sc->ntdre) next = sc->trans_ring + sc->trans_next; } while (!(next->md->md1 & (STP | OWN | ENP | MDERR))); if (next->md->md1 & STP) { int unit = sc->arpcom.ac_if.if_unit; log(LOG_ERR, "lnc%d: Start of packet found before end of previous in transmit ring -- Resetting\n", unit); lnc_reset(sc); return; } if (next->md->md1 & OWN) { if (lookahead) { /* * Looked ahead into a packet still * being transmitted */ sc->trans_next = start_of_packet; break; } else { int unit = sc->arpcom.ac_if.if_unit; log(LOG_ERR, "lnc%d: End of transmitted packet not found -- Resetting\n", unit); lnc_reset(sc); return; } } } /* * Check for ERR first since other flags are irrelevant if an * error occurred. */ if (next->md->md1 & MDERR) { int unit = sc->arpcom.ac_if.if_unit; LNCSTATS(terr) sc->arpcom.ac_if.if_oerrors++; if (next->md->md3 & LCOL) { LNCSTATS(lcol) log(LOG_ERR, "lnc%d: Transmit late collision -- Net error?\n", unit); sc->arpcom.ac_if.if_collisions++; /* * Clear TBUFF since it's not valid when LCOL * set */ next->md->md3 &= ~TBUFF; } if (next->md->md3 & LCAR) { LNCSTATS(lcar) log(LOG_ERR, "lnc%d: Loss of carrier during transmit -- Net error?\n", unit); } if (next->md->md3 & RTRY) { LNCSTATS(rtry) log(LOG_ERR, "lnc%d: Transmit of packet failed after 16 attempts -- TDR = %d\n", unit, ((sc->trans_ring + sc->trans_next)->md->md3 & TDR)); sc->arpcom.ac_if.if_collisions += 16; /* * Clear TBUFF since it's not valid when RTRY * set */ next->md->md3 &= ~TBUFF; } /* * TBUFF is only valid if neither LCOL nor RTRY are set. * We need to check UFLO after LCOL and RTRY so that we * know whether or not TBUFF is valid. If either are * set then TBUFF will have been cleared above. A * UFLO error will turn off the transmitter so we * have to reset. * */ if (next->md->md3 & UFLO) { LNCSTATS(uflo) /* * If an UFLO has occured it's possibly due * to a TBUFF error */ if (next->md->md3 & TBUFF) { LNCSTATS(tbuff) log(LOG_ERR, "lnc%d: Transmit buffer error -- Resetting\n", unit); } else log(LOG_ERR, "lnc%d: Transmit underflow error -- Resetting\n", unit); lnc_reset(sc); return; } do { INC_MD_PTR(sc->trans_next, sc->ntdre) next = sc->trans_ring + sc->trans_next; } while (!(next->md->md1 & STP) && (sc->trans_next != sc->next_to_send)); } else { /* * Since we check for ERR first then if we get here * the packet was transmitted correctly. There may * still have been non-fatal errors though. * Don't bother checking for DEF, waste of time. */ sc->arpcom.ac_if.if_opackets++; if (next->md->md1 & MORE) { LNCSTATS(more) sc->arpcom.ac_if.if_collisions += 2; } /* * ONE is invalid if LCOL is set. If LCOL was set then * ERR would have also been set and we would have * returned from lnc_tint above. Therefore we can * assume if we arrive here that ONE is valid. * */ if (next->md->md1 & ONE) { LNCSTATS(one) sc->arpcom.ac_if.if_collisions++; } INC_MD_PTR(sc->trans_next, sc->ntdre) next = sc->trans_ring + sc->trans_next; } /* * Clear descriptors and free any mbufs. */ do { start = sc->trans_ring + start_of_packet; start->md->md1 &= HADR; if (sc->nic.mem_mode == DMA_MBUF) { /* Cache clusters on a local queue */ if ((start->buff.mbuf->m_flags & M_EXT) && (sc->mbuf_count < MBUF_CACHE_LIMIT)) { if (sc->mbuf_count) { start->buff.mbuf->m_next = sc->mbufs; sc->mbufs = start->buff.mbuf; } else sc->mbufs = start->buff.mbuf; sc->mbuf_count++; start->buff.mbuf = 0; } else { struct mbuf *junk; MFREE(start->buff.mbuf, junk); start->buff.mbuf = 0; } } sc->pending_transmits--; INC_MD_PTR(start_of_packet, sc->ntdre) }while (start_of_packet != sc->trans_next); /* * There's now at least one free descriptor * in the ring so indicate that we can accept * more packets again. */ sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE; lookahead++; } while (sc->pending_transmits && !(next->md->md1 & OWN)); /* * Clear TINT since we've dealt with all * the completed transmissions. */ outw(sc->rdp, TINT | INEA); /* XXX only while doing if_is comparisons */ if (!(sc->arpcom.ac_if.if_flags & IFF_OACTIVE)) lnc_start(&sc->arpcom.ac_if); } static int lnc_probe(struct isa_device * isa_dev) { int nports; int unit = isa_dev->id_unit; struct lnc_softc *sc = &lnc_softc[unit]; unsigned iobase = isa_dev->id_iobase; #ifdef DIAGNOSTIC int vsw; vsw = inw(isa_dev->id_iobase + PCNET_VSW); printf("Vendor Specific Word = %x\n", vsw); #endif nports = bicc_probe(sc, iobase); if (nports == 0) nports = ne2100_probe(sc, iobase); if (nports == 0) nports = depca_probe(sc, iobase); #ifdef PC98 if (nports == 0) nports = cnet98s_probe(sc, iobase); #endif return (nports); } #ifdef PC98 /* ISA Bus Configuration Registers */ /* XXX - Should be in ic/Am7990.h */ #define MSRDA 0x0000 /* ISACSR0: Master Mode Read Activity */ #define MSWRA 0x0001 /* ISACSR1: Master Mode Write Activity */ #define MC 0x0002 /* ISACSR2: Miscellaneous Configuration */ #define LED1 0x0005 /* ISACSR5: LED1 Status */ #define LED2 0x0006 /* ISACSR6: LED2 Status */ #define LED3 0x0007 /* ISACSR7: LED3 Status */ #define LED_PSE 0x0080 /* Pulse Stretcher */ #define LED_XMTE 0x0010 /* Transmit Status */ #define LED_RVPOLE 0x0008 /* Receive Polarity */ #define LED_RCVE 0x0004 /* Receive Status */ #define LED_JABE 0x0002 /* Jabber */ #define LED_COLE 0x0001 /* Collision */ static int cnet98s_probe(struct lnc_softc *sc, unsigned iobase) { int i; ushort tmp; sc->rap = iobase + CNET98S_RAP; sc->rdp = iobase + CNET98S_RDP; /* Reset */ tmp = inw(iobase + CNET98S_RESET); outw(iobase + CNET98S_RESET, tmp); DELAY(500); sc->nic.ic = pcnet_probe(sc); if ((sc->nic.ic == UNKNOWN) || (sc->nic.ic > PCnet_32)) { return (0); } sc->nic.ident = CNET98S; sc->nic.mem_mode = DMA_FIXED; /* XXX - For now just use the defines */ sc->nrdre = NRDRE; sc->ntdre = NTDRE; /* Extract MAC address from PROM */ for (i = 0; i < ETHER_ADDR_LEN; i++) { sc->arpcom.ac_enaddr[i] = inb(iobase + (i * 2)); } /* * ISA Configuration * * XXX - Following parameters are Contec C-NET(98)S only. * So, check the Ethernet address here. * * Contec uses 00 80 4c ?? ?? ?? */ if (sc->arpcom.ac_enaddr[0] == (u_char)0x00 && sc->arpcom.ac_enaddr[1] == (u_char)0x80 && sc->arpcom.ac_enaddr[2] == (u_char)0x4c) { outw(sc->rap, MSRDA); outw(iobase + CNET98S_IDP, 0x0006); outw(sc->rap, MSWRA); outw(iobase + CNET98S_IDP, 0x0006); #ifdef DIAGNOSTIC outw(sc->rap, MC); printf("ISACSR2 = %x\n", inw(iobase + CNET98S_IDP)); #endif outw(sc->rap, LED1); outw(iobase + CNET98S_IDP, LED_PSE | LED_XMTE); outw(sc->rap, LED2); outw(iobase + CNET98S_IDP, LED_PSE | LED_RCVE); outw(sc->rap, LED3); outw(iobase + CNET98S_IDP, LED_PSE | LED_COLE); } return (CNET98S_IOSIZE); } #endif static int ne2100_probe(struct lnc_softc *sc, unsigned iobase) { int i; sc->rap = iobase + PCNET_RAP; sc->rdp = iobase + PCNET_RDP; sc->nic.ic = pcnet_probe(sc); if ((sc->nic.ic > 0) && (sc->nic.ic < PCnet_PCI)) { sc->nic.ident = NE2100; sc->nic.mem_mode = DMA_FIXED; /* XXX - For now just use the defines */ sc->nrdre = NRDRE; sc->ntdre = NTDRE; /* Extract MAC address from PROM */ for (i = 0; i < ETHER_ADDR_LEN; i++) sc->arpcom.ac_enaddr[i] = inb(iobase + i); return (NE2100_IOSIZE); } else { return (0); } } static int bicc_probe(struct lnc_softc *sc, unsigned iobase) { int i; /* * There isn't any way to determine if a NIC is a BICC. Basically, if * the lance probe succeeds using the i/o addresses of the BICC then * we assume it's a BICC. * */ sc->rap = iobase + BICC_RAP; sc->rdp = iobase + BICC_RDP; /* I think all these cards us the Am7990 */ if ((sc->nic.ic = lance_probe(sc))) { sc->nic.ident = BICC; sc->nic.mem_mode = DMA_FIXED; /* XXX - For now just use the defines */ sc->nrdre = NRDRE; sc->ntdre = NTDRE; /* Extract MAC address from PROM */ for (i = 0; i < ETHER_ADDR_LEN; i++) sc->arpcom.ac_enaddr[i] = inb(iobase + (i * 2)); return (BICC_IOSIZE); } else { return (0); } } /* * I don't have data sheets for the dec cards but it looks like the mac * address is contained in a 32 byte ring. Each time you read from the port * you get the next byte in the ring. The mac address is stored after a * signature so keep searching for the signature first. */ static int dec_macaddr_extract(u_char ring[], struct lnc_softc * sc) { const unsigned char signature[] = {0xff, 0x00, 0x55, 0xaa, 0xff, 0x00, 0x55, 0xaa}; int i, j, rindex; for (i = 0; i < sizeof ring; i++) { for (j = 0, rindex = i; j < sizeof signature; j++) { if (ring[rindex] != signature[j]) break; if (++rindex > sizeof ring) rindex = 0; } if (j == sizeof signature) { for (j = 0, rindex = i; j < ETHER_ADDR_LEN; j++) { sc->arpcom.ac_enaddr[j] = ring[rindex]; if (++rindex > sizeof ring) rindex = 0; } return (1); } } return (0); } static int depca_probe(struct lnc_softc *sc, unsigned iobase) { int i; unsigned char maddr_ring[DEPCA_ADDR_ROM_SIZE]; sc->rap = iobase + DEPCA_RAP; sc->rdp = iobase + DEPCA_RDP; if ((sc->nic.ic = lance_probe(sc))) { sc->nic.ident = DEPCA; sc->nic.mem_mode = SHMEM; /* Extract MAC address from PROM */ for (i = 0; i < DEPCA_ADDR_ROM_SIZE; i++) maddr_ring[i] = inb(iobase + DEPCA_ADP); if (dec_macaddr_extract(maddr_ring, sc)) { return (DEPCA_IOSIZE); } } return (0); } static int lance_probe(struct lnc_softc *sc) { write_csr(sc, CSR0, STOP); if ((inw(sc->rdp) & STOP) && !(read_csr(sc, CSR3))) { /* * Check to see if it's a C-LANCE. For the LANCE the INEA bit * cannot be set while the STOP bit is. This restriction is * removed for the C-LANCE. */ write_csr(sc, CSR0, INEA); if (read_csr(sc, CSR0) & INEA) return (C_LANCE); else return (LANCE); } else return (UNKNOWN); } static int pcnet_probe(struct lnc_softc *sc) { u_long chip_id; int type; /* * The PCnet family don't reset the RAP register on reset so we'll * have to write during the probe :-) It does have an ID register * though so the probe is just a matter of reading it. */ if ((type = lance_probe(sc))) { chip_id = read_csr(sc, CSR89); chip_id <<= 16; chip_id |= read_csr(sc, CSR88); if (chip_id & AMD_MASK) { chip_id >>= 12; switch (chip_id & PART_MASK) { case Am79C960: return (PCnet_ISA); case Am79C961: return (PCnet_ISAplus); case Am79C961A: return (PCnet_ISA_II); case Am79C965: return (PCnet_32); case Am79C970: return (PCnet_PCI); case Am79C970A: return (PCnet_PCI_II); case Am79C971: return (PCnet_FAST); case Am79C972: return (PCnet_FASTplus); case Am79C978: return (PCnet_Home); default: break; } } } return (type); } static int lnc_attach_sc(struct lnc_softc *sc, int unit) { int lnc_mem_size; /* * Allocate memory for use by the controller. * * XXX -- the Am7990 and Am79C960 only have 24 address lines and so can * only access the lower 16Mb of physical memory. For the moment we * assume that malloc will allocate memory within the lower 16Mb * range. This is not a very valid assumption but there's nothing * that can be done about it yet. For shared memory NICs this isn't * relevant. * */ lnc_mem_size = ((NDESC(sc->nrdre) + NDESC(sc->ntdre)) * sizeof(struct host_ring_entry)); if (sc->nic.mem_mode != SHMEM) lnc_mem_size += sizeof(struct init_block) + (sizeof(struct mds) * (NDESC(sc->nrdre) + NDESC(sc->ntdre))) + MEM_SLEW; /* If using DMA to fixed host buffers then allocate memory for them */ if (sc->nic.mem_mode == DMA_FIXED) lnc_mem_size += (NDESC(sc->nrdre) * RECVBUFSIZE) + (NDESC(sc->ntdre) * TRANSBUFSIZE); if (sc->nic.mem_mode != SHMEM) { if (sc->nic.ic < PCnet_32) { /* ISA based cards */ sc->recv_ring = contigmalloc(lnc_mem_size, M_DEVBUF, M_NOWAIT, 0ul, 0xfffffful, 4ul, 0x1000000); } else { /* Non-ISA based cards, 32 bit capable */ #ifdef notyet /* * For the 32 bit driver we're not fussed where we DMA to * though it'll still need to be contiguous */ sc->recv_ring = malloc(lnc_mem_size, M_DEVBUF, M_NOWAIT); #else /* * For now it still needs to be below 16MB because the * descriptor's can only hold 16 bit addresses. */ sc->recv_ring = contigmalloc(lnc_mem_size, M_DEVBUF, M_NOWAIT, 0ul, 0xfffffful, 4ul, 0x1000000); #endif } } if (!sc->recv_ring) { log(LOG_ERR, "lnc%d: Couldn't allocate memory for NIC\n", unit); return (0); /* XXX -- attach failed -- not tested in * calling routines */ } /* Set default mode */ sc->nic.mode = NORMAL; /* Fill in arpcom structure entries */ sc->arpcom.ac_if.if_softc = sc; sc->arpcom.ac_if.if_name = lncdriver.name; sc->arpcom.ac_if.if_unit = unit; sc->arpcom.ac_if.if_mtu = ETHERMTU; sc->arpcom.ac_if.if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; sc->arpcom.ac_if.if_timer = 0; sc->arpcom.ac_if.if_output = ether_output; sc->arpcom.ac_if.if_start = lnc_start; sc->arpcom.ac_if.if_ioctl = lnc_ioctl; sc->arpcom.ac_if.if_watchdog = lnc_watchdog; sc->arpcom.ac_if.if_init = lnc_init; sc->arpcom.ac_if.if_type = IFT_ETHER; sc->arpcom.ac_if.if_addrlen = ETHER_ADDR_LEN; sc->arpcom.ac_if.if_hdrlen = ETHER_HDR_LEN; sc->arpcom.ac_if.if_snd.ifq_maxlen = IFQ_MAXLEN; /* * XXX -- should check return status of if_attach */ if_attach(&sc->arpcom.ac_if); ether_ifattach(&sc->arpcom.ac_if); printf("lnc%d: ", unit); if (sc->nic.ic == LANCE || sc->nic.ic == C_LANCE) printf("%s (%s)", nic_ident[sc->nic.ident], ic_ident[sc->nic.ic]); else printf("%s", ic_ident[sc->nic.ic]); printf(" address %6D\n", sc->arpcom.ac_enaddr, ":"); bpfattach(&sc->arpcom.ac_if, DLT_EN10MB, sizeof(struct ether_header)); return (1); } static int lnc_attach(struct isa_device * isa_dev) { int unit = isa_dev->id_unit; struct lnc_softc *sc = &lnc_softc[unit]; int result; isa_dev->id_ointr = lncintr; result = lnc_attach_sc (sc, unit); if (result == 0) return (0); #ifndef PC98 /* * XXX - is it safe to call isa_dmacascade() after if_attach() * and ether_ifattach() have been called in lnc_attach() ??? */ if ((sc->nic.mem_mode != SHMEM) && (sc->nic.ic < PCnet_32)) isa_dmacascade(isa_dev->id_drq); #endif return result; } #if NPCI > 0 void * lnc_attach_ne2100_pci(int unit, unsigned iobase) { int i; struct lnc_softc *sc = malloc(sizeof *sc, M_DEVBUF, M_NOWAIT); if (sc) { bzero (sc, sizeof *sc); sc->rap = iobase + PCNET_RAP; sc->rdp = iobase + PCNET_RDP; sc->bdp = iobase + PCNET_BDP; sc->nic.ic = pcnet_probe(sc); if (sc->nic.ic >= PCnet_32) { sc->nic.ident = NE2100; sc->nic.mem_mode = DMA_FIXED; /* XXX - For now just use the defines */ sc->nrdre = NRDRE; sc->ntdre = NTDRE; /* Extract MAC address from PROM */ for (i = 0; i < ETHER_ADDR_LEN; i++) sc->arpcom.ac_enaddr[i] = inb(iobase + i); if (lnc_attach_sc(sc, unit) == 0) { free(sc, M_DEVBUF); sc = NULL; } } else { free(sc, M_DEVBUF); sc = NULL; } } return sc; } #endif static void lnc_init(xsc) void *xsc; { struct lnc_softc *sc = xsc; int s, i; char *lnc_mem; /* Check that interface has valid address */ if (TAILQ_EMPTY(&sc->arpcom.ac_if.if_addrhead)) /* XXX unlikely */ return; /* Shut down interface */ s = splimp(); lnc_stop(sc); sc->arpcom.ac_if.if_flags |= IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; /* XXX??? */ /* * This sets up the memory area for the controller. Memory is set up for * the initialisation block (12 words of contiguous memory starting * on a word boundary),the transmit and receive ring structures (each * entry is 4 words long and must start on a quadword boundary) and * the data buffers. * * The alignment tests are particularly paranoid. */ sc->recv_next = 0; sc->trans_ring = sc->recv_ring + NDESC(sc->nrdre); sc->trans_next = 0; if (sc->nic.mem_mode == SHMEM) lnc_mem = (char *) sc->nic.iobase; else lnc_mem = (char *) (sc->trans_ring + NDESC(sc->ntdre)); lnc_mem = (char *)(((int)lnc_mem + 1) & ~1); sc->init_block = (struct init_block *) ((int) lnc_mem & ~1); lnc_mem = (char *) (sc->init_block + 1); lnc_mem = (char *)(((int)lnc_mem + 7) & ~7); /* Initialise pointers to descriptor entries */ for (i = 0; i < NDESC(sc->nrdre); i++) { (sc->recv_ring + i)->md = (struct mds *) lnc_mem; lnc_mem += sizeof(struct mds); } for (i = 0; i < NDESC(sc->ntdre); i++) { (sc->trans_ring + i)->md = (struct mds *) lnc_mem; lnc_mem += sizeof(struct mds); } /* Initialise the remaining ring entries */ if (sc->nic.mem_mode == DMA_MBUF) { sc->mbufs = 0; sc->mbuf_count = 0; /* Free previously allocated mbufs */ if (sc->flags & LNC_INITIALISED) lnc_free_mbufs(sc); for (i = 0; i < NDESC(sc->nrdre); i++) { if (alloc_mbuf_cluster(sc, sc->recv_ring+i)) { log(LOG_ERR, "Initialisation failed -- no mbufs\n"); splx(s); return; } } for (i = 0; i < NDESC(sc->ntdre); i++) { (sc->trans_ring + i)->buff.mbuf = 0; (sc->trans_ring + i)->md->md0 = 0; (sc->trans_ring + i)->md->md1 = 0; (sc->trans_ring + i)->md->md2 = 0; (sc->trans_ring + i)->md->md3 = 0; } } else { for (i = 0; i < NDESC(sc->nrdre); i++) { (sc->recv_ring + i)->md->md0 = kvtop(lnc_mem); (sc->recv_ring + i)->md->md1 = ((kvtop(lnc_mem) >> 16) & 0xff) | OWN; (sc->recv_ring + i)->md->md2 = -RECVBUFSIZE; (sc->recv_ring + i)->md->md3 = 0; (sc->recv_ring + i)->buff.data = lnc_mem; lnc_mem += RECVBUFSIZE; } for (i = 0; i < NDESC(sc->ntdre); i++) { (sc->trans_ring + i)->md->md0 = kvtop(lnc_mem); (sc->trans_ring + i)->md->md1 = ((kvtop(lnc_mem) >> 16) & 0xff); (sc->trans_ring + i)->md->md2 = 0; (sc->trans_ring + i)->md->md3 = 0; (sc->trans_ring + i)->buff.data = lnc_mem; lnc_mem += TRANSBUFSIZE; } } sc->next_to_send = 0; /* Set up initialisation block */ sc->init_block->mode = sc->nic.mode; for (i = 0; i < ETHER_ADDR_LEN; i++) sc->init_block->padr[i] = sc->arpcom.ac_enaddr[i]; lnc_setladrf(sc); sc->init_block->rdra = kvtop(sc->recv_ring->md); sc->init_block->rlen = ((kvtop(sc->recv_ring->md) >> 16) & 0xff) | (sc->nrdre << 13); sc->init_block->tdra = kvtop(sc->trans_ring->md); sc->init_block->tlen = ((kvtop(sc->trans_ring->md) >> 16) & 0xff) | (sc->ntdre << 13); /* Set flags to show that the memory area is valid */ sc->flags |= LNC_INITIALISED; sc->pending_transmits = 0; /* Give the LANCE the physical address of the initialisation block */ if (sc->nic.ic == PCnet_Home) { u_short media; /* Set PHY_SEL to HomeRun */ media = read_bcr(sc, BCR49); media &= ~3; media |= 1; write_bcr(sc, BCR49, media); } write_csr(sc, CSR1, kvtop(sc->init_block)); write_csr(sc, CSR2, (kvtop(sc->init_block) >> 16) & 0xff); /* * Depending on which controller this is, CSR3 has different meanings. * For the Am7990 it controls DMA operations, for the Am79C960 it * controls interrupt masks and transmitter algorithms. In either * case, none of the flags are set. * */ write_csr(sc, CSR3, 0); /* Let's see if it starts */ write_csr(sc, CSR0, INIT); for (i = 0; i < 1000; i++) if (read_csr(sc, CSR0) & IDON) break; /* * Now that the initialisation is complete there's no reason to * access anything except CSR0, so we leave RAP pointing there * so we can just access RDP from now on, saving an outw each * time. */ if (read_csr(sc, CSR0) & IDON) { /* * Enable interrupts, start the LANCE, mark the interface as * running and transmit any pending packets. */ write_csr(sc, CSR0, STRT | INEA); sc->arpcom.ac_if.if_flags |= IFF_RUNNING; sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE; lnc_start(&sc->arpcom.ac_if); } else log(LOG_ERR, "lnc%d: Initialisation failed\n", sc->arpcom.ac_if.if_unit); splx(s); } /* * The interrupt flag (INTR) will be set and provided that the interrupt enable * flag (INEA) is also set, the interrupt pin will be driven low when any of * the following occur: * * 1) Completion of the initialisation routine (IDON). 2) The reception of a * packet (RINT). 3) The transmission of a packet (TINT). 4) A transmitter * timeout error (BABL). 5) A missed packet (MISS). 6) A memory error (MERR). * * The interrupt flag is cleared when all of the above conditions are cleared. * * If the driver is reset from this routine then it first checks to see if any * interrupts have ocurred since the reset and handles them before returning. * This is because the NIC may signify a pending interrupt in CSR0 using the * INTR flag even if a hardware interrupt is currently inhibited (at least I * think it does from reading the data sheets). We may as well deal with * these pending interrupts now rather than get the overhead of another * hardware interrupt immediately upon returning from the interrupt handler. * */ void lncintr_sc(struct lnc_softc *sc) { int unit = sc->arpcom.ac_if.if_unit; u_short csr0; /* * INEA is the only bit that can be cleared by writing a 0 to it so * we have to include it in any writes that clear other flags. */ while ((csr0 = inw(sc->rdp)) & INTR) { /* * Clear interrupt flags early to avoid race conditions. The * controller can still set these flags even while we're in * this interrupt routine. If the flag is still set from the * event that caused this interrupt any new events will * be missed. */ /* outw(sc->rdp, IDON | CERR | BABL | MISS | MERR | RINT | TINT | INEA); */ outw(sc->rdp, csr0); /* We don't do anything with the IDON flag */ if (csr0 & ERR) { if (csr0 & CERR) { log(LOG_ERR, "lnc%d: Heartbeat error -- SQE test failed\n", unit); LNCSTATS(cerr) } if (csr0 & BABL) { log(LOG_ERR, "lnc%d: Babble error - more than 1519 bytes transmitted\n", unit); LNCSTATS(babl) sc->arpcom.ac_if.if_oerrors++; } if (csr0 & MISS) { log(LOG_ERR, "lnc%d: Missed packet -- no receive buffer\n", unit); LNCSTATS(miss) sc->arpcom.ac_if.if_ierrors++; } if (csr0 & MERR) { log(LOG_ERR, "lnc%d: Memory error -- Resetting\n", unit); LNCSTATS(merr) lnc_reset(sc); continue; } } if (csr0 & RINT) { LNCSTATS(rint) lnc_rint(sc); } if (csr0 & TINT) { LNCSTATS(tint) sc->arpcom.ac_if.if_timer = 0; lnc_tint(sc); } /* * If there's room in the transmit descriptor ring then queue * some more transmit packets. */ if (!(sc->arpcom.ac_if.if_flags & IFF_OACTIVE)) lnc_start(&sc->arpcom.ac_if); } } static void lncintr(int unit) { struct lnc_softc *sc = &lnc_softc[unit]; lncintr_sc (sc); } static __inline int mbuf_to_buffer(struct mbuf *m, char *buffer) { int len=0; for( ; m; m = m->m_next) { bcopy(mtod(m, caddr_t), buffer, m->m_len); buffer += m->m_len; len += m->m_len; } return(len); } static __inline struct mbuf * chain_to_cluster(struct mbuf *m) { struct mbuf *new; MGET(new, M_DONTWAIT, MT_DATA); if (new) { MCLGET(new, M_DONTWAIT); if (new->m_ext.ext_buf) { new->m_len = mbuf_to_buffer(m, new->m_data); m_freem(m); return(new); } else m_free(new); } return(0); } /* * IFF_OACTIVE and IFF_RUNNING are checked in ether_output so it's redundant * to check them again since we wouldn't have got here if they were not * appropriately set. This is also called from lnc_init and lncintr but the * flags should be ok at those points too. */ static void lnc_start(struct ifnet *ifp) { struct lnc_softc *sc = ifp->if_softc; struct host_ring_entry *desc; int tmp; int end_of_packet; struct mbuf *head, *m; int len, chunk; int addr; int no_entries_needed; do { IF_DEQUEUE(&sc->arpcom.ac_if.if_snd, head); if (!head) return; if (sc->nic.mem_mode == DMA_MBUF) { no_entries_needed = 0; for (m=head; m; m = m->m_next) no_entries_needed++; /* * We try and avoid bcopy as much as possible * but there are two cases when we use it. * * 1) If there are not enough free entries in the ring * to hold each mbuf in the chain then compact the * chain into a single cluster. * * 2) The Am7990 and Am79C90 must not have less than * 100 bytes in the first descriptor of a chained * packet so it's necessary to shuffle the mbuf * contents to ensure this. */ if (no_entries_needed > (NDESC(sc->ntdre) - sc->pending_transmits)) { if (!(head = chain_to_cluster(head))) { log(LOG_ERR, "lnc%d: Couldn't get mbuf for transmit packet -- Resetting \n ",ifp->if_unit); lnc_reset(sc); return; } } else if ((sc->nic.ic == LANCE) || (sc->nic.ic == C_LANCE)) { if ((head->m_len < 100) && (head->m_next)) { len = 100 - head->m_len; if (M_TRAILINGSPACE(head) < len) { /* * Move data to start of data * area. We assume the first * mbuf has a packet header * and is not a cluster. */ bcopy((caddr_t)head->m_data, (caddr_t)head->m_pktdat, head->m_len); head->m_data = head->m_pktdat; } m = head->m_next; while (m && (len > 0)) { chunk = min(len, m->m_len); bcopy(mtod(m, caddr_t), mtod(head, caddr_t) + head->m_len, chunk); len -= chunk; head->m_len += chunk; m->m_len -= chunk; m->m_data += chunk; if (m->m_len <= 0) { MFREE(m, head->m_next); m = head->m_next; } } } } tmp = sc->next_to_send; /* * On entering this loop we know that tmp points to a * descriptor with a clear OWN bit. */ desc = sc->trans_ring + tmp; len = ETHER_MIN_LEN; for (m = head; m; m = m->m_next) { desc->buff.mbuf = m; addr = kvtop(m->m_data); desc->md->md0 = addr; desc->md->md1 = ((addr >> 16) & 0xff); desc->md->md3 = 0; desc->md->md2 = -m->m_len; sc->pending_transmits++; len -= m->m_len; INC_MD_PTR(tmp, sc->ntdre) desc = sc->trans_ring + tmp; } end_of_packet = tmp; DEC_MD_PTR(tmp, sc->ntdre) desc = sc->trans_ring + tmp; desc->md->md1 |= ENP; if (len > 0) desc->md->md2 -= len; /* * Set OWN bits in reverse order, otherwise the Lance * could start sending the packet before all the * buffers have been relinquished by the host. */ while (tmp != sc->next_to_send) { desc->md->md1 |= OWN; DEC_MD_PTR(tmp, sc->ntdre) desc = sc->trans_ring + tmp; } sc->next_to_send = end_of_packet; desc->md->md1 |= STP | OWN; } else { sc->pending_transmits++; desc = sc->trans_ring + sc->next_to_send; len = mbuf_to_buffer(head, desc->buff.data); desc->md->md3 = 0; desc->md->md2 = -max(len, ETHER_MIN_LEN - ETHER_CRC_LEN); desc->md->md1 |= OWN | STP | ENP; INC_MD_PTR(sc->next_to_send, sc->ntdre) } /* Force an immediate poll of the transmit ring */ outw(sc->rdp, TDMD | INEA); /* * Set a timer so if the buggy Am7990.h shuts * down we can wake it up. */ ifp->if_timer = 2; if (sc->arpcom.ac_if.if_bpf) bpf_mtap(&sc->arpcom.ac_if, head); if (sc->nic.mem_mode != DMA_MBUF) m_freem(head); } while (sc->pending_transmits < NDESC(sc->ntdre)); /* * Transmit ring is full so set IFF_OACTIVE * since we can't buffer any more packets. */ sc->arpcom.ac_if.if_flags |= IFF_OACTIVE; LNCSTATS(trans_ring_full) } static int lnc_ioctl(struct ifnet * ifp, u_long command, caddr_t data) { struct lnc_softc *sc = ifp->if_softc; int s, error = 0; s = splimp(); switch (command) { case SIOCSIFADDR: case SIOCGIFADDR: case SIOCSIFMTU: error = ether_ioctl(ifp, command, data); break; case SIOCSIFFLAGS: #ifdef DEBUG if (ifp->if_flags & IFF_DEBUG) sc->lnc_debug = 1; else sc->lnc_debug = 0; #endif if (ifp->if_flags & IFF_PROMISC) { if (!(sc->nic.mode & PROM)) { sc->nic.mode |= PROM; lnc_init(sc); } } else if (sc->nic.mode & PROM) { sc->nic.mode &= ~PROM; lnc_init(sc); } if ((ifp->if_flags & IFF_ALLMULTI) && !(sc->flags & LNC_ALLMULTI)) { sc->flags |= LNC_ALLMULTI; lnc_init(sc); } else if (!(ifp->if_flags & IFF_ALLMULTI) && (sc->flags & LNC_ALLMULTI)) { sc->flags &= ~LNC_ALLMULTI; lnc_init(sc); } if ((ifp->if_flags & IFF_UP) == 0 && (ifp->if_flags & IFF_RUNNING) != 0) { /* * If interface is marked down and it is running, * then stop it. */ lnc_stop(sc); ifp->if_flags &= ~IFF_RUNNING; } else if ((ifp->if_flags & IFF_UP) != 0 && (ifp->if_flags & IFF_RUNNING) == 0) { /* * If interface is marked up and it is stopped, then * start it. */ lnc_init(sc); } break; case SIOCADDMULTI: case SIOCDELMULTI: lnc_init(sc); error = 0; break; default: error = EINVAL; } (void) splx(s); return error; } static void lnc_watchdog(struct ifnet *ifp) { log(LOG_ERR, "lnc%d: Device timeout -- Resetting\n", ifp->if_unit); ifp->if_oerrors++; lnc_reset(ifp->if_softc); } #ifdef DEBUG void lnc_dump_state(struct lnc_softc *sc) { int i; printf("\nDriver/NIC [%d] state dump\n", sc->arpcom.ac_if.if_unit); printf("Memory access mode: %b\n", sc->nic.mem_mode, MEM_MODES); printf("Host memory\n"); printf("-----------\n"); printf("Receive ring: base = %p, next = %p\n", (void *)sc->recv_ring, (void *)(sc->recv_ring + sc->recv_next)); for (i = 0; i < NDESC(sc->nrdre); i++) printf("\t%d:%p md = %p buff = %p\n", i, (void *)(sc->recv_ring + i), (void *)(sc->recv_ring + i)->md, (void *)(sc->recv_ring + i)->buff.data); printf("Transmit ring: base = %p, next = %p\n", (void *)sc->trans_ring, (void *)(sc->trans_ring + sc->trans_next)); for (i = 0; i < NDESC(sc->ntdre); i++) printf("\t%d:%p md = %p buff = %p\n", i, (void *)(sc->trans_ring + i), (void *)(sc->trans_ring + i)->md, (void *)(sc->trans_ring + i)->buff.data); printf("Lance memory (may be on host(DMA) or card(SHMEM))\n"); printf("Init block = %p\n", (void *)sc->init_block); printf("\tmode = %b rlen:rdra = %x:%x tlen:tdra = %x:%x\n", sc->init_block->mode, INIT_MODE, sc->init_block->rlen, sc->init_block->rdra, sc->init_block->tlen, sc->init_block->tdra); printf("Receive descriptor ring\n"); for (i = 0; i < NDESC(sc->nrdre); i++) printf("\t%d buffer = 0x%x%x, BCNT = %d,\tMCNT = %u,\tflags = %b\n", i, ((sc->recv_ring + i)->md->md1 & HADR), (sc->recv_ring + i)->md->md0, -(short) (sc->recv_ring + i)->md->md2, (sc->recv_ring + i)->md->md3, (((sc->recv_ring + i)->md->md1 & ~HADR) >> 8), RECV_MD1); printf("Transmit descriptor ring\n"); for (i = 0; i < NDESC(sc->ntdre); i++) printf("\t%d buffer = 0x%x%x, BCNT = %d,\tflags = %b %b\n", i, ((sc->trans_ring + i)->md->md1 & HADR), (sc->trans_ring + i)->md->md0, -(short) (sc->trans_ring + i)->md->md2, ((sc->trans_ring + i)->md->md1 >> 8), TRANS_MD1, ((sc->trans_ring + i)->md->md3 >> 10), TRANS_MD3); printf("\nnext_to_send = %x\n", sc->next_to_send); printf("\n CSR0 = %b CSR1 = %x CSR2 = %x CSR3 = %x\n\n", read_csr(sc, CSR0), CSR0_FLAGS, read_csr(sc, CSR1), read_csr(sc, CSR2), read_csr(sc, CSR3)); /* Set RAP back to CSR0 */ outw(sc->rap, CSR0); } void mbuf_dump_chain(struct mbuf * m) { #define MBUF_FLAGS \ "\20\1M_EXT\2M_PKTHDR\3M_EOR\4UNKNOWN\5M_BCAST\6M_MCAST" if (!m) log(LOG_DEBUG, "m == NULL\n"); do { log(LOG_DEBUG, "m = %p\n", (void *)m); log(LOG_DEBUG, "m_hdr.mh_next = %p\n", (void *)m->m_hdr.mh_next); log(LOG_DEBUG, "m_hdr.mh_nextpkt = %p\n", (void *)m->m_hdr.mh_nextpkt); log(LOG_DEBUG, "m_hdr.mh_len = %d\n", m->m_hdr.mh_len); log(LOG_DEBUG, "m_hdr.mh_data = %p\n", (void *)m->m_hdr.mh_data); log(LOG_DEBUG, "m_hdr.mh_type = %d\n", m->m_hdr.mh_type); log(LOG_DEBUG, "m_hdr.mh_flags = %b\n", m->m_hdr.mh_flags, MBUF_FLAGS); if (!(m->m_hdr.mh_flags & (M_PKTHDR | M_EXT))) log(LOG_DEBUG, "M_dat.M_databuf = %p\n", (void *)m->M_dat.M_databuf); else { if (m->m_hdr.mh_flags & M_PKTHDR) { log(LOG_DEBUG, "M_dat.MH.MH_pkthdr.len = %d\n", m->M_dat.MH.MH_pkthdr.len); log(LOG_DEBUG, "M_dat.MH.MH_pkthdr.rcvif = %p\n", (void *)m->M_dat.MH.MH_pkthdr.rcvif); if (!(m->m_hdr.mh_flags & M_EXT)) log(LOG_DEBUG, "M_dat.MH.MH_dat.MH_databuf = %p\n", (void *)m->M_dat.MH.MH_dat.MH_databuf); } if (m->m_hdr.mh_flags & M_EXT) { log(LOG_DEBUG, "M_dat.MH.MH_dat.MH_ext.ext_buff %p\n", (void *)m->M_dat.MH.MH_dat.MH_ext.ext_buf); log(LOG_DEBUG, "M_dat.MH.MH_dat.MH_ext.ext_free %p\n", (void *)m->M_dat.MH.MH_dat.MH_ext.ext_free); log(LOG_DEBUG, "M_dat.MH.MH_dat.MH_ext.ext_size %d\n", m->M_dat.MH.MH_dat.MH_ext.ext_size); } } } while ((m = m->m_next) != NULL); } #endif - -#endif diff --git a/sys/i386/isa/mca_machdep.c b/sys/i386/isa/mca_machdep.c index 8c416cf43917..6b71206394ff 100644 --- a/sys/i386/isa/mca_machdep.c +++ b/sys/i386/isa/mca_machdep.c @@ -1,162 +1,158 @@ /*- * Copyright (c) 1999 Matthew N. Dodd * All rights reserved. * * 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 * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ -#include "mca.h" -#if NMCA > 0 - #include #include #include #include #include #include #include #include #include #include #include #include /* Global MCA bus flag */ int MCA_system = 0; /* System Configuration Block */ struct sys_config { u_int16_t count; u_int8_t model; u_int8_t submodel; u_int8_t bios_rev; u_int8_t feature; #define FEATURE_RESV 0x01 /* Reserved */ #define FEATURE_MCABUS 0x02 /* MicroChannel Architecture */ #define FEATURE_EBDA 0x04 /* Extended BIOS data area allocated */ #define FEATURE_WAITEV 0x08 /* Wait for external event is supported */ #define FEATURE_KBDINT 0x10 /* Keyboard intercept called by Int 09h */ #define FEATURE_RTC 0x20 /* Real-time clock present */ #define FEATURE_IC2 0x40 /* Second interrupt chip present */ #define FEATURE_DMA3 0x80 /* DMA channel 3 used by hard disk BIOS */ u_int8_t pad[3]; } __attribute__ ((packed)); /* Function Prototypes */ static void bios_mcabus_present (void *); SYSINIT(mca_present, SI_SUB_CPU, SI_ORDER_ANY, bios_mcabus_present, NULL); /* Functions */ static void bios_mcabus_present(void * dummy) { struct vm86frame vmf; struct sys_config * scp; vm_offset_t paddr; bzero(&vmf, sizeof(struct vm86frame)); vmf.vmf_ah = 0xc0; if (vm86_intcall(0x15, &vmf)) { if (bootverbose) { printf("BIOS SDT: INT call failed.\n"); } return; } if ((vmf.vmf_ah != 0) && (vmf.vmf_flags & 0x01)) { if (bootverbose) { printf("BIOS SDT: Not supported. Not PS/2?\n"); printf("BIOS SDT: AH 0x%02x, Flags 0x%04x\n", vmf.vmf_ah, vmf.vmf_flags); } return; } paddr = vmf.vmf_es; paddr = (paddr << 4) + vmf.vmf_bx; scp = (struct sys_config *)BIOS_PADDRTOVADDR(paddr); if (bootverbose) { printf("BIOS SDT: model 0x%02x, submodel 0x%02x, bios_rev 0x%02x\n", scp->model, scp->submodel, scp->bios_rev); printf("BIOS SDT: features 0x%b\n", scp->feature, "\20" "\01RESV" "\02MCABUS" "\03EBDA" "\04WAITEV" "\05KBDINT" "\06RTC" "\07IC2" "\08DMA3\n"); } MCA_system = ((scp->feature & FEATURE_MCABUS) ? 1 : 0); if (MCA_system) printf("MicroChannel Architecture System detected.\n"); return; } int mca_bus_nmi (void) { int slot; int retval = 0; int pos5 = 0; /* Disable motherboard setup */ outb(MCA_MB_SETUP_REG, MCA_MB_SETUP_DIS); /* For each slot */ for (slot = 0; slot < MCA_MAX_SLOTS; slot++) { /* Select the slot */ outb(MCA_ADAP_SETUP_REG, slot | MCA_ADAP_SET); pos5 = inb(MCA_POS_REG(MCA_POS5)); /* If Adapter Check is low */ if ((pos5 & MCA_POS5_CHCK) == 0) { retval++; /* If Adapter Check Status is available */ if ((pos5 & MCA_POS5_CHCK_STAT) == 0) { printf("MCA NMI: slot %d, POS6=0x%02x, POS7=0x%02x\n", slot+1, inb( MCA_POS_REG(MCA_POS6) ), inb( MCA_POS_REG(MCA_POS7) )); } else { printf("MCA NMI: slot %d\n", slot+1); } } /* Disable adapter setup */ outb(MCA_ADAP_SETUP_REG, MCA_ADAP_SETUP_DIS); } return (retval); } -#endif diff --git a/sys/i386/isa/mcd.c b/sys/i386/isa/mcd.c index e4da168e3807..923ec0f8fc74 100644 --- a/sys/i386/isa/mcd.c +++ b/sys/i386/isa/mcd.c @@ -1,1836 +1,1833 @@ /* * Copyright 1993 by Holger Veit (data part) * Copyright 1993 by Brian Moore (audio part) * Changes Copyright 1993 by Gary Clark II * Changes Copyright (C) 1994-1995 by Andrey A. Chernov, Moscow, Russia * * Rewrote probe routine to work on newer Mitsumi drives. * Additional changes (C) 1994 by Jordan K. Hubbard * * All rights reserved. * * 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 * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, 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 software was developed by Holger Veit and Brian Moore * for use with "386BSD" and similar operating systems. * "Similar operating systems" includes mainly non-profit oriented * systems for research and education, including but not restricted to * "NetBSD", "FreeBSD", "Mach" (by CMU). * 4. Neither the name of the developer(s) nor the name "386BSD" * may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER(S) ``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 DEVELOPER(S) BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ static const char COPYRIGHT[] = "mcd-driver (C)1993 by H.Veit & B.Moore"; #include "mcd.h" -#if NMCD > 0 #include #include #include #include #include #include #include #include #include #include #include #define MCD_TRACE(format, args...) \ { \ if (mcd_data[unit].debug) { \ printf("mcd%d: status=0x%02x: ", \ unit, mcd_data[unit].status); \ printf(format, ## args); \ } \ } #define mcd_part(dev) ((minor(dev)) & 7) #define mcd_unit(dev) (((minor(dev)) & 0x38) >> 3) #define mcd_phys(dev) (((minor(dev)) & 0x40) >> 6) #define RAW_PART 2 /* flags */ #define MCDVALID 0x0001 /* parameters loaded */ #define MCDINIT 0x0002 /* device is init'd */ #define MCDNEWMODEL 0x0004 /* device is new model */ #define MCDLABEL 0x0008 /* label is read */ #define MCDPROBING 0x0010 /* probing */ #define MCDREADRAW 0x0020 /* read raw mode (2352 bytes) */ #define MCDVOLINFO 0x0040 /* already read volinfo */ #define MCDTOC 0x0080 /* already read toc */ #define MCDMBXBSY 0x0100 /* local mbx is busy */ /* status */ #define MCDAUDIOBSY MCD_ST_AUDIOBSY /* playing audio */ #define MCDDSKCHNG MCD_ST_DSKCHNG /* sensed change of disk */ #define MCDDSKIN MCD_ST_DSKIN /* sensed disk in drive */ #define MCDDOOROPEN MCD_ST_DOOROPEN /* sensed door open */ /* These are apparently the different states a mitsumi can get up to */ #define MCDCDABSENT 0x0030 #define MCDCDPRESENT 0x0020 #define MCDSCLOSED 0x0080 #define MCDSOPEN 0x00a0 #define MCD_MD_UNKNOWN (-1) /* toc */ #define MCD_MAXTOCS 104 /* from the Linux driver */ #define MCD_LASTPLUS1 170 /* special toc entry */ #define MCD_TYPE_UNKNOWN 0 #define MCD_TYPE_LU002S 1 #define MCD_TYPE_LU005S 2 #define MCD_TYPE_LU006S 3 #define MCD_TYPE_FX001 4 #define MCD_TYPE_FX001D 5 struct mcd_mbx { short unit; short port; short retry; short nblk; int sz; u_long skip; struct buf *bp; int p_offset; short count; short mode; }; static struct mcd_data { short type; char *name; short config; short flags; u_char read_command; short status; int blksize; u_long disksize; int iobase; struct disklabel dlabel; int partflags[MAXPARTITIONS]; int openflags; struct mcd_volinfo volinfo; struct mcd_qchninfo toc[MCD_MAXTOCS]; short audio_status; short curr_mode; struct mcd_read2 lastpb; short debug; struct buf_queue_head head; /* head of buf queue */ struct mcd_mbx mbx; } mcd_data[NMCD]; /* reader state machine */ #define MCD_S_BEGIN 0 #define MCD_S_BEGIN1 1 #define MCD_S_WAITSTAT 2 #define MCD_S_WAITMODE 3 #define MCD_S_WAITREAD 4 /* prototypes */ static void mcd_start(int unit); static int mcd_getdisklabel(int unit); #ifdef NOTYET static void mcd_configure(struct mcd_data *cd); #endif static int mcd_get(int unit, char *buf, int nmax); static int mcd_setflags(int unit,struct mcd_data *cd); static int mcd_getstat(int unit,int sflg); static int mcd_send(int unit, int cmd,int nretrys); static void hsg2msf(int hsg, bcd_t *msf); static int msf2hsg(bcd_t *msf, int relative); static int mcd_volinfo(int unit); static ointhand2_t mcdintr; static int mcd_waitrdy(int port,int dly); static timeout_t mcd_timeout; static void mcd_doread(int state, struct mcd_mbx *mbxin); static void mcd_soft_reset(int unit); static int mcd_hard_reset(int unit); static int mcd_setmode(int unit, int mode); static int mcd_getqchan(int unit, struct mcd_qchninfo *q); static int mcd_subchan(int unit, struct ioc_read_subchannel *sc); static int mcd_toc_header(int unit, struct ioc_toc_header *th); static int mcd_read_toc(int unit); static int mcd_toc_entrys(int unit, struct ioc_read_toc_entry *te); #if 0 static int mcd_toc_entry(int unit, struct ioc_read_toc_single_entry *te); #endif static int mcd_stop(int unit); static int mcd_eject(int unit); static int mcd_inject(int unit); static int mcd_playtracks(int unit, struct ioc_play_track *pt); static int mcd_play(int unit, struct mcd_read2 *pb); static int mcd_playmsf(int unit, struct ioc_play_msf *pt); static int mcd_playblocks(int unit, struct ioc_play_blocks *); static int mcd_pause(int unit); static int mcd_resume(int unit); static int mcd_lock_door(int unit, int lock); static int mcd_close_tray(int unit); static int mcd_probe(struct isa_device *dev); static int mcd_attach(struct isa_device *dev); struct isa_driver mcddriver = { mcd_probe, mcd_attach, "mcd" }; static d_open_t mcdopen; static d_close_t mcdclose; static d_ioctl_t mcdioctl; static d_psize_t mcdsize; static d_strategy_t mcdstrategy; #define CDEV_MAJOR 29 #define BDEV_MAJOR 7 static struct cdevsw mcd_cdevsw = { /* open */ mcdopen, /* close */ mcdclose, /* read */ physread, /* write */ nowrite, /* ioctl */ mcdioctl, /* poll */ nopoll, /* mmap */ nommap, /* strategy */ mcdstrategy, /* name */ "mcd", /* maj */ CDEV_MAJOR, /* dump */ nodump, /* psize */ nopsize, /* flags */ D_DISK, /* bmaj */ BDEV_MAJOR }; #define mcd_put(port,byte) outb(port,byte) #define MCD_RETRYS 5 #define MCD_RDRETRYS 8 #define CLOSE_TRAY_SECS 8 #define DISK_SENSE_SECS 3 #define WAIT_FRAC 4 /* several delays */ #define RDELAY_WAITSTAT 300 #define RDELAY_WAITMODE 300 #define RDELAY_WAITREAD 800 #define MIN_DELAY 15 #define DELAY_GETREPLY 5000000 int mcd_attach(struct isa_device *dev) { int unit = dev->id_unit; struct mcd_data *cd = mcd_data + unit; dev->id_ointr = mcdintr; cd->iobase = dev->id_iobase; cd->flags |= MCDINIT; mcd_soft_reset(unit); bufq_init(&cd->head); #ifdef NOTYET /* wire controller for interrupts and dma */ mcd_configure(cd); #endif /* name filled in probe */ make_dev(&mcd_cdevsw, dkmakeminor(unit, 0, 0), UID_ROOT, GID_OPERATOR, 0640, "rmcd%da", unit); make_dev(&mcd_cdevsw, dkmakeminor(unit, 0, RAW_PART), UID_ROOT, GID_OPERATOR, 0640, "rmcd%dc", unit); make_dev(&mcd_cdevsw, dkmakeminor(unit, 0, 0), UID_ROOT, GID_OPERATOR, 0640, "mcd%da", unit); make_dev(&mcd_cdevsw, dkmakeminor(unit, 0, RAW_PART), UID_ROOT, GID_OPERATOR, 0640, "mcd%dc", unit); return 1; } int mcdopen(dev_t dev, int flags, int fmt, struct proc *p) { int unit,part,phys,r,retry; struct mcd_data *cd; unit = mcd_unit(dev); if (unit >= NMCD) return ENXIO; cd = mcd_data + unit; part = mcd_part(dev); phys = mcd_phys(dev); /* not initialized*/ if (!(cd->flags & MCDINIT)) return ENXIO; /* invalidated in the meantime? mark all open part's invalid */ if (!(cd->flags & MCDVALID) && cd->openflags) return ENXIO; if (mcd_getstat(unit,1) == -1) return EIO; if ( (cd->status & (MCDDSKCHNG|MCDDOOROPEN)) || !(cd->status & MCDDSKIN)) for (retry = 0; retry < DISK_SENSE_SECS * WAIT_FRAC; retry++) { (void) tsleep((caddr_t)cd, PSOCK | PCATCH, "mcdsn1", hz/WAIT_FRAC); if ((r = mcd_getstat(unit,1)) == -1) return EIO; if (r != -2) break; } if (( (cd->status & (MCDDOOROPEN|MCDDSKCHNG)) || !(cd->status & MCDDSKIN) ) && major(dev) == CDEV_MAJOR && part == RAW_PART ) { cd->openflags |= (1<partflags[part] |= MCDREADRAW; return 0; } if (cd->status & MCDDOOROPEN) { printf("mcd%d: door is open\n", unit); return ENXIO; } if (!(cd->status & MCDDSKIN)) { printf("mcd%d: no CD inside\n", unit); return ENXIO; } if (cd->status & MCDDSKCHNG) { printf("mcd%d: CD not sensed\n", unit); return ENXIO; } if (mcdsize(dev) < 0) { if (major(dev) == CDEV_MAJOR && part == RAW_PART) { cd->openflags |= (1<partflags[part] |= MCDREADRAW; return 0; } printf("mcd%d: failed to get disk size\n",unit); return ENXIO; } else cd->flags |= MCDVALID; /* XXX get a default disklabel */ mcd_getdisklabel(unit); MCD_TRACE("open: partition=%d, disksize = %ld, blksize=%d\n", part, cd->disksize, cd->blksize); dev->si_bsize_phys = cd->blksize; if (part == RAW_PART || (part < cd->dlabel.d_npartitions && cd->dlabel.d_partitions[part].p_fstype != FS_UNUSED)) { cd->openflags |= (1<partflags[part] |= MCDREADRAW; (void) mcd_lock_door(unit, MCD_LK_LOCK); if (!(cd->flags & MCDVALID)) return ENXIO; return 0; } return ENXIO; } int mcdclose(dev_t dev, int flags, int fmt, struct proc *p) { int unit,part; struct mcd_data *cd; unit = mcd_unit(dev); if (unit >= NMCD) return ENXIO; cd = mcd_data + unit; part = mcd_part(dev); if (!(cd->flags & MCDINIT) || !(cd->openflags & (1<openflags &= ~(1<partflags[part] &= ~MCDREADRAW; return 0; } void mcdstrategy(struct buf *bp) { struct mcd_data *cd; int s; int unit = mcd_unit(bp->b_dev); cd = mcd_data + unit; /* test validity */ /*MCD_TRACE("strategy: buf=0x%lx, unit=%ld, block#=%ld bcount=%ld\n", bp,unit,bp->b_blkno,bp->b_bcount);*/ if (unit >= NMCD || bp->b_blkno < 0) { printf("mcdstrategy: unit = %d, blkno = %ld, bcount = %ld\n", unit, (long)bp->b_blkno, bp->b_bcount); printf("mcd: mcdstratregy failure"); bp->b_error = EINVAL; bp->b_flags |= B_ERROR; goto bad; } /* if device invalidated (e.g. media change, door open), error */ if (!(cd->flags & MCDVALID)) { MCD_TRACE("strategy: drive not valid\n"); bp->b_error = EIO; goto bad; } /* read only */ if (!(bp->b_flags & B_READ)) { bp->b_error = EROFS; goto bad; } /* no data to read */ if (bp->b_bcount == 0) goto done; /* for non raw access, check partition limits */ if (mcd_part(bp->b_dev) != RAW_PART) { if (!(cd->flags & MCDLABEL)) { bp->b_error = EIO; goto bad; } /* adjust transfer if necessary */ if (bounds_check_with_label(bp,&cd->dlabel,1) <= 0) { goto done; } } else { bp->b_pblkno = bp->b_blkno; bp->b_resid = 0; } /* queue it */ s = splbio(); bufqdisksort(&cd->head, bp); splx(s); /* now check whether we can perform processing */ mcd_start(unit); return; bad: bp->b_flags |= B_ERROR; done: bp->b_resid = bp->b_bcount; biodone(bp); return; } static void mcd_start(int unit) { struct mcd_data *cd = mcd_data + unit; struct partition *p; struct buf *bp; int s = splbio(); if (cd->flags & MCDMBXBSY) { splx(s); return; } bp = bufq_first(&cd->head); if (bp != 0) { /* block found to process, dequeue */ /*MCD_TRACE("mcd_start: found block bp=0x%x\n",bp,0,0,0);*/ bufq_remove(&cd->head, bp); splx(s); } else { /* nothing to do */ splx(s); return; } /* changed media? */ if (!(cd->flags & MCDVALID)) { MCD_TRACE("mcd_start: drive not valid\n"); return; } p = cd->dlabel.d_partitions + mcd_part(bp->b_dev); cd->flags |= MCDMBXBSY; if (cd->partflags[mcd_part(bp->b_dev)] & MCDREADRAW) cd->flags |= MCDREADRAW; cd->mbx.unit = unit; cd->mbx.port = cd->iobase; cd->mbx.retry = MCD_RETRYS; cd->mbx.bp = bp; cd->mbx.p_offset = p->p_offset; /* calling the read routine */ mcd_doread(MCD_S_BEGIN,&(cd->mbx)); /* triggers mcd_start, when successful finished */ return; } int mcdioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) { struct mcd_data *cd; int unit,part,retry,r; unit = mcd_unit(dev); part = mcd_part(dev); cd = mcd_data + unit; if (mcd_getstat(unit, 1) == -1) /* detect disk change too */ return EIO; MCD_TRACE("ioctl called 0x%lx\n", cmd); switch (cmd) { case CDIOCSETPATCH: case CDIOCGETVOL: case CDIOCSETVOL: case CDIOCSETMONO: case CDIOCSETSTERIO: case CDIOCSETMUTE: case CDIOCSETLEFT: case CDIOCSETRIGHT: return EINVAL; case CDIOCEJECT: return mcd_eject(unit); case CDIOCSETDEBUG: cd->debug = 1; return 0; case CDIOCCLRDEBUG: cd->debug = 0; return 0; case CDIOCRESET: return mcd_hard_reset(unit); case CDIOCALLOW: return mcd_lock_door(unit, MCD_LK_UNLOCK); case CDIOCPREVENT: return mcd_lock_door(unit, MCD_LK_LOCK); case CDIOCCLOSE: return mcd_inject(unit); } if (!(cd->flags & MCDVALID)) { if ( major(dev) != CDEV_MAJOR || part != RAW_PART || !(cd->openflags & (1<status & (MCDDSKCHNG|MCDDOOROPEN)) || !(cd->status & MCDDSKIN)) for (retry = 0; retry < DISK_SENSE_SECS * WAIT_FRAC; retry++) { (void) tsleep((caddr_t)cd, PSOCK | PCATCH, "mcdsn2", hz/WAIT_FRAC); if ((r = mcd_getstat(unit,1)) == -1) return EIO; if (r != -2) break; } if ( (cd->status & (MCDDOOROPEN|MCDDSKCHNG)) || !(cd->status & MCDDSKIN) || mcdsize(dev) < 0 ) return ENXIO; cd->flags |= MCDVALID; mcd_getdisklabel(unit); if (mcd_phys(dev)) cd->partflags[part] |= MCDREADRAW; (void) mcd_lock_door(unit, MCD_LK_LOCK); if (!(cd->flags & MCDVALID)) return ENXIO; } switch (cmd) { case DIOCGDINFO: *(struct disklabel *) addr = cd->dlabel; return 0; case DIOCGPART: ((struct partinfo *) addr)->disklab = &cd->dlabel; ((struct partinfo *) addr)->part = &cd->dlabel.d_partitions[mcd_part(dev)]; return 0; /* * a bit silly, but someone might want to test something on a * section of cdrom. */ case DIOCWDINFO: case DIOCSDINFO: if ((flags & FWRITE) == 0) return EBADF; else { return setdisklabel(&cd->dlabel, (struct disklabel *) addr, 0); } case DIOCWLABEL: return EBADF; case CDIOCPLAYTRACKS: return mcd_playtracks(unit, (struct ioc_play_track *) addr); case CDIOCPLAYBLOCKS: return mcd_playblocks(unit, (struct ioc_play_blocks *) addr); case CDIOCPLAYMSF: return mcd_playmsf(unit, (struct ioc_play_msf *) addr); case CDIOCREADSUBCHANNEL: return mcd_subchan(unit, (struct ioc_read_subchannel *) addr); case CDIOREADTOCHEADER: return mcd_toc_header(unit, (struct ioc_toc_header *) addr); case CDIOREADTOCENTRYS: return mcd_toc_entrys(unit, (struct ioc_read_toc_entry *) addr); case CDIOCRESUME: return mcd_resume(unit); case CDIOCPAUSE: return mcd_pause(unit); case CDIOCSTART: if (mcd_setmode(unit, MCD_MD_COOKED) != 0) return EIO; return 0; case CDIOCSTOP: return mcd_stop(unit); default: return ENOTTY; } /*NOTREACHED*/ } /* this could have been taken from scsi/cd.c, but it is not clear * whether the scsi cd driver is linked in */ static int mcd_getdisklabel(int unit) { struct mcd_data *cd = mcd_data + unit; if (cd->flags & MCDLABEL) return -1; bzero(&cd->dlabel,sizeof(struct disklabel)); /* filled with spaces first */ strncpy(cd->dlabel.d_typename," ", sizeof(cd->dlabel.d_typename)); strncpy(cd->dlabel.d_typename, cd->name, min(strlen(cd->name), sizeof(cd->dlabel.d_typename) - 1)); strncpy(cd->dlabel.d_packname,"unknown ", sizeof(cd->dlabel.d_packname)); cd->dlabel.d_secsize = cd->blksize; cd->dlabel.d_nsectors = 100; cd->dlabel.d_ntracks = 1; cd->dlabel.d_ncylinders = (cd->disksize/100)+1; cd->dlabel.d_secpercyl = 100; cd->dlabel.d_secperunit = cd->disksize; cd->dlabel.d_rpm = 300; cd->dlabel.d_interleave = 1; cd->dlabel.d_flags = D_REMOVABLE; cd->dlabel.d_npartitions= 1; cd->dlabel.d_partitions[0].p_offset = 0; cd->dlabel.d_partitions[0].p_size = cd->disksize; cd->dlabel.d_partitions[0].p_fstype = 9; cd->dlabel.d_magic = DISKMAGIC; cd->dlabel.d_magic2 = DISKMAGIC; cd->dlabel.d_checksum = dkcksum(&cd->dlabel); cd->flags |= MCDLABEL; return 0; } int mcdsize(dev_t dev) { int size; int unit = mcd_unit(dev); struct mcd_data *cd = mcd_data + unit; if (mcd_volinfo(unit) == 0) { cd->blksize = MCDBLK; size = msf2hsg(cd->volinfo.vol_msf, 0); cd->disksize = size * (MCDBLK/DEV_BSIZE); return 0; } return -1; } /*************************************************************** * lower level of driver starts here **************************************************************/ #ifdef NOTDEF static char irqs[] = { 0x00,0x00,0x10,0x20,0x00,0x30,0x00,0x00, 0x00,0x10,0x40,0x50,0x00,0x00,0x00,0x00 }; static char drqs[] = { 0x00,0x01,0x00,0x03,0x00,0x05,0x06,0x07, }; #endif #ifdef NOT_YET static void mcd_configure(struct mcd_data *cd) { outb(cd->iobase+mcd_config,cd->config); } #endif /* Wait for non-busy - return 0 on timeout */ static int twiddle_thumbs(int port, int unit, int count, char *whine) { int i; for (i = 0; i < count; i++) { if (!(inb(port+MCD_FLAGS) & MFL_STATUS_NOT_AVAIL)) return 1; } if (bootverbose) printf("mcd%d: timeout %s\n", unit, whine); return 0; } /* check to see if a Mitsumi CD-ROM is attached to the ISA bus */ int mcd_probe(struct isa_device *dev) { int port = dev->id_iobase; int unit = dev->id_unit; int i, j; unsigned char stbytes[3]; static int once; if (!once++) cdevsw_add(&mcd_cdevsw); mcd_data[unit].flags = MCDPROBING; #ifdef NOTDEF /* get irq/drq configuration word */ mcd_data[unit].config = irqs[dev->id_irq]; /* | drqs[dev->id_drq];*/ #else mcd_data[unit].config = 0; #endif /* send a reset */ outb(port+MCD_FLAGS, M_RESET); /* * delay awhile by getting any pending garbage (old data) and * throwing it away. */ for (i = 1000000; i != 0; i--) inb(port+MCD_FLAGS); /* Get status */ outb(port+MCD_DATA, MCD_CMDGETSTAT); if (!twiddle_thumbs(port, unit, 1000000, "getting status")) return 0; /* Timeout */ /* Get version information */ outb(port+MCD_DATA, MCD_CMDCONTINFO); for (j = 0; j < 3; j++) { if (!twiddle_thumbs(port, unit, 3000, "getting version info")) return 0; stbytes[j] = (inb(port+MCD_DATA) & 0xFF); } if (stbytes[1] == stbytes[2]) return 0; if (stbytes[2] >= 4 || stbytes[1] != 'M') { outb(port+MCD_CTRL, M_PICKLE); mcd_data[unit].flags |= MCDNEWMODEL; } mcd_data[unit].read_command = MCD_CMDSINGLESPEEDREAD; switch (stbytes[1]) { case 'M': if (stbytes[2] <= 2) { mcd_data[unit].type = MCD_TYPE_LU002S; mcd_data[unit].name = "Mitsumi LU002S"; } else if (stbytes[2] <= 5) { mcd_data[unit].type = MCD_TYPE_LU005S; mcd_data[unit].name = "Mitsumi LU005S"; } else { mcd_data[unit].type = MCD_TYPE_LU006S; mcd_data[unit].name = "Mitsumi LU006S"; } break; case 'F': mcd_data[unit].type = MCD_TYPE_FX001; mcd_data[unit].name = "Mitsumi FX001"; break; case 'D': mcd_data[unit].type = MCD_TYPE_FX001D; mcd_data[unit].name = "Mitsumi FX001D"; mcd_data[unit].read_command = MCD_CMDDOUBLESPEEDREAD; break; default: mcd_data[unit].type = MCD_TYPE_UNKNOWN; mcd_data[unit].name = "Mitsumi ???"; break; } printf("mcd%d: type %s, version info: %c %x\n", unit, mcd_data[unit].name, stbytes[1], stbytes[2]); return 4; } static int mcd_waitrdy(int port,int dly) { int i; /* wait until flag port senses status ready */ for (i=0; iiobase; /* wait data to become ready */ if (mcd_waitrdy(port,dly)<0) { printf("mcd%d: timeout getreply\n",unit); return -1; } /* get the data */ return inb(port+mcd_status) & 0xFF; } static int mcd_getstat(int unit,int sflg) { int i; struct mcd_data *cd = mcd_data + unit; int port = cd->iobase; /* get the status */ if (sflg) outb(port+mcd_command, MCD_CMDGETSTAT); i = mcd_getreply(unit,DELAY_GETREPLY); if (i<0 || (i & MCD_ST_CMDCHECK)) { cd->curr_mode = MCD_MD_UNKNOWN; return -1; } cd->status = i; if (mcd_setflags(unit,cd) < 0) return -2; return cd->status; } static int mcd_setflags(int unit, struct mcd_data *cd) { /* check flags */ if ( (cd->status & (MCDDSKCHNG|MCDDOOROPEN)) || !(cd->status & MCDDSKIN)) { MCD_TRACE("setflags: sensed DSKCHNG or DOOROPEN or !DSKIN\n"); mcd_soft_reset(unit); return -1; } if (cd->status & MCDAUDIOBSY) cd->audio_status = CD_AS_PLAY_IN_PROGRESS; else if (cd->audio_status == CD_AS_PLAY_IN_PROGRESS) cd->audio_status = CD_AS_PLAY_COMPLETED; return 0; } static int mcd_get(int unit, char *buf, int nmax) { int i,k; for (i=0; iflags & MCDVOLINFO) return 0; /*MCD_TRACE("mcd_volinfo: enter\n",0,0,0,0);*/ /* send volume info command */ if (mcd_send(unit,MCD_CMDGETVOLINFO,MCD_RETRYS) < 0) return EIO; /* get data */ if (mcd_get(unit,(char*) &cd->volinfo,sizeof(struct mcd_volinfo)) < 0) { printf("mcd%d: mcd_volinfo: error read data\n",unit); return EIO; } if (cd->volinfo.trk_low > 0 && cd->volinfo.trk_high >= cd->volinfo.trk_low ) { cd->flags |= MCDVOLINFO; /* volinfo is OK */ return 0; } return EINVAL; } static void mcdintr(unit) int unit; { MCD_TRACE("stray interrupt\n"); } /* state machine to process read requests * initialize with MCD_S_BEGIN: calculate sizes, and read status * MCD_S_WAITSTAT: wait for status reply, set mode * MCD_S_WAITMODE: waits for status reply from set mode, set read command * MCD_S_WAITREAD: wait for read ready, read data */ static struct mcd_mbx *mbxsave; static struct callout_handle tohandle = CALLOUT_HANDLE_INITIALIZER(&tohandle); static void mcd_timeout(void *arg) { mcd_doread((int)arg, mbxsave); } static void mcd_doread(int state, struct mcd_mbx *mbxin) { struct mcd_mbx *mbx = (state!=MCD_S_BEGIN) ? mbxsave : mbxin; int unit = mbx->unit; int port = mbx->port; int com_port = mbx->port + mcd_command; int data_port = mbx->port + mcd_rdata; struct buf *bp = mbx->bp; struct mcd_data *cd = mcd_data + unit; int rm,i,k; struct mcd_read2 rbuf; int blknum; caddr_t addr; loop: switch (state) { case MCD_S_BEGIN: mbx = mbxsave = mbxin; case MCD_S_BEGIN1: retry_status: /* get status */ outb(com_port, MCD_CMDGETSTAT); mbx->count = RDELAY_WAITSTAT; tohandle = timeout(mcd_timeout, (caddr_t)MCD_S_WAITSTAT,hz/100); /* XXX */ return; case MCD_S_WAITSTAT: untimeout(mcd_timeout,(caddr_t)MCD_S_WAITSTAT, tohandle); if (mbx->count-- >= 0) { if (inb(port+MCD_FLAGS) & MFL_STATUS_NOT_AVAIL) { timeout(mcd_timeout, (caddr_t)MCD_S_WAITSTAT,hz/100); /* XXX */ return; } cd->status = inb(port+mcd_status) & 0xFF; if (cd->status & MCD_ST_CMDCHECK) goto retry_status; if (mcd_setflags(unit,cd) < 0) goto changed; MCD_TRACE("got WAITSTAT delay=%d\n", RDELAY_WAITSTAT-mbx->count); /* reject, if audio active */ if (cd->status & MCDAUDIOBSY) { printf("mcd%d: audio is active\n",unit); goto readerr; } retry_mode: /* to check for raw/cooked mode */ if (cd->flags & MCDREADRAW) { rm = MCD_MD_RAW; mbx->sz = MCDRBLK; } else { rm = MCD_MD_COOKED; mbx->sz = cd->blksize; } if (rm == cd->curr_mode) goto modedone; mbx->count = RDELAY_WAITMODE; cd->curr_mode = MCD_MD_UNKNOWN; mbx->mode = rm; mcd_put(com_port, MCD_CMDSETMODE); mcd_put(com_port, rm); tohandle = timeout(mcd_timeout, (caddr_t)MCD_S_WAITMODE,hz/100); /* XXX */ return; } else { printf("mcd%d: timeout getstatus\n",unit); goto readerr; } case MCD_S_WAITMODE: untimeout(mcd_timeout,(caddr_t)MCD_S_WAITMODE, tohandle); if (mbx->count-- < 0) { printf("mcd%d: timeout set mode\n",unit); goto readerr; } if (inb(port+MCD_FLAGS) & MFL_STATUS_NOT_AVAIL) { tohandle = timeout(mcd_timeout, (caddr_t)MCD_S_WAITMODE,hz/100); return; } cd->status = inb(port+mcd_status) & 0xFF; if (cd->status & MCD_ST_CMDCHECK) { cd->curr_mode = MCD_MD_UNKNOWN; goto retry_mode; } if (mcd_setflags(unit,cd) < 0) goto changed; cd->curr_mode = mbx->mode; MCD_TRACE("got WAITMODE delay=%d\n", RDELAY_WAITMODE-mbx->count); modedone: /* for first block */ mbx->nblk = (bp->b_bcount + (mbx->sz-1)) / mbx->sz; mbx->skip = 0; nextblock: blknum = (bp->b_blkno / (mbx->sz/DEV_BSIZE)) + mbx->p_offset + mbx->skip/mbx->sz; MCD_TRACE("mcd_doread: read blknum=%d for bp=%p\n", blknum, bp); /* build parameter block */ hsg2msf(blknum,rbuf.start_msf); retry_read: /* send the read command */ disable_intr(); mcd_put(com_port,cd->read_command); mcd_put(com_port,rbuf.start_msf[0]); mcd_put(com_port,rbuf.start_msf[1]); mcd_put(com_port,rbuf.start_msf[2]); mcd_put(com_port,0); mcd_put(com_port,0); mcd_put(com_port,1); enable_intr(); /* Spin briefly (<= 2ms) to avoid missing next block */ for (i = 0; i < 20; i++) { k = inb(port+MCD_FLAGS); if (!(k & MFL_DATA_NOT_AVAIL)) goto got_it; DELAY(100); } mbx->count = RDELAY_WAITREAD; tohandle = timeout(mcd_timeout, (caddr_t)MCD_S_WAITREAD,hz/100); /* XXX */ return; case MCD_S_WAITREAD: untimeout(mcd_timeout,(caddr_t)MCD_S_WAITREAD, tohandle); if (mbx->count-- > 0) { k = inb(port+MCD_FLAGS); if (!(k & MFL_DATA_NOT_AVAIL)) { /* XXX */ MCD_TRACE("got data delay=%d\n", RDELAY_WAITREAD-mbx->count); got_it: /* data is ready */ addr = bp->b_data + mbx->skip; outb(port+mcd_ctl2,0x04); /* XXX */ for (i=0; isz; i++) *addr++ = inb(data_port); outb(port+mcd_ctl2,0x0c); /* XXX */ k = inb(port+MCD_FLAGS); /* If we still have some junk, read it too */ if (!(k & MFL_DATA_NOT_AVAIL)) { outb(port+mcd_ctl2,0x04); /* XXX */ (void)inb(data_port); (void)inb(data_port); outb(port+mcd_ctl2,0x0c); /* XXX */ } if (--mbx->nblk > 0) { mbx->skip += mbx->sz; goto nextblock; } /* return buffer */ bp->b_resid = 0; biodone(bp); cd->flags &= ~(MCDMBXBSY|MCDREADRAW); mcd_start(mbx->unit); return; } if (!(k & MFL_STATUS_NOT_AVAIL)) { cd->status = inb(port+mcd_status) & 0xFF; if (cd->status & MCD_ST_CMDCHECK) goto retry_read; if (mcd_setflags(unit,cd) < 0) goto changed; } tohandle = timeout(mcd_timeout, (caddr_t)MCD_S_WAITREAD,hz/100); /* XXX */ return; } else { printf("mcd%d: timeout read data\n",unit); goto readerr; } } readerr: if (mbx->retry-- > 0) { printf("mcd%d: retrying\n",unit); state = MCD_S_BEGIN1; goto loop; } harderr: /* invalidate the buffer */ bp->b_flags |= B_ERROR; bp->b_resid = bp->b_bcount; biodone(bp); cd->flags &= ~(MCDMBXBSY|MCDREADRAW); mcd_start(mbx->unit); return; changed: printf("mcd%d: media changed\n", unit); goto harderr; #ifdef NOTDEF printf("mcd%d: unit timeout, resetting\n",mbx->unit); outb(mbx->port+mcd_reset,MCD_CMDRESET); DELAY(300000); (void)mcd_getstat(mbx->unit,1); (void)mcd_getstat(mbx->unit,1); /*cd->status &= ~MCDDSKCHNG; */ cd->debug = 1; /* preventive set debug mode */ #endif } static int mcd_lock_door(int unit, int lock) { struct mcd_data *cd = mcd_data + unit; int port = cd->iobase; outb(port+mcd_command, MCD_CMDLOCKDRV); outb(port+mcd_command, lock); if (mcd_getstat(unit,0) == -1) return EIO; return 0; } static int mcd_close_tray(int unit) { struct mcd_data *cd = mcd_data + unit; int port = cd->iobase; int retry, r; if (mcd_getstat(unit,1) == -1) return EIO; if (cd->status & MCDDOOROPEN) { outb(port+mcd_command, MCD_CMDCLOSETRAY); for (retry = 0; retry < CLOSE_TRAY_SECS * WAIT_FRAC; retry++) { if (inb(port+MCD_FLAGS) & MFL_STATUS_NOT_AVAIL) (void) tsleep((caddr_t)cd, PSOCK | PCATCH, "mcdcls", hz/WAIT_FRAC); else { if ((r = mcd_getstat(unit,0)) == -1) return EIO; return 0; } } return ENXIO; } return 0; } static int mcd_eject(int unit) { struct mcd_data *cd = mcd_data + unit; int port = cd->iobase, r; if (mcd_getstat(unit,1) == -1) /* detect disk change too */ return EIO; if (cd->status & MCDDOOROPEN) return 0; if ((r = mcd_stop(unit)) == EIO) return r; outb(port+mcd_command, MCD_CMDEJECTDISK); if (mcd_getstat(unit,0) == -1) return EIO; return 0; } static int mcd_inject(int unit) { struct mcd_data *cd = mcd_data + unit; if (mcd_getstat(unit,1) == -1) /* detect disk change too */ return EIO; if (cd->status & MCDDOOROPEN) return mcd_close_tray(unit); return 0; } static int mcd_hard_reset(int unit) { struct mcd_data *cd = mcd_data + unit; int port = cd->iobase; outb(port+mcd_reset,MCD_CMDRESET); cd->curr_mode = MCD_MD_UNKNOWN; cd->audio_status = CD_AS_AUDIO_INVALID; return 0; } static void mcd_soft_reset(int unit) { struct mcd_data *cd = mcd_data + unit; int i; cd->flags &= (MCDINIT|MCDPROBING|MCDNEWMODEL); cd->curr_mode = MCD_MD_UNKNOWN; for (i=0; ipartflags[i] = 0; cd->audio_status = CD_AS_AUDIO_INVALID; } static int mcd_setmode(int unit, int mode) { struct mcd_data *cd = mcd_data + unit; int port = cd->iobase; int retry, st; if (cd->curr_mode == mode) return 0; if (cd->debug) printf("mcd%d: setting mode to %d\n", unit, mode); for(retry=0; retrycurr_mode = MCD_MD_UNKNOWN; outb(port+mcd_command, MCD_CMDSETMODE); outb(port+mcd_command, mode); if ((st = mcd_getstat(unit, 0)) >= 0) { cd->curr_mode = mode; return 0; } if (st == -2) { printf("mcd%d: media changed\n", unit); break; } } return -1; } static int mcd_toc_header(int unit, struct ioc_toc_header *th) { struct mcd_data *cd = mcd_data + unit; int r; if ((r = mcd_volinfo(unit)) != 0) return r; th->starting_track = bcd2bin(cd->volinfo.trk_low); th->ending_track = bcd2bin(cd->volinfo.trk_high); th->len = 2 * sizeof(u_char) /* start & end tracks */ + (th->ending_track + 1 - th->starting_track + 1) * sizeof(struct cd_toc_entry); return 0; } static int mcd_read_toc(int unit) { struct mcd_data *cd = mcd_data + unit; struct ioc_toc_header th; struct mcd_qchninfo q; int rc, trk, idx, retry; /* Only read TOC if needed */ if (cd->flags & MCDTOC) return 0; if (cd->debug) printf("mcd%d: reading toc header\n", unit); if ((rc = mcd_toc_header(unit, &th)) != 0) return rc; if (mcd_send(unit, MCD_CMDSTOPAUDIO, MCD_RETRYS) < 0) return EIO; if (mcd_setmode(unit, MCD_MD_TOC) != 0) return EIO; if (cd->debug) printf("mcd%d: get_toc reading qchannel info\n",unit); for(trk=th.starting_track; trk<=th.ending_track; trk++) cd->toc[trk].idx_no = 0; trk = th.ending_track - th.starting_track + 1; for(retry=0; retry<600 && trk>0; retry++) { if (mcd_getqchan(unit, &q) < 0) break; idx = bcd2bin(q.idx_no); if (idx>=th.starting_track && idx<=th.ending_track && q.trk_no==0) { if (cd->toc[idx].idx_no == 0) { cd->toc[idx] = q; trk--; } } } if (mcd_setmode(unit, MCD_MD_COOKED) != 0) return EIO; if (trk != 0) return ENXIO; /* add a fake last+1 */ idx = th.ending_track + 1; cd->toc[idx].control = cd->toc[idx-1].control; cd->toc[idx].addr_type = cd->toc[idx-1].addr_type; cd->toc[idx].trk_no = 0; cd->toc[idx].idx_no = MCD_LASTPLUS1; cd->toc[idx].hd_pos_msf[0] = cd->volinfo.vol_msf[0]; cd->toc[idx].hd_pos_msf[1] = cd->volinfo.vol_msf[1]; cd->toc[idx].hd_pos_msf[2] = cd->volinfo.vol_msf[2]; if (cd->debug) { int i; for (i = th.starting_track; i <= idx; i++) printf("mcd%d: trk %d idx %d pos %d %d %d\n", unit, i, cd->toc[i].idx_no > 0x99 ? cd->toc[i].idx_no : bcd2bin(cd->toc[i].idx_no), bcd2bin(cd->toc[i].hd_pos_msf[0]), bcd2bin(cd->toc[i].hd_pos_msf[1]), bcd2bin(cd->toc[i].hd_pos_msf[2])); } cd->flags |= MCDTOC; return 0; } #if 0 static int mcd_toc_entry(int unit, struct ioc_read_toc_single_entry *te) { struct mcd_data *cd = mcd_data + unit; struct ioc_toc_header th; int rc, trk; if (te->address_format != CD_MSF_FORMAT && te->address_format != CD_LBA_FORMAT) return EINVAL; /* Copy the toc header */ if ((rc = mcd_toc_header(unit, &th)) != 0) return rc; /* verify starting track */ trk = te->track; if (trk == 0) trk = th.starting_track; else if (trk == MCD_LASTPLUS1) trk = th.ending_track + 1; else if (trk < th.starting_track || trk > th.ending_track + 1) return EINVAL; /* Make sure we have a valid toc */ if ((rc=mcd_read_toc(unit)) != 0) return rc; /* Copy the TOC data. */ if (cd->toc[trk].idx_no == 0) return EIO; te->entry.control = cd->toc[trk].control; te->entry.addr_type = cd->toc[trk].addr_type; te->entry.track = cd->toc[trk].idx_no > 0x99 ? cd->toc[trk].idx_no : bcd2bin(cd->toc[trk].idx_no); switch (te->address_format) { case CD_MSF_FORMAT: te->entry.addr.msf.unused = 0; te->entry.addr.msf.minute = bcd2bin(cd->toc[trk].hd_pos_msf[0]); te->entry.addr.msf.second = bcd2bin(cd->toc[trk].hd_pos_msf[1]); te->entry.addr.msf.frame = bcd2bin(cd->toc[trk].hd_pos_msf[2]); break; case CD_LBA_FORMAT: te->entry.addr.lba = htonl(msf2hsg(cd->toc[trk].hd_pos_msf, 0)); break; } return 0; } #endif static int mcd_toc_entrys(int unit, struct ioc_read_toc_entry *te) { struct mcd_data *cd = mcd_data + unit; struct cd_toc_entry entries[MCD_MAXTOCS]; struct ioc_toc_header th; int rc, n, trk, len; if ( te->data_len < sizeof(entries[0]) || (te->data_len % sizeof(entries[0])) != 0 || (te->address_format != CD_MSF_FORMAT && te->address_format != CD_LBA_FORMAT) ) return EINVAL; /* Copy the toc header */ if ((rc = mcd_toc_header(unit, &th)) != 0) return rc; /* verify starting track */ trk = te->starting_track; if (trk == 0) trk = th.starting_track; else if (trk == MCD_LASTPLUS1) trk = th.ending_track + 1; else if (trk < th.starting_track || trk > th.ending_track + 1) return EINVAL; len = ((th.ending_track + 1 - trk) + 1) * sizeof(entries[0]); if (te->data_len < len) len = te->data_len; if (len > sizeof(entries)) return EINVAL; /* Make sure we have a valid toc */ if ((rc=mcd_read_toc(unit)) != 0) return rc; /* Copy the TOC data. */ for (n = 0; len > 0 && trk <= th.ending_track + 1; trk++) { if (cd->toc[trk].idx_no == 0) continue; entries[n].control = cd->toc[trk].control; entries[n].addr_type = cd->toc[trk].addr_type; entries[n].track = cd->toc[trk].idx_no > 0x99 ? cd->toc[trk].idx_no : bcd2bin(cd->toc[trk].idx_no); switch (te->address_format) { case CD_MSF_FORMAT: entries[n].addr.msf.unused = 0; entries[n].addr.msf.minute = bcd2bin(cd->toc[trk].hd_pos_msf[0]); entries[n].addr.msf.second = bcd2bin(cd->toc[trk].hd_pos_msf[1]); entries[n].addr.msf.frame = bcd2bin(cd->toc[trk].hd_pos_msf[2]); break; case CD_LBA_FORMAT: entries[n].addr.lba = htonl(msf2hsg(cd->toc[trk].hd_pos_msf, 0)); break; } len -= sizeof(struct cd_toc_entry); n++; } /* copy the data back */ return copyout(entries, te->data, n * sizeof(struct cd_toc_entry)); } static int mcd_stop(int unit) { struct mcd_data *cd = mcd_data + unit; /* Verify current status */ if (cd->audio_status != CD_AS_PLAY_IN_PROGRESS && cd->audio_status != CD_AS_PLAY_PAUSED && cd->audio_status != CD_AS_PLAY_COMPLETED) { if (cd->debug) printf("mcd%d: stop attempted when not playing, audio status %d\n", unit, cd->audio_status); return EINVAL; } if (cd->audio_status == CD_AS_PLAY_IN_PROGRESS) if (mcd_send(unit, MCD_CMDSTOPAUDIO, MCD_RETRYS) < 0) return EIO; cd->audio_status = CD_AS_PLAY_COMPLETED; return 0; } static int mcd_getqchan(int unit, struct mcd_qchninfo *q) { struct mcd_data *cd = mcd_data + unit; if (mcd_send(unit, MCD_CMDGETQCHN, MCD_RETRYS) < 0) return -1; if (mcd_get(unit, (char *) q, sizeof(struct mcd_qchninfo)) < 0) return -1; if (cd->debug) { printf("mcd%d: getqchan control=0x%x addr_type=0x%x trk=%d ind=%d ttm=%d:%d.%d dtm=%d:%d.%d\n", unit, q->control, q->addr_type, bcd2bin(q->trk_no), bcd2bin(q->idx_no), bcd2bin(q->trk_size_msf[0]), bcd2bin(q->trk_size_msf[1]), bcd2bin(q->trk_size_msf[2]), bcd2bin(q->hd_pos_msf[0]), bcd2bin(q->hd_pos_msf[1]), bcd2bin(q->hd_pos_msf[2])); } return 0; } static int mcd_subchan(int unit, struct ioc_read_subchannel *sc) { struct mcd_data *cd = mcd_data + unit; struct mcd_qchninfo q; struct cd_sub_channel_info data; int lba; if (cd->debug) printf("mcd%d: subchan af=%d, df=%d\n", unit, sc->address_format, sc->data_format); if (sc->address_format != CD_MSF_FORMAT && sc->address_format != CD_LBA_FORMAT) return EINVAL; if (sc->data_format != CD_CURRENT_POSITION && sc->data_format != CD_MEDIA_CATALOG) return EINVAL; if (mcd_setmode(unit, MCD_MD_COOKED) != 0) return EIO; if (mcd_getqchan(unit, &q) < 0) return EIO; data.header.audio_status = cd->audio_status; data.what.position.data_format = sc->data_format; switch (sc->data_format) { case CD_MEDIA_CATALOG: data.what.media_catalog.mc_valid = 1; data.what.media_catalog.mc_number[0] = '\0'; break; case CD_CURRENT_POSITION: data.what.position.control = q.control; data.what.position.addr_type = q.addr_type; data.what.position.track_number = bcd2bin(q.trk_no); data.what.position.index_number = bcd2bin(q.idx_no); switch (sc->address_format) { case CD_MSF_FORMAT: data.what.position.reladdr.msf.unused = 0; data.what.position.reladdr.msf.minute = bcd2bin(q.trk_size_msf[0]); data.what.position.reladdr.msf.second = bcd2bin(q.trk_size_msf[1]); data.what.position.reladdr.msf.frame = bcd2bin(q.trk_size_msf[2]); data.what.position.absaddr.msf.unused = 0; data.what.position.absaddr.msf.minute = bcd2bin(q.hd_pos_msf[0]); data.what.position.absaddr.msf.second = bcd2bin(q.hd_pos_msf[1]); data.what.position.absaddr.msf.frame = bcd2bin(q.hd_pos_msf[2]); break; case CD_LBA_FORMAT: lba = msf2hsg(q.trk_size_msf, 1); /* * Pre-gap has index number of 0, and decreasing MSF * address. Must be converted to negative LBA, per * SCSI spec. */ if (data.what.position.index_number == 0) lba = -lba; data.what.position.reladdr.lba = htonl(lba); data.what.position.absaddr.lba = htonl(msf2hsg(q.hd_pos_msf, 0)); break; } break; } return copyout(&data, sc->data, min(sizeof(struct cd_sub_channel_info), sc->data_len)); } static int mcd_playmsf(int unit, struct ioc_play_msf *p) { struct mcd_data *cd = mcd_data + unit; struct mcd_read2 pb; if (cd->debug) printf("mcd%d: playmsf: from %d:%d.%d to %d:%d.%d\n", unit, p->start_m, p->start_s, p->start_f, p->end_m, p->end_s, p->end_f); if ((p->start_m * 60 * 75 + p->start_s * 75 + p->start_f) >= (p->end_m * 60 * 75 + p->end_s * 75 + p->end_f) || (p->end_m * 60 * 75 + p->end_s * 75 + p->end_f) > M_msf(cd->volinfo.vol_msf) * 60 * 75 + S_msf(cd->volinfo.vol_msf) * 75 + F_msf(cd->volinfo.vol_msf)) return EINVAL; pb.start_msf[0] = bin2bcd(p->start_m); pb.start_msf[1] = bin2bcd(p->start_s); pb.start_msf[2] = bin2bcd(p->start_f); pb.end_msf[0] = bin2bcd(p->end_m); pb.end_msf[1] = bin2bcd(p->end_s); pb.end_msf[2] = bin2bcd(p->end_f); if (mcd_setmode(unit, MCD_MD_COOKED) != 0) return EIO; return mcd_play(unit, &pb); } static int mcd_playtracks(int unit, struct ioc_play_track *pt) { struct mcd_data *cd = mcd_data + unit; struct mcd_read2 pb; int a = pt->start_track; int z = pt->end_track; int rc, i; if ((rc = mcd_read_toc(unit)) != 0) return rc; if (cd->debug) printf("mcd%d: playtracks from %d:%d to %d:%d\n", unit, a, pt->start_index, z, pt->end_index); if ( a < bcd2bin(cd->volinfo.trk_low) || a > bcd2bin(cd->volinfo.trk_high) || a > z || z < bcd2bin(cd->volinfo.trk_low) || z > bcd2bin(cd->volinfo.trk_high)) return EINVAL; for (i = 0; i < 3; i++) { pb.start_msf[i] = cd->toc[a].hd_pos_msf[i]; pb.end_msf[i] = cd->toc[z+1].hd_pos_msf[i]; } if (mcd_setmode(unit, MCD_MD_COOKED) != 0) return EIO; return mcd_play(unit, &pb); } static int mcd_playblocks(int unit, struct ioc_play_blocks *p) { struct mcd_data *cd = mcd_data + unit; struct mcd_read2 pb; if (cd->debug) printf("mcd%d: playblocks: blkno %d length %d\n", unit, p->blk, p->len); if (p->blk > cd->disksize || p->len > cd->disksize || p->blk < 0 || p->len < 0 || (p->blk + p->len) > cd->disksize) return EINVAL; hsg2msf(p->blk, pb.start_msf); hsg2msf(p->blk + p->len, pb.end_msf); if (mcd_setmode(unit, MCD_MD_COOKED) != 0) return EIO; return mcd_play(unit, &pb); } static int mcd_play(int unit, struct mcd_read2 *pb) { struct mcd_data *cd = mcd_data + unit; int com_port = cd->iobase + mcd_command; int retry, st = -1, status; cd->lastpb = *pb; for(retry=0; retrystart_msf[0]); outb(com_port, pb->start_msf[1]); outb(com_port, pb->start_msf[2]); outb(com_port, pb->end_msf[0]); outb(com_port, pb->end_msf[1]); outb(com_port, pb->end_msf[2]); enable_intr(); status=mcd_getstat(unit, 0); if (status == -1) continue; else if (status != -2) st = 0; break; } if (status == -2) { printf("mcd%d: media changed\n", unit); return ENXIO; } if (cd->debug) printf("mcd%d: mcd_play retry=%d, status=0x%02x\n", unit, retry, status); if (st < 0) return ENXIO; cd->audio_status = CD_AS_PLAY_IN_PROGRESS; return 0; } static int mcd_pause(int unit) { struct mcd_data *cd = mcd_data + unit; struct mcd_qchninfo q; int rc; /* Verify current status */ if (cd->audio_status != CD_AS_PLAY_IN_PROGRESS && cd->audio_status != CD_AS_PLAY_PAUSED) { if (cd->debug) printf("mcd%d: pause attempted when not playing, audio status %d\n", unit, cd->audio_status); return EINVAL; } /* Get the current position */ if (mcd_getqchan(unit, &q) < 0) return EIO; /* Copy it into lastpb */ cd->lastpb.start_msf[0] = q.hd_pos_msf[0]; cd->lastpb.start_msf[1] = q.hd_pos_msf[1]; cd->lastpb.start_msf[2] = q.hd_pos_msf[2]; /* Stop playing */ if ((rc=mcd_stop(unit)) != 0) return rc; /* Set the proper status and exit */ cd->audio_status = CD_AS_PLAY_PAUSED; return 0; } static int mcd_resume(int unit) { struct mcd_data *cd = mcd_data + unit; if (cd->audio_status != CD_AS_PLAY_PAUSED) return EINVAL; return mcd_play(unit, &cd->lastpb); } - -#endif /* NMCD > 0 */ diff --git a/sys/i386/isa/mse.c b/sys/i386/isa/mse.c index 332e020333d8..5dd8376702c8 100644 --- a/sys/i386/isa/mse.c +++ b/sys/i386/isa/mse.c @@ -1,771 +1,768 @@ /* * Copyright 1992 by the University of Guelph * * Permission to use, copy and modify this * software and its documentation for any purpose and without * fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright * notice and this permission notice appear in supporting * documentation. * University of Guelph makes no representations about the suitability of * this software for any purpose. It is provided "as is" * without express or implied warranty. * * $FreeBSD$ */ /* * Driver for the Logitech and ATI Inport Bus mice for use with 386bsd and * the X386 port, courtesy of * Rick Macklem, rick@snowhite.cis.uoguelph.ca * Caveats: The driver currently uses spltty(), but doesn't use any * generic tty code. It could use splmse() (that only masks off the * bus mouse interrupt, but that would require hacking in i386/isa/icu.s. * (This may be worth the effort, since the Logitech generates 30/60 * interrupts/sec continuously while it is open.) * NB: The ATI has NOT been tested yet! */ /* * Modification history: * Sep 6, 1994 -- Lars Fredriksen(fredriks@mcs.com) * improved probe based on input from Logitech. * * Oct 19, 1992 -- E. Stark (stark@cs.sunysb.edu) * fixes to make it work with Microsoft InPort busmouse * * Jan, 1993 -- E. Stark (stark@cs.sunysb.edu) * added patches for new "select" interface * * May 4, 1993 -- E. Stark (stark@cs.sunysb.edu) * changed position of some spl()'s in mseread * * October 8, 1993 -- E. Stark (stark@cs.sunysb.edu) * limit maximum negative x/y value to -127 to work around XFree problem * that causes spurious button pushes. */ #include "mse.h" -#if NMSE > 0 #include #include #include #include #include #include #include #include #include #include /* driver configuration flags (config) */ #define MSE_CONFIG_ACCEL 0x00f0 /* acceleration factor */ #define MSE_CONFIG_FLAGS (MSE_CONFIG_ACCEL) static int mseprobe(struct isa_device *); static int mseattach(struct isa_device *); struct isa_driver msedriver = { mseprobe, mseattach, "mse" }; static d_open_t mseopen; static d_close_t mseclose; static d_read_t mseread; static d_ioctl_t mseioctl; static d_poll_t msepoll; #define CDEV_MAJOR 27 static struct cdevsw mse_cdevsw = { /* open */ mseopen, /* close */ mseclose, /* read */ mseread, /* write */ nowrite, /* ioctl */ mseioctl, /* poll */ msepoll, /* mmap */ nommap, /* strategy */ nostrategy, /* name */ "mse", /* maj */ CDEV_MAJOR, /* dump */ nodump, /* psize */ nopsize, /* flags */ 0, /* bmaj */ -1 }; static ointhand2_t mseintr; /* * Software control structure for mouse. The sc_enablemouse(), * sc_disablemouse() and sc_getmouse() routines must be called spl'd(). */ static struct mse_softc { int sc_flags; int sc_mousetype; struct selinfo sc_selp; u_int sc_port; void (*sc_enablemouse) __P((u_int port)); void (*sc_disablemouse) __P((u_int port)); void (*sc_getmouse) __P((u_int port, int *dx, int *dy, int *but)); int sc_deltax; int sc_deltay; int sc_obuttons; int sc_buttons; int sc_bytesread; u_char sc_bytes[MOUSE_SYS_PACKETSIZE]; mousehw_t hw; mousemode_t mode; mousestatus_t status; } mse_sc[NMSE]; /* Flags */ #define MSESC_OPEN 0x1 #define MSESC_WANT 0x2 /* and Mouse Types */ #define MSE_NONE 0 /* don't move this! */ #define MSE_LOGITECH 0x1 #define MSE_ATIINPORT 0x2 #define MSE_LOGI_SIG 0xA5 #define MSE_PORTA 0 #define MSE_PORTB 1 #define MSE_PORTC 2 #define MSE_PORTD 3 #define MSE_UNIT(dev) (minor(dev) >> 1) #define MSE_NBLOCKIO(dev) (minor(dev) & 0x1) /* * Logitech bus mouse definitions */ #define MSE_SETUP 0x91 /* What does this mean? */ /* The definition for the control port */ /* is as follows: */ /* D7 = Mode set flag (1 = active) */ /* D6,D5 = Mode selection (port A) */ /* 00 = Mode 0 = Basic I/O */ /* 01 = Mode 1 = Strobed I/O */ /* 10 = Mode 2 = Bi-dir bus */ /* D4 = Port A direction (1 = input)*/ /* D3 = Port C (upper 4 bits) */ /* direction. (1 = input) */ /* D2 = Mode selection (port B & C) */ /* 0 = Mode 0 = Basic I/O */ /* 1 = Mode 1 = Strobed I/O */ /* D1 = Port B direction (1 = input)*/ /* D0 = Port C (lower 4 bits) */ /* direction. (1 = input) */ /* So 91 means Basic I/O on all 3 ports,*/ /* Port A is an input port, B is an */ /* output port, C is split with upper */ /* 4 bits being an output port and lower*/ /* 4 bits an input port, and enable the */ /* sucker. */ /* Courtesy Intel 8255 databook. Lars */ #define MSE_HOLD 0x80 #define MSE_RXLOW 0x00 #define MSE_RXHIGH 0x20 #define MSE_RYLOW 0x40 #define MSE_RYHIGH 0x60 #define MSE_DISINTR 0x10 #define MSE_INTREN 0x00 static int mse_probelogi __P((struct isa_device *idp)); static void mse_disablelogi __P((u_int port)); static void mse_getlogi __P((u_int port, int *dx, int *dy, int *but)); static void mse_enablelogi __P((u_int port)); /* * ATI Inport mouse definitions */ #define MSE_INPORT_RESET 0x80 #define MSE_INPORT_STATUS 0x00 #define MSE_INPORT_DX 0x01 #define MSE_INPORT_DY 0x02 #define MSE_INPORT_MODE 0x07 #define MSE_INPORT_HOLD 0x20 #define MSE_INPORT_INTREN 0x09 static int mse_probeati __P((struct isa_device *idp)); static void mse_enableati __P((u_int port)); static void mse_disableati __P((u_int port)); static void mse_getati __P((u_int port, int *dx, int *dy, int *but)); #define MSEPRI (PZERO + 3) /* * Table of mouse types. * Keep the Logitech last, since I haven't figured out how to probe it * properly yet. (Someday I'll have the documentation.) */ static struct mse_types { int m_type; /* Type of bus mouse */ int (*m_probe) __P((struct isa_device *idp)); /* Probe routine to test for it */ void (*m_enable) __P((u_int port)); /* Start routine */ void (*m_disable) __P((u_int port)); /* Disable interrupts routine */ void (*m_get) __P((u_int port, int *dx, int *dy, int *but)); /* and get mouse status */ mousehw_t m_hw; /* buttons iftype type model hwid */ mousemode_t m_mode; /* proto rate res accel level size mask */ } mse_types[] = { { MSE_ATIINPORT, mse_probeati, mse_enableati, mse_disableati, mse_getati, { 2, MOUSE_IF_INPORT, MOUSE_MOUSE, MOUSE_MODEL_GENERIC, 0, }, { MOUSE_PROTO_INPORT, -1, -1, 0, 0, MOUSE_MSC_PACKETSIZE, { MOUSE_MSC_SYNCMASK, MOUSE_MSC_SYNC, }, }, }, { MSE_LOGITECH, mse_probelogi, mse_enablelogi, mse_disablelogi, mse_getlogi, { 2, MOUSE_IF_BUS, MOUSE_MOUSE, MOUSE_MODEL_GENERIC, 0, }, { MOUSE_PROTO_BUS, -1, -1, 0, 0, MOUSE_MSC_PACKETSIZE, { MOUSE_MSC_SYNCMASK, MOUSE_MSC_SYNC, }, }, }, { 0, }, }; int mseprobe(idp) register struct isa_device *idp; { register struct mse_softc *sc = &mse_sc[idp->id_unit]; register int i; /* * Check for each mouse type in the table. */ i = 0; while (mse_types[i].m_type) { if ((*mse_types[i].m_probe)(idp)) { sc->sc_mousetype = mse_types[i].m_type; sc->sc_enablemouse = mse_types[i].m_enable; sc->sc_disablemouse = mse_types[i].m_disable; sc->sc_getmouse = mse_types[i].m_get; sc->hw = mse_types[i].m_hw; sc->mode = mse_types[i].m_mode; return (1); } i++; } return (0); } int mseattach(idp) struct isa_device *idp; { int unit = idp->id_unit; struct mse_softc *sc = &mse_sc[unit]; idp->id_ointr = mseintr; sc->sc_port = idp->id_iobase; sc->mode.accelfactor = (idp->id_flags & MSE_CONFIG_ACCEL) >> 4; make_dev(&mse_cdevsw, unit << 1, 0, 0, 0600, "mse%d", unit); make_dev(&mse_cdevsw, (unit<<1)+1, 0, 0, 0600, "nmse%d", unit); return (1); } /* * Exclusive open the mouse, initialize it and enable interrupts. */ static int mseopen(dev, flags, fmt, p) dev_t dev; int flags; int fmt; struct proc *p; { register struct mse_softc *sc; int s; if (MSE_UNIT(dev) >= NMSE) return (ENXIO); sc = &mse_sc[MSE_UNIT(dev)]; if (sc->sc_mousetype == MSE_NONE) return (ENXIO); if (sc->sc_flags & MSESC_OPEN) return (EBUSY); sc->sc_flags |= MSESC_OPEN; sc->sc_obuttons = sc->sc_buttons = MOUSE_MSC_BUTTONS; sc->sc_deltax = sc->sc_deltay = 0; sc->sc_bytesread = sc->mode.packetsize = MOUSE_MSC_PACKETSIZE; sc->mode.level = 0; sc->status.flags = 0; sc->status.button = sc->status.obutton = 0; sc->status.dx = sc->status.dy = sc->status.dz = 0; /* * Initialize mouse interface and enable interrupts. */ s = spltty(); (*sc->sc_enablemouse)(sc->sc_port); splx(s); return (0); } /* * mseclose: just turn off mouse innterrupts. */ static int mseclose(dev, flags, fmt, p) dev_t dev; int flags; int fmt; struct proc *p; { struct mse_softc *sc = &mse_sc[MSE_UNIT(dev)]; int s; s = spltty(); (*sc->sc_disablemouse)(sc->sc_port); sc->sc_flags &= ~MSESC_OPEN; splx(s); return(0); } /* * mseread: return mouse info using the MSC serial protocol, but without * using bytes 4 and 5. * (Yes this is cheesy, but it makes the X386 server happy, so...) */ static int mseread(dev, uio, ioflag) dev_t dev; struct uio *uio; int ioflag; { register struct mse_softc *sc = &mse_sc[MSE_UNIT(dev)]; int xfer, s, error; /* * If there are no protocol bytes to be read, set up a new protocol * packet. */ s = spltty(); /* XXX Should be its own spl, but where is imlXX() */ if (sc->sc_bytesread >= sc->mode.packetsize) { while (sc->sc_deltax == 0 && sc->sc_deltay == 0 && (sc->sc_obuttons ^ sc->sc_buttons) == 0) { if (MSE_NBLOCKIO(dev)) { splx(s); return (0); } sc->sc_flags |= MSESC_WANT; error = tsleep((caddr_t)sc, MSEPRI | PCATCH, "mseread", 0); if (error) { splx(s); return (error); } } /* * Generate protocol bytes. * For some reason X386 expects 5 bytes but never uses * the fourth or fifth? */ sc->sc_bytes[0] = sc->mode.syncmask[1] | (sc->sc_buttons & ~sc->mode.syncmask[0]); if (sc->sc_deltax > 127) sc->sc_deltax = 127; if (sc->sc_deltax < -127) sc->sc_deltax = -127; sc->sc_deltay = -sc->sc_deltay; /* Otherwise mousey goes wrong way */ if (sc->sc_deltay > 127) sc->sc_deltay = 127; if (sc->sc_deltay < -127) sc->sc_deltay = -127; sc->sc_bytes[1] = sc->sc_deltax; sc->sc_bytes[2] = sc->sc_deltay; sc->sc_bytes[3] = sc->sc_bytes[4] = 0; sc->sc_bytes[5] = sc->sc_bytes[6] = 0; sc->sc_bytes[7] = MOUSE_SYS_EXTBUTTONS; sc->sc_obuttons = sc->sc_buttons; sc->sc_deltax = sc->sc_deltay = 0; sc->sc_bytesread = 0; } splx(s); xfer = min(uio->uio_resid, sc->mode.packetsize - sc->sc_bytesread); error = uiomove(&sc->sc_bytes[sc->sc_bytesread], xfer, uio); if (error) return (error); sc->sc_bytesread += xfer; return(0); } /* * mseioctl: process ioctl commands. */ static int mseioctl(dev, cmd, addr, flag, p) dev_t dev; u_long cmd; caddr_t addr; int flag; struct proc *p; { register struct mse_softc *sc = &mse_sc[MSE_UNIT(dev)]; mousestatus_t status; int err = 0; int s; switch (cmd) { case MOUSE_GETHWINFO: s = spltty(); *(mousehw_t *)addr = sc->hw; if (sc->mode.level == 0) ((mousehw_t *)addr)->model = MOUSE_MODEL_GENERIC; splx(s); break; case MOUSE_GETMODE: s = spltty(); *(mousemode_t *)addr = sc->mode; switch (sc->mode.level) { case 0: break; case 1: ((mousemode_t *)addr)->protocol = MOUSE_PROTO_SYSMOUSE; ((mousemode_t *)addr)->syncmask[0] = MOUSE_SYS_SYNCMASK; ((mousemode_t *)addr)->syncmask[1] = MOUSE_SYS_SYNC; break; } splx(s); break; case MOUSE_SETMODE: switch (((mousemode_t *)addr)->level) { case 0: case 1: break; default: return (EINVAL); } if (((mousemode_t *)addr)->accelfactor < -1) return (EINVAL); else if (((mousemode_t *)addr)->accelfactor >= 0) sc->mode.accelfactor = ((mousemode_t *)addr)->accelfactor; sc->mode.level = ((mousemode_t *)addr)->level; switch (sc->mode.level) { case 0: sc->sc_bytesread = sc->mode.packetsize = MOUSE_MSC_PACKETSIZE; break; case 1: sc->sc_bytesread = sc->mode.packetsize = MOUSE_SYS_PACKETSIZE; break; } break; case MOUSE_GETLEVEL: *(int *)addr = sc->mode.level; break; case MOUSE_SETLEVEL: switch (*(int *)addr) { case 0: sc->mode.level = *(int *)addr; sc->sc_bytesread = sc->mode.packetsize = MOUSE_MSC_PACKETSIZE; break; case 1: sc->mode.level = *(int *)addr; sc->sc_bytesread = sc->mode.packetsize = MOUSE_SYS_PACKETSIZE; break; default: return (EINVAL); } break; case MOUSE_GETSTATUS: s = spltty(); status = sc->status; sc->status.flags = 0; sc->status.obutton = sc->status.button; sc->status.button = 0; sc->status.dx = 0; sc->status.dy = 0; sc->status.dz = 0; splx(s); *(mousestatus_t *)addr = status; break; case MOUSE_READSTATE: case MOUSE_READDATA: return (ENODEV); #if (defined(MOUSE_GETVARS)) case MOUSE_GETVARS: case MOUSE_SETVARS: return (ENODEV); #endif default: return (ENOTTY); } return (err); } /* * msepoll: check for mouse input to be processed. */ static int msepoll(dev, events, p) dev_t dev; int events; struct proc *p; { register struct mse_softc *sc = &mse_sc[MSE_UNIT(dev)]; int s; int revents = 0; s = spltty(); if (events & (POLLIN | POLLRDNORM)) { if (sc->sc_bytesread != sc->mode.packetsize || sc->sc_deltax != 0 || sc->sc_deltay != 0 || (sc->sc_obuttons ^ sc->sc_buttons) != 0) revents |= events & (POLLIN | POLLRDNORM); else { /* * Since this is an exclusive open device, any previous * proc pointer is trash now, so we can just assign it. */ selrecord(p, &sc->sc_selp); } } splx(s); return (revents); } /* * mseintr: update mouse status. sc_deltax and sc_deltay are accumulative. */ static void mseintr(unit) int unit; { /* * the table to turn MouseSystem button bits (MOUSE_MSC_BUTTON?UP) * into `mousestatus' button bits (MOUSE_BUTTON?DOWN). */ static int butmap[8] = { 0, MOUSE_BUTTON3DOWN, MOUSE_BUTTON2DOWN, MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN, MOUSE_BUTTON1DOWN, MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN, MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN, MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN }; register struct mse_softc *sc = &mse_sc[unit]; int dx, dy, but; int sign; #ifdef DEBUG static int mse_intrcnt = 0; if((mse_intrcnt++ % 10000) == 0) printf("mseintr\n"); #endif /* DEBUG */ if ((sc->sc_flags & MSESC_OPEN) == 0) return; (*sc->sc_getmouse)(sc->sc_port, &dx, &dy, &but); if (sc->mode.accelfactor > 0) { sign = (dx < 0); dx = dx * dx / sc->mode.accelfactor; if (dx == 0) dx = 1; if (sign) dx = -dx; sign = (dy < 0); dy = dy * dy / sc->mode.accelfactor; if (dy == 0) dy = 1; if (sign) dy = -dy; } sc->sc_deltax += dx; sc->sc_deltay += dy; sc->sc_buttons = but; but = butmap[~but & MOUSE_MSC_BUTTONS]; sc->status.dx += dx; sc->status.dy += dy; sc->status.flags |= ((dx || dy) ? MOUSE_POSCHANGED : 0) | (sc->status.button ^ but); sc->status.button = but; /* * If mouse state has changed, wake up anyone wanting to know. */ if (sc->sc_deltax != 0 || sc->sc_deltay != 0 || (sc->sc_obuttons ^ sc->sc_buttons) != 0) { if (sc->sc_flags & MSESC_WANT) { sc->sc_flags &= ~MSESC_WANT; wakeup((caddr_t)sc); } selwakeup(&sc->sc_selp); } } /* * Routines for the Logitech mouse. */ /* * Test for a Logitech bus mouse and return 1 if it is. * (until I know how to use the signature port properly, just disable * interrupts and return 1) */ static int mse_probelogi(idp) register struct isa_device *idp; { int sig; outb(idp->id_iobase + MSE_PORTD, MSE_SETUP); /* set the signature port */ outb(idp->id_iobase + MSE_PORTB, MSE_LOGI_SIG); DELAY(30000); /* 30 ms delay */ sig = inb(idp->id_iobase + MSE_PORTB) & 0xFF; if (sig == MSE_LOGI_SIG) { outb(idp->id_iobase + MSE_PORTC, MSE_DISINTR); return(1); } else { if (bootverbose) printf("mse%d: wrong signature %x\n",idp->id_unit,sig); return(0); } } /* * Initialize Logitech mouse and enable interrupts. */ static void mse_enablelogi(port) register u_int port; { int dx, dy, but; outb(port + MSE_PORTD, MSE_SETUP); mse_getlogi(port, &dx, &dy, &but); } /* * Disable interrupts for Logitech mouse. */ static void mse_disablelogi(port) register u_int port; { outb(port + MSE_PORTC, MSE_DISINTR); } /* * Get the current dx, dy and button up/down state. */ static void mse_getlogi(port, dx, dy, but) register u_int port; int *dx; int *dy; int *but; { register char x, y; outb(port + MSE_PORTC, MSE_HOLD | MSE_RXLOW); x = inb(port + MSE_PORTA); *but = (x >> 5) & MOUSE_MSC_BUTTONS; x &= 0xf; outb(port + MSE_PORTC, MSE_HOLD | MSE_RXHIGH); x |= (inb(port + MSE_PORTA) << 4); outb(port + MSE_PORTC, MSE_HOLD | MSE_RYLOW); y = (inb(port + MSE_PORTA) & 0xf); outb(port + MSE_PORTC, MSE_HOLD | MSE_RYHIGH); y |= (inb(port + MSE_PORTA) << 4); *dx = x; *dy = y; outb(port + MSE_PORTC, MSE_INTREN); } /* * Routines for the ATI Inport bus mouse. */ /* * Test for a ATI Inport bus mouse and return 1 if it is. * (do not enable interrupts) */ static int mse_probeati(idp) register struct isa_device *idp; { int i; for (i = 0; i < 2; i++) if (inb(idp->id_iobase + MSE_PORTC) == 0xde) return (1); return (0); } /* * Initialize ATI Inport mouse and enable interrupts. */ static void mse_enableati(port) register u_int port; { outb(port + MSE_PORTA, MSE_INPORT_RESET); outb(port + MSE_PORTA, MSE_INPORT_MODE); outb(port + MSE_PORTB, MSE_INPORT_INTREN); } /* * Disable interrupts for ATI Inport mouse. */ static void mse_disableati(port) register u_int port; { outb(port + MSE_PORTA, MSE_INPORT_MODE); outb(port + MSE_PORTB, 0); } /* * Get current dx, dy and up/down button state. */ static void mse_getati(port, dx, dy, but) register u_int port; int *dx; int *dy; int *but; { register char byte; outb(port + MSE_PORTA, MSE_INPORT_MODE); outb(port + MSE_PORTB, MSE_INPORT_HOLD); outb(port + MSE_PORTA, MSE_INPORT_STATUS); *but = ~inb(port + MSE_PORTB) & MOUSE_MSC_BUTTONS; outb(port + MSE_PORTA, MSE_INPORT_DX); byte = inb(port + MSE_PORTB); *dx = byte; outb(port + MSE_PORTA, MSE_INPORT_DY); byte = inb(port + MSE_PORTB); *dy = byte; outb(port + MSE_PORTA, MSE_INPORT_MODE); outb(port + MSE_PORTB, MSE_INPORT_INTREN); } - -#endif /* NMSE */ diff --git a/sys/i386/isa/npx.c b/sys/i386/isa/npx.c index c9d92f4bbfa8..30914d017355 100644 --- a/sys/i386/isa/npx.c +++ b/sys/i386/isa/npx.c @@ -1,933 +1,928 @@ /*- * Copyright (c) 1990 William Jolitz. * Copyright (c) 1991 The Regents of the University of California. * All rights reserved. * * 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 * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, 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 the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)npx.c 7.2 (Berkeley) 5/12/91 * $FreeBSD$ */ -#include "npx.h" -#if NNPX > 0 - #include "opt_debug_npx.h" #include "opt_math_emulate.h" #include #include #include #include #include #include #include #include #include #include #ifdef NPX_DEBUG #include #endif #include #ifndef SMP #include #endif #include #include #include #include #include #include #ifndef SMP #include #endif #include #include #include #ifndef SMP #include #include #include #endif /* * 387 and 287 Numeric Coprocessor Extension (NPX) Driver. */ /* Configuration flags. */ #define NPX_DISABLE_I586_OPTIMIZED_BCOPY (1 << 0) #define NPX_DISABLE_I586_OPTIMIZED_BZERO (1 << 1) #define NPX_DISABLE_I586_OPTIMIZED_COPYIO (1 << 2) #define NPX_PREFER_EMULATOR (1 << 3) #ifdef __GNUC__ #define fldcw(addr) __asm("fldcw %0" : : "m" (*(addr))) #define fnclex() __asm("fnclex") #define fninit() __asm("fninit") #define fnop() __asm("fnop") #define fnsave(addr) __asm __volatile("fnsave %0" : "=m" (*(addr))) #define fnstcw(addr) __asm __volatile("fnstcw %0" : "=m" (*(addr))) #define fnstsw(addr) __asm __volatile("fnstsw %0" : "=m" (*(addr))) #define fp_divide_by_0() __asm("fldz; fld1; fdiv %st,%st(1); fnop") #define frstor(addr) __asm("frstor %0" : : "m" (*(addr))) #define start_emulating() __asm("smsw %%ax; orb %0,%%al; lmsw %%ax" \ : : "n" (CR0_TS) : "ax") #define stop_emulating() __asm("clts") #else /* not __GNUC__ */ void fldcw __P((caddr_t addr)); void fnclex __P((void)); void fninit __P((void)); void fnop __P((void)); void fnsave __P((caddr_t addr)); void fnstcw __P((caddr_t addr)); void fnstsw __P((caddr_t addr)); void fp_divide_by_0 __P((void)); void frstor __P((caddr_t addr)); void start_emulating __P((void)); void stop_emulating __P((void)); #endif /* __GNUC__ */ typedef u_char bool_t; static int npx_attach __P((device_t dev)); void npx_intr __P((void *)); static void npx_identify __P((driver_t *driver, device_t parent)); static int npx_probe __P((device_t dev)); static int npx_probe1 __P((device_t dev)); #ifdef I586_CPU static long timezero __P((const char *funcname, void (*func)(void *buf, size_t len))); #endif /* I586_CPU */ int hw_float; /* XXX currently just alias for npx_exists */ SYSCTL_INT(_hw,HW_FLOATINGPT, floatingpoint, CTLFLAG_RD, &hw_float, 0, "Floatingpoint instructions executed in hardware"); #ifndef SMP static u_int npx0_imask = SWI_CLOCK_MASK; static struct gate_descriptor npx_idt_probeintr; static int npx_intrno; static volatile u_int npx_intrs_while_probing; static volatile u_int npx_traps_while_probing; #endif static bool_t npx_ex16; static bool_t npx_exists; static bool_t npx_irq13; static int npx_irq; /* irq number */ #ifndef SMP /* * Special interrupt handlers. Someday intr0-intr15 will be used to count * interrupts. We'll still need a special exception 16 handler. The busy * latch stuff in probeintr() can be moved to npxprobe(). */ inthand_t probeintr; __asm(" \n\ .text \n\ .p2align 2,0x90 \n\ .type " __XSTRING(CNAME(probeintr)) ",@function \n\ " __XSTRING(CNAME(probeintr)) ": \n\ ss \n\ incl " __XSTRING(CNAME(npx_intrs_while_probing)) " \n\ pushl %eax \n\ movb $0x20,%al # EOI (asm in strings loses cpp features) \n\ outb %al,$0xa0 # IO_ICU2 \n\ outb %al,$0x20 # IO_ICU1 \n\ movb $0,%al \n\ outb %al,$0xf0 # clear BUSY# latch \n\ popl %eax \n\ iret \n\ "); inthand_t probetrap; __asm(" \n\ .text \n\ .p2align 2,0x90 \n\ .type " __XSTRING(CNAME(probetrap)) ",@function \n\ " __XSTRING(CNAME(probetrap)) ": \n\ ss \n\ incl " __XSTRING(CNAME(npx_traps_while_probing)) " \n\ fnclex \n\ iret \n\ "); #endif /* SMP */ /* * Identify routine. Create a connection point on our parent for probing. */ static void npx_identify(driver, parent) driver_t *driver; device_t parent; { device_t child; child = BUS_ADD_CHILD(parent, 0, "npx", 0); if (child == NULL) panic("npx_identify"); } /* * Probe routine. Initialize cr0 to give correct behaviour for [f]wait * whether the device exists or not (XXX should be elsewhere). Set flags * to tell npxattach() what to do. Modify device struct if npx doesn't * need to use interrupts. Return 1 if device exists. */ static int npx_probe(dev) device_t dev; { #ifdef SMP if (resource_int_value("npx", 0, "irq", &npx_irq) != 0) npx_irq = 13; return npx_probe1(dev); #else /* SMP */ int result; u_long save_eflags; u_char save_icu1_mask; u_char save_icu2_mask; struct gate_descriptor save_idt_npxintr; struct gate_descriptor save_idt_npxtrap; /* * This routine is now just a wrapper for npxprobe1(), to install * special npx interrupt and trap handlers, to enable npx interrupts * and to disable other interrupts. Someday isa_configure() will * install suitable handlers and run with interrupts enabled so we * won't need to do so much here. */ if (resource_int_value("npx", 0, "irq", &npx_irq) != 0) npx_irq = 13; npx_intrno = NRSVIDT + npx_irq; save_eflags = read_eflags(); disable_intr(); save_icu1_mask = inb(IO_ICU1 + 1); save_icu2_mask = inb(IO_ICU2 + 1); save_idt_npxintr = idt[npx_intrno]; save_idt_npxtrap = idt[16]; outb(IO_ICU1 + 1, ~IRQ_SLAVE); outb(IO_ICU2 + 1, ~(1 << (npx_irq - 8))); setidt(16, probetrap, SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(npx_intrno, probeintr, SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); npx_idt_probeintr = idt[npx_intrno]; enable_intr(); result = npx_probe1(dev); disable_intr(); outb(IO_ICU1 + 1, save_icu1_mask); outb(IO_ICU2 + 1, save_icu2_mask); idt[npx_intrno] = save_idt_npxintr; idt[16] = save_idt_npxtrap; write_eflags(save_eflags); return (result); #endif /* SMP */ } static int npx_probe1(dev) device_t dev; { #ifndef SMP u_short control; u_short status; #endif /* * Partially reset the coprocessor, if any. Some BIOS's don't reset * it after a warm boot. */ outb(0xf1, 0); /* full reset on some systems, NOP on others */ outb(0xf0, 0); /* clear BUSY# latch */ /* * Prepare to trap all ESC (i.e., NPX) instructions and all WAIT * instructions. We must set the CR0_MP bit and use the CR0_TS * bit to control the trap, because setting the CR0_EM bit does * not cause WAIT instructions to trap. It's important to trap * WAIT instructions - otherwise the "wait" variants of no-wait * control instructions would degenerate to the "no-wait" variants * after FP context switches but work correctly otherwise. It's * particularly important to trap WAITs when there is no NPX - * otherwise the "wait" variants would always degenerate. * * Try setting CR0_NE to get correct error reporting on 486DX's. * Setting it should fail or do nothing on lesser processors. */ load_cr0(rcr0() | CR0_MP | CR0_NE); /* * But don't trap while we're probing. */ stop_emulating(); /* * Finish resetting the coprocessor, if any. If there is an error * pending, then we may get a bogus IRQ13, but probeintr() will handle * it OK. Bogus halts have never been observed, but we enabled * IRQ13 and cleared the BUSY# latch early to handle them anyway. */ fninit(); #ifdef SMP /* * Exception 16 MUST work for SMP. */ npx_irq13 = 0; npx_ex16 = hw_float = npx_exists = 1; device_set_desc(dev, "math processor"); return (0); #else /* !SMP */ device_set_desc(dev, "math processor"); /* * Don't use fwait here because it might hang. * Don't use fnop here because it usually hangs if there is no FPU. */ DELAY(1000); /* wait for any IRQ13 */ #ifdef DIAGNOSTIC if (npx_intrs_while_probing != 0) printf("fninit caused %u bogus npx interrupt(s)\n", npx_intrs_while_probing); if (npx_traps_while_probing != 0) printf("fninit caused %u bogus npx trap(s)\n", npx_traps_while_probing); #endif /* * Check for a status of mostly zero. */ status = 0x5a5a; fnstsw(&status); if ((status & 0xb8ff) == 0) { /* * Good, now check for a proper control word. */ control = 0x5a5a; fnstcw(&control); if ((control & 0x1f3f) == 0x033f) { hw_float = npx_exists = 1; /* * We have an npx, now divide by 0 to see if exception * 16 works. */ control &= ~(1 << 2); /* enable divide by 0 trap */ fldcw(&control); npx_traps_while_probing = npx_intrs_while_probing = 0; fp_divide_by_0(); if (npx_traps_while_probing != 0) { /* * Good, exception 16 works. */ npx_ex16 = 1; return (0); } if (npx_intrs_while_probing != 0) { int rid; struct resource *r; void *intr; /* * Bad, we are stuck with IRQ13. */ npx_irq13 = 1; /* * npxattach would be too late to set npx0_imask */ npx0_imask |= (1 << npx_irq); /* * We allocate these resources permanently, * so there is no need to keep track of them. */ rid = 0; r = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, IO_NPX, IO_NPX, IO_NPXSIZE, RF_ACTIVE); if (r == 0) panic("npx: can't get ports"); rid = 0; r = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, npx_irq, npx_irq, 1, RF_ACTIVE); if (r == 0) panic("npx: can't get IRQ"); BUS_SETUP_INTR(device_get_parent(dev), dev, r, INTR_TYPE_MISC, npx_intr, 0, &intr); if (intr == 0) panic("npx: can't create intr"); return (0); } /* * Worse, even IRQ13 is broken. Use emulator. */ } } /* * Probe failed, but we want to get to npxattach to initialize the * emulator and say that it has been installed. XXX handle devices * that aren't really devices better. */ return (0); #endif /* SMP */ } /* * Attach routine - announce which it is, and wire into system */ int npx_attach(dev) device_t dev; { int flags; if (resource_int_value("npx", 0, "flags", &flags) != 0) flags = 0; if (flags) device_printf(dev, "flags 0x%x ", flags); if (npx_irq13) { device_printf(dev, "using IRQ 13 interface\n"); } else { #if defined(MATH_EMULATE) || defined(GPL_MATH_EMULATE) if (npx_ex16) { if (!(flags & NPX_PREFER_EMULATOR)) device_printf(dev, "INT 16 interface\n"); else { device_printf(dev, "FPU exists, but flags request " "emulator\n"); hw_float = npx_exists = 0; } } else if (npx_exists) { device_printf(dev, "error reporting broken; using 387 emulator\n"); hw_float = npx_exists = 0; } else device_printf(dev, "387 emulator\n"); #else if (npx_ex16) { device_printf(dev, "INT 16 interface\n"); if (flags & NPX_PREFER_EMULATOR) { device_printf(dev, "emulator requested, but none compiled " "into kernel, using FPU\n"); } } else device_printf(dev, "no 387 emulator in kernel and no FPU!\n"); #endif } npxinit(__INITIAL_NPXCW__); #ifdef I586_CPU if (cpu_class == CPUCLASS_586 && npx_ex16 && npx_exists && timezero("i586_bzero()", i586_bzero) < timezero("bzero()", bzero) * 4 / 5) { if (!(flags & NPX_DISABLE_I586_OPTIMIZED_BCOPY)) { bcopy_vector = i586_bcopy; ovbcopy_vector = i586_bcopy; } if (!(flags & NPX_DISABLE_I586_OPTIMIZED_BZERO)) bzero = i586_bzero; if (!(flags & NPX_DISABLE_I586_OPTIMIZED_COPYIO)) { copyin_vector = i586_copyin; copyout_vector = i586_copyout; } } #endif return (0); /* XXX unused */ } /* * Initialize floating point unit. */ void npxinit(control) u_short control; { struct save87 dummy; if (!npx_exists) return; /* * fninit has the same h/w bugs as fnsave. Use the detoxified * fnsave to throw away any junk in the fpu. npxsave() initializes * the fpu and sets npxproc = NULL as important side effects. */ npxsave(&dummy); stop_emulating(); fldcw(&control); if (curpcb != NULL) fnsave(&curpcb->pcb_savefpu); start_emulating(); } /* * Free coprocessor (if we have it). */ void npxexit(p) struct proc *p; { if (p == npxproc) npxsave(&curpcb->pcb_savefpu); #ifdef NPX_DEBUG if (npx_exists) { u_int masked_exceptions; masked_exceptions = curpcb->pcb_savefpu.sv_env.en_cw & curpcb->pcb_savefpu.sv_env.en_sw & 0x7f; /* * Log exceptions that would have trapped with the old * control word (overflow, divide by 0, and invalid operand). */ if (masked_exceptions & 0x0d) log(LOG_ERR, "pid %d (%s) exited with masked floating point exceptions 0x%02x\n", p->p_pid, p->p_comm, masked_exceptions); } #endif } /* * The following mechanism is used to ensure that the FPE_... value * that is passed as a trapcode to the signal handler of the user * process does not have more than one bit set. * * Multiple bits may be set if the user process modifies the control * word while a status word bit is already set. While this is a sign * of bad coding, we have no choise than to narrow them down to one * bit, since we must not send a trapcode that is not exactly one of * the FPE_ macros. * * The mechanism has a static table with 127 entries. Each combination * of the 7 FPU status word exception bits directly translates to a * position in this table, where a single FPE_... value is stored. * This FPE_... value stored there is considered the "most important" * of the exception bits and will be sent as the signal code. The * precedence of the bits is based upon Intel Document "Numerical * Applications", Chapter "Special Computational Situations". * * The macro to choose one of these values does these steps: 1) Throw * away status word bits that cannot be masked. 2) Throw away the bits * currently masked in the control word, assuming the user isn't * interested in them anymore. 3) Reinsert status word bit 7 (stack * fault) if it is set, which cannot be masked but must be presered. * 4) Use the remaining bits to point into the trapcode table. * * The 6 maskable bits in order of their preference, as stated in the * above referenced Intel manual: * 1 Invalid operation (FP_X_INV) * 1a Stack underflow * 1b Stack overflow * 1c Operand of unsupported format * 1d SNaN operand. * 2 QNaN operand (not an exception, irrelavant here) * 3 Any other invalid-operation not mentioned above or zero divide * (FP_X_INV, FP_X_DZ) * 4 Denormal operand (FP_X_DNML) * 5 Numeric over/underflow (FP_X_OFL, FP_X_UFL) * 6 Inexact result (FP_X_IMP) */ static char fpetable[128] = { 0, FPE_FLTINV, /* 1 - INV */ FPE_FLTUND, /* 2 - DNML */ FPE_FLTINV, /* 3 - INV | DNML */ FPE_FLTDIV, /* 4 - DZ */ FPE_FLTINV, /* 5 - INV | DZ */ FPE_FLTDIV, /* 6 - DNML | DZ */ FPE_FLTINV, /* 7 - INV | DNML | DZ */ FPE_FLTOVF, /* 8 - OFL */ FPE_FLTINV, /* 9 - INV | OFL */ FPE_FLTUND, /* A - DNML | OFL */ FPE_FLTINV, /* B - INV | DNML | OFL */ FPE_FLTDIV, /* C - DZ | OFL */ FPE_FLTINV, /* D - INV | DZ | OFL */ FPE_FLTDIV, /* E - DNML | DZ | OFL */ FPE_FLTINV, /* F - INV | DNML | DZ | OFL */ FPE_FLTUND, /* 10 - UFL */ FPE_FLTINV, /* 11 - INV | UFL */ FPE_FLTUND, /* 12 - DNML | UFL */ FPE_FLTINV, /* 13 - INV | DNML | UFL */ FPE_FLTDIV, /* 14 - DZ | UFL */ FPE_FLTINV, /* 15 - INV | DZ | UFL */ FPE_FLTDIV, /* 16 - DNML | DZ | UFL */ FPE_FLTINV, /* 17 - INV | DNML | DZ | UFL */ FPE_FLTOVF, /* 18 - OFL | UFL */ FPE_FLTINV, /* 19 - INV | OFL | UFL */ FPE_FLTUND, /* 1A - DNML | OFL | UFL */ FPE_FLTINV, /* 1B - INV | DNML | OFL | UFL */ FPE_FLTDIV, /* 1C - DZ | OFL | UFL */ FPE_FLTINV, /* 1D - INV | DZ | OFL | UFL */ FPE_FLTDIV, /* 1E - DNML | DZ | OFL | UFL */ FPE_FLTINV, /* 1F - INV | DNML | DZ | OFL | UFL */ FPE_FLTRES, /* 20 - IMP */ FPE_FLTINV, /* 21 - INV | IMP */ FPE_FLTUND, /* 22 - DNML | IMP */ FPE_FLTINV, /* 23 - INV | DNML | IMP */ FPE_FLTDIV, /* 24 - DZ | IMP */ FPE_FLTINV, /* 25 - INV | DZ | IMP */ FPE_FLTDIV, /* 26 - DNML | DZ | IMP */ FPE_FLTINV, /* 27 - INV | DNML | DZ | IMP */ FPE_FLTOVF, /* 28 - OFL | IMP */ FPE_FLTINV, /* 29 - INV | OFL | IMP */ FPE_FLTUND, /* 2A - DNML | OFL | IMP */ FPE_FLTINV, /* 2B - INV | DNML | OFL | IMP */ FPE_FLTDIV, /* 2C - DZ | OFL | IMP */ FPE_FLTINV, /* 2D - INV | DZ | OFL | IMP */ FPE_FLTDIV, /* 2E - DNML | DZ | OFL | IMP */ FPE_FLTINV, /* 2F - INV | DNML | DZ | OFL | IMP */ FPE_FLTUND, /* 30 - UFL | IMP */ FPE_FLTINV, /* 31 - INV | UFL | IMP */ FPE_FLTUND, /* 32 - DNML | UFL | IMP */ FPE_FLTINV, /* 33 - INV | DNML | UFL | IMP */ FPE_FLTDIV, /* 34 - DZ | UFL | IMP */ FPE_FLTINV, /* 35 - INV | DZ | UFL | IMP */ FPE_FLTDIV, /* 36 - DNML | DZ | UFL | IMP */ FPE_FLTINV, /* 37 - INV | DNML | DZ | UFL | IMP */ FPE_FLTOVF, /* 38 - OFL | UFL | IMP */ FPE_FLTINV, /* 39 - INV | OFL | UFL | IMP */ FPE_FLTUND, /* 3A - DNML | OFL | UFL | IMP */ FPE_FLTINV, /* 3B - INV | DNML | OFL | UFL | IMP */ FPE_FLTDIV, /* 3C - DZ | OFL | UFL | IMP */ FPE_FLTINV, /* 3D - INV | DZ | OFL | UFL | IMP */ FPE_FLTDIV, /* 3E - DNML | DZ | OFL | UFL | IMP */ FPE_FLTINV, /* 3F - INV | DNML | DZ | OFL | UFL | IMP */ FPE_FLTSUB, /* 40 - STK */ FPE_FLTSUB, /* 41 - INV | STK */ FPE_FLTUND, /* 42 - DNML | STK */ FPE_FLTSUB, /* 43 - INV | DNML | STK */ FPE_FLTDIV, /* 44 - DZ | STK */ FPE_FLTSUB, /* 45 - INV | DZ | STK */ FPE_FLTDIV, /* 46 - DNML | DZ | STK */ FPE_FLTSUB, /* 47 - INV | DNML | DZ | STK */ FPE_FLTOVF, /* 48 - OFL | STK */ FPE_FLTSUB, /* 49 - INV | OFL | STK */ FPE_FLTUND, /* 4A - DNML | OFL | STK */ FPE_FLTSUB, /* 4B - INV | DNML | OFL | STK */ FPE_FLTDIV, /* 4C - DZ | OFL | STK */ FPE_FLTSUB, /* 4D - INV | DZ | OFL | STK */ FPE_FLTDIV, /* 4E - DNML | DZ | OFL | STK */ FPE_FLTSUB, /* 4F - INV | DNML | DZ | OFL | STK */ FPE_FLTUND, /* 50 - UFL | STK */ FPE_FLTSUB, /* 51 - INV | UFL | STK */ FPE_FLTUND, /* 52 - DNML | UFL | STK */ FPE_FLTSUB, /* 53 - INV | DNML | UFL | STK */ FPE_FLTDIV, /* 54 - DZ | UFL | STK */ FPE_FLTSUB, /* 55 - INV | DZ | UFL | STK */ FPE_FLTDIV, /* 56 - DNML | DZ | UFL | STK */ FPE_FLTSUB, /* 57 - INV | DNML | DZ | UFL | STK */ FPE_FLTOVF, /* 58 - OFL | UFL | STK */ FPE_FLTSUB, /* 59 - INV | OFL | UFL | STK */ FPE_FLTUND, /* 5A - DNML | OFL | UFL | STK */ FPE_FLTSUB, /* 5B - INV | DNML | OFL | UFL | STK */ FPE_FLTDIV, /* 5C - DZ | OFL | UFL | STK */ FPE_FLTSUB, /* 5D - INV | DZ | OFL | UFL | STK */ FPE_FLTDIV, /* 5E - DNML | DZ | OFL | UFL | STK */ FPE_FLTSUB, /* 5F - INV | DNML | DZ | OFL | UFL | STK */ FPE_FLTRES, /* 60 - IMP | STK */ FPE_FLTSUB, /* 61 - INV | IMP | STK */ FPE_FLTUND, /* 62 - DNML | IMP | STK */ FPE_FLTSUB, /* 63 - INV | DNML | IMP | STK */ FPE_FLTDIV, /* 64 - DZ | IMP | STK */ FPE_FLTSUB, /* 65 - INV | DZ | IMP | STK */ FPE_FLTDIV, /* 66 - DNML | DZ | IMP | STK */ FPE_FLTSUB, /* 67 - INV | DNML | DZ | IMP | STK */ FPE_FLTOVF, /* 68 - OFL | IMP | STK */ FPE_FLTSUB, /* 69 - INV | OFL | IMP | STK */ FPE_FLTUND, /* 6A - DNML | OFL | IMP | STK */ FPE_FLTSUB, /* 6B - INV | DNML | OFL | IMP | STK */ FPE_FLTDIV, /* 6C - DZ | OFL | IMP | STK */ FPE_FLTSUB, /* 6D - INV | DZ | OFL | IMP | STK */ FPE_FLTDIV, /* 6E - DNML | DZ | OFL | IMP | STK */ FPE_FLTSUB, /* 6F - INV | DNML | DZ | OFL | IMP | STK */ FPE_FLTUND, /* 70 - UFL | IMP | STK */ FPE_FLTSUB, /* 71 - INV | UFL | IMP | STK */ FPE_FLTUND, /* 72 - DNML | UFL | IMP | STK */ FPE_FLTSUB, /* 73 - INV | DNML | UFL | IMP | STK */ FPE_FLTDIV, /* 74 - DZ | UFL | IMP | STK */ FPE_FLTSUB, /* 75 - INV | DZ | UFL | IMP | STK */ FPE_FLTDIV, /* 76 - DNML | DZ | UFL | IMP | STK */ FPE_FLTSUB, /* 77 - INV | DNML | DZ | UFL | IMP | STK */ FPE_FLTOVF, /* 78 - OFL | UFL | IMP | STK */ FPE_FLTSUB, /* 79 - INV | OFL | UFL | IMP | STK */ FPE_FLTUND, /* 7A - DNML | OFL | UFL | IMP | STK */ FPE_FLTSUB, /* 7B - INV | DNML | OFL | UFL | IMP | STK */ FPE_FLTDIV, /* 7C - DZ | OFL | UFL | IMP | STK */ FPE_FLTSUB, /* 7D - INV | DZ | OFL | UFL | IMP | STK */ FPE_FLTDIV, /* 7E - DNML | DZ | OFL | UFL | IMP | STK */ FPE_FLTSUB, /* 7F - INV | DNML | DZ | OFL | UFL | IMP | STK */ }; /* * Preserve the FP status word, clear FP exceptions, then generate a SIGFPE. * * Clearing exceptions is necessary mainly to avoid IRQ13 bugs. We now * depend on longjmp() restoring a usable state. Restoring the state * or examining it might fail if we didn't clear exceptions. * * The error code chosen will be one of the FPE_... macros. It will be * sent as the second argument to old BSD-style signal handlers and as * "siginfo_t->si_code" (second argument) to SA_SIGINFO signal handlers. * * XXX the FP state is not preserved across signal handlers. So signal * handlers cannot afford to do FP unless they preserve the state or * longjmp() out. Both preserving the state and longjmp()ing may be * destroyed by IRQ13 bugs. Clearing FP exceptions is not an acceptable * solution for signals other than SIGFPE. */ void npx_intr(dummy) void *dummy; { int code; u_short control; struct intrframe *frame; if (npxproc == NULL || !npx_exists) { printf("npxintr: npxproc = %p, curproc = %p, npx_exists = %d\n", npxproc, curproc, npx_exists); panic("npxintr from nowhere"); } if (npxproc != curproc) { printf("npxintr: npxproc = %p, curproc = %p, npx_exists = %d\n", npxproc, curproc, npx_exists); panic("npxintr from non-current process"); } outb(0xf0, 0); fnstsw(&curpcb->pcb_savefpu.sv_ex_sw); fnstcw(&control); fnclex(); /* * Pass exception to process. */ frame = (struct intrframe *)&dummy; /* XXX */ if ((ISPL(frame->if_cs) == SEL_UPL) || (frame->if_eflags & PSL_VM)) { /* * Interrupt is essentially a trap, so we can afford to call * the SIGFPE handler (if any) as soon as the interrupt * returns. * * XXX little or nothing is gained from this, and plenty is * lost - the interrupt frame has to contain the trap frame * (this is otherwise only necessary for the rescheduling trap * in doreti, and the frame for that could easily be set up * just before it is used). */ curproc->p_md.md_regs = INTR_TO_TRAPFRAME(frame); /* * Encode the appropriate code for detailed information on * this exception. */ code = fpetable[(curpcb->pcb_savefpu.sv_ex_sw & ~control & 0x3f) | (curpcb->pcb_savefpu.sv_ex_sw & 0x40)]; trapsignal(curproc, SIGFPE, code); } else { /* * Nested interrupt. These losers occur when: * o an IRQ13 is bogusly generated at a bogus time, e.g.: * o immediately after an fnsave or frstor of an * error state. * o a couple of 386 instructions after * "fstpl _memvar" causes a stack overflow. * These are especially nasty when combined with a * trace trap. * o an IRQ13 occurs at the same time as another higher- * priority interrupt. * * Treat them like a true async interrupt. */ psignal(curproc, SIGFPE); } } /* * Implement device not available (DNA) exception * * It would be better to switch FP context here (if curproc != npxproc) * and not necessarily for every context switch, but it is too hard to * access foreign pcb's. */ int npxdna() { if (!npx_exists) return (0); if (npxproc != NULL) { printf("npxdna: npxproc = %p, curproc = %p\n", npxproc, curproc); panic("npxdna"); } stop_emulating(); /* * Record new context early in case frstor causes an IRQ13. */ npxproc = curproc; curpcb->pcb_savefpu.sv_ex_sw = 0; /* * The following frstor may cause an IRQ13 when the state being * restored has a pending error. The error will appear to have been * triggered by the current (npx) user instruction even when that * instruction is a no-wait instruction that should not trigger an * error (e.g., fnclex). On at least one 486 system all of the * no-wait instructions are broken the same as frstor, so our * treatment does not amplify the breakage. On at least one * 386/Cyrix 387 system, fnclex works correctly while frstor and * fnsave are broken, so our treatment breaks fnclex if it is the * first FPU instruction after a context switch. */ frstor(&curpcb->pcb_savefpu); return (1); } /* * Wrapper for fnsave instruction to handle h/w bugs. If there is an error * pending, then fnsave generates a bogus IRQ13 on some systems. Force * any IRQ13 to be handled immediately, and then ignore it. This routine is * often called at splhigh so it must not use many system services. In * particular, it's much easier to install a special handler than to * guarantee that it's safe to use npxintr() and its supporting code. */ void npxsave(addr) struct save87 *addr; { #ifdef SMP stop_emulating(); fnsave(addr); /* fnop(); */ start_emulating(); npxproc = NULL; #else /* SMP */ u_char icu1_mask; u_char icu2_mask; u_char old_icu1_mask; u_char old_icu2_mask; struct gate_descriptor save_idt_npxintr; disable_intr(); old_icu1_mask = inb(IO_ICU1 + 1); old_icu2_mask = inb(IO_ICU2 + 1); save_idt_npxintr = idt[npx_intrno]; outb(IO_ICU1 + 1, old_icu1_mask & ~(IRQ_SLAVE | npx0_imask)); outb(IO_ICU2 + 1, old_icu2_mask & ~(npx0_imask >> 8)); idt[npx_intrno] = npx_idt_probeintr; enable_intr(); stop_emulating(); fnsave(addr); fnop(); start_emulating(); npxproc = NULL; disable_intr(); icu1_mask = inb(IO_ICU1 + 1); /* masks may have changed */ icu2_mask = inb(IO_ICU2 + 1); outb(IO_ICU1 + 1, (icu1_mask & ~npx0_imask) | (old_icu1_mask & npx0_imask)); outb(IO_ICU2 + 1, (icu2_mask & ~(npx0_imask >> 8)) | (old_icu2_mask & (npx0_imask >> 8))); idt[npx_intrno] = save_idt_npxintr; enable_intr(); /* back to usual state */ #endif /* SMP */ } #ifdef I586_CPU static long timezero(funcname, func) const char *funcname; void (*func) __P((void *buf, size_t len)); { void *buf; #define BUFSIZE 1000000 long usec; struct timeval finish, start; buf = malloc(BUFSIZE, M_TEMP, M_NOWAIT); if (buf == NULL) return (BUFSIZE); microtime(&start); (*func)(buf, BUFSIZE); microtime(&finish); usec = 1000000 * (finish.tv_sec - start.tv_sec) + finish.tv_usec - start.tv_usec; if (usec <= 0) usec = 1; if (bootverbose) printf("%s bandwidth = %ld bytes/sec\n", funcname, (long)(BUFSIZE * (int64_t)1000000 / usec)); free(buf, M_TEMP); return (usec); } #endif /* I586_CPU */ static device_method_t npx_methods[] = { /* Device interface */ DEVMETHOD(device_identify, npx_identify), DEVMETHOD(device_probe, npx_probe), DEVMETHOD(device_attach, npx_attach), DEVMETHOD(device_detach, bus_generic_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), { 0, 0 } }; static driver_t npx_driver = { "npx", npx_methods, 1, /* no softc */ }; static devclass_t npx_devclass; /* * We prefer to attach to the root nexus so that the usual case (exception 16) * doesn't describe the processor as being `on isa'. */ DRIVER_MODULE(npx, nexus, npx_driver, npx_devclass, 0, 0); - -#endif /* NNPX > 0 */ diff --git a/sys/i386/isa/spigot.c b/sys/i386/isa/spigot.c index bea8ec33967b..cdb05b72d54a 100644 --- a/sys/i386/isa/spigot.c +++ b/sys/i386/isa/spigot.c @@ -1,282 +1,279 @@ /* * Video spigot capture driver. * * Copyright (c) 1995, Jim Lowe. All rights reserved. * * 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 * notice, this list of conditions and the following disclaimer. 2. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * This is the minimum driver code required to make a spigot work. * Unfortunatly, I can't include a real driver since the information * on the spigot is under non-disclosure. You can pick up a library * that will work with this driver from * ftp://ftp.cs.uwm.edu/pub/FreeBSD-UWM. The library contains the * source that I can release as well as several object modules and * functions that allows one to read spigot data. See the code for * spigot_grab.c that is included with the library data. * * The vendor will not allow me to release the spigot library code. * Please don't ask me for it. * * To use this driver you will need the spigot library. The library is * available from: * * ftp.cs.uwm.edu://pub/FreeBSD-UWM/spigot/spigot.tar.gz * * Version 1.7, December 1995. * * $FreeBSD$ * */ #include "spigot.h" -#if NSPIGOT > 0 #if NSPIGOT > 1 error "Can only have 1 spigot configured." #endif #include "opt_spigot.h" #include #include #include #include #include #include #include #include #include #include #include static struct spigot_softc { u_long flags; u_long maddr; struct proc *p; u_long signal_num; u_short irq; } spigot_softc[NSPIGOT]; /* flags in softc */ #define OPEN 0x01 #define ALIVE 0x02 #define UNIT(dev) minor(dev) static int spigot_probe(struct isa_device *id); static int spigot_attach(struct isa_device *id); struct isa_driver spigotdriver = {spigot_probe, spigot_attach, "spigot"}; static d_open_t spigot_open; static d_close_t spigot_close; static d_read_t spigot_read; static d_write_t spigot_write; static d_ioctl_t spigot_ioctl; static d_mmap_t spigot_mmap; #define CDEV_MAJOR 11 static struct cdevsw spigot_cdevsw = { /* open */ spigot_open, /* close */ spigot_close, /* read */ spigot_read, /* write */ spigot_write, /* ioctl */ spigot_ioctl, /* poll */ nopoll, /* mmap */ spigot_mmap, /* strategy */ nostrategy, /* name */ "spigot", /* maj */ CDEV_MAJOR, /* dump */ nodump, /* psize */ nopsize, /* flags */ 0, /* bmaj */ -1 }; static ointhand2_t spigintr; static int spigot_probe(struct isa_device *devp) { int status; struct spigot_softc *ss=(struct spigot_softc *)&spigot_softc[devp->id_unit]; static int once; if (!once++) cdevsw_add(&spigot_cdevsw); ss->flags = 0; ss->maddr = 0; ss->irq = 0; if(devp->id_iobase != 0xad6 || inb(0xad9) == 0xff) status = 0; /* not found */ else { status = 1; /* found */ ss->flags |= ALIVE; } return(status); } static int spigot_attach(struct isa_device *devp) { int unit; struct spigot_softc *ss= &spigot_softc[unit = devp->id_unit]; devp->id_ointr = spigintr; ss->maddr = kvtop(devp->id_maddr); ss->irq = devp->id_irq; make_dev(&spigot_cdevsw, unit, 0, 0, 0644, "spigot%d", unit); return 1; } static int spigot_open(dev_t dev, int flags, int fmt, struct proc *p) { int error; struct spigot_softc *ss = (struct spigot_softc *)&spigot_softc[UNIT(dev)]; if((ss->flags & ALIVE) == 0) return ENXIO; if(ss->flags & OPEN) return EBUSY; #if !defined(SPIGOT_UNSECURE) /* * Don't allow open() unless the process has sufficient privileges, * since mapping the i/o page and granting i/o privilege would * require sufficient privilege soon and nothing much can be done * without them. */ error = suser(p); if (error != 0) return error; if (securelevel > 0) return EPERM; #endif ss->flags |= OPEN; ss->p = 0; ss->signal_num = 0; return 0; } static int spigot_close(dev_t dev, int flags, int fmt, struct proc *p) { struct spigot_softc *ss = (struct spigot_softc *)&spigot_softc[UNIT(dev)]; ss->flags &= ~OPEN; ss->p = 0; ss->signal_num = 0; outb(0xad6, 0); return 0; } static int spigot_write(dev_t dev, struct uio *uio, int ioflag) { return ENXIO; } static int spigot_read(dev_t dev, struct uio *uio, int ioflag) { return ENXIO; } static int spigot_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) { int error; struct spigot_softc *ss = (struct spigot_softc *)&spigot_softc[UNIT(dev)]; struct spigot_info *info; if(!data) return(EINVAL); switch(cmd){ case SPIGOT_SETINT: ss->p = p; ss->signal_num = *((int *)data); break; case SPIGOT_IOPL_ON: /* allow access to the IO PAGE */ #if !defined(SPIGOT_UNSECURE) error = suser(p); if (error != 0) return error; if (securelevel > 0) return EPERM; #endif p->p_md.md_regs->tf_eflags |= PSL_IOPL; break; case SPIGOT_IOPL_OFF: /* deny access to the IO PAGE */ p->p_md.md_regs->tf_eflags &= ~PSL_IOPL; break; case SPIGOT_GET_INFO: info = (struct spigot_info *)data; info->maddr = ss->maddr; info->irq = ss->irq; break; default: return ENOTTY; } return 0; } /* * Interrupt procedure. * Just call a user level interrupt routine. */ static void spigintr(int unit) { struct spigot_softc *ss = (struct spigot_softc *)&spigot_softc[unit]; if(ss->p && ss->signal_num) psignal(ss->p, ss->signal_num); } static int spigot_mmap(dev_t dev, vm_offset_t offset, int nprot) { struct spigot_softc *ss = (struct spigot_softc *)&spigot_softc[0]; if(offset != 0) { printf("spigot mmap failed, offset = 0x%x != 0x0\n", offset); return -1; } if(nprot & PROT_EXEC) return -1; return i386_btop(ss->maddr); } - -#endif /* NSPIGOT */