Index: head/sys/alpha/alpha/autoconf.c =================================================================== --- head/sys/alpha/alpha/autoconf.c (revision 45719) +++ head/sys/alpha/alpha/autoconf.c (revision 45720) @@ -1,314 +1,314 @@ /*- * Copyright (c) 1998 Doug Rabson * 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. * - * $Id: autoconf.c,v 1.14 1999/03/12 14:44:46 gallatin Exp $ + * $Id: autoconf.c,v 1.15 1999/03/28 17:33:38 dfr Exp $ */ #include "opt_bootp.h" #include "opt_ffs.h" #include "opt_cd9660.h" #include "opt_mfs.h" #include "opt_nfsroot.h" #include #include #include #include #include /* for BASE_SLICE, MAX_SLICES */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void configure __P((void *)); SYSINIT(configure, SI_SUB_CONFIGURE, SI_ORDER_THIRD, configure, NULL) static void configure_finish __P((void)); static void configure_start __P((void)); static int setdumpdev __P((dev_t dev)); device_t isa_bus_device = 0; struct cam_sim *boot_sim = 0; static void configure_start() { } static void configure_finish() { } extern void pci_configure(void); static int atoi(const char *s) { int n = 0; while (*s >= '0' && *s <= '9') n = n * 10 + (*s++ - '0'); return n; } static const char * bootdev_field(int which) { char *p = bootinfo.booted_dev; char *q; static char field[128]; /* Skip characters to find the right field */ for (; which; which--) { while (*p != ' ' && *p != '\0') p++; if (*p) p++; } /* Copy out the field and return it */ q = field; while (*p != ' ' && *p != '\0') *q++ = *p++; *q = '\0'; return field; } static const char * bootdev_protocol(void) { return bootdev_field(0); } static int bootdev_slot(void) { return atoi(bootdev_field(2)); } static int bootdev_unit(void) { return atoi(bootdev_field(5)); } #if 0 static int bootdev_bus(void) { return atoi(bootdev_field(1)); } static int bootdev_channel(void) { return atoi(bootdev_field(3)); } static const char * bootdev_remote_address(void) { return bootdev_field(4); } static int bootdev_boot_dev_type(void) { return atoi(bootdev_field(6)); } static const char * bootdev_ctrl_dev_type(void) { return bootdev_field(7); } #endif void alpha_register_pci_scsi(int bus, int slot, struct cam_sim *sim) { if (!strcmp(bootdev_protocol(), "SCSI")) { int boot_slot = bootdev_slot(); if (bus == boot_slot / 1000 && slot == boot_slot % 1000) boot_sim = sim; } } /* * Determine i/o configuration for a machine. */ static void configure(void *dummy) { configure_start(); device_add_child(root_bus, platform.iobus, 0, 0); root_bus_configure(); if((hwrpb->rpb_type != ST_DEC_3000_300) && (hwrpb->rpb_type != ST_DEC_3000_500)){ - pci_configure(); + /* pci_configure(); */ /* * Probe ISA devices after everything. */ if (isa_bus_device) bus_generic_attach(isa_bus_device); } configure_finish(); cninit_finish(); /* * Now we're ready to handle (pending) interrupts. * XXX this is slightly misplaced. */ spl0(); cold = 0; } void cpu_rootconf() { #ifdef MFS_ROOT if (!mountrootfsname) { extern u_char *mfs_getimage __P((void)); if (bootverbose) printf("Considering MFS root f/s.\n"); if (mfs_getimage()) mountrootfsname = "mfs"; else if (bootverbose) printf("No MFS image available as root f/s.\n"); } #endif #if defined(FFS) || defined(FFS_ROOT) if (!mountrootfsname) { static char rootname[] = "da0a"; if (bootverbose) printf("Considering UFS root f/s.\n"); mountrootfsname = "ufs"; if (boot_sim) { struct cam_path *path; struct cam_periph *periph; xpt_create_path(&path, NULL, cam_sim_path(boot_sim), bootdev_unit() / 100, 0); periph = cam_periph_find(path, "da"); if (periph) rootdev = makedev(4, dkmakeminor(periph->unit_number, COMPATIBILITY_SLICE, 0)); xpt_free_path(path); } rootdevs[0] = rootdev; rootname[2] += dkunit(minor(rootdev)); rootdevnames[0] = rootname; } #endif } void cpu_dumpconf() { if (setdumpdev(dumpdev) != 0) dumpdev = NODEV; } static int setdumpdev(dev) dev_t dev; { int maj, psize; long newdumplo; if (dev == NODEV) { dumpdev = dev; return (0); } maj = major(dev); if (maj >= nblkdev || bdevsw[maj] == NULL) return (ENXIO); /* XXX is this right? */ if (bdevsw[maj]->d_psize == NULL) return (ENXIO); /* XXX should be ENODEV ? */ psize = bdevsw[maj]->d_psize(dev); if (psize == -1) return (ENXIO); /* XXX should be ENODEV ? */ /* * XXX should clean up checking in dumpsys() to be more like this, * and nuke dodump sysctl (too many knobs), and move this to * kern_shutdown.c... */ newdumplo = psize - Maxmem * PAGE_SIZE / DEV_BSIZE; if (newdumplo < 0) return (ENOSPC); dumpdev = dev; dumplo = newdumplo; return (0); } static int sysctl_kern_dumpdev SYSCTL_HANDLER_ARGS { int error; dev_t ndumpdev; ndumpdev = dumpdev; error = sysctl_handle_opaque(oidp, &ndumpdev, sizeof ndumpdev, req); if (error == 0 && req->newptr != NULL) error = setdumpdev(ndumpdev); return (error); } SYSCTL_PROC(_kern, KERN_DUMPDEV, dumpdev, CTLTYPE_OPAQUE|CTLFLAG_RW, 0, sizeof dumpdev, sysctl_kern_dumpdev, "T,dev_t", ""); Index: head/sys/alpha/conf/files.alpha =================================================================== --- head/sys/alpha/conf/files.alpha (revision 45719) +++ head/sys/alpha/conf/files.alpha (revision 45720) @@ -1,159 +1,169 @@ # This file tells config what files go into building a kernel, # files marked standard are always included. # -# $Id: files.alpha,v 1.16 1999/01/23 16:53:26 dfr Exp $ +# $Id: files.alpha,v 1.17 1999/03/10 10:36:50 yokota Exp $ # # The long compile-with and dependency lines are required because of # limitations in config: backslash-newline doesn't work in strings, and # dependency lines other than the first are silently ignored. # # font8x16.o optional std8x16font \ compile-with "uudecode < /usr/share/syscons/fonts/${STD8X16FONT}-8x16.fnt && file2c 'unsigned char font_16[16*256] = {' '};' < ${STD8X16FONT}-8x16 > font8x16.c && ${CC} -c ${CFLAGS} font8x16.c" \ no-implicit-rule before-depend \ clean "${STD8X16FONT}-8x16 font8x16.c" # atkbdmap.h optional atkbd_dflt_keymap \ compile-with "kbdcontrol -L ${ATKBD_DFLT_KEYMAP} | sed -e 's/^static keymap_t.* = /static keymap_t key_map = /' -e 's/^static accentmap_t.* = /static accentmap_t accent_map = /' > atkbdmap.h" \ no-obj no-implicit-rule before-depend \ clean "atkbdmap.h" # alpha/alpha/autoconf.c standard device-driver alpha/alpha/cpuconf.c standard alpha/alpha/atomic.s standard alpha/alpha/dec_kn8ae.c optional dec_kn8ae alpha/alpha/dec_eb164.c optional dec_eb164 alpha/alpha/dec_eb64plus.c optional dec_eb64plus alpha/alpha/dec_kn20aa.c optional dec_kn20aa alpha/alpha/dec_2100_a50.c optional dec_2100_a50 alpha/alpha/dec_st550.c optional dec_st550 alpha/alpha/dec_axppci_33.c optional dec_axppci_33 alpha/alpha/dec_3000_300.c optional dec_3000_300 alpha/alpha/dec_3000_500.c optional dec_3000_500 alpha/alpha/mountroot.c optional slice alpha/alpha/ipl_funcs.c standard alpha/alpha/pal.s standard alpha/alpha/busdma_machdep.c standard alpha/alpha/cons.c standard alpha/alpha/prom.c standard alpha/alpha/promcons.c standard alpha/alpha/prom_disp.s standard alpha/alpha/alpha-gdbstub.c optional ddb alpha/alpha/db_disasm.c optional ddb alpha/alpha/db_interface.c optional ddb alpha/alpha/db_trace.c optional ddb alpha/alpha/exception.s standard alpha/alpha/in_cksum.c optional inet # locore.s needs to be handled in Makefile to put it first. Otherwise it's # now normal. # alpha/alpha/locore.s standard alpha/alpha/machdep.c standard alpha/alpha/fp_emulate.c standard alpha/alpha/ieee_float.c standard alpha/alpha/mem.c standard alpha/alpha/mp_machdep.c optional smp alpha/alpha/perfmon.c optional perfmon profiling-routine alpha/alpha/perfmon.c optional perfmon alpha/alpha/pmap.c standard alpha/alpha/procfs_machdep.c standard alpha/alpha/simplelock.s optional smp alpha/alpha/support.s standard alpha/alpha/swtch.s standard alpha/alpha/sys_machdep.c standard alpha/alpha/trap.c standard alpha/alpha/interrupt.c standard alpha/alpha/userconfig.c optional userconfig alpha/alpha/vm_machdep.c standard alpha/alpha/clock.c standard clock_if.o standard \ dependency "clock_if.c" \ compile-with "${NORMAL_C}" \ no-implicit-rule local clock_if.c standard \ dependency "$S/kern/makedevops.pl $S/alpha/alpha/clock_if.m" \ compile-with "perl $S/kern/makedevops.pl -c $S/alpha/alpha/clock_if.m" \ no-obj no-implicit-rule before-depend local \ clean "clock_if.c" clock_if.h standard \ dependency "$S/kern/makedevops.pl $S/alpha/alpha/clock_if.m" \ compile-with "perl $S/kern/makedevops.pl -h $S/alpha/alpha/clock_if.m" \ no-obj no-implicit-rule before-depend \ clean "clock_if.h" alpha/alpha/diskslice_machdep.c standard alpha/tlsb/tlsb.c optional tlsb alpha/tlsb/gbus.c optional gbus alpha/tlsb/kftxx.c optional kft alpha/tlsb/mcclock_tlsb.c optional gbus alpha/tlsb/zs_tlsb.c optional gbus alpha/tlsb/dwlpx.c optional dwlpx alpha/tc/tcasic.c optional tcasic alpha/tc/tc.c optional tc alpha/tc/ioasic.c optional tc alpha/tc/mcclock_ioasic.c optional tc alpha/tc/if_le_ioasic.c optional le device-driver alpha/tc/if_le_dec.c optional le device-driver alpha/tc/am7990.c optional le device-driver alpha/tc/tcds.c optional tcds device-driver alpha/tc/tcds_dma.c optional tcds device-driver alpha/tc/esp.c optional esp device-driver dev/dec/mcclock.c standard device-driver mcclock_if.o standard \ dependency "mcclock_if.c" \ compile-with "${NORMAL_C}" \ no-implicit-rule local mcclock_if.c standard \ dependency "$S/kern/makedevops.pl $S/dev/dec/mcclock_if.m" \ compile-with "perl $S/kern/makedevops.pl -c $S/dev/dec/mcclock_if.m" \ no-obj no-implicit-rule before-depend local \ clean "mcclock_if.c" mcclock_if.h standard \ dependency "$S/kern/makedevops.pl $S/dev/dec/mcclock_if.m" \ compile-with "perl $S/kern/makedevops.pl -h $S/dev/dec/mcclock_if.m" \ no-obj no-implicit-rule before-depend \ clean "mcclock_if.h" alpha/pci/cia.c optional cia +alpha/pci/cia_pci.c optional cia alpha/pci/pci_eb164_intr.s optional cia alpha/pci/apecs.c optional apecs +alpha/pci/apecs_pci.c optional apecs alpha/pci/pci_eb64plus_intr.s optional apecs alpha/pci/lca.c optional lca +alpha/pci/lca_pci.c optional lca alpha/pci/pcibus.c optional pci alpha/isa/isa.c optional isa alpha/isa/mcclock_isa.c optional isa alpha/alpha/elf_machdep.c standard libkern/bcd.c standard libkern/bcmp.c standard libkern/ffs.c standard libkern/inet_ntoa.c standard libkern/index.c standard libkern/mcount.c optional profiling-routine libkern/qsort.c standard libkern/random.c standard libkern/rindex.c standard libkern/scanc.c standard libkern/skpc.c standard libkern/strcat.c standard libkern/strcmp.c standard libkern/strcpy.c standard libkern/strlen.c standard libkern/strncmp.c standard libkern/strncpy.c standard libkern/alpha/htonl.S standard libkern/alpha/htons.S standard libkern/alpha/ntohl.S standard libkern/alpha/ntohs.S standard isa/sio.c optional sio device-driver dev/fb/fb.c optional fb device-driver dev/fb/fb.c optional vga device-driver isa/vga_isa.c optional vga device-driver dev/fb/splash.c optional splash dev/kbd/atkbd.c optional atkbd device-driver isa/atkbd_isa.c optional atkbd device-driver dev/kbd/atkbdc.c optional atkbdc device-driver isa/atkbdc_isa.c optional atkbdc device-driver dev/kbd/kbd.c optional atkbd device-driver dev/kbd/kbd.c optional kbd device-driver dev/kbd/kbd.c optional ukbd device-driver dev/syscons/syscons.c optional sc device-driver dev/syscons/scvidctl.c optional sc device-driver isa/syscons_isa.c optional sc device-driver isa/psm.c optional psm device-driver +dev/ata/ata-all.c optional ata device-driver +dev/ata/ata-dma.c optional ata device-driver +dev/ata/atapi-all.c optional ata device-driver +dev/ata/ata-disk.c optional atadisk device-driver +dev/ata/atapi-cd.c optional atapicd device-driver +dev/ata/atapi-fd.c optional atapifd device-driver +dev/ata/atapi-tape.c optional atapist device-driver Index: head/sys/alpha/include/chipset.h =================================================================== --- head/sys/alpha/include/chipset.h (revision 45719) +++ head/sys/alpha/include/chipset.h (revision 45720) @@ -1,124 +1,117 @@ /*- * Copyright (c) 1998 Doug Rabson * 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. * - * $Id: chipset.h,v 1.5 1998/10/06 14:18:39 dfr Exp $ + * $Id: chipset.h,v 1.6 1998/11/15 18:25:16 dfr Exp $ */ #ifndef _MACHINE_CHIPSET_H_ #define _MACHINE_CHIPSET_H_ typedef u_int8_t alpha_chipset_inb_t(u_int32_t port); typedef u_int16_t alpha_chipset_inw_t(u_int32_t port); typedef u_int32_t alpha_chipset_inl_t(u_int32_t port); typedef void alpha_chipset_outb_t(u_int32_t port, u_int8_t data); typedef void alpha_chipset_outw_t(u_int32_t port, u_int16_t data); typedef void alpha_chipset_outl_t(u_int32_t port, u_int32_t data); typedef u_int8_t alpha_chipset_readb_t(u_int32_t pa); typedef u_int16_t alpha_chipset_readw_t(u_int32_t pa); typedef u_int32_t alpha_chipset_readl_t(u_int32_t pa); typedef void alpha_chipset_writeb_t(u_int32_t pa, u_int8_t data); typedef void alpha_chipset_writew_t(u_int32_t pa, u_int16_t data); typedef void alpha_chipset_writel_t(u_int32_t pa, u_int32_t data); typedef int alpha_chipset_maxdevs_t(u_int bus); typedef u_int8_t alpha_chipset_cfgreadb_t(u_int, u_int, u_int, u_int); typedef u_int16_t alpha_chipset_cfgreadw_t(u_int, u_int, u_int, u_int); typedef u_int32_t alpha_chipset_cfgreadl_t(u_int, u_int, u_int, u_int); typedef void alpha_chipset_cfgwriteb_t(u_int, u_int, u_int, u_int, u_int8_t); typedef void alpha_chipset_cfgwritew_t(u_int, u_int, u_int, u_int, u_int16_t); typedef void alpha_chipset_cfgwritel_t(u_int, u_int, u_int, u_int, u_int32_t); typedef vm_offset_t alpha_chipset_addrcvt_t(vm_offset_t); typedef u_int64_t alpha_chipset_read_hae_t(void); typedef void alpha_chipset_write_hae_t(u_int64_t); typedef struct alpha_chipset { /* * I/O port access */ alpha_chipset_inb_t* inb; alpha_chipset_inw_t* inw; alpha_chipset_inl_t* inl; alpha_chipset_outb_t* outb; alpha_chipset_outw_t* outw; alpha_chipset_outl_t* outl; /* * Memory access */ alpha_chipset_readb_t* readb; alpha_chipset_readw_t* readw; alpha_chipset_readl_t* readl; alpha_chipset_writeb_t* writeb; alpha_chipset_writew_t* writew; alpha_chipset_writel_t* writel; /* * PCI configuration access */ alpha_chipset_maxdevs_t* maxdevs; alpha_chipset_cfgreadb_t* cfgreadb; alpha_chipset_cfgreadw_t* cfgreadw; alpha_chipset_cfgreadl_t* cfgreadl; alpha_chipset_cfgwriteb_t* cfgwriteb; alpha_chipset_cfgwritew_t* cfgwritew; alpha_chipset_cfgwritel_t* cfgwritel; /* * PCI address space translation functions */ alpha_chipset_addrcvt_t* cvt_to_dense; alpha_chipset_addrcvt_t* cvt_to_bwx; /* * Access the HAE register */ alpha_chipset_read_hae_t* read_hae; alpha_chipset_write_hae_t* write_hae; - - /* - * PCI interrupt device. - * (XXX hack until I change pci code to use new - * device framework.) - */ - void* intrdev; } alpha_chipset_t; extern alpha_chipset_t chipset; /* * Exported sysctl variables describing the PCI chipset. */ extern char chipset_type[10]; extern int chipset_bwx; extern long chipset_ports; extern long chipset_memory; extern long chipset_dense; extern long chipset_hae_mask; #endif /* !_MACHINE_CHIPSET_H_ */ Index: head/sys/alpha/include/cpuconf.h =================================================================== --- head/sys/alpha/include/cpuconf.h (revision 45719) +++ head/sys/alpha/include/cpuconf.h (revision 45720) @@ -1,122 +1,123 @@ /* $NetBSD: cpuconf.h,v 1.7 1997/11/06 00:42:03 thorpej Exp $ */ #ifndef _ALPHA_CPUCONF_H #define _ALPHA_CPUCONF_H /* * Copyright (c) 1996 Christopher G. Demetriou. 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 Christopher G. Demetriou * for the NetBSD Project. * 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. */ /* * Additional reworking by Matthew Jacob for NASA/Ames Research Center. * Copyright (c) 1997 */ #ifdef KERNEL /* * Platform Specific Information and Function Hooks. * * The tags family and model information are strings describing the platform. * * The tag iobus describes the primary iobus for the platform- primarily * to give a hint as to where to start configuring. The likely choices * are one of tcasic, lca, apecs, cia, or tlsb. * */ struct device; /* XXX */ extern struct platform { /* * Platform Information. */ const char *family; /* Family Name */ const char *model; /* Model (variant) Name */ const char *iobus; /* Primary iobus name */ /* * Platform Specific Function Hooks * cons_init - console initialization * device_register - boot configuration aid * iointr - I/O interrupt handler * clockintr - Clock Interrupt Handler * mcheck_handler - Platform Specific Machine Check Handler */ void (*cons_init) __P((void)); void (*device_register) __P((struct device *, void *)); void (*iointr) __P((void *, unsigned long)); void (*clockintr) __P((void *)); void (*mcheck_handler) __P((unsigned long, struct trapframe *, unsigned long, unsigned long)); void (*pci_intr_init) __P((void)); void (*pci_intr_map) __P((void *)); void (*pci_intr_disable) __P((int)); void (*pci_intr_enable) __P((int)); + int (*pci_setup_ide_intr) __P((int chan, void (*fn)(void*), void *arg)); } platform; /* * Lookup table entry for Alpha system variations. */ struct alpha_variation_table { u_int64_t avt_variation; /* variation, from HWRPB */ const char *avt_model; /* model string */ }; /* * There is an array of functions to initialize the platform structure. * * It's responsible for filling in the family, model_name and iobus * tags. It may optionally fill in the cons_init, device_register and * mcheck_handler tags. * * The iointr tag is filled in by set_iointr (in interrupt.c). * The clockintr tag is filled in by cpu_initclocks (in clock.c). * * nocpu is function to call when you can't figure what platform you're on. * There's no return from this function. */ struct cpuinit { void (*init) __P((int)); const char *option; }; #define cpu_notsupp(st) { platform_not_supported, st } #define cpu_init(fn, opt) { fn, opt } /* * Misc. support routines. */ const char *alpha_dsr_sysname __P((void)); const char *alpha_variation_name __P((u_int64_t variation, const struct alpha_variation_table *avtp)); const char *alpha_unknown_sysname __P((void)); extern struct cpuinit cpuinit[]; extern int ncpuinit; extern void platform_not_configured __P((int)); extern void platform_not_supported __P((int)); #endif /* KERNEL */ #endif /* !_ALPHA_CPUCONF_H */ Index: head/sys/alpha/include/cpufunc.h =================================================================== --- head/sys/alpha/include/cpufunc.h (revision 45719) +++ head/sys/alpha/include/cpufunc.h (revision 45720) @@ -1,71 +1,105 @@ /*- * Copyright (c) 1998 Doug Rabson * 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. * - * $Id: cpufunc.h,v 1.2 1998/07/22 08:25:39 dfr Exp $ + * $Id: cpufunc.h,v 1.3 1998/08/17 08:21:31 dfr Exp $ */ #ifndef _MACHINE_CPUFUNC_H_ #define _MACHINE_CPUFUNC_H_ #ifdef KERNEL #include #include #ifdef __GNUC__ static __inline void breakpoint(void) { __asm __volatile("call_pal 0x81"); /* XXX bugchk */ } #endif #define inb(port) chipset.inb(port) #define inw(port) chipset.inw(port) #define inl(port) chipset.inl(port) #define outb(port, data) chipset.outb(port, data) #define outw(port, data) chipset.outw(port, data) #define outl(port, data) chipset.outl(port, data) #define readb(pa) chipset.readb(pa) #define readw(pa) chipset.readw(pa) #define readl(pa) chipset.readl(pa) #define writeb(pa,v) chipset.writeb(pa,v) #define writew(pa,v) chipset.writew(pa,v) #define writel(pa,v) chipset.writel(pa,v) /* + * Bulk i/o (for IDE driver). + */ +static __inline void insw(u_int32_t port, void *buffer, size_t count) +{ + u_int16_t *p = (u_int16_t *) buffer; + while (count--) + *p++ = inw(port); +} + +static __inline void insl(u_int32_t port, void *buffer, size_t count) +{ + u_int32_t *p = (u_int32_t *) buffer; + while (count--) + *p++ = inl(port); +} + +static __inline void outsw(u_int32_t port, const void *buffer, size_t count) +{ + const u_int16_t *p = (const u_int16_t *) buffer; + while (count--) + outw(port, *p++); +} + +static __inline void outsl(u_int32_t port, const void *buffer, size_t count) +{ + const u_int32_t *p = (const u_int32_t *) buffer; + while (count--) + outl(port, *p++); +} + +/* * String version of IO memory access ops: */ extern void memcpy_fromio(void *, u_int32_t, size_t); extern void memcpy_toio(u_int32_t, void *, size_t); +extern void memcpy_io(u_int32_t, u_int32_t, size_t); extern void memset_io(u_int32_t, int, size_t); +extern void memsetw(void *, int, size_t); +extern void memsetw_io(u_int32_t, int, size_t); #endif /* KERNEL */ #endif /* !_MACHINE_CPUFUNC_H_ */ Index: head/sys/alpha/isa/isa.c =================================================================== --- head/sys/alpha/isa/isa.c (revision 45719) +++ head/sys/alpha/isa/isa.c (revision 45720) @@ -1,701 +1,730 @@ /*- * Copyright (c) 1998 Doug Rabson * 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. * - * $Id: isa.c,v 1.8 1998/11/28 09:55:16 dfr Exp $ + * $Id: isa.c,v 1.9 1999/01/23 16:53:27 dfr Exp $ */ #include #include #include #include #include +#include #include #include #include #include +#include #include #include #include MALLOC_DEFINE(M_ISADEV, "isadev", "ISA device"); /* * The structure used to attach devices to the Isa. */ struct isa_device { u_short id_port[ISA_NPORT_IVARS]; u_short id_portsize[ISA_NPORT_IVARS]; vm_offset_t id_maddr[ISA_NMEM_IVARS]; vm_size_t id_msize[ISA_NMEM_IVARS]; int id_irq[ISA_NIRQ_IVARS]; int id_drq[ISA_NDRQ_IVARS]; int id_flags; struct resource *id_portres[ISA_NPORT_IVARS]; struct resource *id_memres[ISA_NMEM_IVARS]; struct resource *id_irqres[ISA_NIRQ_IVARS]; struct resource *id_drqres[ISA_NDRQ_IVARS]; }; #define DEVTOISA(dev) ((struct isa_device*) device_get_ivars(dev)) static devclass_t isa_devclass; static struct rman isa_irq_rman; /* * Device methods */ static int isa_probe(device_t dev); static int isa_attach(device_t dev); static void isa_print_child(device_t dev, device_t child); static int isa_read_ivar(device_t dev, device_t child, int which, u_long *result); static int isa_write_ivar(device_t dev, device_t child, int which, u_long result); static struct resource *isa_alloc_resource(device_t bus, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags); static int isa_release_resource(device_t bus, device_t child, int type, int rid, struct resource *r); -static int isa_setup_intr(device_t dev, device_t child, struct resource *irq, - driver_intr_t *intr, void *arg, void **cookiep); -static int isa_teardown_intr(device_t dev, device_t child, - struct resource *irq, void *cookie); static device_method_t isa_methods[] = { /* Device interface */ DEVMETHOD(device_probe, isa_probe), DEVMETHOD(device_attach, isa_attach), DEVMETHOD(device_detach, bus_generic_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), /* Bus interface */ DEVMETHOD(bus_print_child, isa_print_child), DEVMETHOD(bus_read_ivar, isa_read_ivar), DEVMETHOD(bus_write_ivar, isa_write_ivar), DEVMETHOD(bus_alloc_resource, isa_alloc_resource), DEVMETHOD(bus_release_resource, isa_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_setup_intr, isa_setup_intr), DEVMETHOD(bus_teardown_intr, isa_teardown_intr), { 0, 0 } }; static driver_t isa_driver = { "isa", isa_methods, DRIVER_TYPE_MISC, 1, /* no softc */ }; static void isa_add_device(device_t dev, const char *name, int unit) { struct isa_device *idev; device_t child; int sensitive, t; static device_t last_sensitive; if (resource_int_value(name, unit, "sensitive", &sensitive) != 0) sensitive = 0; idev = malloc(sizeof(struct isa_device), M_ISADEV, M_NOWAIT); if (!idev) return; bzero(idev, sizeof *idev); if (resource_int_value(name, unit, "port", &t) == 0) idev->id_port[0] = t; else idev->id_port[0] = 0; idev->id_port[1] = 0; if (resource_int_value(name, unit, "portsize", &t) == 0) idev->id_portsize[0] = t; else idev->id_portsize[0] = 0; idev->id_portsize[1] = 0; - if (resource_int_value(name, unit, "iomem", &t) == 0) + if (resource_int_value(name, unit, "maddr", &t) == 0) idev->id_maddr[0] = t; else idev->id_maddr[0] = 0; idev->id_maddr[1] = 0; if (resource_int_value(name, unit, "msize", &t) == 0) idev->id_msize[0] = t; else idev->id_msize[0] = 0; idev->id_msize[1] = 0; if (resource_int_value(name, unit, "flags", &t) == 0) idev->id_flags = t; else idev->id_flags = 0; if (resource_int_value(name, unit, "irq", &t) == 0) idev->id_irq[0] = t; else idev->id_irq[0] = -1; idev->id_irq[1] = -1; if (resource_int_value(name, unit, "drq", &t) == 0) idev->id_drq[0] = t; else idev->id_drq[0] = -1; idev->id_drq[1] = -1; if (sensitive) child = device_add_child_after(dev, last_sensitive, name, unit, idev); else child = device_add_child(dev, name, unit, idev); if (child == 0) return; else if (sensitive) last_sensitive = child; if (resource_int_value(name, unit, "disabled", &t) == 0 && t != 0) device_disable(child); } static void isa_intr_enable(int irq) { int s = splhigh(); if (irq < 8) outb(IO_ICU1+1, inb(IO_ICU1+1) & ~(1 << irq)); else outb(IO_ICU2+1, inb(IO_ICU2+1) & ~(1 << (irq - 8))); splx(s); } static void isa_intr_disable(int irq) { int s = splhigh(); if (irq < 8) outb(IO_ICU1+1, inb(IO_ICU1+1) | (1 << irq)); else outb(IO_ICU2+1, inb(IO_ICU2+1) | (1 << (irq - 8))); splx(s); } int isa_irq_pending(void) { u_char irr1; u_char irr2; irr1 = inb(IO_ICU1); irr2 = inb(IO_ICU2); return ((irr2 << 8) | irr1); } int isa_irq_mask(void) { u_char irr1; u_char irr2; irr1 = inb(IO_ICU1+1); irr2 = inb(IO_ICU2+1); return ((irr2 << 8) | irr1); } /* * At 'probe' time, we add all the devices which we know about to the * bus. The generic attach routine will probe and attach them if they * are alive. */ static int isa_probe(device_t dev) { int i; + device_set_desc(dev, "ISA bus"); + /* * Add all devices configured to be attached to isa0. */ for (i = resource_query_string(-1, "at", "isa0"); i != -1; i = resource_query_string(i, "at", "isa0")) { isa_add_device(dev, resource_query_name(i), resource_query_unit(i)); } /* * and isa? */ for (i = resource_query_string(-1, "at", "isa"); i != -1; i = resource_query_string(i, "at", "isa")) { isa_add_device(dev, resource_query_name(i), resource_query_unit(i)); } - isa_irq_rman.rm_start = 0; - isa_irq_rman.rm_end = 15; - isa_irq_rman.rm_type = RMAN_ARRAY; - isa_irq_rman.rm_descr = "ISA Interrupt request lines"; - if (rman_init(&isa_irq_rman) - || rman_manage_region(&isa_irq_rman, 0, 1) - || rman_manage_region(&isa_irq_rman, 3, 15)) - panic("isa_probe isa_irq_rman"); + isa_init_intr(); return 0; } extern device_t isa_bus_device; static int isa_attach(device_t dev) { if (bootverbose) printf("isa_attach: mask=%04x\n", isa_irq_mask()); - /* mask all isa interrupts */ - outb(IO_ICU1+1, 0xff); - outb(IO_ICU2+1, 0xff); - - /* make sure chaining irq is enabled */ - isa_intr_enable(2); - /* * Arrange for bus_generic_attach(dev) to be called later. */ isa_bus_device = dev; return 0; } static void isa_print_child(device_t bus, device_t dev) { struct isa_device *id = DEVTOISA(dev); if (id->id_port[0] > 0 || id->id_port[1] || id->id_maddr[0] > 0 || id->id_maddr[1] || id->id_irq[0] >= 0 || id->id_irq[1] >= 0 || id->id_drq[0] >= 0 || id->id_drq[1] >= 0) printf(" at"); if (id->id_port[0] && id->id_port[1]) { printf(" ports %#x", (u_int)id->id_port[0]); if (id->id_portsize[0]) printf("-%#x", (u_int)(id->id_port[0] + id->id_portsize[0] - 1)); printf(" and %#x", (u_int)id->id_port[1]); if (id->id_portsize[1]) printf("-%#x", (u_int)(id->id_port[1] + id->id_portsize[1] - 1)); } else if (id->id_port[0]) { printf(" port %#x", (u_int)id->id_port[0]); if (id->id_portsize[0]) printf("-%#x", (u_int)(id->id_port[0] + id->id_portsize[0] - 1)); } else if (id->id_port[1]) { printf(" port %#x", (u_int)id->id_port[1]); if (id->id_portsize[1]) printf("-%#x", (u_int)(id->id_port[1] + id->id_portsize[1] - 1)); } if (id->id_maddr[0] && id->id_maddr[1]) { printf(" iomem %#x", (u_int)id->id_maddr[0]); if (id->id_msize[0]) printf("-%#x", (u_int)(id->id_maddr[0] + id->id_msize[0] - 1)); printf(" and %#x", (u_int)id->id_maddr[1]); if (id->id_msize[1]) printf("-%#x", (u_int)(id->id_maddr[1] + id->id_msize[1] - 1)); } else if (id->id_maddr[0]) { printf(" iomem %#x", (u_int)id->id_maddr[0]); if (id->id_msize[0]) printf("-%#x", (u_int)(id->id_maddr[0] + id->id_msize[0] - 1)); } else if (id->id_maddr[1]) { printf(" iomem %#x", (u_int)id->id_maddr[1]); if (id->id_msize[1]) printf("-%#x", (u_int)(id->id_maddr[1] + id->id_msize[1] - 1)); } +#if 0 if (id->id_irq[0] >= 0 && id->id_irq[1] >= 0) printf(" irqs %d and %d", id->id_irq[0], id->id_irq[1]); else if (id->id_irq[0] >= 0) printf(" irq %d", id->id_irq[0]); else if (id->id_irq[1] >= 0) printf(" irq %d", id->id_irq[1]); +#endif if (id->id_drq[0] >= 0 && id->id_drq[1] >= 0) printf(" drqs %d and %d", id->id_drq[0], id->id_drq[1]); else if (id->id_drq[0] >= 0) printf(" drq %d", id->id_drq[0]); else if (id->id_drq[1] >= 0) printf(" drq %d", id->id_drq[1]); if (id->id_flags) printf(" flags %#x", id->id_flags); printf(" on %s%d", device_get_name(bus), device_get_unit(bus)); } static int isa_read_ivar(device_t bus, device_t dev, int index, u_long* result) { struct isa_device* idev = DEVTOISA(dev); switch (index) { case ISA_IVAR_PORT_0: *result = idev->id_port[0]; break; case ISA_IVAR_PORT_1: *result = idev->id_port[1]; break; case ISA_IVAR_PORTSIZE_0: *result = idev->id_portsize[0]; break; case ISA_IVAR_PORTSIZE_1: *result = idev->id_portsize[1]; break; case ISA_IVAR_MADDR_0: *result = idev->id_maddr[0]; break; case ISA_IVAR_MADDR_1: *result = idev->id_maddr[1]; break; case ISA_IVAR_MSIZE_0: *result = idev->id_msize[0]; break; case ISA_IVAR_MSIZE_1: *result = idev->id_msize[1]; break; case ISA_IVAR_IRQ_0: *result = idev->id_irq[0]; break; case ISA_IVAR_IRQ_1: *result = idev->id_irq[1]; break; case ISA_IVAR_DRQ_0: *result = idev->id_drq[0]; break; case ISA_IVAR_DRQ_1: *result = idev->id_drq[1]; break; case ISA_IVAR_FLAGS: *result = idev->id_flags; break; + default: + return (ENOENT); } - return ENOENT; + return (0); } static int isa_write_ivar(device_t bus, device_t dev, int index, u_long value) { struct isa_device* idev = DEVTOISA(dev); switch (index) { case ISA_IVAR_PORT_0: idev->id_port[0] = value; break; case ISA_IVAR_PORT_1: idev->id_port[1] = value; break; case ISA_IVAR_PORTSIZE_0: idev->id_portsize[0] = value; break; case ISA_IVAR_PORTSIZE_1: idev->id_portsize[1] = value; break; case ISA_IVAR_MADDR_0: idev->id_maddr[0] = value; break; case ISA_IVAR_MADDR_1: idev->id_maddr[1] = value; break; case ISA_IVAR_MSIZE_0: idev->id_msize[0] = value; break; case ISA_IVAR_MSIZE_1: idev->id_msize[1] = value; break; case ISA_IVAR_IRQ_0: idev->id_irq[0] = value; break; case ISA_IVAR_IRQ_1: idev->id_irq[1] = value; break; case ISA_IVAR_DRQ_0: idev->id_drq[0] = value; break; case ISA_IVAR_DRQ_1: idev->id_drq[1] = value; break; case ISA_IVAR_FLAGS: idev->id_flags = value; break; default: return (ENOENT); } return (0); } +void isa_init_intr(void) +{ + static int initted = 0; + + if (initted) return; + initted = 1; + + isa_irq_rman.rm_start = 0; + isa_irq_rman.rm_end = 15; + isa_irq_rman.rm_type = RMAN_ARRAY; + isa_irq_rman.rm_descr = "ISA Interrupt request lines"; + if (rman_init(&isa_irq_rman) + || rman_manage_region(&isa_irq_rman, 0, 1) + || rman_manage_region(&isa_irq_rman, 3, 15)) + panic("isa_probe isa_irq_rman"); + + /* mask all isa interrupts */ + outb(IO_ICU1+1, 0xff); + outb(IO_ICU2+1, 0xff); + + /* make sure chaining irq is enabled */ + isa_intr_enable(2); +} + +struct resource * +isa_alloc_intr(device_t bus, device_t child, int irq) +{ + return rman_reserve_resource(&isa_irq_rman, irq, irq, 1, + 0, child); +} + +int +isa_release_intr(device_t bus, device_t child, struct resource *r) +{ + return rman_release_resource(r); +} + /* * This implementation simply passes the request up to the parent * bus, which in our case is the pci chipset device, substituting any * configured values if the caller defaulted. We can get away with * this because there is no special mapping for ISA resources on this * platform. When porting this code to another architecture, it may be * necessary to interpose a mapping layer here. * * We manage our own interrupt resources since ISA interrupts go through * the ISA PIC, not the PCI interrupt controller. */ static struct resource * isa_alloc_resource(device_t bus, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { int isdefault; struct resource *rv, **rvp; struct isa_device *id; if (child) { /* * If this is our child, then use the isa_device to find * defaults and to record results. */ if (device_get_devclass(device_get_parent(child)) == isa_devclass) id = DEVTOISA(child); else id = NULL; } else id = NULL; isdefault = (start == 0UL && end == ~0UL && *rid == 0); if (*rid > 1) return 0; switch (type) { case SYS_RES_IRQ: /* * The hack implementation of intr_create() passes a * NULL child device. */ - if (isdefault && (id == NULL || id->id_irq[0] >= 0)) { + if (isdefault && id && id->id_irq[0] >= 0) { start = id->id_irq[0]; end = id->id_irq[0]; count = 1; } rv = rman_reserve_resource(&isa_irq_rman, start, end, count, 0, child); if (!rv) return 0; if (id) { id->id_irqres[*rid] = rv; id->id_irq[*rid] = rv->r_start; } return rv; case SYS_RES_MEMORY: if (isdefault && id->id_maddr[0]) { start = id->id_maddr[0]; count = max(count, (u_long)id->id_msize[0]); end = id->id_maddr[0] + count; } rvp = &id->id_memres[*rid]; break; case SYS_RES_IOPORT: if (isdefault && id->id_port[0]) { start = id->id_port[0]; count = max(count, (u_long)id->id_portsize[0]); end = id->id_port[0] + count; } rvp = &id->id_portres[*rid]; break; default: return 0; } /* * If the client attempts to reallocate a resource without * releasing what was there previously, die horribly so that * he knows how he !@#$ed up. */ if (*rvp != 0) panic("%s%d: (%d, %d) not free for %s%d\n", device_get_name(bus), device_get_unit(bus), type, *rid, device_get_name(child), device_get_unit(child)); /* * nexus_alloc_resource had better not change *rid... */ rv = BUS_ALLOC_RESOURCE(device_get_parent(bus), child, type, rid, start, end, count, flags); if ((*rvp = rv) != 0) { switch (type) { case SYS_RES_MEMORY: id->id_maddr[*rid] = rv->r_start; id->id_msize[*rid] = count; break; case SYS_RES_IOPORT: id->id_port[*rid] = rv->r_start; id->id_portsize[*rid] = count; break; } } return rv; } static int isa_release_resource(device_t bus, device_t child, int type, int rid, struct resource *r) { int rv; - struct resource **rp; struct isa_device *id = DEVTOISA(child); if (rid > 1) return EINVAL; switch (type) { case SYS_RES_IRQ: return (rman_release_resource(r)); case SYS_RES_DRQ: case SYS_RES_IOPORT: case SYS_RES_MEMORY: break; default: return (ENOENT); } rv = BUS_RELEASE_RESOURCE(device_get_parent(bus), child, type, rid, r); - if (rv) { + if (rv == 0) { switch (type) { case SYS_RES_IRQ: id->id_irqres[rid] = 0; id->id_irq[rid] = -1; break; case SYS_RES_DRQ: id->id_drqres[rid] = 0; id->id_drq[rid] = -1; break; case SYS_RES_MEMORY: id->id_memres[rid] = 0; id->id_maddr[rid] = 0; id->id_msize[rid] = 0; break; case SYS_RES_IOPORT: id->id_portres[rid] = 0; id->id_port[rid] = 0; id->id_portsize[rid] = 0; break; default: return ENOENT; } } return rv; } struct isa_intr { void *ih; driver_intr_t *intr; void *arg; int irq; }; /* * Wrap ISA interrupt routines so that we can feed non-specific * EOI to the PICs. */ static void isa_handle_intr(void *arg) { struct isa_intr *ii = arg; int irq = ii->irq; ii->intr(ii->arg); if (ii->irq > 7) outb(IO_ICU2, 0x20 | (irq & 7)); outb(IO_ICU1, 0x20 | (irq > 7 ? 2 : irq)); } -static int +int isa_setup_intr(device_t dev, device_t child, struct resource *irq, driver_intr_t *intr, void *arg, void **cookiep) { struct isa_intr *ii; int error; error = rman_activate_resource(irq); if (error) return error; ii = malloc(sizeof(struct isa_intr), M_DEVBUF, M_NOWAIT); if (!ii) return ENOMEM; ii->intr = intr; ii->arg = arg; ii->irq = irq->r_start; error = alpha_setup_intr(0x800 + (irq->r_start << 4), isa_handle_intr, ii, &ii->ih, &intrcnt[INTRCNT_ISA_IRQ + irq->r_start]); if (error) { free(ii, M_DEVBUF); return error; } isa_intr_enable(irq->r_start); *cookiep = ii; + + if (child) + device_printf(child, "interrupting at ISA irq %d\n", + (int)irq->r_start); + return 0; } -static int +int isa_teardown_intr(device_t dev, device_t child, struct resource *irq, void *cookie) { struct isa_intr *ii = cookie; alpha_teardown_intr(ii->ih); isa_intr_disable(irq->r_start); return 0; } -DRIVER_MODULE(isa, cia, isa_driver, isa_devclass, 0, 0); -DRIVER_MODULE(isa, apecs, isa_driver, isa_devclass, 0, 0); -DRIVER_MODULE(isa, lca, isa_driver, isa_devclass, 0, 0); +DRIVER_MODULE(isa, isab, isa_driver, isa_devclass, 0, 0); Index: head/sys/alpha/isa/isavar.h =================================================================== --- head/sys/alpha/isa/isavar.h (nonexistent) +++ head/sys/alpha/isa/isavar.h (revision 45720) @@ -0,0 +1,39 @@ +/*- + * Copyright (c) 1998 Doug Rabson + * 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. + * + * $Id$ + */ + +/* + * Export low-level interrupt handling code for chipsets which route + * interrupts via the ISA interrupt controller. + */ +void isa_init_intr(void); +struct resource *isa_alloc_intr(device_t bus, device_t child, int irq); +int isa_release_intr(device_t bus, device_t child, struct resource *r); +int isa_setup_intr(device_t dev, device_t child, struct resource *irq, + driver_intr_t *intr, void *arg, void **cookiep); +int isa_teardown_intr(device_t dev, device_t child, struct resource *irq, + void *cookie); Property changes on: head/sys/alpha/isa/isavar.h ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: head/sys/alpha/pci/apecs.c =================================================================== --- head/sys/alpha/pci/apecs.c (revision 45719) +++ head/sys/alpha/pci/apecs.c (revision 45720) @@ -1,575 +1,609 @@ /*- * Copyright (c) 1998 Doug Rabson * 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. * - * $Id: apecs.c,v 1.4 1998/12/04 22:54:42 archie Exp $ + * $Id: apecs.c,v 1.5 1999/01/18 20:15:07 gallatin Exp $ */ /* * Copyright (c) 1995, 1996 Carnegie-Mellon University. * All rights reserved. * * Author: Chris G. Demetriou * * Permission to use, copy, modify and distribute this software and * its documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. */ /* * Additional Copyright (c) 1998 by Andrew Gallatin for Duke University */ #include #include #include #include #include +#include #include #include #include #include +#include #include +#include #include #include #include #include #define KV(pa) ALPHA_PHYS_TO_K0SEG(pa) static devclass_t apecs_devclass; static device_t apecs0; /* XXX only one for now */ -static device_t isa0; struct apecs_softc { vm_offset_t dmem_base; /* dense memory */ vm_offset_t smem_base; /* sparse memory */ vm_offset_t io_base; /* dense i/o */ vm_offset_t cfg0_base; /* dense pci0 config */ vm_offset_t cfg1_base; /* dense pci1 config */ }; #define APECS_SOFTC(dev) (struct apecs_softc*) device_get_softc(dev) static alpha_chipset_inb_t apecs_swiz_inb; static alpha_chipset_inw_t apecs_swiz_inw; static alpha_chipset_inl_t apecs_swiz_inl; static alpha_chipset_outb_t apecs_swiz_outb; static alpha_chipset_outw_t apecs_swiz_outw; static alpha_chipset_outl_t apecs_swiz_outl; static alpha_chipset_readb_t apecs_swiz_readb; static alpha_chipset_readw_t apecs_swiz_readw; static alpha_chipset_readl_t apecs_swiz_readl; static alpha_chipset_writeb_t apecs_swiz_writeb; static alpha_chipset_writew_t apecs_swiz_writew; static alpha_chipset_writel_t apecs_swiz_writel; static alpha_chipset_maxdevs_t apecs_swiz_maxdevs; static alpha_chipset_cfgreadb_t apecs_swiz_cfgreadb; static alpha_chipset_cfgreadw_t apecs_swiz_cfgreadw; static alpha_chipset_cfgreadl_t apecs_swiz_cfgreadl; static alpha_chipset_cfgwriteb_t apecs_swiz_cfgwriteb; static alpha_chipset_cfgwritew_t apecs_swiz_cfgwritew; static alpha_chipset_cfgwritel_t apecs_swiz_cfgwritel; static alpha_chipset_addrcvt_t apecs_cvt_dense; static alpha_chipset_read_hae_t apecs_read_hae; static alpha_chipset_write_hae_t apecs_write_hae; static alpha_chipset_t apecs_swiz_chipset = { apecs_swiz_inb, apecs_swiz_inw, apecs_swiz_inl, apecs_swiz_outb, apecs_swiz_outw, apecs_swiz_outl, apecs_swiz_readb, apecs_swiz_readw, apecs_swiz_readl, apecs_swiz_writeb, apecs_swiz_writew, apecs_swiz_writel, apecs_swiz_maxdevs, apecs_swiz_cfgreadb, apecs_swiz_cfgreadw, apecs_swiz_cfgreadl, apecs_swiz_cfgwriteb, apecs_swiz_cfgwritew, apecs_swiz_cfgwritel, apecs_cvt_dense, NULL, apecs_read_hae, apecs_write_hae, }; static int apecs_swiz_maxdevs(u_int b) { return 12; /* XXX */ } static u_int8_t apecs_swiz_inb(u_int32_t port) { alpha_mb(); return SPARSE_READ_BYTE(KV(APECS_PCI_SIO), port); } static u_int16_t apecs_swiz_inw(u_int32_t port) { alpha_mb(); return SPARSE_READ_WORD(KV(APECS_PCI_SIO), port); } static u_int32_t apecs_swiz_inl(u_int32_t port) { alpha_mb(); return SPARSE_READ_LONG(KV(APECS_PCI_SIO), port); } static void apecs_swiz_outb(u_int32_t port, u_int8_t data) { SPARSE_WRITE_BYTE(KV(APECS_PCI_SIO), port, data); alpha_wmb(); } static void apecs_swiz_outw(u_int32_t port, u_int16_t data) { SPARSE_WRITE_WORD(KV(APECS_PCI_SIO), port, data); alpha_wmb(); } static void apecs_swiz_outl(u_int32_t port, u_int32_t data) { SPARSE_WRITE_LONG(KV(APECS_PCI_SIO), port, data); alpha_wmb(); } /* * Memory functions. * * XXX linux does 32-bit reads/writes via dense space. This doesn't * appear to work for devices behind a ppb. I'm using sparse * accesses & they appear to work just fine everywhere. */ static u_int32_t apecs_hae_mem; #define REG1 (1UL << 24) static __inline void apecs_swiz_set_hae_mem(u_int32_t *pa) { int s; u_int32_t msb; if(*pa >= REG1){ msb = *pa & 0xf8000000; *pa -= msb; s = splhigh(); if (msb != apecs_hae_mem) { apecs_hae_mem = msb; REGVAL(EPIC_HAXR1) = apecs_hae_mem; alpha_mb(); apecs_hae_mem = REGVAL(EPIC_HAXR1); } splx(s); } } static u_int8_t apecs_swiz_readb(u_int32_t pa) { alpha_mb(); apecs_swiz_set_hae_mem(&pa); return SPARSE_READ_BYTE(KV(APECS_PCI_SPARSE), pa); } static u_int16_t apecs_swiz_readw(u_int32_t pa) { alpha_mb(); apecs_swiz_set_hae_mem(&pa); return SPARSE_READ_WORD(KV(APECS_PCI_SPARSE), pa); } static u_int32_t apecs_swiz_readl(u_int32_t pa) { alpha_mb(); apecs_swiz_set_hae_mem(&pa); return SPARSE_READ_LONG(KV(APECS_PCI_SPARSE), pa); } static void apecs_swiz_writeb(u_int32_t pa, u_int8_t data) { apecs_swiz_set_hae_mem(&pa); SPARSE_WRITE_BYTE(KV(APECS_PCI_SPARSE), pa, data); alpha_wmb(); } static void apecs_swiz_writew(u_int32_t pa, u_int16_t data) { apecs_swiz_set_hae_mem(&pa); SPARSE_WRITE_WORD(KV(APECS_PCI_SPARSE), pa, data); alpha_wmb(); } static void apecs_swiz_writel(u_int32_t pa, u_int32_t data) { apecs_swiz_set_hae_mem(&pa); SPARSE_WRITE_LONG(KV(APECS_PCI_SPARSE), pa, data); alpha_wmb(); } #define APECS_SWIZ_CFGOFF(b, s, f, r) \ (((b) << 16) | ((s) << 11) | ((f) << 8) | (r)) #define APECS_TYPE1_SETUP(b,s,old_haxr2) if((b)) { \ do { \ (s) = splhigh(); \ (old_haxr2) = REGVAL(EPIC_HAXR2); \ alpha_mb(); \ REGVAL(EPIC_HAXR2) = (old_haxr2) | 0x1; \ alpha_mb(); \ } while(0); \ } #define APECS_TYPE1_TEARDOWN(b,s,old_haxr2) if((b)) { \ do { \ alpha_mb(); \ REGVAL(EPIC_HAXR2) = (old_haxr2); \ alpha_mb(); \ splx((s)); \ } while(0); \ } #define SWIZ_CFGREAD(b, s, f, r, width, type) \ type val = ~0; \ int ipl = 0; \ u_int32_t old_haxr2 = 0; \ struct apecs_softc* sc = APECS_SOFTC(apecs0); \ vm_offset_t off = APECS_SWIZ_CFGOFF(b, s, f, r); \ vm_offset_t kv = SPARSE_##width##_ADDRESS(sc->cfg0_base, off); \ alpha_mb(); \ APECS_TYPE1_SETUP(b,ipl,old_haxr2); \ if (!badaddr((caddr_t)kv, sizeof(type))) { \ val = SPARSE_##width##_EXTRACT(off, SPARSE_READ(kv)); \ } \ APECS_TYPE1_TEARDOWN(b,ipl,old_haxr2); \ return val; #define SWIZ_CFGWRITE(b, s, f, r, data, width, type) \ int ipl = 0; \ u_int32_t old_haxr2 = 0; \ struct apecs_softc* sc = APECS_SOFTC(apecs0); \ vm_offset_t off = APECS_SWIZ_CFGOFF(b, s, f, r); \ vm_offset_t kv = SPARSE_##width##_ADDRESS(sc->cfg0_base, off); \ alpha_mb(); \ APECS_TYPE1_SETUP(b,ipl,old_haxr2); \ if (!badaddr((caddr_t)kv, sizeof(type))) { \ SPARSE_WRITE(kv, SPARSE_##width##_INSERT(off, data)); \ alpha_wmb(); \ } \ APECS_TYPE1_TEARDOWN(b,ipl,old_haxr2); \ return; #if 1 static u_int8_t apecs_swiz_cfgreadb(u_int b, u_int s, u_int f, u_int r) { SWIZ_CFGREAD(b, s, f, r, BYTE, u_int8_t); } static u_int16_t apecs_swiz_cfgreadw(u_int b, u_int s, u_int f, u_int r) { SWIZ_CFGREAD(b, s, f, r, WORD, u_int16_t); } static u_int32_t apecs_swiz_cfgreadl(u_int b, u_int s, u_int f, u_int r) { SWIZ_CFGREAD(b, s, f, r, LONG, u_int32_t); } static void apecs_swiz_cfgwriteb(u_int b, u_int s, u_int f, u_int r, u_int8_t data) { SWIZ_CFGWRITE(b, s, f, r, data, BYTE, u_int8_t); } static void apecs_swiz_cfgwritew(u_int b, u_int s, u_int f, u_int r, u_int16_t data) { SWIZ_CFGWRITE(b, s, f, r, data, WORD, u_int16_t); } static void apecs_swiz_cfgwritel(u_int b, u_int s, u_int f, u_int r, u_int32_t data) { SWIZ_CFGWRITE(b, s, f, r, data, LONG, u_int32_t); } #else static u_int8_t apecs_swiz_cfgreadb(u_int b, u_int s, u_int f, u_int r) { struct apecs_softc* sc = APECS_SOFTC(apecs0); vm_offset_t off = APECS_SWIZ_CFGOFF(b, s, f, r); alpha_mb(); if (badaddr((caddr_t)(sc->cfg0_base + SPARSE_BYTE_OFFSET(off)), 1)) return ~0; return SPARSE_READ_BYTE(sc->cfg0_base, off); } static u_int16_t apecs_swiz_cfgreadw(u_int b, u_int s, u_int f, u_int r) { struct apecs_softc* sc = APECS_SOFTC(apecs0); vm_offset_t off = APECS_SWIZ_CFGOFF(b, s, f, r); alpha_mb(); if (badaddr((caddr_t)(sc->cfg0_base + SPARSE_WORD_OFFSET(off)), 2)) return ~0; return SPARSE_READ_WORD(sc->cfg0_base, off); } static u_int32_t apecs_swiz_cfgreadl(u_int b, u_int s, u_int f, u_int r) { struct apecs_softc* sc = APECS_SOFTC(apecs0); vm_offset_t off = APECS_SWIZ_CFGOFF(b, s, f, r); alpha_mb(); if (badaddr((caddr_t)(sc->cfg0_base + SPARSE_LONG_OFFSET(off)), 4)) return ~0; return SPARSE_READ_LONG(sc->cfg0_base, off); } static void apecs_swiz_cfgwriteb(u_int b, u_int s, u_int f, u_int r, u_int8_t data) { struct apecs_softc* sc = APECS_SOFTC(apecs0); vm_offset_t off = APECS_SWIZ_CFGOFF(b, s, f, r); if (badaddr((caddr_t)(sc->cfg0_base + SPARSE_BYTE_OFFSET(off)), 1)) return; SPARSE_WRITE_BYTE(sc->cfg0_base, off, data); alpha_wmb(); } static void apecs_swiz_cfgwritew(u_int b, u_int s, u_int f, u_int r, u_int16_t data) { struct apecs_softc* sc = APECS_SOFTC(apecs0); vm_offset_t off = APECS_SWIZ_CFGOFF(b, s, f, r); if (badaddr((caddr_t)(sc->cfg0_base + SPARSE_WORD_OFFSET(off)), 2)) return; SPARSE_WRITE_WORD(sc->cfg0_base, off, data); alpha_wmb(); } static void apecs_swiz_cfgwritel(u_int b, u_int s, u_int f, u_int r, u_int32_t data) { struct apecs_softc* sc = APECS_SOFTC(apecs0); vm_offset_t off = APECS_SWIZ_CFGOFF(b, s, f, r); if (badaddr((caddr_t)(sc->cfg0_base + SPARSE_LONG_OFFSET(off)), 4)) return; SPARSE_WRITE_LONG(sc->cfg0_base, off, data); alpha_wmb(); } #endif static vm_offset_t apecs_cvt_dense(vm_offset_t addr) { addr &= 0xffffffffUL; return (addr | APECS_PCI_DENSE); } static u_int64_t apecs_read_hae(void) { return apecs_hae_mem & 0xf8000000; } static void apecs_write_hae(u_int64_t hae) { u_int32_t pa = hae; apecs_swiz_set_hae_mem(&pa); } static int apecs_probe(device_t dev); static int apecs_attach(device_t dev); +static struct resource *apecs_alloc_resource(device_t bus, device_t child, + int type, int *rid, u_long start, + u_long end, u_long count, + u_int flags); +static int apecs_release_resource(device_t bus, device_t child, + int type, int rid, struct resource *r); static int apecs_setup_intr(device_t dev, device_t child, struct resource *irq, driver_intr_t *intr, void *arg, void **cookiep); static int apecs_teardown_intr(device_t dev, device_t child, struct resource *irq, void *cookie); + static device_method_t apecs_methods[] = { /* Device interface */ DEVMETHOD(device_probe, apecs_probe), DEVMETHOD(device_attach, apecs_attach), /* Bus interface */ - DEVMETHOD(bus_alloc_resource, pci_alloc_resource), - DEVMETHOD(bus_release_resource, pci_release_resource), + DEVMETHOD(bus_alloc_resource, apecs_alloc_resource), + DEVMETHOD(bus_release_resource, apecs_release_resource), DEVMETHOD(bus_activate_resource, pci_activate_resource), DEVMETHOD(bus_deactivate_resource, pci_deactivate_resource), DEVMETHOD(bus_setup_intr, apecs_setup_intr), DEVMETHOD(bus_teardown_intr, apecs_teardown_intr), { 0, 0 } }; static driver_t apecs_driver = { "apecs", apecs_methods, DRIVER_TYPE_MISC, sizeof(struct apecs_softc), }; void apecs_init() { static int initted = 0; if (initted) return; initted = 1; if (platform.pci_intr_init) platform.pci_intr_init(); chipset = apecs_swiz_chipset; } static int apecs_probe(device_t dev) { int memwidth; if (apecs0) return ENXIO; apecs0 = dev; memwidth = (REGVAL(COMANCHE_GCR) & COMANCHE_GCR_WIDEMEM) != 0 ? 128 : 64; if(memwidth == 64){ device_set_desc(dev, "DECchip 21071 Core Logic chipset"); } else { device_set_desc(dev, "DECchip 21072 Core Logic chipset"); } apecs_hae_mem = REGVAL(EPIC_HAXR1); pci_init_resources(); + isa_init_intr(); - isa0 = device_add_child(dev, "isa", 0, 0); + device_add_child(dev, "pcib", 0, 0); return 0; } -extern void isa_intr(void* frame, u_long vector); - static int apecs_attach(device_t dev) { struct apecs_softc* sc = APECS_SOFTC(dev); apecs_init(); - /* - * the avanti routes interrupts through the isa interrupt - * controller, so we need to special case it - */ - if(hwrpb->rpb_type == ST_DEC_2100_A50) - chipset.intrdev = isa0; - else - chipset.intrdev = apecs0; - sc->dmem_base = APECS_PCI_DENSE; sc->smem_base = APECS_PCI_SPARSE; sc->io_base = APECS_PCI_SIO; sc->cfg0_base = KV(APECS_PCI_CONF); sc->cfg1_base = NULL; set_iointr(alpha_dispatch_intr); snprintf(chipset_type, sizeof(chipset_type), "apecs"); chipset_bwx = 0; chipset_ports = APECS_PCI_SIO; chipset_memory = APECS_PCI_SPARSE; chipset_dense = APECS_PCI_DENSE; chipset_hae_mask = EPIC_HAXR1_EADDR; bus_generic_attach(dev); return 0; } +static struct resource * +apecs_alloc_resource(device_t bus, device_t child, int type, int *rid, + u_long start, u_long end, u_long count, u_int flags) +{ + if (type == SYS_RES_IRQ) + return isa_alloc_intr(bus, child, start); + else + return pci_alloc_resource(bus, child, type, rid, + start, end, count, flags); +} + static int +apecs_release_resource(device_t bus, device_t child, int type, int rid, + struct resource *r) +{ + if (type == SYS_RES_IRQ) + return isa_release_intr(bus, child, r); + else + return pci_release_resource(bus, child, type, rid, r); +} + +static int apecs_setup_intr(device_t dev, device_t child, struct resource *irq, driver_intr_t *intr, void *arg, void **cookiep) { int error; + /* + * the avanti routes interrupts through the isa interrupt + * controller, so we need to special case it + */ + if(hwrpb->rpb_type == ST_DEC_2100_A50) + return isa_setup_intr(dev, child, irq, intr, arg, cookiep); + error = rman_activate_resource(irq); if (error) return error; error = alpha_setup_intr(0x900 + (irq->r_start << 4), intr, arg, cookiep, &intrcnt[INTRCNT_EB64PLUS_IRQ + irq->r_start]); if (error) return error; /* Enable PCI interrupt */ platform.pci_intr_enable(irq->r_start); return 0; } static int apecs_teardown_intr(device_t dev, device_t child, struct resource *irq, void *cookie) { + /* + * the avanti routes interrupts through the isa interrupt + * controller, so we need to special case it + */ + if(hwrpb->rpb_type == ST_DEC_2100_A50) + return isa_teardown_intr(dev, child, irq, cookie); + alpha_teardown_intr(cookie); return rman_deactivate_resource(irq); } DRIVER_MODULE(apecs, root, apecs_driver, apecs_devclass, 0, 0); Index: head/sys/alpha/pci/apecs_pci.c =================================================================== --- head/sys/alpha/pci/apecs_pci.c (nonexistent) +++ head/sys/alpha/pci/apecs_pci.c (revision 45720) @@ -0,0 +1,73 @@ +/*- + * Copyright (c) 1998 Doug Rabson + * 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, SPEAPECSL, 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. + * + * $Id$ + */ + +#include +#include +#include +#include +#include +#include +#include + +static devclass_t pcib_devclass; + +static int +apecs_pcib_probe(device_t dev) +{ + device_set_desc(dev, "2107x PCI host bus adapter"); + + device_add_child(dev, "pci", 0, 0); + + return 0; +} + +static device_method_t apecs_pcib_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, apecs_pcib_probe), + DEVMETHOD(device_attach, bus_generic_attach), + + /* Bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), + DEVMETHOD(bus_release_resource, bus_generic_release_resource), + DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), + DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), + DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), + DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), + + { 0, 0 } +}; + +static driver_t apecs_pcib_driver = { + "pcib", + apecs_pcib_methods, + DRIVER_TYPE_MISC, + 1, +}; + +DRIVER_MODULE(pcib, apecs, apecs_pcib_driver, pcib_devclass, 0, 0); Property changes on: head/sys/alpha/pci/apecs_pci.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: head/sys/alpha/pci/cia.c =================================================================== --- head/sys/alpha/pci/cia.c (revision 45719) +++ head/sys/alpha/pci/cia.c (revision 45720) @@ -1,847 +1,850 @@ /*- * Copyright (c) 1998 Doug Rabson * 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. * - * $Id: cia.c,v 1.14 1998/12/04 22:54:42 archie Exp $ + * $Id: cia.c,v 1.15 1999/03/28 17:52:17 dfr Exp $ */ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, * NASA Ames Research Center. * * 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 NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */ /* * Copyright (c) 1995, 1996 Carnegie-Mellon University. * All rights reserved. * * Author: Chris G. Demetriou * * Permission to use, copy, modify and distribute this software and * its documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. */ #include "opt_cpu.h" #include #include #include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #define KV(pa) ALPHA_PHYS_TO_K0SEG(pa) static devclass_t cia_devclass; static device_t cia0; /* XXX only one for now */ static u_int32_t cia_hae_mem; static int cia_rev, cia_ispyxis, cia_config; struct cia_softc { int junk; /* no softc */ }; #define CIA_SOFTC(dev) (struct cia_softc*) device_get_softc(dev) static alpha_chipset_inb_t cia_bwx_inb, cia_swiz_inb; static alpha_chipset_inw_t cia_bwx_inw, cia_swiz_inw; static alpha_chipset_inl_t cia_bwx_inl, cia_swiz_inl; static alpha_chipset_outb_t cia_bwx_outb, cia_swiz_outb; static alpha_chipset_outw_t cia_bwx_outw, cia_swiz_outw; static alpha_chipset_outl_t cia_bwx_outl, cia_swiz_outl; static alpha_chipset_readb_t cia_bwx_readb, cia_swiz_readb; static alpha_chipset_readw_t cia_bwx_readw, cia_swiz_readw; static alpha_chipset_readl_t cia_bwx_readl, cia_swiz_readl; static alpha_chipset_writeb_t cia_bwx_writeb, cia_swiz_writeb; static alpha_chipset_writew_t cia_bwx_writew, cia_swiz_writew; static alpha_chipset_writel_t cia_bwx_writel, cia_swiz_writel; static alpha_chipset_maxdevs_t cia_bwx_maxdevs, cia_swiz_maxdevs; static alpha_chipset_cfgreadb_t cia_bwx_cfgreadb, cia_swiz_cfgreadb; static alpha_chipset_cfgreadw_t cia_bwx_cfgreadw, cia_swiz_cfgreadw; static alpha_chipset_cfgreadl_t cia_bwx_cfgreadl, cia_swiz_cfgreadl; static alpha_chipset_cfgwriteb_t cia_bwx_cfgwriteb, cia_swiz_cfgwriteb; static alpha_chipset_cfgwritew_t cia_bwx_cfgwritew, cia_swiz_cfgwritew; static alpha_chipset_cfgwritel_t cia_bwx_cfgwritel, cia_swiz_cfgwritel; static alpha_chipset_addrcvt_t cia_cvt_dense, cia_cvt_bwx; static alpha_chipset_read_hae_t cia_read_hae; static alpha_chipset_write_hae_t cia_write_hae; static alpha_chipset_t cia_bwx_chipset = { cia_bwx_inb, cia_bwx_inw, cia_bwx_inl, cia_bwx_outb, cia_bwx_outw, cia_bwx_outl, cia_bwx_readb, cia_bwx_readw, cia_bwx_readl, cia_bwx_writeb, cia_bwx_writew, cia_bwx_writel, cia_bwx_maxdevs, cia_bwx_cfgreadb, cia_bwx_cfgreadw, cia_bwx_cfgreadl, cia_bwx_cfgwriteb, cia_bwx_cfgwritew, cia_bwx_cfgwritel, cia_cvt_dense, cia_cvt_bwx, cia_read_hae, cia_write_hae, }; static alpha_chipset_t cia_swiz_chipset = { cia_swiz_inb, cia_swiz_inw, cia_swiz_inl, cia_swiz_outb, cia_swiz_outw, cia_swiz_outl, cia_swiz_readb, cia_swiz_readw, cia_swiz_readl, cia_swiz_writeb, cia_swiz_writew, cia_swiz_writel, cia_swiz_maxdevs, cia_swiz_cfgreadb, cia_swiz_cfgreadw, cia_swiz_cfgreadl, cia_swiz_cfgwriteb, cia_swiz_cfgwritew, cia_swiz_cfgwritel, cia_cvt_dense, NULL, cia_read_hae, cia_write_hae, }; static u_int8_t cia_bwx_inb(u_int32_t port) { alpha_mb(); return ldbu(KV(CIA_EV56_BWIO+BWX_EV56_INT1 + port)); } static u_int16_t cia_bwx_inw(u_int32_t port) { alpha_mb(); return ldwu(KV(CIA_EV56_BWIO+BWX_EV56_INT2 + port)); } static u_int32_t cia_bwx_inl(u_int32_t port) { alpha_mb(); return ldl(KV(CIA_EV56_BWIO+BWX_EV56_INT4 + port)); } static void cia_bwx_outb(u_int32_t port, u_int8_t data) { stb(KV(CIA_EV56_BWIO+BWX_EV56_INT1 + port), data); alpha_wmb(); } static void cia_bwx_outw(u_int32_t port, u_int16_t data) { stw(KV(CIA_EV56_BWIO+BWX_EV56_INT2 + port), data); alpha_wmb(); } static void cia_bwx_outl(u_int32_t port, u_int32_t data) { stl(KV(CIA_EV56_BWIO+BWX_EV56_INT4 + port), data); alpha_wmb(); } static u_int8_t cia_bwx_readb(u_int32_t pa) { alpha_mb(); return ldbu(KV(CIA_EV56_BWMEM+BWX_EV56_INT1 + pa)); } static u_int16_t cia_bwx_readw(u_int32_t pa) { alpha_mb(); return ldwu(KV(CIA_EV56_BWMEM+BWX_EV56_INT2 + pa)); } static u_int32_t cia_bwx_readl(u_int32_t pa) { alpha_mb(); return ldl(KV(CIA_EV56_BWMEM+BWX_EV56_INT4 + pa)); } static void cia_bwx_writeb(u_int32_t pa, u_int8_t data) { stb(KV(CIA_EV56_BWMEM+BWX_EV56_INT1 + pa), data); alpha_wmb(); } static void cia_bwx_writew(u_int32_t pa, u_int16_t data) { stw(KV(CIA_EV56_BWMEM+BWX_EV56_INT2 + pa), data); alpha_wmb(); } static void cia_bwx_writel(u_int32_t pa, u_int32_t data) { stl(KV(CIA_EV56_BWMEM+BWX_EV56_INT4 + pa), data); alpha_wmb(); } static int cia_bwx_maxdevs(u_int b) { return 12; /* XXX */ } static void cia_clear_abort(void) { /* * Some (apparently-common) revisions of EB164 and AlphaStation * firmware do the Wrong thing with PCI master and target aborts, * which are caused by accesing the configuration space of devices * that don't exist (for example). * * To work around this, we clear the CIA error register's PCI * master and target abort bits before touching PCI configuration * space and check it afterwards. If it indicates a master or target * abort, the device wasn't there so we return 0xffffffff. */ REGVAL(CIA_CSR_CIA_ERR) = CIA_ERR_RCVD_MAS_ABT|CIA_ERR_RCVD_TAR_ABT; alpha_mb(); alpha_pal_draina(); } static int cia_check_abort(void) { u_int32_t errbits; int ba = 0; alpha_pal_draina(); alpha_mb(); errbits = REGVAL(CIA_CSR_CIA_ERR); if (errbits & (CIA_ERR_RCVD_MAS_ABT|CIA_ERR_RCVD_TAR_ABT)) ba = 1; if (errbits) { REGVAL(CIA_CSR_CIA_ERR) = errbits; alpha_mb(); alpha_pal_draina(); } return ba; } #define CIA_BWX_CFGADDR(b, s, f, r) \ KV(((b) ? CIA_EV56_BWCONF1 : CIA_EV56_BWCONF0) \ | ((b) << 16) | ((s) << 11) | ((f) << 8) | (r)) static u_int8_t cia_bwx_cfgreadb(u_int b, u_int s, u_int f, u_int r) { vm_offset_t va = CIA_BWX_CFGADDR(b, s, f, r); u_int8_t data; cia_clear_abort(); if (badaddr((caddr_t)va, 1)) { cia_check_abort(); return ~0; } data = ldbu(va+BWX_EV56_INT1); if (cia_check_abort()) return ~0; return data; } static u_int16_t cia_bwx_cfgreadw(u_int b, u_int s, u_int f, u_int r) { vm_offset_t va = CIA_BWX_CFGADDR(b, s, f, r); u_int16_t data; cia_clear_abort(); if (badaddr((caddr_t)va, 2)) { cia_check_abort(); return ~0; } data = ldwu(va+BWX_EV56_INT2); if (cia_check_abort()) return ~0; return data; } static u_int32_t cia_bwx_cfgreadl(u_int b, u_int s, u_int f, u_int r) { vm_offset_t va = CIA_BWX_CFGADDR(b, s, f, r); u_int32_t data; cia_clear_abort(); if (badaddr((caddr_t)va, 4)) { cia_check_abort(); return ~0; } data = ldl(va+BWX_EV56_INT4); if (cia_check_abort()) return ~0; return data; } static void cia_bwx_cfgwriteb(u_int b, u_int s, u_int f, u_int r, u_int8_t data) { vm_offset_t va = CIA_BWX_CFGADDR(b, s, f, r); cia_clear_abort(); if (badaddr((caddr_t)va, 1)) return; stb(va+BWX_EV56_INT1, data); cia_check_abort(); } static void cia_bwx_cfgwritew(u_int b, u_int s, u_int f, u_int r, u_int16_t data) { vm_offset_t va = CIA_BWX_CFGADDR(b, s, f, r); if (badaddr((caddr_t)va, 2)) return; stw(va+BWX_EV56_INT2, data); cia_check_abort(); } static void cia_bwx_cfgwritel(u_int b, u_int s, u_int f, u_int r, u_int32_t data) { vm_offset_t va = CIA_BWX_CFGADDR(b, s, f, r); if (badaddr((caddr_t)va, 4)) return; stl(va+BWX_EV56_INT4, data); cia_check_abort(); } static u_int8_t cia_swiz_inb(u_int32_t port) { alpha_mb(); return SPARSE_READ_BYTE(KV(CIA_PCI_SIO1), port); } static u_int16_t cia_swiz_inw(u_int32_t port) { alpha_mb(); return SPARSE_READ_WORD(KV(CIA_PCI_SIO1), port); } static u_int32_t cia_swiz_inl(u_int32_t port) { alpha_mb(); return SPARSE_READ_LONG(KV(CIA_PCI_SIO1), port); } static void cia_swiz_outb(u_int32_t port, u_int8_t data) { SPARSE_WRITE_BYTE(KV(CIA_PCI_SIO1), port, data); alpha_wmb(); } static void cia_swiz_outw(u_int32_t port, u_int16_t data) { SPARSE_WRITE_WORD(KV(CIA_PCI_SIO1), port, data); alpha_wmb(); } static void cia_swiz_outl(u_int32_t port, u_int32_t data) { SPARSE_WRITE_LONG(KV(CIA_PCI_SIO1), port, data); alpha_wmb(); } static __inline void cia_swiz_set_hae_mem(u_int32_t *pa) { /* Only bother with region 1 */ #define REG1 (7 << 29) if ((cia_hae_mem & REG1) != (*pa & REG1)) { /* * Seems fairly paranoid but this is what Linux does... */ u_int32_t msb = *pa & REG1; int s = splhigh(); cia_hae_mem = (cia_hae_mem & ~REG1) | msb; REGVAL(CIA_CSR_HAE_MEM) = cia_hae_mem; alpha_mb(); cia_hae_mem = REGVAL(CIA_CSR_HAE_MEM); splx(s); *pa -= msb; } } static u_int8_t cia_swiz_readb(u_int32_t pa) { alpha_mb(); cia_swiz_set_hae_mem(&pa); return SPARSE_READ_BYTE(KV(CIA_PCI_SMEM1), pa); } static u_int16_t cia_swiz_readw(u_int32_t pa) { alpha_mb(); cia_swiz_set_hae_mem(&pa); return SPARSE_READ_WORD(KV(CIA_PCI_SMEM1), pa); } static u_int32_t cia_swiz_readl(u_int32_t pa) { alpha_mb(); cia_swiz_set_hae_mem(&pa); return SPARSE_READ_LONG(KV(CIA_PCI_SMEM1), pa); } static void cia_swiz_writeb(u_int32_t pa, u_int8_t data) { cia_swiz_set_hae_mem(&pa); SPARSE_WRITE_BYTE(KV(CIA_PCI_SMEM1), pa, data); alpha_wmb(); } static void cia_swiz_writew(u_int32_t pa, u_int16_t data) { cia_swiz_set_hae_mem(&pa); SPARSE_WRITE_WORD(KV(CIA_PCI_SMEM1), pa, data); alpha_wmb(); } static void cia_swiz_writel(u_int32_t pa, u_int32_t data) { cia_swiz_set_hae_mem(&pa); SPARSE_WRITE_LONG(KV(CIA_PCI_SMEM1), pa, data); alpha_wmb(); } static int cia_swiz_maxdevs(u_int b) { return 12; /* XXX */ } #define CIA_SWIZ_CFGOFF(b, s, f, r) \ (((b) << 16) | ((s) << 11) | ((f) << 8) | (r)) /* when doing a type 1 pci configuration space access, we * must set a bit in the CIA_CSR_CFG register & clear it * when we're done */ #define CIA_TYPE1_SETUP(b,s,old_cfg) if((b)) { \ do { \ (s) = splhigh(); \ (old_cfg) = REGVAL(CIA_CSR_CFG); \ alpha_mb(); \ REGVAL(CIA_CSR_CFG) = (old_cfg) | 0x1; \ alpha_mb(); \ } while(0); \ } #define CIA_TYPE1_TEARDOWN(b,s,old_cfg) if((b)) { \ do { \ alpha_mb(); \ REGVAL(CIA_CSR_CFG) = (old_cfg); \ alpha_mb(); \ splx((s)); \ } while(0); \ } #define SWIZ_CFGREAD(b, s, f, r, width, type) \ type val = ~0; \ int ipl = 0; \ u_int32_t old_cfg = 0; \ vm_offset_t off = CIA_SWIZ_CFGOFF(b, s, f, r); \ vm_offset_t kv = SPARSE_##width##_ADDRESS(KV(CIA_PCI_CONF), off); \ alpha_mb(); \ CIA_TYPE1_SETUP(b,ipl,old_cfg); \ if (!badaddr((caddr_t)kv, sizeof(type))) { \ val = SPARSE_##width##_EXTRACT(off, SPARSE_READ(kv)); \ } \ CIA_TYPE1_TEARDOWN(b,ipl,old_cfg); \ return val; #define SWIZ_CFGWRITE(b, s, f, r, data, width, type) \ int ipl = 0; \ u_int32_t old_cfg = 0; \ vm_offset_t off = CIA_SWIZ_CFGOFF(b, s, f, r); \ vm_offset_t kv = SPARSE_##width##_ADDRESS(KV(CIA_PCI_CONF), off); \ alpha_mb(); \ CIA_TYPE1_SETUP(b,ipl,old_cfg); \ if (!badaddr((caddr_t)kv, sizeof(type))) { \ SPARSE_WRITE(kv, SPARSE_##width##_INSERT(off, data)); \ alpha_wmb(); \ } \ CIA_TYPE1_TEARDOWN(b,ipl,old_cfg); \ return; static u_int8_t cia_swiz_cfgreadb(u_int b, u_int s, u_int f, u_int r) { SWIZ_CFGREAD(b, s, f, r, BYTE, u_int8_t); } static u_int16_t cia_swiz_cfgreadw(u_int b, u_int s, u_int f, u_int r) { SWIZ_CFGREAD(b, s, f, r, WORD, u_int16_t); } static u_int32_t cia_swiz_cfgreadl(u_int b, u_int s, u_int f, u_int r) { SWIZ_CFGREAD(b, s, f, r, LONG, u_int32_t); } static void cia_swiz_cfgwriteb(u_int b, u_int s, u_int f, u_int r, u_int8_t data) { SWIZ_CFGWRITE(b, s, f, r, data, BYTE, u_int8_t); } static void cia_swiz_cfgwritew(u_int b, u_int s, u_int f, u_int r, u_int16_t data) { SWIZ_CFGWRITE(b, s, f, r, data, WORD, u_int16_t); } static void cia_swiz_cfgwritel(u_int b, u_int s, u_int f, u_int r, u_int32_t data) { SWIZ_CFGWRITE(b, s, f, r, data, LONG, u_int32_t); } vm_offset_t cia_cvt_dense(vm_offset_t addr) { addr &= 0xffffffffUL; return (addr | CIA_PCI_DENSE); } vm_offset_t cia_cvt_bwx(vm_offset_t addr) { addr &= 0xffffffffUL; return (addr |= CIA_EV56_BWMEM); } static u_int64_t cia_read_hae(void) { return cia_hae_mem & REG1; } static void cia_write_hae(u_int64_t hae) { u_int32_t pa = hae; cia_swiz_set_hae_mem(&pa); } static int cia_probe(device_t dev); static int cia_attach(device_t dev); static int cia_setup_intr(device_t dev, device_t child, struct resource *irq, driver_intr_t *intr, void *arg, void **cookiep); static int cia_teardown_intr(device_t dev, device_t child, struct resource *irq, void *cookie); static device_method_t cia_methods[] = { /* Device interface */ DEVMETHOD(device_probe, cia_probe), DEVMETHOD(device_attach, cia_attach), /* Bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_alloc_resource, pci_alloc_resource), DEVMETHOD(bus_release_resource, pci_release_resource), DEVMETHOD(bus_activate_resource, pci_activate_resource), DEVMETHOD(bus_deactivate_resource, pci_deactivate_resource), DEVMETHOD(bus_setup_intr, cia_setup_intr), DEVMETHOD(bus_teardown_intr, cia_teardown_intr), { 0, 0 } }; static driver_t cia_driver = { "cia", cia_methods, DRIVER_TYPE_MISC, sizeof(struct cia_softc), }; void cia_init() { static int initted = 0; if (initted) return; initted = 1; cia_rev = REGVAL(CIA_CSR_REV) & REV_MASK; /* * Determine if we have a Pyxis. Only two systypes can * have this: the EB164 systype (AlphaPC164LX and AlphaPC164SX) * and the DEC_ST550 systype (Miata). */ if ((hwrpb->rpb_type == ST_EB164 && (hwrpb->rpb_variation & SV_ST_MASK) >= SV_ST_ALPHAPC164LX_400) || hwrpb->rpb_type == ST_DEC_550) cia_ispyxis = TRUE; else cia_ispyxis = FALSE; /* * ALCOR/ALCOR2 Revisions >= 2 and Pyxis have the CNFG register. */ if (cia_rev >= 2 || cia_ispyxis) cia_config = REGVAL(CIA_CSR_CNFG); else cia_config = 0; if (alpha_implver() != ALPHA_IMPLVER_EV5 || alpha_amask(ALPHA_AMASK_BWX) || !(cia_config & CNFG_BWEN)) chipset = cia_swiz_chipset; else chipset = cia_bwx_chipset; cia_hae_mem = REGVAL(CIA_CSR_HAE_MEM); #if 0 chipset = cia_swiz_chipset; /* XXX */ cia_ispyxis = 0; #endif if (platform.pci_intr_init) platform.pci_intr_init(); } static int cia_probe(device_t dev) { if (cia0) return ENXIO; cia0 = dev; - device_set_desc(dev, "2117x PCI adapter"); /* XXX */ + device_set_desc(dev, "2117x Core Logic chipset"); /* XXX */ pci_init_resources(); + isa_init_intr(); - device_add_child(dev, "isa", 0, 0); + device_add_child(dev, "pcib", 0, 0); return 0; } static int cia_attach(device_t dev) { - struct cia_softc* sc = CIA_SOFTC(dev); char* name; int pass; cia_init(); - chipset.intrdev = dev; name = cia_ispyxis ? "Pyxis" : "ALCOR/ALCOR2"; if (cia_ispyxis) { name = "Pyxis"; pass = cia_rev; } else { name = "ALCOR/ALCOR2"; pass = cia_rev+1; } printf("cia0: %s, pass %d\n", name, pass); if (cia_config) printf("cia0: extended capabilities: %b\n", cia_config, CIA_CSR_CNFG_BITS); #ifdef DEC_ST550 if (hwrpb->rpb_type == ST_DEC_550 && (hwrpb->rpb_variation & SV_ST_MASK) < SV_ST_MIATA_1_5) { /* * Miata 1 systems have a bug: DMA cannot cross * an 8k boundary! Make sure PCI read prefetching * is disabled on these chips. Note that secondary * PCI busses don't have this problem, because of * the way PPBs handle PCI read requests. * * In the 21174 Technical Reference Manual, this is * actually documented as "Pyxis Pass 1", but apparently * there are chips that report themselves as "Pass 1" * which do not have the bug! Miatas with the Cypress * PCI-ISA bridge (i.e. Miata 1.5 and Miata 2) do not * have the bug, so we use this check. * * XXX We also need to deal with this boundary constraint * XXX in the PCI bus 0 (and ISA) DMA tags, but some * XXX drivers are going to need to be changed first. */ u_int32_t ctrl; /* XXX no bets... */ printf("cia0: WARNING: Pyxis pass 1 DMA bug; no bets...\n"); alpha_mb(); ctrl = REGVAL(CIA_CSR_CTRL); ctrl &= ~(CTRL_RD_TYPE|CTRL_RL_TYPE|CTRL_RM_TYPE); REGVAL(CIA_CSR_CTRL) = ctrl; alpha_mb(); } #endif if (!platform.iointr) /* XXX */ set_iointr(alpha_dispatch_intr); if (cia_ispyxis) { snprintf(chipset_type, sizeof(chipset_type), "pyxis"); chipset_bwx = 1; chipset_ports = CIA_EV56_BWIO; chipset_memory = CIA_EV56_BWMEM; chipset_dense = CIA_PCI_DENSE; } else { snprintf(chipset_type, sizeof(chipset_type), "cia"); chipset_bwx = 0; chipset_ports = CIA_PCI_SIO1; chipset_memory = CIA_PCI_SMEM1; chipset_dense = CIA_PCI_DENSE; chipset_hae_mask = 7L << 29; } bus_generic_attach(dev); return 0; } static int cia_setup_intr(device_t dev, device_t child, struct resource *irq, driver_intr_t *intr, void *arg, void **cookiep) { int error; error = rman_activate_resource(irq); if (error) return error; error = alpha_setup_intr(0x900 + (irq->r_start << 4), intr, arg, cookiep, &intrcnt[INTRCNT_EB164_IRQ + irq->r_start]); if (error) return error; /* Enable PCI interrupt */ platform.pci_intr_enable(irq->r_start); + device_printf(child, "interrupting at CIA irq %d\n", + (int) irq->r_start); + return 0; } static int cia_teardown_intr(device_t dev, device_t child, struct resource *irq, void *cookie) { alpha_teardown_intr(cookie); return rman_deactivate_resource(irq); } DRIVER_MODULE(cia, root, cia_driver, cia_devclass, 0, 0); - Index: head/sys/alpha/pci/cia_pci.c =================================================================== --- head/sys/alpha/pci/cia_pci.c (nonexistent) +++ head/sys/alpha/pci/cia_pci.c (revision 45720) @@ -0,0 +1,73 @@ +/*- + * Copyright (c) 1998 Doug Rabson + * 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. + * + * $Id$ + */ + +#include +#include +#include +#include +#include +#include +#include + +static devclass_t pcib_devclass; + +static int +cia_pcib_probe(device_t dev) +{ + device_set_desc(dev, "2117x PCI host bus adapter"); + + device_add_child(dev, "pci", 0, 0); + + return 0; +} + +static device_method_t cia_pcib_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, cia_pcib_probe), + DEVMETHOD(device_attach, bus_generic_attach), + + /* Bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), + DEVMETHOD(bus_release_resource, bus_generic_release_resource), + DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), + DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), + DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), + DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), + + { 0, 0 } +}; + +static driver_t cia_pcib_driver = { + "pcib", + cia_pcib_methods, + DRIVER_TYPE_MISC, + 1, +}; + +DRIVER_MODULE(pcib, cia, cia_pcib_driver, pcib_devclass, 0, 0); Property changes on: head/sys/alpha/pci/cia_pci.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: head/sys/alpha/pci/lca.c =================================================================== --- head/sys/alpha/pci/lca.c (revision 45719) +++ head/sys/alpha/pci/lca.c (revision 45720) @@ -1,412 +1,442 @@ /*- * Copyright (c) 1998 Doug Rabson * 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. * - * $Id: lca.c,v 1.4 1998/11/15 18:25:16 dfr Exp $ + * $Id: lca.c,v 1.5 1998/12/04 22:54:42 archie Exp $ */ #include #include #include #include #include #include #include #include -#include +#include #include +#include #include +#include #define KV(pa) ALPHA_PHYS_TO_K0SEG(pa) static devclass_t lca_devclass; static device_t lca0; /* XXX only one for now */ -static device_t isa0; struct lca_softc { int junk; }; #define LCA_SOFTC(dev) (struct lca_softc*) device_get_softc(dev) static alpha_chipset_inb_t lca_inb; static alpha_chipset_inw_t lca_inw; static alpha_chipset_inl_t lca_inl; static alpha_chipset_outb_t lca_outb; static alpha_chipset_outw_t lca_outw; static alpha_chipset_outl_t lca_outl; static alpha_chipset_readb_t lca_readb; static alpha_chipset_readw_t lca_readw; static alpha_chipset_readl_t lca_readl; static alpha_chipset_writeb_t lca_writeb; static alpha_chipset_writew_t lca_writew; static alpha_chipset_writel_t lca_writel; static alpha_chipset_maxdevs_t lca_maxdevs; static alpha_chipset_cfgreadb_t lca_cfgreadb; static alpha_chipset_cfgreadw_t lca_cfgreadw; static alpha_chipset_cfgreadl_t lca_cfgreadl; static alpha_chipset_cfgwriteb_t lca_cfgwriteb; static alpha_chipset_cfgwritew_t lca_cfgwritew; static alpha_chipset_cfgwritel_t lca_cfgwritel; static alpha_chipset_addrcvt_t lca_cvt_dense; static alpha_chipset_read_hae_t lca_read_hae; static alpha_chipset_write_hae_t lca_write_hae; static alpha_chipset_t lca_chipset = { lca_inb, lca_inw, lca_inl, lca_outb, lca_outw, lca_outl, lca_readb, lca_readw, lca_readl, lca_writeb, lca_writew, lca_writel, lca_maxdevs, lca_cfgreadb, lca_cfgreadw, lca_cfgreadl, lca_cfgwriteb, lca_cfgwritew, lca_cfgwritel, lca_cvt_dense, NULL, lca_read_hae, lca_write_hae, }; static u_int8_t lca_inb(u_int32_t port) { alpha_mb(); return SPARSE_READ_BYTE(KV(LCA_PCI_SIO), port); } static u_int16_t lca_inw(u_int32_t port) { alpha_mb(); return SPARSE_READ_WORD(KV(LCA_PCI_SIO), port); } static u_int32_t lca_inl(u_int32_t port) { alpha_mb(); return SPARSE_READ_LONG(KV(LCA_PCI_SIO), port); } static void lca_outb(u_int32_t port, u_int8_t data) { SPARSE_WRITE_BYTE(KV(LCA_PCI_SIO), port, data); alpha_wmb(); } static void lca_outw(u_int32_t port, u_int16_t data) { SPARSE_WRITE_WORD(KV(LCA_PCI_SIO), port, data); alpha_wmb(); } static void lca_outl(u_int32_t port, u_int32_t data) { SPARSE_WRITE_LONG(KV(LCA_PCI_SIO), port, data); alpha_wmb(); } /* * The LCA HAE is write-only. According to NetBSD, this is where it starts. */ static u_int32_t lca_hae_mem = 0x80000000; /* * The first 16Mb ignores the HAE. The next 112Mb uses the HAE to set * the high bits of the PCI address. */ #define REG1 (1UL << 24) static __inline void lca_set_hae_mem(u_int32_t *pa) { int s; u_int32_t msb; if(*pa >= REG1){ msb = *pa & 0xf8000000; *pa -= msb; s = splhigh(); if (msb != lca_hae_mem) { lca_hae_mem = msb; REGVAL(LCA_IOC_HAE) = lca_hae_mem; alpha_mb(); alpha_mb(); } splx(s); } } static u_int8_t lca_readb(u_int32_t pa) { alpha_mb(); lca_set_hae_mem(&pa); return SPARSE_READ_BYTE(KV(LCA_PCI_SPARSE), pa); } static u_int16_t lca_readw(u_int32_t pa) { alpha_mb(); lca_set_hae_mem(&pa); return SPARSE_READ_WORD(KV(LCA_PCI_SPARSE), pa); } static u_int32_t lca_readl(u_int32_t pa) { alpha_mb(); lca_set_hae_mem(&pa); return SPARSE_READ_LONG(KV(LCA_PCI_SPARSE), pa); } static void lca_writeb(u_int32_t pa, u_int8_t data) { lca_set_hae_mem(&pa); SPARSE_WRITE_BYTE(KV(LCA_PCI_SPARSE), pa, data); alpha_wmb(); } static void lca_writew(u_int32_t pa, u_int16_t data) { lca_set_hae_mem(&pa); SPARSE_WRITE_WORD(KV(LCA_PCI_SPARSE), pa, data); alpha_wmb(); } static void lca_writel(u_int32_t pa, u_int32_t data) { lca_set_hae_mem(&pa); SPARSE_WRITE_LONG(KV(LCA_PCI_SPARSE), pa, data); alpha_wmb(); } static int lca_maxdevs(u_int b) { return 12; /* XXX */ } #define LCA_CFGOFF(b, s, f, r) \ ((b) ? (((b) << 16) | ((s) << 11) | ((f) << 8) | (r)) \ : ((1 << ((s) + 11)) | ((f) << 8) | (r))) #define LCA_TYPE1_SETUP(b,s) if ((b)) { \ do { \ (s) = splhigh(); \ alpha_mb(); \ REGVAL(LCA_IOC_CONF) = 1; \ alpha_mb(); \ } while(0); \ } #define LCA_TYPE1_TEARDOWN(b,s) if ((b)) { \ do { \ alpha_mb(); \ REGVAL(LCA_IOC_CONF) = 0; \ alpha_mb(); \ splx((s)); \ } while(0); \ } #define CFGREAD(b, s, f, r, width, type) \ type val = ~0; \ int ipl = 0; \ vm_offset_t off = LCA_CFGOFF(b, s, f, r); \ vm_offset_t kv = SPARSE_##width##_ADDRESS(KV(LCA_PCI_CONF), off); \ alpha_mb(); \ LCA_TYPE1_SETUP(b,ipl); \ if (!badaddr((caddr_t)kv, sizeof(type))) { \ val = SPARSE_##width##_EXTRACT(off, SPARSE_READ(kv)); \ } \ LCA_TYPE1_TEARDOWN(b,ipl); \ return val #define CFGWRITE(b, s, f, r, data, width, type) \ int ipl = 0; \ vm_offset_t off = LCA_CFGOFF(b, s, f, r); \ vm_offset_t kv = SPARSE_##width##_ADDRESS(KV(LCA_PCI_CONF), off); \ alpha_mb(); \ LCA_TYPE1_SETUP(b,ipl); \ if (!badaddr((caddr_t)kv, sizeof(type))) { \ SPARSE_WRITE(kv, SPARSE_##width##_INSERT(off, data)); \ alpha_wmb(); \ } \ LCA_TYPE1_TEARDOWN(b,ipl); \ return static u_int8_t lca_cfgreadb(u_int b, u_int s, u_int f, u_int r) { CFGREAD(b, s, f, r, BYTE, u_int8_t); } static u_int16_t lca_cfgreadw(u_int b, u_int s, u_int f, u_int r) { CFGREAD(b, s, f, r, WORD, u_int16_t); } static u_int32_t lca_cfgreadl(u_int b, u_int s, u_int f, u_int r) { CFGREAD(b, s, f, r, LONG, u_int32_t); } static void lca_cfgwriteb(u_int b, u_int s, u_int f, u_int r, u_int8_t data) { CFGWRITE(b, s, f, r, data, BYTE, u_int8_t); } static void lca_cfgwritew(u_int b, u_int s, u_int f, u_int r, u_int16_t data) { CFGWRITE(b, s, f, r, data, WORD, u_int16_t); } static void lca_cfgwritel(u_int b, u_int s, u_int f, u_int r, u_int32_t data) { CFGWRITE(b, s, f, r, data, LONG, u_int16_t); } static vm_offset_t lca_cvt_dense(vm_offset_t addr) { addr &= 0xffffffffUL; return (addr | LCA_PCI_DENSE); } static u_int64_t lca_read_hae(void) { return lca_hae_mem & 0xf8000000; } static void lca_write_hae(u_int64_t hae) { u_int32_t pa = hae; lca_set_hae_mem(&pa); } static int lca_probe(device_t dev); static int lca_attach(device_t dev); -static void *lca_create_intr(device_t dev, device_t child, int irq, driver_intr_t *intr, void *arg); -static int lca_connect_intr(device_t dev, void* ih); +static struct resource *lca_alloc_resource(device_t bus, device_t child, + int type, int *rid, u_long start, + u_long end, u_long count, + u_int flags); +static int lca_release_resource(device_t bus, device_t child, + int type, int rid, struct resource *r); static device_method_t lca_methods[] = { /* Device interface */ DEVMETHOD(device_probe, lca_probe), DEVMETHOD(device_attach, lca_attach), /* Bus interface */ - DEVMETHOD(bus_alloc_resource, pci_alloc_resource), - DEVMETHOD(bus_release_resource, pci_release_resource), + DEVMETHOD(bus_alloc_resource, lca_alloc_resource), + DEVMETHOD(bus_release_resource, lca_release_resource), DEVMETHOD(bus_activate_resource, pci_activate_resource), DEVMETHOD(bus_deactivate_resource, pci_deactivate_resource), + DEVMETHOD(bus_setup_intr, isa_setup_intr), + DEVMETHOD(bus_teardown_intr, isa_teardown_intr), { 0, 0 } }; static driver_t lca_driver = { "lca", lca_methods, DRIVER_TYPE_MISC, sizeof(struct lca_softc), }; void lca_init() { static int initted = 0; if (initted) return; initted = 1; /* Type 0 PCI conf access. */ REGVAL64(LCA_IOC_CONF) = 0; if (platform.pci_intr_init) platform.pci_intr_init(); chipset = lca_chipset; } static int lca_probe(device_t dev) { if (lca0) return ENXIO; lca0 = dev; - device_set_desc(dev, "21066 PCI adapter"); /* XXX */ + device_set_desc(dev, "21066 Core Logic chipset"); /* XXX */ - isa0 = device_add_child(dev, "isa", 0, 0); + pci_init_resources(); + isa_init_intr(); + device_add_child(dev, "pcib", 0, 0); + return 0; } static int lca_attach(device_t dev) { struct lca_softc* sc = LCA_SOFTC(dev); lca_init(); - chipset.intrdev = isa0; set_iointr(alpha_dispatch_intr); snprintf(chipset_type, sizeof(chipset_type), "lca"); chipset_bwx = 0; chipset_ports = LCA_PCI_SIO; chipset_memory = LCA_PCI_SPARSE; chipset_dense = LCA_PCI_DENSE; chipset_hae_mask = IOC_HAE_ADDREXT; bus_generic_attach(dev); return 0; +} + +static struct resource * +lca_alloc_resource(device_t bus, device_t child, int type, int *rid, + u_long start, u_long end, u_long count, u_int flags) +{ + if (type == SYS_RES_IRQ) + return isa_alloc_intr(bus, child, start); + else + return pci_alloc_resource(bus, child, type, rid, + start, end, count, flags); +} + +static int +lca_release_resource(device_t bus, device_t child, int type, int rid, + struct resource *r) +{ + if (type == SYS_RES_IRQ) + return isa_release_intr(bus, child, r); + else + return pci_release_resource(bus, child, type, rid, r); } DRIVER_MODULE(lca, root, lca_driver, lca_devclass, 0, 0); Index: head/sys/alpha/pci/lca_pci.c =================================================================== --- head/sys/alpha/pci/lca_pci.c (nonexistent) +++ head/sys/alpha/pci/lca_pci.c (revision 45720) @@ -0,0 +1,73 @@ +/*- + * Copyright (c) 1998 Doug Rabson + * 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, SPELCAL, 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. + * + * $Id$ + */ + +#include +#include +#include +#include +#include +#include +#include + +static devclass_t pcib_devclass; + +static int +lca_pcib_probe(device_t dev) +{ + device_set_desc(dev, "21066 PCI host bus adapter"); + + device_add_child(dev, "pci", 0, 0); + + return 0; +} + +static device_method_t lca_pcib_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, lca_pcib_probe), + DEVMETHOD(device_attach, bus_generic_attach), + + /* Bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), + DEVMETHOD(bus_release_resource, bus_generic_release_resource), + DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), + DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), + DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), + DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), + + { 0, 0 } +}; + +static driver_t lca_pcib_driver = { + "pcib", + lca_pcib_methods, + DRIVER_TYPE_MISC, + 1, +}; + +DRIVER_MODULE(pcib, lca, lca_pcib_driver, pcib_devclass, 0, 0); Property changes on: head/sys/alpha/pci/lca_pci.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: head/sys/alpha/pci/pcibus.c =================================================================== --- head/sys/alpha/pci/pcibus.c (revision 45719) +++ head/sys/alpha/pci/pcibus.c (revision 45720) @@ -1,325 +1,383 @@ /* * Copyright (c) 1997, Stefan Esser * 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 unmodified, 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 ``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. * - * $Id: pcibus.c,v 1.7 1998/11/18 23:53:12 dfr Exp $ + * $Id: pcibus.c,v 1.8 1998/12/27 18:03:29 dfr Exp $ * */ #include #include #include #include #include +#include #include #include #include #include #include #include #include +#include char chipset_type[10]; int chipset_bwx = 0; long chipset_ports = 0; long chipset_memory = 0; long chipset_dense = 0; long chipset_hae_mask = 0; SYSCTL_NODE(_hw, OID_AUTO, chipset, CTLFLAG_RW, 0, "PCI chipset information"); SYSCTL_STRING(_hw_chipset, OID_AUTO, type, CTLFLAG_RD, chipset_type, 0, "PCI chipset type"); SYSCTL_INT(_hw_chipset, OID_AUTO, bwx, CTLFLAG_RD, &chipset_bwx, 0, "PCI chipset supports BWX access"); SYSCTL_LONG(_hw_chipset, OID_AUTO, ports, CTLFLAG_RD, &chipset_ports, "PCI chipset port address"); SYSCTL_LONG(_hw_chipset, OID_AUTO, memory, CTLFLAG_RD, &chipset_memory, "PCI chipset memory address"); SYSCTL_LONG(_hw_chipset, OID_AUTO, dense, CTLFLAG_RD, &chipset_dense, "PCI chipset dense memory address"); SYSCTL_LONG(_hw_chipset, OID_AUTO, hae_mask, CTLFLAG_RD, &chipset_hae_mask, "PCI chipset mask for HAE register"); #ifdef notyet /* return max number of devices on the bus */ int pci_maxdevs(pcicfgregs *cfg) { return chipset.maxdevs(cfg->bus); } #endif /* read configuration space register */ int pci_cfgread(pcicfgregs *cfg, int reg, int bytes) { switch (bytes) { case 1: return chipset.cfgreadb(cfg->bus, cfg->slot, cfg->func, reg); case 2: return chipset.cfgreadw(cfg->bus, cfg->slot, cfg->func, reg); case 4: return chipset.cfgreadl(cfg->bus, cfg->slot, cfg->func, reg); } return ~0; } /* write configuration space register */ void pci_cfgwrite(pcicfgregs *cfg, int reg, int data, int bytes) { switch (bytes) { case 1: return chipset.cfgwriteb(cfg->bus, cfg->slot, cfg->func, reg, data); case 2: return chipset.cfgwritew(cfg->bus, cfg->slot, cfg->func, reg, data); case 4: return chipset.cfgwritel(cfg->bus, cfg->slot, cfg->func, reg, data); } } int pci_cfgopen(void) { return 1; } vm_offset_t pci_cvt_to_dense(vm_offset_t sparse) { if(chipset.cvt_to_dense) return chipset.cvt_to_dense(sparse); else return NULL; } vm_offset_t pci_cvt_to_bwx(vm_offset_t sparse) { if(chipset.cvt_to_bwx) return chipset.cvt_to_bwx(sparse); else return NULL; } +#if 0 + /* * These can disappear when I update the pci code to use the new * device framework. */ struct intrec * intr_create(void *dev_instance, int irq, inthand2_t handler, void *arg, intrmask_t *maskptr, int flags) { struct resource *res; device_t pcib = chipset.intrdev; int zero = 0; void *cookie; res = BUS_ALLOC_RESOURCE(pcib, NULL, SYS_RES_IRQ, &zero, irq, irq, 1, RF_SHAREABLE | RF_ACTIVE); if (BUS_SETUP_INTR(pcib, pcib, res, (driver_intr_t *)handler, arg, &cookie)) return 0; return (struct intrec *)cookie; } int intr_connect(struct intrec *idesc) { /* * intr_create has already connected it (doesn't matter for the * only consumer of this interface (pci). */ return 0; } +#endif + void alpha_platform_assign_pciintr(pcicfgregs *cfg) { if(platform.pci_intr_map) platform.pci_intr_map((void *)cfg); } +int +alpha_platform_setup_ide_intr(int chan, driver_intr_t *fn, void *arg) +{ + if (platform.pci_setup_ide_intr) + return platform.pci_setup_ide_intr(chan, fn, arg); + else { + int irqs[2] = { 14, 15 }; + void *junk; + struct resource *res; + res = isa_alloc_intr(0, 0, irqs[chan]); + return isa_setup_intr(0, 0, res, fn, arg, &junk); + } +} + static struct rman irq_rman, port_rman, mem_rman; void pci_init_resources() { irq_rman.rm_start = 0; irq_rman.rm_end = 32; irq_rman.rm_type = RMAN_ARRAY; irq_rman.rm_descr = "PCI Interrupt request lines"; if (rman_init(&irq_rman) || rman_manage_region(&irq_rman, 0, 31)) - panic("cia_probe irq_rman"); + panic("pci_init_resources irq_rman"); port_rman.rm_start = 0; - port_rman.rm_end = 0xffff; + port_rman.rm_end = ~0u; port_rman.rm_type = RMAN_ARRAY; port_rman.rm_descr = "I/O ports"; if (rman_init(&port_rman) - || rman_manage_region(&port_rman, 0, 0xffff)) - panic("cia_probe port_rman"); + || rman_manage_region(&port_rman, 0x0, (1L << 32))) + panic("pci_init_resources port_rman"); mem_rman.rm_start = 0; mem_rman.rm_end = ~0u; mem_rman.rm_type = RMAN_ARRAY; - mem_rman.rm_descr = "I/O memory addresses"; + mem_rman.rm_descr = "I/O memory"; if (rman_init(&mem_rman) || rman_manage_region(&mem_rman, 0x0, (1L << 32))) - panic("cia_probe mem_rman"); + panic("pci_init_resources mem_rman"); } /* * Allocate a resource on behalf of child. NB: child is usually going to be a * child of one of our descendants, not a direct child of the pci chipset. */ struct resource * pci_alloc_resource(device_t bus, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { struct rman *rm; + struct resource *rv; switch (type) { case SYS_RES_IRQ: rm = &irq_rman; break; case SYS_RES_IOPORT: rm = &port_rman; break; case SYS_RES_MEMORY: rm = &mem_rman; break; default: return 0; } - return rman_reserve_resource(rm, start, end, count, flags, child); + rv = rman_reserve_resource(rm, start, end, count, flags, child); + if (rv == 0) + return 0; + + if (type == SYS_RES_MEMORY) { + rman_set_bustag(rv, ALPHA_BUS_SPACE_MEM); + rman_set_bushandle(rv, rv->r_start); + rman_set_virtual(rv, (void *) rv->r_start); /* XXX */ + } else if (type == SYS_RES_IOPORT) { + rman_set_bustag(rv, ALPHA_BUS_SPACE_IO); + rman_set_bushandle(rv, rv->r_start); + } + + return rv; } int pci_activate_resource(device_t bus, device_t child, int type, int rid, struct resource *r) { return (rman_activate_resource(r)); } int pci_deactivate_resource(device_t bus, device_t child, int type, int rid, struct resource *r) { return (rman_deactivate_resource(r)); } int pci_release_resource(device_t bus, device_t child, int type, int rid, struct resource *r) { return (rman_release_resource(r)); } void memcpy_fromio(void *d, u_int32_t s, size_t size) { char *cp = d; while (size--) *cp++ = readb(s++); } void memcpy_toio(u_int32_t d, void *s, size_t size) { char *cp = s; while (size--) writeb(d++, *cp++); } void +memcpy_io(u_int32_t d, u_int32_t s, size_t size) +{ + while (size--) + writeb(d++, readb(s++)); +} + +void memset_io(u_int32_t d, int val, size_t size) { while (size--) writeb(d++, val); } +void +memsetw(void *d, int val, size_t size) +{ + u_int16_t *sp = d; + + while (size--) + *sp++ = val; +} + +void +memsetw_io(u_int32_t d, int val, size_t size) +{ + while (size--) { + writew(d, val); + d += sizeof(u_int16_t); + } +} + #include "opt_ddb.h" #ifdef DDB #include DB_COMMAND(in, db_in) { int c; int size; - u_int32_t val; if (!have_addr) return; size = -1; while (c = *modif++) { switch (c) { case 'b': size = 1; break; case 'w': size = 2; break; case 'l': size = 4; break; } } if (size < 0) { db_printf("bad size\n"); return; } if (count <= 0) count = 1; while (--count >= 0) { - db_printf("%08x:\t", addr); + db_printf("%08lx:\t", addr); switch (size) { case 1: db_printf("%02x\n", inb(addr)); break; case 2: db_printf("%04x\n", inw(addr)); break; case 4: db_printf("%08x\n", inl(addr)); break; } } } #endif Index: head/sys/alpha/tc/tc.c =================================================================== --- head/sys/alpha/tc/tc.c (revision 45719) +++ head/sys/alpha/tc/tc.c (revision 45720) @@ -1,704 +1,702 @@ -/* $Id$ */ +/* $Id: tc.c,v 1.1 1998/08/20 08:27:10 dfr Exp $ */ /* * Copyright (c) 1994, 1995, 1996 Carnegie-Mellon University. * All rights reserved. * * Author: Chris G. Demetriou * * Permission to use, copy, modify and distribute this software and * its documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. */ #include "opt_cpu.h" #include #include #include #include #include #include #include #include #include #include #include /*#include */ #define KV(pa) ALPHA_PHYS_TO_K0SEG(pa) static devclass_t tc_devclass; device_t tc0; /* XXX only one for now */ struct tc_softc { device_t sc_dv; int sc_speed; int sc_nslots; int nbuiltins; struct tc_builtin *builtins; struct tc_slotdesc *sc_slots; void (*sc_intr_establish) __P((struct device *, void *, tc_intrlevel_t, int (*)(void *), void *)); void (*sc_intr_disestablish) __P((struct device *, void *)); /* bus_dma_tag_t (*sc_get_dma_tag) __P((int)); */ }; #define NTC_ROMOFFS 2 static tc_offset_t tc_slot_romoffs[NTC_ROMOFFS] = { TC_SLOT_ROM, TC_SLOT_PROTOROM, }; #define TC_SOFTC(dev) (struct tc_softc*) device_get_softc(dev) static int tc_probe(device_t dev); static int tc_attach(device_t dev); static void tc_print_child(device_t bus, device_t dev); static driver_intr_t tc_intr; int tc_checkslot( tc_addr_t slotbase, char *namep); static device_method_t tc_methods[] = { /* Device interface */ DEVMETHOD(device_probe, tc_probe), DEVMETHOD(device_attach, tc_attach), /* Bus interface */ DEVMETHOD(bus_print_child, tc_print_child), { 0, 0 }, }; static driver_t tc_driver = { "tc", tc_methods, DRIVER_TYPE_MISC, sizeof(struct tc_softc), }; #define C(x) ((void *)(u_long)x) int tc_intrnull __P((void *)); struct tcintr { int (*tci_func) __P((void *)); void *tci_arg; }; #ifdef DEC_3000_300 void tc_3000_300_intr_setup __P((void)); void tc_3000_300_intr_establish __P((struct device *, void *, tc_intrlevel_t, int (*)(void *), void *)); void tc_3000_300_intr_disestablish __P((struct device *, void *)); void tc_3000_300_iointr __P((void *, unsigned long)); #define DEC_3000_300_IOASIC_ADDR KV(0x1a0000000) struct tc_slotdesc tc_3000_300_slots[] = { { KV(0x100000000), C(TC_3000_300_DEV_OPT0), }, /* 0 - opt slot 0 */ { KV(0x120000000), C(TC_3000_300_DEV_OPT1), }, /* 1 - opt slot 1 */ { KV(0x180000000), C(TC_3000_300_DEV_BOGUS), }, /* 2 - TCDS ASIC */ { KV(0x1a0000000), C(TC_3000_300_DEV_BOGUS), }, /* 3 - IOCTL ASIC */ { KV(0x1c0000000), C(TC_3000_300_DEV_CXTURBO), }, /* 4 - CXTurbo */ }; int tc_3000_300_nslots = sizeof(tc_3000_300_slots) / sizeof(tc_3000_300_slots[0]); struct tc_builtin tc_3000_300_builtins[] = { #ifdef notyet { "PMAGB-BA", 4, 0x02000000, C(TC_3000_300_DEV_CXTURBO), }, #endif { "ioasic", 3, 0x00000000, C(TC_3000_300_DEV_IOASIC), }, { "tcds", 2, 0x00000000, C(TC_3000_300_DEV_TCDS), }, }; int tc_3000_300_nbuiltins = sizeof(tc_3000_300_builtins) / sizeof(tc_3000_300_builtins[0]); struct tcintr tc_3000_300_intr[TC_3000_300_NCOOKIES]; #endif /* DEC_3000_300 */ #ifdef DEC_3000_500 void tc_3000_500_intr_setup __P((void)); void tc_3000_500_intr_establish __P((struct device *, void *, tc_intrlevel_t, int (*)(void *), void *)); void tc_3000_500_intr_disestablish __P((struct device *, void *)); void tc_3000_500_iointr __P((void *, unsigned long)); struct tc_slotdesc tc_3000_500_slots[] = { { KV(0x100000000), C(TC_3000_500_DEV_OPT0), }, /* 0 - opt slot 0 */ { KV(0x120000000), C(TC_3000_500_DEV_OPT1), }, /* 1 - opt slot 1 */ { KV(0x140000000), C(TC_3000_500_DEV_OPT2), }, /* 2 - opt slot 2 */ { KV(0x160000000), C(TC_3000_500_DEV_OPT3), }, /* 3 - opt slot 3 */ { KV(0x180000000), C(TC_3000_500_DEV_OPT4), }, /* 4 - opt slot 4 */ { KV(0x1a0000000), C(TC_3000_500_DEV_OPT5), }, /* 5 - opt slot 5 */ { KV(0x1c0000000), C(TC_3000_500_DEV_BOGUS), }, /* 6 - TCDS ASIC */ { KV(0x1e0000000), C(TC_3000_500_DEV_BOGUS), }, /* 7 - IOCTL ASIC */ }; int tc_3000_500_nslots = sizeof(tc_3000_500_slots) / sizeof(tc_3000_500_slots[0]); struct tc_builtin tc_3000_500_builtins[] = { { "ioasic", 7, 0x00000000, C(TC_3000_500_DEV_IOASIC), }, #ifdef notyet { "PMAGB-BA", 7, 0x02000000, C(TC_3000_500_DEV_CXTURBO), }, #endif { "tcds", 6, 0x00000000, C(TC_3000_500_DEV_TCDS), }, }; int tc_3000_500_nbuiltins = sizeof(tc_3000_500_builtins) / sizeof(tc_3000_500_builtins[0]); u_int32_t tc_3000_500_intrbits[TC_3000_500_NCOOKIES] = { TC_3000_500_IR_OPT0, TC_3000_500_IR_OPT1, TC_3000_500_IR_OPT2, TC_3000_500_IR_OPT3, TC_3000_500_IR_OPT4, TC_3000_500_IR_OPT5, TC_3000_500_IR_TCDS, TC_3000_500_IR_IOASIC, TC_3000_500_IR_CXTURBO, }; struct tcintr tc_3000_500_intr[TC_3000_500_NCOOKIES]; u_int32_t tc_3000_500_imask; /* intrs we want to ignore; mirrors IMR. */ #endif /* DEC_3000_500 */ #ifdef DEC_3000_300 void tc_3000_300_intr_setup() { volatile u_int32_t *imskp; u_long i; /* * Disable all interrupts that we can (can't disable builtins). */ imskp = (volatile u_int32_t *)IOASIC_REG_IMSK(DEC_3000_300_IOASIC_ADDR); *imskp &= ~(IOASIC_INTR_300_OPT0 | IOASIC_INTR_300_OPT1); /* * Set up interrupt handlers. */ for (i = 0; i < TC_3000_300_NCOOKIES; i++) { tc_3000_300_intr[i].tci_func = tc_intrnull; tc_3000_300_intr[i].tci_arg = (void *)i; } } void tc_3000_300_intr_establish(tcadev, cookie, level, func, arg) struct device *tcadev; void *cookie, *arg; tc_intrlevel_t level; int (*func) __P((void *)); { volatile u_int32_t *imskp; u_long dev = (u_long)cookie; #ifdef DIAGNOSTIC /* XXX bounds-check cookie. */ #endif if (tc_3000_300_intr[dev].tci_func != tc_intrnull) panic("tc_3000_300_intr_establish: cookie %d twice", dev); tc_3000_300_intr[dev].tci_func = func; tc_3000_300_intr[dev].tci_arg = arg; imskp = (volatile u_int32_t *)IOASIC_REG_IMSK(DEC_3000_300_IOASIC_ADDR); switch (dev) { case TC_3000_300_DEV_OPT0: *imskp |= IOASIC_INTR_300_OPT0; break; case TC_3000_300_DEV_OPT1: *imskp |= IOASIC_INTR_300_OPT1; break; default: /* interrupts for builtins always enabled */ break; } } void tc_3000_300_intr_disestablish(tcadev, cookie) struct device *tcadev; void *cookie; { volatile u_int32_t *imskp; u_long dev = (u_long)cookie; #ifdef DIAGNOSTIC /* XXX bounds-check cookie. */ #endif if (tc_3000_300_intr[dev].tci_func == tc_intrnull) panic("tc_3000_300_intr_disestablish: cookie %d bad intr", dev); imskp = (volatile u_int32_t *)IOASIC_REG_IMSK(DEC_3000_300_IOASIC_ADDR); switch (dev) { case TC_3000_300_DEV_OPT0: *imskp &= ~IOASIC_INTR_300_OPT0; break; case TC_3000_300_DEV_OPT1: *imskp &= ~IOASIC_INTR_300_OPT1; break; default: /* interrupts for builtins always enabled */ break; } tc_3000_300_intr[dev].tci_func = tc_intrnull; tc_3000_300_intr[dev].tci_arg = (void *)dev; } void tc_3000_300_iointr(framep, vec) void *framep; unsigned long vec; { u_int32_t tcir, ioasicir, ioasicimr; int ifound; #ifdef DIAGNOSTIC int s; if (vec != 0x800) panic("INVALID ASSUMPTION: vec 0x%lx, not 0x800", vec); s = splhigh(); if (s != ALPHA_PSL_IPL_IO) panic("INVALID ASSUMPTION: IPL %d, not %d", s, ALPHA_PSL_IPL_IO); splx(s); #endif do { tc_syncbus(); /* find out what interrupts/errors occurred */ tcir = *(volatile u_int32_t *)TC_3000_300_IR; ioasicir = *(volatile u_int32_t *) IOASIC_REG_INTR(DEC_3000_300_IOASIC_ADDR); ioasicimr = *(volatile u_int32_t *) IOASIC_REG_IMSK(DEC_3000_300_IOASIC_ADDR); tc_mb(); /* Ignore interrupts that aren't enabled out. */ ioasicir &= ioasicimr; /* clear the interrupts/errors we found. */ *(volatile u_int32_t *)TC_3000_300_IR = tcir; /* XXX can't clear TC option slot interrupts here? */ tc_wmb(); ifound = 0; #define CHECKINTR(slot, flag) \ if (flag) { \ ifound = 1; \ (*tc_3000_300_intr[slot].tci_func) \ (tc_3000_300_intr[slot].tci_arg); \ } /* Do them in order of priority; highest slot # first. */ CHECKINTR(TC_3000_300_DEV_CXTURBO, tcir & TC_3000_300_IR_CXTURBO); CHECKINTR(TC_3000_300_DEV_IOASIC, (tcir & TC_3000_300_IR_IOASIC) && (ioasicir & ~(IOASIC_INTR_300_OPT1|IOASIC_INTR_300_OPT0))); CHECKINTR(TC_3000_300_DEV_TCDS, tcir & TC_3000_300_IR_TCDS); CHECKINTR(TC_3000_300_DEV_OPT1, ioasicir & IOASIC_INTR_300_OPT1); CHECKINTR(TC_3000_300_DEV_OPT0, ioasicir & IOASIC_INTR_300_OPT0); #undef CHECKINTR #ifdef DIAGNOSTIC #define PRINTINTR(msg, bits) \ if (tcir & bits) \ printf(msg); PRINTINTR("BCache tag parity error\n", TC_3000_300_IR_BCTAGPARITY); PRINTINTR("TC overrun error\n", TC_3000_300_IR_TCOVERRUN); PRINTINTR("TC I/O timeout\n", TC_3000_300_IR_TCTIMEOUT); PRINTINTR("Bcache parity error\n", TC_3000_300_IR_BCACHEPARITY); PRINTINTR("Memory parity error\n", TC_3000_300_IR_MEMPARITY); #undef PRINTINTR #endif } while (ifound); } #endif /* DEC_3000_300 */ #ifdef DEC_3000_500 void tc_3000_500_intr_setup() { u_long i; /* * Disable all slot interrupts. Note that this cannot * actually disable CXTurbo, TCDS, and IOASIC interrupts. */ tc_3000_500_imask = *(volatile u_int32_t *)TC_3000_500_IMR_READ; for (i = 0; i < TC_3000_500_NCOOKIES; i++) tc_3000_500_imask |= tc_3000_500_intrbits[i]; *(volatile u_int32_t *)TC_3000_500_IMR_WRITE = tc_3000_500_imask; tc_mb(); /* * Set up interrupt handlers. */ for (i = 0; i < TC_3000_500_NCOOKIES; i++) { tc_3000_500_intr[i].tci_func = tc_intrnull; tc_3000_500_intr[i].tci_arg = (void *)i; } } void tc_3000_500_intr_establish(tcadev, cookie, level, func, arg) struct device *tcadev; void *cookie, *arg; tc_intrlevel_t level; int (*func) __P((void *)); { u_long dev = (u_long)cookie; #ifdef DIAGNOSTIC /* XXX bounds-check cookie. */ #endif if (tc_3000_500_intr[dev].tci_func != tc_intrnull) panic("tc_3000_500_intr_establish: cookie %d twice", dev); tc_3000_500_intr[dev].tci_func = func; tc_3000_500_intr[dev].tci_arg = arg; tc_3000_500_imask &= ~tc_3000_500_intrbits[dev]; *(volatile u_int32_t *)TC_3000_500_IMR_WRITE = tc_3000_500_imask; tc_mb(); } void tc_3000_500_intr_disestablish(tcadev, cookie) struct device *tcadev; void *cookie; { u_long dev = (u_long)cookie; #ifdef DIAGNOSTIC /* XXX bounds-check cookie. */ #endif if (tc_3000_500_intr[dev].tci_func == tc_intrnull) panic("tc_3000_500_intr_disestablish: cookie %d bad intr", dev); tc_3000_500_imask |= tc_3000_500_intrbits[dev]; *(volatile u_int32_t *)TC_3000_500_IMR_WRITE = tc_3000_500_imask; tc_mb(); tc_3000_500_intr[dev].tci_func = tc_intrnull; tc_3000_500_intr[dev].tci_arg = (void *)dev; } void tc_3000_500_iointr(framep, vec) void *framep; unsigned long vec; { u_int32_t ir; int ifound; #ifdef DIAGNOSTIC int s; if (vec != 0x800) panic("INVALID ASSUMPTION: vec 0x%lx, not 0x800", vec); s = splhigh(); if (s != ALPHA_PSL_IPL_IO) panic("INVALID ASSUMPTION: IPL %d, not %d", s, ALPHA_PSL_IPL_IO); splx(s); #endif do { tc_syncbus(); ir = *(volatile u_int32_t *)TC_3000_500_IR_CLEAR; /* Ignore interrupts that we haven't enabled. */ ir &= ~(tc_3000_500_imask & 0x1ff); ifound = 0; #define CHECKINTR(slot) \ if (ir & tc_3000_500_intrbits[slot]) { \ ifound = 1; \ (*tc_3000_500_intr[slot].tci_func) \ (tc_3000_500_intr[slot].tci_arg); \ } /* Do them in order of priority; highest slot # first. */ CHECKINTR(TC_3000_500_DEV_CXTURBO); CHECKINTR(TC_3000_500_DEV_IOASIC); CHECKINTR(TC_3000_500_DEV_TCDS); CHECKINTR(TC_3000_500_DEV_OPT5); CHECKINTR(TC_3000_500_DEV_OPT4); CHECKINTR(TC_3000_500_DEV_OPT3); CHECKINTR(TC_3000_500_DEV_OPT2); CHECKINTR(TC_3000_500_DEV_OPT1); CHECKINTR(TC_3000_500_DEV_OPT0); #undef CHECKINTR #ifdef DIAGNOSTIC #define PRINTINTR(msg, bits) \ if (ir & bits) \ printf(msg); PRINTINTR("Second error occurred\n", TC_3000_500_IR_ERR2); PRINTINTR("DMA buffer error\n", TC_3000_500_IR_DMABE); PRINTINTR("DMA cross 2K boundary\n", TC_3000_500_IR_DMA2K); PRINTINTR("TC reset in progress\n", TC_3000_500_IR_TCRESET); PRINTINTR("TC parity error\n", TC_3000_500_IR_TCPAR); PRINTINTR("DMA tag error\n", TC_3000_500_IR_DMATAG); PRINTINTR("Single-bit error\n", TC_3000_500_IR_DMASBE); PRINTINTR("Double-bit error\n", TC_3000_500_IR_DMADBE); PRINTINTR("TC I/O timeout\n", TC_3000_500_IR_TCTIMEOUT); PRINTINTR("DMA block too long\n", TC_3000_500_IR_DMABLOCK); PRINTINTR("Invalid I/O address\n", TC_3000_500_IR_IOADDR); PRINTINTR("DMA scatter/gather invalid\n", TC_3000_500_IR_DMASG); PRINTINTR("Scatter/gather parity error\n", TC_3000_500_IR_SGPAR); #undef PRINTINTR #endif } while (ifound); } #if 0 /* * tc_3000_500_ioslot -- * Set the PBS bits for devices on the TC. */ void tc_3000_500_ioslot(slot, flags, set) u_int32_t slot, flags; int set; { volatile u_int32_t *iosp; u_int32_t ios; int s; iosp = (volatile u_int32_t *)TC_3000_500_IOSLOT; ios = *iosp; flags <<= (slot * 3); if (set) ios |= flags; else ios &= ~flags; s = splhigh(); *iosp = ios; tc_mb(); splx(s); } #endif #endif /* DEC_3000_500 */ int tc_intrnull(val) void *val; { panic("tc_intrnull: uncaught TC intr for cookie %ld\n", (u_long)val); } static int tc_probe(device_t dev) { if((hwrpb->rpb_type != ST_DEC_3000_300) && (hwrpb->rpb_type != ST_DEC_3000_500)) return ENXIO; tc0 = dev; if(hwrpb->rpb_type == ST_DEC_3000_300) { device_set_desc(dev, "12.5 Mhz Turbochannel Bus"); } else { device_set_desc(dev, "25 Mhz Turbochannel Bus"); } return 0; } static int tc_attach(device_t dev) { struct tc_softc* sc = TC_SOFTC(dev); device_t parent = device_get_parent(dev); vm_offset_t regs; tc_addr_t tcaddr; const struct tc_builtin *builtin; struct tc_slotdesc *slot; struct tc_attach_args *ta; int i; device_t child = NULL; tc0 = dev; - - chipset.intrdev = dev; switch(hwrpb->rpb_type){ #ifdef DEC_3000_300 case ST_DEC_3000_300: sc->sc_speed = TC_SPEED_12_5_MHZ; sc->sc_nslots = tc_3000_300_nslots; sc->sc_slots = tc_3000_300_slots; sc->nbuiltins = tc_3000_300_nbuiltins; sc->builtins = tc_3000_300_builtins; tc_3000_300_intr_setup(); set_iointr(tc_3000_300_iointr); sc->sc_intr_establish = tc_3000_300_intr_establish; sc->sc_intr_disestablish = tc_3000_300_intr_disestablish; break; #endif /* DEC_3000_500 */ #ifdef DEC_3000_500 case ST_DEC_3000_500: sc->sc_speed = TC_SPEED_25_MHZ; sc->sc_nslots = tc_3000_500_nslots; sc->sc_slots = tc_3000_500_slots; sc->nbuiltins = tc_3000_500_nbuiltins; sc->builtins = tc_3000_500_builtins; tc_3000_500_intr_setup(); set_iointr(tc_3000_500_iointr); sc->sc_intr_establish = tc_3000_500_intr_establish; sc->sc_intr_disestablish = tc_3000_500_intr_disestablish; break; #endif /* DEC_3000_500 */ default: panic("tcattach: bad cpu type"); } /* * Try to configure each built-in device */ for (i = 0; i < sc->nbuiltins; i++) { builtin = &sc->builtins[i]; tcaddr = sc->sc_slots[builtin->tcb_slot].tcs_addr + builtin->tcb_offset; if (tc_badaddr(tcaddr)) continue; ta = malloc(sizeof(struct tc_attach_args), M_DEVBUF, M_NOWAIT); if (!ta) continue; ta->ta_slot = builtin->tcb_slot; ta->ta_offset = builtin->tcb_offset; ta->ta_addr = tcaddr; ta->ta_cookie = builtin->tcb_cookie; ta->ta_busspeed = sc->sc_speed; child = device_add_child(dev, builtin->tcb_modname, 0, ta); device_probe_and_attach(child); } return 0; } int tc_checkslot(slotbase, namep) tc_addr_t slotbase; char *namep; { struct tc_rommap *romp; int i, j; for (i = 0; i < NTC_ROMOFFS; i++) { romp = (struct tc_rommap *) (slotbase + tc_slot_romoffs[i]); switch (romp->tcr_width.v) { case 1: case 2: case 4: break; default: continue; } if (romp->tcr_stride.v != 4) continue; for (j = 0; j < 4; j++) if (romp->tcr_test[j+0*romp->tcr_stride.v] != 0x55 || romp->tcr_test[j+1*romp->tcr_stride.v] != 0x00 || romp->tcr_test[j+2*romp->tcr_stride.v] != 0xaa || romp->tcr_test[j+3*romp->tcr_stride.v] != 0xff) continue; for (j = 0; j < TC_ROM_LLEN; j++) namep[j] = romp->tcr_modname[j].v; namep[j] = '\0'; return (1); } return (0); } void tc_intr_establish(dev, cookie, level, handler, arg) struct device *dev; void *cookie, *arg; tc_intrlevel_t level; int (*handler) __P((void *)); { struct tc_softc *sc = (struct tc_softc *)device_get_softc(dev); (*sc->sc_intr_establish)(device_get_parent(dev), cookie, level, handler, arg); } void tc_intr_disestablish(dev, cookie) struct device *dev; void *cookie; { struct tc_softc *sc = (struct tc_softc *)device_get_softc(dev); (*sc->sc_intr_disestablish)(device_get_parent(dev), cookie); } static void tc_print_child(device_t bus, device_t dev) { printf(" at %s%d", device_get_name(bus), device_get_unit(bus)); } DRIVER_MODULE(tc, tcasic, tc_driver, tc_devclass, 0, 0); Index: head/sys/alpha/tc/tcasic.c =================================================================== --- head/sys/alpha/tc/tcasic.c (revision 45719) +++ head/sys/alpha/tc/tcasic.c (revision 45720) @@ -1,115 +1,114 @@ -/* $Id$ */ +/* $Id: tcasic.c,v 1.1 1998/08/20 08:27:11 dfr Exp $ */ /* from $NetBSD: tcasic.c,v 1.23 1998/05/14 00:01:31 thorpej Exp $ */ /* * Copyright (c) 1994, 1995, 1996 Carnegie-Mellon University. * All rights reserved. * * Author: Chris G. Demetriou * * Permission to use, copy, modify and distribute this software and * its documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. */ #include "opt_cpu.h" #include #include #include #include #include #include /*#include */ #define KV(pa) ALPHA_PHYS_TO_K0SEG(pa) static devclass_t tcasic_devclass; static device_t tcasic0; /* XXX only one for now */ struct tcasic_softc { vm_offset_t dmem_base; /* dense memory */ vm_offset_t smem_base; /* sparse memory */ vm_offset_t io_base; /* sparse i/o */ vm_offset_t cfg_base; /* sparse pci config */ }; #define TCASIC_SOFTC(dev) (struct tcasic_softc*) device_get_softc(dev) static int tcasic_probe(device_t dev); static int tcasic_attach(device_t dev); static void tcasic_print_child(device_t bus, device_t dev); static device_method_t tcasic_methods[] = { /* Device interface */ DEVMETHOD(device_probe, tcasic_probe), DEVMETHOD(device_attach, tcasic_attach), DEVMETHOD(bus_print_child, tcasic_print_child), { 0, 0 } }; static driver_t tcasic_driver = { "tcasic", tcasic_methods, DRIVER_TYPE_MISC, sizeof(struct tcasic_softc), }; extern device_t tc0; static int tcasic_probe(device_t dev) { if (tcasic0) return ENXIO; if((hwrpb->rpb_type != ST_DEC_3000_300) && (hwrpb->rpb_type != ST_DEC_3000_500)) return ENXIO; tcasic0 = dev; device_set_desc(dev, "Turbochannel Host Bus Adapter"); tc0 = device_add_child(dev, "tc", 0, 0); return 0; } static int tcasic_attach(device_t dev) { struct tcasic_softc* sc = TCASIC_SOFTC(dev); device_t parent = device_get_parent(dev); vm_offset_t regs; tcasic0 = dev; /* chipset = tcasic_chipset;*/ - chipset.intrdev = dev; device_probe_and_attach(tc0); return 0; } static void tcasic_print_child(device_t bus, device_t dev) { printf(" at %s%d", device_get_name(bus), device_get_unit(bus)); } DRIVER_MODULE(tcasic, root, tcasic_driver, tcasic_devclass, 0, 0); Index: head/sys/alpha/tlsb/dwlpx.c =================================================================== --- head/sys/alpha/tlsb/dwlpx.c (revision 45719) +++ head/sys/alpha/tlsb/dwlpx.c (revision 45720) @@ -1,321 +1,321 @@ /*- * Copyright (c) 1998 Doug Rabson * 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. * - * $Id: dwlpx.c,v 1.6 1998/09/04 08:01:26 dfr Exp $ + * $Id: dwlpx.c,v 1.7 1998/11/15 18:25:16 dfr Exp $ */ #include "opt_simos.h" #include #include #include #include #include #include #include #include #include #include #define KV(pa) ALPHA_PHYS_TO_K0SEG(pa) #define DWLPX_BASE(n, h) ((((u_long)(n) - 4) << 36) \ | ((u_long)(h) << 34) \ | (1L << 39)) static devclass_t dwlpx_devclass; static device_t dwlpx0; /* XXX only one for now */ struct dwlpx_softc { vm_offset_t dmem_base; /* dense memory */ vm_offset_t smem_base; /* sparse memory */ vm_offset_t io_base; /* sparse i/o */ vm_offset_t cfg_base; /* sparse pci config */ }; #define DWLPX_SOFTC(dev) (struct dwlpx_softc*) device_get_softc(dev) static alpha_chipset_inb_t dwlpx_inb; static alpha_chipset_inw_t dwlpx_inw; static alpha_chipset_inl_t dwlpx_inl; static alpha_chipset_outb_t dwlpx_outb; static alpha_chipset_outw_t dwlpx_outw; static alpha_chipset_outl_t dwlpx_outl; static alpha_chipset_readb_t dwlpx_readb; static alpha_chipset_readw_t dwlpx_readw; static alpha_chipset_readl_t dwlpx_readl; static alpha_chipset_writeb_t dwlpx_writeb; static alpha_chipset_writew_t dwlpx_writew; static alpha_chipset_writel_t dwlpx_writel; static alpha_chipset_maxdevs_t dwlpx_maxdevs; static alpha_chipset_cfgreadb_t dwlpx_cfgreadb; static alpha_chipset_cfgreadw_t dwlpx_cfgreadw; static alpha_chipset_cfgreadl_t dwlpx_cfgreadl; static alpha_chipset_cfgwriteb_t dwlpx_cfgwriteb; static alpha_chipset_cfgwritew_t dwlpx_cfgwritew; static alpha_chipset_cfgwritel_t dwlpx_cfgwritel; static alpha_chipset_t dwlpx_chipset = { dwlpx_inb, dwlpx_inw, dwlpx_inl, dwlpx_outb, dwlpx_outw, dwlpx_outl, dwlpx_readb, dwlpx_readw, dwlpx_readl, dwlpx_writeb, dwlpx_writew, dwlpx_writel, dwlpx_maxdevs, dwlpx_cfgreadb, dwlpx_cfgreadw, dwlpx_cfgreadl, dwlpx_cfgwriteb, dwlpx_cfgwritew, dwlpx_cfgwritel, }; /* * For supporting multiple busses, we will encode the dwlpx unit number into * the port address as Linux does. */ static u_int8_t dwlpx_inb(u_int32_t port) { struct dwlpx_softc* sc = DWLPX_SOFTC(dwlpx0); return SPARSE_READ_BYTE(sc->io_base, port); } static u_int16_t dwlpx_inw(u_int32_t port) { struct dwlpx_softc* sc = DWLPX_SOFTC(dwlpx0); return SPARSE_READ_WORD(sc->io_base, port); } static u_int32_t dwlpx_inl(u_int32_t port) { struct dwlpx_softc* sc = DWLPX_SOFTC(dwlpx0); return SPARSE_READ_LONG(sc->io_base, port); } static void dwlpx_outb(u_int32_t port, u_int8_t data) { struct dwlpx_softc* sc = DWLPX_SOFTC(dwlpx0); SPARSE_WRITE_BYTE(sc->io_base, port, data); } static void dwlpx_outw(u_int32_t port, u_int16_t data) { struct dwlpx_softc* sc = DWLPX_SOFTC(dwlpx0); SPARSE_WRITE_WORD(sc->io_base, port, data); } static void dwlpx_outl(u_int32_t port, u_int32_t data) { struct dwlpx_softc* sc = DWLPX_SOFTC(dwlpx0); SPARSE_WRITE_LONG(sc->io_base, port, data); } static u_int8_t dwlpx_readb(u_int32_t pa) { struct dwlpx_softc* sc = DWLPX_SOFTC(dwlpx0); return SPARSE_READ_BYTE(sc->smem_base, pa); } static u_int16_t dwlpx_readw(u_int32_t pa) { struct dwlpx_softc* sc = DWLPX_SOFTC(dwlpx0); return SPARSE_READ_WORD(sc->smem_base, pa); } static u_int32_t dwlpx_readl(u_int32_t pa) { struct dwlpx_softc* sc = DWLPX_SOFTC(dwlpx0); return SPARSE_READ_LONG(sc->smem_base, pa); } static void dwlpx_writeb(u_int32_t pa, u_int8_t data) { struct dwlpx_softc* sc = DWLPX_SOFTC(dwlpx0); SPARSE_WRITE_BYTE(sc->smem_base, pa, data); } static void dwlpx_writew(u_int32_t pa, u_int16_t data) { struct dwlpx_softc* sc = DWLPX_SOFTC(dwlpx0); SPARSE_WRITE_WORD(sc->smem_base, pa, data); } static void dwlpx_writel(u_int32_t pa, u_int32_t data) { struct dwlpx_softc* sc = DWLPX_SOFTC(dwlpx0); SPARSE_WRITE_LONG(sc->smem_base, pa, data); } static int dwlpx_maxdevs(u_int b) { return 12; /* XXX */ } /* XXX only support bus 0 */ #define DWLPX_CFGOFF(b, s, f, r) \ (((b) << 16) | ((s) << 11) | ((f) << 8) | (r)) #define CFGREAD(b, s, f, r, width) \ struct dwlpx_softc* sc = DWLPX_SOFTC(dwlpx0); \ vm_offset_t off = DWLPX_CFGOFF(b, s, f, r); \ vm_offset_t kv = SPARSE_##width##_ADDRESS(sc->cfg_base, off); \ if (badaddr((caddr_t)kv, 4)) return ~0; \ return SPARSE_##width##_EXTRACT(off, SPARSE_READ(kv)) #define CFGWRITE(b, s, f, r, data, width) \ struct dwlpx_softc* sc = DWLPX_SOFTC(dwlpx0); \ vm_offset_t off = DWLPX_CFGOFF(b, s, f, r); \ vm_offset_t kv = SPARSE_##width##_ADDRESS(sc->cfg_base, off); \ if (badaddr((caddr_t)kv, 4)) return; \ SPARSE_WRITE(kv, SPARSE_##width##_INSERT(off, data)) static u_int8_t dwlpx_cfgreadb(u_int b, u_int s, u_int f, u_int r) { CFGREAD(b, s, f, r, BYTE); } static u_int16_t dwlpx_cfgreadw(u_int b, u_int s, u_int f, u_int r) { CFGREAD(b, s, f, r, WORD); } static u_int32_t dwlpx_cfgreadl(u_int b, u_int s, u_int f, u_int r) { CFGREAD(b, s, f, r, LONG); } static void dwlpx_cfgwriteb(u_int b, u_int s, u_int f, u_int r, u_int8_t data) { CFGWRITE(b, s, f, r, data, BYTE); } static void dwlpx_cfgwritew(u_int b, u_int s, u_int f, u_int r, u_int16_t data) { CFGWRITE(b, s, f, r, data, WORD); } static void dwlpx_cfgwritel(u_int b, u_int s, u_int f, u_int r, u_int32_t data) { CFGWRITE(b, s, f, r, data, LONG); } static int dwlpx_probe(device_t dev); static int dwlpx_attach(device_t dev); static driver_intr_t dwlpx_intr; static device_method_t dwlpx_methods[] = { /* Device interface */ DEVMETHOD(device_probe, dwlpx_probe), DEVMETHOD(device_attach, dwlpx_attach), { 0, 0 } }; static driver_t dwlpx_driver = { "dwlpx", dwlpx_methods, DRIVER_TYPE_MISC, sizeof(struct dwlpx_softc), }; static int dwlpx_probe(device_t dev) { if (dwlpx0) return ENXIO; dwlpx0 = dev; device_set_desc(dev, "DWLPA or DWLPB PCI adapter"); return 0; } static int dwlpx_attach(device_t dev) { struct dwlpx_softc* sc = DWLPX_SOFTC(dev); device_t parent = device_get_parent(dev); vm_offset_t regs; void *intr; dwlpx0 = dev; chipset = dwlpx_chipset; - chipset.intrdev = dev; + /* chipset.intrdev = dev; */ regs = KV(DWLPX_BASE(kft_get_node(dev), kft_get_hosenum(dev))); sc->dmem_base = regs + (0L << 32); sc->smem_base = regs + (1L << 32); sc->io_base = regs + (2L << 32); sc->cfg_base = regs + (3L << 32); *(u_int32_t*) (regs + PCIA_CTL(0)) = 1; /* Type1 config cycles */ return BUS_SETUP_INTR(parent, dev, NULL, dwlpx_intr, 0, &intr); return 0; } static void dwlpx_intr(void* arg) { #ifdef SIMOS extern void simos_intr(int); simos_intr(0); #endif } DRIVER_MODULE(dwlpx, kft, dwlpx_driver, dwlpx_devclass, 0, 0); Index: head/sys/amd64/amd64/autoconf.c =================================================================== --- head/sys/amd64/amd64/autoconf.c (revision 45719) +++ head/sys/amd64/amd64/autoconf.c (revision 45720) @@ -1,499 +1,507 @@ /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * William Jolitz. * * 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: @(#)autoconf.c 7.1 (Berkeley) 5/9/91 - * $Id: autoconf.c,v 1.111 1999/01/19 00:10:59 peter Exp $ + * $Id: autoconf.c,v 1.112 1999/04/15 14:52:24 bde Exp $ */ /* * Setup the system to run on the current machine. * * Configure() is called at boot time and initializes the vba * device tables and the memory controller monitoring. Available * devices are determined (from possibilities mentioned in ioconf.c), * and the drivers are initialized. */ #include "opt_bootp.h" #include "opt_ffs.h" #include "opt_cd9660.h" #include "opt_mfs.h" #include "opt_nfsroot.h" +#include "opt_bus.h" #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef APIC_IO #include #endif /* APIC_IO */ #include #include "isa.h" #if NISA > 0 +#ifdef OLD_BUS_ARCH #include +#else +device_t isa_bus_device = 0; +#endif /* OLD_BUS_ARCH */ #endif #include "pnp.h" #if NPNP > 0 +#ifdef OLD_BUS_ARCH #include #endif +#endif #include "eisa.h" #if NEISA > 0 #include #endif #include "pci.h" #if NPCI > 0 #include #endif -#include - static void configure_first __P((void *)); static void configure __P((void *)); static void configure_final __P((void *)); static void configure_finish __P((void)); static void configure_start __P((void)); static int setdumpdev __P((dev_t dev)); static void setroot __P((void)); SYSINIT(configure1, SI_SUB_CONFIGURE, SI_ORDER_FIRST, configure_first, NULL); /* SI_ORDER_SECOND is hookable */ SYSINIT(configure2, SI_SUB_CONFIGURE, SI_ORDER_THIRD, configure, NULL); /* SI_ORDER_MIDDLE is hookable */ SYSINIT(configure3, SI_SUB_CONFIGURE, SI_ORDER_ANY, configure_final, NULL); #if defined(CD9660) || defined(CD9660_ROOT) #include #include #include #include /* * XXX All this CD-ROM root stuff is fairly messy. Ick. * * We need to try out all our potential CDROM drives, so we need a table. */ static struct { char *name; int major; } try_cdrom[] = { { "cd", 6 }, { "mcd", 7 }, { "scd", 16 }, { "matcd", 17 }, { "wcd", 19 }, { 0, 0} }; static int find_cdrom_root __P((void)); static int find_cdrom_root() { int i, j, error; struct cdevsw *bd; dev_t orootdev; #if CD9660_ROOTDELAY > 0 DELAY(CD9660_ROOTDELAY * 1000000); #endif orootdev = rootdev; for (i = 0 ; i < 2; i++) for (j = 0 ; try_cdrom[j].name ; j++) { if (try_cdrom[j].major >= nblkdev) continue; rootdev = makedev(try_cdrom[j].major, i * 8); bd = bdevsw[major(rootdev)]; if (bd == NULL || bd->d_open == NULL) continue; if (bootverbose) printf("trying %s%d as rootdev (0x%x)\n", try_cdrom[j].name, i, rootdev); error = (bd->d_open)(rootdev, FREAD, S_IFBLK, curproc); if (error == 0) { if (bd->d_close != NULL) (bd->d_close)(rootdev, FREAD, S_IFBLK, curproc); return 0; } } rootdev = orootdev; return EINVAL; } #endif /* CD9660 || CD9660_ROOT */ #ifdef MFS_ROOT extern u_char *mfs_getimage __P((void)); #endif static void configure_start() { } static void configure_finish() { } +device_t nexus_dev; + /* * Determine i/o configuration for a machine. */ static void configure_first(dummy) void *dummy; { configure_start(); /* DDB hook? */ } static void configure(dummy) void *dummy; { /* Allow all routines to decide for themselves if they want intrs */ /* * XXX Since this cannot be achieved on all architectures, we should * XXX go back to disabling all interrupts until configuration is * XXX completed and switch any devices that rely on the current * XXX behavior to no longer rely on interrupts or to register an * XXX interrupt_driven_config_hook for the task. */ /* * XXX The above is wrong, because we're implicitly at splhigh(), * XXX and should stay there, so enabling interrupts in the CPU * XXX and the ICU at most gives pending interrupts which just get * XXX in the way. */ #ifdef APIC_IO bsp_apic_configure(); enable_intr(); #else enable_intr(); INTREN(IRQ_SLAVE); #endif /* APIC_IO */ #if NEISA > 0 eisa_configure(); #endif -#if NPCI > 0 - pci_configure(); -#endif - #if NPNP > 0 pnp_configure(); #endif -#if NISA > 0 - isa_configure(); -#endif + /* nexus0 is the top of the i386 device tree */ + device_add_child(root_bus, "nexus", 0, 0); /* initialize new bus architecture */ root_bus_configure(); + +#if NISA > 0 + if (isa_bus_device) + bus_generic_attach(isa_bus_device); +#endif /* * Now we're ready to handle (pending) interrupts. * XXX this is slightly misplaced. */ spl0(); /* * Allow lowering of the ipl to the lowest kernel level if we * panic (or call tsleep() before clearing `cold'). No level is * completely safe (since a panic may occur in a critical region * at splhigh()), but we want at least bio interrupts to work. */ safepri = cpl; } static void configure_final(dummy) void *dummy; { int i; configure_finish(); /* DDB hook? */ cninit_finish(); if (bootverbose) { #ifdef APIC_IO imen_dump(); #endif /* APIC_IO */ /* * Print out the BIOS's idea of the disk geometries. */ printf("BIOS Geometries:\n"); for (i = 0; i < N_BIOS_GEOM; i++) { unsigned long bios_geom; int max_cylinder, max_head, max_sector; bios_geom = bootinfo.bi_bios_geom[i]; /* * XXX the bootstrap punts a 1200K floppy geometry * when the get-disk-geometry interrupt fails. Skip * drives that have this geometry. */ if (bios_geom == 0x4f010f) continue; printf(" %x:%08lx ", i, bios_geom); max_cylinder = bios_geom >> 16; max_head = (bios_geom >> 8) & 0xff; max_sector = bios_geom & 0xff; printf( "0..%d=%d cylinders, 0..%d=%d heads, 1..%d=%d sectors\n", max_cylinder, max_cylinder + 1, max_head, max_head + 1, max_sector, max_sector); } printf(" %d accounted for\n", bootinfo.bi_n_bios_used); printf("Device configuration finished.\n"); } cold = 0; } void cpu_rootconf() { /* * XXX NetBSD has a much cleaner approach to finding root. * XXX We should adopt their code. */ #if defined(CD9660) || defined(CD9660_ROOT) if ((boothowto & RB_CDROM)) { if (bootverbose) printf("Considering CD-ROM root f/s.\n"); /* NB: find_cdrom_root() sets rootdev if successful. */ if (find_cdrom_root() == 0) mountrootfsname = "cd9660"; else if (bootverbose) printf("No CD-ROM available as root f/s.\n"); } #endif #ifdef MFS_ROOT if (!mountrootfsname) { if (bootverbose) printf("Considering MFS root f/s.\n"); if (mfs_getimage()) mountrootfsname = "mfs"; else if (bootverbose) printf("No MFS image available as root f/s.\n"); } #endif #ifdef BOOTP_NFSROOT if (!mountrootfsname && !nfs_diskless_valid) { if (bootverbose) printf("Considering BOOTP NFS root f/s.\n"); mountrootfsname = "nfs"; } #endif /* BOOTP_NFSROOT */ #if defined(NFS) || defined(NFS_ROOT) if (!mountrootfsname && nfs_diskless_valid) { if (bootverbose) printf("Considering NFS root f/s.\n"); mountrootfsname = "nfs"; } #endif /* NFS */ #if defined(FFS) || defined(FFS_ROOT) if (!mountrootfsname) { mountrootfsname = "ufs"; if (bootverbose) printf("Considering FFS root f/s.\n"); if (boothowto & RB_ASKNAME) setconf(); else setroot(); } #endif if (!mountrootfsname) { panic("Nobody wants to mount my root for me"); } } void cpu_dumpconf() { if (setdumpdev(dumpdev) != 0) dumpdev = NODEV; } static int setdumpdev(dev) dev_t dev; { int maj, psize; long newdumplo; if (dev == NODEV) { dumpdev = dev; return (0); } maj = major(dev); if (maj >= nblkdev || bdevsw[maj] == NULL) return (ENXIO); /* XXX is this right? */ if (bdevsw[maj]->d_psize == NULL) return (ENXIO); /* XXX should be ENODEV ? */ psize = bdevsw[maj]->d_psize(dev); if (psize == -1) return (ENXIO); /* XXX should be ENODEV ? */ /* * XXX should clean up checking in dumpsys() to be more like this, * and nuke dodump sysctl (too many knobs), and move this to * kern_shutdown.c... */ newdumplo = psize - Maxmem * PAGE_SIZE / DEV_BSIZE; if (newdumplo < 0) return (ENOSPC); dumpdev = dev; dumplo = newdumplo; return (0); } u_long bootdev = 0; /* not a dev_t - encoding is different */ #define FDMAJOR 2 #define FDUNITSHIFT 6 /* * Attempt to find the device from which we were booted. * If we can do so, and not instructed not to do so, * set rootdevs[] and rootdevnames[] to correspond to the * boot device(s). */ static void setroot() { int majdev, mindev, unit, slice, part; dev_t newrootdev; char partname[2]; char *sname; if (boothowto & RB_DFLTROOT || (bootdev & B_MAGICMASK) != B_DEVMAGIC) return; majdev = B_TYPE(bootdev); if (majdev >= nblkdev || bdevsw[majdev] == NULL) return; unit = B_UNIT(bootdev); slice = B_SLICE(bootdev); if (slice == WHOLE_DISK_SLICE) slice = COMPATIBILITY_SLICE; if (slice < 0 || slice >= MAX_SLICES) return; /* * XXX kludge for inconsistent unit numbering and lack of slice * support for floppies. */ if (majdev == FDMAJOR) { slice = COMPATIBILITY_SLICE; part = RAW_PART; mindev = unit << FDUNITSHIFT; } else { part = B_PARTITION(bootdev); mindev = dkmakeminor(unit, slice, part); } newrootdev = makedev(majdev, mindev); rootdevs[0] = newrootdev; sname = dsname(bdevsw[majdev]->d_name, unit, slice, part, partname); rootdevnames[0] = malloc(strlen(sname) + 2, M_DEVBUF, M_NOWAIT); sprintf(rootdevnames[0], "%s%s", sname, partname); /* * For properly dangerously dedicated disks (ones with a historical * bogus partition table), the boot blocks will give slice = 4, but * the kernel will only provide the compatibility slice since it * knows that slice 4 is not a real slice. Arrange to try mounting * the compatibility slice as root if mounting the slice passed by * the boot blocks fails. This handles the dangerously dedicated * case and perhaps others. */ if (slice == COMPATIBILITY_SLICE) return; slice = COMPATIBILITY_SLICE; rootdevs[1] = dkmodslice(newrootdev, slice); sname = dsname(bdevsw[majdev]->d_name, unit, slice, part, partname); rootdevnames[1] = malloc(strlen(sname) + 2, M_DEVBUF, M_NOWAIT); sprintf(rootdevnames[1], "%s%s", sname, partname); } static int sysctl_kern_dumpdev SYSCTL_HANDLER_ARGS { int error; dev_t ndumpdev; ndumpdev = dumpdev; error = sysctl_handle_opaque(oidp, &ndumpdev, sizeof ndumpdev, req); if (error == 0 && req->newptr != NULL) error = setdumpdev(ndumpdev); return (error); } SYSCTL_PROC(_kern, KERN_DUMPDEV, dumpdev, CTLTYPE_OPAQUE|CTLFLAG_RW, 0, sizeof dumpdev, sysctl_kern_dumpdev, "T,dev_t", ""); Index: head/sys/amd64/amd64/exception.S =================================================================== --- head/sys/amd64/amd64/exception.S (revision 45719) +++ head/sys/amd64/amd64/exception.S (revision 45720) @@ -1,372 +1,372 @@ /*- * Copyright (c) 1990 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. * - * $Id: exception.s,v 1.56 1999/02/25 11:03:08 bde Exp $ + * $Id: exception.s,v 1.57 1999/02/28 10:53:28 bde Exp $ */ #include "npx.h" #include "opt_vm86.h" #include #include #include #include #include #ifdef SMP #include /** CPL_AND_CML, REAL_ */ #endif #include "assym.s" #ifndef SMP #define ECPL_LOCK /* make these nops */ #define ECPL_UNLOCK #define ICPL_LOCK #define ICPL_UNLOCK #define FAST_ICPL_UNLOCK #define AICPL_LOCK #define AICPL_UNLOCK #define AVCPL_LOCK #define AVCPL_UNLOCK #endif /* SMP */ #define KCSEL 0x08 /* kernel code selector */ #define KDSEL 0x10 /* kernel data selector */ #define SEL_RPL_MASK 0x0003 #define TRAPF_CS_OFF (13 * 4) .text /*****************************************************************************/ /* Trap handling */ /*****************************************************************************/ /* * Trap and fault vector routines */ #define IDTVEC(name) ALIGN_TEXT; .globl __CONCAT(_X,name); __CONCAT(_X,name): #define TRAP(a) pushl $(a) ; jmp _alltraps /* * XXX - debugger traps are now interrupt gates so at least bdb doesn't lose * control. The sti's give the standard losing behaviour for ddb and kgdb. */ #ifdef BDE_DEBUGGER #define BDBTRAP(name) \ ss ; \ cmpb $0,_bdb_exists ; \ je 1f ; \ testb $SEL_RPL_MASK,4(%esp) ; \ jne 1f ; \ ss ; \ .globl __CONCAT(__CONCAT(bdb_,name),_ljmp); \ __CONCAT(__CONCAT(bdb_,name),_ljmp): \ ljmp $0,$0 ; \ 1: #else #define BDBTRAP(name) #endif #define BPTTRAP(a) testl $PSL_I,4+8(%esp) ; je 1f ; sti ; 1: ; TRAP(a) MCOUNT_LABEL(user) MCOUNT_LABEL(btrap) IDTVEC(div) pushl $0; TRAP(T_DIVIDE) IDTVEC(dbg) BDBTRAP(dbg) pushl $0; BPTTRAP(T_TRCTRAP) IDTVEC(nmi) pushl $0; TRAP(T_NMI) IDTVEC(bpt) BDBTRAP(bpt) pushl $0; BPTTRAP(T_BPTFLT) IDTVEC(ofl) pushl $0; TRAP(T_OFLOW) IDTVEC(bnd) pushl $0; TRAP(T_BOUND) IDTVEC(ill) pushl $0; TRAP(T_PRIVINFLT) IDTVEC(dna) pushl $0; TRAP(T_DNA) IDTVEC(fpusegm) pushl $0; TRAP(T_FPOPFLT) IDTVEC(tss) TRAP(T_TSSFLT) IDTVEC(missing) TRAP(T_SEGNPFLT) IDTVEC(stk) TRAP(T_STKFLT) IDTVEC(prot) TRAP(T_PROTFLT) IDTVEC(page) TRAP(T_PAGEFLT) IDTVEC(mchk) pushl $0; TRAP(T_MCHK) IDTVEC(rsvd) pushl $0; TRAP(T_RESERVED) IDTVEC(fpu) #if NNPX > 0 /* * Handle like an interrupt (except for accounting) so that we can - * call npxintr to clear the error. It would be better to handle + * call npx_intr to clear the error. It would be better to handle * npx interrupts as traps. This used to be difficult for nested * interrupts, but now it is fairly easy - mask nested ones the * same as SWI_AST's. */ pushl $0 /* dummy error code */ pushl $0 /* dummy trap type */ pushal pushl %ds pushl %es /* now stack frame is a trap frame */ movl $KDSEL,%eax movl %ax,%ds movl %ax,%es FAKE_MCOUNT(12*4(%esp)) #ifdef SMP MPLOCKED incl _cnt+V_TRAP FPU_LOCK ECPL_LOCK #ifdef CPL_AND_CML movl _cml,%eax pushl %eax /* save original cml */ orl $SWI_AST_MASK,%eax movl %eax,_cml #else movl _cpl,%eax pushl %eax /* save original cpl */ orl $SWI_AST_MASK,%eax movl %eax,_cpl #endif /* CPL_AND_CML */ ECPL_UNLOCK pushl $0 /* dummy unit to finish intr frame */ #else /* SMP */ movl _cpl,%eax pushl %eax pushl $0 /* dummy unit to finish intr frame */ incl _cnt+V_TRAP orl $SWI_AST_MASK,%eax movl %eax,_cpl #endif /* SMP */ - call _npxintr + call _npx_intr incb _intr_nesting_level MEXITCOUNT jmp _doreti #else /* NNPX > 0 */ pushl $0; TRAP(T_ARITHTRAP) #endif /* NNPX > 0 */ IDTVEC(align) TRAP(T_ALIGNFLT) SUPERALIGN_TEXT .globl _alltraps _alltraps: pushal pushl %ds pushl %es alltraps_with_regs_pushed: movl $KDSEL,%eax movl %ax,%ds movl %ax,%es FAKE_MCOUNT(12*4(%esp)) calltrap: FAKE_MCOUNT(_btrap) /* init "from" _btrap -> calltrap */ MPLOCKED incl _cnt+V_TRAP ALIGN_LOCK ECPL_LOCK #ifdef CPL_AND_CML movl _cml,%eax movl %eax,%ebx /* keep orig. cml here during trap() */ orl $SWI_AST_MASK,%eax movl %eax,_cml #else movl _cpl,%eax movl %eax,%ebx /* keep orig. cpl here during trap() */ orl $SWI_AST_MASK,%eax movl %eax,_cpl #endif ECPL_UNLOCK call _trap /* * Return via _doreti to handle ASTs. Have to change trap frame * to interrupt frame. */ pushl %ebx /* cpl to restore */ subl $4,%esp /* dummy unit to finish intr frame */ MPLOCKED incb _intr_nesting_level MEXITCOUNT jmp _doreti /* * Call gate entry for syscall. * The intersegment call has been set up to specify one dummy parameter. * This leaves a place to put eflags so that the call frame can be * converted to a trap frame. Note that the eflags is (semi-)bogusly * pushed into (what will be) tf_err and then copied later into the * final spot. It has to be done this way because esp can't be just * temporarily altered for the pushfl - an interrupt might come in * and clobber the saved cs/eip. */ SUPERALIGN_TEXT IDTVEC(syscall) pushfl /* save eflags in tf_err for now */ subl $4,%esp /* skip over tf_trapno */ pushal pushl %ds pushl %es movl $KDSEL,%eax /* switch to kernel segments */ movl %ax,%ds movl %ax,%es movl TF_ERR(%esp),%eax /* copy saved eflags to final spot */ movl %eax,TF_EFLAGS(%esp) movl $7,TF_ERR(%esp) /* sizeof "lcall 7,0" */ FAKE_MCOUNT(12*4(%esp)) MPLOCKED incl _cnt+V_SYSCALL SYSCALL_LOCK ECPL_LOCK #ifdef CPL_AND_CML movl $SWI_AST_MASK,_cml #else movl $SWI_AST_MASK,_cpl #endif ECPL_UNLOCK call _syscall /* * Return via _doreti to handle ASTs. */ pushl $0 /* cpl to restore */ subl $4,%esp /* dummy unit to finish intr frame */ movb $1,_intr_nesting_level MEXITCOUNT jmp _doreti /* * Call gate entry for Linux/NetBSD syscall (int 0x80) */ SUPERALIGN_TEXT IDTVEC(int0x80_syscall) subl $8,%esp /* skip over tf_trapno and tf_err */ pushal pushl %ds pushl %es movl $KDSEL,%eax /* switch to kernel segments */ movl %ax,%ds movl %ax,%es movl $2,TF_ERR(%esp) /* sizeof "int 0x80" */ FAKE_MCOUNT(12*4(%esp)) MPLOCKED incl _cnt+V_SYSCALL ALTSYSCALL_LOCK ECPL_LOCK #ifdef CPL_AND_CML movl $SWI_AST_MASK,_cml #else movl $SWI_AST_MASK,_cpl #endif ECPL_UNLOCK call _syscall /* * Return via _doreti to handle ASTs. */ pushl $0 /* cpl to restore */ subl $4,%esp /* dummy unit to finish intr frame */ movb $1,_intr_nesting_level MEXITCOUNT jmp _doreti ENTRY(fork_trampoline) call _spl0 #ifdef SMP cmpl $0,_switchtime jne 1f pushl $_switchtime call _microuptime popl %edx movl _ticks,%eax movl %eax,_switchticks 1: #endif /* * cpu_set_fork_handler intercepts this function call to * have this call a non-return function to stay in kernel mode. * initproc has its own fork handler, but it does return. */ pushl %ebx /* arg1 */ call %esi /* function */ addl $4,%esp /* cut from syscall */ /* * Return via _doreti to handle ASTs. */ pushl $0 /* cpl to restore */ subl $4,%esp /* dummy unit to finish intr frame */ movb $1,_intr_nesting_level MEXITCOUNT jmp _doreti #ifdef VM86 /* * Include vm86 call routines, which want to call _doreti. */ #include "i386/i386/vm86bios.s" #endif /* VM86 */ /* * Include what was once config+isa-dependent code. * XXX it should be in a stand-alone file. It's still icu-dependent and * belongs in i386/isa. */ #include "i386/isa/vector.s" /* * Include what was once icu-dependent code. * XXX it should be merged into this file (also move the definition of * imen to vector.s or isa.c). * Before including it, set up a normal asm environment so that vector.s * doesn't have to know that stuff is included after it. */ .data ALIGN_DATA .text SUPERALIGN_TEXT #include "i386/isa/ipl.s" Index: head/sys/amd64/amd64/exception.s =================================================================== --- head/sys/amd64/amd64/exception.s (revision 45719) +++ head/sys/amd64/amd64/exception.s (revision 45720) @@ -1,372 +1,372 @@ /*- * Copyright (c) 1990 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. * - * $Id: exception.s,v 1.56 1999/02/25 11:03:08 bde Exp $ + * $Id: exception.s,v 1.57 1999/02/28 10:53:28 bde Exp $ */ #include "npx.h" #include "opt_vm86.h" #include #include #include #include #include #ifdef SMP #include /** CPL_AND_CML, REAL_ */ #endif #include "assym.s" #ifndef SMP #define ECPL_LOCK /* make these nops */ #define ECPL_UNLOCK #define ICPL_LOCK #define ICPL_UNLOCK #define FAST_ICPL_UNLOCK #define AICPL_LOCK #define AICPL_UNLOCK #define AVCPL_LOCK #define AVCPL_UNLOCK #endif /* SMP */ #define KCSEL 0x08 /* kernel code selector */ #define KDSEL 0x10 /* kernel data selector */ #define SEL_RPL_MASK 0x0003 #define TRAPF_CS_OFF (13 * 4) .text /*****************************************************************************/ /* Trap handling */ /*****************************************************************************/ /* * Trap and fault vector routines */ #define IDTVEC(name) ALIGN_TEXT; .globl __CONCAT(_X,name); __CONCAT(_X,name): #define TRAP(a) pushl $(a) ; jmp _alltraps /* * XXX - debugger traps are now interrupt gates so at least bdb doesn't lose * control. The sti's give the standard losing behaviour for ddb and kgdb. */ #ifdef BDE_DEBUGGER #define BDBTRAP(name) \ ss ; \ cmpb $0,_bdb_exists ; \ je 1f ; \ testb $SEL_RPL_MASK,4(%esp) ; \ jne 1f ; \ ss ; \ .globl __CONCAT(__CONCAT(bdb_,name),_ljmp); \ __CONCAT(__CONCAT(bdb_,name),_ljmp): \ ljmp $0,$0 ; \ 1: #else #define BDBTRAP(name) #endif #define BPTTRAP(a) testl $PSL_I,4+8(%esp) ; je 1f ; sti ; 1: ; TRAP(a) MCOUNT_LABEL(user) MCOUNT_LABEL(btrap) IDTVEC(div) pushl $0; TRAP(T_DIVIDE) IDTVEC(dbg) BDBTRAP(dbg) pushl $0; BPTTRAP(T_TRCTRAP) IDTVEC(nmi) pushl $0; TRAP(T_NMI) IDTVEC(bpt) BDBTRAP(bpt) pushl $0; BPTTRAP(T_BPTFLT) IDTVEC(ofl) pushl $0; TRAP(T_OFLOW) IDTVEC(bnd) pushl $0; TRAP(T_BOUND) IDTVEC(ill) pushl $0; TRAP(T_PRIVINFLT) IDTVEC(dna) pushl $0; TRAP(T_DNA) IDTVEC(fpusegm) pushl $0; TRAP(T_FPOPFLT) IDTVEC(tss) TRAP(T_TSSFLT) IDTVEC(missing) TRAP(T_SEGNPFLT) IDTVEC(stk) TRAP(T_STKFLT) IDTVEC(prot) TRAP(T_PROTFLT) IDTVEC(page) TRAP(T_PAGEFLT) IDTVEC(mchk) pushl $0; TRAP(T_MCHK) IDTVEC(rsvd) pushl $0; TRAP(T_RESERVED) IDTVEC(fpu) #if NNPX > 0 /* * Handle like an interrupt (except for accounting) so that we can - * call npxintr to clear the error. It would be better to handle + * call npx_intr to clear the error. It would be better to handle * npx interrupts as traps. This used to be difficult for nested * interrupts, but now it is fairly easy - mask nested ones the * same as SWI_AST's. */ pushl $0 /* dummy error code */ pushl $0 /* dummy trap type */ pushal pushl %ds pushl %es /* now stack frame is a trap frame */ movl $KDSEL,%eax movl %ax,%ds movl %ax,%es FAKE_MCOUNT(12*4(%esp)) #ifdef SMP MPLOCKED incl _cnt+V_TRAP FPU_LOCK ECPL_LOCK #ifdef CPL_AND_CML movl _cml,%eax pushl %eax /* save original cml */ orl $SWI_AST_MASK,%eax movl %eax,_cml #else movl _cpl,%eax pushl %eax /* save original cpl */ orl $SWI_AST_MASK,%eax movl %eax,_cpl #endif /* CPL_AND_CML */ ECPL_UNLOCK pushl $0 /* dummy unit to finish intr frame */ #else /* SMP */ movl _cpl,%eax pushl %eax pushl $0 /* dummy unit to finish intr frame */ incl _cnt+V_TRAP orl $SWI_AST_MASK,%eax movl %eax,_cpl #endif /* SMP */ - call _npxintr + call _npx_intr incb _intr_nesting_level MEXITCOUNT jmp _doreti #else /* NNPX > 0 */ pushl $0; TRAP(T_ARITHTRAP) #endif /* NNPX > 0 */ IDTVEC(align) TRAP(T_ALIGNFLT) SUPERALIGN_TEXT .globl _alltraps _alltraps: pushal pushl %ds pushl %es alltraps_with_regs_pushed: movl $KDSEL,%eax movl %ax,%ds movl %ax,%es FAKE_MCOUNT(12*4(%esp)) calltrap: FAKE_MCOUNT(_btrap) /* init "from" _btrap -> calltrap */ MPLOCKED incl _cnt+V_TRAP ALIGN_LOCK ECPL_LOCK #ifdef CPL_AND_CML movl _cml,%eax movl %eax,%ebx /* keep orig. cml here during trap() */ orl $SWI_AST_MASK,%eax movl %eax,_cml #else movl _cpl,%eax movl %eax,%ebx /* keep orig. cpl here during trap() */ orl $SWI_AST_MASK,%eax movl %eax,_cpl #endif ECPL_UNLOCK call _trap /* * Return via _doreti to handle ASTs. Have to change trap frame * to interrupt frame. */ pushl %ebx /* cpl to restore */ subl $4,%esp /* dummy unit to finish intr frame */ MPLOCKED incb _intr_nesting_level MEXITCOUNT jmp _doreti /* * Call gate entry for syscall. * The intersegment call has been set up to specify one dummy parameter. * This leaves a place to put eflags so that the call frame can be * converted to a trap frame. Note that the eflags is (semi-)bogusly * pushed into (what will be) tf_err and then copied later into the * final spot. It has to be done this way because esp can't be just * temporarily altered for the pushfl - an interrupt might come in * and clobber the saved cs/eip. */ SUPERALIGN_TEXT IDTVEC(syscall) pushfl /* save eflags in tf_err for now */ subl $4,%esp /* skip over tf_trapno */ pushal pushl %ds pushl %es movl $KDSEL,%eax /* switch to kernel segments */ movl %ax,%ds movl %ax,%es movl TF_ERR(%esp),%eax /* copy saved eflags to final spot */ movl %eax,TF_EFLAGS(%esp) movl $7,TF_ERR(%esp) /* sizeof "lcall 7,0" */ FAKE_MCOUNT(12*4(%esp)) MPLOCKED incl _cnt+V_SYSCALL SYSCALL_LOCK ECPL_LOCK #ifdef CPL_AND_CML movl $SWI_AST_MASK,_cml #else movl $SWI_AST_MASK,_cpl #endif ECPL_UNLOCK call _syscall /* * Return via _doreti to handle ASTs. */ pushl $0 /* cpl to restore */ subl $4,%esp /* dummy unit to finish intr frame */ movb $1,_intr_nesting_level MEXITCOUNT jmp _doreti /* * Call gate entry for Linux/NetBSD syscall (int 0x80) */ SUPERALIGN_TEXT IDTVEC(int0x80_syscall) subl $8,%esp /* skip over tf_trapno and tf_err */ pushal pushl %ds pushl %es movl $KDSEL,%eax /* switch to kernel segments */ movl %ax,%ds movl %ax,%es movl $2,TF_ERR(%esp) /* sizeof "int 0x80" */ FAKE_MCOUNT(12*4(%esp)) MPLOCKED incl _cnt+V_SYSCALL ALTSYSCALL_LOCK ECPL_LOCK #ifdef CPL_AND_CML movl $SWI_AST_MASK,_cml #else movl $SWI_AST_MASK,_cpl #endif ECPL_UNLOCK call _syscall /* * Return via _doreti to handle ASTs. */ pushl $0 /* cpl to restore */ subl $4,%esp /* dummy unit to finish intr frame */ movb $1,_intr_nesting_level MEXITCOUNT jmp _doreti ENTRY(fork_trampoline) call _spl0 #ifdef SMP cmpl $0,_switchtime jne 1f pushl $_switchtime call _microuptime popl %edx movl _ticks,%eax movl %eax,_switchticks 1: #endif /* * cpu_set_fork_handler intercepts this function call to * have this call a non-return function to stay in kernel mode. * initproc has its own fork handler, but it does return. */ pushl %ebx /* arg1 */ call %esi /* function */ addl $4,%esp /* cut from syscall */ /* * Return via _doreti to handle ASTs. */ pushl $0 /* cpl to restore */ subl $4,%esp /* dummy unit to finish intr frame */ movb $1,_intr_nesting_level MEXITCOUNT jmp _doreti #ifdef VM86 /* * Include vm86 call routines, which want to call _doreti. */ #include "i386/i386/vm86bios.s" #endif /* VM86 */ /* * Include what was once config+isa-dependent code. * XXX it should be in a stand-alone file. It's still icu-dependent and * belongs in i386/isa. */ #include "i386/isa/vector.s" /* * Include what was once icu-dependent code. * XXX it should be merged into this file (also move the definition of * imen to vector.s or isa.c). * Before including it, set up a normal asm environment so that vector.s * doesn't have to know that stuff is included after it. */ .data ALIGN_DATA .text SUPERALIGN_TEXT #include "i386/isa/ipl.s" Index: head/sys/amd64/amd64/fpu.c =================================================================== --- head/sys/amd64/amd64/fpu.c (revision 45719) +++ head/sys/amd64/amd64/fpu.c (revision 45720) @@ -1,689 +1,730 @@ /*- * 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 - * $Id: npx.c,v 1.65 1999/01/08 16:29:59 bde Exp $ + * $Id: npx.c,v 1.66 1999/03/28 23:28:18 dt Exp $ */ #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 -#include /* * 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) -/* XXX - should be in header file. */ -ointhand2_t npxintr; - #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 npxattach __P((struct isa_device *dvp)); -static int npxprobe __P((struct isa_device *dvp)); -static int npxprobe1 __P((struct isa_device *dvp)); +static int npx_attach __P((device_t dev)); + void npx_intr __P((void *)); +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 */ -struct isa_driver npxdriver = { - npxprobe, npxattach, "npx", -}; - 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; #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\ " __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\ " __XSTRING(CNAME(probetrap)) ": \n\ ss \n\ incl " __XSTRING(CNAME(npx_traps_while_probing)) " \n\ fnclex \n\ iret \n\ "); #endif /* SMP */ /* * 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 -npxprobe(dvp) - struct isa_device *dvp; +npx_probe(dev) + device_t dev; { -#ifdef SMP +/*#ifdef SMP*/ +#if 1 - return npxprobe1(dvp); + 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. */ - npx_intrno = NRSVIDT + ffs(dvp->id_irq) - 1; + npx_intrno = NRSVIDT + 13; 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 | dvp->id_irq)); - outb(IO_ICU2 + 1, ~(dvp->id_irq >> 8)); + outb(IO_ICU1 + 1, ~IRQ_SLAVE); + outb(IO_ICU2 + 1, ~(1 << (13 - 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 = npxprobe1(dvp); + 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 -npxprobe1(dvp) - struct isa_device *dvp; +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 - +/*#ifdef SMP*/ +#if 1 /* * Exception 16 MUST work for SMP. */ npx_irq13 = 0; npx_ex16 = hw_float = npx_exists = 1; - dvp->id_irq = 0; /* zap the interrupt */ - /* - * special return value to flag that we do not - * actually use any I/O registers - */ - return (-1); + device_set_desc(dev, "math processor"); + return (0); -#else /* SMP */ +#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; - dvp->id_irq = 0; /* zap the interrupt */ - /* - * special return value to flag that we do not - * actually use any I/O registers - */ - return (-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 |= dvp->id_irq; - return (IO_NPXSIZE); + npx0_imask |= (1 << 13); + + /* + * 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, 13, 13, + 1, RF_ACTIVE); + if (r == 0) + panic("npx: can't get IRQ"); + BUS_SETUP_INTR(device_get_parent(dev), + dev, r, 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. */ - dvp->id_irq = 0; - /* - * special return value to flag that we do not - * actually use any I/O registers - */ - return (-1); - + return (0); #endif /* SMP */ } /* * Attach routine - announce which it is, and wire into system */ int -npxattach(dvp) - struct isa_device *dvp; +npx_attach(dev) + device_t dev; { - dvp->id_ointr = npxintr; + int flags; - /* The caller has printed "irq 13" for the npx_irq13 case. */ - if (!npx_irq13) { - printf("npx%d: ", dvp->id_unit); + device_print_prettyname(dev); + if (npx_irq13) { + printf("using IRQ 13 interface\n"); + } else { if (npx_ex16) printf("INT 16 interface\n"); #if defined(MATH_EMULATE) || defined(GPL_MATH_EMULATE) else if (npx_exists) { printf("error reporting broken; using 387 emulator\n"); hw_float = npx_exists = 0; } else printf("387 emulator\n"); #else else printf("no 387 emulator in kernel!\n"); #endif } npxinit(__INITIAL_NPXCW__); #ifdef I586_CPU + if (resource_int_value("npx", 0, "flags", &flags) != 0) + flags = 0; + if (cpu_class == CPUCLASS_586 && npx_ex16 && timezero("i586_bzero()", i586_bzero) < timezero("bzero()", bzero) * 4 / 5) { - if (!(dvp->id_flags & NPX_DISABLE_I586_OPTIMIZED_BCOPY)) { + if (!(flags & NPX_DISABLE_I586_OPTIMIZED_BCOPY)) { bcopy_vector = i586_bcopy; ovbcopy_vector = i586_bcopy; } - if (!(dvp->id_flags & NPX_DISABLE_I586_OPTIMIZED_BZERO)) + if (!(flags & NPX_DISABLE_I586_OPTIMIZED_BZERO)) bzero = i586_bzero; - if (!(dvp->id_flags & NPX_DISABLE_I586_OPTIMIZED_COPYIO)) { + if (!(flags & NPX_DISABLE_I586_OPTIMIZED_COPYIO)) { copyin_vector = i586_copyin; copyout_vector = i586_copyout; } } #endif - return (1); /* XXX unused */ + 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 } /* * 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. * * XXX there is no standard way to tell SIGFPE handlers about the error * state. The old interface: * * void handler(int sig, int code, struct sigcontext *scp); * * is broken because it is non-ANSI and because the FP state is not in * struct sigcontext. * * 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 -npxintr(unit) - int unit; +npx_intr(dummy) + void *dummy; { int code; 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); fnclex(); /* * Pass exception to process. */ - frame = (struct intrframe *)&unit; /* XXX */ + 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 = (struct trapframe *)&frame->if_es; #ifdef notyet /* * Encode the appropriate code for detailed information on * this exception. */ code = XXX_ENCODE(curpcb->pcb_savefpu.sv_ex_sw); #else code = 0; /* XXX */ #endif 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_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, + DRIVER_TYPE_MISC, + 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 */ Index: head/sys/amd64/amd64/legacy.c =================================================================== --- head/sys/amd64/amd64/legacy.c (nonexistent) +++ head/sys/amd64/amd64/legacy.c (revision 45720) @@ -0,0 +1,409 @@ +/* + * Copyright 1998 Massachusetts Institute of Technology + * + * Permission to use, copy, modify, and distribute this software and + * its documentation for any purpose and without fee is hereby + * granted, provided that both the above copyright notice and this + * permission notice appear in all copies, that both the above + * copyright notice and this permission notice appear in all + * supporting documentation, and that the name of M.I.T. not be used + * in advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. M.I.T. makes + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS + * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT + * SHALL M.I.T. 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. + * + * $Id$ + */ + +/* + * This code implements a `root nexus' for Intel Architecture + * machines. The function of the root nexus is to serve as an + * attachment point for both processors and buses, and to manage + * resources which are common to all of them. In particular, + * this code implements the core resource managers for interrupt + * requests, DMA requests (which rightfully should be a part of the + * ISA code but it's easier to do it here for now), I/O port addresses, + * and I/O memory address space. + */ + +#include "opt_smp.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#ifdef APIC_IO +#include +#include +#endif + +#include +#include +#include + +#include + +#include "eisa.h" +#include "isa.h" +#include "pci.h" +#include "npx.h" +#include "apm.h" + +static struct rman irq_rman, drq_rman, port_rman, mem_rman; + +static int nexus_probe(device_t); +static void nexus_print_child(device_t, device_t); +static struct resource *nexus_alloc_resource(device_t, device_t, int, int *, + u_long, u_long, u_long, u_int); +static int nexus_activate_resource(device_t, device_t, int, int, + struct resource *); +static int nexus_deactivate_resource(device_t, device_t, int, int, + struct resource *); +static int nexus_release_resource(device_t, device_t, int, int, + struct resource *); +static int nexus_setup_intr(device_t, device_t, struct resource *, + void (*)(void *), void *, void **); +static int nexus_teardown_intr(device_t, device_t, struct resource *, + void *); + +static device_method_t nexus_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, nexus_probe), + DEVMETHOD(device_attach, bus_generic_attach), + DEVMETHOD(device_detach, bus_generic_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + + /* Bus interface */ + DEVMETHOD(bus_print_child, nexus_print_child), + DEVMETHOD(bus_read_ivar, bus_generic_read_ivar), + DEVMETHOD(bus_write_ivar, bus_generic_write_ivar), + DEVMETHOD(bus_alloc_resource, nexus_alloc_resource), + DEVMETHOD(bus_release_resource, nexus_release_resource), + DEVMETHOD(bus_activate_resource, nexus_activate_resource), + DEVMETHOD(bus_deactivate_resource, nexus_deactivate_resource), + DEVMETHOD(bus_setup_intr, nexus_setup_intr), + DEVMETHOD(bus_teardown_intr, nexus_teardown_intr), + + { 0, 0 } +}; + +static driver_t nexus_driver = { + "nexus", + nexus_methods, + DRIVER_TYPE_MISC, + 1, /* no softc */ +}; +static devclass_t nexus_devclass; + +DRIVER_MODULE(nexus, root, nexus_driver, nexus_devclass, 0, 0); + +#ifdef APIC_IO +#define LASTIRQ (NINTR - 1) +#else +#define LASTIRQ 15 +#endif + +static int +nexus_probe(device_t dev) +{ + device_t child; + + device_quiet(dev); /* suppress attach message for neatness */ + + irq_rman.rm_start = 0; + irq_rman.rm_end = LASTIRQ; + irq_rman.rm_type = RMAN_ARRAY; + irq_rman.rm_descr = "Interrupt request lines"; + if (rman_init(&irq_rman) + || rman_manage_region(&irq_rman, 0, 1) + || rman_manage_region(&irq_rman, 3, LASTIRQ)) + panic("nexus_probe irq_rman"); + + drq_rman.rm_start = 0; + drq_rman.rm_end = 7; + drq_rman.rm_type = RMAN_ARRAY; + drq_rman.rm_descr = "DMA request lines"; + /* XXX drq 0 not available on some machines */ + if (rman_init(&drq_rman) + || rman_manage_region(&drq_rman, 0, 7)) + panic("nexus_probe drq_rman"); + + port_rman.rm_start = 0; + port_rman.rm_end = 0xffff; + port_rman.rm_type = RMAN_ARRAY; + port_rman.rm_descr = "I/O ports"; + if (rman_init(&port_rman) + || rman_manage_region(&port_rman, 0, 0xffff)) + panic("nexus_probe port_rman"); + + mem_rman.rm_start = 0; + mem_rman.rm_end = ~0u; + mem_rman.rm_type = RMAN_ARRAY; + mem_rman.rm_descr = "I/O memory addresses"; + if (rman_init(&mem_rman) + || rman_manage_region(&mem_rman, 0, ~0)) + panic("nexus_probe mem_rman"); + +#if NNPX > 0 + child = device_add_child(dev, "npx", 0, 0); + if (child == 0) + panic("nexus_probe npx"); +#endif /* NNPX > 0 */ +#if NAPM > 0 + child = device_add_child(dev, "apm", 0, 0); + if (child == 0) + panic("nexus_probe apm"); +#endif /* NAPM > 0 */ +#if NPCI > 0 + /* Add a PCI bridge if pci bus is present */ + if (pci_cfgopen() != 0) { + child = device_add_child(dev, "pcib", 0, 0); + if (child == 0) + panic("nexus_probe pcib"); + } +#endif +#if 0 && NEISA > 0 + child = device_add_child(dev, "eisa", 0, 0); + if (child == 0) + panic("nexus_probe eisa"); +#endif +#if NISA > 0 + /* Add an ISA bus directly if pci bus is not present */ + if (pci_cfgopen() == 0) { + child = device_add_child(dev, "isa", 0, 0); + if (child == 0) + panic("nexus_probe isa"); + } +#endif + return 0; +} + +static void +nexus_print_child(device_t bus, device_t child) +{ + printf(" on motherboard"); +} + +/* + * Allocate a resource on behalf of child. NB: child is usually going to be a + * child of one of our descendants, not a direct child of nexus0. + * (Exceptions include npx.) + */ +static struct resource * +nexus_alloc_resource(device_t bus, device_t child, int type, int *rid, + u_long start, u_long end, u_long count, u_int flags) +{ + struct resource *rv; + struct rman *rm; + + switch (type) { + case SYS_RES_IRQ: + rm = &irq_rman; + break; + + case SYS_RES_DRQ: + rm = &drq_rman; + break; + + case SYS_RES_IOPORT: + rm = &port_rman; + break; + + case SYS_RES_MEMORY: + rm = &mem_rman; + break; + + default: + return 0; + } + + rv = rman_reserve_resource(rm, start, end, count, flags, child); + if (rv == 0) + return 0; + + if (type == SYS_RES_MEMORY) { + caddr_t vaddr = 0; + + if (rv->r_end < 1024 * 1024 * 1024) { + /* + * The first 1Mb is mapped at KERNBASE. + */ + vaddr = (caddr_t)((uintptr_t)KERNBASE + rv->r_start); + } else { + u_int32_t paddr; + u_int32_t psize; + u_int32_t poffs; + + paddr = rv->r_start; + psize = rv->r_end - rv->r_start; + + poffs = paddr - trunc_page(paddr); + vaddr = (caddr_t) pmap_mapdev(paddr-poffs, psize+poffs) + poffs; + } + rman_set_virtual(rv, vaddr); + rman_set_bustag(rv, I386_BUS_SPACE_MEM); + rman_set_bushandle(rv, (bus_space_handle_t) vaddr); + } else if (type == SYS_RES_IOPORT) { + rman_set_bustag(rv, I386_BUS_SPACE_IO); + rman_set_bushandle(rv, rv->r_start); + } + return rv; +} + +static int +nexus_activate_resource(device_t bus, device_t child, int type, int rid, + struct resource *r) +{ + return (rman_activate_resource(r)); +} + +static int +nexus_deactivate_resource(device_t bus, device_t child, int type, int rid, + struct resource *r) +{ + return (rman_deactivate_resource(r)); +} + +static int +nexus_release_resource(device_t bus, device_t child, int type, int rid, + struct resource *r) +{ + return (rman_release_resource(r)); +} + +/* + * Currently this uses the really grody interface from kern/kern_intr.c + * (which really doesn't belong in kern/anything.c). Eventually, all of + * the code in kern_intr.c and machdep_intr.c should get moved here, since + * this is going to be the official interface. + */ +static int +nexus_setup_intr(device_t bus, device_t child, struct resource *irq, + void (*ihand)(void *), void *arg, void **cookiep) +{ + intrmask_t *mask; + driver_t *driver; + int error, icflags; + + if (child) + device_printf(child, "interrupting at irq %d\n", + (int)irq->r_start); + + *cookiep = 0; + if (irq->r_flags & RF_SHAREABLE) + icflags = 0; + else + icflags = INTR_EXCL; + + driver = device_get_driver(child); + switch (driver->type) { + case DRIVER_TYPE_TTY: + mask = &tty_imask; + break; + case (DRIVER_TYPE_TTY | DRIVER_TYPE_FAST): + mask = &tty_imask; + icflags |= INTR_FAST; + break; + case DRIVER_TYPE_BIO: + mask = &bio_imask; + break; + case DRIVER_TYPE_NET: + mask = &net_imask; + break; + case DRIVER_TYPE_CAM: + mask = &cam_imask; + break; + case DRIVER_TYPE_MISC: + mask = 0; + break; + default: + panic("still using grody create_intr interface"); + } + + /* + * We depend here on rman_activate_resource() being idempotent. + */ + error = rman_activate_resource(irq); + if (error) + return (error); + + *cookiep = intr_create((void *)(intptr_t)-1, irq->r_start, ihand, arg, + mask, icflags); + if (*cookiep) + error = intr_connect(*cookiep); + else + error = EINVAL; /* XXX ??? */ + + return (error); +} + +static int +nexus_teardown_intr(device_t dev, device_t child, struct resource *r, void *ih) +{ + return (intr_destroy(ih)); +} + +static devclass_t pcib_devclass; + +static int +nexus_pcib_probe(device_t dev) +{ + device_set_desc(dev, "PCI host bus adapter"); + + device_add_child(dev, "pci", 0, 0); + + return 0; +} + +static device_method_t nexus_pcib_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, nexus_pcib_probe), + DEVMETHOD(device_attach, bus_generic_attach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + + /* Bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), + DEVMETHOD(bus_release_resource, bus_generic_release_resource), + DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), + DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), + DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), + DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), + + { 0, 0 } +}; + +static driver_t nexus_pcib_driver = { + "pcib", + nexus_pcib_methods, + DRIVER_TYPE_MISC, + 1, +}; + +DRIVER_MODULE(pcib, nexus, nexus_pcib_driver, pcib_devclass, 0, 0); Property changes on: head/sys/amd64/amd64/legacy.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: head/sys/amd64/amd64/machdep.c =================================================================== --- head/sys/amd64/amd64/machdep.c (revision 45719) +++ head/sys/amd64/amd64/machdep.c (revision 45720) @@ -1,1940 +1,1946 @@ /*- * Copyright (c) 1992 Terrence R. Lambert. * Copyright (c) 1982, 1987, 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * William Jolitz. * * 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: @(#)machdep.c 7.4 (Berkeley) 6/3/91 - * $Id: machdep.c,v 1.327 1999/03/06 04:46:18 wollman Exp $ + * $Id: machdep.c,v 1.328 1999/04/03 22:19:58 jdp Exp $ */ #include "apm.h" #include "ether.h" #include "npx.h" #include "opt_atalk.h" #include "opt_cpu.h" #include "opt_ddb.h" #include "opt_inet.h" #include "opt_ipx.h" #include "opt_maxmem.h" #include "opt_msgbuf.h" #include "opt_perfmon.h" #include "opt_smp.h" #include "opt_sysvipc.h" #include "opt_user_ldt.h" #include "opt_userconfig.h" #include "opt_vm86.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include #ifdef SYSVSHM #include #endif #ifdef SYSVMSG #include #endif #ifdef SYSVSEM #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(INET) || defined(IPX) || defined(NATM) || defined(NETATALK) \ || NETHER > 0 || defined(NS) #define NETISR #endif #ifdef NETISR #include #endif #include #include #include #include #include #include #include #include #include /* pcb.h included via sys/user.h */ #ifdef SMP #include #endif #ifdef PERFMON #include #endif +#ifdef OLD_BUS_ARCH #include +#endif #include #ifndef VM86 #include #endif #include #include extern void init386 __P((int first)); extern void dblfault_handler __P((void)); extern void printcpuinfo(void); /* XXX header file */ extern void earlysetcpuclass(void); /* same header file */ extern void finishidentcpu(void); extern void panicifcpuunsupported(void); extern void initializecpu(void); static void cpu_startup __P((void *)); SYSINIT(cpu, SI_SUB_CPU, SI_ORDER_FIRST, cpu_startup, NULL) static MALLOC_DEFINE(M_MBUF, "mbuf", "mbuf"); int _udatasel, _ucodesel; u_int atdevbase; #if defined(SWTCH_OPTIM_STATS) extern int swtch_optim_stats; SYSCTL_INT(_debug, OID_AUTO, swtch_optim_stats, CTLFLAG_RD, &swtch_optim_stats, 0, ""); SYSCTL_INT(_debug, OID_AUTO, tlb_flush_count, CTLFLAG_RD, &tlb_flush_count, 0, ""); #endif #ifdef PC98 static int ispc98 = 1; #else static int ispc98 = 0; #endif SYSCTL_INT(_machdep, OID_AUTO, ispc98, CTLFLAG_RD, &ispc98, 0, ""); int physmem = 0; int cold = 1; static int sysctl_hw_physmem SYSCTL_HANDLER_ARGS { int error = sysctl_handle_int(oidp, 0, ctob(physmem), req); return (error); } SYSCTL_PROC(_hw, HW_PHYSMEM, physmem, CTLTYPE_INT|CTLFLAG_RD, 0, 0, sysctl_hw_physmem, "I", ""); static int sysctl_hw_usermem SYSCTL_HANDLER_ARGS { int error = sysctl_handle_int(oidp, 0, ctob(physmem - cnt.v_wire_count), req); return (error); } SYSCTL_PROC(_hw, HW_USERMEM, usermem, CTLTYPE_INT|CTLFLAG_RD, 0, 0, sysctl_hw_usermem, "I", ""); static int sysctl_hw_availpages SYSCTL_HANDLER_ARGS { int error = sysctl_handle_int(oidp, 0, i386_btop(avail_end - avail_start), req); return (error); } SYSCTL_PROC(_hw, OID_AUTO, availpages, CTLTYPE_INT|CTLFLAG_RD, 0, 0, sysctl_hw_availpages, "I", ""); static int sysctl_machdep_msgbuf SYSCTL_HANDLER_ARGS { int error; /* Unwind the buffer, so that it's linear (possibly starting with * some initial nulls). */ error=sysctl_handle_opaque(oidp,msgbufp->msg_ptr+msgbufp->msg_bufr, msgbufp->msg_size-msgbufp->msg_bufr,req); if(error) return(error); if(msgbufp->msg_bufr>0) { error=sysctl_handle_opaque(oidp,msgbufp->msg_ptr, msgbufp->msg_bufr,req); } return(error); } SYSCTL_PROC(_machdep, OID_AUTO, msgbuf, CTLTYPE_STRING|CTLFLAG_RD, 0, 0, sysctl_machdep_msgbuf, "A","Contents of kernel message buffer"); static int msgbuf_clear; static int sysctl_machdep_msgbuf_clear SYSCTL_HANDLER_ARGS { int error; error = sysctl_handle_int(oidp, oidp->oid_arg1, oidp->oid_arg2, req); if (!error && req->newptr) { /* Clear the buffer and reset write pointer */ bzero(msgbufp->msg_ptr,msgbufp->msg_size); msgbufp->msg_bufr=msgbufp->msg_bufx=0; msgbuf_clear=0; } return (error); } SYSCTL_PROC(_machdep, OID_AUTO, msgbuf_clear, CTLTYPE_INT|CTLFLAG_RW, &msgbuf_clear, 0, sysctl_machdep_msgbuf_clear, "I", "Clear kernel message buffer"); int bootverbose = 0, Maxmem = 0; long dumplo; vm_offset_t phys_avail[10]; /* must be 2 less so 0 0 can signal end of chunks */ #define PHYS_AVAIL_ARRAY_END ((sizeof(phys_avail) / sizeof(vm_offset_t)) - 2) #ifdef NETISR static void setup_netisrs __P((struct linker_set *)); #endif static vm_offset_t buffer_sva, buffer_eva; vm_offset_t clean_sva, clean_eva; static vm_offset_t pager_sva, pager_eva; #ifdef NETISR extern struct linker_set netisr_set; #endif #if NNPX > 0 extern struct isa_driver npxdriver; #endif #define offsetof(type, member) ((size_t)(&((type *)0)->member)) static void cpu_startup(dummy) void *dummy; { register unsigned i; register caddr_t v; vm_offset_t maxaddr; vm_size_t size = 0; int firstaddr; vm_offset_t minaddr; if (boothowto & RB_VERBOSE) bootverbose++; /* * Good {morning,afternoon,evening,night}. */ printf(version); earlysetcpuclass(); startrtclock(); printcpuinfo(); panicifcpuunsupported(); #ifdef PERFMON perfmon_init(); #endif printf("real memory = %u (%uK bytes)\n", ptoa(Maxmem), ptoa(Maxmem) / 1024); /* * Display any holes after the first chunk of extended memory. */ if (bootverbose) { int indx; printf("Physical memory chunk(s):\n"); for (indx = 0; phys_avail[indx + 1] != 0; indx += 2) { int size1 = phys_avail[indx + 1] - phys_avail[indx]; printf("0x%08x - 0x%08x, %u bytes (%u pages)\n", phys_avail[indx], phys_avail[indx + 1] - 1, size1, size1 / PAGE_SIZE); } } #ifdef NETISR /* * Quickly wire in netisrs. */ setup_netisrs(&netisr_set); #endif /* * Calculate callout wheel size */ for (callwheelsize = 1, callwheelbits = 0; callwheelsize < ncallout; callwheelsize <<= 1, ++callwheelbits) ; callwheelmask = callwheelsize - 1; /* * Allocate space for system data structures. * The first available kernel virtual address is in "v". * As pages of kernel virtual memory are allocated, "v" is incremented. * As pages of memory are allocated and cleared, * "firstaddr" is incremented. * An index into the kernel page table corresponding to the * virtual memory address maintained in "v" is kept in "mapaddr". */ /* * Make two passes. The first pass calculates how much memory is * needed and allocates it. The second pass assigns virtual * addresses to the various data structures. */ firstaddr = 0; again: v = (caddr_t)firstaddr; #define valloc(name, type, num) \ (name) = (type *)v; v = (caddr_t)((name)+(num)) #define valloclim(name, type, num, lim) \ (name) = (type *)v; v = (caddr_t)((lim) = ((name)+(num))) valloc(callout, struct callout, ncallout); valloc(callwheel, struct callout_tailq, callwheelsize); #ifdef SYSVSHM valloc(shmsegs, struct shmid_ds, shminfo.shmmni); #endif #ifdef SYSVSEM valloc(sema, struct semid_ds, seminfo.semmni); valloc(sem, struct sem, seminfo.semmns); /* This is pretty disgusting! */ valloc(semu, int, (seminfo.semmnu * seminfo.semusz) / sizeof(int)); #endif #ifdef SYSVMSG valloc(msgpool, char, msginfo.msgmax); valloc(msgmaps, struct msgmap, msginfo.msgseg); valloc(msghdrs, struct msg, msginfo.msgtql); valloc(msqids, struct msqid_ds, msginfo.msgmni); #endif if (nbuf == 0) { nbuf = 30; if( physmem > 1024) nbuf += min((physmem - 1024) / 8, 2048); } nswbuf = max(min(nbuf/4, 64), 16); valloc(swbuf, struct buf, nswbuf); valloc(buf, struct buf, nbuf); /* * End of first pass, size has been calculated so allocate memory */ if (firstaddr == 0) { size = (vm_size_t)(v - firstaddr); firstaddr = (int)kmem_alloc(kernel_map, round_page(size)); if (firstaddr == 0) panic("startup: no room for tables"); goto again; } /* * End of second pass, addresses have been assigned */ if ((vm_size_t)(v - firstaddr) != size) panic("startup: table size inconsistency"); clean_map = kmem_suballoc(kernel_map, &clean_sva, &clean_eva, (nbuf*BKVASIZE) + (nswbuf*MAXPHYS) + pager_map_size); buffer_map = kmem_suballoc(clean_map, &buffer_sva, &buffer_eva, (nbuf*BKVASIZE)); pager_map = kmem_suballoc(clean_map, &pager_sva, &pager_eva, (nswbuf*MAXPHYS) + pager_map_size); pager_map->system_map = 1; exec_map = kmem_suballoc(kernel_map, &minaddr, &maxaddr, (16*(ARG_MAX+(PAGE_SIZE*3)))); /* * Finally, allocate mbuf pool. Since mclrefcnt is an off-size * we use the more space efficient malloc in place of kmem_alloc. */ { vm_offset_t mb_map_size; int xclusters; /* Allow override of NMBCLUSTERS from the kernel environment */ if (getenv_int("kern.ipc.nmbclusters", &xclusters) && xclusters > nmbclusters) nmbclusters = xclusters; mb_map_size = nmbufs * MSIZE + nmbclusters * MCLBYTES; mb_map_size = roundup2(mb_map_size, max(MCLBYTES, PAGE_SIZE)); mclrefcnt = malloc(mb_map_size / MCLBYTES, M_MBUF, M_NOWAIT); bzero(mclrefcnt, mb_map_size / MCLBYTES); mb_map = kmem_suballoc(kmem_map, (vm_offset_t *)&mbutl, &maxaddr, mb_map_size); mb_map->system_map = 1; } /* * Initialize callouts */ SLIST_INIT(&callfree); for (i = 0; i < ncallout; i++) { callout_init(&callout[i]); callout[i].c_flags = CALLOUT_LOCAL_ALLOC; SLIST_INSERT_HEAD(&callfree, &callout[i], c_links.sle); } for (i = 0; i < callwheelsize; i++) { TAILQ_INIT(&callwheel[i]); } #if defined(USERCONFIG) userconfig(); cninit(); /* the preferred console may have changed */ #endif printf("avail memory = %u (%uK bytes)\n", ptoa(cnt.v_free_count), ptoa(cnt.v_free_count) / 1024); /* * Set up buffers, so they can be used to read disk labels. */ bufinit(); vm_pager_bufferinit(); #ifdef SMP /* * OK, enough kmem_alloc/malloc state should be up, lets get on with it! */ mp_start(); /* fire up the APs and APICs */ mp_announce(); #endif /* SMP */ } #ifdef NETISR int register_netisr(num, handler) int num; netisr_t *handler; { if (num < 0 || num >= (sizeof(netisrs)/sizeof(*netisrs)) ) { printf("register_netisr: bad isr number: %d\n", num); return (EINVAL); } netisrs[num] = handler; return (0); } static void setup_netisrs(ls) struct linker_set *ls; { int i; const struct netisrtab *nit; for(i = 0; ls->ls_items[i]; i++) { nit = (const struct netisrtab *)ls->ls_items[i]; register_netisr(nit->nit_num, nit->nit_isr); } } #endif /* NETISR */ /* * Send an interrupt to process. * * Stack is set up to allow sigcode stored * at top to call routine, followed by kcall * to sigreturn routine below. After sigreturn * resets the signal mask, the stack, and the * frame pointer, it returns to the user * specified pc, psl. */ void sendsig(catcher, sig, mask, code) sig_t catcher; int sig, mask; u_long code; { register struct proc *p = curproc; register struct trapframe *regs; register struct sigframe *fp; struct sigframe sf; struct sigacts *psp = p->p_sigacts; int oonstack; regs = p->p_md.md_regs; oonstack = psp->ps_sigstk.ss_flags & SS_ONSTACK; /* * Allocate and validate space for the signal handler context. */ if ((psp->ps_flags & SAS_ALTSTACK) && !oonstack && (psp->ps_sigonstack & sigmask(sig))) { fp = (struct sigframe *)(psp->ps_sigstk.ss_sp + psp->ps_sigstk.ss_size - sizeof(struct sigframe)); psp->ps_sigstk.ss_flags |= SS_ONSTACK; } else { fp = (struct sigframe *)regs->tf_esp - 1; } /* * grow() will return FALSE if the fp will not fit inside the stack * and the stack can not be grown. useracc will return FALSE * if access is denied. */ #ifdef VM_STACK if ((grow_stack (p, (int)fp) == FALSE) || #else if ((grow(p, (int)fp) == FALSE) || #endif (useracc((caddr_t)fp, sizeof(struct sigframe), B_WRITE) == FALSE)) { /* * Process has trashed its stack; give it an illegal * instruction to halt it in its tracks. */ SIGACTION(p, SIGILL) = SIG_DFL; sig = sigmask(SIGILL); p->p_sigignore &= ~sig; p->p_sigcatch &= ~sig; p->p_sigmask &= ~sig; psignal(p, SIGILL); return; } /* * Build the argument list for the signal handler. */ if (p->p_sysent->sv_sigtbl) { if (sig < p->p_sysent->sv_sigsize) sig = p->p_sysent->sv_sigtbl[sig]; else sig = p->p_sysent->sv_sigsize + 1; } sf.sf_signum = sig; sf.sf_code = code; sf.sf_scp = &fp->sf_sc; sf.sf_addr = (char *) regs->tf_err; sf.sf_handler = catcher; /* save scratch registers */ sf.sf_sc.sc_eax = regs->tf_eax; sf.sf_sc.sc_ebx = regs->tf_ebx; sf.sf_sc.sc_ecx = regs->tf_ecx; sf.sf_sc.sc_edx = regs->tf_edx; sf.sf_sc.sc_esi = regs->tf_esi; sf.sf_sc.sc_edi = regs->tf_edi; sf.sf_sc.sc_cs = regs->tf_cs; sf.sf_sc.sc_ds = regs->tf_ds; sf.sf_sc.sc_ss = regs->tf_ss; sf.sf_sc.sc_es = regs->tf_es; sf.sf_sc.sc_isp = regs->tf_isp; /* * Build the signal context to be used by sigreturn. */ sf.sf_sc.sc_onstack = oonstack; sf.sf_sc.sc_mask = mask; sf.sf_sc.sc_sp = regs->tf_esp; sf.sf_sc.sc_fp = regs->tf_ebp; sf.sf_sc.sc_pc = regs->tf_eip; sf.sf_sc.sc_ps = regs->tf_eflags; sf.sf_sc.sc_trapno = regs->tf_trapno; sf.sf_sc.sc_err = regs->tf_err; #ifdef VM86 /* * If we're a vm86 process, we want to save the segment registers. * We also change eflags to be our emulated eflags, not the actual * eflags. */ if (regs->tf_eflags & PSL_VM) { struct trapframe_vm86 *tf = (struct trapframe_vm86 *)regs; struct vm86_kernel *vm86 = &p->p_addr->u_pcb.pcb_ext->ext_vm86; sf.sf_sc.sc_gs = tf->tf_vm86_gs; sf.sf_sc.sc_fs = tf->tf_vm86_fs; sf.sf_sc.sc_es = tf->tf_vm86_es; sf.sf_sc.sc_ds = tf->tf_vm86_ds; if (vm86->vm86_has_vme == 0) sf.sf_sc.sc_ps = (tf->tf_eflags & ~(PSL_VIF | PSL_VIP)) | (vm86->vm86_eflags & (PSL_VIF | PSL_VIP)); /* * We should never have PSL_T set when returning from vm86 * mode. It may be set here if we deliver a signal before * getting to vm86 mode, so turn it off. * * Clear PSL_NT to inhibit T_TSSFLT faults on return from * syscalls made by the signal handler. This just avoids * wasting time for our lazy fixup of such faults. PSL_NT * does nothing in vm86 mode, but vm86 programs can set it * almost legitimately in probes for old cpu types. */ tf->tf_eflags &= ~(PSL_VM | PSL_NT | PSL_T | PSL_VIF | PSL_VIP); } #endif /* VM86 */ /* * Copy the sigframe out to the user's stack. */ if (copyout(&sf, fp, sizeof(struct sigframe)) != 0) { /* * Something is wrong with the stack pointer. * ...Kill the process. */ sigexit(p, SIGILL); } regs->tf_esp = (int)fp; regs->tf_eip = PS_STRINGS - *(p->p_sysent->sv_szsigcode); regs->tf_cs = _ucodesel; regs->tf_ds = _udatasel; regs->tf_es = _udatasel; regs->tf_ss = _udatasel; } /* * System call to cleanup state after a signal * has been taken. Reset signal mask and * stack state from context left by sendsig (above). * Return to previous pc and psl as specified by * context left by sendsig. Check carefully to * make sure that the user has not modified the * state to gain improper privileges. */ int sigreturn(p, uap) struct proc *p; struct sigreturn_args /* { struct sigcontext *sigcntxp; } */ *uap; { register struct sigcontext *scp; register struct sigframe *fp; register struct trapframe *regs = p->p_md.md_regs; int eflags; /* * (XXX old comment) regs->tf_esp points to the return address. * The user scp pointer is above that. * The return address is faked in the signal trampoline code * for consistency. */ scp = uap->sigcntxp; fp = (struct sigframe *) ((caddr_t)scp - offsetof(struct sigframe, sf_sc)); if (useracc((caddr_t)fp, sizeof (*fp), B_WRITE) == 0) return(EFAULT); eflags = scp->sc_ps; #ifdef VM86 if (eflags & PSL_VM) { struct trapframe_vm86 *tf = (struct trapframe_vm86 *)regs; struct vm86_kernel *vm86; /* * if pcb_ext == 0 or vm86_inited == 0, the user hasn't * set up the vm86 area, and we can't enter vm86 mode. */ if (p->p_addr->u_pcb.pcb_ext == 0) return (EINVAL); vm86 = &p->p_addr->u_pcb.pcb_ext->ext_vm86; if (vm86->vm86_inited == 0) return (EINVAL); /* go back to user mode if both flags are set */ if ((eflags & PSL_VIP) && (eflags & PSL_VIF)) trapsignal(p, SIGBUS, 0); if (vm86->vm86_has_vme) { eflags = (tf->tf_eflags & ~VME_USERCHANGE) | (eflags & VME_USERCHANGE) | PSL_VM; } else { vm86->vm86_eflags = eflags; /* save VIF, VIP */ eflags = (tf->tf_eflags & ~VM_USERCHANGE) | (eflags & VM_USERCHANGE) | PSL_VM; } tf->tf_vm86_ds = scp->sc_ds; tf->tf_vm86_es = scp->sc_es; tf->tf_vm86_fs = scp->sc_fs; tf->tf_vm86_gs = scp->sc_gs; tf->tf_ds = _udatasel; tf->tf_es = _udatasel; } else { #endif /* VM86 */ /* * Don't allow users to change privileged or reserved flags. */ #define EFLAGS_SECURE(ef, oef) ((((ef) ^ (oef)) & ~PSL_USERCHANGE) == 0) /* * XXX do allow users to change the privileged flag PSL_RF. * The cpu sets PSL_RF in tf_eflags for faults. Debuggers * should sometimes set it there too. tf_eflags is kept in * the signal context during signal handling and there is no * other place to remember it, so the PSL_RF bit may be * corrupted by the signal handler without us knowing. * Corruption of the PSL_RF bit at worst causes one more or * one less debugger trap, so allowing it is fairly harmless. */ if (!EFLAGS_SECURE(eflags & ~PSL_RF, regs->tf_eflags & ~PSL_RF)) { #ifdef DEBUG printf("sigreturn: eflags = 0x%x\n", eflags); #endif return(EINVAL); } /* * Don't allow users to load a valid privileged %cs. Let the * hardware check for invalid selectors, excess privilege in * other selectors, invalid %eip's and invalid %esp's. */ #define CS_SECURE(cs) (ISPL(cs) == SEL_UPL) if (!CS_SECURE(scp->sc_cs)) { #ifdef DEBUG printf("sigreturn: cs = 0x%x\n", scp->sc_cs); #endif trapsignal(p, SIGBUS, T_PROTFLT); return(EINVAL); } regs->tf_ds = scp->sc_ds; regs->tf_es = scp->sc_es; #ifdef VM86 } #endif /* restore scratch registers */ regs->tf_eax = scp->sc_eax; regs->tf_ebx = scp->sc_ebx; regs->tf_ecx = scp->sc_ecx; regs->tf_edx = scp->sc_edx; regs->tf_esi = scp->sc_esi; regs->tf_edi = scp->sc_edi; regs->tf_cs = scp->sc_cs; regs->tf_ss = scp->sc_ss; regs->tf_isp = scp->sc_isp; if (useracc((caddr_t)scp, sizeof (*scp), B_WRITE) == 0) return(EINVAL); if (scp->sc_onstack & 01) p->p_sigacts->ps_sigstk.ss_flags |= SS_ONSTACK; else p->p_sigacts->ps_sigstk.ss_flags &= ~SS_ONSTACK; p->p_sigmask = scp->sc_mask & ~sigcantmask; regs->tf_ebp = scp->sc_fp; regs->tf_esp = scp->sc_sp; regs->tf_eip = scp->sc_pc; regs->tf_eflags = eflags; return(EJUSTRETURN); } /* * Machine dependent boot() routine * * I haven't seen anything to put here yet * Possibly some stuff might be grafted back here from boot() */ void cpu_boot(int howto) { } /* * Shutdown the CPU as much as possible */ void cpu_halt(void) { for (;;) __asm__ ("hlt"); } /* * Clear registers on exec */ void setregs(p, entry, stack, ps_strings) struct proc *p; u_long entry; u_long stack; u_long ps_strings; { struct trapframe *regs = p->p_md.md_regs; struct pcb *pcb = &p->p_addr->u_pcb; #ifdef USER_LDT /* was i386_user_cleanup() in NetBSD */ if (pcb->pcb_ldt) { if (pcb == curpcb) { lldt(_default_ldt); currentldt = _default_ldt; } kmem_free(kernel_map, (vm_offset_t)pcb->pcb_ldt, pcb->pcb_ldt_len * sizeof(union descriptor)); pcb->pcb_ldt_len = (int)pcb->pcb_ldt = 0; } #endif bzero((char *)regs, sizeof(struct trapframe)); regs->tf_eip = entry; regs->tf_esp = stack; regs->tf_eflags = PSL_USER | (regs->tf_eflags & PSL_T); regs->tf_ss = _udatasel; regs->tf_ds = _udatasel; regs->tf_es = _udatasel; regs->tf_cs = _ucodesel; /* PS_STRINGS value for BSD/OS binaries. It is 0 for non-BSD/OS. */ regs->tf_ebx = ps_strings; /* reset %fs and %gs as well */ pcb->pcb_fs = _udatasel; pcb->pcb_gs = _udatasel; if (pcb == curpcb) { __asm("movw %w0,%%fs" : : "r" (_udatasel)); __asm("movw %w0,%%gs" : : "r" (_udatasel)); } /* * Initialize the math emulator (if any) for the current process. * Actually, just clear the bit that says that the emulator has * been initialized. Initialization is delayed until the process * traps to the emulator (if it is done at all) mainly because * emulators don't provide an entry point for initialization. */ p->p_addr->u_pcb.pcb_flags &= ~FP_SOFTFP; /* * Arrange to trap the next npx or `fwait' instruction (see npx.c * for why fwait must be trapped at least if there is an npx or an * emulator). This is mainly to handle the case where npx0 is not * configured, since the npx routines normally set up the trap * otherwise. It should be done only at boot time, but doing it * here allows modifying `npx_exists' for testing the emulator on * systems with an npx. */ load_cr0(rcr0() | CR0_MP | CR0_TS); #if NNPX > 0 /* Initialize the npx (if any) for the current process. */ npxinit(__INITIAL_NPXCW__); #endif /* * XXX - Linux emulator * Make sure sure edx is 0x0 on entry. Linux binaries depend * on it. */ p->p_retval[1] = 0; } static int sysctl_machdep_adjkerntz SYSCTL_HANDLER_ARGS { int error; error = sysctl_handle_int(oidp, oidp->oid_arg1, oidp->oid_arg2, req); if (!error && req->newptr) resettodr(); return (error); } SYSCTL_PROC(_machdep, CPU_ADJKERNTZ, adjkerntz, CTLTYPE_INT|CTLFLAG_RW, &adjkerntz, 0, sysctl_machdep_adjkerntz, "I", ""); SYSCTL_INT(_machdep, CPU_DISRTCSET, disable_rtc_set, CTLFLAG_RW, &disable_rtc_set, 0, ""); SYSCTL_STRUCT(_machdep, CPU_BOOTINFO, bootinfo, CTLFLAG_RD, &bootinfo, bootinfo, ""); SYSCTL_INT(_machdep, CPU_WALLCLOCK, wall_cmos_clock, CTLFLAG_RW, &wall_cmos_clock, 0, ""); /* * Initialize 386 and configure to run kernel */ /* * Initialize segments & interrupt table */ int _default_ldt; #ifdef SMP union descriptor gdt[NGDT + NCPU]; /* global descriptor table */ #else union descriptor gdt[NGDT]; /* global descriptor table */ #endif struct gate_descriptor idt[NIDT]; /* interrupt descriptor table */ union descriptor ldt[NLDT]; /* local descriptor table */ #ifdef SMP /* table descriptors - used to load tables by microp */ struct region_descriptor r_gdt, r_idt; #endif extern struct i386tss common_tss; /* One tss per cpu */ #ifdef VM86 extern struct segment_descriptor common_tssd; extern int private_tss; /* flag indicating private tss */ extern u_int my_tr; /* which task register setting */ #endif /* VM86 */ #if defined(I586_CPU) && !defined(NO_F00F_HACK) struct gate_descriptor *t_idt; extern int has_f00f_bug; #endif static struct i386tss dblfault_tss; static char dblfault_stack[PAGE_SIZE]; extern struct user *proc0paddr; /* software prototypes -- in more palatable form */ struct soft_segment_descriptor gdt_segs[ #ifdef SMP NGDT + NCPU #endif ] = { /* GNULL_SEL 0 Null Descriptor */ { 0x0, /* segment base address */ 0x0, /* length */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GCODE_SEL 1 Code Descriptor for kernel */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMERA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GDATA_SEL 2 Data Descriptor for kernel */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMRWA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GLDT_SEL 3 LDT Descriptor */ { (int) ldt, /* segment base address */ sizeof(ldt)-1, /* length - all address space */ SDT_SYSLDT, /* segment type */ SEL_UPL, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* unused - default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GTGATE_SEL 4 Null Descriptor - Placeholder */ { 0x0, /* segment base address */ 0x0, /* length - all address space */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GPANIC_SEL 5 Panic Tss Descriptor */ { (int) &dblfault_tss, /* segment base address */ sizeof(struct i386tss)-1,/* length - all address space */ SDT_SYS386TSS, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* unused - default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GPROC0_SEL 6 Proc 0 Tss Descriptor */ { (int) &common_tss, /* segment base address */ sizeof(struct i386tss)-1,/* length - all address space */ SDT_SYS386TSS, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* unused - default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GUSERLDT_SEL 7 User LDT Descriptor per process */ { (int) ldt, /* segment base address */ (512 * sizeof(union descriptor)-1), /* length */ SDT_SYSLDT, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* unused - default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GAPMCODE32_SEL 8 APM BIOS 32-bit interface (32bit Code) */ { 0, /* segment base address (overwritten by APM) */ 0xfffff, /* length */ SDT_MEMERA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GAPMCODE16_SEL 9 APM BIOS 32-bit interface (16bit Code) */ { 0, /* segment base address (overwritten by APM) */ 0xfffff, /* length */ SDT_MEMERA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GAPMDATA_SEL 10 APM BIOS 32-bit interface (Data) */ { 0, /* segment base address (overwritten by APM) */ 0xfffff, /* length */ SDT_MEMRWA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, }; static struct soft_segment_descriptor ldt_segs[] = { /* Null Descriptor - overwritten by call gate */ { 0x0, /* segment base address */ 0x0, /* length - all address space */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* Null Descriptor - overwritten by call gate */ { 0x0, /* segment base address */ 0x0, /* length - all address space */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* Null Descriptor - overwritten by call gate */ { 0x0, /* segment base address */ 0x0, /* length - all address space */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* Code Descriptor for user */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMERA, /* segment type */ SEL_UPL, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* Null Descriptor - overwritten by call gate */ { 0x0, /* segment base address */ 0x0, /* length - all address space */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* Data Descriptor for user */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMRWA, /* segment type */ SEL_UPL, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, }; void setidt(idx, func, typ, dpl, selec) int idx; inthand_t *func; int typ; int dpl; int selec; { struct gate_descriptor *ip; #if defined(I586_CPU) && !defined(NO_F00F_HACK) ip = (t_idt != NULL ? t_idt : idt) + idx; #else ip = idt + idx; #endif ip->gd_looffset = (int)func; ip->gd_selector = selec; ip->gd_stkcpy = 0; ip->gd_xx = 0; ip->gd_type = typ; ip->gd_dpl = dpl; ip->gd_p = 1; ip->gd_hioffset = ((int)func)>>16 ; } #define IDTVEC(name) __CONCAT(X,name) extern inthand_t IDTVEC(div), IDTVEC(dbg), IDTVEC(nmi), IDTVEC(bpt), IDTVEC(ofl), IDTVEC(bnd), IDTVEC(ill), IDTVEC(dna), IDTVEC(fpusegm), IDTVEC(tss), IDTVEC(missing), IDTVEC(stk), IDTVEC(prot), IDTVEC(page), IDTVEC(mchk), IDTVEC(rsvd), IDTVEC(fpu), IDTVEC(align), IDTVEC(syscall), IDTVEC(int0x80_syscall); void sdtossd(sd, ssd) struct segment_descriptor *sd; struct soft_segment_descriptor *ssd; { ssd->ssd_base = (sd->sd_hibase << 24) | sd->sd_lobase; ssd->ssd_limit = (sd->sd_hilimit << 16) | sd->sd_lolimit; ssd->ssd_type = sd->sd_type; ssd->ssd_dpl = sd->sd_dpl; ssd->ssd_p = sd->sd_p; ssd->ssd_def32 = sd->sd_def32; ssd->ssd_gran = sd->sd_gran; } void init386(first) int first; { int x; unsigned biosbasemem, biosextmem; struct gate_descriptor *gdp; int gsel_tss; +#if NNPX > 0 + int msize; +#endif - struct isa_device *idp; #ifndef SMP /* table descriptors - used to load tables by microp */ struct region_descriptor r_gdt, r_idt; #endif int pagesinbase, pagesinext; vm_offset_t target_page; int pa_indx, off; int speculative_mprobe; /* * Prevent lowering of the ipl if we call tsleep() early. */ safepri = cpl; proc0.p_addr = proc0paddr; atdevbase = ISA_HOLE_START + KERNBASE; /* * Initialize the console before we print anything out. */ cninit(); /* * make gdt memory segments, the code segment goes up to end of the * page with etext in it, the data segment goes to the end of * the address space */ /* * XXX text protection is temporarily (?) disabled. The limit was * i386_btop(round_page(etext)) - 1. */ gdt_segs[GCODE_SEL].ssd_limit = i386_btop(0) - 1; gdt_segs[GDATA_SEL].ssd_limit = i386_btop(0) - 1; #ifdef BDE_DEBUGGER #define NGDT1 8 /* avoid overwriting db entries with APM ones */ #else #define NGDT1 (sizeof gdt_segs / sizeof gdt_segs[0]) #endif for (x = 0; x < NGDT1; x++) ssdtosd(&gdt_segs[x], &gdt[x].sd); #ifdef VM86 common_tssd = gdt[GPROC0_SEL].sd; #endif /* VM86 */ #ifdef SMP /* * Spin these up now. init_secondary() grabs them. We could use * #for(x,y,z) / #endfor cpp directives if they existed. */ for (x = 0; x < NCPU; x++) { gdt_segs[NGDT + x] = gdt_segs[GPROC0_SEL]; ssdtosd(&gdt_segs[NGDT + x], &gdt[NGDT + x].sd); } #endif /* make ldt memory segments */ /* * The data segment limit must not cover the user area because we * don't want the user area to be writable in copyout() etc. (page * level protection is lost in kernel mode on 386's). Also, we * don't want the user area to be writable directly (page level * protection of the user area is not available on 486's with * CR0_WP set, because there is no user-read/kernel-write mode). * * XXX - VM_MAXUSER_ADDRESS is an end address, not a max. And it * should be spelled ...MAX_USER... */ #define VM_END_USER_RW_ADDRESS VM_MAXUSER_ADDRESS /* * The code segment limit has to cover the user area until we move * the signal trampoline out of the user area. This is safe because * the code segment cannot be written to directly. */ #define VM_END_USER_R_ADDRESS (VM_END_USER_RW_ADDRESS + UPAGES * PAGE_SIZE) ldt_segs[LUCODE_SEL].ssd_limit = i386_btop(VM_END_USER_R_ADDRESS) - 1; ldt_segs[LUDATA_SEL].ssd_limit = i386_btop(VM_END_USER_RW_ADDRESS) - 1; for (x = 0; x < sizeof ldt_segs / sizeof ldt_segs[0]; x++) ssdtosd(&ldt_segs[x], &ldt[x].sd); /* exceptions */ for (x = 0; x < NIDT; x++) setidt(x, &IDTVEC(rsvd), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(0, &IDTVEC(div), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(1, &IDTVEC(dbg), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(2, &IDTVEC(nmi), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(3, &IDTVEC(bpt), SDT_SYS386TGT, SEL_UPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(4, &IDTVEC(ofl), SDT_SYS386TGT, SEL_UPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(5, &IDTVEC(bnd), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(6, &IDTVEC(ill), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(7, &IDTVEC(dna), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(8, 0, SDT_SYSTASKGT, SEL_KPL, GSEL(GPANIC_SEL, SEL_KPL)); setidt(9, &IDTVEC(fpusegm), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(10, &IDTVEC(tss), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(11, &IDTVEC(missing), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(12, &IDTVEC(stk), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(13, &IDTVEC(prot), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(14, &IDTVEC(page), SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(15, &IDTVEC(rsvd), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(16, &IDTVEC(fpu), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(17, &IDTVEC(align), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(18, &IDTVEC(mchk), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(0x80, &IDTVEC(int0x80_syscall), SDT_SYS386TGT, SEL_UPL, GSEL(GCODE_SEL, SEL_KPL)); #include "isa.h" #if NISA >0 isa_defaultirq(); #endif rand_initialize(); r_gdt.rd_limit = sizeof(gdt) - 1; r_gdt.rd_base = (int) gdt; lgdt(&r_gdt); r_idt.rd_limit = sizeof(idt) - 1; r_idt.rd_base = (int) idt; lidt(&r_idt); _default_ldt = GSEL(GLDT_SEL, SEL_KPL); lldt(_default_ldt); #ifdef USER_LDT currentldt = _default_ldt; #endif #ifdef DDB kdb_init(); if (boothowto & RB_KDB) Debugger("Boot flags requested debugger"); #endif finishidentcpu(); /* Final stage of CPU initialization */ setidt(6, &IDTVEC(ill), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(13, &IDTVEC(prot), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); initializecpu(); /* Initialize CPU registers */ /* make an initial tss so cpu can get interrupt stack on syscall! */ #ifdef VM86 common_tss.tss_esp0 = (int) proc0.p_addr + UPAGES*PAGE_SIZE - 16; #else common_tss.tss_esp0 = (int) proc0.p_addr + UPAGES*PAGE_SIZE; #endif /* VM86 */ common_tss.tss_ss0 = GSEL(GDATA_SEL, SEL_KPL) ; common_tss.tss_ioopt = (sizeof common_tss) << 16; gsel_tss = GSEL(GPROC0_SEL, SEL_KPL); ltr(gsel_tss); #ifdef VM86 private_tss = 0; my_tr = GPROC0_SEL; #endif dblfault_tss.tss_esp = dblfault_tss.tss_esp0 = dblfault_tss.tss_esp1 = dblfault_tss.tss_esp2 = (int) &dblfault_stack[sizeof(dblfault_stack)]; dblfault_tss.tss_ss = dblfault_tss.tss_ss0 = dblfault_tss.tss_ss1 = dblfault_tss.tss_ss2 = GSEL(GDATA_SEL, SEL_KPL); dblfault_tss.tss_cr3 = (int)IdlePTD; dblfault_tss.tss_eip = (int) dblfault_handler; dblfault_tss.tss_eflags = PSL_KERNEL; dblfault_tss.tss_ds = dblfault_tss.tss_es = dblfault_tss.tss_fs = dblfault_tss.tss_gs = GSEL(GDATA_SEL, SEL_KPL); dblfault_tss.tss_cs = GSEL(GCODE_SEL, SEL_KPL); dblfault_tss.tss_ldt = GSEL(GLDT_SEL, SEL_KPL); #ifdef VM86 initial_bioscalls(&biosbasemem, &biosextmem); #else /* Use BIOS values stored in RTC CMOS RAM, since probing * breaks certain 386 AT relics. */ biosbasemem = rtcin(RTC_BASELO)+ (rtcin(RTC_BASEHI)<<8); biosextmem = rtcin(RTC_EXTLO)+ (rtcin(RTC_EXTHI)<<8); #endif /* * If BIOS tells us that it has more than 640k in the basemem, * don't believe it - set it to 640k. */ if (biosbasemem > 640) { printf("Preposterous RTC basemem of %uK, truncating to 640K\n", biosbasemem); biosbasemem = 640; } if (bootinfo.bi_memsizes_valid && bootinfo.bi_basemem > 640) { printf("Preposterous BIOS basemem of %uK, truncating to 640K\n", bootinfo.bi_basemem); bootinfo.bi_basemem = 640; } /* * Warn if the official BIOS interface disagrees with the RTC * interface used above about the amount of base memory or the * amount of extended memory. Prefer the BIOS value for the base * memory. This is necessary for machines that `steal' base * memory for use as BIOS memory, at least if we are going to use * the BIOS for apm. Prefer the RTC value for extended memory. * Eventually the hackish interface shouldn't even be looked at. */ if (bootinfo.bi_memsizes_valid) { if (bootinfo.bi_basemem != biosbasemem) { vm_offset_t pa; printf( "BIOS basemem (%uK) != RTC basemem (%uK), setting to BIOS value\n", bootinfo.bi_basemem, biosbasemem); biosbasemem = bootinfo.bi_basemem; /* * XXX if biosbasemem is now < 640, there is `hole' * between the end of base memory and the start of * ISA memory. The hole may be empty or it may * contain BIOS code or data. Map it read/write so * that the BIOS can write to it. (Memory from 0 to * the physical end of the kernel is mapped read-only * to begin with and then parts of it are remapped. * The parts that aren't remapped form holes that * remain read-only and are unused by the kernel. * The base memory area is below the physical end of * the kernel and right now forms a read-only hole. * The part of it from PAGE_SIZE to * (trunc_page(biosbasemem * 1024) - 1) will be * remapped and used by the kernel later.) * * This code is similar to the code used in * pmap_mapdev, but since no memory needs to be * allocated we simply change the mapping. */ for (pa = trunc_page(biosbasemem * 1024); pa < ISA_HOLE_START; pa += PAGE_SIZE) { unsigned *pte; pte = (unsigned *)vtopte(pa + KERNBASE); *pte = pa | PG_RW | PG_V; } } if (bootinfo.bi_extmem != biosextmem) printf("BIOS extmem (%uK) != RTC extmem (%uK)\n", bootinfo.bi_extmem, biosextmem); } #ifdef SMP /* make hole for AP bootstrap code */ pagesinbase = mp_bootaddress(biosbasemem) / PAGE_SIZE; #else pagesinbase = biosbasemem * 1024 / PAGE_SIZE; #endif pagesinext = biosextmem * 1024 / PAGE_SIZE; /* * Special hack for chipsets that still remap the 384k hole when * there's 16MB of memory - this really confuses people that * are trying to use bus mastering ISA controllers with the * "16MB limit"; they only have 16MB, but the remapping puts * them beyond the limit. */ /* * If extended memory is between 15-16MB (16-17MB phys address range), * chop it to 15MB. */ if ((pagesinext > 3840) && (pagesinext < 4096)) pagesinext = 3840; /* * Maxmem isn't the "maximum memory", it's one larger than the * highest page of the physical address space. It should be * called something like "Maxphyspage". */ Maxmem = pagesinext + 0x100000/PAGE_SIZE; /* * Indicate that we wish to do a speculative search for memory beyond * the end of the reported size if the indicated amount is 64MB (0x4000 * pages) - which is the largest amount that the BIOS/bootblocks can * currently report. If a specific amount of memory is indicated via * the MAXMEM option or the npx0 "msize", then don't do the speculative * memory probe. */ if (Maxmem >= 0x4000) speculative_mprobe = TRUE; else speculative_mprobe = FALSE; #ifdef MAXMEM Maxmem = MAXMEM/4; speculative_mprobe = FALSE; #endif #if NNPX > 0 - idp = find_isadev(isa_devtab_null, &npxdriver, 0); - if (idp != NULL && idp->id_msize != 0) { - Maxmem = idp->id_msize / 4; - speculative_mprobe = FALSE; + if (resource_int_value("npx", 0, "msize", &msize) == 0) { + if (msize != 0) { + Maxmem = msize / 4; + speculative_mprobe = FALSE; + } } #endif #ifdef SMP /* look for the MP hardware - needed for apic addresses */ mp_probe(); #endif /* call pmap initialization to make new kernel address space */ pmap_bootstrap (first, 0); /* * Size up each available chunk of physical memory. */ /* * We currently don't bother testing base memory. * XXX ...but we probably should. */ pa_indx = 0; if (pagesinbase > 1) { phys_avail[pa_indx++] = PAGE_SIZE; /* skip first page of memory */ phys_avail[pa_indx] = ptoa(pagesinbase);/* memory up to the ISA hole */ physmem = pagesinbase - 1; } else { /* point at first chunk end */ pa_indx++; } for (target_page = avail_start; target_page < ptoa(Maxmem); target_page += PAGE_SIZE) { int tmp, page_bad; page_bad = FALSE; /* * map page into kernel: valid, read/write, non-cacheable */ *(int *)CMAP1 = PG_V | PG_RW | PG_N | target_page; invltlb(); tmp = *(int *)CADDR1; /* * Test for alternating 1's and 0's */ *(volatile int *)CADDR1 = 0xaaaaaaaa; if (*(volatile int *)CADDR1 != 0xaaaaaaaa) { page_bad = TRUE; } /* * Test for alternating 0's and 1's */ *(volatile int *)CADDR1 = 0x55555555; if (*(volatile int *)CADDR1 != 0x55555555) { page_bad = TRUE; } /* * Test for all 1's */ *(volatile int *)CADDR1 = 0xffffffff; if (*(volatile int *)CADDR1 != 0xffffffff) { page_bad = TRUE; } /* * Test for all 0's */ *(volatile int *)CADDR1 = 0x0; if (*(volatile int *)CADDR1 != 0x0) { /* * test of page failed */ page_bad = TRUE; } /* * Restore original value. */ *(int *)CADDR1 = tmp; /* * Adjust array of valid/good pages. */ if (page_bad == FALSE) { /* * If this good page is a continuation of the * previous set of good pages, then just increase * the end pointer. Otherwise start a new chunk. * Note that "end" points one higher than end, * making the range >= start and < end. * If we're also doing a speculative memory * test and we at or past the end, bump up Maxmem * so that we keep going. The first bad page * will terminate the loop. */ if (phys_avail[pa_indx] == target_page) { phys_avail[pa_indx] += PAGE_SIZE; if (speculative_mprobe == TRUE && phys_avail[pa_indx] >= (64*1024*1024)) Maxmem++; } else { pa_indx++; if (pa_indx == PHYS_AVAIL_ARRAY_END) { printf("Too many holes in the physical address space, giving up\n"); pa_indx--; break; } phys_avail[pa_indx++] = target_page; /* start */ phys_avail[pa_indx] = target_page + PAGE_SIZE; /* end */ } physmem++; } } *(int *)CMAP1 = 0; invltlb(); /* * XXX * The last chunk must contain at least one page plus the message * buffer to avoid complicating other code (message buffer address * calculation, etc.). */ while (phys_avail[pa_indx - 1] + PAGE_SIZE + round_page(MSGBUF_SIZE) >= phys_avail[pa_indx]) { physmem -= atop(phys_avail[pa_indx] - phys_avail[pa_indx - 1]); phys_avail[pa_indx--] = 0; phys_avail[pa_indx--] = 0; } Maxmem = atop(phys_avail[pa_indx]); /* Trim off space for the message buffer. */ phys_avail[pa_indx] -= round_page(MSGBUF_SIZE); avail_end = phys_avail[pa_indx]; /* now running on new page tables, configured,and u/iom is accessible */ /* Map the message buffer. */ for (off = 0; off < round_page(MSGBUF_SIZE); off += PAGE_SIZE) pmap_enter(kernel_pmap, (vm_offset_t)msgbufp + off, avail_end + off, VM_PROT_ALL, TRUE); msgbufinit(msgbufp, MSGBUF_SIZE); /* make a call gate to reenter kernel with */ gdp = &ldt[LSYS5CALLS_SEL].gd; x = (int) &IDTVEC(syscall); gdp->gd_looffset = x++; gdp->gd_selector = GSEL(GCODE_SEL,SEL_KPL); gdp->gd_stkcpy = 1; gdp->gd_type = SDT_SYS386CGT; gdp->gd_dpl = SEL_UPL; gdp->gd_p = 1; gdp->gd_hioffset = ((int) &IDTVEC(syscall)) >>16; /* XXX does this work? */ ldt[LBSDICALLS_SEL] = ldt[LSYS5CALLS_SEL]; ldt[LSOL26CALLS_SEL] = ldt[LSYS5CALLS_SEL]; /* transfer to user mode */ _ucodesel = LSEL(LUCODE_SEL, SEL_UPL); _udatasel = LSEL(LUDATA_SEL, SEL_UPL); /* setup proc 0's pcb */ proc0.p_addr->u_pcb.pcb_flags = 0; proc0.p_addr->u_pcb.pcb_cr3 = (int)IdlePTD; #ifdef SMP proc0.p_addr->u_pcb.pcb_mpnest = 1; #endif #ifdef VM86 proc0.p_addr->u_pcb.pcb_ext = 0; #endif /* Sigh, relocate physical addresses left from bootstrap */ if (bootinfo.bi_modulep) { preload_metadata = (caddr_t)bootinfo.bi_modulep + KERNBASE; preload_bootstrap_relocate(KERNBASE); } if (bootinfo.bi_envp) kern_envp = (caddr_t)bootinfo.bi_envp + KERNBASE; } #if defined(I586_CPU) && !defined(NO_F00F_HACK) static void f00f_hack(void *unused); SYSINIT(f00f_hack, SI_SUB_INTRINSIC, SI_ORDER_FIRST, f00f_hack, NULL); static void f00f_hack(void *unused) { #ifndef SMP struct region_descriptor r_idt; #endif vm_offset_t tmp; if (!has_f00f_bug) return; printf("Intel Pentium detected, installing workaround for F00F bug\n"); r_idt.rd_limit = sizeof(idt) - 1; tmp = kmem_alloc(kernel_map, PAGE_SIZE * 2); if (tmp == 0) panic("kmem_alloc returned 0"); if (((unsigned int)tmp & (PAGE_SIZE-1)) != 0) panic("kmem_alloc returned non-page-aligned memory"); /* Put the first seven entries in the lower page */ t_idt = (struct gate_descriptor*)(tmp + PAGE_SIZE - (7*8)); bcopy(idt, t_idt, sizeof(idt)); r_idt.rd_base = (int)t_idt; lidt(&r_idt); if (vm_map_protect(kernel_map, tmp, tmp + PAGE_SIZE, VM_PROT_READ, FALSE) != KERN_SUCCESS) panic("vm_map_protect failed"); return; } #endif /* defined(I586_CPU) && !NO_F00F_HACK */ int ptrace_set_pc(p, addr) struct proc *p; unsigned long addr; { p->p_md.md_regs->tf_eip = addr; return (0); } int ptrace_single_step(p) struct proc *p; { p->p_md.md_regs->tf_eflags |= PSL_T; return (0); } int ptrace_read_u_check(p, addr, len) struct proc *p; vm_offset_t addr; size_t len; { vm_offset_t gap; if ((vm_offset_t) (addr + len) < addr) return EPERM; if ((vm_offset_t) (addr + len) <= sizeof(struct user)) return 0; gap = (char *) p->p_md.md_regs - (char *) p->p_addr; if ((vm_offset_t) addr < gap) return EPERM; if ((vm_offset_t) (addr + len) <= (vm_offset_t) (gap + sizeof(struct trapframe))) return 0; return EPERM; } int ptrace_write_u(p, off, data) struct proc *p; vm_offset_t off; long data; { struct trapframe frame_copy; vm_offset_t min; struct trapframe *tp; /* * Privileged kernel state is scattered all over the user area. * Only allow write access to parts of regs and to fpregs. */ min = (char *)p->p_md.md_regs - (char *)p->p_addr; if (off >= min && off <= min + sizeof(struct trapframe) - sizeof(int)) { tp = p->p_md.md_regs; frame_copy = *tp; *(int *)((char *)&frame_copy + (off - min)) = data; if (!EFLAGS_SECURE(frame_copy.tf_eflags, tp->tf_eflags) || !CS_SECURE(frame_copy.tf_cs)) return (EINVAL); *(int*)((char *)p->p_addr + off) = data; return (0); } min = offsetof(struct user, u_pcb) + offsetof(struct pcb, pcb_savefpu); if (off >= min && off <= min + sizeof(struct save87) - sizeof(int)) { *(int*)((char *)p->p_addr + off) = data; return (0); } return (EFAULT); } int fill_regs(p, regs) struct proc *p; struct reg *regs; { struct pcb *pcb; struct trapframe *tp; tp = p->p_md.md_regs; regs->r_es = tp->tf_es; regs->r_ds = tp->tf_ds; regs->r_edi = tp->tf_edi; regs->r_esi = tp->tf_esi; regs->r_ebp = tp->tf_ebp; regs->r_ebx = tp->tf_ebx; regs->r_edx = tp->tf_edx; regs->r_ecx = tp->tf_ecx; regs->r_eax = tp->tf_eax; regs->r_eip = tp->tf_eip; regs->r_cs = tp->tf_cs; regs->r_eflags = tp->tf_eflags; regs->r_esp = tp->tf_esp; regs->r_ss = tp->tf_ss; pcb = &p->p_addr->u_pcb; regs->r_fs = pcb->pcb_fs; regs->r_gs = pcb->pcb_gs; return (0); } int set_regs(p, regs) struct proc *p; struct reg *regs; { struct pcb *pcb; struct trapframe *tp; tp = p->p_md.md_regs; if (!EFLAGS_SECURE(regs->r_eflags, tp->tf_eflags) || !CS_SECURE(regs->r_cs)) return (EINVAL); tp->tf_es = regs->r_es; tp->tf_ds = regs->r_ds; tp->tf_edi = regs->r_edi; tp->tf_esi = regs->r_esi; tp->tf_ebp = regs->r_ebp; tp->tf_ebx = regs->r_ebx; tp->tf_edx = regs->r_edx; tp->tf_ecx = regs->r_ecx; tp->tf_eax = regs->r_eax; tp->tf_eip = regs->r_eip; tp->tf_cs = regs->r_cs; tp->tf_eflags = regs->r_eflags; tp->tf_esp = regs->r_esp; tp->tf_ss = regs->r_ss; pcb = &p->p_addr->u_pcb; pcb->pcb_fs = regs->r_fs; pcb->pcb_gs = regs->r_gs; return (0); } int fill_fpregs(p, fpregs) struct proc *p; struct fpreg *fpregs; { bcopy(&p->p_addr->u_pcb.pcb_savefpu, fpregs, sizeof *fpregs); return (0); } int set_fpregs(p, fpregs) struct proc *p; struct fpreg *fpregs; { bcopy(fpregs, &p->p_addr->u_pcb.pcb_savefpu, sizeof *fpregs); return (0); } #ifndef DDB void Debugger(const char *msg) { printf("Debugger(\"%s\") called.\n", msg); } #endif /* no DDB */ #include /* * Determine the size of the transfer, and make sure it is * within the boundaries of the partition. Adjust transfer * if needed, and signal errors or early completion. */ int bounds_check_with_label(struct buf *bp, struct disklabel *lp, int wlabel) { struct partition *p = lp->d_partitions + dkpart(bp->b_dev); int labelsect = lp->d_partitions[0].p_offset; int maxsz = p->p_size, sz = (bp->b_bcount + DEV_BSIZE - 1) >> DEV_BSHIFT; /* overwriting disk label ? */ /* XXX should also protect bootstrap in first 8K */ if (bp->b_blkno + p->p_offset <= LABELSECTOR + labelsect && #if LABELSECTOR != 0 bp->b_blkno + p->p_offset + sz > LABELSECTOR + labelsect && #endif (bp->b_flags & B_READ) == 0 && wlabel == 0) { bp->b_error = EROFS; goto bad; } #if defined(DOSBBSECTOR) && defined(notyet) /* overwriting master boot record? */ if (bp->b_blkno + p->p_offset <= DOSBBSECTOR && (bp->b_flags & B_READ) == 0 && wlabel == 0) { bp->b_error = EROFS; goto bad; } #endif /* beyond partition? */ if (bp->b_blkno < 0 || bp->b_blkno + sz > maxsz) { /* if exactly at end of disk, return an EOF */ if (bp->b_blkno == maxsz) { bp->b_resid = bp->b_bcount; return(0); } /* or truncate if part of it fits */ sz = maxsz - bp->b_blkno; if (sz <= 0) { bp->b_error = EINVAL; goto bad; } bp->b_bcount = sz << DEV_BSHIFT; } bp->b_pblkno = bp->b_blkno + p->p_offset; return(1); bad: bp->b_flags |= B_ERROR; return(-1); } #ifdef DDB /* * Provide inb() and outb() as functions. They are normally only * available as macros calling inlined functions, thus cannot be * called inside DDB. * * The actual code is stolen from , and de-inlined. */ #undef inb #undef outb /* silence compiler warnings */ u_char inb(u_int); void outb(u_int, u_char); u_char inb(u_int port) { u_char data; /* * We use %%dx and not %1 here because i/o is done at %dx and not at * %edx, while gcc generates inferior code (movw instead of movl) * if we tell it to load (u_short) port. */ __asm __volatile("inb %%dx,%0" : "=a" (data) : "d" (port)); return (data); } void outb(u_int port, u_char data) { u_char al; /* * Use an unnecessary assignment to help gcc's register allocator. * This make a large difference for gcc-1.40 and a tiny difference * for gcc-2.6.0. For gcc-1.40, al had to be ``asm("ax")'' for * best results. gcc-2.6.0 can't handle this. */ al = data; __asm __volatile("outb %0,%%dx" : : "a" (al), "d" (port)); } #endif /* DDB */ Index: head/sys/amd64/amd64/nexus.c =================================================================== --- head/sys/amd64/amd64/nexus.c (nonexistent) +++ head/sys/amd64/amd64/nexus.c (revision 45720) @@ -0,0 +1,409 @@ +/* + * Copyright 1998 Massachusetts Institute of Technology + * + * Permission to use, copy, modify, and distribute this software and + * its documentation for any purpose and without fee is hereby + * granted, provided that both the above copyright notice and this + * permission notice appear in all copies, that both the above + * copyright notice and this permission notice appear in all + * supporting documentation, and that the name of M.I.T. not be used + * in advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. M.I.T. makes + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS + * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT + * SHALL M.I.T. 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. + * + * $Id$ + */ + +/* + * This code implements a `root nexus' for Intel Architecture + * machines. The function of the root nexus is to serve as an + * attachment point for both processors and buses, and to manage + * resources which are common to all of them. In particular, + * this code implements the core resource managers for interrupt + * requests, DMA requests (which rightfully should be a part of the + * ISA code but it's easier to do it here for now), I/O port addresses, + * and I/O memory address space. + */ + +#include "opt_smp.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#ifdef APIC_IO +#include +#include +#endif + +#include +#include +#include + +#include + +#include "eisa.h" +#include "isa.h" +#include "pci.h" +#include "npx.h" +#include "apm.h" + +static struct rman irq_rman, drq_rman, port_rman, mem_rman; + +static int nexus_probe(device_t); +static void nexus_print_child(device_t, device_t); +static struct resource *nexus_alloc_resource(device_t, device_t, int, int *, + u_long, u_long, u_long, u_int); +static int nexus_activate_resource(device_t, device_t, int, int, + struct resource *); +static int nexus_deactivate_resource(device_t, device_t, int, int, + struct resource *); +static int nexus_release_resource(device_t, device_t, int, int, + struct resource *); +static int nexus_setup_intr(device_t, device_t, struct resource *, + void (*)(void *), void *, void **); +static int nexus_teardown_intr(device_t, device_t, struct resource *, + void *); + +static device_method_t nexus_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, nexus_probe), + DEVMETHOD(device_attach, bus_generic_attach), + DEVMETHOD(device_detach, bus_generic_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + + /* Bus interface */ + DEVMETHOD(bus_print_child, nexus_print_child), + DEVMETHOD(bus_read_ivar, bus_generic_read_ivar), + DEVMETHOD(bus_write_ivar, bus_generic_write_ivar), + DEVMETHOD(bus_alloc_resource, nexus_alloc_resource), + DEVMETHOD(bus_release_resource, nexus_release_resource), + DEVMETHOD(bus_activate_resource, nexus_activate_resource), + DEVMETHOD(bus_deactivate_resource, nexus_deactivate_resource), + DEVMETHOD(bus_setup_intr, nexus_setup_intr), + DEVMETHOD(bus_teardown_intr, nexus_teardown_intr), + + { 0, 0 } +}; + +static driver_t nexus_driver = { + "nexus", + nexus_methods, + DRIVER_TYPE_MISC, + 1, /* no softc */ +}; +static devclass_t nexus_devclass; + +DRIVER_MODULE(nexus, root, nexus_driver, nexus_devclass, 0, 0); + +#ifdef APIC_IO +#define LASTIRQ (NINTR - 1) +#else +#define LASTIRQ 15 +#endif + +static int +nexus_probe(device_t dev) +{ + device_t child; + + device_quiet(dev); /* suppress attach message for neatness */ + + irq_rman.rm_start = 0; + irq_rman.rm_end = LASTIRQ; + irq_rman.rm_type = RMAN_ARRAY; + irq_rman.rm_descr = "Interrupt request lines"; + if (rman_init(&irq_rman) + || rman_manage_region(&irq_rman, 0, 1) + || rman_manage_region(&irq_rman, 3, LASTIRQ)) + panic("nexus_probe irq_rman"); + + drq_rman.rm_start = 0; + drq_rman.rm_end = 7; + drq_rman.rm_type = RMAN_ARRAY; + drq_rman.rm_descr = "DMA request lines"; + /* XXX drq 0 not available on some machines */ + if (rman_init(&drq_rman) + || rman_manage_region(&drq_rman, 0, 7)) + panic("nexus_probe drq_rman"); + + port_rman.rm_start = 0; + port_rman.rm_end = 0xffff; + port_rman.rm_type = RMAN_ARRAY; + port_rman.rm_descr = "I/O ports"; + if (rman_init(&port_rman) + || rman_manage_region(&port_rman, 0, 0xffff)) + panic("nexus_probe port_rman"); + + mem_rman.rm_start = 0; + mem_rman.rm_end = ~0u; + mem_rman.rm_type = RMAN_ARRAY; + mem_rman.rm_descr = "I/O memory addresses"; + if (rman_init(&mem_rman) + || rman_manage_region(&mem_rman, 0, ~0)) + panic("nexus_probe mem_rman"); + +#if NNPX > 0 + child = device_add_child(dev, "npx", 0, 0); + if (child == 0) + panic("nexus_probe npx"); +#endif /* NNPX > 0 */ +#if NAPM > 0 + child = device_add_child(dev, "apm", 0, 0); + if (child == 0) + panic("nexus_probe apm"); +#endif /* NAPM > 0 */ +#if NPCI > 0 + /* Add a PCI bridge if pci bus is present */ + if (pci_cfgopen() != 0) { + child = device_add_child(dev, "pcib", 0, 0); + if (child == 0) + panic("nexus_probe pcib"); + } +#endif +#if 0 && NEISA > 0 + child = device_add_child(dev, "eisa", 0, 0); + if (child == 0) + panic("nexus_probe eisa"); +#endif +#if NISA > 0 + /* Add an ISA bus directly if pci bus is not present */ + if (pci_cfgopen() == 0) { + child = device_add_child(dev, "isa", 0, 0); + if (child == 0) + panic("nexus_probe isa"); + } +#endif + return 0; +} + +static void +nexus_print_child(device_t bus, device_t child) +{ + printf(" on motherboard"); +} + +/* + * Allocate a resource on behalf of child. NB: child is usually going to be a + * child of one of our descendants, not a direct child of nexus0. + * (Exceptions include npx.) + */ +static struct resource * +nexus_alloc_resource(device_t bus, device_t child, int type, int *rid, + u_long start, u_long end, u_long count, u_int flags) +{ + struct resource *rv; + struct rman *rm; + + switch (type) { + case SYS_RES_IRQ: + rm = &irq_rman; + break; + + case SYS_RES_DRQ: + rm = &drq_rman; + break; + + case SYS_RES_IOPORT: + rm = &port_rman; + break; + + case SYS_RES_MEMORY: + rm = &mem_rman; + break; + + default: + return 0; + } + + rv = rman_reserve_resource(rm, start, end, count, flags, child); + if (rv == 0) + return 0; + + if (type == SYS_RES_MEMORY) { + caddr_t vaddr = 0; + + if (rv->r_end < 1024 * 1024 * 1024) { + /* + * The first 1Mb is mapped at KERNBASE. + */ + vaddr = (caddr_t)((uintptr_t)KERNBASE + rv->r_start); + } else { + u_int32_t paddr; + u_int32_t psize; + u_int32_t poffs; + + paddr = rv->r_start; + psize = rv->r_end - rv->r_start; + + poffs = paddr - trunc_page(paddr); + vaddr = (caddr_t) pmap_mapdev(paddr-poffs, psize+poffs) + poffs; + } + rman_set_virtual(rv, vaddr); + rman_set_bustag(rv, I386_BUS_SPACE_MEM); + rman_set_bushandle(rv, (bus_space_handle_t) vaddr); + } else if (type == SYS_RES_IOPORT) { + rman_set_bustag(rv, I386_BUS_SPACE_IO); + rman_set_bushandle(rv, rv->r_start); + } + return rv; +} + +static int +nexus_activate_resource(device_t bus, device_t child, int type, int rid, + struct resource *r) +{ + return (rman_activate_resource(r)); +} + +static int +nexus_deactivate_resource(device_t bus, device_t child, int type, int rid, + struct resource *r) +{ + return (rman_deactivate_resource(r)); +} + +static int +nexus_release_resource(device_t bus, device_t child, int type, int rid, + struct resource *r) +{ + return (rman_release_resource(r)); +} + +/* + * Currently this uses the really grody interface from kern/kern_intr.c + * (which really doesn't belong in kern/anything.c). Eventually, all of + * the code in kern_intr.c and machdep_intr.c should get moved here, since + * this is going to be the official interface. + */ +static int +nexus_setup_intr(device_t bus, device_t child, struct resource *irq, + void (*ihand)(void *), void *arg, void **cookiep) +{ + intrmask_t *mask; + driver_t *driver; + int error, icflags; + + if (child) + device_printf(child, "interrupting at irq %d\n", + (int)irq->r_start); + + *cookiep = 0; + if (irq->r_flags & RF_SHAREABLE) + icflags = 0; + else + icflags = INTR_EXCL; + + driver = device_get_driver(child); + switch (driver->type) { + case DRIVER_TYPE_TTY: + mask = &tty_imask; + break; + case (DRIVER_TYPE_TTY | DRIVER_TYPE_FAST): + mask = &tty_imask; + icflags |= INTR_FAST; + break; + case DRIVER_TYPE_BIO: + mask = &bio_imask; + break; + case DRIVER_TYPE_NET: + mask = &net_imask; + break; + case DRIVER_TYPE_CAM: + mask = &cam_imask; + break; + case DRIVER_TYPE_MISC: + mask = 0; + break; + default: + panic("still using grody create_intr interface"); + } + + /* + * We depend here on rman_activate_resource() being idempotent. + */ + error = rman_activate_resource(irq); + if (error) + return (error); + + *cookiep = intr_create((void *)(intptr_t)-1, irq->r_start, ihand, arg, + mask, icflags); + if (*cookiep) + error = intr_connect(*cookiep); + else + error = EINVAL; /* XXX ??? */ + + return (error); +} + +static int +nexus_teardown_intr(device_t dev, device_t child, struct resource *r, void *ih) +{ + return (intr_destroy(ih)); +} + +static devclass_t pcib_devclass; + +static int +nexus_pcib_probe(device_t dev) +{ + device_set_desc(dev, "PCI host bus adapter"); + + device_add_child(dev, "pci", 0, 0); + + return 0; +} + +static device_method_t nexus_pcib_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, nexus_pcib_probe), + DEVMETHOD(device_attach, bus_generic_attach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + + /* Bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), + DEVMETHOD(bus_release_resource, bus_generic_release_resource), + DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), + DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), + DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), + DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), + + { 0, 0 } +}; + +static driver_t nexus_pcib_driver = { + "pcib", + nexus_pcib_methods, + DRIVER_TYPE_MISC, + 1, +}; + +DRIVER_MODULE(pcib, nexus, nexus_pcib_driver, pcib_devclass, 0, 0); Property changes on: head/sys/amd64/amd64/nexus.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: head/sys/amd64/conf/GENERIC =================================================================== --- head/sys/amd64/conf/GENERIC (revision 45719) +++ head/sys/amd64/conf/GENERIC (revision 45720) @@ -1,225 +1,225 @@ # # GENERIC -- Generic machine with WD/AHx/NCR/BTx family disks # # For more information read the handbook part System Administration -> # Configuring the FreeBSD Kernel -> The Configuration File. # The handbook is available in /usr/share/doc/handbook or online as # latest version from the FreeBSD World Wide Web server # # # An exhaustive list of options and more detailed explanations of the # device lines is present in the ./LINT configuration file. If you are # in doubt as to the purpose or necessity of a line, check first in LINT. # -# $Id: GENERIC,v 1.160 1999/04/16 16:17:05 n_hibma Exp $ +# $Id: GENERIC,v 1.161 1999/04/16 18:27:18 jkh Exp $ machine "i386" cpu "I386_CPU" cpu "I486_CPU" cpu "I586_CPU" cpu "I686_CPU" ident GENERIC maxusers 32 #makeoptions DEBUG="-g" #Build kernel with gdb(1) debug symbols options MATH_EMULATE #Support for x87 emulation options INET #InterNETworking options FFS #Berkeley Fast Filesystem options FFS_ROOT #FFS usable as root device [keep this!] options MFS #Memory Filesystem options MFS_ROOT #MFS usable as root device, "MFS" req'ed options NFS #Network Filesystem options NFS_ROOT #NFS usable as root device, "NFS" req'ed options MSDOSFS #MSDOS Filesystem options "CD9660" #ISO 9660 Filesystem options "CD9660_ROOT" #CD-ROM usable as root. "CD9660" req'ed options PROCFS #Process filesystem options "COMPAT_43" #Compatible with BSD 4.3 [KEEP THIS!] options SCSI_DELAY=15000 #Be pessimistic about Joe SCSI device options UCONSOLE #Allow users to grab the console options FAILSAFE #Be conservative options USERCONFIG #boot -c editor options VISUAL_USERCONFIG #visual boot -c editor config kernel root on wd0 # To make an SMP kernel, the next two are needed #options SMP # Symmetric MultiProcessor Kernel #options APIC_IO # Symmetric (APIC) I/O # Optionally these may need tweaked, (defaults shown): #options NCPU=2 # number of CPUs #options NBUS=4 # number of busses #options NAPIC=1 # number of IO APICs #options NINTR=24 # number of INTs -controller isa0 -controller pnp0 # PnP support for ISA -controller eisa0 -controller pci0 +controller isa0 at nexus? +#controller pnp0 # PnP support for ISA +#controller eisa0 +controller pci0 at nexus? controller fdc0 at isa? port "IO_FD1" bio irq 6 drq 2 disk fd0 at fdc0 drive 0 disk fd1 at fdc0 drive 1 controller wdc0 at isa? port "IO_WD1" bio irq 14 disk wd0 at wdc0 drive 0 disk wd1 at wdc0 drive 1 controller wdc1 at isa? port "IO_WD2" bio irq 15 disk wd2 at wdc1 drive 0 disk wd3 at wdc1 drive 1 device wcd0 #IDE CD-ROM device wfd0 #IDE Floppy (e.g. LS-120) # A single entry for any of these controllers (ncr, ahb, ahc) is # sufficient for any number of installed devices. controller ncr0 controller ahb0 controller ahc0 controller isp0 # This controller offers a number of configuration options, too many to # document here - see the LINT file in this directory and look up the # dpt0 entry there for much fuller documentation on this. controller dpt0 controller adv0 at isa? port ? cam irq ? controller adw0 controller bt0 at isa? port ? cam irq ? controller aha0 at isa? port ? cam irq ? controller scbus0 device da0 device sa0 device pass0 device cd0 #Only need one of these, the code dynamically grows device wt0 at isa? port 0x300 bio irq 5 drq 1 device mcd0 at isa? port 0x300 bio irq 10 controller matcd0 at isa? port 0x230 bio device scd0 at isa? port 0x230 bio # atkbdc0 controlls both the keyboard and the PS/2 mouse -controller atkbdc0 at isa? port IO_KBD tty -device atkbd0 at isa? tty irq 1 -device psm0 at isa? tty irq 12 +controller atkbdc0 at isa? port IO_KBD +device atkbd0 at atkbdc? tty irq 1 +device psm0 at atkbdc? tty irq 12 device vga0 at isa? port ? conflicts # splash screen/screen saver pseudo-device splash # syscons is the default console driver, resembling an SCO console device sc0 at isa? tty # Enable this and PCVT_FREEBSD for pcvt vt220 compatible console driver #device vt0 at isa? tty #options XSERVER # support for X server #options FAT_CURSOR # start with block cursor # If you have a ThinkPAD, uncomment this along with the rest of the PCVT lines #options PCVT_SCANSET=2 # IBM keyboards are non-std -device npx0 at isa? port IO_NPX irq 13 +device npx0 at nexus? port IO_NPX irq 13 # # Laptop support (see LINT for more options) # -device apm0 at isa? disable flags 0x31 # Advanced Power Management +device apm0 at nexus? disable flags 0x31 # Advanced Power Management # PCCARD (PCMCIA) support #controller card0 #device pcic0 at card? #device pcic1 at card? device sio0 at isa? port "IO_COM1" flags 0x10 tty irq 4 device sio1 at isa? port "IO_COM2" tty irq 3 device sio2 at isa? disable port "IO_COM3" tty irq 5 device sio3 at isa? disable port "IO_COM4" tty irq 9 # Parallel port device ppc0 at isa? port? tty irq 7 controller ppbus0 device lpt0 at ppbus? device plip0 at ppbus? device ppi0 at ppbus? #controller vpo0 at ppbus? # # The following Ethernet NICs are all PCI devices. # device ax0 # ASIX AX88140A device de0 # DEC/Intel DC21x4x (``Tulip'') device fxp0 # Intel EtherExpress PRO/100B (82557, 82558) device mx0 # Macronix 98713/98715/98725 (``PMAC'') device pn0 # Lite-On 82c168/82c169 (``PNIC'') device rl0 # RealTek 8129/8139 device tl0 # Texas Instruments ThunderLAN device tx0 # SMC 9432TX (83c170 ``EPIC'') device vr0 # VIA Rhine, Rhine II device vx0 # 3Com 3c590, 3c595 (``Vortex'') device wb0 # Winbond W89C840F device xl0 # 3Com 3c90x (``Boomerang'', ``Cyclone'') # Order is important here due to intrusive probes, do *not* alphabetize # this list of network interfaces until the probes have been fixed. # Right now it appears that the ie0 must be probed before ep0. See # revision 1.20 of this file. device ed0 at isa? port 0x280 net irq 10 iomem 0xd8000 device ie0 at isa? port 0x300 net irq 10 iomem 0xd0000 device ep0 at isa? port 0x300 net irq 10 device ex0 at isa? port? net irq? device fe0 at isa? port 0x300 net irq ? device le0 at isa? port 0x300 net irq 5 iomem 0xd0000 device lnc0 at isa? port 0x280 net irq 10 drq 0 -device ze0 at isa? port 0x300 net irq 10 iomem 0xd8000 -device zp0 at isa? port 0x300 net irq 10 iomem 0xd8000 +#device ze0 at isa? port 0x300 net irq 10 iomem 0xd8000 +#device zp0 at isa? port 0x300 net irq 10 iomem 0xd8000 device cs0 at isa? port 0x300 net irq ? pseudo-device loop pseudo-device ether pseudo-device sl 1 pseudo-device ppp 1 pseudo-device tun 1 pseudo-device pty 16 pseudo-device gzip # Exec gzipped a.out's # KTRACE enables the system-call tracing facility ktrace(2). # This adds 4 KB bloat to your kernel, and slightly increases # the costs of each syscall. options KTRACE #kernel tracing # This provides support for System V shared memory and message queues. # options SYSVSHM options SYSVMSG options SYSVSEM # The `bpfilter' pseudo-device enables the Berkeley Packet Filter. Be # aware of the legal and administrative consequences of enabling this # option. The number of devices determines the maximum number of # simultaneous BPF clients programs runnable. #pseudo-device bpfilter 4 #Berkeley packet filter # USB support #controller uhci0 #controller ohci0 #controller usb0 # # for the moment we have to specify the priorities of the device # drivers explicitly by the ordering in the list below. This will # be changed in the future. # #device ums0 #device ukbd0 #device ulpt0 #device uhid0 #device ugen0 Index: head/sys/amd64/isa/intr_machdep.c =================================================================== --- head/sys/amd64/isa/intr_machdep.c (revision 45719) +++ head/sys/amd64/isa/intr_machdep.c (revision 45720) @@ -1,516 +1,518 @@ /*- * Copyright (c) 1991 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * William Jolitz. * * 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: @(#)isa.c 7.2 (Berkeley) 5/13/91 - * $Id: intr_machdep.c,v 1.16 1999/01/08 19:17:48 bde Exp $ + * $Id: intr_machdep.c,v 1.17 1999/04/14 14:26:36 bde Exp $ */ #include "opt_auto_eoi.h" #include #ifndef SMP #include #endif #include #include #include #include #include #if defined(APIC_IO) #include #include /** FAST_HI */ #endif /* APIC_IO */ #include #ifdef PC98 #include #include #include #else #include #endif #include #include #include #ifdef APIC_IO #include #endif /* XXX should be in suitable include files */ #ifdef PC98 #define ICU_IMR_OFFSET 2 /* IO_ICU{1,2} + 2 */ #define ICU_SLAVEID 7 #else #define ICU_IMR_OFFSET 1 /* IO_ICU{1,2} + 1 */ #define ICU_SLAVEID 2 #endif #ifdef APIC_IO /* * This is to accommodate "mixed-mode" programming for * motherboards that don't connect the 8254 to the IO APIC. */ #define AUTO_EOI_1 1 #endif #define NR_INTRNAMES (1 + ICU_LEN + 2 * ICU_LEN) u_long *intr_countp[ICU_LEN]; inthand2_t *intr_handler[ICU_LEN]; u_int intr_mask[ICU_LEN]; static u_int* intr_mptr[ICU_LEN]; void *intr_unit[ICU_LEN]; static inthand_t *fastintr[ICU_LEN] = { &IDTVEC(fastintr0), &IDTVEC(fastintr1), &IDTVEC(fastintr2), &IDTVEC(fastintr3), &IDTVEC(fastintr4), &IDTVEC(fastintr5), &IDTVEC(fastintr6), &IDTVEC(fastintr7), &IDTVEC(fastintr8), &IDTVEC(fastintr9), &IDTVEC(fastintr10), &IDTVEC(fastintr11), &IDTVEC(fastintr12), &IDTVEC(fastintr13), &IDTVEC(fastintr14), &IDTVEC(fastintr15) #if defined(APIC_IO) , &IDTVEC(fastintr16), &IDTVEC(fastintr17), &IDTVEC(fastintr18), &IDTVEC(fastintr19), &IDTVEC(fastintr20), &IDTVEC(fastintr21), &IDTVEC(fastintr22), &IDTVEC(fastintr23) #endif /* APIC_IO */ }; static inthand_t *slowintr[ICU_LEN] = { &IDTVEC(intr0), &IDTVEC(intr1), &IDTVEC(intr2), &IDTVEC(intr3), &IDTVEC(intr4), &IDTVEC(intr5), &IDTVEC(intr6), &IDTVEC(intr7), &IDTVEC(intr8), &IDTVEC(intr9), &IDTVEC(intr10), &IDTVEC(intr11), &IDTVEC(intr12), &IDTVEC(intr13), &IDTVEC(intr14), &IDTVEC(intr15) #if defined(APIC_IO) , &IDTVEC(intr16), &IDTVEC(intr17), &IDTVEC(intr18), &IDTVEC(intr19), &IDTVEC(intr20), &IDTVEC(intr21), &IDTVEC(intr22), &IDTVEC(intr23) #endif /* APIC_IO */ }; static inthand2_t isa_strayintr; #ifdef PC98 #define NMI_PARITY 0x04 #define NMI_EPARITY 0x02 #else #define NMI_PARITY (1 << 7) #define NMI_IOCHAN (1 << 6) #define ENMI_WATCHDOG (1 << 7) #define ENMI_BUSTIMER (1 << 6) #define ENMI_IOSTATUS (1 << 5) #endif /* * Handle a NMI, possibly a machine check. * return true to panic system, false to ignore. */ int isa_nmi(cd) int cd; { #ifdef PC98 int port = inb(0x33); if (epson_machine_id == 0x20) epson_outb(0xc16, epson_inb(0xc16) | 0x1); if (port & NMI_PARITY) { panic("BASE RAM parity error, likely hardware failure."); } else if (port & NMI_EPARITY) { panic("EXTENDED RAM parity error, likely hardware failure."); } else { printf("\nNMI Resume ??\n"); return(0); } #else /* IBM-PC */ int isa_port = inb(0x61); int eisa_port = inb(0x461); if (isa_port & NMI_PARITY) panic("RAM parity error, likely hardware failure."); if (isa_port & NMI_IOCHAN) panic("I/O channel check, likely hardware failure."); /* * On a real EISA machine, this will never happen. However it can * happen on ISA machines which implement XT style floating point * error handling (very rare). Save them from a meaningless panic. */ if (eisa_port == 0xff) return(0); if (eisa_port & ENMI_WATCHDOG) panic("EISA watchdog timer expired, likely hardware failure."); if (eisa_port & ENMI_BUSTIMER) panic("EISA bus timeout, likely hardware failure."); if (eisa_port & ENMI_IOSTATUS) panic("EISA I/O port status error."); printf("\nNMI ISA %x, EISA %x\n", isa_port, eisa_port); return(0); #endif } /* * Fill in default interrupt table (in case of spuruious interrupt * during configuration of kernel, setup interrupt control unit */ void isa_defaultirq() { int i; /* icu vectors */ for (i = 0; i < ICU_LEN; i++) icu_unset(i, (inthand2_t *)NULL); /* initialize 8259's */ outb(IO_ICU1, 0x11); /* reset; program device, four bytes */ outb(IO_ICU1+ICU_IMR_OFFSET, NRSVIDT); /* starting at this vector index */ outb(IO_ICU1+ICU_IMR_OFFSET, IRQ_SLAVE); /* slave on line 7 */ #ifdef PC98 #ifdef AUTO_EOI_1 outb(IO_ICU1+ICU_IMR_OFFSET, 0x1f); /* (master) auto EOI, 8086 mode */ #else outb(IO_ICU1+ICU_IMR_OFFSET, 0x1d); /* (master) 8086 mode */ #endif #else /* IBM-PC */ #ifdef AUTO_EOI_1 outb(IO_ICU1+ICU_IMR_OFFSET, 2 | 1); /* auto EOI, 8086 mode */ #else outb(IO_ICU1+ICU_IMR_OFFSET, 1); /* 8086 mode */ #endif #endif /* PC98 */ outb(IO_ICU1+ICU_IMR_OFFSET, 0xff); /* leave interrupts masked */ outb(IO_ICU1, 0x0a); /* default to IRR on read */ #ifndef PC98 outb(IO_ICU1, 0xc0 | (3 - 1)); /* pri order 3-7, 0-2 (com2 first) */ #endif /* !PC98 */ outb(IO_ICU2, 0x11); /* reset; program device, four bytes */ outb(IO_ICU2+ICU_IMR_OFFSET, NRSVIDT+8); /* staring at this vector index */ outb(IO_ICU2+ICU_IMR_OFFSET, ICU_SLAVEID); /* my slave id is 7 */ #ifdef PC98 outb(IO_ICU2+ICU_IMR_OFFSET,9); /* 8086 mode */ #else /* IBM-PC */ #ifdef AUTO_EOI_2 outb(IO_ICU2+ICU_IMR_OFFSET, 2 | 1); /* auto EOI, 8086 mode */ #else outb(IO_ICU2+ICU_IMR_OFFSET,1); /* 8086 mode */ #endif #endif /* PC98 */ outb(IO_ICU2+ICU_IMR_OFFSET, 0xff); /* leave interrupts masked */ outb(IO_ICU2, 0x0a); /* default to IRR on read */ } /* * Caught a stray interrupt, notify */ static void isa_strayintr(vcookiep) void *vcookiep; { int intr = (void **)vcookiep - &intr_unit[0]; /* DON'T BOTHER FOR NOW! */ /* for some reason, we get bursts of intr #7, even if not enabled! */ /* * Well the reason you got bursts of intr #7 is because someone * raised an interrupt line and dropped it before the 8259 could * prioritize it. This is documented in the intel data book. This * means you have BAD hardware! I have changed this so that only * the first 5 get logged, then it quits logging them, and puts * out a special message. rgrimes 3/25/1993 */ /* * XXX TODO print a different message for #7 if it is for a * glitch. Glitches can be distinguished from real #7's by * testing that the in-service bit is _not_ set. The test * must be done before sending an EOI so it can't be done if * we are using AUTO_EOI_1. */ if (intrcnt[1 + intr] <= 5) log(LOG_ERR, "stray irq %d\n", intr); if (intrcnt[1 + intr] == 5) log(LOG_CRIT, "too many stray irq %d's; not logging any more\n", intr); } /* * Return a bitmap of the current interrupt requests. This is 8259-specific * and is only suitable for use at probe time. */ intrmask_t isa_irq_pending() { u_char irr1; u_char irr2; irr1 = inb(IO_ICU1); irr2 = inb(IO_ICU2); return ((irr2 << 8) | irr1); } int update_intr_masks(void) { int intr, n=0; u_int mask,*maskptr; for (intr=0; intr < ICU_LEN; intr ++) { #if defined(APIC_IO) /* no 8259 SLAVE to ignore */ #else if (intr==ICU_SLAVEID) continue; /* ignore 8259 SLAVE output */ #endif /* APIC_IO */ maskptr = intr_mptr[intr]; if (!maskptr) continue; *maskptr |= 1 << intr; mask = *maskptr; if (mask != intr_mask[intr]) { #if 0 printf ("intr_mask[%2d] old=%08x new=%08x ptr=%p.\n", intr, intr_mask[intr], mask, maskptr); #endif intr_mask[intr]=mask; n++; } } return (n); } static const char * isa_get_nameunit(int id) { static char buf[32]; struct isa_device *dp; if (id == -1) return ("pci"); /* XXX may also be eisa */ if (id == 0) return ("clk0"); /* XXX may also be sloppy driver */ if (id == 1) return ("rtc0"); +#if 0 for (dp = isa_devtab_bio; dp->id_driver != NULL; dp++) if (dp->id_id == id) goto found_device; for (dp = isa_devtab_cam; dp->id_driver != NULL; dp++) if (dp->id_id == id) goto found_device; for (dp = isa_devtab_net; dp->id_driver != NULL; dp++) if (dp->id_id == id) goto found_device; for (dp = isa_devtab_null; dp->id_driver != NULL; dp++) if (dp->id_id == id) goto found_device; for (dp = isa_devtab_tty; dp->id_driver != NULL; dp++) if (dp->id_id == id) goto found_device; +#endif return "???"; found_device: snprintf(buf, sizeof(buf), "%s%d", dp->id_driver->name, dp->id_unit); return (buf); } void update_intrname(int intr, int device_id) { char buf[32]; char *cp; const char *name; int name_index, off, strayintr; /* * Initialise strings for bitbucket and stray interrupt counters. * These have statically allocated indices 0 and 1 through ICU_LEN. */ if (intrnames[0] == '\0') { off = sprintf(intrnames, "???") + 1; for (strayintr = 0; strayintr < ICU_LEN; strayintr++) off += sprintf(intrnames + off, "stray irq%d", strayintr) + 1; } name = isa_get_nameunit(device_id); if (snprintf(buf, sizeof(buf), "%s irq%d", name, intr) >= sizeof(buf)) goto use_bitbucket; /* * Search for `buf' in `intrnames'. In the usual case when it is * not found, append it to the end if there is enough space (the \0 * terminator for the previous string, if any, becomes a separator). */ for (cp = intrnames, name_index = 0; cp != eintrnames && name_index < NR_INTRNAMES; cp += strlen(cp) + 1, name_index++) { if (*cp == '\0') { if (strlen(buf) >= eintrnames - cp) break; strcpy(cp, buf); goto found; } if (strcmp(cp, buf) == 0) goto found; } use_bitbucket: printf("update_intrname: counting %s irq%d as %s\n", name, intr, intrnames); name_index = 0; found: intr_countp[intr] = &intrcnt[name_index]; } int icu_setup(int intr, inthand2_t *handler, void *arg, u_int *maskptr, int flags) { #ifdef FAST_HI int select; /* the select register is 8 bits */ int vector; u_int32_t value; /* the window register is 32 bits */ #endif /* FAST_HI */ u_long ef; u_int mask = (maskptr ? *maskptr : 0); #if defined(APIC_IO) if ((u_int)intr >= ICU_LEN) /* no 8259 SLAVE to ignore */ #else if ((u_int)intr >= ICU_LEN || intr == ICU_SLAVEID) #endif /* APIC_IO */ if (intr_handler[intr] != isa_strayintr) return (EBUSY); ef = read_eflags(); disable_intr(); intr_handler[intr] = handler; intr_mptr[intr] = maskptr; intr_mask[intr] = mask | (1 << intr); intr_unit[intr] = arg; #ifdef FAST_HI if (flags & INTR_FAST) { vector = TPR_FAST_INTS + intr; setidt(vector, fastintr[intr], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); } else { vector = TPR_SLOW_INTS + intr; #ifdef APIC_INTR_REORDER #ifdef APIC_INTR_HIGHPRI_CLOCK /* XXX: Hack (kludge?) for more accurate clock. */ if (intr == apic_8254_intr || intr == 8) { vector = TPR_FAST_INTS + intr; } #endif #endif setidt(vector, slowintr[intr], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); } #ifdef APIC_INTR_REORDER set_lapic_isrloc(intr, vector); #endif /* * Reprogram the vector in the IO APIC. */ if (int_to_apicintpin[intr].ioapic >= 0) { select = int_to_apicintpin[intr].redirindex; value = io_apic_read(int_to_apicintpin[intr].ioapic, select) & ~IOART_INTVEC; io_apic_write(int_to_apicintpin[intr].ioapic, select, value | vector); } #else setidt(ICU_OFFSET + intr, flags & INTR_FAST ? fastintr[intr] : slowintr[intr], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); #endif /* FAST_HI */ INTREN(1 << intr); MPINTR_UNLOCK(); write_eflags(ef); return (0); } void register_imask(dvp, mask) struct isa_device *dvp; u_int mask; { if (dvp->id_alive && dvp->id_irq) { int intr; intr = ffs(dvp->id_irq) - 1; intr_mask[intr] = mask | (1 <= ICU_LEN || handler != intr_handler[intr]) return (EINVAL); INTRDIS(1 << intr); ef = read_eflags(); disable_intr(); intr_countp[intr] = &intrcnt[1 + intr]; intr_handler[intr] = isa_strayintr; intr_mptr[intr] = NULL; intr_mask[intr] = HWI_MASK | SWI_MASK; intr_unit[intr] = &intr_unit[intr]; #ifdef FAST_HI_XXX /* XXX how do I re-create dvp here? */ setidt(flags & INTR_FAST ? TPR_FAST_INTS + intr : TPR_SLOW_INTS + intr, slowintr[intr], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); #else /* FAST_HI */ #ifdef APIC_INTR_REORDER set_lapic_isrloc(intr, ICU_OFFSET + intr); #endif setidt(ICU_OFFSET + intr, slowintr[intr], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); #endif /* FAST_HI */ MPINTR_UNLOCK(); write_eflags(ef); return (0); } Index: head/sys/amd64/isa/isa.c =================================================================== --- head/sys/amd64/isa/isa.c (revision 45719) +++ head/sys/amd64/isa/isa.c (revision 45720) @@ -1,1073 +1,623 @@ /*- - * Copyright (c) 1991 The Regents of the University of California. + * Copyright (c) 1998 Doug Rabson * All rights reserved. * - * This code is derived from software contributed to Berkeley by - * William Jolitz. - * * 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 + * 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 REGENTS OR CONTRIBUTORS BE LIABLE + * 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. * - * from: @(#)isa.c 7.2 (Berkeley) 5/13/91 - * $Id: isa.c,v 1.117 1998/11/29 15:42:40 phk Exp $ + * $Id: isa.c,v 1.4 1998/09/16 08:23:51 dfr Exp $ */ /* - * code to manage AT bus + * Modifications for Intel architecture by Garrett A. Wollman. + * Copyright 1998 Massachusetts Institute of Technology * - * 92/08/18 Frank P. MacLachlan (fpm@crash.cts.com): - * Fixed uninitialized variable problem and added code to deal - * with DMA page boundaries in isa_dmarangecheck(). Fixed word - * mode DMA count compution and reorganized DMA setup code in - * isa_dmastart() + * Permission to use, copy, modify, and distribute this software and + * its documentation for any purpose and without fee is hereby + * granted, provided that both the above copyright notice and this + * permission notice appear in all copies, that both the above + * copyright notice and this permission notice appear in all + * supporting documentation, and that the name of M.I.T. not be used + * in advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. M.I.T. makes + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS + * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT + * SHALL M.I.T. 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. */ #include #include -#include +#include +#include #include -#include -#include -#ifdef APIC_IO -#include -#endif /* APIC_IO */ -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include -#include +#include -#include "pnp.h" -#if NPNP > 0 -#include -#endif +#include -/* -** Register definitions for DMA controller 1 (channels 0..3): -*/ -#define DMA1_CHN(c) (IO_DMA1 + 1*(2*(c))) /* addr reg for channel c */ -#define DMA1_SMSK (IO_DMA1 + 1*10) /* single mask register */ -#define DMA1_MODE (IO_DMA1 + 1*11) /* mode register */ -#define DMA1_FFC (IO_DMA1 + 1*12) /* clear first/last FF */ +MALLOC_DEFINE(M_ISADEV, "isadev", "ISA device"); /* -** Register definitions for DMA controller 2 (channels 4..7): -*/ -#define DMA2_CHN(c) (IO_DMA2 + 2*(2*(c))) /* addr reg for channel c */ -#define DMA2_SMSK (IO_DMA2 + 2*10) /* single mask register */ -#define DMA2_MODE (IO_DMA2 + 2*11) /* mode register */ -#define DMA2_FFC (IO_DMA2 + 2*12) /* clear first/last FF */ + * The structure used to attach devices to the isa bus. + */ +struct isa_device { + short id_port[ISA_NPORT_IVARS]; + u_short id_portsize[ISA_NPORT_IVARS]; + vm_offset_t id_maddr[ISA_NMEM_IVARS]; + vm_size_t id_msize[ISA_NMEM_IVARS]; + int id_irq[ISA_NIRQ_IVARS]; + int id_drq[ISA_NDRQ_IVARS]; + int id_flags; + struct resource *id_portres[ISA_NPORT_IVARS]; + struct resource *id_memres[ISA_NMEM_IVARS]; + struct resource *id_irqres[ISA_NIRQ_IVARS]; + struct resource *id_drqres[ISA_NDRQ_IVARS]; +}; -static void config_isadev __P((struct isa_device *isdp, u_int *mp)); -static void config_isadev_c __P((struct isa_device *isdp, u_int *mp, - int reconfig)); -static void conflict __P((struct isa_device *dvp, struct isa_device *tmpdvp, - int item, char const *whatnot, char const *reason, - char const *format)); -static int haveseen __P((struct isa_device *dvp, struct isa_device *tmpdvp, - u_int checkbits)); -static int isa_dmarangecheck __P((caddr_t va, u_int length, int chan)); +#define DEVTOISA(dev) ((struct isa_device *) device_get_ivars(dev)) -/* - * print a conflict message - */ +static devclass_t isa_devclass; + static void -conflict(dvp, tmpdvp, item, whatnot, reason, format) - struct isa_device *dvp; - struct isa_device *tmpdvp; - int item; - char const *whatnot; - char const *reason; - char const *format; +isa_add_device(device_t dev, const char *name, int unit) { - printf("%s%d not %sed due to %s conflict with %s%d at ", - dvp->id_driver->name, dvp->id_unit, whatnot, reason, - tmpdvp->id_driver->name, tmpdvp->id_unit); - printf(format, item); - printf("\n"); -} + struct isa_device *idev; + device_t child; + int sensitive, t; + static device_t last_sensitive; -/* - * Check to see if things are already in use, like IRQ's, I/O addresses - * and Memory addresses. - */ -static int -haveseen(dvp, tmpdvp, checkbits) - struct isa_device *dvp; - struct isa_device *tmpdvp; - u_int checkbits; -{ - /* - * Ignore all conflicts except IRQ ones if conflicts are allowed. - */ - if (dvp->id_conflicts) - checkbits &= ~(CC_DRQ | CC_IOADDR | CC_MEMADDR); - /* - * Only check against devices that have already been found. - */ - if (tmpdvp->id_alive) { - char const *whatnot; + if (resource_int_value(name, unit, "sensitive", &sensitive) != 0) + sensitive = 0; - /* - * Check for device driver & unit conflict; just drop probing - * a device which has already probed true. This is usually - * not strictly a conflict, but rather the case of somebody - * having specified several mutually exclusive configurations - * for a single device. - */ - if (tmpdvp->id_driver == dvp->id_driver && - tmpdvp->id_unit == dvp->id_unit) { - return 1; - } + idev = malloc(sizeof(struct isa_device), M_ISADEV, M_NOWAIT); + if (!idev) + return; + bzero(idev, sizeof *idev); - whatnot = checkbits & CC_ATTACH ? "attach" : "prob"; - /* - * Check for I/O address conflict. We can only check the - * starting address of the device against the range of the - * device that has already been probed since we do not - * know how many I/O addresses this device uses. - */ - if (checkbits & CC_IOADDR && tmpdvp->id_alive != -1) { - if ((dvp->id_iobase >= tmpdvp->id_iobase) && - (dvp->id_iobase <= - (tmpdvp->id_iobase + tmpdvp->id_alive - 1))) { - if (!(checkbits & CC_QUIET)) - conflict(dvp, tmpdvp, dvp->id_iobase, - whatnot, "I/O address", "0x%x"); - return 1; - } - } - /* - * Check for Memory address conflict. We can check for - * range overlap, but it will not catch all cases since the - * driver may adjust the msize paramater during probe, for - * now we just check that the starting address does not - * fall within any allocated region. - * XXX could add a second check after the probe for overlap, - * since at that time we would know the full range. - * XXX KERNBASE is a hack, we should have vaddr in the table! - */ - if (checkbits & CC_MEMADDR && tmpdvp->id_maddr) { - if ((KERNBASE + dvp->id_maddr >= tmpdvp->id_maddr) && - (KERNBASE + dvp->id_maddr <= - (tmpdvp->id_maddr + tmpdvp->id_msize - 1))) { - if (!(checkbits & CC_QUIET)) - conflict(dvp, tmpdvp, - (int)dvp->id_maddr, whatnot, - "maddr", "0x%x"); - return 1; - } - } - /* - * Check for IRQ conflicts. - */ - if (checkbits & CC_IRQ && tmpdvp->id_irq) { - if (tmpdvp->id_irq == dvp->id_irq) { - if (!(checkbits & CC_QUIET)) - conflict(dvp, tmpdvp, - ffs(dvp->id_irq) - 1, whatnot, - "irq", "%d"); - return 1; - } - } - /* - * Check for DRQ conflicts. - */ - if (checkbits & CC_DRQ && tmpdvp->id_drq != -1) { - if (tmpdvp->id_drq == dvp->id_drq) { - if (!(checkbits & CC_QUIET)) - conflict(dvp, tmpdvp, dvp->id_drq, - whatnot, "drq", "%d"); - return 1; - } - } - } - return 0; -} + if (resource_int_value(name, unit, "port", &t) == 0) + idev->id_port[0] = t; + else + idev->id_port[0] = -1; + idev->id_port[1] = 0; -#ifdef RESOURCE_CHECK -#include + if (resource_int_value(name, unit, "portsize", &t) == 0) + idev->id_portsize[0] = t; + else + idev->id_portsize[0] = 0; + idev->id_portsize[1] = 0; -static int -checkone (struct isa_device *dvp, int type, addr_t low, addr_t high, - char *resname, char *resfmt, int attaching) -{ - int result = 0; - if (bootverbose) { - if (low == high) - printf("\tcheck %s: 0x%x\n", resname, low); - else - printf("\tcheck %s: 0x%x to 0x%x\n", - resname, low, high); - } - if (resource_check(type, RESF_NONE, low, high) != NULL) { - char *whatnot = attaching ? "attach" : "prob"; - static struct isa_device dummydev; - static struct isa_driver dummydrv; - struct isa_device *tmpdvp = &dummydev; + if (resource_int_value(name, unit, "maddr", &t) == 0) + idev->id_maddr[0] = t; + else + idev->id_maddr[0] = 0; + idev->id_maddr[1] = 0; - dummydev.id_driver = &dummydrv; - dummydev.id_unit = 0; - dummydrv.name = "pci"; - conflict(dvp, tmpdvp, low, whatnot, resname, resfmt); - result = 1; - } else if (attaching) { - if (low == high) - printf("\tregister %s: 0x%x\n", resname, low); - else - printf("\tregister %s: 0x%x to 0x%x\n", - resname, low, high); - resource_claim(dvp, type, RESF_NONE, low, high); - } - return (result); -} + if (resource_int_value(name, unit, "msize", &t) == 0) + idev->id_msize[0] = t; + else + idev->id_msize[0] = 0; + idev->id_msize[1] = 0; -static int -check_pciconflict(struct isa_device *dvp, int checkbits) -{ - int result = 0; - int attaching = (checkbits & CC_ATTACH) != 0; + if (resource_int_value(name, unit, "flags", &t) == 0) + idev->id_flags = t; + else + idev->id_flags = 0; - if (checkbits & CC_MEMADDR) { - long maddr = dvp->id_maddr; - long msize = dvp->id_msize; - if (msize > 0) { - if (checkone(dvp, REST_MEM, maddr, maddr + msize - 1, - "maddr", "0x%x", attaching) != 0) { - result = 1; - attaching = 0; - } - } - } - if (checkbits & CC_IOADDR) { - unsigned iobase = dvp->id_iobase; - unsigned iosize = dvp->id_alive; - if (iosize == -1) - iosize = 1; /* XXX can't do much about this ... */ - if (iosize > 0) { - if (checkone(dvp, REST_PORT, iobase, iobase + iosize -1, - "I/O address", "0x%x", attaching) != 0) { - result = 1; - attaching = 0; - } - } - } - if (checkbits & CC_IRQ) { - int irq = ffs(dvp->id_irq) - 1; - if (irq >= 0) { - if (checkone(dvp, REST_INT, irq, irq, - "irq", "%d", attaching) != 0) { - result = 1; - attaching = 0; - } - } - } - if (checkbits & CC_DRQ) { - int drq = dvp->id_drq; - if (drq >= 0) { - if (checkone(dvp, REST_DMA, drq, drq, - "drq", "%d", attaching) != 0) { - result = 1; - attaching = 0; - } - } - } - if (result != 0) - resource_free (dvp); - return (result); -} -#endif /* RESOURCE_CHECK */ + if (resource_int_value(name, unit, "irq", &t) == 0) + idev->id_irq[0] = t; + else + idev->id_irq[0] = -1; + idev->id_irq[1] = -1; -/* - * Search through all the isa_devtab_* tables looking for anything that - * conflicts with the current device. - */ -int -haveseen_isadev(dvp, checkbits) - struct isa_device *dvp; - u_int checkbits; -{ -#if NPNP > 0 - struct pnp_dlist_node *nod; -#endif - struct isa_device *tmpdvp; - int status = 0; + if (resource_int_value(name, unit, "drq", &t) == 0) + idev->id_drq[0] = t; + else + idev->id_drq[0] = -1; + idev->id_drq[1] = -1; - for (tmpdvp = isa_devtab_tty; tmpdvp->id_driver; tmpdvp++) { - status |= haveseen(dvp, tmpdvp, checkbits); - if (status) - return status; - } - for (tmpdvp = isa_devtab_bio; tmpdvp->id_driver; tmpdvp++) { - status |= haveseen(dvp, tmpdvp, checkbits); - if (status) - return status; - } - for (tmpdvp = isa_devtab_net; tmpdvp->id_driver; tmpdvp++) { - status |= haveseen(dvp, tmpdvp, checkbits); - if (status) - return status; - } - for (tmpdvp = isa_devtab_cam; tmpdvp->id_driver; tmpdvp++) { - status |= haveseen(dvp, tmpdvp, checkbits); - if (status) - return status; - } - for (tmpdvp = isa_devtab_null; tmpdvp->id_driver; tmpdvp++) { - status |= haveseen(dvp, tmpdvp, checkbits); - if (status) - return status; - } -#if NPNP > 0 - for (nod = pnp_device_list; nod != NULL; nod = nod->next) - if (status |= haveseen(dvp, &(nod->dev), checkbits)) - return status; -#endif -#ifdef RESOURCE_CHECK - if (!dvp->id_conflicts) - status = check_pciconflict(dvp, checkbits); - else if (bootverbose) - printf("\tnot checking for resource conflicts ...\n"); -#endif /* RESOURCE_CHECK */ - return(status); + if (sensitive) + child = device_add_child_after(dev, last_sensitive, name, + unit, idev); + else + child = device_add_child(dev, name, unit, idev); + if (child == 0) + return; + else if (sensitive) + last_sensitive = child; + + if (resource_int_value(name, unit, "disabled", &t) == 0 && t != 0) + device_disable(child); } /* - * Configure all ISA devices + * At 'probe' time, we add all the devices which we know about to the + * bus. The generic attach routine will probe and attach them if they + * are alive. */ -void -isa_configure() +static int +isa_probe(device_t dev) { - struct isa_device *dvp; + int i; + static char buf[] = "isaXXX"; - printf("Probing for devices on the ISA bus:\n"); - /* First probe all the sensitive probes */ - for (dvp = isa_devtab_tty; dvp->id_driver; dvp++) - if (dvp->id_driver->sensitive_hw) - config_isadev(dvp, &tty_imask); - for (dvp = isa_devtab_bio; dvp->id_driver; dvp++) - if (dvp->id_driver->sensitive_hw) - config_isadev(dvp, &bio_imask); - for (dvp = isa_devtab_net; dvp->id_driver; dvp++) - if (dvp->id_driver->sensitive_hw) - config_isadev(dvp, &net_imask); - for (dvp = isa_devtab_cam; dvp->id_driver; dvp++) - if (dvp->id_driver->sensitive_hw) - config_isadev(dvp, &cam_imask); - for (dvp = isa_devtab_null; dvp->id_driver; dvp++) - if (dvp->id_driver->sensitive_hw) - config_isadev(dvp, (u_int *)NULL); + device_set_desc(dev, "ISA bus"); - /* Then all the bad ones */ - for (dvp = isa_devtab_tty; dvp->id_driver; dvp++) - if (!dvp->id_driver->sensitive_hw) - config_isadev(dvp, &tty_imask); - for (dvp = isa_devtab_bio; dvp->id_driver; dvp++) - if (!dvp->id_driver->sensitive_hw) - config_isadev(dvp, &bio_imask); - for (dvp = isa_devtab_net; dvp->id_driver; dvp++) - if (!dvp->id_driver->sensitive_hw) - config_isadev(dvp, &net_imask); - for (dvp = isa_devtab_cam; dvp->id_driver; dvp++) - if (!dvp->id_driver->sensitive_hw) - config_isadev(dvp, &cam_imask); - for (dvp = isa_devtab_null; dvp->id_driver; dvp++) - if (!dvp->id_driver->sensitive_hw) - config_isadev(dvp, (u_int *)NULL); + /* + * Add all devices configured to be attached to isa0. + */ + sprintf(buf, "isa%d", device_get_unit(dev)); + for (i = resource_query_string(-1, "at", buf); + i != -1; + i = resource_query_string(i, "at", buf)) { + isa_add_device(dev, resource_query_name(i), + resource_query_unit(i)); + } -/* - * XXX we should really add the tty device to net_imask when the line is - * switched to SLIPDISC, and then remove it when it is switched away from - * SLIPDISC. No need to block out ALL ttys during a splimp when only one - * of them is running slip. - * - * XXX actually, blocking all ttys during a splimp doesn't matter so much - * with sio because the serial interrupt layer doesn't use tty_imask. Only - * non-serial ttys suffer. It's more stupid that ALL 'net's are blocked - * during spltty. - */ -#include "sl.h" -#if NSL > 0 - net_imask |= tty_imask; - tty_imask = net_imask; -#endif - - if (bootverbose) - printf("imasks: bio %x, tty %x, net %x\n", - bio_imask, tty_imask, net_imask); - /* - * Finish initializing intr_mask[]. Note that the partly - * constructed masks aren't actually used since we're at splhigh. - * For fully dynamic initialization, register_intr() and - * icu_unset() will have to adjust the masks for _all_ - * interrupts and for tty_imask, etc. + * and isa? */ - for (dvp = isa_devtab_tty; dvp->id_driver; dvp++) - register_imask(dvp, tty_imask); - for (dvp = isa_devtab_bio; dvp->id_driver; dvp++) - register_imask(dvp, bio_imask); - for (dvp = isa_devtab_net; dvp->id_driver; dvp++) - register_imask(dvp, net_imask); - for (dvp = isa_devtab_cam; dvp->id_driver; dvp++) - register_imask(dvp, cam_imask); - for (dvp = isa_devtab_null; dvp->id_driver; dvp++) - register_imask(dvp, SWI_CLOCK_MASK); -} + for (i = resource_query_string(-1, "at", "isa"); + i != -1; + i = resource_query_string(i, "at", "isa")) { + isa_add_device(dev, resource_query_name(i), + resource_query_unit(i)); + } -/* - * Configure an ISA device. - */ -static void -config_isadev(isdp, mp) - struct isa_device *isdp; - u_int *mp; -{ - config_isadev_c(isdp, mp, 0); + isa_wrap_old_drivers(); + + return 0; } -void -reconfig_isadev(isdp, mp) - struct isa_device *isdp; - u_int *mp; +extern device_t isa_bus_device; + +static int +isa_attach(device_t dev) { - config_isadev_c(isdp, mp, 1); + /* + * Arrange for bus_generic_attach(dev) to be called later. + */ + isa_bus_device = dev; + return 0; } static void -config_isadev_c(isdp, mp, reconfig) - struct isa_device *isdp; - u_int *mp; - int reconfig; +isa_print_child(device_t bus, device_t dev) { - u_int checkbits; - int id_alive; - int last_alive; - struct isa_driver *dp = isdp->id_driver; + struct isa_device *id = DEVTOISA(dev); - if (!isdp->id_enabled) { - if (bootverbose) - printf("%s%d: disabled, not probed.\n", dp->name, isdp->id_unit); - return; + if (id->id_port[0] > 0 || id->id_port[1] > 0 + || id->id_maddr[0] > 0 || id->id_maddr[1] > 0 + || id->id_irq[0] >= 0 || id->id_irq[1] >= 0 + || id->id_drq[0] >= 0 || id->id_drq[1] >= 0) + printf(" at"); + if (id->id_port[0] > 0 && id->id_port[1] > 0) { + printf(" ports %#x", (u_int)id->id_port[0]); + if (id->id_portsize[0] > 1) + printf("-%#x", (u_int)(id->id_port[0] + + id->id_portsize[0] - 1)); + printf(" and %#x", (u_int)id->id_port[1]); + if (id->id_portsize[1] > 1) + printf("-%#x", (u_int)(id->id_port[1] + + id->id_portsize[1] - 1)); + } else if (id->id_port[0] > 0) { + printf(" port %#x", (u_int)id->id_port[0]); + if (id->id_portsize[0] > 1) + printf("-%#x", (u_int)(id->id_port[0] + + id->id_portsize[0] - 1)); + } else if (id->id_port[1] > 0) { + printf(" port %#x", (u_int)id->id_port[1]); + if (id->id_portsize[1] > 1) + printf("-%#x", (u_int)(id->id_port[1] + + id->id_portsize[1] - 1)); } - checkbits = CC_DRQ | CC_IOADDR | CC_MEMADDR; - if (!reconfig && haveseen_isadev(isdp, checkbits)) - return; - if (!reconfig && isdp->id_maddr) { - isdp->id_maddr -= ISA_HOLE_START; - isdp->id_maddr += atdevbase; + if (id->id_maddr[0] && id->id_maddr[1]) { + printf(" iomem %#x", (u_int)id->id_maddr[0]); + if (id->id_msize[0]) + printf("-%#x", (u_int)(id->id_maddr[0] + + id->id_msize[0] - 1)); + printf(" and %#x", (u_int)id->id_maddr[1]); + if (id->id_msize[1]) + printf("-%#x", (u_int)(id->id_maddr[1] + + id->id_msize[1] - 1)); + } else if (id->id_maddr[0]) { + printf(" iomem %#x", (u_int)id->id_maddr[0]); + if (id->id_msize[0]) + printf("-%#x", (u_int)(id->id_maddr[0] + + id->id_msize[0] - 1)); + } else if (id->id_maddr[1]) { + printf(" iomem %#x", (u_int)id->id_maddr[1]); + if (id->id_msize[1]) + printf("-%#x", (u_int)(id->id_maddr[1] + + id->id_msize[1] - 1)); } - if (reconfig) { - last_alive = isdp->id_alive; - isdp->id_reconfig = 1; - } - else { - last_alive = 0; - isdp->id_reconfig = 0; - } - id_alive = (*dp->probe)(isdp); - if (id_alive) { - /* - * Only print the I/O address range if id_alive != -1 - * Right now this is a temporary fix just for the new - * NPX code so that if it finds a 486 that can use trap - * 16 it will not report I/O addresses. - * Rod Grimes 04/26/94 - */ - if (!isdp->id_reconfig) { - printf("%s%d", dp->name, isdp->id_unit); - if (id_alive != -1) { - if (isdp->id_iobase == -1) - printf(" at"); - else { - printf(" at 0x%x", isdp->id_iobase); - if (isdp->id_iobase + id_alive - 1 != - isdp->id_iobase) { - printf("-0x%x", - isdp->id_iobase + id_alive - 1); - } - } - } - if (isdp->id_irq) - printf(" irq %d", ffs(isdp->id_irq) - 1); - if (isdp->id_drq != -1) - printf(" drq %d", isdp->id_drq); - if (isdp->id_maddr) - printf(" maddr 0x%lx", kvtop(isdp->id_maddr)); - if (isdp->id_msize) - printf(" msize %d", isdp->id_msize); - if (isdp->id_flags) - printf(" flags 0x%x", isdp->id_flags); - if (isdp->id_iobase && !(isdp->id_iobase & 0xf300)) { - printf(" on motherboard"); - } else if (isdp->id_iobase >= 0x1000 && - !(isdp->id_iobase & 0x300)) { - printf (" on eisa slot %d", - isdp->id_iobase >> 12); - } else { - printf (" on isa"); - } - printf("\n"); - /* - * Check for conflicts again. The driver may have - * changed *dvp. We should weaken the early check - * since the driver may have been able to change - * *dvp to avoid conflicts if given a chance. We - * already skip the early check for IRQs and force - * a check for IRQs in the next group of checks. - */ - checkbits |= CC_ATTACH | CC_IRQ; - if (haveseen_isadev(isdp, checkbits)) - return; - isdp->id_alive = id_alive; - } - (*dp->attach)(isdp); - if (isdp->id_irq != 0 && isdp->id_intr == NULL) - printf("%s%d: irq with no handler\n", - dp->name, isdp->id_unit); - if (isdp->id_irq != 0 && isdp->id_intr != NULL) { -#ifdef APIC_IO - /* - * Some motherboards use upper IRQs for traditional - * ISA INTerrupt sources. In particular we have - * seen the secondary IDE connected to IRQ20. - * This code detects and fixes this situation. - */ - u_int apic_mask; - int rirq; + if (id->id_irq[0] >= 0 && id->id_irq[1] >= 0) + printf(" irqs %d and %d", id->id_irq[0], id->id_irq[1]); + else if (id->id_irq[0] >= 0) + printf(" irq %d", id->id_irq[0]); + else if (id->id_irq[1] >= 0) + printf(" irq %d", id->id_irq[1]); + if (id->id_drq[0] >= 0 && id->id_drq[1] >= 0) + printf(" drqs %d and %d", id->id_drq[0], id->id_drq[1]); + else if (id->id_drq[0] >= 0) + printf(" drq %d", id->id_drq[0]); + else if (id->id_drq[1] >= 0) + printf(" drq %d", id->id_drq[1]); - apic_mask = isa_apic_mask(isdp->id_irq); - if (apic_mask != isdp->id_irq) { - rirq = ffs(isdp->id_irq) - 1; - isdp->id_irq = apic_mask; - undirect_isa_irq(rirq); /* free for ISA */ - } -#endif /* APIC_IO */ - register_intr(ffs(isdp->id_irq) - 1, isdp->id_id, - isdp->id_ri_flags, isdp->id_intr, - mp, isdp->id_unit); - } - } else { - if (isdp->id_reconfig) { - (*dp->attach)(isdp); /* reconfiguration attach */ - } - if (!last_alive) { - if (!isdp->id_reconfig) { - printf("%s%d not found", - dp->name, isdp->id_unit); - if (isdp->id_iobase != -1) - printf(" at 0x%x", isdp->id_iobase); - printf("\n"); - } - } else { -#if 0 - /* This code has not been tested.... */ - if (isdp->id_irq != 0 && isdp->id_intr != NULL) { - icu_unset(ffs(isdp->id_irq) - 1, - isdp->id_intr); - if (mp) - INTRUNMASK(*mp, isdp->id_irq); - } -#else - printf ("icu_unset() not supported here ...\n"); -#endif - } - } + if (id->id_flags) + printf(" flags %#x", id->id_flags); + + printf(" on %s%d", + device_get_name(bus), device_get_unit(bus)); } -static caddr_t dma_bouncebuf[8]; -static u_int dma_bouncebufsize[8]; -static u_int8_t dma_bounced = 0; -static u_int8_t dma_busy = 0; /* Used in isa_dmastart() */ -static u_int8_t dma_inuse = 0; /* User for acquire/release */ -static u_int8_t dma_auto_mode = 0; - -#define VALID_DMA_MASK (7) - -/* high byte of address is stored in this port for i-th dma channel */ -static int dmapageport[8] = { 0x87, 0x83, 0x81, 0x82, 0x8f, 0x8b, 0x89, 0x8a }; - -/* - * Setup a DMA channel's bounce buffer. - */ -void -isa_dmainit(chan, bouncebufsize) - int chan; - u_int bouncebufsize; +static int +isa_read_ivar(device_t bus, device_t dev, int index, uintptr_t * result) { - void *buf; + struct isa_device* idev = DEVTOISA(dev); -#ifdef DIAGNOSTIC - if (chan & ~VALID_DMA_MASK) - panic("isa_dmainit: channel out of range"); - - if (dma_bouncebuf[chan] != NULL) - panic("isa_dmainit: impossible request"); -#endif - - dma_bouncebufsize[chan] = bouncebufsize; - - /* Try malloc() first. It works better if it works. */ - buf = malloc(bouncebufsize, M_DEVBUF, M_NOWAIT); - if (buf != NULL) { - if (isa_dmarangecheck(buf, bouncebufsize, chan) == 0) { - dma_bouncebuf[chan] = buf; - return; - } - free(buf, M_DEVBUF); + switch (index) { + case ISA_IVAR_PORT_0: + *result = idev->id_port[0]; + break; + case ISA_IVAR_PORT_1: + *result = idev->id_port[1]; + break; + case ISA_IVAR_PORTSIZE_0: + *result = idev->id_portsize[0]; + break; + case ISA_IVAR_PORTSIZE_1: + *result = idev->id_portsize[1]; + break; + case ISA_IVAR_MADDR_0: + *result = idev->id_maddr[0]; + break; + case ISA_IVAR_MADDR_1: + *result = idev->id_maddr[1]; + break; + case ISA_IVAR_MSIZE_0: + *result = idev->id_msize[0]; + break; + case ISA_IVAR_MSIZE_1: + *result = idev->id_msize[1]; + break; + case ISA_IVAR_IRQ_0: + *result = idev->id_irq[0]; + break; + case ISA_IVAR_IRQ_1: + *result = idev->id_irq[1]; + break; + case ISA_IVAR_DRQ_0: + *result = idev->id_drq[0]; + break; + case ISA_IVAR_DRQ_1: + *result = idev->id_drq[1]; + break; + case ISA_IVAR_FLAGS: + *result = idev->id_flags; + break; } - buf = contigmalloc(bouncebufsize, M_DEVBUF, M_NOWAIT, 0ul, 0xfffffful, - 1ul, chan & 4 ? 0x20000ul : 0x10000ul); - if (buf == NULL) - printf("isa_dmainit(%d, %d) failed\n", chan, bouncebufsize); - else - dma_bouncebuf[chan] = buf; + return ENOENT; } /* - * Register a DMA channel's usage. Usually called from a device driver - * in open() or during its initialization. + * XXX -- this interface is pretty much irrelevant in the presence of + * BUS_ALLOC_RESOURCE / BUS_RELEASE_RESOURCE (at least for the ivars which + * are defined at this point). */ -int -isa_dma_acquire(chan) - int chan; +static int +isa_write_ivar(device_t bus, device_t dev, + int index, uintptr_t value) { -#ifdef DIAGNOSTIC - if (chan & ~VALID_DMA_MASK) - panic("isa_dma_acquire: channel out of range"); -#endif + struct isa_device* idev = DEVTOISA(dev); - if (dma_inuse & (1 << chan)) { - printf("isa_dma_acquire: channel %d already in use\n", chan); - return (EBUSY); + switch (index) { + case ISA_IVAR_PORT_0: + idev->id_port[0] = value; + break; + case ISA_IVAR_PORT_1: + idev->id_port[1] = value; + break; + case ISA_IVAR_PORTSIZE_0: + idev->id_portsize[0] = value; + break; + case ISA_IVAR_PORTSIZE_1: + idev->id_portsize[1] = value; + break; + case ISA_IVAR_MADDR_0: + idev->id_maddr[0] = value; + break; + case ISA_IVAR_MADDR_1: + idev->id_maddr[1] = value; + break; + case ISA_IVAR_MSIZE_0: + idev->id_msize[0] = value; + break; + case ISA_IVAR_MSIZE_1: + idev->id_msize[1] = value; + break; + case ISA_IVAR_IRQ_0: + idev->id_irq[0] = value; + break; + case ISA_IVAR_IRQ_1: + idev->id_irq[1] = value; + break; + case ISA_IVAR_DRQ_0: + idev->id_drq[0] = value; + break; + case ISA_IVAR_DRQ_1: + idev->id_drq[1] = value; + break; + case ISA_IVAR_FLAGS: + idev->id_flags = value; + break; + default: + return (ENOENT); } - dma_inuse |= (1 << chan); - dma_auto_mode &= ~(1 << chan); - return (0); } /* - * Unregister a DMA channel's usage. Usually called from a device driver - * during close() or during its shutdown. + * This implementation simply passes the request up to the parent + * bus, which in our case is the special i386 nexus, substituting any + * configured values if the caller defaulted. We can get away with + * this because there is no special mapping for ISA resources on an Intel + * platform. When porting this code to another architecture, it may be + * necessary to interpose a mapping layer here. */ -void -isa_dma_release(chan) - int chan; +static struct resource * +isa_alloc_resource(device_t bus, device_t child, int type, int *rid, + u_long start, u_long end, u_long count, u_int flags) { -#ifdef DIAGNOSTIC - if (chan & ~VALID_DMA_MASK) - panic("isa_dma_release: channel out of range"); + int isdefault; + struct resource *rv, **rvp = 0; + struct isa_device *id = DEVTOISA(child); - if ((dma_inuse & (1 << chan)) == 0) - printf("isa_dma_release: channel %d not in use\n", chan); -#endif - - if (dma_busy & (1 << chan)) { - dma_busy &= ~(1 << chan); - /* - * XXX We should also do "dma_bounced &= (1 << chan);" - * because we are acting on behalf of isa_dmadone() which - * was not called to end the last DMA operation. This does - * not matter now, but it may in the future. + if (child) { + /* + * If this is our child, then use the isa_device to find + * defaults and to record results. */ - } + if (device_get_devclass(device_get_parent(child)) == isa_devclass) + id = DEVTOISA(child); + else + id = NULL; + } else + id = NULL; + isdefault = (id != NULL && start == 0UL && end == ~0UL && *rid == 0); + if (*rid > 1) + return 0; - dma_inuse &= ~(1 << chan); - dma_auto_mode &= ~(1 << chan); -} + switch (type) { + case SYS_RES_IRQ: + if (isdefault && id->id_irq[0] >= 0) { + start = id->id_irq[0]; + end = id->id_irq[0]; + count = 1; + } + if (id) + rvp = &id->id_irqres[*rid]; + break; -/* - * isa_dmacascade(): program 8237 DMA controller channel to accept - * external dma control by a board. - */ -void -isa_dmacascade(chan) - int chan; -{ -#ifdef DIAGNOSTIC - if (chan & ~VALID_DMA_MASK) - panic("isa_dmacascade: channel out of range"); -#endif + case SYS_RES_DRQ: + if (isdefault && id->id_drq[0] >= 0) { + start = id->id_drq[0]; + end = id->id_drq[0]; + count = 1; + } + if (id) + rvp = &id->id_drqres[*rid]; + break; - /* set dma channel mode, and set dma channel mode */ - if ((chan & 4) == 0) { - outb(DMA1_MODE, DMA37MD_CASCADE | chan); - outb(DMA1_SMSK, chan); - } else { - outb(DMA2_MODE, DMA37MD_CASCADE | (chan & 3)); - outb(DMA2_SMSK, chan & 3); - } -} + case SYS_RES_MEMORY: + if (isdefault && id->id_maddr[0]) { + start = id->id_maddr[0]; + count = max(count, (u_long)id->id_msize[0]); + end = id->id_maddr[0] + count; + } + if (id) + rvp = &id->id_memres[*rid]; + break; -/* - * isa_dmastart(): program 8237 DMA controller channel, avoid page alignment - * problems by using a bounce buffer. - */ -void -isa_dmastart(int flags, caddr_t addr, u_int nbytes, int chan) -{ - vm_offset_t phys; - int waport; - caddr_t newaddr; + case SYS_RES_IOPORT: + if (isdefault && id->id_port[0]) { + start = id->id_port[0]; + count = max(count, (u_long)id->id_portsize[0]); + end = id->id_port[0] + count; + } + if (id) + rvp = &id->id_portres[*rid]; + break; -#ifdef DIAGNOSTIC - if (chan & ~VALID_DMA_MASK) - panic("isa_dmastart: channel out of range"); + default: + return 0; + } - if ((chan < 4 && nbytes > (1<<16)) - || (chan >= 4 && (nbytes > (1<<17) || (u_int)addr & 1))) - panic("isa_dmastart: impossible request"); + /* + * If the client attempts to reallocate a resource without + * releasing what was there previously, die horribly so that + * he knows how he !@#$ed up. + */ + if (rvp && *rvp != 0) + panic("%s%d: (%d, %d) not free for %s%d\n", + device_get_name(bus), device_get_unit(bus), + type, *rid, + device_get_name(child), device_get_unit(child)); - if ((dma_inuse & (1 << chan)) == 0) - printf("isa_dmastart: channel %d not acquired\n", chan); -#endif - -#if 0 /* - * XXX This should be checked, but drivers like ad1848 only call - * isa_dmastart() once because they use Auto DMA mode. If we - * leave this in, drivers that do this will print this continuously. + * nexus_alloc_resource had better not change *rid... */ - if (dma_busy & (1 << chan)) - printf("isa_dmastart: channel %d busy\n", chan); -#endif + rv = BUS_ALLOC_RESOURCE(device_get_parent(bus), child, type, rid, + start, end, count, flags); + if (rvp && (*rvp = rv) != 0) { + switch (type) { + case SYS_RES_MEMORY: + id->id_maddr[*rid] = rv->r_start; + id->id_msize[*rid] = count; + break; + case SYS_RES_IOPORT: + id->id_port[*rid] = rv->r_start; + id->id_portsize[*rid] = count; + break; + case SYS_RES_IRQ: + id->id_irq[*rid] = rv->r_start; + break; + case SYS_RES_DRQ: + id->id_drq[*rid] = rv->r_start; + break; + } + } + return rv; +} - dma_busy |= (1 << chan); +static int +isa_release_resource(device_t bus, device_t child, int type, int rid, + struct resource *r) +{ + int rv; + struct isa_device *id = DEVTOISA(child); - if (isa_dmarangecheck(addr, nbytes, chan)) { - if (dma_bouncebuf[chan] == NULL - || dma_bouncebufsize[chan] < nbytes) - panic("isa_dmastart: bad bounce buffer"); - dma_bounced |= (1 << chan); - newaddr = dma_bouncebuf[chan]; + if (rid > 1) + return EINVAL; - /* copy bounce buffer on write */ - if (!(flags & B_READ)) - bcopy(addr, newaddr, nbytes); - addr = newaddr; + switch (type) { + case SYS_RES_IRQ: + case SYS_RES_DRQ: + case SYS_RES_IOPORT: + case SYS_RES_MEMORY: + break; + default: + return (ENOENT); } - /* translate to physical */ - phys = pmap_extract(pmap_kernel(), (vm_offset_t)addr); + rv = BUS_RELEASE_RESOURCE(device_get_parent(bus), child, type, rid, r); - if (flags & B_RAW) { - dma_auto_mode |= (1 << chan); - } else { - dma_auto_mode &= ~(1 << chan); +#if 0 + if (rv) { + /* Kludge, isa as a child of pci doesn't have mapping regs */ + printf("WARNING: isa_release_resource: BUS_RELEASE_RESOURCE() failed: %d\n", rv); + rv = 0; } +#endif - if ((chan & 4) == 0) { - /* - * Program one of DMA channels 0..3. These are - * byte mode channels. - */ - /* set dma channel mode, and reset address ff */ + if (rv == 0) { + switch (type) { + case SYS_RES_IRQ: + id->id_irqres[rid] = 0; + id->id_irq[rid] = -1; + break; - /* If B_RAW flag is set, then use autoinitialise mode */ - if (flags & B_RAW) { - if (flags & B_READ) - outb(DMA1_MODE, DMA37MD_AUTO|DMA37MD_WRITE|chan); - else - outb(DMA1_MODE, DMA37MD_AUTO|DMA37MD_READ|chan); - } - else - if (flags & B_READ) - outb(DMA1_MODE, DMA37MD_SINGLE|DMA37MD_WRITE|chan); - else - outb(DMA1_MODE, DMA37MD_SINGLE|DMA37MD_READ|chan); - outb(DMA1_FFC, 0); + case SYS_RES_DRQ: + id->id_drqres[rid] = 0; + id->id_drq[rid] = -1; + break; - /* send start address */ - waport = DMA1_CHN(chan); - outb(waport, phys); - outb(waport, phys>>8); - outb(dmapageport[chan], phys>>16); + case SYS_RES_MEMORY: + id->id_memres[rid] = 0; + id->id_maddr[rid] = 0; + id->id_msize[rid] = 0; + break; - /* send count */ - outb(waport + 1, --nbytes); - outb(waport + 1, nbytes>>8); + case SYS_RES_IOPORT: + id->id_portres[rid] = 0; + id->id_port[rid] = 0; + id->id_portsize[rid] = 0; + break; - /* unmask channel */ - outb(DMA1_SMSK, chan); - } else { - /* - * Program one of DMA channels 4..7. These are - * word mode channels. - */ - /* set dma channel mode, and reset address ff */ - - /* If B_RAW flag is set, then use autoinitialise mode */ - if (flags & B_RAW) { - if (flags & B_READ) - outb(DMA2_MODE, DMA37MD_AUTO|DMA37MD_WRITE|(chan&3)); - else - outb(DMA2_MODE, DMA37MD_AUTO|DMA37MD_READ|(chan&3)); + default: + return ENOENT; } - else - if (flags & B_READ) - outb(DMA2_MODE, DMA37MD_SINGLE|DMA37MD_WRITE|(chan&3)); - else - outb(DMA2_MODE, DMA37MD_SINGLE|DMA37MD_READ|(chan&3)); - outb(DMA2_FFC, 0); - - /* send start address */ - waport = DMA2_CHN(chan - 4); - outb(waport, phys>>1); - outb(waport, phys>>9); - outb(dmapageport[chan], phys>>16); - - /* send count */ - nbytes >>= 1; - outb(waport + 2, --nbytes); - outb(waport + 2, nbytes>>8); - - /* unmask channel */ - outb(DMA2_SMSK, chan & 3); } -} -void -isa_dmadone(int flags, caddr_t addr, int nbytes, int chan) -{ -#ifdef DIAGNOSTIC - if (chan & ~VALID_DMA_MASK) - panic("isa_dmadone: channel out of range"); - - if ((dma_inuse & (1 << chan)) == 0) - printf("isa_dmadone: channel %d not acquired\n", chan); -#endif - - if (((dma_busy & (1 << chan)) == 0) && - (dma_auto_mode & (1 << chan)) == 0 ) - printf("isa_dmadone: channel %d not busy\n", chan); - - if ((dma_auto_mode & (1 << chan)) == 0) - outb(chan & 4 ? DMA2_SMSK : DMA1_SMSK, (chan & 3) | 4); - - if (dma_bounced & (1 << chan)) { - /* copy bounce buffer on read */ - if (flags & B_READ) - bcopy(dma_bouncebuf[chan], addr, nbytes); - - dma_bounced &= ~(1 << chan); - } - dma_busy &= ~(1 << chan); + return rv; } /* - * Check for problems with the address range of a DMA transfer - * (non-contiguous physical pages, outside of bus address space, - * crossing DMA page boundaries). - * Return true if special handling needed. + * We can't use the bus_generic_* versions of these methods because those + * methods always pass the bus param as the requesting device, and we need + * to pass the child (the i386 nexus knows about this and is prepared to + * deal). */ - static int -isa_dmarangecheck(caddr_t va, u_int length, int chan) +isa_setup_intr(device_t bus, device_t child, struct resource *r, + void (*ihand)(void *), void *arg, void **cookiep) { - vm_offset_t phys, priorpage = 0, endva; - u_int dma_pgmsk = (chan & 4) ? ~(128*1024-1) : ~(64*1024-1); - - endva = (vm_offset_t)round_page((vm_offset_t)va + length); - for (; va < (caddr_t) endva ; va += PAGE_SIZE) { - phys = trunc_page(pmap_extract(pmap_kernel(), (vm_offset_t)va)); -#define ISARAM_END RAM_END - if (phys == 0) - panic("isa_dmacheck: no physical page present"); - if (phys >= ISARAM_END) - return (1); - if (priorpage) { - if (priorpage + PAGE_SIZE != phys) - return (1); - /* check if crossing a DMA page boundary */ - if (((u_int)priorpage ^ (u_int)phys) & dma_pgmsk) - return (1); - } - priorpage = phys; - } - return (0); + return (BUS_SETUP_INTR(device_get_parent(bus), child, r, ihand, arg, + cookiep)); } -/* - * Query the progress of a transfer on a DMA channel. - * - * To avoid having to interrupt a transfer in progress, we sample - * each of the high and low databytes twice, and apply the following - * logic to determine the correct count. - * - * Reads are performed with interrupts disabled, thus it is to be - * expected that the time between reads is very small. At most - * one rollover in the low count byte can be expected within the - * four reads that are performed. - * - * There are three gaps in which a rollover can occur : - * - * - read low1 - * gap1 - * - read high1 - * gap2 - * - read low2 - * gap3 - * - read high2 - * - * If a rollover occurs in gap1 or gap2, the low2 value will be - * greater than the low1 value. In this case, low2 and high2 are a - * corresponding pair. - * - * In any other case, low1 and high1 can be considered to be correct. - * - * The function returns the number of bytes remaining in the transfer, - * or -1 if the channel requested is not active. - * - */ -int -isa_dmastatus(int chan) +static int +isa_teardown_intr(device_t bus, device_t child, struct resource *r, + void *cookie) { - u_long cnt = 0; - int ffport, waport; - u_long low1, high1, low2, high2; - - /* channel active? */ - if ((dma_inuse & (1 << chan)) == 0) { - printf("isa_dmastatus: channel %d not active\n", chan); - return(-1); - } - /* channel busy? */ - - if (((dma_busy & (1 << chan)) == 0) && - (dma_auto_mode & (1 << chan)) == 0 ) { - printf("chan %d not busy\n", chan); - return -2 ; - } - if (chan < 4) { /* low DMA controller */ - ffport = DMA1_FFC; - waport = DMA1_CHN(chan) + 1; - } else { /* high DMA controller */ - ffport = DMA2_FFC; - waport = DMA2_CHN(chan - 4) + 2; - } - - disable_intr(); /* no interrupts Mr Jones! */ - outb(ffport, 0); /* clear register LSB flipflop */ - low1 = inb(waport); - high1 = inb(waport); - outb(ffport, 0); /* clear again */ - low2 = inb(waport); - high2 = inb(waport); - enable_intr(); /* enable interrupts again */ - - /* - * Now decide if a wrap has tried to skew our results. - * Note that after TC, the count will read 0xffff, while we want - * to return zero, so we add and then mask to compensate. - */ - if (low1 >= low2) { - cnt = (low1 + (high1 << 8) + 1) & 0xffff; - } else { - cnt = (low2 + (high2 << 8) + 1) & 0xffff; - } - - if (chan >= 4) /* high channels move words */ - cnt *= 2; - return(cnt); + return (BUS_TEARDOWN_INTR(device_get_parent(bus), child, r, cookie)); } -/* - * Stop a DMA transfer currently in progress. - */ -int -isa_dmastop(int chan) -{ - if ((dma_inuse & (1 << chan)) == 0) - printf("isa_dmastop: channel %d not acquired\n", chan); +static device_method_t isa_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, isa_probe), + DEVMETHOD(device_attach, isa_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), - if (((dma_busy & (1 << chan)) == 0) && - ((dma_auto_mode & (1 << chan)) == 0)) { - printf("chan %d not busy\n", chan); - return -2 ; - } - - if ((chan & 4) == 0) { - outb(DMA1_SMSK, (chan & 3) | 4 /* disable mask */); - } else { - outb(DMA2_SMSK, (chan & 3) | 4 /* disable mask */); - } - return(isa_dmastatus(chan)); -} + /* Bus interface */ + DEVMETHOD(bus_print_child, isa_print_child), + DEVMETHOD(bus_read_ivar, isa_read_ivar), + DEVMETHOD(bus_write_ivar, isa_write_ivar), + DEVMETHOD(bus_alloc_resource, isa_alloc_resource), + DEVMETHOD(bus_release_resource, isa_release_resource), + DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), + DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), + DEVMETHOD(bus_setup_intr, isa_setup_intr), + DEVMETHOD(bus_teardown_intr, isa_teardown_intr), -/* - * Find the highest priority enabled display device. Since we can't - * distinguish display devices from ttys, depend on display devices - * being sensitive and before sensitive non-display devices (if any) - * in isa_devtab_tty. - * - * XXX we should add capability flags IAMDISPLAY and ISUPPORTCONSOLES. - */ -struct isa_device * -find_display() -{ - struct isa_device *dvp; + { 0, 0 } +}; - for (dvp = isa_devtab_tty; dvp->id_driver != NULL; dvp++) - if (dvp->id_driver->sensitive_hw && dvp->id_enabled) - return (dvp); - return (NULL); -} +static driver_t isa_driver = { + "isa", + isa_methods, + DRIVER_TYPE_MISC, + 1, /* no softc */ +}; /* - * find an ISA device in a given isa_devtab_* table, given - * the table to search, the expected id_driver entry, and the unit number. - * - * this function is defined in isa_device.h, and this location is debatable; - * i put it there because it's useless w/o, and directly operates on - * the other stuff in that file. - * + * ISA can be attached to a PCI-ISA bridge or directly to the nexus. */ - -struct isa_device * -find_isadev(table, driverp, unit) - struct isa_device *table; - struct isa_driver *driverp; - int unit; -{ - if (driverp == NULL) /* sanity check */ - return (NULL); - - while ((table->id_driver != driverp) || (table->id_unit != unit)) { - if (table->id_driver == 0) - return NULL; - - table++; - } - - return (table); -} +DRIVER_MODULE(isa, isab, isa_driver, isa_devclass, 0, 0); +DRIVER_MODULE(isa, nexus, isa_driver, isa_devclass, 0, 0); Index: head/sys/amd64/isa/isa_dma.c =================================================================== --- head/sys/amd64/isa/isa_dma.c (nonexistent) +++ head/sys/amd64/isa/isa_dma.c (revision 45720) @@ -0,0 +1,510 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * William Jolitz. + * + * 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: @(#)isa.c 7.2 (Berkeley) 5/13/91 + * $Id: isa.c,v 1.117 1998/11/29 15:42:40 phk Exp $ + */ + +/* + * code to manage AT bus + * + * 92/08/18 Frank P. MacLachlan (fpm@crash.cts.com): + * Fixed uninitialized variable problem and added code to deal + * with DMA page boundaries in isa_dmarangecheck(). Fixed word + * mode DMA count compution and reorganized DMA setup code in + * isa_dmastart() + */ + +#include +#include +#include +#include +#include +#include +#ifdef APIC_IO +#include +#endif /* APIC_IO */ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "pnp.h" +#if NPNP > 0 +#include +#endif + +/* +** Register definitions for DMA controller 1 (channels 0..3): +*/ +#define DMA1_CHN(c) (IO_DMA1 + 1*(2*(c))) /* addr reg for channel c */ +#define DMA1_SMSK (IO_DMA1 + 1*10) /* single mask register */ +#define DMA1_MODE (IO_DMA1 + 1*11) /* mode register */ +#define DMA1_FFC (IO_DMA1 + 1*12) /* clear first/last FF */ + +/* +** Register definitions for DMA controller 2 (channels 4..7): +*/ +#define DMA2_CHN(c) (IO_DMA2 + 2*(2*(c))) /* addr reg for channel c */ +#define DMA2_SMSK (IO_DMA2 + 2*10) /* single mask register */ +#define DMA2_MODE (IO_DMA2 + 2*11) /* mode register */ +#define DMA2_FFC (IO_DMA2 + 2*12) /* clear first/last FF */ + +static int isa_dmarangecheck __P((caddr_t va, u_int length, int chan)); + +static caddr_t dma_bouncebuf[8]; +static u_int dma_bouncebufsize[8]; +static u_int8_t dma_bounced = 0; +static u_int8_t dma_busy = 0; /* Used in isa_dmastart() */ +static u_int8_t dma_inuse = 0; /* User for acquire/release */ +static u_int8_t dma_auto_mode = 0; + +#define VALID_DMA_MASK (7) + +/* high byte of address is stored in this port for i-th dma channel */ +static int dmapageport[8] = { 0x87, 0x83, 0x81, 0x82, 0x8f, 0x8b, 0x89, 0x8a }; + +/* + * Setup a DMA channel's bounce buffer. + */ +void +isa_dmainit(chan, bouncebufsize) + int chan; + u_int bouncebufsize; +{ + void *buf; + +#ifdef DIAGNOSTIC + if (chan & ~VALID_DMA_MASK) + panic("isa_dmainit: channel out of range"); + + if (dma_bouncebuf[chan] != NULL) + panic("isa_dmainit: impossible request"); +#endif + + dma_bouncebufsize[chan] = bouncebufsize; + + /* Try malloc() first. It works better if it works. */ + buf = malloc(bouncebufsize, M_DEVBUF, M_NOWAIT); + if (buf != NULL) { + if (isa_dmarangecheck(buf, bouncebufsize, chan) == 0) { + dma_bouncebuf[chan] = buf; + return; + } + free(buf, M_DEVBUF); + } + buf = contigmalloc(bouncebufsize, M_DEVBUF, M_NOWAIT, 0ul, 0xfffffful, + 1ul, chan & 4 ? 0x20000ul : 0x10000ul); + if (buf == NULL) + printf("isa_dmainit(%d, %d) failed\n", chan, bouncebufsize); + else + dma_bouncebuf[chan] = buf; +} + +/* + * Register a DMA channel's usage. Usually called from a device driver + * in open() or during its initialization. + */ +int +isa_dma_acquire(chan) + int chan; +{ +#ifdef DIAGNOSTIC + if (chan & ~VALID_DMA_MASK) + panic("isa_dma_acquire: channel out of range"); +#endif + + if (dma_inuse & (1 << chan)) { + printf("isa_dma_acquire: channel %d already in use\n", chan); + return (EBUSY); + } + dma_inuse |= (1 << chan); + dma_auto_mode &= ~(1 << chan); + + return (0); +} + +/* + * Unregister a DMA channel's usage. Usually called from a device driver + * during close() or during its shutdown. + */ +void +isa_dma_release(chan) + int chan; +{ +#ifdef DIAGNOSTIC + if (chan & ~VALID_DMA_MASK) + panic("isa_dma_release: channel out of range"); + + if ((dma_inuse & (1 << chan)) == 0) + printf("isa_dma_release: channel %d not in use\n", chan); +#endif + + if (dma_busy & (1 << chan)) { + dma_busy &= ~(1 << chan); + /* + * XXX We should also do "dma_bounced &= (1 << chan);" + * because we are acting on behalf of isa_dmadone() which + * was not called to end the last DMA operation. This does + * not matter now, but it may in the future. + */ + } + + dma_inuse &= ~(1 << chan); + dma_auto_mode &= ~(1 << chan); +} + +/* + * isa_dmacascade(): program 8237 DMA controller channel to accept + * external dma control by a board. + */ +void +isa_dmacascade(chan) + int chan; +{ +#ifdef DIAGNOSTIC + if (chan & ~VALID_DMA_MASK) + panic("isa_dmacascade: channel out of range"); +#endif + + /* set dma channel mode, and set dma channel mode */ + if ((chan & 4) == 0) { + outb(DMA1_MODE, DMA37MD_CASCADE | chan); + outb(DMA1_SMSK, chan); + } else { + outb(DMA2_MODE, DMA37MD_CASCADE | (chan & 3)); + outb(DMA2_SMSK, chan & 3); + } +} + +/* + * isa_dmastart(): program 8237 DMA controller channel, avoid page alignment + * problems by using a bounce buffer. + */ +void +isa_dmastart(int flags, caddr_t addr, u_int nbytes, int chan) +{ + vm_offset_t phys; + int waport; + caddr_t newaddr; + +#ifdef DIAGNOSTIC + if (chan & ~VALID_DMA_MASK) + panic("isa_dmastart: channel out of range"); + + if ((chan < 4 && nbytes > (1<<16)) + || (chan >= 4 && (nbytes > (1<<17) || (u_int)addr & 1))) + panic("isa_dmastart: impossible request"); + + if ((dma_inuse & (1 << chan)) == 0) + printf("isa_dmastart: channel %d not acquired\n", chan); +#endif + +#if 0 + /* + * XXX This should be checked, but drivers like ad1848 only call + * isa_dmastart() once because they use Auto DMA mode. If we + * leave this in, drivers that do this will print this continuously. + */ + if (dma_busy & (1 << chan)) + printf("isa_dmastart: channel %d busy\n", chan); +#endif + + dma_busy |= (1 << chan); + + if (isa_dmarangecheck(addr, nbytes, chan)) { + if (dma_bouncebuf[chan] == NULL + || dma_bouncebufsize[chan] < nbytes) + panic("isa_dmastart: bad bounce buffer"); + dma_bounced |= (1 << chan); + newaddr = dma_bouncebuf[chan]; + + /* copy bounce buffer on write */ + if (!(flags & B_READ)) + bcopy(addr, newaddr, nbytes); + addr = newaddr; + } + + /* translate to physical */ + phys = pmap_extract(pmap_kernel(), (vm_offset_t)addr); + + if (flags & B_RAW) { + dma_auto_mode |= (1 << chan); + } else { + dma_auto_mode &= ~(1 << chan); + } + + if ((chan & 4) == 0) { + /* + * Program one of DMA channels 0..3. These are + * byte mode channels. + */ + /* set dma channel mode, and reset address ff */ + + /* If B_RAW flag is set, then use autoinitialise mode */ + if (flags & B_RAW) { + if (flags & B_READ) + outb(DMA1_MODE, DMA37MD_AUTO|DMA37MD_WRITE|chan); + else + outb(DMA1_MODE, DMA37MD_AUTO|DMA37MD_READ|chan); + } + else + if (flags & B_READ) + outb(DMA1_MODE, DMA37MD_SINGLE|DMA37MD_WRITE|chan); + else + outb(DMA1_MODE, DMA37MD_SINGLE|DMA37MD_READ|chan); + outb(DMA1_FFC, 0); + + /* send start address */ + waport = DMA1_CHN(chan); + outb(waport, phys); + outb(waport, phys>>8); + outb(dmapageport[chan], phys>>16); + + /* send count */ + outb(waport + 1, --nbytes); + outb(waport + 1, nbytes>>8); + + /* unmask channel */ + outb(DMA1_SMSK, chan); + } else { + /* + * Program one of DMA channels 4..7. These are + * word mode channels. + */ + /* set dma channel mode, and reset address ff */ + + /* If B_RAW flag is set, then use autoinitialise mode */ + if (flags & B_RAW) { + if (flags & B_READ) + outb(DMA2_MODE, DMA37MD_AUTO|DMA37MD_WRITE|(chan&3)); + else + outb(DMA2_MODE, DMA37MD_AUTO|DMA37MD_READ|(chan&3)); + } + else + if (flags & B_READ) + outb(DMA2_MODE, DMA37MD_SINGLE|DMA37MD_WRITE|(chan&3)); + else + outb(DMA2_MODE, DMA37MD_SINGLE|DMA37MD_READ|(chan&3)); + outb(DMA2_FFC, 0); + + /* send start address */ + waport = DMA2_CHN(chan - 4); + outb(waport, phys>>1); + outb(waport, phys>>9); + outb(dmapageport[chan], phys>>16); + + /* send count */ + nbytes >>= 1; + outb(waport + 2, --nbytes); + outb(waport + 2, nbytes>>8); + + /* unmask channel */ + outb(DMA2_SMSK, chan & 3); + } +} + +void +isa_dmadone(int flags, caddr_t addr, int nbytes, int chan) +{ +#ifdef DIAGNOSTIC + if (chan & ~VALID_DMA_MASK) + panic("isa_dmadone: channel out of range"); + + if ((dma_inuse & (1 << chan)) == 0) + printf("isa_dmadone: channel %d not acquired\n", chan); +#endif + + if (((dma_busy & (1 << chan)) == 0) && + (dma_auto_mode & (1 << chan)) == 0 ) + printf("isa_dmadone: channel %d not busy\n", chan); + + if ((dma_auto_mode & (1 << chan)) == 0) + outb(chan & 4 ? DMA2_SMSK : DMA1_SMSK, (chan & 3) | 4); + + if (dma_bounced & (1 << chan)) { + /* copy bounce buffer on read */ + if (flags & B_READ) + bcopy(dma_bouncebuf[chan], addr, nbytes); + + dma_bounced &= ~(1 << chan); + } + dma_busy &= ~(1 << chan); +} + +/* + * Check for problems with the address range of a DMA transfer + * (non-contiguous physical pages, outside of bus address space, + * crossing DMA page boundaries). + * Return true if special handling needed. + */ + +static int +isa_dmarangecheck(caddr_t va, u_int length, int chan) +{ + vm_offset_t phys, priorpage = 0, endva; + u_int dma_pgmsk = (chan & 4) ? ~(128*1024-1) : ~(64*1024-1); + + endva = (vm_offset_t)round_page((vm_offset_t)va + length); + for (; va < (caddr_t) endva ; va += PAGE_SIZE) { + phys = trunc_page(pmap_extract(pmap_kernel(), (vm_offset_t)va)); +#define ISARAM_END RAM_END + if (phys == 0) + panic("isa_dmacheck: no physical page present"); + if (phys >= ISARAM_END) + return (1); + if (priorpage) { + if (priorpage + PAGE_SIZE != phys) + return (1); + /* check if crossing a DMA page boundary */ + if (((u_int)priorpage ^ (u_int)phys) & dma_pgmsk) + return (1); + } + priorpage = phys; + } + return (0); +} + +/* + * Query the progress of a transfer on a DMA channel. + * + * To avoid having to interrupt a transfer in progress, we sample + * each of the high and low databytes twice, and apply the following + * logic to determine the correct count. + * + * Reads are performed with interrupts disabled, thus it is to be + * expected that the time between reads is very small. At most + * one rollover in the low count byte can be expected within the + * four reads that are performed. + * + * There are three gaps in which a rollover can occur : + * + * - read low1 + * gap1 + * - read high1 + * gap2 + * - read low2 + * gap3 + * - read high2 + * + * If a rollover occurs in gap1 or gap2, the low2 value will be + * greater than the low1 value. In this case, low2 and high2 are a + * corresponding pair. + * + * In any other case, low1 and high1 can be considered to be correct. + * + * The function returns the number of bytes remaining in the transfer, + * or -1 if the channel requested is not active. + * + */ +int +isa_dmastatus(int chan) +{ + u_long cnt = 0; + int ffport, waport; + u_long low1, high1, low2, high2; + + /* channel active? */ + if ((dma_inuse & (1 << chan)) == 0) { + printf("isa_dmastatus: channel %d not active\n", chan); + return(-1); + } + /* channel busy? */ + + if (((dma_busy & (1 << chan)) == 0) && + (dma_auto_mode & (1 << chan)) == 0 ) { + printf("chan %d not busy\n", chan); + return -2 ; + } + if (chan < 4) { /* low DMA controller */ + ffport = DMA1_FFC; + waport = DMA1_CHN(chan) + 1; + } else { /* high DMA controller */ + ffport = DMA2_FFC; + waport = DMA2_CHN(chan - 4) + 2; + } + + disable_intr(); /* no interrupts Mr Jones! */ + outb(ffport, 0); /* clear register LSB flipflop */ + low1 = inb(waport); + high1 = inb(waport); + outb(ffport, 0); /* clear again */ + low2 = inb(waport); + high2 = inb(waport); + enable_intr(); /* enable interrupts again */ + + /* + * Now decide if a wrap has tried to skew our results. + * Note that after TC, the count will read 0xffff, while we want + * to return zero, so we add and then mask to compensate. + */ + if (low1 >= low2) { + cnt = (low1 + (high1 << 8) + 1) & 0xffff; + } else { + cnt = (low2 + (high2 << 8) + 1) & 0xffff; + } + + if (chan >= 4) /* high channels move words */ + cnt *= 2; + return(cnt); +} + +/* + * Stop a DMA transfer currently in progress. + */ +int +isa_dmastop(int chan) +{ + if ((dma_inuse & (1 << chan)) == 0) + printf("isa_dmastop: channel %d not acquired\n", chan); + + if (((dma_busy & (1 << chan)) == 0) && + ((dma_auto_mode & (1 << chan)) == 0)) { + printf("chan %d not busy\n", chan); + return -2 ; + } + + if ((chan & 4) == 0) { + outb(DMA1_SMSK, (chan & 3) | 4 /* disable mask */); + } else { + outb(DMA2_SMSK, (chan & 3) | 4 /* disable mask */); + } + return(isa_dmastatus(chan)); +} Property changes on: head/sys/amd64/isa/isa_dma.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: head/sys/amd64/isa/isa_dma.h =================================================================== --- head/sys/amd64/isa/isa_dma.h (nonexistent) +++ head/sys/amd64/isa/isa_dma.h (revision 45720) @@ -0,0 +1,51 @@ +/*- + * 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: @(#)isa_device.h 7.1 (Berkeley) 5/9/91 + * $Id: isa_device.h,v 1.57 1999/01/17 06:33:43 bde Exp $ + */ + +#ifndef _I386_ISA_ISA_DMA_H_ +#define _I386_ISA_ISA_DMA_H_ + +#ifdef KERNEL +void isa_dmacascade __P((int chan)); +void isa_dmadone __P((int flags, caddr_t addr, int nbytes, int chan)); +void isa_dmainit __P((int chan, u_int bouncebufsize)); +void isa_dmastart __P((int flags, caddr_t addr, u_int nbytes, int chan)); +int isa_dma_acquire __P((int chan)); +void isa_dma_release __P((int chan)); +int isa_dmastatus __P((int chan)); +int isa_dmastop __P((int chan)); +#endif /* KERNEL */ + +#endif /* !_I386_ISA_ISA_DMA_H_ */ Property changes on: head/sys/amd64/isa/isa_dma.h ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: head/sys/amd64/isa/nmi.c =================================================================== --- head/sys/amd64/isa/nmi.c (revision 45719) +++ head/sys/amd64/isa/nmi.c (revision 45720) @@ -1,516 +1,518 @@ /*- * Copyright (c) 1991 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * William Jolitz. * * 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: @(#)isa.c 7.2 (Berkeley) 5/13/91 - * $Id: intr_machdep.c,v 1.16 1999/01/08 19:17:48 bde Exp $ + * $Id: intr_machdep.c,v 1.17 1999/04/14 14:26:36 bde Exp $ */ #include "opt_auto_eoi.h" #include #ifndef SMP #include #endif #include #include #include #include #include #if defined(APIC_IO) #include #include /** FAST_HI */ #endif /* APIC_IO */ #include #ifdef PC98 #include #include #include #else #include #endif #include #include #include #ifdef APIC_IO #include #endif /* XXX should be in suitable include files */ #ifdef PC98 #define ICU_IMR_OFFSET 2 /* IO_ICU{1,2} + 2 */ #define ICU_SLAVEID 7 #else #define ICU_IMR_OFFSET 1 /* IO_ICU{1,2} + 1 */ #define ICU_SLAVEID 2 #endif #ifdef APIC_IO /* * This is to accommodate "mixed-mode" programming for * motherboards that don't connect the 8254 to the IO APIC. */ #define AUTO_EOI_1 1 #endif #define NR_INTRNAMES (1 + ICU_LEN + 2 * ICU_LEN) u_long *intr_countp[ICU_LEN]; inthand2_t *intr_handler[ICU_LEN]; u_int intr_mask[ICU_LEN]; static u_int* intr_mptr[ICU_LEN]; void *intr_unit[ICU_LEN]; static inthand_t *fastintr[ICU_LEN] = { &IDTVEC(fastintr0), &IDTVEC(fastintr1), &IDTVEC(fastintr2), &IDTVEC(fastintr3), &IDTVEC(fastintr4), &IDTVEC(fastintr5), &IDTVEC(fastintr6), &IDTVEC(fastintr7), &IDTVEC(fastintr8), &IDTVEC(fastintr9), &IDTVEC(fastintr10), &IDTVEC(fastintr11), &IDTVEC(fastintr12), &IDTVEC(fastintr13), &IDTVEC(fastintr14), &IDTVEC(fastintr15) #if defined(APIC_IO) , &IDTVEC(fastintr16), &IDTVEC(fastintr17), &IDTVEC(fastintr18), &IDTVEC(fastintr19), &IDTVEC(fastintr20), &IDTVEC(fastintr21), &IDTVEC(fastintr22), &IDTVEC(fastintr23) #endif /* APIC_IO */ }; static inthand_t *slowintr[ICU_LEN] = { &IDTVEC(intr0), &IDTVEC(intr1), &IDTVEC(intr2), &IDTVEC(intr3), &IDTVEC(intr4), &IDTVEC(intr5), &IDTVEC(intr6), &IDTVEC(intr7), &IDTVEC(intr8), &IDTVEC(intr9), &IDTVEC(intr10), &IDTVEC(intr11), &IDTVEC(intr12), &IDTVEC(intr13), &IDTVEC(intr14), &IDTVEC(intr15) #if defined(APIC_IO) , &IDTVEC(intr16), &IDTVEC(intr17), &IDTVEC(intr18), &IDTVEC(intr19), &IDTVEC(intr20), &IDTVEC(intr21), &IDTVEC(intr22), &IDTVEC(intr23) #endif /* APIC_IO */ }; static inthand2_t isa_strayintr; #ifdef PC98 #define NMI_PARITY 0x04 #define NMI_EPARITY 0x02 #else #define NMI_PARITY (1 << 7) #define NMI_IOCHAN (1 << 6) #define ENMI_WATCHDOG (1 << 7) #define ENMI_BUSTIMER (1 << 6) #define ENMI_IOSTATUS (1 << 5) #endif /* * Handle a NMI, possibly a machine check. * return true to panic system, false to ignore. */ int isa_nmi(cd) int cd; { #ifdef PC98 int port = inb(0x33); if (epson_machine_id == 0x20) epson_outb(0xc16, epson_inb(0xc16) | 0x1); if (port & NMI_PARITY) { panic("BASE RAM parity error, likely hardware failure."); } else if (port & NMI_EPARITY) { panic("EXTENDED RAM parity error, likely hardware failure."); } else { printf("\nNMI Resume ??\n"); return(0); } #else /* IBM-PC */ int isa_port = inb(0x61); int eisa_port = inb(0x461); if (isa_port & NMI_PARITY) panic("RAM parity error, likely hardware failure."); if (isa_port & NMI_IOCHAN) panic("I/O channel check, likely hardware failure."); /* * On a real EISA machine, this will never happen. However it can * happen on ISA machines which implement XT style floating point * error handling (very rare). Save them from a meaningless panic. */ if (eisa_port == 0xff) return(0); if (eisa_port & ENMI_WATCHDOG) panic("EISA watchdog timer expired, likely hardware failure."); if (eisa_port & ENMI_BUSTIMER) panic("EISA bus timeout, likely hardware failure."); if (eisa_port & ENMI_IOSTATUS) panic("EISA I/O port status error."); printf("\nNMI ISA %x, EISA %x\n", isa_port, eisa_port); return(0); #endif } /* * Fill in default interrupt table (in case of spuruious interrupt * during configuration of kernel, setup interrupt control unit */ void isa_defaultirq() { int i; /* icu vectors */ for (i = 0; i < ICU_LEN; i++) icu_unset(i, (inthand2_t *)NULL); /* initialize 8259's */ outb(IO_ICU1, 0x11); /* reset; program device, four bytes */ outb(IO_ICU1+ICU_IMR_OFFSET, NRSVIDT); /* starting at this vector index */ outb(IO_ICU1+ICU_IMR_OFFSET, IRQ_SLAVE); /* slave on line 7 */ #ifdef PC98 #ifdef AUTO_EOI_1 outb(IO_ICU1+ICU_IMR_OFFSET, 0x1f); /* (master) auto EOI, 8086 mode */ #else outb(IO_ICU1+ICU_IMR_OFFSET, 0x1d); /* (master) 8086 mode */ #endif #else /* IBM-PC */ #ifdef AUTO_EOI_1 outb(IO_ICU1+ICU_IMR_OFFSET, 2 | 1); /* auto EOI, 8086 mode */ #else outb(IO_ICU1+ICU_IMR_OFFSET, 1); /* 8086 mode */ #endif #endif /* PC98 */ outb(IO_ICU1+ICU_IMR_OFFSET, 0xff); /* leave interrupts masked */ outb(IO_ICU1, 0x0a); /* default to IRR on read */ #ifndef PC98 outb(IO_ICU1, 0xc0 | (3 - 1)); /* pri order 3-7, 0-2 (com2 first) */ #endif /* !PC98 */ outb(IO_ICU2, 0x11); /* reset; program device, four bytes */ outb(IO_ICU2+ICU_IMR_OFFSET, NRSVIDT+8); /* staring at this vector index */ outb(IO_ICU2+ICU_IMR_OFFSET, ICU_SLAVEID); /* my slave id is 7 */ #ifdef PC98 outb(IO_ICU2+ICU_IMR_OFFSET,9); /* 8086 mode */ #else /* IBM-PC */ #ifdef AUTO_EOI_2 outb(IO_ICU2+ICU_IMR_OFFSET, 2 | 1); /* auto EOI, 8086 mode */ #else outb(IO_ICU2+ICU_IMR_OFFSET,1); /* 8086 mode */ #endif #endif /* PC98 */ outb(IO_ICU2+ICU_IMR_OFFSET, 0xff); /* leave interrupts masked */ outb(IO_ICU2, 0x0a); /* default to IRR on read */ } /* * Caught a stray interrupt, notify */ static void isa_strayintr(vcookiep) void *vcookiep; { int intr = (void **)vcookiep - &intr_unit[0]; /* DON'T BOTHER FOR NOW! */ /* for some reason, we get bursts of intr #7, even if not enabled! */ /* * Well the reason you got bursts of intr #7 is because someone * raised an interrupt line and dropped it before the 8259 could * prioritize it. This is documented in the intel data book. This * means you have BAD hardware! I have changed this so that only * the first 5 get logged, then it quits logging them, and puts * out a special message. rgrimes 3/25/1993 */ /* * XXX TODO print a different message for #7 if it is for a * glitch. Glitches can be distinguished from real #7's by * testing that the in-service bit is _not_ set. The test * must be done before sending an EOI so it can't be done if * we are using AUTO_EOI_1. */ if (intrcnt[1 + intr] <= 5) log(LOG_ERR, "stray irq %d\n", intr); if (intrcnt[1 + intr] == 5) log(LOG_CRIT, "too many stray irq %d's; not logging any more\n", intr); } /* * Return a bitmap of the current interrupt requests. This is 8259-specific * and is only suitable for use at probe time. */ intrmask_t isa_irq_pending() { u_char irr1; u_char irr2; irr1 = inb(IO_ICU1); irr2 = inb(IO_ICU2); return ((irr2 << 8) | irr1); } int update_intr_masks(void) { int intr, n=0; u_int mask,*maskptr; for (intr=0; intr < ICU_LEN; intr ++) { #if defined(APIC_IO) /* no 8259 SLAVE to ignore */ #else if (intr==ICU_SLAVEID) continue; /* ignore 8259 SLAVE output */ #endif /* APIC_IO */ maskptr = intr_mptr[intr]; if (!maskptr) continue; *maskptr |= 1 << intr; mask = *maskptr; if (mask != intr_mask[intr]) { #if 0 printf ("intr_mask[%2d] old=%08x new=%08x ptr=%p.\n", intr, intr_mask[intr], mask, maskptr); #endif intr_mask[intr]=mask; n++; } } return (n); } static const char * isa_get_nameunit(int id) { static char buf[32]; struct isa_device *dp; if (id == -1) return ("pci"); /* XXX may also be eisa */ if (id == 0) return ("clk0"); /* XXX may also be sloppy driver */ if (id == 1) return ("rtc0"); +#if 0 for (dp = isa_devtab_bio; dp->id_driver != NULL; dp++) if (dp->id_id == id) goto found_device; for (dp = isa_devtab_cam; dp->id_driver != NULL; dp++) if (dp->id_id == id) goto found_device; for (dp = isa_devtab_net; dp->id_driver != NULL; dp++) if (dp->id_id == id) goto found_device; for (dp = isa_devtab_null; dp->id_driver != NULL; dp++) if (dp->id_id == id) goto found_device; for (dp = isa_devtab_tty; dp->id_driver != NULL; dp++) if (dp->id_id == id) goto found_device; +#endif return "???"; found_device: snprintf(buf, sizeof(buf), "%s%d", dp->id_driver->name, dp->id_unit); return (buf); } void update_intrname(int intr, int device_id) { char buf[32]; char *cp; const char *name; int name_index, off, strayintr; /* * Initialise strings for bitbucket and stray interrupt counters. * These have statically allocated indices 0 and 1 through ICU_LEN. */ if (intrnames[0] == '\0') { off = sprintf(intrnames, "???") + 1; for (strayintr = 0; strayintr < ICU_LEN; strayintr++) off += sprintf(intrnames + off, "stray irq%d", strayintr) + 1; } name = isa_get_nameunit(device_id); if (snprintf(buf, sizeof(buf), "%s irq%d", name, intr) >= sizeof(buf)) goto use_bitbucket; /* * Search for `buf' in `intrnames'. In the usual case when it is * not found, append it to the end if there is enough space (the \0 * terminator for the previous string, if any, becomes a separator). */ for (cp = intrnames, name_index = 0; cp != eintrnames && name_index < NR_INTRNAMES; cp += strlen(cp) + 1, name_index++) { if (*cp == '\0') { if (strlen(buf) >= eintrnames - cp) break; strcpy(cp, buf); goto found; } if (strcmp(cp, buf) == 0) goto found; } use_bitbucket: printf("update_intrname: counting %s irq%d as %s\n", name, intr, intrnames); name_index = 0; found: intr_countp[intr] = &intrcnt[name_index]; } int icu_setup(int intr, inthand2_t *handler, void *arg, u_int *maskptr, int flags) { #ifdef FAST_HI int select; /* the select register is 8 bits */ int vector; u_int32_t value; /* the window register is 32 bits */ #endif /* FAST_HI */ u_long ef; u_int mask = (maskptr ? *maskptr : 0); #if defined(APIC_IO) if ((u_int)intr >= ICU_LEN) /* no 8259 SLAVE to ignore */ #else if ((u_int)intr >= ICU_LEN || intr == ICU_SLAVEID) #endif /* APIC_IO */ if (intr_handler[intr] != isa_strayintr) return (EBUSY); ef = read_eflags(); disable_intr(); intr_handler[intr] = handler; intr_mptr[intr] = maskptr; intr_mask[intr] = mask | (1 << intr); intr_unit[intr] = arg; #ifdef FAST_HI if (flags & INTR_FAST) { vector = TPR_FAST_INTS + intr; setidt(vector, fastintr[intr], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); } else { vector = TPR_SLOW_INTS + intr; #ifdef APIC_INTR_REORDER #ifdef APIC_INTR_HIGHPRI_CLOCK /* XXX: Hack (kludge?) for more accurate clock. */ if (intr == apic_8254_intr || intr == 8) { vector = TPR_FAST_INTS + intr; } #endif #endif setidt(vector, slowintr[intr], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); } #ifdef APIC_INTR_REORDER set_lapic_isrloc(intr, vector); #endif /* * Reprogram the vector in the IO APIC. */ if (int_to_apicintpin[intr].ioapic >= 0) { select = int_to_apicintpin[intr].redirindex; value = io_apic_read(int_to_apicintpin[intr].ioapic, select) & ~IOART_INTVEC; io_apic_write(int_to_apicintpin[intr].ioapic, select, value | vector); } #else setidt(ICU_OFFSET + intr, flags & INTR_FAST ? fastintr[intr] : slowintr[intr], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); #endif /* FAST_HI */ INTREN(1 << intr); MPINTR_UNLOCK(); write_eflags(ef); return (0); } void register_imask(dvp, mask) struct isa_device *dvp; u_int mask; { if (dvp->id_alive && dvp->id_irq) { int intr; intr = ffs(dvp->id_irq) - 1; intr_mask[intr] = mask | (1 <= ICU_LEN || handler != intr_handler[intr]) return (EINVAL); INTRDIS(1 << intr); ef = read_eflags(); disable_intr(); intr_countp[intr] = &intrcnt[1 + intr]; intr_handler[intr] = isa_strayintr; intr_mptr[intr] = NULL; intr_mask[intr] = HWI_MASK | SWI_MASK; intr_unit[intr] = &intr_unit[intr]; #ifdef FAST_HI_XXX /* XXX how do I re-create dvp here? */ setidt(flags & INTR_FAST ? TPR_FAST_INTS + intr : TPR_SLOW_INTS + intr, slowintr[intr], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); #else /* FAST_HI */ #ifdef APIC_INTR_REORDER set_lapic_isrloc(intr, ICU_OFFSET + intr); #endif setidt(ICU_OFFSET + intr, slowintr[intr], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); #endif /* FAST_HI */ MPINTR_UNLOCK(); write_eflags(ef); return (0); } Index: head/sys/amd64/isa/npx.c =================================================================== --- head/sys/amd64/isa/npx.c (revision 45719) +++ head/sys/amd64/isa/npx.c (revision 45720) @@ -1,689 +1,730 @@ /*- * 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 - * $Id: npx.c,v 1.65 1999/01/08 16:29:59 bde Exp $ + * $Id: npx.c,v 1.66 1999/03/28 23:28:18 dt Exp $ */ #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 -#include /* * 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) -/* XXX - should be in header file. */ -ointhand2_t npxintr; - #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 npxattach __P((struct isa_device *dvp)); -static int npxprobe __P((struct isa_device *dvp)); -static int npxprobe1 __P((struct isa_device *dvp)); +static int npx_attach __P((device_t dev)); + void npx_intr __P((void *)); +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 */ -struct isa_driver npxdriver = { - npxprobe, npxattach, "npx", -}; - 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; #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\ " __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\ " __XSTRING(CNAME(probetrap)) ": \n\ ss \n\ incl " __XSTRING(CNAME(npx_traps_while_probing)) " \n\ fnclex \n\ iret \n\ "); #endif /* SMP */ /* * 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 -npxprobe(dvp) - struct isa_device *dvp; +npx_probe(dev) + device_t dev; { -#ifdef SMP +/*#ifdef SMP*/ +#if 1 - return npxprobe1(dvp); + 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. */ - npx_intrno = NRSVIDT + ffs(dvp->id_irq) - 1; + npx_intrno = NRSVIDT + 13; 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 | dvp->id_irq)); - outb(IO_ICU2 + 1, ~(dvp->id_irq >> 8)); + outb(IO_ICU1 + 1, ~IRQ_SLAVE); + outb(IO_ICU2 + 1, ~(1 << (13 - 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 = npxprobe1(dvp); + 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 -npxprobe1(dvp) - struct isa_device *dvp; +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 - +/*#ifdef SMP*/ +#if 1 /* * Exception 16 MUST work for SMP. */ npx_irq13 = 0; npx_ex16 = hw_float = npx_exists = 1; - dvp->id_irq = 0; /* zap the interrupt */ - /* - * special return value to flag that we do not - * actually use any I/O registers - */ - return (-1); + device_set_desc(dev, "math processor"); + return (0); -#else /* SMP */ +#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; - dvp->id_irq = 0; /* zap the interrupt */ - /* - * special return value to flag that we do not - * actually use any I/O registers - */ - return (-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 |= dvp->id_irq; - return (IO_NPXSIZE); + npx0_imask |= (1 << 13); + + /* + * 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, 13, 13, + 1, RF_ACTIVE); + if (r == 0) + panic("npx: can't get IRQ"); + BUS_SETUP_INTR(device_get_parent(dev), + dev, r, 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. */ - dvp->id_irq = 0; - /* - * special return value to flag that we do not - * actually use any I/O registers - */ - return (-1); - + return (0); #endif /* SMP */ } /* * Attach routine - announce which it is, and wire into system */ int -npxattach(dvp) - struct isa_device *dvp; +npx_attach(dev) + device_t dev; { - dvp->id_ointr = npxintr; + int flags; - /* The caller has printed "irq 13" for the npx_irq13 case. */ - if (!npx_irq13) { - printf("npx%d: ", dvp->id_unit); + device_print_prettyname(dev); + if (npx_irq13) { + printf("using IRQ 13 interface\n"); + } else { if (npx_ex16) printf("INT 16 interface\n"); #if defined(MATH_EMULATE) || defined(GPL_MATH_EMULATE) else if (npx_exists) { printf("error reporting broken; using 387 emulator\n"); hw_float = npx_exists = 0; } else printf("387 emulator\n"); #else else printf("no 387 emulator in kernel!\n"); #endif } npxinit(__INITIAL_NPXCW__); #ifdef I586_CPU + if (resource_int_value("npx", 0, "flags", &flags) != 0) + flags = 0; + if (cpu_class == CPUCLASS_586 && npx_ex16 && timezero("i586_bzero()", i586_bzero) < timezero("bzero()", bzero) * 4 / 5) { - if (!(dvp->id_flags & NPX_DISABLE_I586_OPTIMIZED_BCOPY)) { + if (!(flags & NPX_DISABLE_I586_OPTIMIZED_BCOPY)) { bcopy_vector = i586_bcopy; ovbcopy_vector = i586_bcopy; } - if (!(dvp->id_flags & NPX_DISABLE_I586_OPTIMIZED_BZERO)) + if (!(flags & NPX_DISABLE_I586_OPTIMIZED_BZERO)) bzero = i586_bzero; - if (!(dvp->id_flags & NPX_DISABLE_I586_OPTIMIZED_COPYIO)) { + if (!(flags & NPX_DISABLE_I586_OPTIMIZED_COPYIO)) { copyin_vector = i586_copyin; copyout_vector = i586_copyout; } } #endif - return (1); /* XXX unused */ + 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 } /* * 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. * * XXX there is no standard way to tell SIGFPE handlers about the error * state. The old interface: * * void handler(int sig, int code, struct sigcontext *scp); * * is broken because it is non-ANSI and because the FP state is not in * struct sigcontext. * * 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 -npxintr(unit) - int unit; +npx_intr(dummy) + void *dummy; { int code; 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); fnclex(); /* * Pass exception to process. */ - frame = (struct intrframe *)&unit; /* XXX */ + 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 = (struct trapframe *)&frame->if_es; #ifdef notyet /* * Encode the appropriate code for detailed information on * this exception. */ code = XXX_ENCODE(curpcb->pcb_savefpu.sv_ex_sw); #else code = 0; /* XXX */ #endif 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_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, + DRIVER_TYPE_MISC, + 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 */ Index: head/sys/boot/alpha/boot1/boot1.c =================================================================== --- head/sys/boot/alpha/boot1/boot1.c (revision 45719) +++ head/sys/boot/alpha/boot1/boot1.c (revision 45720) @@ -1,188 +1,236 @@ /* - * $Id: boot1.c,v 1.2 1998/09/26 10:51:36 dfr Exp $ + * $Id: boot1.c,v 1.3 1998/10/18 19:05:07 dfr Exp $ * From $NetBSD: bootxx.c,v 1.4 1997/09/06 14:08:29 drochner Exp $ */ /* * Copyright (c) 1995 Carnegie-Mellon University. * All rights reserved. * * Author: Chris G. Demetriou * * Permission to use, copy, modify and distribute this software and * its documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. */ #include #include #define DEBUGxx extern end[]; int errno; char *heap = (char*) end; void putchar(int c) { if (c == '\n') prom_putchar('\r'); prom_putchar(c); } +int +getchar() +{ + return prom_getchar(); +} + +int +ischar() +{ + return prom_poll(); +} + void puts(const char *s) { while (*s) putchar(*s++); } void * malloc(size_t size) { char *p = heap; size = (size + 7) & ~7; heap += size; return p; } void free(void * p) { } void panic(const char *message, ...) { puts(message); puts("\r\n"); halt(); } int prom_fd = 0; int devopen() { prom_return_t ret; char devname[64]; if (prom_fd) return; ret.bits = prom_getenv(PROM_E_BOOTED_DEV, devname, sizeof devname); ret.bits = prom_open(devname, ret.u.retval + 1); if (ret.u.status) panic("devopen: open failed\n"); prom_fd = ret.u.retval; /* XXX read disklabel and setup partition offset */ return 0; } #ifdef DEBUG void puthex(u_long v) { int digit; char hex[] = "0123456789abcdef"; if (!v) { puts("0"); return; } for (digit = 0; v >= (0x10L << digit); digit += 4) ; for (; digit >= 0; digit -= 4) putchar(hex[(v >> digit) & 0xf]); } #endif void devread(char *buf, int block, size_t size) { #ifdef DEBUG puts("devread("); puthex((u_long)buf); puts(","); puthex(block); puts(","); puthex(size); puts(")\n"); #endif prom_read(prom_fd, size, buf, block); } void devclose() { if (prom_fd) { prom_close(prom_fd); prom_fd = 0; } } void +getfilename(char *filename) +{ + int c; + char *p; + + puts("Boot: "); + + while ((c = getchar()) != '\n') { + if (c == '\b') { + if (p > filename) { + puts("\b \b"); + p--; + } + } else + *p++ = c; + } + *p = '\0'; + return; +} + +void loadfile(char *name, char *addr) { int n; + char filename[512]; + char *p; + restart: + puts("Loading "); + puts(name); + puts("\n"); + if (openrd(name)) { puts("Can't open file "); puts(name); puts("\n"); halt(); } + p = addr; do { - n = readit(addr, 1024); - addr += n; + n = readit(p, 1024); + p += n; + if (ischar()) { + puts("Stop!\n"); + devclose(); + getfilename(filename); + name = filename; + goto restart; + } twiddle(); } while (n > 0); devclose(); } void main() { char *loadaddr = (char*) SECONDARY_LOAD_ADDRESS; char *p; void (*entry) __P((void)); int i; init_prom_calls(); loadfile("/boot/loader", loadaddr); entry = (void (*)())loadaddr; (*entry)(); } Index: head/sys/boot/alpha/libalpha/prom.c =================================================================== --- head/sys/boot/alpha/libalpha/prom.c (revision 45719) +++ head/sys/boot/alpha/libalpha/prom.c (revision 45720) @@ -1,165 +1,165 @@ -/* $Id$ */ +/* $Id: prom.c,v 1.1.1.1 1998/08/21 03:17:42 msmith Exp $ */ /* $NetBSD: prom.c,v 1.3 1997/09/06 14:03:58 drochner Exp $ */ /* * Mach Operating System * Copyright (c) 1992 Carnegie Mellon University * All Rights Reserved. * * Permission to use, copy, modify and distribute this software and its * documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie Mellon * the rights to redistribute these changes. */ #include #include #include #include "common.h" #include "bootstrap.h" int console; static void prom_probe(struct console *cp); static int prom_init(int); void prom_putchar(int); int prom_getchar(void); -static int prom_poll(void); +int prom_poll(void); struct console promconsole = { "prom", "SRM firmware console", 0, prom_probe, prom_init, prom_putchar, prom_getchar, prom_poll, }; void init_prom_calls() { extern struct prom_vec prom_dispatch_v; struct rpb *r; struct crb *c; char buf[4]; r = (struct rpb *)HWRPB_ADDR; c = (struct crb *)((u_int8_t *)r + r->rpb_crb_off); prom_dispatch_v.routine_arg = c->crb_v_dispatch; prom_dispatch_v.routine = c->crb_v_dispatch->entry_va; /* Look for console tty. */ prom_getenv(PROM_E_TTY_DEV, buf, 4); console = buf[0] - '0'; } static void prom_probe(struct console *cp) { init_prom_calls(); cp->c_flags |= C_PRESENTIN|C_PRESENTOUT; } static int prom_init(int arg) { return 0; } void prom_putchar(int c) { prom_return_t ret; char cbuf; cbuf = c; do { ret.bits = prom_dispatch(PROM_R_PUTS, console, &cbuf, 1); } while ((ret.u.retval & 1) == 0); } static int saved_char = -1; int prom_getchar() { prom_return_t ret; if (saved_char != -1) { int c = saved_char; saved_char = -1; return c; } for (;;) { ret.bits = prom_dispatch(PROM_R_GETC, console); if (ret.u.status == 0 || ret.u.status == 1) return (ret.u.retval); } } int prom_poll() { prom_return_t ret; if (saved_char != -1) return 1; ret.bits = prom_dispatch(PROM_R_GETC, console); if (ret.u.status == 0 || ret.u.status == 1) { saved_char = ret.u.retval; return 1; } return 0; } int prom_getenv(id, buf, len) int id, len; char *buf; { prom_return_t ret; ret.bits = prom_dispatch(PROM_R_GETENV, id, buf, len-1); if (ret.u.status & 0x4) ret.u.retval = 0; buf[ret.u.retval] = '\0'; return (ret.u.retval); } int prom_open(dev, len) char *dev; int len; { prom_return_t ret; ret.bits = prom_dispatch(PROM_R_OPEN, dev, len); if (ret.u.status & 0x4) return (-1); else return (ret.u.retval); } Index: head/sys/boot/alpha/libalpha/start.S =================================================================== --- head/sys/boot/alpha/libalpha/start.S (revision 45719) +++ head/sys/boot/alpha/libalpha/start.S (revision 45720) @@ -1,88 +1,89 @@ /* - * $Id: start.S,v 1.1.1.1 1998/08/21 03:17:42 msmith Exp $ + * $Id: start.S,v 1.2 1998/10/31 17:12:32 dfr Exp $ * From: $NetBSD: start.S,v 1.4 1998/03/28 00:54:15 cgd Exp $ */ /* * Mach Operating System * Copyright (c) 1992 Carnegie Mellon University * All Rights Reserved. * * Permission to use, copy, modify and distribute this software and its * documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie Mellon * the rights to redistribute these changes. */ #include /* * start -- * Entry point for boot/standalone programs. * * Arguments: * a0 long (first free physical page) * * This is where the prom comes to. Leaves all exception and interrupts * to prom, runs off prom's stack too. No return values. */ .text .set noreorder /* don't reorder instructions */ #define ENTRY_FRAME 32 +#define STACK_SIZE 8192 NESTED(start, 1, ENTRY_FRAME, ra, 0, 0) br pv,Lstartgp Lstartgp: LDGP(pv) lda a0,_edata lda a1,_end subq a1,a0,a1 CALL(bzero) #if defined(NETBOOT) || defined(LOADER) - lda sp,stack + 8192 - ENTRY_FRAME + lda sp,stack + STACK_SIZE - ENTRY_FRAME #endif CALL(main) /* transfer to C */ XLEAF(_rtt, 0) XLEAF(halt, 0) call_pal PAL_halt /* halt if we ever return */ END(start) /* * Dummy routine for GCC2. */ LEAF(__main,0) RET END(__main) /* * cpu_number * Return the cpu number, using the whami instruction. */ LEAF(cpu_number, 0) call_pal PAL_VMS_mfpr_whami RET END(cpu_number) #if defined(NETBOOT) || defined(LOADER) -BSS(stack, 8192) +BSS(stack, STACK_SIZE) #endif Index: head/sys/boot/alpha/netboot/Makefile =================================================================== --- head/sys/boot/alpha/netboot/Makefile (revision 45719) +++ head/sys/boot/alpha/netboot/Makefile (revision 45720) @@ -1,69 +1,70 @@ # $NetBSD: Makefile,v 1.12 1998/02/19 14:18:36 drochner Exp $ BASE= netboot PROG= ${BASE} NOMAN= NEWVERSWHAT= "SRM net boot" .PATH: ${.CURDIR}/../common # i386-specific bootstrap sources SRCS+= main.c conf.c dev_net.c # Always add MI sources .PATH: ${.CURDIR}/../../common .include <${.CURDIR}/../../common/Makefile.inc> CFLAGS+= -mno-fp-regs CFLAGS+= -I${.CURDIR}/../../common -I${.CURDIR} +CFLAGS+= -I${.CURDIR}/../../.. -I. CFLAGS+= -I${.OBJDIR} CFLAGS+= -DNETBOOT CLEANFILES+= vers.c vers.o gensetdefs.o gensetdefs setdef0.o setdef1.o \ setdefs.h start.o CLEANFILES+= ${BASE} ${BASE}.sym ${BASE}.list CFLAGS+= -Wall CFLAGS+= -I${LIBSTANDDIR} CFLAGS+= -I${.CURDIR}/.. CRT= start.o STRIP= BINDIR?= /boot all: ${BASE} vers.o: ${.CURDIR}/newvers.sh ${.CURDIR}/Makefile sh ${.CURDIR}/newvers.sh ${.CURDIR}/version ${NEWVERSWHAT} ${CC} -c vers.c ${BASE}: ${BASE}.sym objcopy -O binary ${BASE}.sym ${BASE} ${BASE}.nosym: ${BASE}.sym cp ${BASE}.sym ${BASE}.nosym strip ${BASE}.nosym ${BASE}.sym: ${OBJS} ${LIBSTAND} ${LIBALPHA} ${CRT} vers.o setdef0.o setdef1.o ${LD} -o ${BASE}.sym -M -e start -N -Ttext ${PRIMARY_LOAD_ADDRESS} \ ${CRT} setdef0.o ${OBJS} setdef1.o \ vers.o ${LIBSTAND} ${LIBALPHA} ${LIBSTAND} >${.OBJDIR}/${BASE}.list start.o: ${.CURDIR}/../libalpha/start.S ${CC} -c ${CFLAGS} $< setdef0.o: setdefs.h setdef1.o: setdefs.h .include setdefs.h: gensetdefs ${OBJS} @echo Generating linker sets @./gensetdefs ${OBJS} >setdefs.h gensetdefs: gensetdefs.o ${CC} -static gensetdefs.o -o $@ gensetdefs.o: gensetdefs.c ${CC} -c $< Index: head/sys/boot/common/bootstrap.h =================================================================== --- head/sys/boot/common/bootstrap.h (revision 45719) +++ head/sys/boot/common/bootstrap.h (revision 45720) @@ -1,300 +1,300 @@ /*- * Copyright (c) 1998 Michael Smith * 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. * - * $Id: bootstrap.h,v 1.19 1999/01/22 23:50:13 msmith Exp $ + * $Id: bootstrap.h,v 1.20 1999/02/04 17:06:45 dcs Exp $ */ #include #include /* XXX debugging */ extern struct console vidconsole; #define MARK(s, c) {vidconsole.c_out(s); vidconsole.c_out(c); while (!vidconsole.c_ready()) ; vidconsole.c_in();} /* * Generic device specifier; architecture-dependant * versions may be larger, but should be allowed to * overlap. */ struct devdesc { struct devsw *d_dev; int d_type; #define DEVT_NONE 0 #define DEVT_DISK 1 #define DEVT_NET 2 }; /* Commands and return values; nonzero return sets command_errmsg != NULL */ typedef int (bootblk_cmd_t)(int argc, char *argv[]); extern char *command_errmsg; extern char command_errbuf[]; /* XXX blah, length */ #define CMD_OK 0 #define CMD_ERROR 1 /* interp.c */ extern void interact(void); extern int include(char *filename); /* interp_parse.c */ extern int parse(int *argc, char ***argv, char *str); /* interp_forth.c */ extern void bf_init(void); extern int bf_run(char *line); /* boot.c */ extern int autoboot(int delay, char *prompt); extern void autoboot_maybe(void); /* misc.c */ extern char *unargv(int argc, char *argv[]); extern void hexdump(caddr_t region, size_t len); extern size_t strlenout(vm_offset_t str); extern char *strdupout(vm_offset_t str); /* bcache.c */ extern int bcache_init(int nblks, size_t bsize); /* * Disk block cache */ struct bcache_devdata { int (*dv_strategy)(void *devdata, int rw, daddr_t blk, size_t size, void *buf, size_t *rsize); void *dv_devdata; }; /* * Modular console support. */ struct console { char *c_name; char *c_desc; int c_flags; #define C_PRESENTIN (1<<0) #define C_PRESENTOUT (1<<1) #define C_ACTIVEIN (1<<2) #define C_ACTIVEOUT (1<<3) void (* c_probe)(struct console *cp); /* set c_flags to match hardware */ int (* c_init)(int arg); /* reinit XXX may need more args */ void (* c_out)(int c); /* emit c */ int (* c_in)(void); /* wait for and return input */ int (* c_ready)(void); /* return nonzer if input waiting */ }; extern struct console *consoles[]; extern void cons_probe(void); /* * Plug-and-play enumerator/configurator interface. */ struct pnphandler { char *pp_name; /* handler/bus name */ void (* pp_enumerate)(void); /* enumerate PnP devices, add to chain */ }; struct pnpident { char *id_ident; /* ASCII identifier, actual format varies with bus/handler */ STAILQ_ENTRY(pnpident) id_link; }; struct pnpinfo { char *pi_desc; /* ASCII description, optional */ int pi_revision; /* optional revision (or -1) if not supported */ char *pi_module; /* module/args nominated to handle device */ int pi_argc; /* module arguments */ char **pi_argv; struct pnphandler *pi_handler; /* handler which detected this device */ STAILQ_HEAD(,pnpident) pi_ident; /* list of identifiers */ STAILQ_ENTRY(pnpinfo) pi_link; }; extern struct pnphandler *pnphandlers[]; /* provided by MD code */ extern void pnp_addident(struct pnpinfo *pi, char *ident); extern struct pnpinfo *pnp_allocinfo(void); extern void pnp_freeinfo(struct pnpinfo *pi); extern void pnp_addinfo(struct pnpinfo *pi); extern char *pnp_eisaformat(u_int8_t *data); /* * < 0 - No ISA in system * == 0 - Maybe ISA, search for read data port * > 0 - ISA in system, value is read data port address */ extern int isapnp_readport; /* * Module metadata header. * * Metadata are allocated on our heap, and copied into kernel space * before executing the kernel. */ struct module_metadata { size_t md_size; u_int16_t md_type; struct module_metadata *md_next; char md_data[0]; /* data are immediately appended */ }; /* * Loaded module information. * * At least one module (the kernel) must be loaded in order to boot. * The kernel is always loaded first. * * String fields (m_name, m_type) should be dynamically allocated. */ struct loaded_module { char *m_name; /* module name */ char *m_type; /* verbose module type, eg 'ELF kernel', 'pnptable', etc. */ char *m_args; /* arguments for the module */ struct module_metadata *m_metadata; /* metadata that will be placed in the module directory */ int m_loader; /* index of the loader that read the file */ vm_offset_t m_addr; /* load address */ size_t m_size; /* module size */ struct loaded_module *m_next; /* next module */ }; struct module_format { /* Load function must return EFTYPE if it can't handle the module supplied */ int (* l_load)(char *filename, vm_offset_t dest, struct loaded_module **result); /* Only a loader that will load a kernel (first module) should have an exec handler */ int (* l_exec)(struct loaded_module *mp); }; extern struct module_format *module_formats[]; /* supplied by consumer */ extern struct loaded_module *loaded_modules; extern int mod_load(char *name, int argc, char *argv[]); extern int mod_loadobj(char *type, char *name); extern struct loaded_module *mod_findmodule(char *name, char *type); extern void mod_addmetadata(struct loaded_module *mp, int type, size_t size, void *p); extern struct module_metadata *mod_findmetadata(struct loaded_module *mp, int type); extern void mod_discard(struct loaded_module *mp); extern struct loaded_module *mod_allocmodule(void); /* MI module loaders */ extern int aout_loadmodule(char *filename, vm_offset_t dest, struct loaded_module **result); extern vm_offset_t aout_findsym(char *name, struct loaded_module *mp); extern int elf_loadmodule(char *filename, vm_offset_t dest, struct loaded_module **result); #ifndef NEW_LINKER_SET #include /* XXX just for conversion's sake, until we move to the new linker set code */ #define SET_FOREACH(pvar, set) \ - for (pvar = set.ls_items; \ - pvar < set.ls_items + set.ls_length; \ + for ((char*) pvar = set.ls_items; \ + (char*) pvar < (char*) &set.ls_items[set.ls_length]; \ pvar++) #else /* NEW_LINKER_SET */ /* * Private macros, not to be used outside this header file. */ #define __MAKE_SET(set, sym) \ static void *__CONCAT(__setentry,__LINE__) \ __attribute__((__section__("set_" #set),__unused__)) = &sym #define __SET_BEGIN(set) \ ({ extern void *__CONCAT(__start_set_,set); \ &__CONCAT(__start_set_,set); }) #define __SET_END(set) \ ({ extern void *__CONCAT(__stop_set_,set); \ &__CONCAT(__stop_set_,set); }) /* * Public macros. */ /* Add an entry to a set. */ #define TEXT_SET(set, sym) __MAKE_SET(set, sym) #define DATA_SET(set, sym) __MAKE_SET(set, sym) #define BSS_SET(set, sym) __MAKE_SET(set, sym) #define ABS_SET(set, sym) __MAKE_SET(set, sym) /* * Iterate over all the elements of a set. * * Sets always contain addresses of things, and "pvar" points to words * containing those addresses. Thus is must be declared as "type **pvar", * and the address of each set item is obtained inside the loop by "*pvar". */ #define SET_FOREACH(pvar, set) \ for (pvar = (__typeof__(pvar))__SET_BEGIN(set); \ pvar < (__typeof__(pvar))__SET_END(set); pvar++) #endif /* * Support for commands */ struct bootblk_command { const char *c_name; const char *c_desc; bootblk_cmd_t *c_fn; }; #define COMMAND_SET(tag, key, desc, func) \ static bootblk_cmd_t func; \ static struct bootblk_command _cmd_ ## tag = { key, desc, func }; \ DATA_SET(Xcommand_set, _cmd_ ## tag); extern struct linker_set Xcommand_set; /* * The intention of the architecture switch is to provide a convenient * encapsulation of the interface between the bootstrap MI and MD code. * MD code may selectively populate the switch at runtime based on the * actual configuration of the target system. */ struct arch_switch { /* Automatically load modules as required by detected hardware */ int (* arch_autoload)(); /* Locate the device for (name), return pointer to tail in (*path) */ int (*arch_getdev)(void **dev, const char *name, const char **path); /* Copy from local address space to module address space, similar to bcopy() */ int (*arch_copyin)(void *src, vm_offset_t dest, size_t len); /* Copy to local address space from module address space, similar to bcopy() */ int (*arch_copyout)(vm_offset_t src, void *dest, size_t len); /* Read from file to module address space, same semantics as read() */ int (*arch_readin)(int fd, vm_offset_t dest, size_t len); /* Perform ISA byte port I/O (only for systems with ISA) */ int (*arch_isainb)(int port); void (*arch_isaoutb)(int port, int value); }; extern struct arch_switch archsw; /* This must be provided by the MD code, but should it be in the archsw? */ extern void delay(int delay); Index: head/sys/boot/ofw/libofw/ofw_console.c =================================================================== --- head/sys/boot/ofw/libofw/ofw_console.c (revision 45719) +++ head/sys/boot/ofw/libofw/ofw_console.c (revision 45720) @@ -1,165 +1,165 @@ -/* $Id$ */ +/* $Id: prom.c,v 1.1.1.1 1998/08/21 03:17:42 msmith Exp $ */ /* $NetBSD: prom.c,v 1.3 1997/09/06 14:03:58 drochner Exp $ */ /* * Mach Operating System * Copyright (c) 1992 Carnegie Mellon University * All Rights Reserved. * * Permission to use, copy, modify and distribute this software and its * documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie Mellon * the rights to redistribute these changes. */ #include #include #include #include "common.h" #include "bootstrap.h" int console; static void prom_probe(struct console *cp); static int prom_init(int); void prom_putchar(int); int prom_getchar(void); -static int prom_poll(void); +int prom_poll(void); struct console promconsole = { "prom", "SRM firmware console", 0, prom_probe, prom_init, prom_putchar, prom_getchar, prom_poll, }; void init_prom_calls() { extern struct prom_vec prom_dispatch_v; struct rpb *r; struct crb *c; char buf[4]; r = (struct rpb *)HWRPB_ADDR; c = (struct crb *)((u_int8_t *)r + r->rpb_crb_off); prom_dispatch_v.routine_arg = c->crb_v_dispatch; prom_dispatch_v.routine = c->crb_v_dispatch->entry_va; /* Look for console tty. */ prom_getenv(PROM_E_TTY_DEV, buf, 4); console = buf[0] - '0'; } static void prom_probe(struct console *cp) { init_prom_calls(); cp->c_flags |= C_PRESENTIN|C_PRESENTOUT; } static int prom_init(int arg) { return 0; } void prom_putchar(int c) { prom_return_t ret; char cbuf; cbuf = c; do { ret.bits = prom_dispatch(PROM_R_PUTS, console, &cbuf, 1); } while ((ret.u.retval & 1) == 0); } static int saved_char = -1; int prom_getchar() { prom_return_t ret; if (saved_char != -1) { int c = saved_char; saved_char = -1; return c; } for (;;) { ret.bits = prom_dispatch(PROM_R_GETC, console); if (ret.u.status == 0 || ret.u.status == 1) return (ret.u.retval); } } int prom_poll() { prom_return_t ret; if (saved_char != -1) return 1; ret.bits = prom_dispatch(PROM_R_GETC, console); if (ret.u.status == 0 || ret.u.status == 1) { saved_char = ret.u.retval; return 1; } return 0; } int prom_getenv(id, buf, len) int id, len; char *buf; { prom_return_t ret; ret.bits = prom_dispatch(PROM_R_GETENV, id, buf, len-1); if (ret.u.status & 0x4) ret.u.retval = 0; buf[ret.u.retval] = '\0'; return (ret.u.retval); } int prom_open(dev, len) char *dev; int len; { prom_return_t ret; ret.bits = prom_dispatch(PROM_R_OPEN, dev, len); if (ret.u.status & 0x4) return (-1); else return (ret.u.retval); } Index: head/sys/conf/Makefile.i386 =================================================================== --- head/sys/conf/Makefile.i386 (revision 45719) +++ head/sys/conf/Makefile.i386 (revision 45720) @@ -1,280 +1,280 @@ # Makefile.i386 -- with config changes. # Copyright 1990 W. Jolitz # from: @(#)Makefile.i386 7.1 5/10/91 -# $Id: Makefile.i386,v 1.144 1999/04/13 18:25:08 peter Exp $ +# $Id: Makefile.i386,v 1.145 1999/04/15 14:52:23 bde Exp $ # # Makefile for FreeBSD # # This makefile is constructed from a machine description: # config machineid # Most changes should be made in the machine description # /sys/i386/conf/``machineid'' # after which you should do # config machineid # Generic makefile changes should be made in # /sys/i386/conf/Makefile.i386 # after which config should be rerun for all machines. # # Which version of config(8) is required. -%VERSREQ= 300012 +%VERSREQ= 400013 KERNFORMAT?= elf STD8X16FONT?= iso .if exists(./@/.) S= ./@ .else S= ../.. .endif I386= ${S}/i386 COPTFLAGS?=-O INCLUDES= -nostdinc -I- -I. -I$S # This hack is to allow kernel compiles to succeed on machines w/out srcdist .if exists($S/../include) INCLUDES+= -I$S/../include .else INCLUDES+= -I/usr/include .endif COPTS= ${INCLUDES} ${IDENT} -DKERNEL -DVM_STACK -include opt_global.h CFLAGS= ${COPTFLAGS} ${CWARNFLAGS} ${DEBUG} ${COPTS} # XXX LOCORE means "don't declare C stuff" not "for locore.s". ASM_CFLAGS= -x assembler-with-cpp -DLOCORE ${CFLAGS} # Use the default object format for genassym, etc. GEN_CFLAGS= ${COPTFLAGS} ${CWARNFLAGS} ${DEBUG} ${COPTS} # Select the correct set of tools. Can't set OBJFORMAT here because it # doesn't get exported into the environment, and if it were exported # then it might break building of genassym, etc. .if ${KERNFORMAT} == "elf" CFLAGS+= -elf .else CFLAGS+= -aout .endif LOAD_ADDRESS?= C0100000 DEFINED_PROF= ${PROF} .if defined(PROF) CFLAGS+= -malign-functions=4 .if ${PROFLEVEL} >= 2 IDENT+= -DGPROF4 -DGUPROF PROF+= -mprofiler-epilogue .endif .endif NORMAL_C= ${CC} -c ${CFLAGS} ${PROF} $< NORMAL_C_C= ${CC} -c ${CFLAGS} ${PROF} $< NORMAL_S= ${CC} -c ${ASM_CFLAGS} $< DRIVER_C= ${CC} -c ${CFLAGS} ${PROF} $< DRIVER_C_C= ${CC} -c ${CFLAGS} ${PROF} $< DRIVER_S= ${CC} -c -x ${ASM_CFLAGS} $< PROFILE_C= ${CC} -c ${CFLAGS} $< GEN_CFILES= ${I386}/i386/genassym.c # setdef0.c and setdef1.c are intentionally # omitted from SYSTEM_CFILES. They include setdefs.h, a header which # is generated from all of ${OBJS}. We don't want to have to compile # everything just to do a make depend. SYSTEM_CFILES= ioconf.c param.c vnode_if.c config.c SYSTEM_SFILES= ${I386}/i386/locore.s SYSTEM_DEP= Makefile symbols.exclude symbols.sort ${SYSTEM_OBJS} .if ${CFLAGS:M-g} == "" SYMORDER_EXCLUDE=-x symbols.exclude .endif SYSTEM_LD_HEAD= @echo loading ${.TARGET}; rm -f ${.TARGET} .if ${KERNFORMAT} == aout || ${KERNFORMAT} == aoutkld SYSTEM_OBJS= locore.o vnode_if.o ${OBJS} ioconf.o param.o config.o SYSTEM_LD= @${LD} -aout -Bforcedynamic -Z -T ${LOAD_ADDRESS} -o ${.TARGET} -X ${SYSTEM_OBJS} vers.o SYSTEM_LD_TAIL= @echo rearranging symbols; \ symorder -m ${SYMORDER_EXCLUDE} symbols.sort ${.TARGET}; \ size -aout ${.TARGET} ; chmod 755 ${.TARGET} .endif .if ${KERNFORMAT} == elf SYSTEM_OBJS= locore.o setdef0.o vnode_if.o ${OBJS} ioconf.o param.o config.o \ setdef1.o hack.So SYSTEM_LD= @${LD} -elf -Bdynamic -T $S/i386/conf/kernel.script \ -export-dynamic -dynamic-linker /red/herring \ -o ${.TARGET} -X ${SYSTEM_OBJS} vers.o SYSTEM_LD_TAIL= @size -elf ${.TARGET} ; chmod 755 ${.TARGET} SYSTEM_DEP+= $S/i386/conf/kernel.script .endif .if defined(DEBUG) FULLKERNEL= ${KERNEL}.debug .else FULLKERNEL= ${KERNEL} .endif %BEFORE_DEPEND %OBJS %CFILES %SFILES %MFILES %LOAD %CLEAN .if !exists(.depend) ${SYSTEM_OBJS}: vnode_if.h ${BEFORE_DEPEND:M*.h} .endif clean: rm -f *.o *.so *.So *.ko *.s eddep errs genassym gensetdefs \ ${KERNEL} ${FULLKERNEL} linterrs makelinks param.c \ setdef[01].c setdefs.h symbols.exclude symbols.sort tags \ vers.c vnode_if.c vnode_if.h ${CLEAN} #lint: /tmp param.c # @lint -hbxn -DGENERIC -Dvolatile= ${COPTS} \ # ${I386}/i386/Locore.c ${CFILES} ioconf.c param.c | \ # grep -v 'struct/union .* never defined' | \ # grep -v 'possible pointer alignment problem' symbols.exclude: echo "gcc2_compiled." >symbols.exclude echo "___gnu_compiled_c" >>symbols.exclude symbols.sort: ${I386}/i386/symbols.raw grep -v '^#' ${I386}/i386/symbols.raw \ | sed 's/^ //' | sort -u > symbols.sort locore.o: ${I386}/i386/locore.s assym.s ${NORMAL_S} .if ${KERNFORMAT} == elf # This is a hack. BFD "optimizes" away dynamic mode if there are no # dynamic references. We could probably do a '-Bforcedynamic' mode like # in the a.out ld. For now, this works. hack.So: Makefile touch hack.c ${CC} -elf -shared -nostdlib hack.c -o hack.So rm -f hack.c .endif .ORDER: setdefs.h setdef0.c setdef1.c setdef0.o: setdef0.c setdefs.h ${NORMAL_C} setdef1.o: setdef1.c setdefs.h ${NORMAL_C} setdef0.c setdef1.c setdefs.h: ${OBJS} @echo generating linker set emulation glue for ELF @gensetdefs ${OBJS} # this rule stops ./assym.s in .depend from causing problems ./assym.s: assym.s assym.s: genassym ./genassym >assym.s genassym.o: ${I386}/i386/genassym.c ${CC} -c ${GEN_CFLAGS} ${I386}/i386/genassym.c genassym: genassym.o ${CC} ${GEN_CFLAGS} genassym.o -o ${.TARGET} ${SYSTEM_OBJS} genassym.o vers.o: opt_global.h # XXX this assumes that the options for NORMAL_C* and DRIVER_C* are identical. depend: assym.s param.c vnode_if.h ${BEFORE_DEPEND} rm -f .newdep mkdep -a -f .newdep ${CFLAGS} ${CFILES} ${SYSTEM_CFILES} mkdep -a -f .newdep ${GEN_CFLAGS} ${GEN_CFILES} env MKDEP_CPP="${CC} -E" \ mkdep -a -f .newdep ${ASM_CFLAGS} ${SFILES} ${SYSTEM_SFILES} rm -f .depend mv -f .newdep .depend cleandepend: rm -f .depend links: egrep '#if' ${CFILES:Nswapkernel.c} | sed -f $S/conf/defines | \ sed -e 's/:.*//' -e 's/\.c/.o/' | sort -u > dontlink echo ${CFILES:Nswapkernel.c} | tr -s ' ' '\12' | sed 's/\.c/.o/' | \ sort -u | comm -23 - dontlink | \ sed 's,../.*/\(.*.o\),rm -f \1;ln -s ../GENERIC/\1 \1,' > makelinks sh makelinks && rm -f dontlink tags: @echo "see $S/kern/Makefile for tags" .if defined(DEBUG) ${KERNEL}: ${FULLKERNEL} .if ${KERNFORMAT} == "elf" objcopy --strip-debug ${FULLKERNEL} ${KERNEL} .else cp ${FULLKERNEL} ${KERNEL} strip -d kernel .endif .endif install install.debug: @if [ ! -f ${KERNEL}${.TARGET:S/install//} ] ; then \ echo "You must first build a kernel first." ; \ exit 1 ; \ fi .if exists(${DESTDIR}/${KERNEL}) -chflags noschg ${DESTDIR}/${KERNEL} mv ${DESTDIR}/${KERNEL} ${DESTDIR}/${KERNEL}.old .endif PATH=$${PATH}:/sbin:/usr/sbin; \ if [ `sysctl -n kern.bootfile` = ${DESTDIR}/${KERNEL} ] ; then \ sysctl -w kern.bootfile=${DESTDIR}/${KERNEL}.old ; \ if [ -f /var/db/kvm_kernel.db ] ; then \ mv -f /var/db/kvm_kernel.db /var/db/kvm_kernel.old.db ; \ fi \ fi install -c -m 555 -o root -g wheel -fschg \ ${KERNEL}${.TARGET:S/install//} ${DESTDIR}/${KERNEL} config.o: ${NORMAL_C} ioconf.o: ${NORMAL_C} param.c: $S/conf/param.c -rm -f param.c cp $S/conf/param.c . param.o: ${NORMAL_C} vers.c: $S/conf/newvers.sh $S/sys/param.h ${SYSTEM_DEP} sh $S/conf/newvers.sh ${KERN_IDENT} ${IDENT} # XXX strictly, everything depends on Makefile because changes to ${PROF} # only appear there, but we don't handle that. vers.o: ${NORMAL_C} .ORDER: vnode_if.c vnode_if.h vnode_if.c vnode_if.h: $S/kern/vnode_if.sh $S/kern/vnode_if.src sh $S/kern/vnode_if.sh $S/kern/vnode_if.src vnode_if.o: ${NORMAL_C} .if exists($S/../share/mk) .include "$S/../share/mk/bsd.kern.mk" .else .include .endif %RULES # DO NOT DELETE THIS LINE -- make depend uses it Index: head/sys/conf/Makefile.powerpc =================================================================== --- head/sys/conf/Makefile.powerpc (revision 45719) +++ head/sys/conf/Makefile.powerpc (revision 45720) @@ -1,280 +1,280 @@ # Makefile.i386 -- with config changes. # Copyright 1990 W. Jolitz # from: @(#)Makefile.i386 7.1 5/10/91 -# $Id: Makefile.i386,v 1.144 1999/04/13 18:25:08 peter Exp $ +# $Id: Makefile.i386,v 1.145 1999/04/15 14:52:23 bde Exp $ # # Makefile for FreeBSD # # This makefile is constructed from a machine description: # config machineid # Most changes should be made in the machine description # /sys/i386/conf/``machineid'' # after which you should do # config machineid # Generic makefile changes should be made in # /sys/i386/conf/Makefile.i386 # after which config should be rerun for all machines. # # Which version of config(8) is required. -%VERSREQ= 300012 +%VERSREQ= 400013 KERNFORMAT?= elf STD8X16FONT?= iso .if exists(./@/.) S= ./@ .else S= ../.. .endif I386= ${S}/i386 COPTFLAGS?=-O INCLUDES= -nostdinc -I- -I. -I$S # This hack is to allow kernel compiles to succeed on machines w/out srcdist .if exists($S/../include) INCLUDES+= -I$S/../include .else INCLUDES+= -I/usr/include .endif COPTS= ${INCLUDES} ${IDENT} -DKERNEL -DVM_STACK -include opt_global.h CFLAGS= ${COPTFLAGS} ${CWARNFLAGS} ${DEBUG} ${COPTS} # XXX LOCORE means "don't declare C stuff" not "for locore.s". ASM_CFLAGS= -x assembler-with-cpp -DLOCORE ${CFLAGS} # Use the default object format for genassym, etc. GEN_CFLAGS= ${COPTFLAGS} ${CWARNFLAGS} ${DEBUG} ${COPTS} # Select the correct set of tools. Can't set OBJFORMAT here because it # doesn't get exported into the environment, and if it were exported # then it might break building of genassym, etc. .if ${KERNFORMAT} == "elf" CFLAGS+= -elf .else CFLAGS+= -aout .endif LOAD_ADDRESS?= C0100000 DEFINED_PROF= ${PROF} .if defined(PROF) CFLAGS+= -malign-functions=4 .if ${PROFLEVEL} >= 2 IDENT+= -DGPROF4 -DGUPROF PROF+= -mprofiler-epilogue .endif .endif NORMAL_C= ${CC} -c ${CFLAGS} ${PROF} $< NORMAL_C_C= ${CC} -c ${CFLAGS} ${PROF} $< NORMAL_S= ${CC} -c ${ASM_CFLAGS} $< DRIVER_C= ${CC} -c ${CFLAGS} ${PROF} $< DRIVER_C_C= ${CC} -c ${CFLAGS} ${PROF} $< DRIVER_S= ${CC} -c -x ${ASM_CFLAGS} $< PROFILE_C= ${CC} -c ${CFLAGS} $< GEN_CFILES= ${I386}/i386/genassym.c # setdef0.c and setdef1.c are intentionally # omitted from SYSTEM_CFILES. They include setdefs.h, a header which # is generated from all of ${OBJS}. We don't want to have to compile # everything just to do a make depend. SYSTEM_CFILES= ioconf.c param.c vnode_if.c config.c SYSTEM_SFILES= ${I386}/i386/locore.s SYSTEM_DEP= Makefile symbols.exclude symbols.sort ${SYSTEM_OBJS} .if ${CFLAGS:M-g} == "" SYMORDER_EXCLUDE=-x symbols.exclude .endif SYSTEM_LD_HEAD= @echo loading ${.TARGET}; rm -f ${.TARGET} .if ${KERNFORMAT} == aout || ${KERNFORMAT} == aoutkld SYSTEM_OBJS= locore.o vnode_if.o ${OBJS} ioconf.o param.o config.o SYSTEM_LD= @${LD} -aout -Bforcedynamic -Z -T ${LOAD_ADDRESS} -o ${.TARGET} -X ${SYSTEM_OBJS} vers.o SYSTEM_LD_TAIL= @echo rearranging symbols; \ symorder -m ${SYMORDER_EXCLUDE} symbols.sort ${.TARGET}; \ size -aout ${.TARGET} ; chmod 755 ${.TARGET} .endif .if ${KERNFORMAT} == elf SYSTEM_OBJS= locore.o setdef0.o vnode_if.o ${OBJS} ioconf.o param.o config.o \ setdef1.o hack.So SYSTEM_LD= @${LD} -elf -Bdynamic -T $S/i386/conf/kernel.script \ -export-dynamic -dynamic-linker /red/herring \ -o ${.TARGET} -X ${SYSTEM_OBJS} vers.o SYSTEM_LD_TAIL= @size -elf ${.TARGET} ; chmod 755 ${.TARGET} SYSTEM_DEP+= $S/i386/conf/kernel.script .endif .if defined(DEBUG) FULLKERNEL= ${KERNEL}.debug .else FULLKERNEL= ${KERNEL} .endif %BEFORE_DEPEND %OBJS %CFILES %SFILES %MFILES %LOAD %CLEAN .if !exists(.depend) ${SYSTEM_OBJS}: vnode_if.h ${BEFORE_DEPEND:M*.h} .endif clean: rm -f *.o *.so *.So *.ko *.s eddep errs genassym gensetdefs \ ${KERNEL} ${FULLKERNEL} linterrs makelinks param.c \ setdef[01].c setdefs.h symbols.exclude symbols.sort tags \ vers.c vnode_if.c vnode_if.h ${CLEAN} #lint: /tmp param.c # @lint -hbxn -DGENERIC -Dvolatile= ${COPTS} \ # ${I386}/i386/Locore.c ${CFILES} ioconf.c param.c | \ # grep -v 'struct/union .* never defined' | \ # grep -v 'possible pointer alignment problem' symbols.exclude: echo "gcc2_compiled." >symbols.exclude echo "___gnu_compiled_c" >>symbols.exclude symbols.sort: ${I386}/i386/symbols.raw grep -v '^#' ${I386}/i386/symbols.raw \ | sed 's/^ //' | sort -u > symbols.sort locore.o: ${I386}/i386/locore.s assym.s ${NORMAL_S} .if ${KERNFORMAT} == elf # This is a hack. BFD "optimizes" away dynamic mode if there are no # dynamic references. We could probably do a '-Bforcedynamic' mode like # in the a.out ld. For now, this works. hack.So: Makefile touch hack.c ${CC} -elf -shared -nostdlib hack.c -o hack.So rm -f hack.c .endif .ORDER: setdefs.h setdef0.c setdef1.c setdef0.o: setdef0.c setdefs.h ${NORMAL_C} setdef1.o: setdef1.c setdefs.h ${NORMAL_C} setdef0.c setdef1.c setdefs.h: ${OBJS} @echo generating linker set emulation glue for ELF @gensetdefs ${OBJS} # this rule stops ./assym.s in .depend from causing problems ./assym.s: assym.s assym.s: genassym ./genassym >assym.s genassym.o: ${I386}/i386/genassym.c ${CC} -c ${GEN_CFLAGS} ${I386}/i386/genassym.c genassym: genassym.o ${CC} ${GEN_CFLAGS} genassym.o -o ${.TARGET} ${SYSTEM_OBJS} genassym.o vers.o: opt_global.h # XXX this assumes that the options for NORMAL_C* and DRIVER_C* are identical. depend: assym.s param.c vnode_if.h ${BEFORE_DEPEND} rm -f .newdep mkdep -a -f .newdep ${CFLAGS} ${CFILES} ${SYSTEM_CFILES} mkdep -a -f .newdep ${GEN_CFLAGS} ${GEN_CFILES} env MKDEP_CPP="${CC} -E" \ mkdep -a -f .newdep ${ASM_CFLAGS} ${SFILES} ${SYSTEM_SFILES} rm -f .depend mv -f .newdep .depend cleandepend: rm -f .depend links: egrep '#if' ${CFILES:Nswapkernel.c} | sed -f $S/conf/defines | \ sed -e 's/:.*//' -e 's/\.c/.o/' | sort -u > dontlink echo ${CFILES:Nswapkernel.c} | tr -s ' ' '\12' | sed 's/\.c/.o/' | \ sort -u | comm -23 - dontlink | \ sed 's,../.*/\(.*.o\),rm -f \1;ln -s ../GENERIC/\1 \1,' > makelinks sh makelinks && rm -f dontlink tags: @echo "see $S/kern/Makefile for tags" .if defined(DEBUG) ${KERNEL}: ${FULLKERNEL} .if ${KERNFORMAT} == "elf" objcopy --strip-debug ${FULLKERNEL} ${KERNEL} .else cp ${FULLKERNEL} ${KERNEL} strip -d kernel .endif .endif install install.debug: @if [ ! -f ${KERNEL}${.TARGET:S/install//} ] ; then \ echo "You must first build a kernel first." ; \ exit 1 ; \ fi .if exists(${DESTDIR}/${KERNEL}) -chflags noschg ${DESTDIR}/${KERNEL} mv ${DESTDIR}/${KERNEL} ${DESTDIR}/${KERNEL}.old .endif PATH=$${PATH}:/sbin:/usr/sbin; \ if [ `sysctl -n kern.bootfile` = ${DESTDIR}/${KERNEL} ] ; then \ sysctl -w kern.bootfile=${DESTDIR}/${KERNEL}.old ; \ if [ -f /var/db/kvm_kernel.db ] ; then \ mv -f /var/db/kvm_kernel.db /var/db/kvm_kernel.old.db ; \ fi \ fi install -c -m 555 -o root -g wheel -fschg \ ${KERNEL}${.TARGET:S/install//} ${DESTDIR}/${KERNEL} config.o: ${NORMAL_C} ioconf.o: ${NORMAL_C} param.c: $S/conf/param.c -rm -f param.c cp $S/conf/param.c . param.o: ${NORMAL_C} vers.c: $S/conf/newvers.sh $S/sys/param.h ${SYSTEM_DEP} sh $S/conf/newvers.sh ${KERN_IDENT} ${IDENT} # XXX strictly, everything depends on Makefile because changes to ${PROF} # only appear there, but we don't handle that. vers.o: ${NORMAL_C} .ORDER: vnode_if.c vnode_if.h vnode_if.c vnode_if.h: $S/kern/vnode_if.sh $S/kern/vnode_if.src sh $S/kern/vnode_if.sh $S/kern/vnode_if.src vnode_if.o: ${NORMAL_C} .if exists($S/../share/mk) .include "$S/../share/mk/bsd.kern.mk" .else .include .endif %RULES # DO NOT DELETE THIS LINE -- make depend uses it Index: head/sys/conf/NOTES =================================================================== --- head/sys/conf/NOTES (revision 45719) +++ head/sys/conf/NOTES (revision 45720) @@ -1,2130 +1,2130 @@ # # LINT -- config file for checking all the sources, tries to pull in # as much of the source tree as it can. # -# $Id: LINT,v 1.581 1999/04/14 16:54:00 peter Exp $ +# $Id: LINT,v 1.582 1999/04/16 16:17:04 n_hibma Exp $ # # NB: You probably don't want to try running a kernel built from this # file. Instead, you should start from GENERIC, and add options from # this file as required. # # # This directive is mandatory; it defines the architecture to be # configured for; in this case, the 386 family based IBM-PC and # compatibles. # machine "i386" # # This is the ``identification'' of the kernel. Usually this should # be the same as the name of your kernel. # ident LINT # # The `maxusers' parameter controls the static sizing of a number of # internal system tables by a complicated formula defined in param.c. # maxusers 10 # # The `makeoptions' parameter allows variables to be passed to the # generated Makefile in the build area. DEBUG happens to be magic. # The following is equivalent to 'config -g KERNELNAME' and creates # 'kernel.debug' compiled with -g debugging as well as a normal # 'kernel'. Use 'make install.debug' to install the debug kernel # but that isn't normally necessary as the debug symbols are not loaded # by the kernel and are not useful there anyway. # #makeoptions DEBUG="-g" #Build kernel with gdb(1) debug symbols # # Certain applications can grow to be larger than the 128M limit # that FreeBSD initially imposes. Below are some options to # allow that limit to grow to 256MB, and can be increased further # with changing the parameters. MAXDSIZ is the maximum that the # limit can be set to, and the DFLDSIZ is the default value for # the limit. You might want to set the default lower than the # max, and explicitly set the maximum with a shell command for processes # that regularly exceed the limit like INND. # options "MAXDSIZ=(256*1024*1024)" options "DFLDSIZ=(256*1024*1024)" # When this is set, be extra conservative in various parts of the kernel # and choose functionality over speed (on the widest variety of systems). options FAILSAFE # Options for the VM subsystem #options PQ_NOOPT # No coloring options PQ_LARGECACHE # color for 512k/16k cache #options PQ_HUGECACHE # color for 1024k/16k cache # This allows you to actually store this configuration file into # the kernel binary itself, where it may be later read by saying: # strings -aout -n 3 /kernel | grep ^___ | sed -e 's/^___//' > MYKERNEL # options INCLUDE_CONFIG_FILE # Include this file in kernel # # This directive defines a number of things: # - The compiled kernel is to be called `kernel' # - The root filesystem might be on partition wd0a # - Crash dumps will be written to wd0b, if possible. Specifying the # dump device here is not recommended. Use dumpon(8). # config kernel root on wd0 dumps on wd0 ##################################################################### # SMP OPTIONS: # # SMP enables building of a Symmetric MultiProcessor Kernel. # APIC_IO enables the use of the IO APIC for Symmetric I/O. # NCPU sets the number of CPUs, defaults to 2. # NBUS sets the number of busses, defaults to 4. # NAPIC sets the number of IO APICs on the motherboard, defaults to 1. # NINTR sets the total number of INTs provided by the motherboard. # # Notes: # # An SMP kernel will ONLY run on an Intel MP spec. qualified motherboard. # # Be sure to disable 'cpu "I386_CPU"' && 'cpu "I486_CPU"' for SMP kernels. # # Check the 'Rogue SMP hardware' section to see if additional options # are required by your hardware. # # Mandatory: options SMP # Symmetric MultiProcessor Kernel options APIC_IO # Symmetric (APIC) I/O # Optional, these are the defaults plus 1: options NCPU=5 # number of CPUs options NBUS=5 # number of busses options NAPIC=2 # number of IO APICs options NINTR=25 # number of INTs # # Rogue SMP hardware: # # Bridged PCI cards: # # The MP tables of most of the current generation MP motherboards # do NOT properly support bridged PCI cards. To use one of these # cards you should refer to ??? ##################################################################### # CPU OPTIONS # # You must specify at least one CPU (the one you intend to run on); # deleting the specification for CPUs you don't need to use may make # parts of the system run faster. This is especially true removing # I386_CPU. # cpu "I386_CPU" cpu "I486_CPU" cpu "I586_CPU" # aka Pentium(tm) cpu "I686_CPU" # aka Pentium Pro(tm) # # Options for CPU features. # # CPU_BLUELIGHTNING_FPU_OP_CACHE enables FPU operand cache on IBM # BlueLightning CPU. It works only with Cyrix FPU, and this option # should not be used with Intel FPU. # # CPU_BLUELIGHTNING_3X enables triple-clock mode on IBM Blue Lightning # CPU if CPU supports it. The default is double-clock mode on # BlueLightning CPU box. # # CPU_BTB_EN enables branch target buffer on Cyrix 5x86 (NOTE 1). # # CPU_DIRECT_MAPPED_CACHE sets L1 cache of Cyrix 486DLC CPU in direct # mapped mode. Default is 2-way set associative mode. # # CPU_CYRIX_NO_LOCK enables weak locking for the entire address space # of Cyrix 6x86 and 6x86MX CPUs. If this option is not set and # FAILESAFE is defined, NO_LOCK bit of CCR1 is cleared. (NOTE 3) # # CPU_DISABLE_5X86_LSSER disables load store serialize (i.e. enables # reorder). This option should not be used if you use memory mapped # I/O device(s). # # CPU_FASTER_5X86_FPU enables faster FPU exception handler. # # CPU_I486_ON_386 enables CPU cache on i486 based CPU upgrade products # for i386 machines. # # CPU_IORT defines I/O clock delay time (NOTE 1). Default vaules of # I/O clock delay time on Cyrix 5x86 and 6x86 are 0 and 7,respectively # (no clock delay). # # CPU_LOOP_EN prevents flushing the prefetch buffer if the destination # of a jump is already present in the prefetch buffer on Cyrix 5x86(NOTE # 1). # # CPU_RSTK_EN enables return stack on Cyrix 5x86 (NOTE 1). # # CPU_SUSP_HLT enables suspend on HALT. If this option is set, CPU # enters suspend mode following execution of HALT instruction. # # CPU_WT_ALLOC enables write allocation on Cyrix 6x86/6x86MX and AMD # K5/K6/K6-2 cpus. # # CYRIX_CACHE_WORKS enables CPU cache on Cyrix 486 CPUs with cache # flush at hold state. # # CYRIX_CACHE_REALLY_WORKS enables (1) CPU cache on Cyrix 486 CPUs # without cache flush at hold state, and (2) write-back CPU cache on # Cyrix 6x86 whose revision < 2.7 (NOTE 2). # # NO_F00F_HACK disables the hack that prevents Pentiums (and ONLY # Pentiums) from locking up when a LOCK CMPXCHG8B instruction is # executed. This should be included for ALL kernels that won't run # on a Pentium. # # NO_MEMORY_HOLE is an optimisation for systems with AMD K6 processors # which indicates that the 15-16MB range is *definitely* not being # occupied by an ISA memory hole. # # NOTE 1: The options, CPU_BTB_EN, CPU_LOOP_EN, CPU_IORT, # CPU_LOOP_ENand CPU_RSTK_EN should not be used becasue of CPU bugs. # These options may crash your system. # # NOTE 2: If CYRIX_CACHE_REALLY_WORKS is not set, CPU cache is enabled # in write-through mode when revision < 2.7. If revision of Cyrix # 6x86 >= 2.7, CPU cache is always enabled in write-back mode. # # NOTE 3: This option may cause failures for software that requires # locked cycles in order to operate correctly. # options "CPU_BLUELIGHTNING_FPU_OP_CACHE" options "CPU_BLUELIGHTNING_3X" options "CPU_BTB_EN" options "CPU_DIRECT_MAPPED_CACHE" options "CPU_DISABLE_5X86_LSSER" options "CPU_FASTER_5X86_FPU" options "CPU_I486_ON_386" options "CPU_IORT" options "CPU_LOOP_EN" options "CPU_RSTK_EN" options "CPU_SUSP_HLT" options "CPU_WT_ALLOC" options "CYRIX_CACHE_WORKS" options "CYRIX_CACHE_REALLY_WORKS" #options "NO_F00F_HACK" # # A math emulator is mandatory if you wish to run on hardware which # does not have a floating-point processor. Pick either the original, # bogus (but freely-distributable) math emulator, or a much more # fully-featured but GPL-licensed emulator taken from Linux. # options MATH_EMULATE #Support for x87 emulation # Don't enable both of these in a real config. options GPL_MATH_EMULATE #Support for x87 emulation via #new math emulator ##################################################################### # COMPATIBILITY OPTIONS # # Implement system calls compatible with 4.3BSD and older versions of # FreeBSD. You probably do NOT want to remove this as much current code # still relies on the 4.3 emulation. # options "COMPAT_43" # # Statically compile in the i386 a.out LKM compatability support. # Also available as an KLD module. # options LKM # # Allow user-mode programs to manipulate their local descriptor tables. # This option is required for the WINE Windows(tm) emulator, and is # not used by anything else (that we know of). # options USER_LDT #allow user-level control of i386 ldt # # These three options provide support for System V Interface # Definition-style interprocess communication, in the form of shared # memory, semaphores, and message queues, respectively. # options SYSVSHM options SYSVSEM options SYSVMSG # # This option includes a MD5 routine in the kernel, this is used for # various authentication and privacy uses. # options "MD5" # # Allow processes to switch to vm86 mode, as well as enabling direct # user-mode access to the I/O port space. This option is necessary for # the doscmd emulator to run. # options "VM86" ##################################################################### # DEBUGGING OPTIONS # # Enable the kernel debugger. # options DDB # # Don't drop into DDB for a panic. Intended for unattended operation # where you may want to drop to DDB from the console, but still want # the machine to recover from a panic # options DDB_UNATTENDED # # If using GDB remote mode to debug the kernel, there's a non-standard # extension to the remote protocol that can be used to use the serial # port as both the debugging port and the system console. It's non- # standard and you're on your own if you enable it. See also the # "remotechat" variables in the FreeBSD specific version of gdb. # options GDB_REMOTE_CHAT # # KTRACE enables the system-call tracing facility ktrace(2). # options KTRACE #kernel tracing # # The INVARIANTS option is used in a number of source files to enable # extra sanity checking of internal structures. This support is not # enabled by default because of the extra time it would take to check # for these conditions, which can only occur as a result of # programming errors. # options INVARIANTS # # The INVARIANT_SUPPORT option makes us compile in support for # verifying some of the internal structures. It is a prerequisite for # 'INVARIANTS', as enabling 'INVARIANTS' will make these functions be # called. The intent is that you can set 'INVARIANTS' for single # source files (by changing the source file or specifying it on the # command line) if you have 'INVARIANT_SUPPORT' enabled. # options INVARIANT_SUPPORT # # The DIAGNOSTIC option is used to enable extra debugging information # from some parts of the kernel. As this makes everything more noisy, # it is disabled by default. # options DIAGNOSTIC # # PERFMON causes the driver for Pentium/Pentium Pro performance counters # to be compiled. See perfmon(4) for more information. # options PERFMON # # This option let some drivers co-exist that can't co-exist in a running # system. This is used to be able to compile all kernel code in one go for # quality assurance purposes (like this file, which the option takes it name # from.) # options COMPILING_LINT # XXX - this doesn't belong here. # Allow ordinary users to take the console - this is useful for X. options UCONSOLE # XXX - this doesn't belong here either options USERCONFIG #boot -c editor options INTRO_USERCONFIG #imply -c and show intro screen options VISUAL_USERCONFIG #visual boot -c editor ##################################################################### # NETWORKING OPTIONS # # Protocol families: # Only the INET (Internet) family is officially supported in FreeBSD. # Source code for the NS (Xerox Network Service) is provided for amusement # value. # options INET #Internet communications protocols options IPX #IPX/SPX communications protocols options IPXIP #IPX in IP encapsulation (not available) options IPTUNNEL #IP in IPX encapsulation (not available) options NETATALK #Appletalk communications protocols # These are currently broken but are shipped due to interest. #options NS #Xerox NS protocols # These are currently broken and are no longer shipped due to lack # of interest. #options CCITT #X.25 network layer #options ISO #options TPIP #ISO TP class 4 over IP #options TPCONS #ISO TP class 0 over X.25 #options LLC #X.25 link layer for Ethernets #options HDLC #X.25 link layer for serial lines #options EON #ISO CLNP over IP #options NSIP #XNS over IP # # Network interfaces: # The `loop' pseudo-device is MANDATORY when networking is enabled. # The `ether' pseudo-device provides generic code to handle # Ethernets; it is MANDATORY when a Ethernet device driver is # configured or token-ring is enabled. # The 'fddi' pseudo-device provides generic code to support FDDI. # The `sppp' pseudo-device serves a similar role for certain types # of synchronous PPP links (like `cx', `ar'). # The `sl' pseudo-device implements the Serial Line IP (SLIP) service. # The `ppp' pseudo-device implements the Point-to-Point Protocol. # The `bpfilter' pseudo-device enables the Berkeley Packet Filter. Be # aware of the legal and administrative consequences of enabling this # option. The number of devices determines the maximum number of # simultaneous BPF clients programs runnable. # The `disc' pseudo-device implements a minimal network interface, # which throws away all packets sent and never receives any. It is # included for testing purposes. # The `tun' pseudo-device implements (user-)ppp and nos-tun # The `streams' pseudo-device implements SysVR4 STREAMS emulation. # # The PPP_BSDCOMP option enables support for compress(1) style entire # packet compression, the PPP_DEFLATE is for zlib/gzip style compression. # PPP_FILTER enables code for filtering the ppp data stream and selecting # events for resetting the demand dial activity timer - requires bpfilter. # See pppd(8) for more details. # pseudo-device ether #Generic Ethernet pseudo-device token #Generic TokenRing pseudo-device fddi #Generic FDDI pseudo-device sppp #Generic Synchronous PPP pseudo-device loop #Network loopback device pseudo-device bpfilter 4 #Berkeley packet filter pseudo-device disc #Discard device pseudo-device tun 1 #Tunnel driver (ppp(8), nos-tun(8)) pseudo-device sl 2 #Serial Line IP pseudo-device ppp 2 #Point-to-point protocol pseudo-device streams options PPP_BSDCOMP #PPP BSD-compress support options PPP_DEFLATE #PPP zlib/deflate/gzip support options PPP_FILTER #enable bpf filtering (needs bpfilter) # # Internet family options: # # TCP_COMPAT_42 causes the TCP code to emulate certain bugs present in # 4.2BSD. This option should not be used unless you have a 4.2BSD # machine and TCP connections fail. # # MROUTING enables the kernel multicast packet forwarder, which works # with mrouted(8). # # IPFIREWALL enables support for IP firewall construction, in # conjunction with the `ipfw' program. IPFIREWALL_VERBOSE sends # logged packets to the system logger. IPFIREWALL_VERBOSE_LIMIT # limits the number of times a matching entry can be logged. # # WARNING: IPFIREWALL defaults to a policy of "deny ip from any to any" # and if you do not add other rules during startup to allow access, # YOU WILL LOCK YOURSELF OUT. It is suggested that you set firewall=open # in /etc/rc.conf when first enabling this feature, then refining the # firewall rules in /etc/rc.firewall after you've tested that the new kernel # feature works properly. # # IPFIREWALL_DEFAULT_TO_ACCEPT causes the default rule (at boot) to # allow everything. Use with care, if a cracker can crash your # firewall machine, they can get to your protected machines. However, # if you are using it as an as-needed filter for specific problems as # they arise, then this may be for you. Changing the default to 'allow' # means that you won't get stuck if the kernel and /sbin/ipfw binary get # out of sync. # # IPDIVERT enables the divert IP sockets, used by ``ipfw divert'' # # IPFILTER enables Darren Reed's ipfilter package. # IPFILTER_LOG enables ipfilter's logging. # IPFILTER_LKM enables LKM support for an ipfilter module (untested). # # IPSTEALTH enables code to support stealth forwarding (i.e., forwarding # packets without touching the ttl). This can be useful to hide firewalls # from traceroute and similar tools. # # TCPDEBUG is undocumented. # options "TCP_COMPAT_42" #emulate 4.2BSD TCP bugs options MROUTING # Multicast routing options IPFIREWALL #firewall options IPFIREWALL_VERBOSE #print information about # dropped packets options IPFIREWALL_FORWARD #enable transparent proxy support options "IPFIREWALL_VERBOSE_LIMIT=100" #limit verbosity options IPFIREWALL_DEFAULT_TO_ACCEPT #allow everything by default options IPDIVERT #divert sockets options IPFILTER #kernel ipfilter support options IPFILTER_LOG #ipfilter logging #options IPFILTER_LKM #kernel support for ip_fil.o LKM options IPSTEALTH #support for stealth forwarding options TCPDEBUG # ICMP_BANDLIM enables icmp error response bandwidth limiting. You # typically want this option as it will help protect the machine from # D.O.S. packet attacks. # options "ICMP_BANDLIM" # DUMMYNET enables the "dummynet" bandwidth limiter. You need # IPFIREWALL as well. See the dummynet(4) manpage for more info. # BRIDGE enables bridging between ethernet cards -- see bridge(4). # You can use IPFIREWALL and dummynet together with bridging. options DUMMYNET options BRIDGE # # ATM (HARP version) options # # ATM_CORE includes the base ATM functionality code. This must be included # for ATM support. # # ATM_IP includes support for running IP over ATM. # # At least one (and usually only one) of the following signalling managers # must be included (note that all signalling managers include PVC support): # ATM_SIGPVC includes support for the PVC-only signalling manager `sigpvc'. # ATM_SPANS includes support for the `spans' signalling manager, which runs # the FORE Systems's proprietary SPANS signalling protocol. # ATM_UNI includes support for the `uni30' and `uni31' signalling managers, # which run the ATM Forum UNI 3.x signalling protocols. # # The `hea' driver provides support for the Efficient Networks, Inc. # ENI-155p ATM PCI Adapter. # # The `hfa' driver provides support for the FORE Systems, Inc. # PCA-200E ATM PCI Adapter. # options ATM_CORE #core ATM protocol family options ATM_IP #IP over ATM support options ATM_SIGPVC #SIGPVC signalling manager options ATM_SPANS #SPANS signalling manager options ATM_UNI #UNI signalling manager device hea0 #Efficient ENI-155p ATM PCI device hfa0 #FORE PCA-200E ATM PCI ##################################################################### # FILESYSTEM OPTIONS # # Only the root, /usr, and /tmp filesystems need be statically # compiled; everything else will be automatically loaded at mount # time. (Exception: the UFS family---FFS, and MFS --- cannot # currently be demand-loaded.) Some people still prefer to statically # compile other filesystems as well. # # NB: The NULL, PORTAL, UMAP and UNION filesystems are known to be # buggy, and WILL panic your system if you attempt to do anything with # them. They are included here as an incentive for some enterprising # soul to sit down and fix them. # # One of these is mandatory: options FFS #Fast filesystem options MFS #Memory File System options NFS #Network File System # The rest are optional: # options NFS_NOSERVER #Disable the NFS-server code. options "CD9660" #ISO 9660 filesystem options FDESC #File descriptor filesystem options KERNFS #Kernel filesystem options MSDOSFS #MS DOS File System options NTFS #NT File System options NULLFS #NULL filesystem options PORTAL #Portal filesystem options PROCFS #Process filesystem options UMAPFS #UID map filesystem options UNION #Union filesystem # The xFS_ROOT options REQUIRE the associated ``options xFS'' options "CD9660_ROOT" #CD-ROM usable as root device options FFS_ROOT #FFS usable as root device options MFS_ROOT #MFS usable as root device options NFS_ROOT #NFS usable as root device # This code is still experimental (e.g. doesn't handle disk slices well). # Also, 'options MFS' is currently incompatible with DEVFS. options DEVFS #devices filesystem # Soft updates is technique for improving file system speed and # making abrupt shutdown less risky. It is not enabled by default due # to copyright restraints on the code that implement it. # # Read ../../ufs/ffs/README.softupdates to learn what you need to # do to enable this. ../../../contrib/sys/softupdates/README gives # more details on how they actually work. # #options SOFTUPDATES # Make space in the kernel for a MFS root filesystem. Define to the number # of kilobytes to reserve for the filesystem. options MFS_ROOT_SIZE=10 # Allows MFS filesystems to be exported via nfs options EXPORTMFS # Allow this many swap-devices. options NSWAPDEV=20 # Disk quotas are supported when this option is enabled. options QUOTA #enable disk quotas # Add more checking code to various filesystems #options NULLFS_DIAGNOSTIC #options KERNFS_DIAGNOSTIC #options UMAPFS_DIAGNOSTIC #options UNION_DIAGNOSTIC # In particular multi-session CD-Rs might require a huge amount of # time in order to "settle". If we are about mounting them as the # root f/s, we gotta wait a little. # # The number is supposed to be in seconds. options "CD9660_ROOTDELAY=20" # If you are running a machine just as a fileserver for PC and MAC # users, using SAMBA or Netatalk, you may consider setting this option # and keeping all those users' directories on a filesystem that is # mounted with the suiddir option. This gives new files the same # ownership as the directory (similiar to group). It's a security hole # if you let these users run programs, so confine it to file-servers # (but it'll save you lots of headaches in those cases). Root owned # directories are exempt and X bits are cleared. The suid bit must be # set on the directory as well; see chmod(1) PC owners can't see/set # ownerships so they keep getting their toes trodden on. This saves # you all the support calls as the filesystem it's used on will act as # they expect: "It's my dir so it must be my file". # options SUIDDIR # Add some error checking code to the null_bypass routine # in the NULL filesystem #options SAFETY # NFS options: options "NFS_MINATTRTIMO=3" # VREG attrib cache timeout in sec options "NFS_MAXATTRTIMO=60" options "NFS_MINDIRATTRTIMO=30" # VDIR attrib cache timeout in sec options "NFS_MAXDIRATTRTIMO=60" options "NFS_GATHERDELAY=10" # Default write gather delay (msec) options "NFS_UIDHASHSIZ=29" # Tune the size of nfssvc_sock with this options "NFS_WDELAYHASHSIZ=16" # and with this options "NFS_MUIDHASHSIZ=63" # Tune the size of nfsmount with this options NFS_DEBUG # Enable NFS Debugging # Coda stuff: options CODA #CODA filesystem. pseudo-device vcoda 4 #coda minicache <-> venus comm. # # Add support for the EXT2FS filesystem of Linux fame. Be a bit # careful with this - the ext2fs code has a tendency to lag behind # changes and not be exercised very much, so mounting read/write could # be dangerous (and even mounting read only could result in panics.) # options "EXT2FS" ##################################################################### # POSIX P1003.1B # Real time extensions added int the 1993 Posix # P1003_1B: Infrastructure # _KPOSIX_PRIORITY_SCHEDULING: Build in _POSIX_PRIORITY_SCHEDULING # _KPOSIX_VERSION: Version kernel is built for options "P1003_1B" options "_KPOSIX_PRIORITY_SCHEDULING" options "_KPOSIX_VERSION=199309L" ##################################################################### # SCSI DEVICES # SCSI DEVICE CONFIGURATION # The SCSI subsystem consists of the `base' SCSI code, a number of # high-level SCSI device `type' drivers, and the low-level host-adapter # device drivers. The host adapters are listed in the ISA and PCI # device configuration sections below. # # Beginning with FreeBSD 2.0.5 you can wire down your SCSI devices so # that a given bus, target, and LUN always come on line as the same # device unit. In earlier versions the unit numbers were assigned # in the order that the devices were probed on the SCSI bus. This # means that if you removed a disk drive, you may have had to rewrite # your /etc/fstab file, and also that you had to be careful when adding # a new disk as it may have been probed earlier and moved your device # configuration around. # This old behavior is maintained as the default behavior. The unit # assignment begins with the first non-wired down unit for a device # type. For example, if you wire a disk as "da3" then the first # non-wired disk will be assigned da4. # The syntax for wiring down devices is: # controller scbus0 at ahc0 # Single bus device # controller scbus1 at ahc1 bus 0 # Single bus device # controller scbus3 at ahc2 bus 0 # Twin bus device # controller scbus2 at ahc2 bus 1 # Twin bus device # disk da0 at scbus0 target 0 unit 0 # disk da1 at scbus3 target 1 # disk da2 at scbus2 target 3 # tape st1 at scbus1 target 6 # device cd0 at scbus? # "units" (SCSI logical unit number) that are not specified are # treated as if specified as LUN 0. # All SCSI devices allocate as many units as are required. # The "unknown" device (uk? in pre-2.0.5) is now part of the base SCSI # configuration and doesn't have to be explicitly configured. controller scbus0 #base SCSI code device ch0 #SCSI media changers device da0 #SCSI direct access devices (aka disks) device sa0 #SCSI tapes device cd0 #SCSI CD-ROMs #device od0 #SCSI optical disk device pass0 #CAM passthrough driver # The previous devices (ch, da, st, cd) are recognized by config. # config doesn't (and shouldn't) know about these newer ones, # so we have to specify that they are on a SCSI bus with the "at scbus?" # clause. device pt0 at scbus? # SCSI processor type device sctarg0 at scbus? # SCSI target # CAM OPTIONS: # debugging options: # -- NOTE -- If you specify one of the bus/target/lun options, you must # specify them all! # CAMDEBUG: When defined enables debugging macros # CAM_DEBUG_BUS: Debug the given bus. Use -1 to debug all busses. # CAM_DEBUG_TARGET: Debug the given target. Use -1 to debug all targets. # CAM_DEBUG_LUN: Debug the given lun. Use -1 to debug all luns. # CAM_DEBUG_FLAGS: OR together CAM_DEBUG_INFO, CAM_DEBUG_TRACE, # CAM_DEBUG_SUBTRACE, and CAM_DEBUG_CDB # # CAM_MAX_HIGHPOWER: Maximum number of concurrent high power (start unit) cmds # SCSI_NO_SENSE_STRINGS: When defined disables sense descriptions # SCSI_NO_OP_STRINGS: When defined disables opcode descriptions # SCSI_REPORT_GEOMETRY: Always report disk geometry at boot up instead # of only when booting verbosely. # SCSI_DELAY: The number of MILLISECONDS to freeze the SIM (scsi adapter) # queue after a bus reset, and the number of milliseconds to # freeze the device queue after a bus device reset. options CAMDEBUG options "CAM_DEBUG_BUS=-1" options "CAM_DEBUG_TARGET=-1" options "CAM_DEBUG_LUN=-1" options "CAM_DEBUG_FLAGS=CAM_DEBUG_INFO|CAM_DEBUG_TRACE|CAM_DEBUG_CDB" options "CAM_MAX_HIGHPOWER=4" options SCSI_NO_SENSE_STRINGS options SCSI_NO_OP_STRINGS options SCSI_REPORT_GEOMETRY options SCSI_DELAY=8000 # Be pessimistic about Joe SCSI device # Options for the CAM CDROM driver: # CHANGER_MIN_BUSY_SECONDS: Guaranteed minimum time quantum for a changer LUN # CHANGER_MAX_BUSY_SECONDS: Maximum time quantum per changer LUN, only # enforced if there is I/O waiting for another LUN # The compiled in defaults for these variables are 2 and 10 seconds, # respectively. # # These can also be changed on the fly with the following sysctl variables: # kern.cam.cd.changer.min_busy_seconds # kern.cam.cd.changer.max_busy_seconds # options "CHANGER_MIN_BUSY_SECONDS=2" options "CHANGER_MAX_BUSY_SECONDS=10" # Options for the CAM sequential access driver: # SA_SPACE_TIMEOUT: Timeout for space operations, in minutes # SA_REWIND_TIMEOUT: Timeout for rewind operations, in minutes # SA_ERASE_TIMEOUT: Timeout for erase operations, in minutes options "SA_SPACE_TIMEOUT=(60)" options "SA_REWIND_TIMEOUT=(2*60)" options "SA_ERASE_TIMEOUT=(4*60)" ##################################################################### # MISCELLANEOUS DEVICES AND OPTIONS # The `pty' device usually turns out to be ``effectively mandatory'', # as it is required for `telnetd', `rlogind', `screen', `emacs', and # `xterm', among others. pseudo-device pty 16 #Pseudo ttys - can go as high as 256 pseudo-device speaker #Play IBM BASIC-style noises out your speaker pseudo-device gzip #Exec gzipped a.out's pseudo-device vn #Vnode driver (turns a file into a device) pseudo-device snp 3 #Snoop device - to look at pty/vty/etc.. pseudo-device ccd 4 #Concatenated disk driver # Configuring Vinum into the kernel is not necessary, since the kld # module gets started automatically when vinum(8) starts. This # device is also untested. Use at your own risk. # # The option VINUMDEBUG must match the value set in CFLAGS # in /usr/src/sbin/vinum/Makefile. Failure to do so will result in # the following message from vinum(8): # # Can't get vinum config: Invalid argument # # see vinum(4) for more reasons not to use these options. pseudo-device vinum #Vinum concat/mirror/raid driver options VINUMDEBUG #enable Vinum debugging hooks # These are only for watching for bitrot in old tty code. # broken #pseudo-device tb # Size of the kernel message buffer. Should be N * pagesize. options "MSGBUF_SIZE=40960" ##################################################################### # HARDWARE DEVICE CONFIGURATION # ISA and EISA devices: # EISA support is available for some device, so they can be auto-probed. # Micro Channel is not supported at all. # # Mandatory ISA devices: isa, npx # -controller isa0 +controller isa0 at nexus? # # Options for `isa': # # AUTO_EOI_1 enables the `automatic EOI' feature for the master 8259A # interrupt controller. This saves about 0.7-1.25 usec for each interrupt. # This option breaks suspend/resume on some portables. # # AUTO_EOI_2 enables the `automatic EOI' feature for the slave 8259A # interrupt controller. This saves about 0.7-1.25 usec for each interrupt. # Automatic EOI is documented not to work for for the slave with the # original i8259A, but it works for some clones and some integrated # versions. # # MAXMEM specifies the amount of RAM on the machine; if this is not # specified, FreeBSD will first read the amount of memory from the CMOS # RAM, so the amount of memory will initially be limited to 64MB or 16MB # depending on the BIOS. If the BIOS reports 64MB, a memory probe will # then attempt to detect the installed amount of RAM. If this probe # fails to detect >64MB RAM you will have to use the MAXMEM option. # The amount is in kilobytes, so for a machine with 128MB of RAM, it would # be 131072 (128 * 1024). # # TUNE_1542 enables the automatic ISA bus speed selection for the # Adaptec 1542 boards. Does not work for all boards, use it with caution. # # BROKEN_KEYBOARD_RESET disables the use of the keyboard controller to # reset the CPU for reboot. This is needed on some systems with broken # keyboard controllers. # # PAS_JOYSTICK_ENABLE enables the gameport on the ProAudio Spectrum options "AUTO_EOI_1" #options "AUTO_EOI_2" options "MAXMEM=(128*1024)" options "TUNE_1542" #options BROKEN_KEYBOARD_RESET #options PAS_JOYSTICK_ENABLE # Enable support for the kernel PLL to use an external PPS signal, # under supervision of [x]ntpd(8) # More info in ntpd documentation: http://www.eecis.udel.edu/~ntp options PPS_SYNC # If you see the "calcru: negative time of %ld usec for pid %d (%s)\n" # message you probably have some broken sw/hw which disables interrupts # for too long. You can make the system more resistant to this by # choosing a high value for NTIMECOUNTER. The default is 5, there # is no upper limit but more than a couple of hundred are not productive. # A better strategy may be to sysctl -w kern.timecounter.method=1 options "NTIMECOUNTER=20" # Enable PnP support in the kernel. This allows you to automaticly # attach to PnP cards for drivers that support it and allows you to # configure cards from USERCONFIG. See pnp(4) for more info. controller pnp0 # The keyboard controller; it controlls the keyboard and the PS/2 mouse. -controller atkbdc0 at isa? port IO_KBD tty +controller atkbdc0 at isa? port IO_KBD # The AT keyboard -device atkbd0 at isa? tty irq 1 +device atkbd0 at atkbdc? tty irq 1 # Options for atkbd: options ATKBD_DFLT_KEYMAP # specify the built-in keymap makeoptions ATKBD_DFLT_KEYMAP="jp.106" # These options are valid for other keyboard drivers as well. options KBD_DISABLE_KEYMAP_LOAD # refuse to load a keymap options KBD_INSTALL_CDEV # install a CDEV entry in /dev # `flags' for atkbd: # 0x01 Force detection of keyboard, else we always assume a keyboard # 0x02 Don't reset keyboard, useful for some newer ThinkPads # 0x04 Old-style (XT) keyboard support, useful for older ThinkPads # PS/2 mouse -device psm0 at isa? tty irq 12 +device psm0 at atkbdc? tty irq 12 # Options for psm: options PSM_HOOKAPM #hook the APM resume event, useful #for some laptops options PSM_RESETAFTERSUSPEND #reset the device at the resume event # The video card driver. device vga0 at isa? port ? conflicts # Options for vga: # Try the following option if the mouse pointer is not drawn correctly # or font does not seem to be loaded properly. May cause flicker on # some systems. options VGA_ALT_SEQACCESS # If you can dispense with some vga driver features, you may want to # use the following options to save some memory. options VGA_NO_FONT_LOADING # don't save/load font options VGA_NO_MODE_CHANGE # don't change video modes # Older video cards may require this option for proper operation. options VGA_SLOW_IOACCESS # do byte-wide i/o's to TS and GDC regs # To include support for VESA video modes options VESA # needs VM86 defined too!! # Splash screen at start up! Screen savers require this too. pseudo-device splash # The pcvt console driver (vt220 compatible). device vt0 at isa? tty options XSERVER # support for running an X server. options FAT_CURSOR # start with block cursor # This PCVT option is for keyboards such as those used on IBM ThinkPad laptops options PCVT_SCANSET=2 # IBM keyboards are non-std # Other PCVT options are documented in pcvt(4). options "PCVT_24LINESDEF" options PCVT_CTRL_ALT_DEL options PCVT_EMU_MOUSE options PCVT_FREEBSD=211 options PCVT_META_ESC options PCVT_NSCREENS=9 options PCVT_PRETTYSCRNS options PCVT_SCREENSAVER options PCVT_USEKBDSEC options "PCVT_VT220KEYB" # The syscons console driver (sco color console compatible). device sc0 at isa? tty options MAXCONS=16 # number of virtual consoles options "STD8X16FONT" # Compile font in makeoptions "STD8X16FONT"="cp850" options SC_HISTORY_SIZE=200 # number of history buffer lines options SC_DISABLE_REBOOT # disable reboot key sequence # # `flags' for sc0: # 0x01 Use a 'visual' bell # 0x02 Use a 'blink' cursor # 0x04 Use a 'underline' cursor # 0x06 Use a 'blinking underline' (destructive) cursor # 0x40 Make the bell quiet if it is rung in the backgroud vty. # # The Numeric Processing eXtension driver. This should be configured if # your machine has a math co-processor, unless the coprocessor is very # buggy. If it is not configured then you *must* configure math emulation # (see above). If both npx0 and emulation are configured, then only npx0 # is used (provided it works). -device npx0 at isa? port IO_NPX iosiz 0x0 flags 0x0 irq 13 +device npx0 at nexus? port IO_NPX iosiz 0x0 flags 0x0 irq 13 # # `flags' for npx0: # 0x01 don't use the npx registers to optimize bcopy # 0x02 don't use the npx registers to optimize bzero # 0x04 don't use the npx registers to optimize copyin or copyout. # The npx registers are normally used to optimize copying and zeroing when # all of the following conditions are satisfied: # "I586_CPU" is an option # the cpu is an i586 (perhaps not a Pentium) # the probe for npx0 succeeds # INT 16 exception handling works. # Then copying and zeroing using the npx registers is normally 30-100% faster. # The flags can be used to control cases where it doesn't work or is slower. # Setting them at boot time using userconfig works right (the optimizations # are not used until later in the bootstrap when npx0 is attached). # # # `iosiz' for npx0: # This can be used instead of the MAXMEM option to set the memory size. If # it is nonzero, then it overrides both the MAXMEM option and the memory # size reported by the BIOS. Setting it at boot time using userconfig takes # effect on the next reboot after the change has been recorded in the kernel # binary (the size is used early in the boot before userconfig has a chance # to change it). # # # Optional ISA and EISA devices: # # # SCSI host adapters: `aha', `bt' # # adv: All Narrow SCSI bus AdvanSys controllers. # adw: Second Generation AdvanSys controllers including the ADV940UW. # aha: Adaptec 154x # ahc: Adaptec 274x/284x/294x # bt: Most Buslogic controllers # # Note that the order is important in order for Buslogic cards to be # probed correctly. # controller bt0 at isa? port "IO_BT0" cam irq ? controller adv0 at isa? port ? cam irq ? controller adw0 controller aha0 at isa? port ? cam irq ? # # ATA and ATAPI devices # This is work in progress, use at your own risk. # It currently reuses the majors of wd.c and friends. # It cannot co-exist with the old system in one kernel. # You only need one "controller ata0" for it to find all # PCI devices on modern machines. #controller ata0 #device atadisk0 # ATA disk drives #device atapicd0 # ATAPI CDROM drives #device atapifd0 # ATAPI floppy drives #device atapist0 # ATAPI tape drives # # If you need ISA only devices, this is the lines to add: #controller ata1 at isa? port "IO_WD1" bio irq 14 #controller ata2 at isa? port "IO_WD2" bio irq 15 # # All the controller lines can coexist, the driver will # find out which ones are there. # # ST-506, ESDI, and IDE hard disks: `wdc' and `wd' # # The flags fields are used to enable the multi-sector I/O and # the 32BIT I/O modes. The flags may be used in either the controller # definition or in the individual disk definitions. The controller # definition is supported for the boot configuration stuff. # # Each drive has a 16 bit flags value defined: # The low 8 bits are the maximum value for the multi-sector I/O, # where 0xff defaults to the maximum that the drive can handle. # The high bit of the 16 bit flags (0x8000) allows probing for # 32 bit transfers. Bit 14 (0x4000) enables a hack to wake # up powered-down laptop drives. Bit 13 (0x2000) allows # probing for PCI IDE DMA controllers, such as Intel's PIIX # south bridges. Bit 12 (0x1000) sets LBA mode instead of the # default CHS mode for accessing the drive. See the wd.4 man page. # # The flags field for the drives can be specified in the controller # specification with the low 16 bits for drive 0, and the high 16 bits # for drive 1. # e.g.: #controller wdc0 at isa? port "IO_WD1" bio irq 14 flags 0x00ff8004 # # specifies that drive 0 will be allowed to probe for 32 bit transfers and # a maximum multi-sector transfer of 4 sectors, and drive 1 will not be # allowed to probe for 32 bit transfers, but will allow multi-sector # transfers up to the maximum that the drive supports. # # If you are using a PCI controller that is not running in compatibility # mode (for example, it is a 2nd IDE PCI interface), then use config line(s) # such as: # #controller wdc2 at isa? port "0" bio irq ? flags 0xa0ffa0ff #disk wd4 at wdc2 drive 0 #disk wd5 at wdc2 drive 1 # #controller wdc3 at isa? port "0" bio irq ? flags 0xa0ffa0ff #disk wd6 at wdc3 drive 0 #disk wd7 at wdc3 drive 1 # # Note that the above config would be useful for a Promise card, when used # on a MB that already has a PIIX controller. Note the bogus irq and port # entries. These are automatically filled in by the IDE/PCI support. # controller wdc0 at isa? port "IO_WD1" bio irq 14 disk wd0 at wdc0 drive 0 disk wd1 at wdc0 drive 1 controller wdc1 at isa? port "IO_WD2" bio irq 15 disk wd2 at wdc1 drive 0 disk wd3 at wdc1 drive 1 # # This option allow you to override the default probe time for IDE # devices, to get a faster probe. Setting this below 10000 violate # the IDE specs, but may still work for you (it will work for most # people). # options IDE_DELAY=8000 # Be optimistic about Joe IDE device # IDE CD-ROM & CD-R/RW driver - requires wdc controller and ATAPI option device wcd0 # IDE floppy driver - requires wdc controller and ATAPI option device wfd0 # IDE tape driver - requires wdc controller and ATAPI option device wst0 # # Standard floppy disk controllers and floppy tapes: `fdc', `fd', and `ft' # controller fdc0 at isa? port "IO_FD1" bio irq 6 drq 2 # # FDC_DEBUG enables floppy debugging. Since the debug output is huge, you # gotta turn it actually on by setting the variable fd_debug with DDB, # however. options FDC_DEBUG # FDC_YE enables support for the floppies used on the Libretto. This is a # pcmcia floppy. You will also need to add #card "Y-E DATA" "External FDD" # config 0x4 "fdc0" 10 # to your pccard.conf file. options FDC_YE # This option is undocumented on purpose. options FDC_PRINT_BOGUS_CHIPTYPE # # Activate this line instead of the fdc0 line above if you happen to # have an Insight floppy tape. Probing them proved to be dangerous # for people with floppy disks only, so it's "hidden" behind a flag: #controller fdc0 at isa? port "IO_FD1" bio flags 1 irq 6 drq 2 disk fd0 at fdc0 drive 0 disk fd1 at fdc0 drive 1 # # Other standard PC hardware: `mse', `sio', etc. # # mse: Logitech and ATI InPort bus mouse ports # sio: serial ports (see sio(4)) device mse0 at isa? port 0x23c tty irq 5 device sio0 at isa? port "IO_COM1" tty flags 0x10 irq 4 # # `flags' for serial drivers that support consoles (only for sio now): # 0x10 enable console support for this unit. The other console flags # are ignored unless this is set. Enabling console support does # not make the unit the preferred console - boot with -h or set # the 0x20 flag for that. Currently, at most one unit can have # console support; the first one (in config file order) with # this flag set is preferred. Setting this flag for sio0 gives # the old behaviour. # 0x20 force this unit to be the console (unless there is another # higher priority console). This replaces the COMCONSOLE option. # 0x40 reserve this unit for low level console operations. Do not # access the device in any normal way. # # PnP `flags' (set via userconfig using pnp x flags y) # 0x1 disable probing of this device. Used to prevent your modem # from being attached as a PnP modem. # # Options for serial drivers that support consoles (only for sio now): options BREAK_TO_DEBUGGER #a BREAK on a comconsole goes to #DDB, if available. options CONSPEED=9600 #default speed for serial console (default 9600) # Options for sio: options COM_ESP #code for Hayes ESP options COM_MULTIPORT #code for some cards with shared IRQs options "EXTRA_SIO=2" #number of extra sio ports to allocate # Other flags for sio that aren't documented in the man page. # 0x20000 enable hardware RTS/CTS and larger FIFOs. Only works for # ST16650A-compatible UARTs. # # Network interfaces: `cx', `ed', `el', `ep', `ie', `is', `le', `lnc' # # ar: Arnet SYNC/570i hdlc sync 2/4 port V.35/X.21 serial driver (requires sppp) # cs: IBM Etherjet and other Crystal Semi CS89x0-based adapters # cx: Cronyx/Sigma multiport sync/async (with Cisco or PPP framing) # ed: Western Digital and SMC 80xx; Novell NE1000 and NE2000; 3Com 3C503 # el: 3Com 3C501 (slow!) # ep: 3Com 3C509 (buggy) # ex: Intel EtherExpress Pro/10 and other i82595-based adapters # fe: Fujitsu MB86960A/MB86965A Ethernet # ie: AT&T StarLAN 10 and EN100; 3Com 3C507; unknown NI5210; Intel EtherExpress # le: Digital Equipment EtherWorks 2 and EtherWorks 3 (DEPCA, DE100, # DE101, DE200, DE201, DE202, DE203, DE204, DE205, DE422) # lnc: Lance/PCnet cards (Isolan, Novell NE2100, NE32-VL, AMD Am7990 & Am79C960) # rdp: RealTek RTL 8002-based pocket ethernet adapters # sr: RISCom/N2 hdlc sync 1/2 port V.35/X.21 serial driver (requires sppp) # wl: Lucent Wavelan (ISA card only). # ze: IBM/National Semiconductor PCMCIA ethernet controller. # zp: 3Com PCMCIA Etherlink III (It does not require shared memory for # send/receive operation, but it needs 'iomem' to read/write the # attribute memory) # oltr: Olicom ISA token-ring adapters OC-3115, OC-3117, OC-3118 and OC-3133 # (no options needed) # device ar0 at isa? port 0x300 net irq 10 iomem 0xd0000 device cs0 at isa? port 0x300 net irq ? device cx0 at isa? port 0x240 net irq 15 drq 7 device ed0 at isa? port 0x280 net irq 5 iomem 0xd8000 device el0 at isa? port 0x300 net irq 9 device ep0 at isa? port 0x300 net irq 10 device ex0 at isa? port? net irq? device fe0 at isa? port 0x300 net irq ? device ie0 at isa? port 0x300 net irq 5 iomem 0xd0000 device ie1 at isa? port 0x360 net irq 7 iomem 0xd0000 device le0 at isa? port 0x300 net irq 5 iomem 0xd0000 device lnc0 at isa? port 0x280 net irq 10 drq 0 device rdp0 at isa? port 0x378 net irq 7 flags 2 device sr0 at isa? port 0x300 net irq 5 iomem 0xd0000 options WLCACHE # enables the signal-strength cache options WLDEBUG # enables verbose debugging output device wl0 at isa? port 0x300 net irq ? # We can (bogusly) include both the dedicated PCCARD drivers and the generic # support when COMPILING_LINT. device ze0 at isa? port 0x300 net irq 5 iomem 0xd8000 device zp0 at isa? port 0x300 net irq 10 iomem 0xd8000 device oltr0 at isa? # # ATM related options # # The `en' device provides support for Efficient Networks (ENI) # ENI-155 PCI midway cards, and the Adaptec 155Mbps PCI ATM cards (ANA-59x0). # # atm pseudo-device provides generic atm functions and is required for # atm devices. # NATM enables the netnatm protocol family that can be used to # bypass TCP/IP. # # the current driver supports only PVC operations (no atm-arp, no multicast). # for more details, please read the original documents at # http://www.ccrc.wustl.edu/pub/chuck/bsdatm/wucs.html # pseudo-device atm device en0 device en1 options NATM #native ATM # # Audio drivers: `snd', `sb', `pas', `gus', `pca' # # snd: Voxware sound support code # sb: SoundBlaster PCM - SoundBlaster, SB Pro, SB16, ProAudioSpectrum # sbxvi: SoundBlaster 16 # sbmidi: SoundBlaster 16 MIDI interface # pas: ProAudioSpectrum PCM and MIDI # gus: Gravis Ultrasound - Ultrasound, Ultrasound 16, Ultrasound MAX # gusxvi: Gravis Ultrasound 16-bit PCM (do not use) # mss: Microsoft Sound System # css: Crystal Sound System (CSS 423x PnP) # sscape: Ensoniq Soundscape MIDI interface # sscape_mss: Ensoniq Soundscape PCM (requires sscape) # opl: Yamaha OPL-2 and OPL-3 FM - SB, SB Pro, SB 16, ProAudioSpectrum # uart: stand-alone 6850 UART for MIDI # mpu: Roland MPU-401 stand-alone card # # Note: It has been reprted that ISA DMA with the SoundBlaster will # lock up the machine (PR docs/5358). If this happens to you, # turning off USWC write posting in your machine's BIOS may fix # the problem. # # Beware! The addresses specified below are also hard-coded in # i386/isa/sound/sound_config.h. If you change the values here, you # must also change the values in the include file. # # pcm: PCM audio through various sound cards. # # This has support for a large number of new audio cards, based on # CS423x, OPTi931, Yamaha OPL-SAx, and also for SB16, GusPnP. # For more information about this driver and supported cards, # see the pcm.4 man page and /sys/i386/isa/snd/CARDS. # # The flags of the device tells the device a bit more info about the # device that normally is obtained through the PnP interface. # bit 2..0 secondary DMA channel; # bit 4 set if the board uses two dma channels; # bit 15..8 board type, overrides autodetection; leave it # zero if don't know what to put in (and you don't, # since this is unsupported at the moment...). # # This driver will use the new PnP code if it's available. # # pca: PCM audio through your PC speaker # # If you have a GUS-MAX card and want to use the CS4231 codec on the # card the drqs for the gus max must be 8 bit (1, 2, or 3). # # If you would like to use the full duplex option on the gus, then define # flags to be the ``read dma channel''. # # options BROKEN_BUS_CLOCK #PAS-16 isn't working and OPTI chipset # options SYMPHONY_PAS #PAS-16 isn't working and SYMPHONY chipset # options EXCLUDE_SBPRO #PAS-16 # options SBC_IRQ=5 #PAS-16. Must match irq on sb0 line. # PAS16: The order of the pas0/sb0/opl0 is important since the # sb emulation is enabled in the pas-16 attach. # # To overide the GUS defaults use: # options GUS_DMA2 # options GUS_DMA # options GUS_IRQ # # The i386/isa/sound/sound.doc has more information. # Controls all "VOXWARE" driver sound devices. See Luigi's driver # below for an alternate which may work better for some cards. # controller snd0 device pas0 at isa? port 0x388 irq 10 drq 6 device sb0 at isa? port 0x220 irq 5 drq 1 device sbxvi0 at isa? drq 5 device sbmidi0 at isa? port 0x330 device awe0 at isa? port 0x620 device gus0 at isa? port 0x220 irq 12 drq 1 #device gus0 at isa? port 0x220 irq 12 drq 1 flags 0x3 device mss0 at isa? port 0x530 irq 10 drq 1 device css0 at isa? port 0x534 irq 5 drq 1 flags 0x08 device sscape0 at isa? port 0x330 irq 9 drq 0 device trix0 at isa? port 0x330 irq 6 drq 0 device sscape_mss0 at isa? port 0x534 irq 5 drq 1 device opl0 at isa? port 0x388 device mpu0 at isa? port 0x330 irq 6 drq 0 device uart0 at isa? port 0x330 irq 5 # Luigi's snd code (use INSTEAD of snd0 and all VOXWARE drivers!). # You may also wish to enable the pnp controller with this, for pnp # sound cards. # #device pcm0 at isa? port ? tty irq 10 drq 1 flags 0x0 # Not controlled by `snd' device pca0 at isa? port "IO_TIMER1" tty # # Miscellaneous hardware: # # mcd: Mitsumi CD-ROM # scd: Sony CD-ROM # matcd: Matsushita/Panasonic CD-ROM # wt: Wangtek and Archive QIC-02/QIC-36 tape drives # ctx: Cortex-I frame grabber # apm: Laptop Advanced Power Management (experimental) # spigot: The Creative Labs Video Spigot video-acquisition board # meteor: Matrox Meteor video capture board # bktr: Brooktree bt848/848a/849/878/879 family video capture and TV Tuner board # cy: Cyclades serial driver # dgb: Digiboard PC/Xi and PC/Xe series driver (ALPHA QUALITY!) # dgm: Digiboard PC/Xem driver # gp: National Instruments AT-GPIB and AT-GPIB/TNT board # asc: GI1904-based hand scanners, e.g. the Trust Amiscan Grey # gsc: Genius GS-4500 hand scanner. # joy: joystick # labpc: National Instrument's Lab-PC and Lab-PC+ # rc: RISCom/8 multiport card # rp: Comtrol Rocketport(ISA) - single card # tw: TW-523 power line interface for use with X-10 home control products # si: Specialix SI/XIO 4-32 port terminal multiplexor # stl: Stallion EasyIO and EasyConnection 8/32 (cd1400 based) # stli: Stallion EasyConnection 8/64, ONboard, Brumby (intelligent) # Notes on APM # The flags takes the following meaning for apm0: # 0x0020 Statclock is broken. # 0x0011 Limit APM protocol to 1.1 or 1.0 # 0x0010 Limit APM protocol to 1.0 # If apm is omitted, some systems require sysctl -w kern.timcounter.method=1 # for correct timekeeping. # Notes on the spigot: # The video spigot is at 0xad6. This port address can not be changed. # The irq values may only be 10, 11, or 15 # I/O memory is an 8kb region. Possible values are: # 0a0000, 0a2000, ..., 0fffff, f00000, f02000, ..., ffffff # The start address must be on an even boundary. # Add the following option if you want to allow non-root users to be able # to access the spigot. This option is not secure because it allows users # direct access to the I/O page. # options SPIGOT_UNSECURE # Notes on the Comtrol Rocketport driver: # # The exact values used for rp0 depend on how many boards you have # in the system. The manufacturer's sample configs are listed as: # # Comtrol Rocketport ISA single card # device rp0 at isa? port 0x280 tty # # If instead you have two ISA cards, one installed at 0x100 and the # second installed at 0x180, then you should add the following to # your kernel configuration file: # # device rp0 at isa? port 0x100 tty # device rp1 at isa? port 0x180 tty # # For 4 ISA cards, it might be something like this: # # device rp0 at isa? port 0x180 tty # device rp1 at isa? port 0x100 tty # device rp2 at isa? port 0x340 tty # device rp3 at isa? port 0x240 tty # # And for PCI cards, you only need say: # # device rp0 # device rp1 # ... # Note: Make sure that any Rocketport PCI devices are specified BEFORE the # ISA Rocketport devices. # Notes on the Digiboard driver: # # The following flag values have special meanings: # 0x01 - alternate layout of pins (dgb & dgm) # 0x02 - use the windowed PC/Xe in 64K mode (dgb only) # Notes on the Specialix SI/XIO driver: # **This is NOT a Specialix supported Driver!** # The host card is memory, not IO mapped. # The Rev 1 host cards use a 64K chunk, on a 32K boundary. # The Rev 2 host cards use a 32K chunk, on a 32K boundary. # The cards can use an IRQ of 11, 12 or 15. # Notes on the Stallion stl and stli drivers: # See src/i386/isa/README.stl for complete instructions. # This is version 0.0.5alpha, unsupported by Stallion. # The stl driver has a secondary IO port hard coded at 0x280. You need # to change src/i386/isa/stallion.c if you reconfigure this on the boards. # The "flags" and "iosiz" settings on the stli driver depend on the board: # EasyConnection 8/64 ISA: flags 23 iosiz 0x1000 # EasyConnection 8/64 EISA: flags 24 iosiz 0x10000 # EasyConnection 8/64 MCA: flags 25 iosiz 0x1000 # ONboard ISA: flags 4 iosiz 0x10000 # ONboard EISA: flags 7 iosiz 0x10000 # ONboard MCA: flags 3 iosiz 0x10000 # Brumby: flags 2 iosiz 0x4000 # Stallion: flags 1 iosiz 0x10000 device mcd0 at isa? port 0x300 bio irq 10 # for the Sony CDU31/33A CDROM device scd0 at isa? port 0x230 bio # for the SoundBlaster 16 multicd - up to 4 devices controller matcd0 at isa? port 0x230 bio device wt0 at isa? port 0x300 bio irq 5 drq 1 device ctx0 at isa? port 0x230 iomem 0xd0000 device spigot0 at isa? port 0xad6 irq 15 iomem 0xee000 -device apm0 at isa? +device apm0 at nexus? device gp0 at isa? port 0x2c0 tty device gsc0 at isa? port "IO_GSC1" tty drq 3 device joy0 at isa? port IO_GAME device cy0 at isa? tty irq 10 iomem 0xd4000 iosiz 0x2000 options CY_PCI_FASTINTR # Use with cy_pci unless irq is shared device dgb0 at isa? port 0x220 iomem 0xfc000 iosiz ? tty options "NDGBPORTS=16" # Defaults to 16*NDGB device dgm0 at isa? port 0x104 iomem 0xd0000 iosiz ? tty device labpc0 at isa? port 0x260 tty irq 5 device rc0 at isa? port 0x220 tty irq 12 device rp0 at isa? port 0x280 tty # the port and irq for tw0 are fictitious device tw0 at isa? port 0x380 tty irq 11 device si0 at isa? iomem 0xd0000 tty irq 12 device asc0 at isa? port "IO_ASC1" tty drq 3 irq 10 device stl0 at isa? port 0x2a0 tty irq 10 device stli0 at isa? port 0x2a0 tty iomem 0xcc000 flags 23 iosiz 0x1000 # You are unlikely to have the hardware for loran0 device loran0 at isa? port ? tty irq 5 # HOT1 Xilinx 6200 card (www.vcc.com) device xrpu0 # # EISA devices: # # The EISA bus device is eisa0. It provides auto-detection and # configuration support for all devices on the EISA bus. # # The `ahb' device provides support for the Adaptec 174X adapter. # # The `ahc' device provides support for the Adaptec 274X and 284X # adapters. The 284X, although a VLB card responds to EISA probes. # # fea: DEC DEFEA EISA FDDI adapter # controller eisa0 controller ahb0 controller ahc0 device fea0 # The aic7xxx driver will attempt to use memory mapped I/O for all PCI # controllers that have it configured only if this option is set. Unfortunately, # this doesn't work on some motherboards, which prevents it from being the # default. options AHC_ALLOW_MEMIO # By default, only 10 EISA slots are probed, since the slot numbers # above clash with the configuration address space of the PCI subsystem, # and the EISA probe is not very smart about this. This is sufficient # for most machines, but in particular the HP NetServer LC series comes # with an onboard AIC7770 dual-channel SCSI controller on EISA slot #11, # thus you need to bump this figure to 12 for them. options "EISA_SLOTS=12" # # PCI devices & PCI options: # # The main PCI bus device is `pci'. It provides auto-detection and # configuration support for all devices on the PCI bus, using either # configuration mode defined in the PCI specification. # # The `ahc' device provides support for the Adaptec 29/3940(U)(W) # and motherboard based AIC7870/AIC7880 adapters. # # The `ncr' device provides support for the NCR 53C810 and 53C825 # self-contained SCSI host adapters. # # The `isp' device provides support for the Qlogic ISP 1020, 1040 # nd 1040B PCI SCSI host adapters, as well as the Qlogic ISP 2100 # FC/AL Host Adapter. # # The `ax' device provides support for PCI fast ethernet adapters # based on the ASIX Electronics AX88140A chip, including the Alfa # Inc. GFC2204. # # The `de' device provides support for the Digital Equipment DC21040 # self-contained Ethernet adapter. # # The `fxp' device provides support for the Intel EtherExpress Pro/100B # PCI Fast Ethernet adapters. # # The `mx' device provides support for various fast ethernet adapters # based on the Macronix 98713, 987615 ans 98725 series chips. # # The `pn' device provides support for various fast ethernet adapters # based on the Lite-On 82c168 and 82c169 PNIC chips, including the # LinkSys LNE100TX, the NetGear FA310TX rev. D1 and the Matrox # FastNIC 10/100. # # The 'rl' device provides support for PCI fast ethernet adapters based # on the RealTek 8129/8139 chipset. Note that the RealTek driver defaults # to useing programmed I/O to do register accesses because memory mapped # mode seems to cause severe lockups on SMP hardware. This driver also # supports the Accton EN1207D `Cheetah' adapter, which uses a chip called # the MPX 5030/5038, which is either a RealTek in disguise or a RealTek # workalike. # # The 'ti' device provides support for PCI gigabit ethernet NICs based # on the Alteon Networks Tigon 1 and Tigon 2 chipsets. This includes the # Alteon AceNIC, the 3Com 3c985, the Netgear GA620 and various others. # Note that you will probably want to bump up NBMCLUSTERS a lot to use # this driver. # # The 'tl' device provides support for the Texas Instruments TNETE100 # series 'ThunderLAN' cards and integrated ethernet controllers. This # includes several Compaq Netelligent 10/100 cards and the built-in # ethernet controllers in several Compaq Prosignia, Proliant and # Deskpro systems. It also supports several Olicom 10Mbps and 10/100 # boards. # # The `tx' device provides support for the SMC 9432TX cards. # # The `vr' device provides support for various fast ethernet adapters # based on the VIA Technologies VT3043 `Rhine I' and VT86C100A `Rhine II' # chips, including the D-Link DFE530TX. # # The `vx' device provides support for the 3Com 3C590 and 3C595 # early support # # The `wb' device provides support for various fast ethernet adapters # based on the Winbond W89C840F chip. Note: this is not the same as # the Winbond W89C940F, which is an NE2000 clone. # # The `xl' device provides support for the 3Com 3c900, 3c905 and # 3c905B (Fast) Etherlink XL cards and integrated controllers. This # includes the integrated 3c905B-TX chips in certain Dell Optiplex and # Dell Precision desktop machines and the integrated 3c905-TX chips # in Dell Latitude laptop docking stations. # # The `fpa' device provides support for the Digital DEFPA PCI FDDI # adapter. pseudo-device fddi is also needed. # # The `meteor' device is a PCI video capture board. It can also have the # following options: # options METEOR_ALLOC_PAGES=xxx preallocate kernel pages for data entry # figure (ROWS*COLUMN*BYTES_PER_PIXEL*FRAME+PAGE_SIZE-1)/PAGE_SIZE # options METEOR_DEALLOC_PAGES remove all allocated pages on close(2) # options METEOR_DEALLOC_ABOVE=xxx remove all allocated pages above the # specified amount. If this value is below the allocated amount no action # taken # options METEOR_SYSTEM_DEFAULT={METEOR_PAL|METEOR_NTSC|METEOR_SECAM}, used # for initialization of fps routine when a signal is not present. # # The 'bktr' device is a PCI video capture device using the Brooktree # bt848/bt848a/bt849/bt878/bt879 chipset. When used with a TV Tuner it forms a # TV card, eg Miro PC/TV,Hauppauge WinCast/TV WinTV, VideoLogic Captivator, # Intel Smart Video III, AverMedia, IMS Turbo. # The following options can be used to override the auto detection # options OVERRIDE_CARD=xxx # options OVERRIDE_TUNER=xxx # options OVERRIDE_MSP=1 # options OVERRIDE_DBX=1 # The current values are found in /usr/src/sys/pci/brooktree848.c # # options BROOKTREE_SYSTEM_DEFAULT=BROOKTREE_PAL # This is required for Dual Crystal (28&35Mhz) boards where PAL is used # to prevent hangs during initialisation. eg VideoLogic Captivator PCI. # # PAL or SECAM users who have a 28Mhz crystal (and no 35Mhz crystal) # must enable PLL mode with this option. eg some new Hauppauge cards. # options BKTR_USE_PLL # # Using sysctl(8) run-time overrides on a per-card basis can be made # # The "oltr" driver supports the following Olicom PCI token-ring adapters # OC-3136, OC-3137, OC-3139, OC-3140, OC-3141, OC-3540, OC-3250 # -controller pci0 +controller pci0 at nexus? controller ahc1 controller ncr0 controller isp0 # # Options for ISP # # SCSI_ISP_NO_FWLOAD_MASK - mask of isp unit numbers (obviously # a max of 32) that you wish to disable # to disable the loading of firmware on. # SCSI_ISP_NO_NVRAM_MASK - mask of isp unit numbers (obviously # a max of 32) that you wish to disable # them picking up information from NVRAM # (for broken cards you can't fix the NVRAM # on- very rare, or for systems you can't # change NVRAM on (e.g. alpha) and you don't # like what's in there) # SCSI_ISP_PREFER_MEM_MAP - control preference for using memory mappings # instead of I/O space mappings. It defaults # to 1 for i386, 0 for alpha. Set to 1 to # unconditionally prefer mapping memory, # else it will use I/O space mappings. Of # course, this can fail if the PCI implement- # ation doesn't support what you want. # # SCSI_ISP_FABRIC enable loading of Fabric f/w flavor (2100). # SCSI_ISP_SCCLUN enable loading of expanded lun f/w (2100). # # ISP_DISABLE_1020_SUPPORT Disable support for 1020/1040 cards # ISP_DISABLE_1080_SUPPORT Disable support for 1080/1240 cards # ISP_DISABLE_2100_SUPPORT Disable support for 2100 cards # (these really just to save code space) # (use of all three will cause the driver to not compile) options SCSI_ISP_NO_FWLOAD_MASK="0x12" # disable FW load for isp1 and isp4 options SCSI_ISP_NO_NVRAM_MASK="0x1" # disable NVRAM for isp0 options SCSI_ISP_PREFER_MEM_MAP="0" # prefer I/O mapping #options "ISP_DISABLE_1020_SUPPORT" #options "ISP_DISABLE_1080_SUPPORT" #options "ISP_DISABLE_2100_SUPPORT" device ax0 device de0 device fxp0 device mx0 device pn0 device rl0 device ti0 device tl0 device tx0 device vr0 device vx0 device wb0 device xl0 device fpa0 device meteor0 device oltr0 # Brooktree driver has been ported to the new I2C framework. Thus, # you'll need at least iicbus, iicbb and smbus. iic/smb are only needed if you # want to control other I2C slaves connected to the external connector of # some cards. # device bktr0 # # PCI options # #options PCI_QUIET #quiets PCI code on chipset settings # # PCCARD/PCMCIA # # card: slot controller # pcic: slots controller card0 device pcic0 at card? device pcic1 at card? # You may need to reset all pccards after resuming options PCIC_RESUME_RESET # reset after resume # # Laptop/Notebook options: # # See also: # apm under `Miscellaneous hardware' # above. # For older notebooks that signal a powerfail condition (external # power supply dropped, or battery state low) by issuing an NMI: options POWERFAIL_NMI # make it beep instead of panicing # # SMB bus # # System Management Bus support provided by the 'smbus' device. # # Supported devices: # smb standard io # # Supported interfaces: # iicsmb I2C to SMB bridge with any iicbus interface # bktr brooktree848 I2C hardware interface # intpm Intel PIIX4 Power Management Unit # alpm Acer Aladdin-IV/V/Pro2 Power Management Unit # controller smbus0 controller intpm0 controller alpm0 device smb0 at smbus? # # I2C Bus # # Philips i2c bus support is provided by the `iicbus' device. # # Supported devices: # ic i2c network interface # iic i2c standard io # iicsmb i2c to smb bridge. Allow i2c i/o with smb commands. # # Supported interfaces: # pcf Philips PCF8584 ISA-bus controller # bktr brooktree848 I2C software interface # # Other: # iicbb generic I2C bit-banging code (needed by lpbb, bktr) # controller iicbus0 controller iicbb0 device ic0 at iicbus? device iic0 at iicbus? device iicsmb0 at iicbus? controller pcf0 at isa? port 0x320 net irq 5 # ISDN4BSD section # i4b passive ISDN cards support (isic - I4b Siemens Isdn Chipset driver) # note that the ``options'' and ``device'' lines must BOTH be defined ! # # Non-PnP Cards: # -------------- # # Teles S0/8 or Niccy 1008 options "TEL_S0_8" #device isic0 at isa? iomem 0xd0000 net irq 5 flags 1 # # Teles S0/16 or Creatix ISDN-S0 or Niccy 1016 options "TEL_S0_16" #device isic0 at isa? port 0xd80 iomem 0xd0000 net irq 5 flags 2 # # Teles S0/16.3 options "TEL_S0_16_3" #device isic0 at isa? port 0xd80 net irq 5 flags 3 # # AVM A1 or AVM Fritz!Card options "AVM_A1" #device isic0 at isa? port 0x340 net irq 5 flags 4 # # USRobotics Sportster ISDN TA intern options "USR_STI" #device isic0 at isa? port 0x268 net irq 5 flags 7 # # ITK ix1 Micro options "ITKIX1" #device isic0 at isa? port 0x398 net irq 10 flags 18 # # PnP-Cards: # ---------- # # Teles S0/16.3 PnP options "TEL_S0_16_3_P" #device isic0 at isa? port ? net irq ? # # Creatix ISDN-S0 P&P options "CRTX_S0_P" #device isic0 at isa? port ? net irq ? # # Dr. Neuhaus Niccy Go@ options "DRN_NGO" #device isic0 at isa? port ? net irq ? # # Sedlbauer Win Speed options "SEDLBAUER" #device isic0 at isa? port ? net irq ? # # Dynalink IS64PH options "DYNALINK" #device isic0 at isa? port ? net irq ? # # ELSA QuickStep 1000pro ISA options "ELSA_QS1ISA" #device isic0 at isa? port ? net irq ? # # PCI-Cards: # ---------- # # ELSA QuickStep 1000pro PCI options "ELSA_QS1PCI" #device isic0 # # PCMCIA-Cards: # ------------- # # AVM PCMCIA Fritz!Card options "AVM_A1_PCMCIA" device isic0 at isa? port 0x340 net irq 5 flags 10 # # Active Cards: # ------------- # # Stollmann Tina-dd control device device tina0 at isa? port 0x260 net irq 10 # # ISDN Protocol Stack # ------------------- # # Q.921 / layer 2 - i4b passive cards D channel handling pseudo-device "i4bq921" # # Q.931 / layer 3 - i4b passive cards D channel handling pseudo-device "i4bq931" # # layer 4 - i4b common passive and active card handling pseudo-device "i4b" # # ISDN devices # ------------ # # userland driver to do ISDN tracing (for passive cards only) pseudo-device "i4btrc" 4 # # userland driver to control the whole thing pseudo-device "i4bctl" # # userland driver for access to raw B channel pseudo-device "i4brbch" 4 # # userland driver for telephony pseudo-device "i4btel" 2 # # network driver for IP over raw HDLC ISDN pseudo-device "i4bipr" 4 # enable VJ header compression detection for ipr i/f options IPR_VJ # # network driver for sync PPP over ISDN pseudo-device "i4bisppp" 4 # Parallel-Port Bus # # Parallel port bus support is provided by the `ppbus' device. # Multiple devices may be attached to the parallel port, devices # are automatically probed and attached when found. # # Supported devices: # vpo Iomega Zip Drive # Requires SCSI disk support ('scbus' and 'da'), best # performance is achieved with ports in EPP 1.9 mode. # lpt Parallel Printer # plip Parallel network interface # ppi General-purpose I/O ("Geek Port") + IEEE1284 I/O # pps Pulse per second Timing Interface # lpbb Philips official parallel port I2C bit-banging interface # # Supported interfaces: # ppc ISA-bus parallel port interfaces. # options "DEBUG_1284" # IEEE1284 signaling protocol debug options "PERIPH_1284" # Makes your computer act as a IEEE1284 # compliant peripheral options "DONTPROBE_1284"# Avoid boot detection of PnP parallel devices options "VP0_DEBUG" # ZIP/ZIP+ debug options "LPT_DEBUG" # Printer driver debug options "PPC_DEBUG" # Parallel chipset level debug options "PLIP_DEBUG" # Parallel network IP interface debug controller ppbus0 controller vpo0 at ppbus? device lpt0 at ppbus? device plip0 at ppbus? device ppi0 at ppbus? device pps0 at ppbus? device lpbb0 at ppbus? device ppc0 at isa? port? tty irq 7 # Kernel BOOTP support options BOOTP # Use BOOTP to obtain IP address/hostname options BOOTP_NFSROOT # NFS mount root filesystem using BOOTP info options "BOOTP_NFSV3" # Use NFS v3 to NFS mount root options BOOTP_COMPAT # Workaround for broken bootp daemons. options "BOOTP_WIRED_TO=fxp0" # Use interface fxp0 for BOOTP # # Add tie-ins for a hardware watchdog. This only enable the hooks; # the user must still supply the actual driver. # options HW_WDOG # # Set the number of PV entries per process. Increasing this can # stop panics related to heavy use of shared memory. However, that can # (combined with large amounts of physical memory) cause panics at # boot time due the kernel running out of VM space. # # If you're tweaking this, you might also want to increase the sysctls # "vm.v_free_min", "vm.v_free_reserved", and "vm.v_free_target". # # The value below is the one more than the default. # options "PMAP_SHPGPERPROC=201" # # Disable swapping. This option removes all code which actually performs # swapping, so it's not possible to turn it back on at run-time. # # This is sometimes usable for systems which don't have any swap space # (see also sysctls "vm.defer_swapspace_pageouts" and # "vm.disable_swapspace_pageouts") # #options NO_SWAPPING # Set the number of sf_bufs to allocate. sf_bufs are virtual buffers # for sendfile(2) that are used to map file VM pages, and normally # default to a quantity that is roughly 16*MAXUSERS+512. You would # typically want about 4 of these for each simultaneous file send. # options "NSFBUFS=1024" # # Enable extra debugging code for locks. This stores the filename and # line of whatever aquired the lock in the lock itself, and change a # number of function calls to pass around the relevant data. This is # not at all useful unless you are debugging lock code. Also note # that it is likely to break e.g. fstat(1) unless you recompile your # userland with -DDEBUG_LOCKS as well. # options DEBUG_LOCKS # More undocumented options for linting. options CLK_CALIBRATION_LOOP options "CLK_USE_I8254_CALIBRATION" options CLK_USE_TSC_CALIBRATION options "TIMER_FREQ=((14318182+6)/12)" options CLUSTERDEBUG options COMPAT_LINUX options CPU_UPGRADE_HW_CACHE options DEBUG options DEBUG_VFS_LOCKS #options DISABLE_PSE options "I586_PMC_GUPROF=0x70000" options "IBCS2" options KEY options KEY_DEBUG options LOCKF_DEBUG options LOUTB options KBD_MAXRETRY=4 options KBD_MAXWAIT=6 options KBD_RESETDELAY=201 options KBDIO_DEBUG=2 options MSGMNB=2049 options MSGMNI=41 options MSGSEG=2049 options MSGSSZ=16 options MSGTQL=41 options NBUF=512 options NETATALKDEBUG options NMBCLUSTERS=1024 options NPX_DEBUG options PANIC_REBOOT_WAIT_TIME=16 options PSM_DEBUG=1 options SCSI_NCR_DEBUG options SCSI_NCR_DFLT_TAGS=4 options SCSI_NCR_MAX_SYNC=10000 options SCSI_NCR_MAX_WIDE=1 options SCSI_NCR_MYADDR=7 options SEMMAP=31 options SEMMNI=11 options SEMMNS=61 options SEMMNU=31 options SEMMSL=61 options SEMOPM=101 options SEMUME=11 options SHOW_BUSYBUFS # List buffers that prevent root unmount options SHMALL=1025 options "SHMMAX=(SHMMAXPGS*PAGE_SIZE+1)" options SHMMAXPGS=1025 options SHMMIN=2 options SHMMNI=33 options SHMSEG=9 options SI_DEBUG options SIMPLELOCK_DEBUG options SPX_HACK options VFS_BIO_DEBUG options ENABLE_ALART # The 'dpt' driver provides support for DPT controllers (http://www.dpt.com/). # These have hardware RAID-{0,1,5} support, and do multi-initiator I/O. # The DPT controllers are commonly re-licensed under other brand-names - # some controllers by Olivetti, Dec, HP, AT&T, SNI, AST, Alphatronic, NEC and # Compaq are actually DPT controllers. # # See sys/dev/dpt for debugging and other subtle options. # DPT_VERIFY_HINTR Performs some strict hardware interrupts testing. # Only use if you suspect PCI bus corruption problems # DPT_RESTRICTED_FREELIST Normally, the freelisat used by the DPT for queue # will grow to accomodate increased use. This growth # will NOT shrink. To restrict the number of queue # slots to exactly what the DPT can hold at one time, # enable this option. # DPT_MEASURE_PERFORMANCE Enables a set of (semi)invasive metrics. Various # instruments are enabled. The tools in # /usr/sbin/dpt_* assume these to be enabled. # DPT_FREELIST_IS_STACK For optimal L{1,2} CPU cache utilization, enable # this option. Otherwise, the transaction queue is # a LIFO. I cannot measure the performance gain. # DPT_HANDLE_TIMEOUTS Normally device timeouts are handled by the DPT. # If you ant the driver to handle timeouts, enable # this option. If your system is very busy, this # option will create more trouble than solve. # DPT_TIMEOUT_FACTOR Used to compute the excessive amount of time to # wait when timing out with the above option. # DPT_DEBUG_xxxx These are controllable from sys/dev/dpt/dpt.h # DPT_LOST_IRQ When enabled, will try, once per second, to catch # any interrupt that got lost. Seems to help in some # DPT-firmware/Motherboard combinations. Minimal # cost, great benefit. # DPT_RESET_HBA Make "reset" actually reset the controller # instead of fudging it. Only enable this if you # are 100% certain you need it. # DPT_SHUTDOWN_SLEEP Reset controller if a request take more than # this number of seconds. Do NOT enable this # unless you are really, really, really certain # you need it. You are advised to call Simon (the # driver author) before setting it, and NEVER, # EVER set it to less than 300s (5 minutes). controller dpt0 # DPT options options DPT_VERIFY_HINTR options DPT_RESTRICTED_FREELIST #!CAM# options DPT_MEASURE_PERFORMANCE options DPT_FREELIST_IS_STACK #!CAM# options DPT_HANDLE_TIMEOUTS options DPT_TIMEOUT_FACTOR=4 options DPT_INTR_DELAY=200 # Some motherboards need that options DPT_LOST_IRQ options DPT_RESET_HBA # Don't EVER set this without having talked to Simon Shapiro on the phone # first. options DPT_SHUTDOWN_SLEEP=500 # USB support # UHCI controller controller uhci0 # OHCI controller controller ohci0 # General USB code (mandatory for USB) controller usb0 # # for the moment we have to specify the priorities of the device # drivers explicitly by the ordering in the list below. This will # be changed in the future. # # USB mouse device ums0 # USB keyboard device ukbd0 # USB printer device ulpt0 # Human Interface Device (anything with buttons and dials) device uhid0 # Generic USB device driver device ugen0 # options UHCI_DEBUG options OHCI_DEBUG options USB_DEBUG options UHUB_DEBUG options UMS_DEBUG options UKBD_DEBUG options UMASS_DEBUG options UHID_DEBUG options UGEN_DEBUG options ULPT_DEBUG Index: head/sys/conf/files =================================================================== --- head/sys/conf/files (revision 45719) +++ head/sys/conf/files (revision 45720) @@ -1,702 +1,717 @@ # # The long compile-with and dependency lines are required because of # limitations in config: backslash-newline doesn't work in strings, and # dependency lines other than the first are silently ignored. # aicasm optional ahc device-driver \ dependency "$S/dev/aic7xxx/*.[chyl]" \ compile-with "${MAKE} -f $S/dev/aic7xxx/Makefile MAKESRCPATH=$S/dev/aic7xxx" \ no-obj no-implicit-rule \ clean "aicasm aicasm_gram.c aicasm_scan.c y.tab.h" aic7xxx_{seq,reg}.h optional ahc device-driver \ compile-with "./aicasm ${INCLUDES} -o aic7xxx_seq.h -r aic7xxx_reg.h $S/dev/aic7xxx/aic7xxx.seq" \ no-obj no-implicit-rule before-depend \ clean "aic7xxx_seq.h aic7xxx_reg.h" \ dependency "$S/dev/aic7xxx/aic7xxx.{reg,seq} $S/cam/scsi/scsi_message.h aicasm" device_if.o standard \ compile-with "${NORMAL_C}" \ no-implicit-rule local device_if.c standard \ dependency "$S/kern/makedevops.pl $S/kern/device_if.m" \ compile-with "perl5 $S/kern/makedevops.pl -c $S/kern/device_if.m" \ no-obj no-implicit-rule before-depend local \ clean "device_if.c" device_if.h standard \ dependency "$S/kern/makedevops.pl $S/kern/device_if.m" \ compile-with "perl5 $S/kern/makedevops.pl -h $S/kern/device_if.m" \ no-obj no-implicit-rule before-depend \ clean "device_if.h" bus_if.o standard \ compile-with "${NORMAL_C}" \ no-implicit-rule local bus_if.c standard \ dependency "$S/kern/makedevops.pl $S/kern/bus_if.m" \ compile-with "perl5 $S/kern/makedevops.pl -c $S/kern/bus_if.m" \ no-obj no-implicit-rule before-depend local \ clean "bus_if.c" bus_if.h standard \ dependency "$S/kern/makedevops.pl $S/kern/bus_if.m" \ compile-with "perl5 $S/kern/makedevops.pl -h $S/kern/bus_if.m" \ no-obj no-implicit-rule before-depend \ clean "bus_if.h" coda/coda_namecache.c optional vcoda coda/coda_fbsd.c optional vcoda coda/coda_psdev.c optional vcoda coda/coda_subr.c optional vcoda coda/coda_venus.c optional vcoda coda/coda_vfsops.c optional vcoda coda/coda_vnops.c optional vcoda cam/cam.c optional scbus cam/cam_xpt.c optional scbus cam/cam_extend.c optional scbus cam/cam_queue.c optional scbus cam/cam_periph.c optional scbus cam/cam_sim.c optional scbus cam/scsi/scsi_all.c optional scbus cam/scsi/scsi_da.c optional da cam/scsi/scsi_pt.c optional pt cam/scsi/scsi_sa.c optional sa cam/scsi/scsi_cd.c optional cd cam/scsi/scsi_ch.c optional ch cam/scsi/scsi_pass.c optional pass cam/scsi/scsi_scan.c optional scan cam/scsi/scsi_target.c optional targ cam/scsi/scsi_targ_bh.c optional targbh ddb/db_access.c optional ddb ddb/db_kld.c optional ddb ddb/db_aout.c optional ddb ddb/db_break.c optional ddb ddb/db_command.c optional ddb ddb/db_examine.c optional ddb ddb/db_expr.c optional ddb ddb/db_input.c optional ddb ddb/db_lex.c optional ddb ddb/db_output.c optional ddb ddb/db_print.c optional ddb ddb/db_ps.c optional ddb ddb/db_run.c optional ddb ddb/db_sym.c optional ddb ddb/db_trap.c optional ddb ddb/db_variables.c optional ddb ddb/db_watch.c optional ddb ddb/db_write_cmd.c optional ddb dev/advansys/advansys.c optional adv device-driver dev/advansys/advlib.c optional adv device-driver dev/advansys/advmcode.c optional adv device-driver dev/advansys/adwcam.c optional adw device-driver dev/advansys/adwlib.c optional adw device-driver dev/advansys/adwmcode.c optional adw device-driver dev/aha/aha.c optional aha device-driver dev/aic7xxx/aic7xxx.c optional ahc device-driver \ dependency "aic7xxx_{reg,seq}.h" dev/aic7xxx/93cx6.c optional ahc device-driver dev/buslogic/bt.c optional bt device-driver dev/ccd/ccd.c optional ccd device-driver dev/isp/isp_freebsd.c optional isp device-driver dev/isp/isp.c optional isp device-driver #dev/dpt/dpt_control.c optional dpt device-driver dev/dpt/dpt_scsi.c optional dpt device-driver dev/en/midway.c optional en device-driver dev/hea/eni.c optional hea device-driver dev/hea/eni_buffer.c optional hea device-driver dev/hea/eni_globals.c optional hea device-driver dev/hea/eni_if.c optional hea device-driver dev/hea/eni_init.c optional hea device-driver dev/hea/eni_intr.c optional hea device-driver dev/hea/eni_receive.c optional hea device-driver dev/hea/eni_transmit.c optional hea device-driver dev/hea/eni_vcm.c optional hea device-driver dev/hfa/fore_buffer.c optional hfa device-driver dev/hfa/fore_command.c optional hfa device-driver dev/hfa/fore_globals.c optional hfa device-driver dev/hfa/fore_if.c optional hfa device-driver dev/hfa/fore_init.c optional hfa device-driver dev/hfa/fore_intr.c optional hfa device-driver dev/hfa/fore_load.c optional hfa device-driver dev/hfa/fore_output.c optional hfa device-driver dev/hfa/fore_receive.c optional hfa device-driver dev/hfa/fore_stats.c optional hfa device-driver dev/hfa/fore_timer.c optional hfa device-driver dev/hfa/fore_transmit.c optional hfa device-driver dev/hfa/fore_vcm.c optional hfa device-driver dev/pdq/pdq.c optional fea device-driver dev/pdq/pdq_ifsubr.c optional fea device-driver dev/pdq/pdq.c optional fpa device-driver dev/pdq/pdq_ifsubr.c optional fpa device-driver dev/ppbus/immio.c optional vpo dev/ppbus/if_plip.c optional plip dev/ppbus/lpbb.c optional lpbb dev/ppbus/lpt.c optional lpt dev/ppbus/ppb_base.c optional ppbus dev/ppbus/ppb_1284.c optional ppbus dev/ppbus/ppb_msq.c optional ppbus dev/ppbus/ppbconf.c optional ppbus dev/ppbus/ppi.c optional ppi dev/ppbus/pps.c optional pps dev/ppbus/vpo.c optional vpo dev/ppbus/vpoio.c optional vpo smbus_if.o optional smbus \ dependency "smbus_if.c smbus_if.h" \ compile-with "${NORMAL_C}" \ no-implicit-rule local smbus_if.c optional smbus \ dependency "$S/kern/makedevops.pl $S/dev/smbus/smbus_if.m" \ compile-with "perl5 $S/kern/makedevops.pl -c $S/dev/smbus/smbus_if.m" \ no-obj no-implicit-rule before-depend local \ clean "smbus_if.c" smbus_if.h optional smbus \ dependency "$S/kern/makedevops.pl $S/dev/smbus/smbus_if.m" \ compile-with "perl5 $S/kern/makedevops.pl -h $S/dev/smbus/smbus_if.m" \ no-obj no-implicit-rule before-depend \ clean "smbus_if.h" dev/smbus/smbconf.c optional smbus dev/smbus/smbus.c optional smbus dev/smbus/smb.c optional smb dev/iicbus/iicbb.c optional iicbb iicbb_if.o optional iicbb \ dependency "iicbb_if.c" \ compile-with "${NORMAL_C}" \ no-implicit-rule local iicbb_if.c optional iicbb \ dependency "$S/kern/makedevops.pl $S/dev/iicbus/iicbb_if.m" \ compile-with "perl5 $S/kern/makedevops.pl -c $S/dev/iicbus/iicbb_if.m" \ no-obj no-implicit-rule before-depend local \ clean "iicbb_if.c" iicbb_if.h optional iicbb \ dependency "$S/kern/makedevops.pl $S/dev/iicbus/iicbb_if.m" \ compile-with "perl5 $S/kern/makedevops.pl -h $S/dev/iicbus/iicbb_if.m" \ no-obj no-implicit-rule before-depend \ clean "iicbb_if.h" dev/iicbus/iicsmb.c optional iicsmb \ dependency "iicbus_if.h" iicbus_if.o optional iicbus \ dependency "iicbus_if.c iicbus_if.h" \ compile-with "${NORMAL_C}" \ no-implicit-rule local iicbus_if.c optional iicbus \ dependency "$S/kern/makedevops.pl $S/dev/iicbus/iicbus_if.m" \ compile-with "perl5 $S/kern/makedevops.pl -c $S/dev/iicbus/iicbus_if.m" \ no-obj no-implicit-rule before-depend local \ clean "iicbus_if.c" iicbus_if.h optional iicbus \ dependency "$S/kern/makedevops.pl $S/dev/iicbus/iicbus_if.m" \ compile-with "perl5 $S/kern/makedevops.pl -h $S/dev/iicbus/iicbus_if.m" \ no-obj no-implicit-rule before-depend \ clean "iicbus_if.h" dev/iicbus/iiconf.c optional iicbus dev/iicbus/iicbus.c optional iicbus dev/iicbus/if_ic.c optional ic dev/iicbus/iic.c optional iic dev/vinum/vinum.c optional vinum device-driver dev/vinum/vinumconfig.c optional vinum device-driver dev/vinum/vinumdaemon.c optional vinum device-driver dev/vinum/vinuminterrupt.c optional vinum device-driver dev/vinum/vinumio.c optional vinum device-driver dev/vinum/vinumioctl.c optional vinum device-driver dev/vinum/vinumlock.c optional vinum device-driver dev/vinum/vinummemory.c optional vinum device-driver dev/vinum/vinumparser.c optional vinum device-driver dev/vinum/vinumrequest.c optional vinum device-driver dev/vinum/vinumrevive.c optional vinum device-driver dev/vinum/vinumstate.c optional vinum device-driver dev/vinum/vinumutil.c optional vinum device-driver dev/vn/vn.c optional vn dev/vx/if_vx.c optional vx device-driver gnu/ext2fs/ext2_alloc.c optional ext2fs gnu/ext2fs/ext2_balloc.c optional ext2fs gnu/ext2fs/ext2_inode.c optional ext2fs gnu/ext2fs/ext2_inode_cnv.c optional ext2fs gnu/ext2fs/ext2_linux_balloc.c optional ext2fs gnu/ext2fs/ext2_linux_ialloc.c optional ext2fs gnu/ext2fs/ext2_lookup.c optional ext2fs gnu/ext2fs/ext2_subr.c optional ext2fs gnu/ext2fs/ext2_vfsops.c optional ext2fs gnu/ext2fs/ext2_vnops.c optional ext2fs # device drivers i4b/driver/i4b_trace.c optional i4btrc device-driver i4b/driver/i4b_rbch.c optional i4brbch device-driver i4b/driver/i4b_tel.c optional i4btel device-driver i4b/driver/i4b_ipr.c optional i4bipr i4b/driver/i4b_ctl.c optional i4bctl device-driver i4b/driver/i4b_isppp.c optional i4bisppp device-driver net/if_spppsubr.c optional sppp # needed by i4bipr net/slcompress.c optional i4bipr # tina-dd control driver i4b/tina-dd/i4b_tina_dd.c optional tina device-driver # support i4b/layer2/i4b_mbuf.c optional i4btrc device-driver # Q.921 handler i4b/layer2/i4b_l2.c optional i4bq921 i4b/layer2/i4b_l2fsm.c optional i4bq921 i4b/layer2/i4b_uframe.c optional i4bq921 i4b/layer2/i4b_tei.c optional i4bq921 i4b/layer2/i4b_sframe.c optional i4bq921 i4b/layer2/i4b_iframe.c optional i4bq921 i4b/layer2/i4b_l2timer.c optional i4bq921 i4b/layer2/i4b_util.c optional i4bq921 i4b/layer2/i4b_lme.c optional i4bq921 # Q.931 handler i4b/layer3/i4b_q931.c optional i4bq931 i4b/layer3/i4b_l3fsm.c optional i4bq931 i4b/layer3/i4b_l3timer.c optional i4bq931 i4b/layer3/i4b_l2if.c optional i4bq931 i4b/layer3/i4b_l4if.c optional i4bq931 i4b/layer3/i4b_q932fac.c optional i4bq931 # isdn device driver, interface to i4bd i4b/layer4/i4b_i4bdrv.c optional i4b device-driver i4b/layer4/i4b_l4.c optional i4b device-driver i4b/layer4/i4b_l4mgmt.c optional i4b device-driver i4b/layer4/i4b_l4timer.c optional i4b device-driver isofs/cd9660/cd9660_bmap.c optional cd9660 isofs/cd9660/cd9660_lookup.c optional cd9660 isofs/cd9660/cd9660_node.c optional cd9660 isofs/cd9660/cd9660_rrip.c optional cd9660 isofs/cd9660/cd9660_util.c optional cd9660 isofs/cd9660/cd9660_vfsops.c optional cd9660 isofs/cd9660/cd9660_vnops.c optional cd9660 kern/imgact_aout.c standard kern/imgact_elf.c standard kern/imgact_gzip.c optional gzip kern/imgact_shell.c standard kern/inflate.c optional gzip kern/init_main.c standard kern/init_sysent.c standard kern/kern_intr.c standard kern/kern_module.c standard kern/kern_linker.c standard kern/link_aout.c standard kern/link_elf.c standard kern/kern_acct.c standard kern/kern_clock.c standard kern/kern_conf.c standard kern/kern_descrip.c standard kern/kern_environment.c standard kern/kern_exec.c standard kern/kern_exit.c standard kern/kern_fork.c standard kern/kern_ktrace.c standard kern/kern_lkm.c optional lkm kern/kern_lock.c standard kern/kern_lockf.c standard kern/kern_malloc.c standard kern/kern_mib.c standard kern/kern_ntptime.c standard kern/kern_physio.c standard kern/kern_proc.c standard kern/kern_prot.c standard kern/kern_resource.c standard kern/kern_shutdown.c standard kern/kern_sig.c standard kern/kern_subr.c standard kern/kern_synch.c standard kern/kern_syscalls.c standard kern/kern_sysctl.c standard kern/kern_time.c standard kern/kern_timeout.c standard kern/kern_xxx.c standard kern/md5c.c standard kern/subr_autoconf.c standard kern/subr_bus.c standard kern/subr_devstat.c standard kern/subr_diskslice.c standard kern/subr_dkbad.c standard kern/subr_log.c standard kern/subr_module.c standard kern/subr_prf.c standard kern/subr_prof.c standard kern/subr_rlist.c standard kern/subr_blist.c standard kern/subr_scanf.c standard kern/subr_xxx.c standard kern/sys_generic.c standard kern/sys_pipe.c standard kern/sys_process.c standard kern/subr_rman.c standard kern/sys_socket.c standard kern/sysv_ipc.c standard kern/sysv_msg.c optional sysvmsg kern/sysv_sem.c optional sysvsem kern/sysv_shm.c optional sysvshm kern/tty.c standard kern/tty_compat.c standard kern/tty_conf.c standard kern/tty_pty.c optional pty kern/tty_snoop.c optional snp kern/tty_subr.c standard kern/tty_tb.c optional tb kern/tty_tty.c standard kern/uipc_domain.c standard kern/uipc_mbuf.c standard kern/uipc_proto.c standard kern/uipc_socket.c standard kern/uipc_socket2.c standard kern/uipc_syscalls.c standard kern/uipc_usrreq.c standard kern/vfs_bio.c standard kern/vfs_cache.c standard kern/vfs_cluster.c standard kern/vfs_conf.c standard kern/vfs_default.c standard kern/vfs_init.c standard kern/vfs_lookup.c standard kern/vfs_subr.c standard kern/vfs_syscalls.c standard kern/vfs_vnops.c standard kern/kern_threads.c standard kern/vfs_aio.c standard miscfs/deadfs/dead_vnops.c standard miscfs/devfs/devfs_tree.c optional devfs miscfs/devfs/devfs_vfsops.c optional devfs miscfs/devfs/devfs_vnops.c optional devfs miscfs/fdesc/fdesc_vfsops.c optional fdesc miscfs/fdesc/fdesc_vnops.c optional fdesc miscfs/fifofs/fifo_vnops.c standard miscfs/kernfs/kernfs_vfsops.c optional kernfs miscfs/kernfs/kernfs_vnops.c optional kernfs miscfs/nullfs/null_subr.c optional nullfs miscfs/nullfs/null_vfsops.c optional nullfs miscfs/nullfs/null_vnops.c optional nullfs miscfs/portal/portal_vfsops.c optional portal miscfs/portal/portal_vnops.c optional portal miscfs/procfs/procfs_ctl.c optional procfs miscfs/procfs/procfs_fpregs.c standard miscfs/procfs/procfs_map.c optional procfs miscfs/procfs/procfs_mem.c standard miscfs/procfs/procfs_note.c optional procfs miscfs/procfs/procfs_regs.c standard miscfs/procfs/procfs_status.c optional procfs miscfs/procfs/procfs_subr.c optional procfs miscfs/procfs/procfs_type.c optional procfs miscfs/procfs/procfs_vfsops.c optional procfs miscfs/procfs/procfs_vnops.c optional procfs miscfs/specfs/spec_vnops.c standard miscfs/umapfs/umap_subr.c optional umapfs miscfs/umapfs/umap_vfsops.c optional umapfs miscfs/umapfs/umap_vnops.c optional umapfs miscfs/union/union_subr.c optional union miscfs/union/union_vfsops.c optional union miscfs/union/union_vnops.c optional union msdosfs/msdosfs_conv.c optional msdosfs msdosfs/msdosfs_denode.c optional msdosfs msdosfs/msdosfs_fat.c optional msdosfs msdosfs/msdosfs_lookup.c optional msdosfs msdosfs/msdosfs_vfsops.c optional msdosfs msdosfs/msdosfs_vnops.c optional msdosfs ntfs/ntfs_vfsops.c optional ntfs ntfs/ntfs_vnops.c optional ntfs ntfs/ntfs_subr.c optional ntfs ntfs/ntfs_compr.c optional ntfs ntfs/ntfs_ihash.c optional ntfs net/bpf.c optional bpfilter net/bpf_filter.c optional bpfilter net/bridge.c optional bridge net/bsd_comp.c optional ppp_bsdcomp #net/hostcache.c standard net/if.c standard net/if_atmsubr.c optional atm net/if_disc.c optional disc net/if_ethersubr.c optional ether net/if_iso88025subr.c optional token net/if_fddisubr.c optional fddi net/if_loop.c optional loop net/if_media.c standard net/if_mib.c standard net/if_ppp.c optional ppp net/if_sl.c optional sl net/if_spppsubr.c optional sppp net/if_tun.c optional tun net/if_vlan.c optional vlan net/ppp_deflate.c optional ppp_deflate net/ppp_tty.c optional ppp net/radix.c standard net/raw_cb.c standard net/raw_usrreq.c standard net/route.c standard net/rtsock.c standard net/slcompress.c optional ppp net/slcompress.c optional sl net/zlib.c optional ppp_deflate netatalk/aarp.c optional netatalk netatalk/at_control.c optional netatalk netatalk/at_proto.c optional netatalk netatalk/at_rmx.c optional netatalkdebug netatalk/ddp_input.c optional netatalk netatalk/ddp_output.c optional netatalk netatalk/ddp_usrreq.c optional netatalk netatm/atm_aal5.c optional atm_core netatm/atm_cm.c optional atm_core netatm/atm_device.c optional atm_core netatm/atm_if.c optional atm_core netatm/atm_proto.c optional atm_core netatm/atm_signal.c optional atm_core netatm/atm_socket.c optional atm_core netatm/atm_subr.c optional atm_core netatm/atm_usrreq.c optional atm_core netatm/ipatm/ipatm_event.c optional atm_ip atm_core netatm/ipatm/ipatm_if.c optional atm_ip atm_core netatm/ipatm/ipatm_input.c optional atm_ip atm_core netatm/ipatm/ipatm_load.c optional atm_ip atm_core netatm/ipatm/ipatm_output.c optional atm_ip atm_core netatm/ipatm/ipatm_usrreq.c optional atm_ip atm_core netatm/ipatm/ipatm_vcm.c optional atm_ip atm_core netatm/sigpvc/sigpvc_if.c optional atm_sigpvc atm_core netatm/sigpvc/sigpvc_subr.c optional atm_sigpvc atm_core netatm/spans/spans_arp.c optional atm_spans atm_core \ dependency "spans_xdr.h" netatm/spans/spans_cls.c optional atm_spans atm_core netatm/spans/spans_if.c optional atm_spans atm_core netatm/spans/spans_kxdr.c optional atm_spans atm_core netatm/spans/spans_msg.c optional atm_spans atm_core netatm/spans/spans_print.c optional atm_spans atm_core netatm/spans/spans_proto.c optional atm_spans atm_core netatm/spans/spans_subr.c optional atm_spans atm_core netatm/spans/spans_util.c optional atm_spans atm_core spans_xdr.h optional atm_spans atm_core \ before-depend \ dependency "$S/netatm/spans/spans_xdr.x" \ compile-with "rpcgen -h -C $S/netatm/spans/spans_xdr.x > spans_xdr.h" \ clean "spans_xdr.h" \ no-obj no-implicit-rule spans_xdr.c optional atm_spans atm_core \ before-depend \ dependency "$S/netatm/spans/spans_xdr.x" \ compile-with "rpcgen -c -C $S/netatm/spans/spans_xdr.x > spans_xdr.c" \ clean "spans_xdr.c" \ no-obj no-implicit-rule local spans_xdr.o optional atm_spans atm_core \ dependency "$S/netatm/spans/spans_xdr.x" \ compile-with "${NORMAL_C}" \ no-implicit-rule local netatm/uni/q2110_sigaa.c optional atm_uni atm_core netatm/uni/q2110_sigcpcs.c optional atm_uni atm_core netatm/uni/q2110_subr.c optional atm_uni atm_core netatm/uni/qsaal1_sigaa.c optional atm_uni atm_core netatm/uni/qsaal1_sigcpcs.c optional atm_uni atm_core netatm/uni/qsaal1_subr.c optional atm_uni atm_core netatm/uni/sscf_uni.c optional atm_uni atm_core netatm/uni/sscf_uni_lower.c optional atm_uni atm_core netatm/uni/sscf_uni_upper.c optional atm_uni atm_core netatm/uni/sscop.c optional atm_uni atm_core netatm/uni/sscop_lower.c optional atm_uni atm_core netatm/uni/sscop_pdu.c optional atm_uni atm_core netatm/uni/sscop_sigaa.c optional atm_uni atm_core netatm/uni/sscop_sigcpcs.c optional atm_uni atm_core netatm/uni/sscop_subr.c optional atm_uni atm_core netatm/uni/sscop_timer.c optional atm_uni atm_core netatm/uni/sscop_upper.c optional atm_uni atm_core netatm/uni/uni_load.c optional atm_uni atm_core netatm/uni/uniarp.c optional atm_uni atm_core netatm/uni/uniarp_cache.c optional atm_uni atm_core netatm/uni/uniarp_input.c optional atm_uni atm_core netatm/uni/uniarp_output.c optional atm_uni atm_core netatm/uni/uniarp_timer.c optional atm_uni atm_core netatm/uni/uniarp_vcm.c optional atm_uni atm_core netatm/uni/uniip.c optional atm_uni atm_core netatm/uni/unisig_decode.c optional atm_uni atm_core netatm/uni/unisig_encode.c optional atm_uni atm_core netatm/uni/unisig_if.c optional atm_uni atm_core netatm/uni/unisig_mbuf.c optional atm_uni atm_core netatm/uni/unisig_msg.c optional atm_uni atm_core netatm/uni/unisig_print.c optional atm_uni atm_core netatm/uni/unisig_proto.c optional atm_uni atm_core netatm/uni/unisig_sigmgr_state.c optional atm_uni atm_core netatm/uni/unisig_subr.c optional atm_uni atm_core netatm/uni/unisig_util.c optional atm_uni atm_core netatm/uni/unisig_vc_state.c optional atm_uni atm_core netinet/fil.c optional ipfilter inet netinet/if_atm.c optional atm netinet/if_ether.c optional ether netinet/igmp.c optional inet netinet/in.c optional inet #netinet/in_hostcache.c optional inet netinet/in_pcb.c optional inet netinet/in_proto.c optional inet netinet/in_rmx.c optional inet netinet/ip_auth.c optional ipfilter inet netinet/ip_divert.c optional ipdivert netinet/ip_dummynet.c optional dummynet netinet/ip_fil.c optional ipfilter inet netinet/ip_flow.c optional inet netinet/ip_frag.c optional ipfilter inet netinet/ip_fw.c optional ipfirewall netinet/ip_icmp.c optional inet netinet/ip_input.c optional inet netinet/ip_log.c optional ipfilter inet netinet/ip_mroute.c optional inet netinet/ip_nat.c optional ipfilter inet netinet/ip_output.c optional inet netinet/ip_proxy.c optional ipfilter inet netinet/ip_state.c optional ipfilter inet netinet/mlf_ipl.c optional ipfilter inet netinet/raw_ip.c optional inet netinet/tcp_debug.c optional tcpdebug netinet/tcp_input.c optional inet netinet/tcp_output.c optional inet netinet/tcp_subr.c optional inet netinet/tcp_timer.c optional inet netinet/tcp_usrreq.c optional inet netinet/udp_usrreq.c optional inet netipx/ipx.c optional ipx netipx/ipx_cksum.c optional ipx netipx/ipx_input.c optional ipx netipx/ipx_ip.c optional ipx netipx/ipx_outputfl.c optional ipx netipx/ipx_pcb.c optional ipx netipx/ipx_proto.c optional ipx netipx/ipx_tun.c optional ipx netipx/ipx_usrreq.c optional ipx netipx/spx_debug.c optional ipx netipx/spx_usrreq.c optional ipx netkey/key.c optional key netkey/key_debug.c optional key_debug netnatm/natm.c optional natm netnatm/natm_pcb.c optional natm netnatm/natm_proto.c optional natm netns/idp_usrreq.c optional ns netns/ns.c optional ns netns/ns_error.c optional ns netns/ns_input.c optional ns netns/ns_ip.c optional ns netns/ns_output.c optional ns netns/ns_pcb.c optional ns netns/ns_proto.c optional ns netns/spp_debug.c optional ns netns/spp_usrreq.c optional ns nfs/nfs_bio.c optional nfs nfs/nfs_node.c optional nfs nfs/nfs_nqlease.c optional nfs nfs/nfs_serv.c optional nfs nfs/nfs_socket.c optional nfs nfs/nfs_srvcache.c optional nfs nfs/nfs_subs.c optional nfs nfs/nfs_syscalls.c optional nfs nfs/nfs_vfsops.c optional nfs nfs/nfs_vnops.c optional nfs nfs/bootp_subr.c optional bootp nfs/krpc_subr.c optional bootp pccard/pccard.c optional card pccard/pccard_beep.c optional card pccard/pcic.c optional pcic device-driver pci/pcic_p.c optional pcic device-driver pci/adv_pci.c optional adv device-driver pci/adw_pci.c optional adw device-driver pci/ahc_pci.c optional ahc device-driver \ dependency "aic7xxx_reg.h $S/pci/ahc_pci.c" pci/brooktree848.c optional bktr device-driver pci/bt848_i2c.c optional bktr device-driver pci/bt_pci.c optional bt device-driver pci/dpt_pci.c optional pci dpt device-driver pci/cy_pci.c optional cy device-driver pci/if_ax.c optional ax device-driver pci/if_de.c optional de device-driver pci/if_ed_p.c optional ed device-driver pci/if_en_pci.c optional en device-driver pci/if_fxp.c optional fxp device-driver pci/if_lnc_p.c optional lnc device-driver pci/if_mx.c optional mx device-driver pci/if_pn.c optional pn device-driver pci/if_fpa.c optional fpa device-driver pci/if_rl.c optional rl device-driver pci/if_sr_p.c optional sr device-driver pci/if_ti.c optional ti device-driver pci/if_tl.c optional tl device-driver pci/if_tx.c optional tx device-driver pci/if_vr.c optional vr device-driver pci/if_vx_pci.c optional vx device-driver pci/if_wb.c optional wb device-driver pci/if_xl.c optional xl device-driver pci/isp_pci.c optional isp device-driver pci/intpm.c optional intpm device-driver pci/meteor.c optional meteor device-driver pci/ncr.c optional ncr device-driver pci/pci.c optional pci device-driver pci/pci_compat.c optional pci pci/pcisupport.c optional pci +pci_if.o optional pci \ + dependency "pci_if.c pci_if.h" \ + compile-with "${NORMAL_C}" \ + no-implicit-rule local +pci_if.c optional pci \ + dependency "$S/kern/makedevops.pl $S/pci/pci_if.m" \ + compile-with "perl5 $S/kern/makedevops.pl -c $S/pci/pci_if.m" \ + no-obj no-implicit-rule before-depend local \ + clean "pci_if.c" +pci_if.h optional pci \ + dependency "$S/kern/makedevops.pl $S/pci/pci_if.m" \ + compile-with "perl5 $S/kern/makedevops.pl -h $S/pci/pci_if.m" \ + no-obj no-implicit-rule before-depend \ + clean "pci_if.h" +pci/tek390.c optional amd device-driver pci/simos.c optional simos device-driver pci/alpm.c optional alpm device-driver pci/xrpu.c optional xrpu device-driver posix4/posix4_mib.c standard posix4/p1003_1b.c standard posix4/ksched.c optional _kposix_priority_scheduling ufs/ffs/ffs_alloc.c optional ffs ufs/ffs/ffs_alloc.c optional mfs ufs/ffs/ffs_balloc.c optional ffs ufs/ffs/ffs_balloc.c optional mfs ufs/ffs/ffs_inode.c optional ffs ufs/ffs/ffs_inode.c optional mfs ufs/ffs/ffs_softdep_stub.c standard ufs/ffs/ffs_softdep.c optional softupdates ufs/ffs/ffs_subr.c optional ffs ufs/ffs/ffs_subr.c optional mfs ufs/ffs/ffs_tables.c optional ffs ufs/ffs/ffs_tables.c optional mfs ufs/ffs/ffs_vfsops.c optional ffs ufs/ffs/ffs_vfsops.c optional mfs ufs/ffs/ffs_vnops.c optional ffs ufs/ffs/ffs_vnops.c optional mfs ufs/mfs/mfs_vfsops.c optional mfs ufs/mfs/mfs_vnops.c optional mfs ufs/ufs/ufs_bmap.c standard ufs/ufs/ufs_disksubr.c standard ufs/ufs/ufs_ihash.c standard ufs/ufs/ufs_inode.c standard ufs/ufs/ufs_lookup.c standard ufs/ufs/ufs_quota.c standard ufs/ufs/ufs_vfsops.c standard ufs/ufs/ufs_vnops.c standard vm/default_pager.c standard vm/device_pager.c standard vm/swap_pager.c standard vm/vm_fault.c standard vm/vm_glue.c standard vm/vm_init.c standard vm/vm_kern.c standard vm/vm_map.c standard vm/vm_meter.c standard vm/vm_mmap.c standard vm/vm_object.c standard vm/vm_page.c standard vm/vm_pageout.c standard vm/vm_pager.c standard vm/vm_swap.c standard vm/vm_unix.c standard vm/vnode_pager.c standard vm/vm_zone.c standard dev/streams/streams.c optional streams device-driver # # USB support pci/uhci_pci.c optional uhci device-driver pci/ohci_pci.c optional ohci device-driver usb_if.o optional usb device-driver \ dependency "usb_if.c" \ compile-with "${NORMAL_C}" \ no-implicit-rule local usb_if.c optional usb device-driver \ dependency "$S/kern/makedevops.pl $S/dev/usb/usb_if.m" \ compile-with "perl5 $S/kern/makedevops.pl -c $S/dev/usb/usb_if.m" \ no-obj no-implicit-rule before-depend local \ clean "usb_if.c" usb_if.h optional usb device-driver \ dependency "$S/kern/makedevops.pl $S/dev/usb/usb_if.m" \ compile-with "perl5 $S/kern/makedevops.pl -h $S/dev/usb/usb_if.m" \ no-obj no-implicit-rule before-depend \ clean "usb_if.h" dev/usb/uhci.c optional uhci device-driver dev/usb/ohci.c optional ohci device-driver dev/usb/usb.c optional usb device-driver dev/usb/usbdi.c optional usb device-driver dev/usb/usbdi_util.c optional usb device-driver #dev/usb/usb_mem.c optional usb device-driver dev/usb/usb_subr.c optional usb device-driver dev/usb/usb_quirks.c optional usb device-driver dev/usb/hid.c optional usb device-driver # ordering in the list of drivers below is important and should # be the inverse of the wanted one (MAKE_SET) dev/usb/ugen.c optional ugen device-driver dev/usb/uhid.c optional uhid device-driver dev/usb/ums.c optional ums device-driver dev/usb/ulpt.c optional ulpt device-driver dev/usb/ukbd.c optional ukbd device-driver dev/usb/umass.c optional umass device-driver dev/usb/uhub.c optional usb device-driver Index: head/sys/conf/files.alpha =================================================================== --- head/sys/conf/files.alpha (revision 45719) +++ head/sys/conf/files.alpha (revision 45720) @@ -1,159 +1,169 @@ # This file tells config what files go into building a kernel, # files marked standard are always included. # -# $Id: files.alpha,v 1.16 1999/01/23 16:53:26 dfr Exp $ +# $Id: files.alpha,v 1.17 1999/03/10 10:36:50 yokota Exp $ # # The long compile-with and dependency lines are required because of # limitations in config: backslash-newline doesn't work in strings, and # dependency lines other than the first are silently ignored. # # font8x16.o optional std8x16font \ compile-with "uudecode < /usr/share/syscons/fonts/${STD8X16FONT}-8x16.fnt && file2c 'unsigned char font_16[16*256] = {' '};' < ${STD8X16FONT}-8x16 > font8x16.c && ${CC} -c ${CFLAGS} font8x16.c" \ no-implicit-rule before-depend \ clean "${STD8X16FONT}-8x16 font8x16.c" # atkbdmap.h optional atkbd_dflt_keymap \ compile-with "kbdcontrol -L ${ATKBD_DFLT_KEYMAP} | sed -e 's/^static keymap_t.* = /static keymap_t key_map = /' -e 's/^static accentmap_t.* = /static accentmap_t accent_map = /' > atkbdmap.h" \ no-obj no-implicit-rule before-depend \ clean "atkbdmap.h" # alpha/alpha/autoconf.c standard device-driver alpha/alpha/cpuconf.c standard alpha/alpha/atomic.s standard alpha/alpha/dec_kn8ae.c optional dec_kn8ae alpha/alpha/dec_eb164.c optional dec_eb164 alpha/alpha/dec_eb64plus.c optional dec_eb64plus alpha/alpha/dec_kn20aa.c optional dec_kn20aa alpha/alpha/dec_2100_a50.c optional dec_2100_a50 alpha/alpha/dec_st550.c optional dec_st550 alpha/alpha/dec_axppci_33.c optional dec_axppci_33 alpha/alpha/dec_3000_300.c optional dec_3000_300 alpha/alpha/dec_3000_500.c optional dec_3000_500 alpha/alpha/mountroot.c optional slice alpha/alpha/ipl_funcs.c standard alpha/alpha/pal.s standard alpha/alpha/busdma_machdep.c standard alpha/alpha/cons.c standard alpha/alpha/prom.c standard alpha/alpha/promcons.c standard alpha/alpha/prom_disp.s standard alpha/alpha/alpha-gdbstub.c optional ddb alpha/alpha/db_disasm.c optional ddb alpha/alpha/db_interface.c optional ddb alpha/alpha/db_trace.c optional ddb alpha/alpha/exception.s standard alpha/alpha/in_cksum.c optional inet # locore.s needs to be handled in Makefile to put it first. Otherwise it's # now normal. # alpha/alpha/locore.s standard alpha/alpha/machdep.c standard alpha/alpha/fp_emulate.c standard alpha/alpha/ieee_float.c standard alpha/alpha/mem.c standard alpha/alpha/mp_machdep.c optional smp alpha/alpha/perfmon.c optional perfmon profiling-routine alpha/alpha/perfmon.c optional perfmon alpha/alpha/pmap.c standard alpha/alpha/procfs_machdep.c standard alpha/alpha/simplelock.s optional smp alpha/alpha/support.s standard alpha/alpha/swtch.s standard alpha/alpha/sys_machdep.c standard alpha/alpha/trap.c standard alpha/alpha/interrupt.c standard alpha/alpha/userconfig.c optional userconfig alpha/alpha/vm_machdep.c standard alpha/alpha/clock.c standard clock_if.o standard \ dependency "clock_if.c" \ compile-with "${NORMAL_C}" \ no-implicit-rule local clock_if.c standard \ dependency "$S/kern/makedevops.pl $S/alpha/alpha/clock_if.m" \ compile-with "perl $S/kern/makedevops.pl -c $S/alpha/alpha/clock_if.m" \ no-obj no-implicit-rule before-depend local \ clean "clock_if.c" clock_if.h standard \ dependency "$S/kern/makedevops.pl $S/alpha/alpha/clock_if.m" \ compile-with "perl $S/kern/makedevops.pl -h $S/alpha/alpha/clock_if.m" \ no-obj no-implicit-rule before-depend \ clean "clock_if.h" alpha/alpha/diskslice_machdep.c standard alpha/tlsb/tlsb.c optional tlsb alpha/tlsb/gbus.c optional gbus alpha/tlsb/kftxx.c optional kft alpha/tlsb/mcclock_tlsb.c optional gbus alpha/tlsb/zs_tlsb.c optional gbus alpha/tlsb/dwlpx.c optional dwlpx alpha/tc/tcasic.c optional tcasic alpha/tc/tc.c optional tc alpha/tc/ioasic.c optional tc alpha/tc/mcclock_ioasic.c optional tc alpha/tc/if_le_ioasic.c optional le device-driver alpha/tc/if_le_dec.c optional le device-driver alpha/tc/am7990.c optional le device-driver alpha/tc/tcds.c optional tcds device-driver alpha/tc/tcds_dma.c optional tcds device-driver alpha/tc/esp.c optional esp device-driver dev/dec/mcclock.c standard device-driver mcclock_if.o standard \ dependency "mcclock_if.c" \ compile-with "${NORMAL_C}" \ no-implicit-rule local mcclock_if.c standard \ dependency "$S/kern/makedevops.pl $S/dev/dec/mcclock_if.m" \ compile-with "perl $S/kern/makedevops.pl -c $S/dev/dec/mcclock_if.m" \ no-obj no-implicit-rule before-depend local \ clean "mcclock_if.c" mcclock_if.h standard \ dependency "$S/kern/makedevops.pl $S/dev/dec/mcclock_if.m" \ compile-with "perl $S/kern/makedevops.pl -h $S/dev/dec/mcclock_if.m" \ no-obj no-implicit-rule before-depend \ clean "mcclock_if.h" alpha/pci/cia.c optional cia +alpha/pci/cia_pci.c optional cia alpha/pci/pci_eb164_intr.s optional cia alpha/pci/apecs.c optional apecs +alpha/pci/apecs_pci.c optional apecs alpha/pci/pci_eb64plus_intr.s optional apecs alpha/pci/lca.c optional lca +alpha/pci/lca_pci.c optional lca alpha/pci/pcibus.c optional pci alpha/isa/isa.c optional isa alpha/isa/mcclock_isa.c optional isa alpha/alpha/elf_machdep.c standard libkern/bcd.c standard libkern/bcmp.c standard libkern/ffs.c standard libkern/inet_ntoa.c standard libkern/index.c standard libkern/mcount.c optional profiling-routine libkern/qsort.c standard libkern/random.c standard libkern/rindex.c standard libkern/scanc.c standard libkern/skpc.c standard libkern/strcat.c standard libkern/strcmp.c standard libkern/strcpy.c standard libkern/strlen.c standard libkern/strncmp.c standard libkern/strncpy.c standard libkern/alpha/htonl.S standard libkern/alpha/htons.S standard libkern/alpha/ntohl.S standard libkern/alpha/ntohs.S standard isa/sio.c optional sio device-driver dev/fb/fb.c optional fb device-driver dev/fb/fb.c optional vga device-driver isa/vga_isa.c optional vga device-driver dev/fb/splash.c optional splash dev/kbd/atkbd.c optional atkbd device-driver isa/atkbd_isa.c optional atkbd device-driver dev/kbd/atkbdc.c optional atkbdc device-driver isa/atkbdc_isa.c optional atkbdc device-driver dev/kbd/kbd.c optional atkbd device-driver dev/kbd/kbd.c optional kbd device-driver dev/kbd/kbd.c optional ukbd device-driver dev/syscons/syscons.c optional sc device-driver dev/syscons/scvidctl.c optional sc device-driver isa/syscons_isa.c optional sc device-driver isa/psm.c optional psm device-driver +dev/ata/ata-all.c optional ata device-driver +dev/ata/ata-dma.c optional ata device-driver +dev/ata/atapi-all.c optional ata device-driver +dev/ata/ata-disk.c optional atadisk device-driver +dev/ata/atapi-cd.c optional atapicd device-driver +dev/ata/atapi-fd.c optional atapifd device-driver +dev/ata/atapi-tape.c optional atapist device-driver Index: head/sys/conf/files.i386 =================================================================== --- head/sys/conf/files.i386 (revision 45719) +++ head/sys/conf/files.i386 (revision 45720) @@ -1,376 +1,379 @@ # This file tells config what files go into building a kernel, # files marked standard are always included. # -# $Id: files.i386,v 1.234 1999/04/13 19:38:10 peter Exp $ +# $Id: files.i386,v 1.235 1999/04/15 14:52:23 bde Exp $ # # The long compile-with and dependency lines are required because of # limitations in config: backslash-newline doesn't work in strings, and # dependency lines other than the first are silently ignored. # linux_genassym optional compat_linux \ dependency "$S/i386/linux/linux_genassym.c $S/i386/linux/linux.h" \ compile-with "${CC} ${CFLAGS} ${PARAM} -UKERNEL -o $@ $<" \ no-obj no-implicit-rule \ clean "linux_genassym" # linux_assym.h optional compat_linux \ dependency "linux_genassym" \ compile-with "./linux_genassym > $@" \ no-obj no-implicit-rule before-depend \ clean "linux_assym.h" # font8x16.o optional std8x16font \ compile-with "uudecode < /usr/share/syscons/fonts/${STD8X16FONT}-8x16.fnt && file2c 'unsigned char font_16[16*256] = {' '};' < ${STD8X16FONT}-8x16 > font8x16.c && ${CC} -c ${CFLAGS} font8x16.c" \ no-implicit-rule before-depend \ clean "${STD8X16FONT}-8x16 font8x16.c" # atkbdmap.h optional atkbd_dflt_keymap \ compile-with "kbdcontrol -L ${ATKBD_DFLT_KEYMAP} | sed -e 's/^static keymap_t.* = /static keymap_t key_map = /' -e 's/^static accentmap_t.* = /static accentmap_t accent_map = /' > atkbdmap.h" \ no-obj no-implicit-rule before-depend \ clean "atkbdmap.h" # ukbdmap.h optional ukbd_dflt_keymap \ compile-with "kbdcontrol -L ${UKBD_DFLT_KEYMAP} | sed -e 's/^static keymap_t.* = /static keymap_t key_map = /' -e 's/^static accentmap_t.* = /static accentmap_t accent_map = /' > ukbdmap.h" \ no-obj no-implicit-rule before-depend \ clean "ukbdmap.h" # dev/ata/ata-all.c optional ata device-driver dev/ata/ata-dma.c optional ata device-driver dev/ata/atapi-all.c optional ata device-driver dev/ata/ata-disk.c optional atadisk device-driver dev/ata/atapi-cd.c optional atapicd device-driver dev/ata/atapi-fd.c optional atapifd device-driver dev/ata/atapi-tape.c optional atapist device-driver dev/fb/fb.c optional fb device-driver dev/fb/fb.c optional vga device-driver dev/fb/splash.c optional splash dev/kbd/atkbd.c optional atkbd device-driver dev/kbd/atkbdc.c optional atkbdc device-driver dev/kbd/kbd.c optional atkbd device-driver dev/kbd/kbd.c optional kbd device-driver dev/kbd/kbd.c optional ukbd device-driver dev/syscons/syscons.c optional sc device-driver dev/syscons/scvidctl.c optional sc device-driver dev/syscons/scvesactl.c optional sc device-driver i386/apm/apm.c optional apm device-driver i386/apm/apm_setup.s optional apm i386/eisa/dpt_eisa.c optional eisa dpt device-driver i386/eisa/3c5x9.c optional ep device-driver i386/eisa/adv_eisa.c optional adv device-driver i386/eisa/ahc_eisa.c optional eisa ahc device-driver \ dependency "aic7xxx_reg.h $S/i386/eisa/ahc_eisa.c" i386/eisa/ahb.c optional ahb device-driver i386/eisa/bt_eisa.c optional bt device-driver i386/eisa/eisaconf.c optional eisa i386/eisa/if_vx_eisa.c optional vx device-driver i386/eisa/if_fea.c optional fea device-driver i386/i386/autoconf.c standard device-driver i386/i386/bios.c standard i386/i386/bioscall.s standard i386/i386/busdma_machdep.c standard i386/i386/cons.c standard i386/i386/db_disasm.c optional ddb i386/i386/db_interface.c optional ddb i386/i386/db_trace.c optional ddb i386/i386/elf_machdep.c standard i386/i386/exception.s standard i386/i386/globals.s standard i386/i386/i386-gdbstub.c optional ddb i386/i386/identcpu.c standard i386/i386/in_cksum.c optional inet i386/i386/initcpu.c standard # locore.s needs to be handled in Makefile to put it first. Otherwise it's # now normal. # i386/i386/locore.s standard i386/i386/machdep.c standard i386/i386/math_emulate.c optional math_emulate i386/i386/mem.c standard i386/i386/i686_mem.c standard i386/i386/mp_machdep.c optional smp i386/i386/mpapic.c optional smp i386/i386/mpboot.s optional smp i386/i386/mplock.s optional smp +i386/i386/nexus.c standard i386/i386/perfmon.c optional perfmon profiling-routine i386/i386/perfmon.c optional perfmon i386/i386/pmap.c standard i386/i386/procfs_machdep.c standard i386/i386/simplelock.s optional smp i386/i386/support.s standard i386/i386/swapgeneric.c standard i386/i386/swtch.s standard i386/i386/sys_machdep.c standard i386/i386/trap.c standard i386/i386/userconfig.c optional userconfig i386/i386/vm_machdep.c standard i386/i386/vm86.c optional vm86 i386/ibcs2/ibcs2_fcntl.c optional ibcs2 i386/ibcs2/ibcs2_stat.c optional ibcs2 i386/ibcs2/ibcs2_ipc.c optional ibcs2 i386/ibcs2/ibcs2_msg.c optional ibcs2 i386/ibcs2/ibcs2_misc.c optional ibcs2 i386/ibcs2/ibcs2_other.c optional ibcs2 i386/ibcs2/ibcs2_signal.c optional ibcs2 i386/ibcs2/ibcs2_ioctl.c optional ibcs2 i386/ibcs2/ibcs2_socksys.c optional ibcs2 i386/ibcs2/ibcs2_sysi86.c optional ibcs2 i386/ibcs2/ibcs2_util.c optional ibcs2 i386/ibcs2/ibcs2_isc.c optional ibcs2 i386/ibcs2/ibcs2_isc_sysent.c optional ibcs2 i386/ibcs2/ibcs2_xenix.c optional ibcs2 i386/ibcs2/ibcs2_xenix_sysent.c optional ibcs2 i386/ibcs2/ibcs2_errno.c optional ibcs2 i386/ibcs2/ibcs2_sysent.c optional ibcs2 i386/ibcs2/ibcs2_sysvec.c optional ibcs2 i386/ibcs2/imgact_coff.c optional ibcs2 i386/isa/adv_isa.c optional adv device-driver #i386/isa/aha1542.c optional aha device-driver i386/isa/aha_isa.c optional aha device-driver -i386/isa/atkbd_isa.c optional atkbd device-driver -i386/isa/atkbdc_isa.c optional atkbdc device-driver +isa/atkbd_isa.c optional atkbd device-driver +isa/atkbdc_isa.c optional atkbdc device-driver i386/isa/bt_isa.c optional bt device-driver i386/isa/clock.c standard i386/isa/cronyx.c optional cx device-driver i386/isa/ctx.c optional ctx device-driver i386/isa/cx.c optional cx device-driver i386/isa/cy.c optional cy device-driver i386/isa/diskslice_machdep.c standard i386/isa/elink.c optional ep device-driver i386/isa/elink.c optional ie device-driver i386/isa/fd.c optional fd device-driver i386/isa/gpib.c optional gp device-driver i386/isa/asc.c optional asc device-driver i386/isa/gsc.c optional gsc device-driver i386/isa/if_ar.c optional ar device-driver i386/isa/if_cs.c optional cs device-driver i386/isa/if_cx.c optional cx device-driver i386/isa/if_ed.c optional ed device-driver i386/isa/if_el.c optional el device-driver i386/isa/if_ep.c optional ep device-driver i386/isa/if_ex.c optional ex device-driver i386/isa/if_fe.c optional fe device-driver i386/isa/if_ie.c optional ie device-driver i386/isa/if_le.c optional le device-driver i386/isa/if_lnc.c optional lnc device-driver i386/isa/if_rdp.c optional rdp device-driver i386/isa/if_sr.c optional sr device-driver i386/isa/if_wl.c optional wl device-driver i386/isa/if_ze.c optional ze device-driver i386/isa/if_zp.c optional zp device-driver contrib/dev/oltr/if_oltr.c optional oltr device-driver trlld.o optional oltr device-driver \ dependency "$S/contrib/dev/oltr/i386-${KERNFORMAT}.trlld.o.uu" \ compile-with "uudecode < $S/contrib/dev/oltr/i386-${KERNFORMAT}.trlld.o.uu" \ no-implicit-rule contrib/dev/oltr/trlldmac.c optional oltr device-driver contrib/dev/oltr/trlldhm.c optional oltr device-driver contrib/dev/oltr/trlldbm.c optional oltr device-driver i386/isa/ipl_funcs.c standard \ compile-with "${CC} -c ${CFLAGS} ${DEFINED_PROF:S/^$/-fomit-frame-pointer/} $<" i386/isa/intr_machdep.c standard i386/isa/isa.c optional isa device-driver i386/isa/istallion.c optional stli device-driver i386/isa/joy.c optional joy device-driver i386/isa/loran.c optional loran device-driver i386/isa/labpc.c optional labpc device-driver i386/isa/mcd.c optional mcd device-driver i386/isa/mse.c optional mse device-driver i386/isa/npx.c mandatory npx device-driver i386/isa/pcaudio.c optional pca device-driver i386/isa/matcd/matcd.c optional matcd device-driver +i386/isa/isa_compat.c optional isa device-driver +i386/isa/isa_dma.c optional isa device-driver i386/isa/pcibus.c optional pci device-driver i386/isa/pcicx.c optional ze device-driver i386/isa/pcicx.c optional zp device-driver i386/isa/pcvt/pcvt_drv.c optional vt device-driver i386/isa/pcvt/pcvt_ext.c optional vt device-driver i386/isa/pcvt/pcvt_kbd.c optional vt device-driver i386/isa/pcvt/pcvt_out.c optional vt device-driver i386/isa/pcvt/pcvt_sup.c optional vt device-driver i386/isa/pcvt/pcvt_vtf.c optional vt device-driver i386/isa/pnp.c optional pnp device-driver i386/isa/prof_machdep.c optional profiling-routine i386/isa/ppc.c optional ppc device-driver i386/isa/pcf.c optional pcf device-driver -i386/isa/psm.c optional psm device-driver +isa/psm.c optional psm device-driver i386/isa/random_machdep.c standard i386/isa/rc.c optional rc device-driver i386/isa/rp.c optional rp device-driver i386/isa/scd.c optional scd device-driver i386/isa/si.c optional si device-driver i386/isa/si2_z280.c optional si device-driver i386/isa/si3_t225.c optional si device-driver -i386/isa/sio.c optional sio device-driver +isa/sio.c optional sio device-driver i386/isa/snd/sound.c optional pcm device-driver i386/isa/snd/dmabuf.c optional pcm device-driver i386/isa/snd/ad1848.c optional pcm device-driver i386/isa/snd/sb_dsp.c optional pcm device-driver i386/isa/snd/clones.c optional pcm device-driver i386/isa/sound/dev_table.c optional snd device-driver i386/isa/sound/soundcard.c optional snd device-driver i386/isa/sound/sound_switch.c optional snd device-driver i386/isa/sound/audio.c optional snd device-driver i386/isa/sound/dmabuf.c optional snd device-driver i386/isa/sound/sys_timer.c optional snd device-driver i386/isa/sound/sequencer.c optional snd device-driver i386/isa/sound/patmgr.c optional snd device-driver i386/isa/sound/adlib_card.c optional opl device-driver i386/isa/sound/opl3.c optional opl device-driver i386/isa/sound/gus_card.c optional gus device-driver i386/isa/sound/gus_midi.c optional gus device-driver i386/isa/sound/gus_vol.c optional gus device-driver i386/isa/sound/gus_wave.c optional gus device-driver i386/isa/sound/ics2101.c optional gus device-driver i386/isa/sound/sound_timer.c optional gus device-driver i386/isa/sound/sound_timer.c optional css device-driver i386/isa/sound/sound_timer.c optional mss device-driver i386/isa/sound/midi_synth.c optional gus device-driver i386/isa/sound/midibuf.c optional gus device-driver i386/isa/sound/ad1848.c optional gusxvi device-driver i386/isa/sound/ad1848.c optional gus device-driver i386/isa/sound/ad1848.c optional mss device-driver i386/isa/sound/ad1848.c optional css device-driver i386/isa/sound/sound_timer.c optional mss device-driver i386/isa/sound/midi_synth.c optional mss device-driver i386/isa/sound/midibuf.c optional mss device-driver i386/isa/sound/mpu401.c optional mpu device-driver i386/isa/sound/midi_synth.c optional mpu device-driver i386/isa/sound/midibuf.c optional mpu device-driver i386/isa/sound/pas2_card.c optional pas device-driver i386/isa/sound/pas2_midi.c optional pas device-driver i386/isa/sound/pas2_mixer.c optional pas device-driver i386/isa/sound/pas2_pcm.c optional pas device-driver i386/isa/sound/midi_synth.c optional pas device-driver i386/isa/sound/midibuf.c optional pas device-driver i386/isa/sound/sb_card.c optional sb device-driver i386/isa/sound/sb_dsp.c optional sb device-driver i386/isa/sound/sb_midi.c optional sb device-driver i386/isa/sound/sb_mixer.c optional sb device-driver i386/isa/sound/midi_synth.c optional sb device-driver i386/isa/sound/midibuf.c optional sb device-driver i386/isa/sound/sb16_dsp.c optional sbxvi device-driver i386/isa/sound/sb16_midi.c optional sbmidi device-driver i386/isa/sound/uart6850.c optional uart device-driver i386/isa/sound/midi_synth.c optional uart device-driver i386/isa/sound/midi_synth.c optional css device-driver i386/isa/sound/midibuf.c optional uart device-driver i386/isa/sound/midibuf.c optional css device-driver i386/isa/sound/trix.c optional trix device-driver i386/isa/sound/adlib_card.c optional trix device-driver i386/isa/sound/opl3.c optional trix device-driver i386/isa/sound/ad1848.c optional trix device-driver i386/isa/sound/sound_timer.c optional trix device-driver i386/isa/sound/sscape.c optional sscape device-driver i386/isa/sound/ad1848.c optional sscape device-driver i386/isa/sound/sound_timer.c optional sscape device-driver i386/isa/sound/mpu401.c optional sscape device-driver i386/isa/sound/midi_synth.c optional sscape device-driver i386/isa/sound/midibuf.c optional sscape device-driver i386/isa/sound/cs4232.c optional css device-driver i386/isa/spigot.c optional spigot device-driver i386/isa/spkr.c optional speaker device-driver i386/isa/stallion.c optional stl device-driver -i386/isa/syscons_isa.c optional sc device-driver +isa/syscons_isa.c optional sc device-driver i386/isa/vesa.c optional vga device-driver -i386/isa/vga_isa.c optional vga device-driver +isa/vga_isa.c optional vga device-driver i386/isa/tw.c optional tw device-driver i386/isa/wd.c optional wdc device-driver i386/isa/wd.c optional wd device-driver i386/isa/atapi.c optional wdc device-driver i386/isa/atapi-cd.c optional wcd device-driver i386/isa/wfd.c optional wfd device-driver i386/isa/wst.c optional wst device-driver i386/isa/wt.c optional wt device-driver i386/linux/imgact_linux.c optional compat_linux i386/linux/linux_dummy.c optional compat_linux i386/linux/linux_file.c optional compat_linux i386/linux/linux_ioctl.c optional compat_linux i386/linux/linux_ipc.c optional compat_linux i386/linux/linux_locore.s optional compat_linux \ dependency "linux_assym.h" i386/linux/linux_misc.c optional compat_linux i386/linux/linux_signal.c optional compat_linux i386/linux/linux_socket.c optional compat_linux i386/linux/linux_stats.c optional compat_linux i386/linux/linux_sysent.c optional compat_linux i386/linux/linux_sysvec.c optional compat_linux i386/linux/linux_util.c optional compat_linux i4b/layer1/i4b_isic.c optional isic device-driver i4b/layer1/i4b_isic_isa.c optional isic device-driver i4b/layer1/i4b_isic_pnp.c optional isic device-driver i4b/layer1/i4b_isic_pci.c optional isic device-driver i4b/layer1/i4b_isic_pcmcia.c optional isic device-driver i4b/layer1/i4b_isac.c optional isic device-driver i4b/layer1/i4b_hscx.c optional isic device-driver i4b/layer1/i4b_l1.c optional isic device-driver i4b/layer1/i4b_l1fsm.c optional isic device-driver i4b/layer1/i4b_bchan.c optional isic device-driver i4b/layer1/i4b_tel_s08.c optional isic device-driver i4b/layer1/i4b_tel_s016.c optional isic device-driver i4b/layer1/i4b_tel_s0163.c optional isic device-driver i4b/layer1/i4b_tel_s0P.c optional isic device-driver i4b/layer1/i4b_ctx_s0P.c optional isic device-driver i4b/layer1/i4b_avm_a1.c optional isic device-driver i4b/layer1/i4b_avm_fritz_pci.c optional isic device-driver i4b/layer1/i4b_avm_fritz_pcmcia.c optional isic device-driver i4b/layer1/i4b_usr_sti.c optional isic device-driver i4b/layer1/i4b_itk_ix1.c optional isic device-driver i4b/layer1/i4b_drn_ngo.c optional isic device-driver i4b/layer1/i4b_sws.c optional isic device-driver i4b/layer1/i4b_dynalink.c optional isic device-driver i4b/layer1/i4b_elsa_qs1i.c optional isic device-driver i4b/layer1/i4b_elsa_qs1p.c optional isic device-driver libkern/bcd.c standard libkern/divdi3.c standard libkern/inet_ntoa.c standard libkern/index.c standard libkern/mcount.c optional profiling-routine libkern/moddi3.c standard libkern/qdivrem.c standard libkern/qsort.c standard libkern/random.c standard libkern/rindex.c standard libkern/scanc.c standard libkern/skpc.c standard libkern/strcat.c standard libkern/strcmp.c standard libkern/strcpy.c standard libkern/strlen.c standard libkern/strncmp.c standard libkern/strncpy.c standard libkern/udivdi3.c standard libkern/umoddi3.c standard gnu/i386/fpemul/div_small.s optional gpl_math_emulate gnu/i386/fpemul/errors.c optional gpl_math_emulate gnu/i386/fpemul/fpu_arith.c optional gpl_math_emulate gnu/i386/fpemul/fpu_aux.c optional gpl_math_emulate gnu/i386/fpemul/fpu_entry.c optional gpl_math_emulate gnu/i386/fpemul/fpu_etc.c optional gpl_math_emulate gnu/i386/fpemul/fpu_trig.c optional gpl_math_emulate gnu/i386/fpemul/get_address.c optional gpl_math_emulate gnu/i386/fpemul/load_store.c optional gpl_math_emulate gnu/i386/fpemul/poly_2xm1.c optional gpl_math_emulate gnu/i386/fpemul/poly_atan.c optional gpl_math_emulate gnu/i386/fpemul/poly_div.s optional gpl_math_emulate gnu/i386/fpemul/poly_l2.c optional gpl_math_emulate gnu/i386/fpemul/poly_mul64.s optional gpl_math_emulate gnu/i386/fpemul/poly_sin.c optional gpl_math_emulate gnu/i386/fpemul/poly_tan.c optional gpl_math_emulate gnu/i386/fpemul/polynomial.s optional gpl_math_emulate gnu/i386/fpemul/reg_add_sub.c optional gpl_math_emulate gnu/i386/fpemul/reg_compare.c optional gpl_math_emulate gnu/i386/fpemul/reg_constant.c optional gpl_math_emulate gnu/i386/fpemul/reg_div.s optional gpl_math_emulate gnu/i386/fpemul/reg_ld_str.c optional gpl_math_emulate gnu/i386/fpemul/reg_mul.c optional gpl_math_emulate gnu/i386/fpemul/reg_norm.s optional gpl_math_emulate gnu/i386/fpemul/reg_round.s optional gpl_math_emulate gnu/i386/fpemul/reg_u_add.s optional gpl_math_emulate gnu/i386/fpemul/reg_u_div.s optional gpl_math_emulate gnu/i386/fpemul/reg_u_mul.s optional gpl_math_emulate gnu/i386/fpemul/reg_u_sub.s optional gpl_math_emulate gnu/i386/fpemul/wm_shrx.s optional gpl_math_emulate gnu/i386/fpemul/wm_sqrt.s optional gpl_math_emulate gnu/i386/isa/dgb.c optional dgb device-driver gnu/i386/isa/dgm.c optional dgm device-driver gnu/i386/isa/sound/awe_wave.c optional awe device-driver pci/es1370.c optional pcm device-driver pci/ide_pci.c optional wd device-driver Index: head/sys/dev/aha/aha_isa.c =================================================================== --- head/sys/dev/aha/aha_isa.c (revision 45719) +++ head/sys/dev/aha/aha_isa.c (revision 45720) @@ -1,291 +1,288 @@ /* * Product specific probe and attach routines for: * Adaptec 154x. * * Derived from code written by: * * Copyright (c) 1998 Justin T. Gibbs * 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, * without modification, immediately at the beginning of the file. * 2. 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 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. * - * $Id: aha_isa.c,v 1.5 1998/11/10 06:44:54 gibbs Exp $ + * $Id: aha_isa.c,v 1.6 1999/01/20 06:21:23 imp Exp $ */ #include "pnp.h" #include #include #include #include #include #include #include #include #if NPNP > 0 #include #endif static int aha_isa_probe(struct isa_device *dev); static int aha_isa_attach(struct isa_device *dev); static void aha_isa_intr(void *unit); struct isa_driver ahadriver = { aha_isa_probe, aha_isa_attach, "aha" }; /* * Check if the device can be found at the port given * and if so, set it up ready for further work * as an argument, takes the isa_device structure from * autoconf.c */ static int aha_isa_probe(dev) struct isa_device *dev; { /* * find unit and check we have that many defined */ struct aha_softc *aha; int port_index; int max_port_index; aha = NULL; /* * Bound our board search if the user has * specified an exact port. */ aha_find_probe_range(dev->id_iobase, &port_index, &max_port_index); if (port_index < 0) return 0; /* Attempt to find an adapter */ for (;port_index <= max_port_index; port_index++) { config_data_t config_data; u_int ioport; int error; ioport = aha_iop_from_bio(port_index); /* * Ensure this port has not already been claimed already * by a PCI, EISA or ISA adapter. */ if (aha_check_probed_iop(ioport) != 0) continue; dev->id_iobase = ioport; if (haveseen_isadev(dev, CC_IOADDR | CC_QUIET)) continue; /* Allocate a softc for use during probing */ aha = aha_alloc(dev->id_unit, I386_BUS_SPACE_IO, ioport); if (aha == NULL) break; /* We're going to attempt to probe it now, so mark it probed */ aha_mark_probed_bio(port_index); /* See if there is really a card present */ if (aha_probe(aha) || aha_fetch_adapter_info(aha)) { aha_free(aha); continue; } /* * Determine our IRQ, and DMA settings and * export them to the configuration system. */ error = aha_cmd(aha, AOP_INQUIRE_CONFIG, NULL, /*parmlen*/0, (u_int8_t*)&config_data, sizeof(config_data), DEFAULT_CMD_TIMEOUT); if (error != 0) { printf("aha_isa_probe: Could not determine IRQ or DMA " "settings for adapter at 0x%x. Failing probe\n", ioport); aha_free(aha); continue; } switch (config_data.dma_chan) { case DMA_CHAN_5: dev->id_drq = 5; break; case DMA_CHAN_6: dev->id_drq = 6; break; case DMA_CHAN_7: dev->id_drq = 7; break; default: printf("aha_isa_probe: Invalid DMA setting " "detected for adapter at 0x%x. " "Failing probe\n", ioport); return (0); } dev->id_irq = (config_data.irq << 9); dev->id_intr = aha_isa_intr; aha_unit++; return (AHA_NREGS); } return (0); } /* * Attach all the sub-devices we can find */ static int aha_isa_attach(dev) struct isa_device *dev; { struct aha_softc *aha; bus_dma_filter_t *filter; void *filter_arg; bus_addr_t lowaddr; aha = aha_softcs[dev->id_unit]; if (dev->id_drq != -1) isa_dmacascade(dev->id_drq); /* Allocate our parent dmatag */ filter = NULL; filter_arg = NULL; lowaddr = BUS_SPACE_MAXADDR_24BIT; if (bus_dma_tag_create(/*parent*/NULL, /*alignemnt*/0, /*boundary*/0, lowaddr, /*highaddr*/BUS_SPACE_MAXADDR, filter, filter_arg, /*maxsize*/BUS_SPACE_MAXSIZE_24BIT, /*nsegments*/BUS_SPACE_UNRESTRICTED, /*maxsegsz*/BUS_SPACE_MAXSIZE_24BIT, /*flags*/0, &aha->parent_dmat) != 0) { aha_free(aha); return (-1); } if (aha_init(aha)) { printf("aha init failed\n"); aha_free(aha); return (-1); } return (aha_attach(aha)); } /* * Handle an ISA interrupt. * XXX should go away as soon as ISA interrupt handlers * take a (void *) arg. */ static void aha_isa_intr(void *unit) { struct aha_softc* arg = aha_softcs[(int)unit]; aha_intr((void *)arg); } /* * support PnP cards if we are using 'em */ #if NPNP > 0 static char *ahapnp_probe(u_long csn, u_long vend_id); static void ahapnp_attach(u_long csn, u_long vend_id, char *name, struct isa_device *dev); static u_long nahapnp = NAHA; static struct pnp_device ahapnp = { "ahapnp", ahapnp_probe, ahapnp_attach, &nahapnp, &bio_imask }; DATA_SET (pnpdevice_set, ahapnp); static char * ahapnp_probe(u_long csn, u_long vend_id) { struct pnp_cinfo d; char *s = NULL; if (vend_id != AHA1542_PNP && vend_id != AHA1542_PNPCOMPAT) return (NULL); read_pnp_parms(&d, 0); if (d.enable == 0 || d.flags & 1) { printf("CSN %lu is disabled.\n", csn); return (NULL); } s = "Adaptec 1542CP"; return (s); } static void ahapnp_attach(u_long csn, u_long vend_id, char *name, struct isa_device *dev) { struct pnp_cinfo d; - struct isa_device *dvp; if (dev->id_unit >= NAHATOT) return; if (read_pnp_parms(&d, 0) == 0) { printf("failed to read pnp parms\n"); return; } write_pnp_parms(&d, 0); enable_pnp_card(); dev->id_iobase = d.port[0]; dev->id_irq = (1 << d.irq[0]); dev->id_intr = aha_intr; dev->id_drq = d.drq[0]; if (dev->id_driver == NULL) { dev->id_driver = &ahadriver; - dvp = find_isadev(isa_devtab_tty, &ahadriver, 0); - if (dvp != NULL) - dev->id_id = dvp->id_id; + dev->id_id = isa_compat_nextid(); } if ((dev->id_alive = aha_isa_probe(dev)) != 0) aha_isa_attach(dev); else printf("aha%d: probe failed\n", dev->id_unit); } #endif Index: head/sys/dev/ata/ata-all.c =================================================================== --- head/sys/dev/ata/ata-all.c (revision 45719) +++ head/sys/dev/ata/ata-all.c (revision 45720) @@ -1,635 +1,655 @@ /*- * Copyright (c) 1998,1999 Søren Schmidt * 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, * without modification, immediately at the beginning of 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. 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. * - * $Id: ata-all.c,v 1.5 1999/03/28 18:57:18 sos Exp $ + * $Id: ata-all.c,v 1.6 1999/04/10 18:53:35 sos Exp $ */ #include "ata.h" #if NATA > 0 #include "isa.h" #include "pci.h" #include "atadisk.h" #include #include #include #include #include #include #include #include #include #include #include +#ifdef __i386__ #include +#endif #include #include +#ifdef __i386__ #include #include #include +#else +#include +#endif #include #include #include /* misc defines */ #define UNIT(dev) (dev>>3 & 0x1f) /* assume 8 minor # per unit */ #define MIN(a,b) ((a)>(b)?(b):(a)) #if NSMP == 0 #define isa_apic_irq(x) x #endif /* prototypes */ -#if NISA > 0 +#if NISA > 0 && defined(__i386__) static int32_t ata_isaprobe(struct isa_device *); static int32_t ata_isaattach(struct isa_device *); #endif #if NPCI > 0 static const char *ata_pciprobe(pcici_t, pcidi_t); static void ata_pciattach(pcici_t, int32_t); static void promise_intr(int32_t); #endif static int32_t ata_probe(int32_t, int32_t, int32_t, pcici_t, int32_t *); static void ataintr(int32_t); static int32_t atanlun = 0; struct ata_softc *atadevices[MAXATA]; + +#if NISA > 0 && defined(__i386__) struct isa_driver atadriver = { ata_isaprobe, ata_isaattach, "ata" }; -#if NISA > 0 static int32_t ata_isaprobe(struct isa_device *devp) { int32_t ctlr, res; for (ctlr = 0; ctlr < atanlun; ctlr++) { if (atadevices[ctlr]->ioaddr == devp->id_iobase) { printf("ata-isa%d: already registered as ata%d\n", devp->id_unit, ctlr); return 0; } } res = ata_probe(devp->id_iobase, devp->id_iobase + ATA_ALTPORT, 0, 0, &devp->id_unit); if (res) devp->id_intr = (inthand2_t *)ataintr; return res; } static int32_t ata_isaattach(struct isa_device *devp) { return 1; } #endif #if NPCI > 0 static u_long ata_pcicount; static struct pci_device ata_pcidevice = { "ata-pci", ata_pciprobe, ata_pciattach, &ata_pcicount, 0 }; DATA_SET(pcidevice_set, ata_pcidevice); static const char * ata_pciprobe(pcici_t tag, pcidi_t type) { u_int32_t data; data = pci_conf_read(tag, PCI_CLASS_REG); if ((data & PCI_CLASS_MASK) == PCI_CLASS_MASS_STORAGE && ((data & PCI_SUBCLASS_MASK) == 0x00010000 || ((data & PCI_SUBCLASS_MASK) == 0x00040000))) { switch (type) { case 0x12308086: return "Intel PIIX IDE controller"; case 0x70108086: return "Intel PIIX3 IDE controller"; case 0x71118086: return "Intel PIIX4 IDE controller"; case 0x4d33105a: return "Promise Ultra/33 IDE controller"; case 0x522910b9: return "AcerLabs Aladdin IDE controller"; + case 0x06401095: + return "CMD 640 IDE controller"; + case 0x06461095: + return "CMD 646 IDE controller"; #if 0 case 0x05711106: return "VIA Apollo IDE controller"; case 0x01021078: return "Cyrix 5530 IDE controller"; #endif default: return "Unknown PCI IDE controller"; } } return NULL; } static void ata_pciattach(pcici_t tag, int32_t unit) { pcidi_t type, class, cmd; int32_t iobase_1, iobase_2, altiobase_1, altiobase_2; int32_t bmaddr_1 = 0, bmaddr_2 = 0, sysctrl = 0, irq1, irq2; int32_t lun; /* set up vendor-specific stuff */ type = pci_conf_read(tag, PCI_ID_REG); class = pci_conf_read(tag, PCI_CLASS_REG); cmd = pci_conf_read(tag, PCI_COMMAND_STATUS_REG); #ifdef ATA_DEBUG printf("ata%d: type=%08x class=%08x cmd=%08x\n", unit, type, class, cmd); #endif /* if this is a Promise controller handle it specially */ if (type == 0x4d33105a) { iobase_1 = pci_conf_read(tag, 0x10) & 0xfffc; altiobase_1 = pci_conf_read(tag, 0x14) & 0xfffc; iobase_2 = pci_conf_read(tag, 0x18) & 0xfffc; altiobase_2 = pci_conf_read(tag, 0x1c) & 0xfffc; irq1 = irq2 = pci_conf_read(tag, PCI_INTERRUPT_REG) & 0xff; bmaddr_1 = pci_conf_read(tag, 0x20) & 0xfffc; bmaddr_2 = bmaddr_1 + ATA_BM_OFFSET1; sysctrl = (pci_conf_read(tag, 0x20) & 0xfffc) + 0x1c; outb(bmaddr_1 + 0x1f, inb(bmaddr_1 + 0x1f) | 0x01); printf("ata-pci%d: Busmastering DMA supported\n", unit); } /* everybody else seems to do it this way */ else { if ((class & 0x100) == 0) { iobase_1 = IO_WD1; altiobase_1 = iobase_1 + ATA_ALTPORT; irq1 = 14; } else { iobase_1 = pci_conf_read(tag, 0x10) & 0xfffc; altiobase_1 = pci_conf_read(tag, 0x14) & 0xfffc; irq1 = pci_conf_read(tag, PCI_INTERRUPT_REG) & 0xff; } if ((class & 0x400) == 0) { iobase_2 = IO_WD2; altiobase_2 = iobase_2 + ATA_ALTPORT; irq2 = 15; } else { iobase_2 = pci_conf_read(tag, 0x18) & 0xfffc; altiobase_2 = pci_conf_read(tag, 0x1c) & 0xfffc; irq2 = pci_conf_read(tag, PCI_INTERRUPT_REG) & 0xff; } /* is this controller busmaster capable ? */ if (pci_conf_read(tag, PCI_CLASS_REG) & 0x8000) { /* is busmastering support turned on ? */ if ((pci_conf_read(tag, PCI_COMMAND_STATUS_REG) & 5) == 5) { /* is there a valid port range to connect to ? */ if ((bmaddr_1 = pci_conf_read(tag, 0x20) & 0xfffc)) { bmaddr_2 = bmaddr_1 + ATA_BM_OFFSET1; printf("ata-pci%d: Busmastering DMA supported\n", unit); } else printf("ata-pci%d: Busmastering DMA not configured\n",unit); } else printf("ata-pci%d: Busmastering DMA not enabled\n", unit); } else printf("ata-pci%d: Busmastering DMA not supported\n", unit); } /* now probe the addresse found for "real" ATA/ATAPI hardware */ lun = 0; if (ata_probe(iobase_1, altiobase_1, bmaddr_1, tag, &lun)) { if (iobase_1 == IO_WD1) +#ifdef __i386__ register_intr(irq1, (int)"", 0, (inthand2_t *)ataintr, &bio_imask, lun); +#else + alpha_platform_setup_ide_intr(0, ataintr, (void *)(intptr_t)lun); +#endif else { if (sysctrl) pci_map_int(tag, (inthand2_t *)promise_intr, - (void *)lun, &bio_imask); + (void *)(intptr_t)lun, &bio_imask); else - pci_map_int(tag, (inthand2_t *)ataintr, (void *)lun,&bio_imask); + pci_map_int(tag, (inthand2_t *)ataintr, (void *)(intptr_t)lun,&bio_imask); } printf("ata%d at 0x%04x irq %d on ata-pci%d\n", lun, iobase_1, isa_apic_irq(irq1), unit); } lun = 1; if (ata_probe(iobase_2, altiobase_2, bmaddr_2, tag, &lun)) { if (iobase_2 == IO_WD2) +#ifdef __i386__ register_intr(irq2, (int)"", 0, (inthand2_t *)ataintr, &bio_imask, lun); +#else + alpha_platform_setup_ide_intr(1, ataintr, (void *)(intptr_t)lun); +#endif else { if (!sysctrl) - pci_map_int(tag, (inthand2_t *)ataintr, (void *)lun,&bio_imask); + pci_map_int(tag, (inthand2_t *)ataintr, (void *)(intptr_t)lun,&bio_imask); } printf("ata%d at 0x%04x irq %d on ata-pci%d\n", lun, iobase_2, isa_apic_irq(irq2), unit); } } static void promise_intr(int32_t unit) { struct ata_softc *scp = atadevices[unit]; int32_t channel = inl((pci_conf_read(scp->tag, 0x20) & 0xfffc) + 0x1c); if (channel & 0x00000400) ataintr(unit); if (channel & 0x00004000) ataintr(unit+1); } #endif static int32_t ata_probe(int32_t ioaddr, int32_t altioaddr, int32_t bmaddr, pcici_t tag, int32_t *unit) { struct ata_softc *scp = atadevices[atanlun]; int32_t mask = 0; int32_t timeout; int32_t lun = atanlun; u_int8_t status0, status1; #ifdef ATA_STATIC_ID atanlun++; #endif if (lun > MAXATA) { printf("ata: unit out of range(%d)\n", lun); return 0; } if (scp) { printf("ata%d: unit already attached\n", lun); return 0; } scp = malloc(sizeof(struct ata_softc), M_DEVBUF, M_NOWAIT); if (scp == NULL) { printf("ata%d: failed to allocate driver storage\n", lun); return 0; } bzero(scp, sizeof(struct ata_softc)); scp->unit = *unit; scp->lun = lun; scp->ioaddr = ioaddr; scp->altioaddr = altioaddr; scp->active = ATA_IDLE; #ifdef ATA_DEBUG printf("ata%d: iobase=0x%04x altiobase=0x%04x\n", scp->lun, scp->ioaddr, scp->altioaddr); #endif /* do we have any signs of ATA/ATAPI HW being present ? */ outb(scp->ioaddr + ATA_DRIVE, ATA_D_IBM | ATA_MASTER); DELAY(1); status0 = inb(scp->ioaddr + ATA_STATUS); outb(scp->ioaddr + ATA_DRIVE, ATA_D_IBM | ATA_SLAVE); DELAY(1); status1 = inb(scp->ioaddr + ATA_STATUS); if ((status0 & 0xf8) != 0xf8) mask |= 0x01; if ((status1 & 0xf8) != 0xf8) mask |= 0x02; #ifdef ATA_DEBUG printf("ata%d: mask=%02x status0=%02x status1=%02x\n", scp->lun, mask, status0, status1); #endif if (!mask) { free(scp, M_DEVBUF); return 0; } /* assert reset for devices and wait for completition */ outb(scp->ioaddr + ATA_DRIVE, ATA_D_IBM | ATA_MASTER); DELAY(1); outb(scp->altioaddr, ATA_A_IDS | ATA_A_RESET); DELAY(1000); outb(scp->altioaddr, ATA_A_IDS); DELAY(1000); inb(scp->ioaddr + ATA_ERROR); DELAY(1); outb(scp->altioaddr, ATA_A_4BIT); DELAY(1); /* wait for BUSY to go inactive */ for (timeout = 0; timeout < 30000*10; timeout++) { outb(scp->ioaddr + ATA_DRIVE, ATA_D_IBM | ATA_MASTER); DELAY(1); status0 = inb(scp->ioaddr + ATA_STATUS); outb(scp->ioaddr + ATA_DRIVE, ATA_D_IBM | ATA_SLAVE); DELAY(1); status1 = inb(scp->ioaddr + ATA_STATUS); if (mask == 0x01) /* wait for master only */ if (!(status0 & ATA_S_BSY)) break; if (mask == 0x02) /* wait for slave only */ if (!(status1 & ATA_S_BSY)) break; if (mask == 0x03) /* wait for both master & slave */ if (!(status0 & ATA_S_BSY) && !(status1 & ATA_S_BSY)) break; DELAY(100); } if (status0 & ATA_S_BSY) mask &= ~0x01; if (status1 & ATA_S_BSY) mask &= ~0x02; #ifdef ATA_DEBUG printf("ata%d: mask=%02x status0=%02x status1=%02x\n", scp->lun, mask, status0, status1); #endif if (!mask) { free(scp, M_DEVBUF); return 0; } /* * OK, we have at least one device on the chain, * check for ATAPI signatures, if none check if its * a good old ATA device. */ outb(scp->ioaddr + ATA_DRIVE, (ATA_D_IBM | ATA_MASTER)); DELAY(1); if (inb(scp->ioaddr + ATA_CYL_LSB) == ATAPI_MAGIC_LSB && inb(scp->ioaddr + ATA_CYL_MSB) == ATAPI_MAGIC_MSB) { scp->devices |= ATA_ATAPI_MASTER; } outb(scp->ioaddr + ATA_DRIVE, (ATA_D_IBM | ATA_SLAVE)); DELAY(1); if (inb(scp->ioaddr + ATA_CYL_LSB) == ATAPI_MAGIC_LSB && inb(scp->ioaddr + ATA_CYL_MSB) == ATAPI_MAGIC_MSB) { scp->devices |= ATA_ATAPI_SLAVE; } if (status0 != 0x00 && !(scp->devices & ATA_ATAPI_MASTER)) { outb(scp->ioaddr + ATA_DRIVE, (ATA_D_IBM | ATA_MASTER)); DELAY(1); outb(scp->ioaddr + ATA_ERROR, 0x58); outb(scp->ioaddr + ATA_CYL_LSB, 0xa5); if (inb(scp->ioaddr + ATA_ERROR) != 0x58 && inb(scp->ioaddr + ATA_CYL_LSB) == 0xa5) { scp->devices |= ATA_ATA_MASTER; } } if (status1 != 0x00 && !(scp->devices & ATA_ATAPI_SLAVE)) { outb(scp->ioaddr + ATA_DRIVE, (ATA_D_IBM | ATA_SLAVE)); DELAY(1); outb(scp->ioaddr + ATA_ERROR, 0x58); outb(scp->ioaddr + ATA_CYL_LSB, 0xa5); if (inb(scp->ioaddr + ATA_ERROR) != 0x58 && inb(scp->ioaddr + ATA_CYL_LSB) == 0xa5) { scp->devices |= ATA_ATA_SLAVE; } } #ifdef ATA_DEBUG printf("ata%d: devices = 0x%x\n", scp->lun, scp->devices); #endif if (!scp->devices) { free(scp, M_DEVBUF); return 0; } bufq_init(&scp->ata_queue); TAILQ_INIT(&scp->atapi_queue); *unit = scp->lun; scp->tag = tag; if (bmaddr) scp->bmaddr = bmaddr; atadevices[scp->lun] = scp; #ifndef ATA_STATIC_ID atanlun++; #endif + outb(scp->ioaddr + ATA_DRIVE, ATA_D_IBM | ATA_MASTER); return ATA_IOSIZE; } static void ataintr(int32_t unit) { struct ata_softc *scp; struct atapi_request *atapi_request; struct buf *ata_request; u_int8_t status; static int32_t intr_count = 0; if (unit < 0 || unit > atanlun) { printf("ataintr: unit %d unusable\n", unit); return; } scp = atadevices[unit]; /* find & call the responsible driver to process this interrupt */ switch (scp->active) { #if NATADISK > 0 case ATA_ACTIVE_ATA: if ((ata_request = bufq_first(&scp->ata_queue))) if (ad_interrupt(ata_request) == ATA_OP_CONTINUES) return; break; #endif case ATA_ACTIVE_ATAPI: if ((atapi_request = TAILQ_FIRST(&scp->atapi_queue))) if (atapi_interrupt(atapi_request) == ATA_OP_CONTINUES) return; break; case ATA_WAIT_INTR: wakeup((caddr_t)scp); break; case ATA_IGNORE_INTR: break; default: case ATA_IDLE: status = inb(scp->ioaddr + ATA_STATUS); if (intr_count++ < 10) printf("ata%d: unwanted interrupt %d status = %02x\n", unit, intr_count, status); return; } scp->active = ATA_IDLE; ata_start(scp); } void ata_start(struct ata_softc *scp) { struct buf *ata_request; struct atapi_request *atapi_request; #ifdef ATA_DEBUG printf("ata_start: entered\n"); #endif if (scp->active != ATA_IDLE) { printf("ata: unwanted ata_start\n"); return; } #if NATADISK > 0 /* find & call the responsible driver if anything on ATA queue */ if ((ata_request = bufq_first(&scp->ata_queue))) { scp->active = ATA_ACTIVE_ATA; ad_transfer(ata_request); #ifdef ATA_DEBUG printf("ata_start: started ata, leaving\n"); #endif return; } #endif /* find & call the responsible driver if anything on ATAPI queue */ if ((atapi_request = TAILQ_FIRST(&scp->atapi_queue))) { scp->active = ATA_ACTIVE_ATAPI; atapi_transfer(atapi_request); #ifdef ATA_DEBUG printf("ata_start: started atapi, leaving\n"); #endif return; } } int32_t ata_wait(struct ata_softc *scp, int32_t device, u_int8_t mask) { u_int8_t status; u_int32_t timeout = 0; while (timeout++ <= 500000) { /* timeout 5 secs */ status = inb(scp->ioaddr + ATA_STATUS); /* if drive fails status, reselect the drive just to be sure */ if (status == 0xff) { printf("ata%d: %s: no status, reselecting device\n", scp->lun, device?"slave":"master"); outb(scp->ioaddr + ATA_DRIVE, ATA_D_IBM | device); DELAY(1); status = inb(scp->ioaddr + ATA_STATUS); } if (status == 0xff) return -1; scp->status = status; if (!(status & ATA_S_BSY)) { if (status & ATA_S_ERROR) scp->error = inb(scp->ioaddr + ATA_ERROR); if ((status & mask) == mask) return (status & ATA_S_ERROR); } if (timeout > 1000) DELAY(1000); else DELAY(10); } return -1; } int32_t ata_command(struct ata_softc *scp, int32_t device, u_int32_t command, u_int32_t cylinder, u_int32_t head, u_int32_t sector, u_int32_t count, u_int32_t feature, int32_t flags) { #ifdef ATA_DEBUG printf("ata_command: addr=%04x, device=%02x, cmd=%02x, c=%d, h=%d, s=%d, count=%d, flags=%02x\n", scp->ioaddr, device, command, cylinder, head, sector, count, flags); #endif /* ready to issue command ? */ if (ata_wait(scp, device, 0) < 0) { printf("ata%d: %s: timeout waiting to give command s=%02x e=%02x\n", scp->lun, device?"slave":"master", scp->status, scp->error); } outb(scp->ioaddr + ATA_FEATURE, feature); outb(scp->ioaddr + ATA_CYL_LSB, cylinder); outb(scp->ioaddr + ATA_CYL_MSB, cylinder >> 8); outb(scp->ioaddr + ATA_DRIVE, ATA_D_IBM | device | head); outb(scp->ioaddr + ATA_SECTOR, sector); outb(scp->ioaddr + ATA_COUNT, count); if (scp->active != ATA_IDLE && flags != ATA_IMMEDIATE) printf("DANGER active=%d\n", scp->active); switch (flags) { case ATA_WAIT_INTR: scp->active = ATA_WAIT_INTR; outb(scp->ioaddr + ATA_CMD, command); if (tsleep((caddr_t)scp, PRIBIO, "atacmd", 500)) { - printf("ata_command: timeout waiting for interrupt"); + printf("ata_command: timeout waiting for interrupt\n"); scp->active = ATA_IDLE; return -1; } break; case ATA_IGNORE_INTR: scp->active = ATA_IGNORE_INTR; outb(scp->ioaddr + ATA_CMD, command); break; case ATA_IMMEDIATE: default: outb(scp->ioaddr + ATA_CMD, command); break; } #ifdef ATA_DEBUG printf("ata_command: leaving\n"); #endif return 0; } void bswap(int8_t *buf, int32_t len) { u_int16_t *p = (u_int16_t*)(buf + len); while (--p >= (u_int16_t*)buf) *p = ntohs(*p); } void btrim(int8_t *buf, int32_t len) { int8_t *p; for (p = buf; p < buf+len; ++p) if (!*p) *p = ' '; for (p = buf + len - 1; p >= buf && *p == ' '; --p) *p = 0; } void bpack(int8_t *src, int8_t *dst, int32_t len) { int32_t i, j, blank; for (i = j = blank = 0 ; i < len-1; i++) { if (blank && src[i] == ' ') continue; if (blank && src[i] != ' ') { dst[j++] = src[i]; blank = 0; continue; } if (src[i] == ' ') blank = 1; dst[j++] = src[i]; } dst[j] = 0x00; } #endif /* NATA > 0 */ Index: head/sys/dev/ata/ata-disk.c =================================================================== --- head/sys/dev/ata/ata-disk.c (revision 45719) +++ head/sys/dev/ata/ata-disk.c (revision 45720) @@ -1,689 +1,689 @@ /*- * Copyright (c) 1998,1999 Søren Schmidt * 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, * without modification, immediately at the beginning of 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. 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. * - * $Id: ata-disk.c,v 1.5 1999/03/28 18:57:18 sos Exp $ + * $Id: ata-disk.c,v 1.6 1999/04/10 18:53:35 sos Exp $ */ #include "ata.h" #include "atadisk.h" #include "opt_devfs.h" #if NATA > 0 && NATADISK > 0 #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DEVFS #include #endif #include #include #include #include #include #include static d_open_t adopen; static d_close_t adclose; static d_read_t adread; static d_write_t adwrite; static d_ioctl_t adioctl; static d_strategy_t adstrategy; static d_psize_t adpsize; #define BDEV_MAJOR 30 #define CDEV_MAJOR 116 static struct cdevsw ad_cdevsw = { adopen, adclose, adread, adwrite, adioctl, nostop, nullreset, nodevtotty, #ifdef NOTYET /* the boot code needs to be fixed to boot arbitrary devices */ seltrue, nommap, adstrategy, "ad", #else seltrue, nommap, adstrategy, "wd", #endif NULL, -1, nodump, adpsize, D_DISK, 0, -1 }; /* misc defines */ #define UNIT(dev) (dev>>3 & 0x1f) /* assume 8 minor # per unit */ #define NUNIT 16 /* max # of devices */ /* prototypes */ static void ad_attach(void *); static int32_t ad_getparam(struct ad_softc *); static void ad_strategy(struct buf *); static void ad_start(struct ad_softc *); static void ad_sleep(struct ad_softc *, int8_t *); static int8_t ad_version(u_int16_t); int32_t ad_timeout(char *data); static void ad_drvinit(void); static struct ad_softc *adtab[NUNIT]; static int32_t adnlun = 0; /* number of config'd drives */ static struct intr_config_hook *ad_attach_hook; static __inline int apiomode(struct ata_params *ap) { if ((ap->atavalid & 2) == 2) { if ((ap->apiomodes & 2) == 2) return 4; if ((ap->apiomodes & 1) == 1) return 3; } return -1; } static __inline int wdmamode(struct ata_params *ap) { if ((ap->atavalid & 2) == 2) { if ((ap->wdmamodes & 4) == 4) return 2; if ((ap->wdmamodes & 2) == 2) return 1; if ((ap->wdmamodes & 1) == 1) return 0; } return -1; } static __inline int udmamode(struct ata_params *ap) { if ((ap->atavalid & 4) == 4) { if ((ap->udmamodes & 4) == 4) return 2; if ((ap->udmamodes & 2) == 2) return 1; if ((ap->udmamodes & 1) == 1) return 0; } return -1; } static void ad_attach(void *notused) { struct ad_softc *adp; int32_t ctlr, dev, secsperint; int8_t model_buf[40+1]; int8_t revision_buf[8+1]; /* now, run through atadevices and look for ATA disks */ for (ctlr=0; ctlrdevices & (dev ? ATA_ATA_SLAVE : ATA_ATA_MASTER)) { #ifdef ATA_STATIC_ID adnlun = dev + ctlr * 2; #endif adp = adtab[adnlun]; if (adp) printf("ad%d: unit already attached\n", adnlun); if (!(adp = malloc(sizeof(struct ad_softc), M_DEVBUF, M_NOWAIT))) { printf("ad%d: failed to allocate driver storage\n", adnlun); continue; } bzero(adp, sizeof(struct ad_softc)); adp->controller = atadevices[ctlr]; adp->unit = (dev == 0) ? ATA_MASTER : ATA_SLAVE; adp->lun = adnlun; if (ad_getparam(adp)) { free(adp, M_DEVBUF); continue; } adp->cylinders = adp->ata_parm->cylinders; adp->heads = adp->ata_parm->heads; adp->sectors = adp->ata_parm->sectors; adp->total_secs = adp->cylinders * adp->heads * adp->sectors; if (adp->cylinders == 16383 && adp->total_secs < adp->ata_parm->lbasize) { adp->total_secs = adp->ata_parm->lbasize; adp->cylinders = adp->total_secs/(adp->heads*adp->sectors); } if (adp->ata_parm->atavalid & ATA_FLAG_54_58 && adp->ata_parm->lbasize) adp->flags |= AD_F_USE_LBA; /* use multiple sectors/interrupt if device supports it */ adp->transfersize = DEV_BSIZE; secsperint = min(adp->ata_parm->nsecperint, 16); if (!ata_command(adp->controller, adp->unit, ATA_C_SET_MULTI, 0, 0, 0, secsperint, 0, ATA_WAIT_INTR) && ata_wait(adp->controller, adp->unit, ATA_S_DRDY) >= 0) adp->transfersize *= secsperint; /* use DMA if drive & controller supports it */ if (!ata_dmainit(adp->controller, adp->unit, apiomode(adp->ata_parm), wdmamode(adp->ata_parm), udmamode(adp->ata_parm))) adp->flags |= AD_F_DMA_ENABLED; bpack(adp->ata_parm->model, model_buf, sizeof(model_buf)); bpack(adp->ata_parm->revision, revision_buf, sizeof(revision_buf)); printf("ad%d: <%s/%s> ATA-%c disk at ata%d as %s\n", adnlun, model_buf, revision_buf, ad_version(adp->ata_parm->versmajor), ctlr, (adp->unit == ATA_MASTER) ? "master" : "slave "); printf("ad%d: %luMB (%u sectors), " "%u cyls, %u heads, %u S/T, %u B/S\n", adnlun, adp->total_secs / ((1024L * 1024L) / DEV_BSIZE), adp->total_secs, adp->cylinders, adp->heads, adp->sectors, DEV_BSIZE); printf("ad%d: piomode=%d, dmamode=%d, udmamode=%d\n", adnlun, apiomode(adp->ata_parm), wdmamode(adp->ata_parm), udmamode(adp->ata_parm)); printf("ad%d: %d secs/int, %d depth queue, %s mode\n", adnlun, adp->transfersize / DEV_BSIZE, adp->ata_parm->queuelen & 0x1f, (adp->flags & AD_F_DMA_ENABLED) ? "DMA" :"PIO"); devstat_add_entry(&adp->stats, "ad", adnlun, DEV_BSIZE, DEVSTAT_NO_ORDERED_TAGS, DEVSTAT_TYPE_DIRECT | DEVSTAT_TYPE_IF_IDE, 0x180); #ifdef DEVFS adp->cdevs_token = devfs_add_devswf(&ad_cdevsw, dkmakeminor(adp->lun, 0, 0), DV_CHR, UID_ROOT, GID_OPERATOR, 0640, "rad%d", adp->lun); adp->bdevs_token = devfs_add_devswf(&ad_cdevsw, dkmakeminor(adp->lun, 0, 0), DV_BLK, UID_ROOT, GID_OPERATOR, 0640, "ad%d", adp->lun); #endif bufq_init(&adp->queue); adtab[adnlun++] = adp; } } } config_intrhook_disestablish(ad_attach_hook); } static int32_t ad_getparam(struct ad_softc *adp) { struct ata_params *ata_parm; int8_t buffer[DEV_BSIZE]; /* select drive */ outb(adp->controller->ioaddr + ATA_DRIVE, ATA_D_IBM | adp->unit); DELAY(1); ata_command(adp->controller, adp->unit, ATA_C_ATA_IDENTIFY, 0, 0, 0, 0, 0, ATA_WAIT_INTR); if (ata_wait(adp->controller, adp->unit, ATA_S_DRDY | ATA_S_DSC | ATA_S_DRQ)) return -1; insw(adp->controller->ioaddr + ATA_DATA, buffer, sizeof(buffer)/sizeof(int16_t)); ata_parm = malloc(sizeof(struct ata_params), M_DEVBUF, M_NOWAIT); if (!ata_parm) return -1; bcopy(buffer, ata_parm, sizeof(struct ata_params)); bswap(ata_parm->model, sizeof(ata_parm->model)); btrim(ata_parm->model, sizeof(ata_parm->model)); bswap(ata_parm->revision, sizeof(ata_parm->revision)); btrim(ata_parm->revision, sizeof(ata_parm->revision)); adp->ata_parm = ata_parm; return 0; } static int adopen(dev_t dev, int32_t flags, int32_t fmt, struct proc *p) { int32_t lun = UNIT(dev); struct ad_softc *adp; struct disklabel label; int32_t error; #ifdef AD_DEBUG printf("adopen: lun=%d adnlun=%d\n", lun, adnlun); #endif if (lun >= adnlun || !(adp = adtab[lun])) return ENXIO; /* spinwait if anybody else is reading the disk label */ /* is this needed anymore ?? SOS XXX */ while (adp->flags & AD_F_LABELLING) tsleep((caddr_t)&adp->flags, PZERO - 1, "adop1", 1); /* protect agains label race */ adp->flags |= AD_F_LABELLING; /* build disklabel and initilize slice tables */ bzero(&label, sizeof label); label.d_secsize = DEV_BSIZE; label.d_nsectors = adp->sectors; label.d_ntracks = adp->heads; label.d_ncylinders = adp->cylinders; label.d_secpercyl = adp->sectors * adp->heads; label.d_secperunit = adp->total_secs; error = dsopen("ad", dev, fmt, 0, &adp->slices, &label, ad_strategy, (ds_setgeom_t *)NULL, &ad_cdevsw); adp->flags &= ~AD_F_LABELLING; ad_sleep(adp, "adop2"); return error; } static int adclose(dev_t dev, int32_t flags, int32_t fmt, struct proc *p) { int32_t lun = UNIT(dev); struct ad_softc *adp; #ifdef AD_DEBUG printf("adclose: lun=%d adnlun=%d\n", lun, adnlun); #endif if (lun >= adnlun || !(adp = adtab[lun])) return ENXIO; dsclose(dev, fmt, adp->slices); return 0; } static int adread(dev_t dev, struct uio *uio, int32_t ioflag) { return physio(adstrategy, NULL, dev, 1, minphys, uio); } static int adwrite(dev_t dev, struct uio *uio, int32_t ioflag) { return physio(adstrategy, NULL, dev, 0, minphys, uio); } static int adioctl(dev_t dev, u_long cmd, caddr_t addr, int32_t flags, struct proc *p) { struct ad_softc *adp; int32_t lun = UNIT(dev); int32_t error = 0; if (lun >= adnlun || !(adp = adtab[lun])) return ENXIO; ad_sleep(adp, "adioct"); error = dsioctl("sd", dev, cmd, addr, flags, &adp->slices, ad_strategy, (ds_setgeom_t *)NULL); if (error != ENOIOCTL) return error; return ENOTTY; } static void adstrategy(struct buf *bp) { struct ad_softc *adp; int32_t lun = UNIT(bp->b_dev); int32_t s; #ifdef AD_DEBUG printf("adstrategy: entered\n"); #endif if (lun >= adnlun || bp->b_blkno < 0 || !(adp = adtab[lun]) || bp->b_bcount % DEV_BSIZE != 0) { bp->b_error = EINVAL; bp->b_flags |= B_ERROR; biodone(bp); return; } if (dscheck(bp, adp->slices) <= 0) { biodone(bp); return; } /* hang around if somebody else is labelling */ if (adp->flags & AD_F_LABELLING) ad_sleep(adp, "adlab"); s = splbio(); bufqdisksort(&adp->queue, bp); if (!adp->active) ad_start(adp); if (adp->controller->active == ATA_IDLE) ata_start(adp->controller); splx(s); return; } static int adpsize(dev_t dev) { struct ad_softc *adp; int32_t lun = UNIT(dev); if (lun >= adnlun || !(adp = adtab[lun])) return -1; return dssize(dev, &adp->slices, adopen, adclose); } static void ad_strategy(struct buf *bp) { adstrategy(bp); } static void ad_start(struct ad_softc *adp) { struct buf *bp; #ifdef AD_DEBUG printf("ad_start:\n"); #endif if (adp->active) { printf("ad_start: should newer be called when active\n"); /* SOS */ return; } if (!(bp = bufq_first(&adp->queue))) return; /* remove from drive queue */ bufq_remove(&adp->queue, bp); bp->b_driver1 = adp; /* link onto controller queue */ bufq_insert_tail(&adp->controller->ata_queue, bp); /* mark the drive as busy */ adp->active = 1; } void ad_transfer(struct buf *bp) { struct ad_softc *adp; u_int32_t blknum, secsprcyl; u_int32_t cylinder, head, sector, count, command; /* get request params */ adp = bp->b_driver1; /* calculate transfer details */ blknum = bp->b_pblkno + (adp->donecount / DEV_BSIZE); #ifdef AD_DEBUG printf("ad_transfer: blknum=%d\n", blknum); #endif if (adp->donecount == 0) { /* setup transfer parameters */ adp->bytecount = bp->b_bcount; count = howmany(adp->bytecount, DEV_BSIZE); if (adp->flags & AD_F_USE_LBA) { sector = (blknum >> 0) & 0xff; cylinder = (blknum >> 8) & 0xffff; head = ((blknum >> 24) & 0xf) | ATA_D_LBA; } else { secsprcyl = adp->sectors * adp->heads; cylinder = blknum / secsprcyl; head = (blknum % secsprcyl) / adp->sectors; sector = (blknum % adp->sectors) + 1; } /* setup first transfer length */ adp->currentsize = min(adp->bytecount, adp->transfersize); devstat_start_transaction(&adp->stats); /* does this drive & transfer work with DMA ? */ adp->flags &= ~AD_F_DMA_USED; if ((adp->flags & AD_F_DMA_ENABLED) && !ata_dmasetup(adp->controller, adp->unit, (void *)bp->b_data, adp->bytecount, (bp->b_flags & B_READ))) { adp->flags |= AD_F_DMA_USED; command = (bp->b_flags&B_READ) ? ATA_C_READ_DMA : ATA_C_WRITE_DMA; adp->currentsize = adp->bytecount; } /* does this drive support multi sector transfers ? */ else if (adp->currentsize > DEV_BSIZE) command = (bp->b_flags&B_READ) ? ATA_C_READ_MULTI:ATA_C_WRITE_MULTI; else command = (bp->b_flags&B_READ) ? ATA_C_READ : ATA_C_WRITE; ata_command(adp->controller, adp->unit, command, cylinder, head, sector, count, 0, ATA_IMMEDIATE); } /* if this is a DMA transaction start it, return and wait for interrupt */ if (adp->flags & AD_F_DMA_USED) { ata_dmastart(adp->controller, adp->unit); #ifdef AD_DEBUG printf("ad_transfer: return waiting for DMA interrupt\n"); #endif return; } /* calculate this transfer length */ adp->currentsize = min(adp->bytecount, adp->transfersize); /* if this is a PIO read operation, return and wait for interrupt */ if (bp->b_flags & B_READ) { #ifdef AD_DEBUG printf("ad_transfer: return waiting for PIO read interrupt\n"); #endif return; } /* ready to write PIO data ? */ if (ata_wait(adp->controller, adp->unit, ATA_S_DRDY | ATA_S_DSC | ATA_S_DRQ) < 0) { printf("ad_transfer: timeout waiting for DRQ"); } /* output the data */ #if 0 outsw(adp->controller->ioaddr + ATA_DATA, - (void *)((int32_t)bp->b_data + adp->donecount), + (void *)((uintptr_t)bp->b_data + adp->donecount), adp->currentsize / sizeof(int16_t)); #else outsl(adp->controller->ioaddr + ATA_DATA, - (void *)((int32_t)bp->b_data + adp->donecount), + (void *)((uintptr_t)bp->b_data + adp->donecount), adp->currentsize / sizeof(int32_t)); #endif adp->bytecount -= adp->currentsize; #ifdef AD_DEBUG printf("ad_transfer: return wrote data\n"); #endif } int32_t ad_interrupt(struct buf *bp) { struct ad_softc *adp = bp->b_driver1; int32_t dma_stat = 0; /* finish DMA transfer */ if (adp->flags & AD_F_DMA_USED) { if (!(ata_dmastatus(adp->controller, adp->unit) & ATA_BMSTAT_INTERRUPT)){ printf("extra SMP interrupt\n"); return ATA_OP_CONTINUES; } dma_stat = ata_dmadone(adp->controller, adp->unit); } /* get drive status */ if (ata_wait(adp->controller, adp->unit, 0) < 0) printf("ad_interrupt: timeout waiting for status"); if (adp->controller->status & (ATA_S_ERROR | ATA_S_CORR) || (adp->flags & AD_F_DMA_USED && dma_stat != ATA_BMSTAT_INTERRUPT)) { oops: printf("ad%d: status=%02x error=%02x\n", adp->lun, adp->controller->status, adp->controller->error); if (adp->controller->status & ATA_S_ERROR) { printf("ad_interrupt: hard error\n"); bp->b_error = EIO; bp->b_flags |= B_ERROR; } if (adp->controller->status & ATA_S_CORR) printf("ad_interrupt: soft ECC\n"); } /* if this was a PIO read operation, get the data */ if (adp->active && !(adp->flags & AD_F_DMA_USED) && ((bp->b_flags & (B_READ | B_ERROR)) == B_READ)) { /* ready to receive data? */ if ((adp->controller->status & (ATA_S_DRDY | ATA_S_DSC | ATA_S_DRQ)) != (ATA_S_DRDY | ATA_S_DSC | ATA_S_DRQ)) printf("ad_interrupt: read interrupt arrived early"); if (ata_wait(adp->controller, adp->unit, ATA_S_DRDY | ATA_S_DSC | ATA_S_DRQ) != 0){ printf("ad_interrupt: read error detected late"); goto oops; } /* data ready, read in */ #if 0 insw(adp->controller->ioaddr + ATA_DATA, - (void *)((int32_t)bp->b_data + adp->donecount), + (void *)((uintptr_t)bp->b_data + adp->donecount), adp->currentsize / sizeof(int16_t)); #else insl(adp->controller->ioaddr + ATA_DATA, - (void *)((int32_t)bp->b_data + adp->donecount), + (void *)((uintptr_t)bp->b_data + adp->donecount), adp->currentsize / sizeof(int32_t)); #endif adp->bytecount -= adp->currentsize; #ifdef AD_DEBUG printf("ad_interrupt: read in data\n"); #endif } /* if this was a DMA operation finish up */ if (adp->active && (adp->flags & AD_F_DMA_USED) && !(bp->b_flags & B_ERROR)) adp->bytecount -= adp->currentsize; /* finish up this tranfer, check for more work on this buffer */ if (adp->controller->active == ATA_ACTIVE_ATA) { if ((bp->b_flags & B_ERROR) == 0) { adp->donecount += adp->currentsize; #ifdef AD_DEBUG printf("ad_interrupt: %s op OK\n", (bp->b_flags & B_READ)?"R":"W"); #endif if (adp->bytecount > 0) { ad_transfer(bp); return ATA_OP_CONTINUES; } } bufq_remove(&adp->controller->ata_queue, bp); bp->b_resid = bp->b_bcount - adp->donecount; biodone(bp); devstat_end_transaction(&adp->stats, bp->b_bcount - bp->b_resid, DEVSTAT_TAG_NONE, (bp->b_flags & B_READ) ? DEVSTAT_READ : DEVSTAT_WRITE); adp->donecount = 0; adp->active = 0; } ad_start(adp); #ifdef AD_DEBUG printf("ad_interrupt: completed\n"); #endif return ATA_OP_FINISHED; } static void ad_sleep(struct ad_softc *adp, int8_t *mesg) { int32_t s = splbio(); while (adp->controller->active != ATA_IDLE) tsleep((caddr_t)&adp->controller->active, PZERO - 1, mesg, 1); splx(s); } static int8_t ad_version(u_int16_t version) { int32_t bit; if (version == 0xffff) return '?'; for (bit = 15; bit >= 0; bit--) if (version & (1<ich_func = ad_attach; if (config_intrhook_establish(ad_attach_hook) != 0) { printf("ad: config_intrhook_establish failed\n"); free(ad_attach_hook, M_TEMP); } } SYSINIT(addev, SI_SUB_DRIVERS, SI_ORDER_SECOND, ad_drvinit, NULL) #endif /* NATA && NATADISK */ Index: head/sys/dev/ata/ata-dma.c =================================================================== --- head/sys/dev/ata/ata-dma.c (revision 45719) +++ head/sys/dev/ata/ata-dma.c (revision 45720) @@ -1,365 +1,371 @@ /*- * Copyright (c) 1998,1999 Søren Schmidt * 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, * without modification, immediately at the beginning of 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. 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. * - * $Id: ata-dma.c,v 1.3 1999/03/30 13:09:47 sos Exp $ + * $Id: ata-dma.c,v 1.4 1999/04/10 18:53:35 sos Exp $ */ #include "ata.h" #include "pci.h" #if NATA > 0 #include #include #include #include #include #include #include #include #include #include +#ifdef __alpha__ +#undef vtophys +#define vtophys(va) (pmap_kextract(((vm_offset_t) (va))) \ + + 1*1024*1024*1024) +#endif + /* misc defines */ #define MIN(a,b) ((a)>(b)?(b):(a)) #if NPCI > 0 int32_t ata_dmainit(struct ata_softc *scp, int32_t device, int32_t apiomode, int32_t wdmamode, int32_t udmamode) { int32_t type, devno, error; void *dmatab; if (!scp->bmaddr) return -1; #ifdef ATA_DEBUGDMA printf("ata%d: dmainit: ioaddr=0x%x altioaddr=0x%x, bmaddr=0x%x\n", scp->lun, scp->ioaddr, scp->altioaddr, scp->bmaddr); #endif if (!(dmatab = malloc(PAGE_SIZE, M_DEVBUF, M_NOWAIT))) return -1; - if (((int)dmatab>>PAGE_SHIFT)^(((int)dmatab+PAGE_SIZE-1)>>PAGE_SHIFT)) { + if (((uintptr_t)dmatab>>PAGE_SHIFT)^(((uintptr_t)dmatab+PAGE_SIZE-1)>>PAGE_SHIFT)) { printf("ata_dmainit: dmatab crosses page boundary, no DMA\n"); free(dmatab, M_DEVBUF); return -1; } scp->dmatab[device ? 1 : 0] = dmatab; type = pci_conf_read(scp->tag, PCI_ID_REG); switch(type) { case 0x71118086: /* Intel PIIX4 */ if (udmamode >= 2) { int32_t mask48, new48; printf("ata%d: %s: settting up UDMA2 mode on PIIX4 chip ", scp->lun, (device) ? "slave" : "master"); error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0, ATA_UDMA2, ATA_C_FEA_SETXFER, ATA_WAIT_INTR); if (error) { printf("failed\n"); break; } printf("OK\n"); devno = (scp->unit << 1) + (device ? 1 : 0); mask48 = (1 << devno) + (3 << (16 + (devno << 2))); new48 = (1 << devno) + (2 << (16 + (devno << 2))); pci_conf_write(scp->tag, 0x48, (pci_conf_read(scp->tag, 0x48) & ~mask48) | new48); return 0; } /* FALLTHROUGH */ case 0x70108086: /* Intel PIIX3 */ if (wdmamode >= 2 && apiomode >= 4) { int32_t mask40, new40, mask44, new44; /* if SITRE not set doit for both channels */ if (!((pci_conf_read(scp->tag, 0x40) >> (scp->unit<<8)) & 0x4000)) { new40 = pci_conf_read(scp->tag, 0x40); new44 = pci_conf_read(scp->tag, 0x44); if (!(new40 & 0x00004000)) { new44 &= ~0x0000000f; new44 |= ((new40&0x00003000)>>10)|((new40&0x00000300)>>8); } if (!(new40 & 0x40000000)) { new44 &= ~0x000000f0; new44 |= ((new40&0x30000000)>>22)|((new40&0x03000000)>>20); } new40 |= 0x40004000; pci_conf_write(scp->tag, 0x40, new40); pci_conf_write(scp->tag, 0x44, new44); } printf("ata%d: %s: settting up WDMA2 mode on PIIX3/4 chip ", scp->lun, (device) ? "slave" : "master"); error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0, ATA_WDMA2, ATA_C_FEA_SETXFER, ATA_WAIT_INTR); if (error) { printf("failed\n"); break; } printf("OK\n"); if (device == ATA_MASTER) { mask40 = 0x0000330f; new40 = 0x00002307; mask44 = 0; new44 = 0; } else { mask40 = 0x000000f0; new40 = 0x00000070; mask44 = 0x0000000f; new44 = 0x0000000b; } if (scp->unit) { mask40 <<= 16; new40 <<= 16; mask44 <<= 4; new44 <<= 4; } pci_conf_write(scp->tag, 0x40, (pci_conf_read(scp->tag, 0x40) & ~mask40) | new40); pci_conf_write(scp->tag, 0x44, (pci_conf_read(scp->tag, 0x44) & ~mask44) | new44); return 0; } break; case 0x12308086: /* Intel PIIX */ /* probably not worth the trouble */ break; case 0x4d33105a: /* Promise Ultra/33 / FastTrack controllers */ devno = (scp->unit << 1) + (device ? 1 : 0); if (udmamode >=2) { printf("ata%d: %s: settting up UDMA2 mode on Promise chip ", scp->lun, (device) ? "slave" : "master"); error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0, ATA_UDMA2, ATA_C_FEA_SETXFER, ATA_WAIT_INTR); if (error) { printf("failed\n"); break; } printf("OK\n"); pci_conf_write(scp->tag, 0x60 + (devno << 2), 0x004127f3); return 0; } else if (wdmamode >= 2 && apiomode >= 4) { printf("ata%d: %s: settting up WDMA2 mode on Promise chip ", scp->lun, (device) ? "slave" : "master"); error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0, ATA_WDMA2, ATA_C_FEA_SETXFER, ATA_WAIT_INTR); if (error) { printf("failed\n"); break; } printf("OK\n"); pci_conf_write(scp->tag, 0x60 + (devno << 2), 0x004367f3); return 0; } else { printf("ata%d: %s: settting up PIO mode on Promise chip OK\n", scp->lun, (device) ? "slave" : "master"); pci_conf_write(scp->tag, 0x60 + (devno << 2), 0x004fe924); } break; case 0x522910b9: /* AcerLabs Aladdin IV/V */ if (udmamode >=2) { int32_t word54 = pci_conf_read(scp->tag, 0x54); printf("ata%d: %s: settting up UDMA2 mode on Aladdin chip ", scp->lun, (device) ? "slave" : "master"); error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0, ATA_UDMA2, ATA_C_FEA_SETXFER, ATA_WAIT_INTR); if (error) { printf("failed\n"); break; } printf("OK\n"); word54 |= 0x5555; word54 |= (0x0000000A << (16 + (scp->unit << 3) + (device << 2))); pci_conf_write(scp->tag, 0x54, word54); return 0; } else if (wdmamode >= 2 && apiomode >= 4) { printf("ata%d: %s: settting up WDMA2 mode on Aladdin chip ", scp->lun, (device) ? "slave" : "master"); error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0, ATA_WDMA2, ATA_C_FEA_SETXFER, ATA_WAIT_INTR); if (error) { printf("failed\n"); break; } printf("OK\n"); return 0; } break; default: /* well, we have no support for this, but try anyways */ if ((wdmamode >= 2 && apiomode >= 4) || udmamode >= 2) { printf("ata%d: %s: settting up generic WDMA2 mode ", scp->lun, (device) ? "slave" : "master"); error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0, ATA_WDMA2, ATA_C_FEA_SETXFER, ATA_WAIT_INTR); if (error) { printf("failed\n"); break; } printf("OK\n"); return 0; } } free(dmatab, M_DEVBUF); return -1; } int32_t ata_dmasetup(struct ata_softc *scp, int32_t device, int8_t *data, int32_t count, int32_t flags) { struct ata_dmaentry *dmatab; u_int32_t dma_count, dma_base; int32_t i = 0; #ifdef ATA_DEBUGDMA printf("ata%d: dmasetup\n", scp->lun); #endif - if (((u_int32_t)data & 1) || (count & 1)) + if (((uintptr_t)data & 1) || (count & 1)) return -1; if (!count) { printf("ata%d: zero length DMA transfer attempt on %s\n", scp->lun, (device ? "slave" : "master")); return -1; } dmatab = scp->dmatab[device ? 1 : 0]; dma_base = vtophys(data); - dma_count = MIN(count, (PAGE_SIZE - ((u_int32_t)data & PAGE_MASK))); + dma_count = MIN(count, (PAGE_SIZE - ((uintptr_t)data & PAGE_MASK))); data += dma_count; count -= dma_count; while (count) { dmatab[i].base = dma_base; dmatab[i].count = (dma_count & 0xffff); i++; if (i >= ATA_DMA_ENTRIES) { printf("ata%d: too many segments in DMA table for %s\n", scp->lun, (device ? "slave" : "master")); return -1; } dma_base = vtophys(data); dma_count = MIN(count, PAGE_SIZE); data += MIN(count, PAGE_SIZE); count -= MIN(count, PAGE_SIZE); } #ifdef ATA_DEBUGDMA printf("ata_dmasetup: base=%08x count%08x\n", dma_base, dma_count); #endif dmatab[i].base = dma_base; dmatab[i].count = (dma_count & 0xffff) | ATA_DMA_EOT; outl(scp->bmaddr + ATA_BMDTP_PORT, vtophys(dmatab)); #ifdef ATA_DEBUGDMA printf("dmatab=%08x %08x\n", vtophys(dmatab), inl(scp->bmaddr+ATA_BMDTP_PORT)); #endif outb(scp->bmaddr + ATA_BMCMD_PORT, flags ? ATA_BMCMD_WRITE_READ:0); outb(scp->bmaddr + ATA_BMSTAT_PORT, (inb(scp->bmaddr + ATA_BMSTAT_PORT) | (ATA_BMSTAT_INTERRUPT | ATA_BMSTAT_ERROR))); return 0; } void ata_dmastart(struct ata_softc *scp, int32_t device) { #ifdef ATA_DEBUGDMA printf("ata%d: dmastart\n", scp->lun); #endif outb(scp->bmaddr + ATA_BMCMD_PORT, inb(scp->bmaddr + ATA_BMCMD_PORT) | ATA_BMCMD_START_STOP); } int32_t ata_dmadone(struct ata_softc *scp, int32_t device) { #ifdef ATA_DEBUGDMA printf("ata%d: dmadone\n", scp->lun); #endif outb(scp->bmaddr + ATA_BMCMD_PORT, inb(scp->bmaddr + ATA_BMCMD_PORT) & ~ATA_BMCMD_START_STOP); return inb(scp->bmaddr + ATA_BMSTAT_PORT) & ATA_BMSTAT_MASK; } int32_t ata_dmastatus(struct ata_softc *scp, int32_t device) { #ifdef ATA_DEBUGDMA printf("ata%d: dmastatus\n", scp->lun); #endif return inb(scp->bmaddr + ATA_BMSTAT_PORT) & ATA_BMSTAT_MASK; } #else /* NPCI > 0 */ int32_t ata_dmainit(struct ata_softc *scp, int32_t device, int32_t piomode, int32_t wdmamode, int32_t udmamode) { return -1; } int32_t ata_dmasetup(struct ata_softc *scp, int32_t device, int8_t *data, int32_t count, int32_t flags) { return -1; } void ata_dmastart(struct ata_softc *scp, int32_t device) { } int32_t ata_dmadone(struct ata_softc *scp, int32_t device) { return -1; } int32_t ata_dmastatus(struct ata_softc *scp, int32_t device) { return -1; } #endif /* NPCI > 0 */ #endif /* NATA > 0 */ Index: head/sys/dev/ata/atapi-all.c =================================================================== --- head/sys/dev/ata/atapi-all.c (revision 45719) +++ head/sys/dev/ata/atapi-all.c (revision 45720) @@ -1,513 +1,513 @@ /*- * Copyright (c) 1998,1999 Søren Schmidt * 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, * without modification, immediately at the beginning of 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. 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. * - * $Id: atapi-all.c,v 1.5 1999/03/28 18:57:19 sos Exp $ + * $Id: atapi-all.c,v 1.6 1999/04/10 18:53:35 sos Exp $ */ #include "ata.h" #include "atapicd.h" #include "atapist.h" #include "atapifd.h" #include "opt_devfs.h" #if NATA > 0 #include #include #include #include #include #include #include #include #include #include #include /* prototypes */ static void atapi_attach(void *); static int32_t atapi_getparam(struct atapi_softc *); static int8_t *atapi_type(int32_t); #ifdef ATAPI_DEBUG static int8_t *atapi_cmd2str(u_int8_t); #endif static int32_t atapi_wait(struct atapi_softc *, u_int8_t); static void atapi_init(void); /* extern references */ int32_t acdattach(struct atapi_softc *); int32_t afdattach(struct atapi_softc *); int32_t astattach(struct atapi_softc *); static struct intr_config_hook *atapi_attach_hook; static void atapi_attach(void *notused) { struct atapi_softc *atp; int32_t ctlr, dev; int8_t model_buf[40+1]; int8_t revision_buf[8+1]; /* now, run through atadevices and look for ATAPI devices */ for (ctlr=0; ctlrdevices & (dev ? ATA_ATAPI_SLAVE : ATA_ATAPI_MASTER)) { if (!(atp = malloc(sizeof(struct atapi_softc), M_DEVBUF, M_NOWAIT))) { printf("atapi: failed to allocate driver storage\n"); continue; } bzero(atp, sizeof(struct atapi_softc)); atp->controller = atadevices[ctlr]; atp->unit = (dev == 0) ? ATA_MASTER : ATA_SLAVE; if (atapi_getparam(atp)) { free(atp, M_DEVBUF); continue; } switch (atp->atapi_parm->device_type) { #if NATAPICD > 0 case ATAPI_TYPE_CDROM: if (acdattach(atp)) goto notfound; break; #endif #if NATAPIFD > 0 case ATAPI_TYPE_DIRECT: if (afdattach(atp)) goto notfound; break; #endif #if NATAPIST > 0 case ATAPI_TYPE_TAPE: if (astattach(atp)) goto notfound; break; #endif notfound: default: bpack(atp->atapi_parm->model, model_buf, sizeof(model_buf)); bpack(atp->atapi_parm->revision, revision_buf, sizeof(revision_buf)); printf("atapi: <%s/%s> %s device at ata%d as %s " "- NO DRIVER!\n", model_buf, revision_buf, atapi_type(atp->atapi_parm->device_type), ctlr, (dev) ? "slave" : "master "); free(atp, M_DEVBUF); } } } } config_intrhook_disestablish(atapi_attach_hook); } static int32_t atapi_getparam(struct atapi_softc *atp) { struct atapi_params *atapi_parm; int8_t buffer[DEV_BSIZE]; /* select drive */ outb(atp->controller->ioaddr + ATA_DRIVE, ATA_D_IBM | atp->unit); DELAY(1); ata_command(atp->controller, atp->unit, ATA_C_ATAPI_IDENTIFY, 0, 0, 0, 0, 0, ATA_WAIT_INTR); if (atapi_wait(atp, ATA_S_DRQ)) return -1; insw(atp->controller->ioaddr + ATA_DATA, buffer, sizeof(buffer)/sizeof(int16_t)); if (atapi_wait(atp, 0)) return -1; if (!(atapi_parm = malloc(sizeof(struct atapi_params), M_DEVBUF, M_NOWAIT))) return -1; bcopy(buffer, atapi_parm, sizeof(struct atapi_params)); if (!((atapi_parm->model[0] == 'N' && atapi_parm->model[1] == 'E') || (atapi_parm->model[0] == 'F' && atapi_parm->model[1] == 'X'))) bswap(atapi_parm->model, sizeof(atapi_parm->model)); btrim(atapi_parm->model, sizeof(atapi_parm->model)); bswap(atapi_parm->revision, sizeof(atapi_parm->revision)); btrim(atapi_parm->revision, sizeof(atapi_parm->revision)); atp->atapi_parm = atapi_parm; return 0; } int32_t atapi_queue_cmd(struct atapi_softc *atp, int8_t *ccb, void *data, int32_t count, int32_t flags, /*timeout,*/ atapi_callback_t callback, void *driver, struct buf *bp) { struct atapi_request *request; int32_t error = 0; int32_t s; if (!(request = malloc(sizeof(struct atapi_request), M_DEVBUF, M_NOWAIT))) return -1; bzero(request, sizeof(struct atapi_request)); request->device = atp; request->data = data; request->bytecount = count; request->flags = flags; if (callback) { request->callback = callback; request->bp = bp; request->driver = driver; } request->ccbsize = (atp->atapi_parm->cmdsize) ? 16 : 12; bcopy(ccb, request->ccb, request->ccbsize); s = splbio(); /* link onto controller queue */ TAILQ_INSERT_TAIL(&atp->controller->atapi_queue, request, chain); /* try to start controller */ if (atp->controller->active == ATA_IDLE) ata_start(atp->controller); splx(s); #ifdef ATAPI_DEBUG printf("atapi: queued %s cmd\n", atapi_cmd2str(ccb[0])); #endif if (!callback) { /* wait for command to complete */ if (tsleep((caddr_t)request, PRIBIO, "atprq", 0/*timeout*/)) error = 0xf0; else error = request->result; #ifdef ATAPI_DEBUG printf("atapi: phew, got back from tsleep\n"); #endif free(request, M_DEVBUF); } return error; } void atapi_transfer(struct atapi_request *request) { struct atapi_softc *atp; int32_t timeout; int8_t reason; /* not needed really */ /* get device params */ atp = request->device; #ifdef ATAPI_DEBUG printf("atapi: trying to start %s cmd\n", atapi_cmd2str(request->ccb[0])); #endif /* start ATAPI operation */ ata_command(atp->controller, atp->unit, ATA_C_PACKET_CMD, request->bytecount, 0, 0, 0, 0, ATA_IMMEDIATE); /* command interrupt device ? just return */ if (atp->atapi_parm->drqtype == ATAPI_DRQT_INTR) return; /* ready to write ATAPI command */ timeout = 5000; /* might be less for fast devices */ while (timeout--) { reason = inb(atp->controller->ioaddr + ATA_IREASON); atp->controller->status = inb(atp->controller->ioaddr + ATA_STATUS); if (((reason & (ATA_I_CMD | ATA_I_IN)) | (atp->controller->status&(ATA_S_DRQ|ATA_S_BSY))) == ATAPI_P_CMDOUT) break; DELAY(20); } if (timeout <= 0) { atp->controller->error = inb(atp->controller->ioaddr + ATA_ERROR); printf("atapi_transfer: bad command phase\n"); /* now what ?? done & again ?? SOS */ } /* this seems to be needed for some (slow) devices */ DELAY(10); /* send actual command */ outsw(atp->controller->ioaddr + ATA_DATA, request->ccb, request->ccbsize / sizeof(int16_t)); } int32_t atapi_interrupt(struct atapi_request *request) { struct atapi_softc *atp; int32_t length, reason, resid; #ifdef ATAPI_DEBUG printf("atapi_interrupt: enter\n"); #endif /* get device params */ atp = request->device; /* get drive status */ if (atapi_wait(atp, 0) < 0) { printf("atapi_interrupt: timeout waiting for status"); /* maybe check sense code ?? SOS */ return ATA_OP_FINISHED; } atp->controller->status = inb(atp->controller->ioaddr + ATA_STATUS); atp->controller->error = inb(atp->controller->ioaddr + ATA_ERROR); length = inb(atp->controller->ioaddr + ATA_CYL_LSB); length |= inb(atp->controller->ioaddr + ATA_CYL_MSB) << 8; reason = (inb(atp->controller->ioaddr + ATA_IREASON)&(ATA_I_CMD|ATA_I_IN)) | (atp->controller->status & ATA_S_DRQ); #ifdef ATAPI_DEBUG printf("atapi_interrupt: length=%d reason=0x%02x\n", length, reason); #endif switch (reason) { case ATAPI_P_CMDOUT: /* send ATAPI command */ if (!(atp->controller->status & ATA_S_DRQ)) printf("atapi_interrupt: command interrupt, but no DRQ\n"); else outsw(atp->controller->ioaddr + ATA_DATA, request->ccb, request->ccbsize / sizeof(int16_t)); return ATA_OP_CONTINUES; case ATAPI_P_WRITE: if (request->flags & A_READ) { printf("atapi_interrupt: trying to write on read buffer\n"); break; } if (request->bytecount < length) { printf("atapi_interrupt: write data underrun %d/%d\n", length, request->bytecount); outsw(atp->controller->ioaddr + ATA_DATA, - (void *)((int32_t)request->data), length / sizeof(int16_t)); + (void *)((uintptr_t)request->data), length / sizeof(int16_t)); for (resid=request->bytecount; residcontroller->ioaddr + ATA_DATA, 0); } else { outsw(atp->controller->ioaddr + ATA_DATA, - (void *)((int32_t)request->data), length / sizeof(int16_t)); + (void *)((uintptr_t)request->data), length / sizeof(int16_t)); } request->bytecount -= length; request->data += length; return ATA_OP_CONTINUES; case ATAPI_P_READ: if (!(request->flags & A_READ)) { printf("atapi_interrupt: trying to read on write buffer\n"); break; } if (request->bytecount < length) { printf("atapi_interrupt: read data overrun %d/%d\n", length, request->bytecount); insw(atp->controller->ioaddr + ATA_DATA, - (void *)((int32_t)request->data), length / sizeof(int16_t)); + (void *)((uintptr_t)request->data), length / sizeof(int16_t)); for (resid=request->bytecount; residcontroller->ioaddr + ATA_DATA); } else { insw(atp->controller->ioaddr + ATA_DATA, - (void *)((int32_t)request->data), length / sizeof(int16_t)); + (void *)((uintptr_t)request->data), length / sizeof(int16_t)); } request->bytecount -= length; request->data += length; return ATA_OP_CONTINUES; case ATAPI_P_ABORT: case ATAPI_P_DONE: request->result = 0; if (atp->controller->status & (ATA_S_ERROR | ATA_S_DWF)) { /* check sense !! SOS */ request->result = atp->controller->error; } #ifdef ATAPI_DEBUG if (request->bytecount > 0) { printf("atapi_interrupt: %s size problem, %d bytes residue\n", (request->flags & A_READ) ? "read" : "write", request->bytecount); } #endif break; default: printf("atapi_interrupt: unknown transfer phase %d\n", reason); } TAILQ_REMOVE(&atp->controller->atapi_queue, request, chain); #ifdef ATAPI_DEBUG printf("atapi_interrupt: error=0x%02x\n", request->result); #endif if (request->callback) { (request->callback)(request); free(request, M_DEVBUF); } else wakeup((caddr_t)request); return ATA_OP_FINISHED; } void atapi_error(struct atapi_softc *atp, int32_t error) { printf("atapi: error = 0x%02x\n", error); } void atapi_dump(int8_t *label, void *data, int32_t len) { u_int8_t *p = data; printf ("atapi: %s %x", label, *p++); while (--len > 0) printf ("-%02x", *p++); printf ("\n"); } static int8_t * atapi_type(int32_t type) { switch (type) { case ATAPI_TYPE_CDROM: return "CDROM"; case ATAPI_TYPE_DIRECT: return "floppy"; case ATAPI_TYPE_TAPE: return "tape"; case ATAPI_TYPE_OPTICAL: return "optical"; default: return "Unknown"; } } #ifdef ATAPI_DEBUG static int8_t * atapi_cmd2str(u_int8_t cmd) { switch (cmd) { case 0x00: return ("TEST_UNIT_READY"); case 0x01: return ("REZERO_UNIT"); case 0x03: return ("REQUEST_SENSE"); case 0x04: return ("FORMAT_UNIT"); case 0x1a: return ("TAPE_MODE_SENSE"); case 0x1b: return ("START_STOP"); case 0x1e: return ("PREVENT_ALLOW"); case 0x25: return ("READ_CAPACITY"); case 0x28: return ("READ_BIG"); case 0x2a: return ("WRITE_BIG"); case 0x35: return ("SYNCHRONIZE_CACHE"); case 0x42: return ("READ_SUBCHANNEL"); case 0x43: return ("READ_TOC"); case 0x51: return ("READ_DISC_INFO"); case 0x52: return ("READ_TRACK_INFO"); case 0x53: return ("RESERVE_TRACK"); case 0x54: return ("SEND_OPC_INFO"); case 0x55: return ("MODE_SELECT"); case 0x58: return ("REPAIR_TRACK"); case 0x59: return ("READ_MASTER_CUE"); case 0x5a: return ("MODE_SENSE"); case 0x5b: return ("CLOSE_TRACK/SESSION"); case 0x5c: return ("READ_BUFFER_CAPACITY"); case 0x5d: return ("SEND_CUE_SHEET"); case 0x47: return ("PLAY_MSF"); case 0x4b: return ("PAUSE"); case 0x48: return ("PLAY_TRACK"); case 0xa1: return ("BLANK_CMD"); case 0xa5: return ("PLAY_BIG"); case 0xb4: return ("PLAY_CD"); case 0xbd: return ("MECH_STATUS"); case 0xbe: return ("READ_CD"); default: { static int8_t buffer[16]; sprintf(buffer, "Unknown 0x%02x", cmd); return buffer; } } } #endif static int32_t atapi_wait(struct atapi_softc *atp, u_int8_t mask) { u_int8_t status; u_int32_t timeout = 0; while (timeout++ <= 500000) { /* timeout 5 secs */ status = inb(atp->controller->ioaddr + ATA_STATUS); /* if drive fails status, reselect the drive just to be sure */ if (status == 0xff) { outb(atp->controller->ioaddr + ATA_DRIVE, ATA_D_IBM | atp->unit); DELAY(1); status = inb(atp->controller->ioaddr + ATA_STATUS); } if (!(status & ATA_S_BSY) && (status & ATA_S_DRDY)) break; DELAY (10); } if (timeout <= 0) return -1; if (!mask) return (status & ATA_S_ERROR); /* Wait 50 msec for bits wanted. */ for (timeout=5000; timeout>0; --timeout) { status = inb(atp->controller->ioaddr + ATA_STATUS); if ((status & mask) == mask) return (status & ATA_S_ERROR); DELAY (10); } return -1; } static void atapi_init(void) { /* register callback for when interrupts are enabled */ if (!(atapi_attach_hook = (struct intr_config_hook *)malloc(sizeof(struct intr_config_hook), M_TEMP, M_NOWAIT))) { printf("atapi: malloc attach_hook failed\n"); return; } bzero(atapi_attach_hook, sizeof(struct intr_config_hook)); atapi_attach_hook->ich_func = atapi_attach; if (config_intrhook_establish(atapi_attach_hook) != 0) { printf("atapi: config_intrhook_establish failed\n"); free(atapi_attach_hook, M_TEMP); } } SYSINIT(atconf, SI_SUB_CONFIGURE, SI_ORDER_SECOND, atapi_init, NULL) #endif /* NATA */ Index: head/sys/dev/atkbdc/atkbd.c =================================================================== --- head/sys/dev/atkbdc/atkbd.c (revision 45719) +++ head/sys/dev/atkbdc/atkbd.c (revision 45720) @@ -1,1422 +1,1421 @@ /*- * Copyright (c) 1999 Kazutaka YOKOTA * 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 as * the first lines of this file unmodified. * 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 AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS BE LIABLE 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. * - * $Id: atkbd.c,v 1.4 1999/01/28 10:55:55 yokota Exp $ + * $Id: atkbd.c,v 1.5 1999/03/10 10:36:52 yokota Exp $ */ #include "atkbd.h" #include "opt_kbd.h" #include "opt_atkbd.h" #include "opt_devfs.h" #if NATKBD > 0 #include #include #include #include #include #include #include #include #include #include #include -#ifndef __i386__ +#if 1 #include #include extern devclass_t atkbd_devclass; #define ATKBD_SOFTC(unit) \ ((atkbd_softc_t *)devclass_get_softc(atkbd_devclass, unit)) #else /* __i386__ */ #include #include extern struct isa_driver atkbddriver; /* XXX: a kludge; see below */ static atkbd_softc_t *atkbd_softc[NATKBD]; #define ATKBD_SOFTC(unit) \ (((unit) >= NATKBD) ? NULL : atkbd_softc[(unit)]) #endif /* __i386__ */ static timeout_t atkbd_timeout; #ifdef KBD_INSTALL_CDEV static d_open_t atkbdopen; static d_close_t atkbdclose; static d_read_t atkbdread; static d_ioctl_t atkbdioctl; static d_poll_t atkbdpoll; static struct cdevsw atkbd_cdevsw = { atkbdopen, atkbdclose, atkbdread, nowrite, atkbdioctl, nostop, nullreset, nodevtotty, atkbdpoll, nommap, NULL, ATKBD_DRIVER_NAME, NULL, -1, }; #endif /* KBD_INSTALL_CDEV */ +#if 0 #ifdef __i386__ atkbd_softc_t *atkbd_get_softc(int unit) { atkbd_softc_t *sc; if (unit >= sizeof(atkbd_softc)/sizeof(atkbd_softc[0])) return NULL; sc = atkbd_softc[unit]; if (sc == NULL) { sc = atkbd_softc[unit] = malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT); if (sc == NULL) return NULL; bzero(sc, sizeof(*sc)); } return sc; } #endif /* __i386__ */ +#endif int atkbd_probe_unit(int unit, int port, int irq, int flags) { keyboard_switch_t *sw; int args[2]; int error; sw = kbd_get_switch(ATKBD_DRIVER_NAME); if (sw == NULL) return ENXIO; args[0] = port; args[1] = irq; error = (*sw->probe)(unit, args, flags); if (error) return error; return 0; } int atkbd_attach_unit(int unit, atkbd_softc_t *sc, int port, int irq, int flags) { keyboard_switch_t *sw; int args[2]; int error; if (sc->flags & ATKBD_ATTACHED) return 0; sw = kbd_get_switch(ATKBD_DRIVER_NAME); if (sw == NULL) return ENXIO; /* reset, initialize and enable the device */ args[0] = port; args[1] = irq; sc->kbd = NULL; error = (*sw->probe)(unit, args, flags); if (error) return error; error = (*sw->init)(unit, &sc->kbd, args, flags); if (error) return error; (*sw->enable)(sc->kbd); #ifdef KBD_INSTALL_CDEV /* attach a virtual keyboard cdev */ error = kbd_attach(makedev(0, ATKBD_MKMINOR(unit)), sc->kbd, &atkbd_cdevsw); if (error) return error; #endif /* * This is a kludge to compensate for lost keyboard interrupts. * A similar code used to be in syscons. See below. XXX */ atkbd_timeout(sc->kbd); if (bootverbose) (*sw->diag)(sc->kbd, bootverbose); sc->flags |= ATKBD_ATTACHED; return 0; } static void atkbd_timeout(void *arg) { keyboard_t *kbd; int s; /* The following comments are extracted from syscons.c (1.287) */ /* * With release 2.1 of the Xaccel server, the keyboard is left * hanging pretty often. Apparently an interrupt from the * keyboard is lost, and I don't know why (yet). * This ugly hack calls scintr if input is ready for the keyboard * and conveniently hides the problem. XXX */ /* * Try removing anything stuck in the keyboard controller; whether * it's a keyboard scan code or mouse data. `scintr()' doesn't * read the mouse data directly, but `kbdio' routines will, as a * side effect. */ s = spltty(); kbd = (keyboard_t *)arg; if ((*kbdsw[kbd->kb_index]->lock)(kbd, TRUE)) { /* * We have seen the lock flag is not set. Let's reset * the flag early, otherwise the LED update routine fails * which may want the lock during the interrupt routine. */ (*kbdsw[kbd->kb_index]->lock)(kbd, FALSE); if ((*kbdsw[kbd->kb_index]->check_char)(kbd)) (*kbdsw[kbd->kb_index]->intr)(kbd, NULL); } splx(s); timeout(atkbd_timeout, arg, hz/10); } /* cdev driver functions */ #ifdef KBD_INSTALL_CDEV static int atkbdopen(dev_t dev, int flag, int mode, struct proc *p) { atkbd_softc_t *sc; sc = ATKBD_SOFTC(ATKBD_UNIT(dev)); if (sc == NULL) return ENXIO; if (mode & (FWRITE | O_CREAT | O_APPEND | O_TRUNC)) return ENODEV; /* FIXME: set the initial input mode (K_XLATE?) and lock state? */ return genkbdopen(&sc->gensc, sc->kbd, flag, mode, p); } static int atkbdclose(dev_t dev, int flag, int mode, struct proc *p) { atkbd_softc_t *sc; sc = ATKBD_SOFTC(ATKBD_UNIT(dev)); return genkbdclose(&sc->gensc, sc->kbd, flag, mode, p); } static int atkbdread(dev_t dev, struct uio *uio, int flag) { atkbd_softc_t *sc; sc = ATKBD_SOFTC(ATKBD_UNIT(dev)); return genkbdread(&sc->gensc, sc->kbd, uio, flag); } static int atkbdioctl(dev_t dev, u_long cmd, caddr_t arg, int flag, struct proc *p) { atkbd_softc_t *sc; sc = ATKBD_SOFTC(ATKBD_UNIT(dev)); return genkbdioctl(&sc->gensc, sc->kbd, cmd, arg, flag, p); } static int atkbdpoll(dev_t dev, int event, struct proc *p) { atkbd_softc_t *sc; sc = ATKBD_SOFTC(ATKBD_UNIT(dev)); return genkbdpoll(&sc->gensc, sc->kbd, event, p); } #endif /* KBD_INSTALL_CDEV */ /* LOW-LEVEL */ #include #include #include #define ATKBD_DEFAULT 0 typedef struct atkbd_state { KBDC kbdc; /* keyboard controller */ /* XXX: don't move this field; pcvt * expects `kbdc' to be the first * field in this structure. */ int ks_mode; /* input mode (K_XLATE,K_RAW,K_CODE) */ int ks_flags; /* flags */ #define COMPOSE (1 << 0) int ks_polling; int ks_state; /* shift/lock key state */ int ks_accents; /* accent key index (> 0) */ u_int ks_composed_char; /* composed char code (> 0) */ u_char ks_prefix; /* AT scan code prefix */ } atkbd_state_t; /* keyboard driver declaration */ static int atkbd_configure(int flags); static kbd_probe_t atkbd_probe; static kbd_init_t atkbd_init; static kbd_term_t atkbd_term; static kbd_intr_t atkbd_intr; static kbd_test_if_t atkbd_test_if; static kbd_enable_t atkbd_enable; static kbd_disable_t atkbd_disable; static kbd_read_t atkbd_read; static kbd_check_t atkbd_check; static kbd_read_char_t atkbd_read_char; static kbd_check_char_t atkbd_check_char; static kbd_ioctl_t atkbd_ioctl; static kbd_lock_t atkbd_lock; static kbd_clear_state_t atkbd_clear_state; static kbd_get_state_t atkbd_get_state; static kbd_set_state_t atkbd_set_state; static kbd_poll_mode_t atkbd_poll; keyboard_switch_t atkbdsw = { atkbd_probe, atkbd_init, atkbd_term, atkbd_intr, atkbd_test_if, atkbd_enable, atkbd_disable, atkbd_read, atkbd_check, atkbd_read_char, atkbd_check_char, atkbd_ioctl, atkbd_lock, atkbd_clear_state, atkbd_get_state, atkbd_set_state, genkbd_get_fkeystr, atkbd_poll, genkbd_diag, }; KEYBOARD_DRIVER(atkbd, atkbdsw, atkbd_configure); /* local functions */ static int setup_kbd_port(KBDC kbdc, int port, int intr); static int get_kbd_echo(KBDC kbdc); static int probe_keyboard(KBDC kbdc, int flags); static int init_keyboard(KBDC kbdc, int *type, int flags); static int write_kbd(KBDC kbdc, int command, int data); static int get_kbd_id(KBDC kbdc); static int typematic(int delay, int rate); /* local variables */ /* the initial key map, accent map and fkey strings */ #ifdef ATKBD_DFLT_KEYMAP #define KBD_DFLT_KEYMAP #include "atkbdmap.h" #endif #include /* structures for the default keyboard */ static keyboard_t default_kbd; static atkbd_state_t default_kbd_state; static keymap_t default_keymap; static accentmap_t default_accentmap; static fkeytab_t default_fkeytab[NUM_FKEYS]; /* * The back door to the keyboard driver! * This function is called by the console driver, via the kbdio module, * to tickle keyboard drivers when the low-level console is being initialized. * Almost nothing in the kernel has been initialied yet. Try to probe * keyboards if possible. * NOTE: because of the way the low-level conole is initialized, this routine * may be called more than once!! */ static int atkbd_configure(int flags) { keyboard_t *kbd; int arg[2]; -#ifdef __i386__ - struct isa_device *dev; int i; /* XXX: a kludge to obtain the device configuration flags */ - dev = find_isadev(isa_devtab_tty, &atkbddriver, 0); - if (dev != NULL) { - flags |= dev->id_flags; + if (resource_int_value("atkbd", 0, "flags", &i) == 0) { + flags |= i; /* if the driver is disabled, unregister the keyboard if any */ - if (!dev->id_enabled) { + if (resource_int_value("atkbd", 0, "disabled", &i) == 0 + && i != 0) { i = kbd_find_keyboard(ATKBD_DRIVER_NAME, ATKBD_DEFAULT); if (i >= 0) { kbd = kbd_get_keyboard(i); kbd_unregister(kbd); kbd->kb_flags &= ~KB_REGISTERED; return 0; } } } -#endif - + /* probe the keyboard controller */ atkbdc_configure(); /* probe the default keyboard */ arg[0] = -1; arg[1] = -1; kbd = NULL; if (atkbd_probe(ATKBD_DEFAULT, arg, flags)) return 0; if (atkbd_init(ATKBD_DEFAULT, &kbd, arg, flags)) return 0; /* return the number of found keyboards */ return 1; } /* low-level functions */ /* detect a keyboard */ static int atkbd_probe(int unit, void *arg, int flags) { KBDC kbdc; int *data = (int *)arg; /* XXX */ if (unit == ATKBD_DEFAULT) { if (KBD_IS_PROBED(&default_kbd)) return 0; } kbdc = kbdc_open(data[0]); if (kbdc == NULL) return ENXIO; if (probe_keyboard(kbdc, flags)) { if (flags & KB_CONF_FAIL_IF_NO_KBD) return ENXIO; } return 0; } /* reset and initialize the device */ static int atkbd_init(int unit, keyboard_t **kbdp, void *arg, int flags) { keyboard_t *kbd; atkbd_state_t *state; keymap_t *keymap; accentmap_t *accmap; fkeytab_t *fkeymap; int fkeymap_size; int *data = (int *)arg; /* XXX */ if (unit == ATKBD_DEFAULT) { *kbdp = kbd = &default_kbd; if (KBD_IS_INITIALIZED(kbd) && KBD_IS_CONFIGURED(kbd)) return 0; state = &default_kbd_state; keymap = &default_keymap; accmap = &default_accentmap; fkeymap = default_fkeytab; fkeymap_size = sizeof(default_fkeytab)/sizeof(default_fkeytab[0]); } else if (*kbdp == NULL) { *kbdp = kbd = malloc(sizeof(*kbd), M_DEVBUF, M_NOWAIT); if (kbd == NULL) return ENOMEM; bzero(kbd, sizeof(*kbd)); state = malloc(sizeof(*state), M_DEVBUF, M_NOWAIT); keymap = malloc(sizeof(key_map), M_DEVBUF, M_NOWAIT); accmap = malloc(sizeof(accent_map), M_DEVBUF, M_NOWAIT); fkeymap = malloc(sizeof(fkey_tab), M_DEVBUF, M_NOWAIT); fkeymap_size = sizeof(fkey_tab)/sizeof(fkey_tab[0]); if ((state == NULL) || (keymap == NULL) || (accmap == NULL) || (fkeymap == NULL)) { if (state != NULL) free(state, M_DEVBUF); if (keymap != NULL) free(keymap, M_DEVBUF); if (accmap != NULL) free(accmap, M_DEVBUF); if (fkeymap != NULL) free(fkeymap, M_DEVBUF); free(kbd, M_DEVBUF); return ENOMEM; } bzero(state, sizeof(*state)); } else if (KBD_IS_INITIALIZED(*kbdp) && KBD_IS_CONFIGURED(*kbdp)) { return 0; } else { kbd = *kbdp; state = (atkbd_state_t *)kbd->kb_data; bzero(state, sizeof(*state)); keymap = kbd->kb_keymap; accmap = kbd->kb_accentmap; fkeymap = kbd->kb_fkeytab; fkeymap_size = kbd->kb_fkeytab_size; } if (!KBD_IS_PROBED(kbd)) { state->kbdc = kbdc_open(data[0]); if (state->kbdc == NULL) return ENXIO; kbd_init_struct(kbd, ATKBD_DRIVER_NAME, KB_OTHER, unit, flags, data[0], IO_KBDSIZE); bcopy(&key_map, keymap, sizeof(key_map)); bcopy(&accent_map, accmap, sizeof(accent_map)); bcopy(fkey_tab, fkeymap, imin(fkeymap_size*sizeof(fkeymap[0]), sizeof(fkey_tab))); kbd_set_maps(kbd, keymap, accmap, fkeymap, fkeymap_size); kbd->kb_data = (void *)state; if (probe_keyboard(state->kbdc, flags)) { /* shouldn't happen */ if (flags & KB_CONF_FAIL_IF_NO_KBD) return ENXIO; } else { KBD_FOUND_DEVICE(kbd); } atkbd_clear_state(kbd); state->ks_mode = K_XLATE; /* * FIXME: set the initial value for lock keys in ks_state * according to the BIOS data? */ KBD_PROBE_DONE(kbd); } if (!KBD_IS_INITIALIZED(kbd) && !(flags & KB_CONF_PROBE_ONLY)) { if (KBD_HAS_DEVICE(kbd) && init_keyboard(state->kbdc, &kbd->kb_type, kbd->kb_config) && (kbd->kb_config & KB_CONF_FAIL_IF_NO_KBD)) return ENXIO; atkbd_ioctl(kbd, KDSETLED, (caddr_t)&state->ks_state); KBD_INIT_DONE(kbd); } if (!KBD_IS_CONFIGURED(kbd)) { if (kbd_register(kbd) < 0) return ENXIO; KBD_CONFIG_DONE(kbd); } return 0; } /* finish using this keyboard */ static int atkbd_term(keyboard_t *kbd) { kbd_unregister(kbd); return 0; } /* keyboard interrupt routine */ static int atkbd_intr(keyboard_t *kbd, void *arg) { atkbd_state_t *state; int c; if (KBD_IS_ACTIVE(kbd) && KBD_IS_BUSY(kbd)) { /* let the callback function to process the input */ (*kbd->kb_callback.kc_func)(kbd, KBDIO_KEYINPUT, kbd->kb_callback.kc_arg); } else { /* read and discard the input; no one is waiting for input */ do { c = atkbd_read_char(kbd, FALSE); } while (c != NOKEY); if (!KBD_HAS_DEVICE(kbd)) { /* * The keyboard was not detected before; * it must have been reconnected! */ state = (atkbd_state_t *)kbd->kb_data; init_keyboard(state->kbdc, &kbd->kb_type, kbd->kb_config); atkbd_ioctl(kbd, KDSETLED, (caddr_t)&state->ks_state); KBD_FOUND_DEVICE(kbd); } } return 0; } /* test the interface to the device */ static int atkbd_test_if(keyboard_t *kbd) { int error; int s; error = 0; empty_both_buffers(((atkbd_state_t *)kbd->kb_data)->kbdc, 10); s = spltty(); if (!test_controller(((atkbd_state_t *)kbd->kb_data)->kbdc)) error = EIO; else if (test_kbd_port(((atkbd_state_t *)kbd->kb_data)->kbdc) != 0) error = EIO; splx(s); return error; } /* * Enable the access to the device; until this function is called, * the client cannot read from the keyboard. */ static int atkbd_enable(keyboard_t *kbd) { int s; s = spltty(); KBD_ACTIVATE(kbd); splx(s); return 0; } /* disallow the access to the device */ static int atkbd_disable(keyboard_t *kbd) { int s; s = spltty(); KBD_DEACTIVATE(kbd); splx(s); return 0; } /* read one byte from the keyboard if it's allowed */ static int atkbd_read(keyboard_t *kbd, int wait) { int c; if (wait) c = read_kbd_data(((atkbd_state_t *)kbd->kb_data)->kbdc); else c = read_kbd_data_no_wait(((atkbd_state_t *)kbd->kb_data)->kbdc); return (KBD_IS_ACTIVE(kbd) ? c : -1); } /* check if data is waiting */ static int atkbd_check(keyboard_t *kbd) { if (!KBD_IS_ACTIVE(kbd)) return FALSE; return kbdc_data_ready(((atkbd_state_t *)kbd->kb_data)->kbdc); } /* read char from the keyboard */ static u_int atkbd_read_char(keyboard_t *kbd, int wait) { atkbd_state_t *state; u_int action; int scancode; int keycode; state = (atkbd_state_t *)kbd->kb_data; next_code: /* do we have a composed char to return? */ if (!(state->ks_flags & COMPOSE) && (state->ks_composed_char > 0)) { action = state->ks_composed_char; state->ks_composed_char = 0; if (action > UCHAR_MAX) return ERRKEY; return action; } /* see if there is something in the keyboard port */ if (wait) { do { scancode = read_kbd_data(state->kbdc); } while (scancode == -1); } else { scancode = read_kbd_data_no_wait(state->kbdc); if (scancode == -1) return NOKEY; } /* return the byte as is for the K_RAW mode */ if (state->ks_mode == K_RAW) return scancode; /* translate the scan code into a keycode */ keycode = scancode & 0x7F; switch (state->ks_prefix) { case 0x00: /* normal scancode */ switch(scancode) { case 0xB8: /* left alt (compose key) released */ if (state->ks_flags & COMPOSE) { state->ks_flags &= ~COMPOSE; if (state->ks_composed_char > UCHAR_MAX) state->ks_composed_char = 0; } break; case 0x38: /* left alt (compose key) pressed */ if (!(state->ks_flags & COMPOSE)) { state->ks_flags |= COMPOSE; state->ks_composed_char = 0; } break; case 0xE0: case 0xE1: state->ks_prefix = scancode; goto next_code; } break; case 0xE0: /* 0xE0 prefix */ state->ks_prefix = 0; switch (keycode) { case 0x1C: /* right enter key */ keycode = 0x59; break; case 0x1D: /* right ctrl key */ keycode = 0x5A; break; case 0x35: /* keypad divide key */ keycode = 0x5B; break; case 0x37: /* print scrn key */ keycode = 0x5C; break; case 0x38: /* right alt key (alt gr) */ keycode = 0x5D; break; case 0x46: /* ctrl-pause/break on AT 101 (see below) */ keycode = 0x68; break; case 0x47: /* grey home key */ keycode = 0x5E; break; case 0x48: /* grey up arrow key */ keycode = 0x5F; break; case 0x49: /* grey page up key */ keycode = 0x60; break; case 0x4B: /* grey left arrow key */ keycode = 0x61; break; case 0x4D: /* grey right arrow key */ keycode = 0x62; break; case 0x4F: /* grey end key */ keycode = 0x63; break; case 0x50: /* grey down arrow key */ keycode = 0x64; break; case 0x51: /* grey page down key */ keycode = 0x65; break; case 0x52: /* grey insert key */ keycode = 0x66; break; case 0x53: /* grey delete key */ keycode = 0x67; break; /* the following 3 are only used on the MS "Natural" keyboard */ case 0x5b: /* left Window key */ keycode = 0x69; break; case 0x5c: /* right Window key */ keycode = 0x6a; break; case 0x5d: /* menu key */ keycode = 0x6b; break; default: /* ignore everything else */ goto next_code; } break; case 0xE1: /* 0xE1 prefix */ /* * The pause/break key on the 101 keyboard produces: * E1-1D-45 E1-9D-C5 * Ctrl-pause/break produces: * E0-46 E0-C6 (See above.) */ state->ks_prefix = 0; if (keycode == 0x1D) state->ks_prefix = 0x1D; goto next_code; /* NOT REACHED */ case 0x1D: /* pause / break */ state->ks_prefix = 0; if (keycode != 0x45) goto next_code; keycode = 0x68; break; } if (kbd->kb_type == KB_84) { switch (keycode) { case 0x37: /* *(numpad)/print screen */ if (state->ks_flags & SHIFTS) keycode = 0x5c; /* print screen */ break; case 0x45: /* num lock/pause */ if (state->ks_flags & CTLS) keycode = 0x68; /* pause */ break; case 0x46: /* scroll lock/break */ if (state->ks_flags & CTLS) keycode = 0x6c; /* break */ break; } } else if (kbd->kb_type == KB_101) { switch (keycode) { case 0x5c: /* print screen */ if (state->ks_flags & ALTS) keycode = 0x54; /* sysrq */ break; case 0x68: /* pause/break */ if (state->ks_flags & CTLS) keycode = 0x6c; /* break */ break; } } /* return the key code in the K_CODE mode */ if (state->ks_mode == K_CODE) return (keycode | (scancode & 0x80)); /* compose a character code */ if (state->ks_flags & COMPOSE) { switch (scancode) { /* key pressed, process it */ case 0x47: case 0x48: case 0x49: /* keypad 7,8,9 */ state->ks_composed_char *= 10; state->ks_composed_char += scancode - 0x40; if (state->ks_composed_char > UCHAR_MAX) return ERRKEY; goto next_code; case 0x4B: case 0x4C: case 0x4D: /* keypad 4,5,6 */ state->ks_composed_char *= 10; state->ks_composed_char += scancode - 0x47; if (state->ks_composed_char > UCHAR_MAX) return ERRKEY; goto next_code; case 0x4F: case 0x50: case 0x51: /* keypad 1,2,3 */ state->ks_composed_char *= 10; state->ks_composed_char += scancode - 0x4E; if (state->ks_composed_char > UCHAR_MAX) return ERRKEY; goto next_code; case 0x52: /* keypad 0 */ state->ks_composed_char *= 10; if (state->ks_composed_char > UCHAR_MAX) return ERRKEY; goto next_code; /* key released, no interest here */ case 0xC7: case 0xC8: case 0xC9: /* keypad 7,8,9 */ case 0xCB: case 0xCC: case 0xCD: /* keypad 4,5,6 */ case 0xCF: case 0xD0: case 0xD1: /* keypad 1,2,3 */ case 0xD2: /* keypad 0 */ goto next_code; case 0x38: /* left alt key */ break; default: if (state->ks_composed_char > 0) { state->ks_flags &= ~COMPOSE; state->ks_composed_char = 0; return ERRKEY; } break; } } /* keycode to key action */ action = genkbd_keyaction(kbd, keycode, scancode & 0x80, &state->ks_state, &state->ks_accents); if (action == NOKEY) goto next_code; else return action; } /* check if char is waiting */ static int atkbd_check_char(keyboard_t *kbd) { atkbd_state_t *state; if (!KBD_IS_ACTIVE(kbd)) return FALSE; state = (atkbd_state_t *)kbd->kb_data; if (!(state->ks_flags & COMPOSE) && (state->ks_composed_char > 0)) return TRUE; return kbdc_data_ready(state->kbdc); } /* some useful control functions */ static int atkbd_ioctl(keyboard_t *kbd, u_long cmd, caddr_t arg) { /* trasnlate LED_XXX bits into the device specific bits */ static u_char ledmap[8] = { 0, 4, 2, 6, 1, 5, 3, 7, }; atkbd_state_t *state = kbd->kb_data; int error; int s; int i; s = spltty(); switch (cmd) { case KDGKBMODE: /* get keyboard mode */ *(int *)arg = state->ks_mode; break; case KDSKBMODE: /* set keyboard mode */ switch (*(int *)arg) { case K_XLATE: if (state->ks_mode != K_XLATE) { /* make lock key state and LED state match */ state->ks_state &= ~LOCK_MASK; state->ks_state |= KBD_LED_VAL(kbd); } /* FALL THROUGH */ case K_RAW: case K_CODE: if (state->ks_mode != *(int *)arg) { atkbd_clear_state(kbd); state->ks_mode = *(int *)arg; } break; default: splx(s); return EINVAL; } break; case KDGETLED: /* get keyboard LED */ *(int *)arg = KBD_LED_VAL(kbd); break; case KDSETLED: /* set keyboard LED */ /* NOTE: lock key state in ks_state won't be changed */ if (*(int *)arg & ~LOCK_MASK) { splx(s); return EINVAL; } i = *(int *)arg; /* replace CAPS LED with ALTGR LED for ALTGR keyboards */ if (kbd->kb_keymap->n_keys > ALTGR_OFFSET) { if (i & ALKED) i |= CLKED; else i &= ~CLKED; } if (KBD_HAS_DEVICE(kbd)) { error = write_kbd(state->kbdc, KBDC_SET_LEDS, ledmap[i & LED_MASK]); if (error) { splx(s); return error; } } KBD_LED_VAL(kbd) = *(int *)arg; break; case KDGKBSTATE: /* get lock key state */ *(int *)arg = state->ks_state & LOCK_MASK; break; case KDSKBSTATE: /* set lock key state */ if (*(int *)arg & ~LOCK_MASK) { splx(s); return EINVAL; } state->ks_state &= ~LOCK_MASK; state->ks_state |= *(int *)arg; splx(s); /* set LEDs and quit */ return atkbd_ioctl(kbd, KDSETLED, arg); case KDSETREPEAT: /* set keyboard repeat rate (new interface) */ splx(s); if (!KBD_HAS_DEVICE(kbd)) return 0; i = typematic(((int *)arg)[0], ((int *)arg)[1]); return write_kbd(state->kbdc, KBDC_SET_TYPEMATIC, i); case KDSETRAD: /* set keyboard repeat rate (old interface) */ splx(s); if (!KBD_HAS_DEVICE(kbd)) return 0; return write_kbd(state->kbdc, KBDC_SET_TYPEMATIC, *(int *)arg); case PIO_KEYMAP: /* set keyboard translation table */ case PIO_KEYMAPENT: /* set keyboard translation table entry */ case PIO_DEADKEYMAP: /* set accent key translation table */ state->ks_accents = 0; /* FALL THROUGH */ default: splx(s); return genkbd_commonioctl(kbd, cmd, arg); } splx(s); return 0; } /* lock the access to the keyboard */ static int atkbd_lock(keyboard_t *kbd, int lock) { return kbdc_lock(((atkbd_state_t *)kbd->kb_data)->kbdc, lock); } /* clear the internal state of the keyboard */ static void atkbd_clear_state(keyboard_t *kbd) { atkbd_state_t *state; state = (atkbd_state_t *)kbd->kb_data; state->ks_flags = 0; state->ks_polling = 0; state->ks_state &= LOCK_MASK; /* preserve locking key state */ state->ks_accents = 0; state->ks_composed_char = 0; #if 0 state->ks_prefix = 0; /* XXX */ #endif } /* save the internal state */ static int atkbd_get_state(keyboard_t *kbd, void *buf, size_t len) { if (len == 0) return sizeof(atkbd_state_t); if (len < sizeof(atkbd_state_t)) return -1; bcopy(kbd->kb_data, buf, sizeof(atkbd_state_t)); return 0; } /* set the internal state */ static int atkbd_set_state(keyboard_t *kbd, void *buf, size_t len) { if (len < sizeof(atkbd_state_t)) return ENOMEM; if (((atkbd_state_t *)kbd->kb_data)->kbdc != ((atkbd_state_t *)buf)->kbdc) return ENOMEM; bcopy(buf, kbd->kb_data, sizeof(atkbd_state_t)); return 0; } static int atkbd_poll(keyboard_t *kbd, int on) { atkbd_state_t *state; int s; state = (atkbd_state_t *)kbd->kb_data; s = spltty(); if (on) ++state->ks_polling; else --state->ks_polling; splx(s); return 0; } /* local functions */ static int setup_kbd_port(KBDC kbdc, int port, int intr) { if (!set_controller_command_byte(kbdc, KBD_KBD_CONTROL_BITS, ((port) ? KBD_ENABLE_KBD_PORT : KBD_DISABLE_KBD_PORT) | ((intr) ? KBD_ENABLE_KBD_INT : KBD_DISABLE_KBD_INT))) return 1; return 0; } static int get_kbd_echo(KBDC kbdc) { /* enable the keyboard port, but disable the keyboard intr. */ if (setup_kbd_port(kbdc, TRUE, FALSE)) /* CONTROLLER ERROR: there is very little we can do... */ return ENXIO; /* see if something is present */ write_kbd_command(kbdc, KBDC_ECHO); if (read_kbd_data(kbdc) != KBD_ECHO) { empty_both_buffers(kbdc, 10); test_controller(kbdc); test_kbd_port(kbdc); return ENXIO; } /* enable the keyboard port and intr. */ if (setup_kbd_port(kbdc, TRUE, TRUE)) { /* * CONTROLLER ERROR * This is serious; the keyboard intr is left disabled! */ return ENXIO; } return 0; } static int probe_keyboard(KBDC kbdc, int flags) { /* * Don't try to print anything in this function. The low-level * console may not have been initialized yet... */ int err; int c; int m; if (!kbdc_lock(kbdc, TRUE)) { /* driver error? */ return ENXIO; } /* flush any noise in the buffer */ empty_both_buffers(kbdc, 10); /* save the current keyboard controller command byte */ m = kbdc_get_device_mask(kbdc) & ~KBD_KBD_CONTROL_BITS; c = get_controller_command_byte(kbdc); if (c == -1) { /* CONTROLLER ERROR */ kbdc_set_device_mask(kbdc, m); kbdc_lock(kbdc, FALSE); return ENXIO; } /* * The keyboard may have been screwed up by the boot block. * We may just be able to recover from error by testing the controller * and the keyboard port. The controller command byte needs to be * saved before this recovery operation, as some controllers seem * to set the command byte to particular values. */ test_controller(kbdc); test_kbd_port(kbdc); err = get_kbd_echo(kbdc); if (err == 0) { kbdc_set_device_mask(kbdc, m | KBD_KBD_CONTROL_BITS); } else { if (c != -1) /* try to restore the command byte as before */ set_controller_command_byte(kbdc, 0xff, c); kbdc_set_device_mask(kbdc, m); } kbdc_lock(kbdc, FALSE); return err; } static int init_keyboard(KBDC kbdc, int *type, int flags) { int codeset; int id; int c; if (!kbdc_lock(kbdc, TRUE)) { /* driver error? */ return EIO; } /* save the current controller command byte */ empty_both_buffers(kbdc, 10); c = get_controller_command_byte(kbdc); if (c == -1) { /* CONTROLLER ERROR */ kbdc_lock(kbdc, FALSE); printf("atkbd: unable to get the current command byte value.\n"); return EIO; } if (bootverbose) printf("atkbd: the current kbd controller command byte %04x\n", c); #if 0 /* override the keyboard lock switch */ c |= KBD_OVERRIDE_KBD_LOCK; #endif /* enable the keyboard port, but disable the keyboard intr. */ if (setup_kbd_port(kbdc, TRUE, FALSE)) { /* CONTROLLER ERROR: there is very little we can do... */ printf("atkbd: unable to set the command byte.\n"); kbdc_lock(kbdc, FALSE); return EIO; } /* * Check if we have an XT keyboard before we attempt to reset it. * The procedure assumes that the keyboard and the controller have * been set up properly by BIOS and have not been messed up * during the boot process. */ codeset = -1; if (flags & KB_CONF_ALT_SCANCODESET) /* the user says there is a XT keyboard */ codeset = 1; #ifdef KBD_DETECT_XT_KEYBOARD else if ((c & KBD_TRANSLATION) == 0) { /* SET_SCANCODE_SET is not always supported; ignore error */ if (send_kbd_command_and_data(kbdc, KBDC_SET_SCANCODE_SET, 0) == KBD_ACK) codeset = read_kbd_data(kbdc); } if (bootverbose) printf("atkbd: scancode set %d\n", codeset); #endif /* KBD_DETECT_XT_KEYBOARD */ *type = KB_OTHER; id = get_kbd_id(kbdc); switch(id) { case 0x41ab: case 0x83ab: *type = KB_101; break; case -1: /* AT 84 keyboard doesn't return ID */ *type = KB_84; break; default: break; } if (bootverbose) printf("atkbd: keyboard ID 0x%x (%d)\n", id, *type); /* reset keyboard hardware */ if (!(flags & KB_CONF_NO_RESET) && !reset_kbd(kbdc)) { /* * KEYBOARD ERROR * Keyboard reset may fail either because the keyboard * doen't exist, or because the keyboard doesn't pass * the self-test, or the keyboard controller on the * motherboard and the keyboard somehow fail to shake hands. * It is just possible, particularly in the last case, * that the keyoard controller may be left in a hung state. * test_controller() and test_kbd_port() appear to bring * the keyboard controller back (I don't know why and how, * though.) */ empty_both_buffers(kbdc, 10); test_controller(kbdc); test_kbd_port(kbdc); /* * We could disable the keyboard port and interrupt... but, * the keyboard may still exist (see above). */ set_controller_command_byte(kbdc, 0xff, c); kbdc_lock(kbdc, FALSE); if (bootverbose) printf("atkbd: failed to reset the keyboard.\n"); return EIO; } /* * Allow us to set the XT_KEYBD flag in UserConfig so that keyboards * such as those on the IBM ThinkPad laptop computers can be used * with the standard console driver. */ if (codeset == 1) { if (send_kbd_command_and_data(kbdc, KBDC_SET_SCANCODE_SET, codeset) == KBD_ACK) { /* XT kbd doesn't need scan code translation */ c &= ~KBD_TRANSLATION; } else { /* * KEYBOARD ERROR * The XT kbd isn't usable unless the proper scan * code set is selected. */ set_controller_command_byte(kbdc, 0xff, c); kbdc_lock(kbdc, FALSE); printf("atkbd: unable to set the XT keyboard mode.\n"); return EIO; } } #ifdef __alpha__ if (send_kbd_command_and_data( kbdc, KBDC_SET_SCANCODE_SET, 2) != KBD_ACK) { printf("atkbd: can't set translation.\n"); } c |= KBD_TRANSLATION; #endif /* enable the keyboard port and intr. */ if (!set_controller_command_byte(kbdc, KBD_KBD_CONTROL_BITS | KBD_TRANSLATION | KBD_OVERRIDE_KBD_LOCK, (c & (KBD_TRANSLATION | KBD_OVERRIDE_KBD_LOCK)) | KBD_ENABLE_KBD_PORT | KBD_ENABLE_KBD_INT)) { /* * CONTROLLER ERROR * This is serious; we are left with the disabled * keyboard intr. */ set_controller_command_byte(kbdc, 0xff, c); kbdc_lock(kbdc, FALSE); printf("atkbd: unable to enable the keyboard port and intr.\n"); return EIO; } kbdc_lock(kbdc, FALSE); return 0; } static int write_kbd(KBDC kbdc, int command, int data) { int s; /* prevent the timeout routine from polling the keyboard */ if (!kbdc_lock(kbdc, TRUE)) return EBUSY; /* disable the keyboard and mouse interrupt */ s = spltty(); #if 0 c = get_controller_command_byte(kbdc); if ((c == -1) || !set_controller_command_byte(kbdc, kbdc_get_device_mask(kbdc), KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT | KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { /* CONTROLLER ERROR */ kbdc_lock(kbdc, FALSE); splx(s); return EIO; } /* * Now that the keyboard controller is told not to generate * the keyboard and mouse interrupts, call `splx()' to allow * the other tty interrupts. The clock interrupt may also occur, * but the timeout routine (`scrn_timer()') will be blocked * by the lock flag set via `kbdc_lock()' */ splx(s); #endif if (send_kbd_command_and_data(kbdc, command, data) != KBD_ACK) send_kbd_command(kbdc, KBDC_ENABLE_KBD); #if 0 /* restore the interrupts */ if (!set_controller_command_byte(kbdc, kbdc_get_device_mask(kbdc), c & (KBD_KBD_CONTROL_BITS | KBD_AUX_CONTROL_BITS))) { /* CONTROLLER ERROR */ } #else splx(s); #endif kbdc_lock(kbdc, FALSE); return 0; } static int get_kbd_id(KBDC kbdc) { int id1, id2; empty_both_buffers(kbdc, 10); id1 = id2 = -1; if (send_kbd_command(kbdc, KBDC_SEND_DEV_ID) != KBD_ACK) return -1; DELAY(10000); /* 10 msec delay */ id1 = read_kbd_data(kbdc); if (id1 != -1) id2 = read_kbd_data(kbdc); if ((id1 == -1) || (id2 == -1)) { empty_both_buffers(kbdc, 10); test_controller(kbdc); test_kbd_port(kbdc); return -1; } return ((id2 << 8) | id1); } static int typematic(int delay, int rate) { static int delays[] = { 250, 500, 750, 1000 }; static int rates[] = { 34, 38, 42, 46, 50, 55, 59, 63, 68, 76, 84, 92, 100, 110, 118, 126, 136, 152, 168, 184, 200, 220, 236, 252, 272, 304, 336, 368, 400, 440, 472, 504 }; int value; int i; for (i = sizeof(delays)/sizeof(delays[0]) - 1; i > 0; --i) { if (delay >= delays[i]) break; } value = i << 5; for (i = sizeof(rates)/sizeof(rates[0]) - 1; i > 0; --i) { if (rate >= rates[i]) break; } value |= i; return value; } #endif /* NATKBD > 0 */ Index: head/sys/dev/atkbdc/atkbd_atkbdc.c =================================================================== --- head/sys/dev/atkbdc/atkbd_atkbdc.c (revision 45719) +++ head/sys/dev/atkbdc/atkbd_atkbdc.c (revision 45720) @@ -1,130 +1,131 @@ /*- * Copyright (c) 1999 Kazutaka YOKOTA * 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 as * the first lines of this file unmodified. * 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 AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS BE LIABLE 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. * - * $Id: atkbd_isa.c,v 1.1 1999/01/23 16:53:27 dfr Exp $ + * $Id: atkbd_isa.c,v 1.2 1999/03/10 10:36:49 yokota Exp $ */ #include "atkbd.h" #include "opt_kbd.h" #if NATKBD > 0 #include #include #include #include #include #include +#include #include #include #include #include #include #include #include devclass_t atkbd_devclass; static int atkbdprobe(device_t dev); static int atkbdattach(device_t dev); static void atkbd_isa_intr(void *arg); static device_method_t atkbd_methods[] = { DEVMETHOD(device_probe, atkbdprobe), DEVMETHOD(device_attach, atkbdattach), { 0, 0 } }; static driver_t atkbd_driver = { ATKBD_DRIVER_NAME, atkbd_methods, DRIVER_TYPE_TTY, sizeof(atkbd_softc_t), }; static int atkbdprobe(device_t dev) { - u_long port; - u_long irq; - u_long flags; + uintptr_t port; + uintptr_t irq; + uintptr_t flags; device_set_desc(dev, "AT Keyboard"); /* obtain parameters */ BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_PORT, &port); BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_IRQ, &irq); BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_FLAGS, &flags); /* probe the device */ return atkbd_probe_unit(device_get_unit(dev), port, irq, flags); } static int atkbdattach(device_t dev) { atkbd_softc_t *sc; - u_long port; - u_long irq; - u_long flags; + uintptr_t port; + uintptr_t irq; + uintptr_t flags; struct resource *res; void *ih; int zero = 0; int error; sc = (atkbd_softc_t *)device_get_softc(dev); BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_PORT, &port); BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_IRQ, &irq); BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_FLAGS, &flags); error = atkbd_attach_unit(device_get_unit(dev), sc, port, irq, flags); if (error) return error; /* declare our interrupt handler */ res = bus_alloc_resource(dev, SYS_RES_IRQ, &zero, irq, irq, 1, RF_SHAREABLE | RF_ACTIVE); BUS_SETUP_INTR(device_get_parent(dev), dev, res, atkbd_isa_intr, sc, &ih); return 0; } static void atkbd_isa_intr(void *arg) { atkbd_softc_t *sc; sc = (atkbd_softc_t *)arg; (*kbdsw[sc->kbd->kb_index]->intr)(sc->kbd, NULL); } DRIVER_MODULE(atkbd, atkbdc, atkbd_driver, atkbd_devclass, 0, 0); #endif /* NATKBD > 0 */ Index: head/sys/dev/atkbdc/atkbd_isa.c =================================================================== --- head/sys/dev/atkbdc/atkbd_isa.c (revision 45719) +++ head/sys/dev/atkbdc/atkbd_isa.c (revision 45720) @@ -1,130 +1,131 @@ /*- * Copyright (c) 1999 Kazutaka YOKOTA * 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 as * the first lines of this file unmodified. * 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 AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS BE LIABLE 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. * - * $Id: atkbd_isa.c,v 1.1 1999/01/23 16:53:27 dfr Exp $ + * $Id: atkbd_isa.c,v 1.2 1999/03/10 10:36:49 yokota Exp $ */ #include "atkbd.h" #include "opt_kbd.h" #if NATKBD > 0 #include #include #include #include #include #include +#include #include #include #include #include #include #include #include devclass_t atkbd_devclass; static int atkbdprobe(device_t dev); static int atkbdattach(device_t dev); static void atkbd_isa_intr(void *arg); static device_method_t atkbd_methods[] = { DEVMETHOD(device_probe, atkbdprobe), DEVMETHOD(device_attach, atkbdattach), { 0, 0 } }; static driver_t atkbd_driver = { ATKBD_DRIVER_NAME, atkbd_methods, DRIVER_TYPE_TTY, sizeof(atkbd_softc_t), }; static int atkbdprobe(device_t dev) { - u_long port; - u_long irq; - u_long flags; + uintptr_t port; + uintptr_t irq; + uintptr_t flags; device_set_desc(dev, "AT Keyboard"); /* obtain parameters */ BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_PORT, &port); BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_IRQ, &irq); BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_FLAGS, &flags); /* probe the device */ return atkbd_probe_unit(device_get_unit(dev), port, irq, flags); } static int atkbdattach(device_t dev) { atkbd_softc_t *sc; - u_long port; - u_long irq; - u_long flags; + uintptr_t port; + uintptr_t irq; + uintptr_t flags; struct resource *res; void *ih; int zero = 0; int error; sc = (atkbd_softc_t *)device_get_softc(dev); BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_PORT, &port); BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_IRQ, &irq); BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_FLAGS, &flags); error = atkbd_attach_unit(device_get_unit(dev), sc, port, irq, flags); if (error) return error; /* declare our interrupt handler */ res = bus_alloc_resource(dev, SYS_RES_IRQ, &zero, irq, irq, 1, RF_SHAREABLE | RF_ACTIVE); BUS_SETUP_INTR(device_get_parent(dev), dev, res, atkbd_isa_intr, sc, &ih); return 0; } static void atkbd_isa_intr(void *arg) { atkbd_softc_t *sc; sc = (atkbd_softc_t *)arg; (*kbdsw[sc->kbd->kb_index]->intr)(sc->kbd, NULL); } DRIVER_MODULE(atkbd, atkbdc, atkbd_driver, atkbd_devclass, 0, 0); #endif /* NATKBD > 0 */ Index: head/sys/dev/atkbdc/psm.c =================================================================== --- head/sys/dev/atkbdc/psm.c (revision 45719) +++ head/sys/dev/atkbdc/psm.c (revision 45720) @@ -1,2231 +1,2232 @@ /*- * Copyright (c) 1992, 1993 Erik Forsberg. * Copyright (c) 1996, 1997 Kazutaka YOKOTA. * 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. * * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN * NO EVENT SHALL I 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. * - * $Id: psm.c,v 1.2 1998/11/15 18:25:17 dfr Exp $ + * $Id: psm.c,v 1.3 1999/01/23 16:53:28 dfr Exp $ */ /* * Ported to 386bsd Oct 17, 1992 * Sandi Donno, Computer Science, University of Cape Town, South Africa * Please send bug reports to sandi@cs.uct.ac.za * * Thanks are also due to Rick Macklem, rick@snowhite.cis.uoguelph.ca - * although I was only partially successful in getting the alpha release * of his "driver for the Logitech and ATI Inport Bus mice for use with * 386bsd and the X386 port" to work with my Microsoft mouse, I nevertheless * found his code to be an invaluable reference when porting this driver * to 386bsd. * * Further modifications for latest 386BSD+patchkit and port to NetBSD, * Andrew Herbert - 8 June 1993 * * Cloned from the Microsoft Bus Mouse driver, also by Erik Forsberg, by * Andrew Herbert - 12 June 1993 * * Modified for PS/2 mouse by Charles Hannum * - 13 June 1993 * * Modified for PS/2 AUX mouse by Shoji Yuen * - 24 October 1993 * * Hardware access routines and probe logic rewritten by * Kazutaka Yokota * - 3, 14, 22 October 1996. * - 12 November 1996. IOCTLs and rearranging `psmread', `psmioctl'... * - 14, 30 November 1996. Uses `kbdio.c'. * - 13 December 1996. Uses queuing version of `kbdio.c'. * - January/February 1997. Tweaked probe logic for * HiNote UltraII/Latitude/Armada laptops. * - 30 July 1997. Added APM support. * - 5 March 1997. Defined driver configuration flags (PSM_CONFIG_XXX). * Improved sync check logic. * Vendor specific support routines. */ #include "psm.h" #include "opt_devfs.h" #include "opt_psm.h" #if NPSM > 0 #include #include #include #include #include #include #include #include #include +#include #include #ifdef DEVFS #include #endif #include #include #include #include #include #include #include #include #include /* * Driver specific options: the following options may be set by * `options' statements in the kernel configuration file. */ /* debugging */ #ifndef PSM_DEBUG #define PSM_DEBUG 0 /* logging: 0: none, 1: brief, 2: verbose */ #endif /* features */ /* #define PSM_HOOKAPM hook the APM resume event */ /* #define PSM_RESETAFTERSUSPEND reset the device at the resume event */ #if NAPM <= 0 #undef PSM_HOOKAPM #endif /* NAPM */ #ifndef PSM_HOOKAPM #undef PSM_RESETAFTERSUSPEND #endif /* PSM_HOOKAPM */ /* end of driver specific options */ /* input queue */ #define PSM_BUFSIZE 960 #define PSM_SMALLBUFSIZE 240 /* operation levels */ #define PSM_LEVEL_BASE 0 #define PSM_LEVEL_STANDARD 1 #define PSM_LEVEL_NATIVE 2 #define PSM_LEVEL_MIN PSM_LEVEL_BASE #define PSM_LEVEL_MAX PSM_LEVEL_NATIVE /* some macros */ #define PSM_UNIT(dev) (minor(dev) >> 1) #define PSM_NBLOCKIO(dev) (minor(dev) & 1) #define PSM_MKMINOR(unit,block) (((unit) << 1) | ((block) ? 0:1)) #ifndef max #define max(x,y) ((x) > (y) ? (x) : (y)) #endif #ifndef min #define min(x,y) ((x) < (y) ? (x) : (y)) #endif /* ring buffer */ typedef struct ringbuf { int count; /* # of valid elements in the buffer */ int head; /* head pointer */ int tail; /* tail poiner */ unsigned char buf[PSM_BUFSIZE]; } ringbuf_t; /* driver control block */ struct psm_softc { /* Driver status information */ struct selinfo rsel; /* Process selecting for Input */ unsigned char state; /* Mouse driver state */ int config; /* driver configuration flags */ int flags; /* other flags */ KBDC kbdc; /* handle to access the keyboard controller */ int addr; /* I/O port address */ mousehw_t hw; /* hardware information */ mousemode_t mode; /* operation mode */ mousemode_t dflt_mode; /* default operation mode */ mousestatus_t status; /* accumulated mouse movement */ ringbuf_t queue; /* mouse status queue */ unsigned char ipacket[16]; /* interim input buffer */ int inputbytes; /* # of bytes in the input buffer */ int button; /* the latest button state */ #ifdef DEVFS void *devfs_token; void *b_devfs_token; #endif #ifdef PSM_HOOKAPM struct apmhook resumehook; #endif }; devclass_t psm_devclass; #define PSM_SOFTC(unit) ((struct psm_softc*)devclass_get_softc(psm_devclass, unit)) /* driver state flags (state) */ #define PSM_VALID 0x80 #define PSM_OPEN 1 /* Device is open */ #define PSM_ASLP 2 /* Waiting for mouse data */ /* driver configuration flags (config) */ #define PSM_CONFIG_RESOLUTION 0x000f /* resolution */ #define PSM_CONFIG_ACCEL 0x00f0 /* acceleration factor */ #define PSM_CONFIG_NOCHECKSYNC 0x0100 /* disable sync. test */ #define PSM_CONFIG_FLAGS (PSM_CONFIG_RESOLUTION \ | PSM_CONFIG_ACCEL \ | PSM_CONFIG_NOCHECKSYNC) /* other flags (flags) */ /* * Pass mouse data packet to the user land program `as is', even if * the mouse has vendor-specific enhanced features and uses non-standard * packet format. Otherwise manipulate the mouse data packet so that * it can be recognized by the programs which can only understand * the standard packet format. */ #define PSM_FLAGS_NATIVEMODE 0x0200 /* for backward compatibility */ #define OLD_MOUSE_GETHWINFO _IOR('M', 1, old_mousehw_t) #define OLD_MOUSE_GETMODE _IOR('M', 2, old_mousemode_t) #define OLD_MOUSE_SETMODE _IOW('M', 3, old_mousemode_t) typedef struct old_mousehw { int buttons; int iftype; int type; int hwid; } old_mousehw_t; typedef struct old_mousemode { int protocol; int rate; int resolution; int accelfactor; } old_mousemode_t; /* packet formatting function */ typedef int packetfunc_t __P((struct psm_softc *, unsigned char *, int *, int, mousestatus_t *)); /* function prototypes */ static int psmprobe __P((device_t)); static int psmattach __P((device_t)); #ifdef PSM_HOOKAPM static int psmresume __P((void *)); #endif static d_open_t psmopen; static d_close_t psmclose; static d_read_t psmread; static d_ioctl_t psmioctl; static d_poll_t psmpoll; static int enable_aux_dev __P((KBDC)); static int disable_aux_dev __P((KBDC)); static int get_mouse_status __P((KBDC, int *, int, int)); static int get_aux_id __P((KBDC)); static int set_mouse_sampling_rate __P((KBDC, int)); static int set_mouse_scaling __P((KBDC, int)); static int set_mouse_resolution __P((KBDC, int)); static int set_mouse_mode __P((KBDC)); static int get_mouse_buttons __P((KBDC)); static int is_a_mouse __P((int)); static void recover_from_error __P((KBDC)); static int restore_controller __P((KBDC, int)); static int reinitialize __P((int, mousemode_t *)); static int doopen __P((int, int)); static char *model_name(int); static void psmintr(void*); /* vendor specific features */ typedef int probefunc_t __P((struct psm_softc *)); static int mouse_id_proc1 __P((KBDC, int, int, int *)); static probefunc_t enable_groller; static probefunc_t enable_gmouse; static probefunc_t enable_aglide; static probefunc_t enable_kmouse; static probefunc_t enable_msintelli; static probefunc_t enable_mmanplus; static int tame_mouse __P((struct psm_softc *, mousestatus_t *, unsigned char *)); static struct { int model; unsigned char syncmask; int packetsize; probefunc_t *probefunc; } vendortype[] = { { MOUSE_MODEL_NET, /* Genius NetMouse */ 0xc8, MOUSE_INTELLI_PACKETSIZE, enable_gmouse, }, { MOUSE_MODEL_NETSCROLL, /* Genius NetScroll */ 0xc8, 6, enable_groller, }, { MOUSE_MODEL_GLIDEPOINT, /* ALPS GlidePoint */ 0xc0, MOUSE_PS2_PACKETSIZE, enable_aglide, }, { MOUSE_MODEL_MOUSEMANPLUS, /* Logitech MouseMan+ */ 0x08, MOUSE_PS2_PACKETSIZE, enable_mmanplus, }, { MOUSE_MODEL_THINK, /* Kensignton ThinkingMouse */ 0x80, MOUSE_PS2_PACKETSIZE, enable_kmouse, }, { MOUSE_MODEL_INTELLI, /* Microsoft IntelliMouse */ 0xc8, MOUSE_INTELLI_PACKETSIZE, enable_msintelli, }, { MOUSE_MODEL_GENERIC, 0xc0, MOUSE_PS2_PACKETSIZE, NULL, }, }; /* device driver declarateion */ static device_method_t psm_methods[] = { /* Device interface */ DEVMETHOD(device_probe, psmprobe), DEVMETHOD(device_attach, psmattach), { 0, 0 } }; static driver_t psm_driver = { "psm", psm_methods, DRIVER_TYPE_TTY, sizeof(struct psm_softc), }; #define CDEV_MAJOR 21 static struct cdevsw psm_cdevsw = { psmopen, psmclose, psmread, nowrite, /* 21 */ psmioctl, nostop, nullreset, nodevtotty, psmpoll, nommap, NULL, "psm", NULL, -1 }; /* debug message level */ static int verbose = PSM_DEBUG; /* device I/O routines */ static int enable_aux_dev(KBDC kbdc) { int res; res = send_aux_command(kbdc, PSMC_ENABLE_DEV); if (verbose >= 2) log(LOG_DEBUG, "psm: ENABLE_DEV return code:%04x\n", res); return (res == PSM_ACK); } static int disable_aux_dev(KBDC kbdc) { int res; res = send_aux_command(kbdc, PSMC_DISABLE_DEV); if (verbose >= 2) log(LOG_DEBUG, "psm: DISABLE_DEV return code:%04x\n", res); return (res == PSM_ACK); } static int get_mouse_status(KBDC kbdc, int *status, int flag, int len) { int cmd; int res; int i; switch (flag) { case 0: default: cmd = PSMC_SEND_DEV_STATUS; break; case 1: cmd = PSMC_SEND_DEV_DATA; break; } empty_aux_buffer(kbdc, 5); res = send_aux_command(kbdc, cmd); if (verbose >= 2) log(LOG_DEBUG, "psm: SEND_AUX_DEV_%s return code:%04x\n", (flag == 1) ? "DATA" : "STATUS", res); if (res != PSM_ACK) return 0; for (i = 0; i < len; ++i) { status[i] = read_aux_data(kbdc); if (status[i] < 0) break; } if (verbose) { log(LOG_DEBUG, "psm: %s %02x %02x %02x\n", (flag == 1) ? "data" : "status", status[0], status[1], status[2]); } return i; } static int get_aux_id(KBDC kbdc) { int res; int id; empty_aux_buffer(kbdc, 5); res = send_aux_command(kbdc, PSMC_SEND_DEV_ID); if (verbose >= 2) log(LOG_DEBUG, "psm: SEND_DEV_ID return code:%04x\n", res); if (res != PSM_ACK) return (-1); /* 10ms delay */ DELAY(10000); id = read_aux_data(kbdc); if (verbose >= 2) log(LOG_DEBUG, "psm: device ID: %04x\n", id); return id; } static int set_mouse_sampling_rate(KBDC kbdc, int rate) { int res; res = send_aux_command_and_data(kbdc, PSMC_SET_SAMPLING_RATE, rate); if (verbose >= 2) log(LOG_DEBUG, "psm: SET_SAMPLING_RATE (%d) %04x\n", rate, res); return ((res == PSM_ACK) ? rate : -1); } static int set_mouse_scaling(KBDC kbdc, int scale) { int res; switch (scale) { case 1: default: scale = PSMC_SET_SCALING11; break; case 2: scale = PSMC_SET_SCALING21; break; } res = send_aux_command(kbdc, scale); if (verbose >= 2) log(LOG_DEBUG, "psm: SET_SCALING%s return code:%04x\n", (scale == PSMC_SET_SCALING21) ? "21" : "11", res); return (res == PSM_ACK); } /* `val' must be 0 through PSMD_MAX_RESOLUTION */ static int set_mouse_resolution(KBDC kbdc, int val) { int res; res = send_aux_command_and_data(kbdc, PSMC_SET_RESOLUTION, val); if (verbose >= 2) log(LOG_DEBUG, "psm: SET_RESOLUTION (%d) %04x\n", val, res); return ((res == PSM_ACK) ? val : -1); } /* * NOTE: once `set_mouse_mode()' is called, the mouse device must be * re-enabled by calling `enable_aux_dev()' */ static int set_mouse_mode(KBDC kbdc) { int res; res = send_aux_command(kbdc, PSMC_SET_STREAM_MODE); if (verbose >= 2) log(LOG_DEBUG, "psm: SET_STREAM_MODE return code:%04x\n", res); return (res == PSM_ACK); } static int get_mouse_buttons(KBDC kbdc) { int c = 2; /* assume two buttons by default */ int status[3]; /* * NOTE: a special sequence to obtain Logitech Mouse specific * information: set resolution to 25 ppi, set scaling to 1:1, set * scaling to 1:1, set scaling to 1:1. Then the second byte of the * mouse status bytes is the number of available buttons. * Some manufactures also support this sequence. */ if (set_mouse_resolution(kbdc, PSMD_RES_LOW) != PSMD_RES_LOW) return c; if (set_mouse_scaling(kbdc, 1) && set_mouse_scaling(kbdc, 1) && set_mouse_scaling(kbdc, 1) && (get_mouse_status(kbdc, status, 0, 3) >= 3)) { if (status[1] != 0) return status[1]; } return c; } /* misc subroutines */ /* * Someday, I will get the complete list of valid pointing devices and * their IDs... XXX */ static int is_a_mouse(int id) { #if 0 static int valid_ids[] = { PSM_MOUSE_ID, /* mouse */ PSM_BALLPOINT_ID, /* ballpoint device */ PSM_INTELLI_ID, /* Intellimouse */ -1 /* end of table */ }; int i; for (i = 0; valid_ids[i] >= 0; ++i) if (valid_ids[i] == id) return TRUE; return FALSE; #else return TRUE; #endif } static char * model_name(int model) { static struct { int model_code; char *model_name; } models[] = { { MOUSE_MODEL_NETSCROLL, "NetScroll Mouse" }, { MOUSE_MODEL_NET, "NetMouse" }, { MOUSE_MODEL_GLIDEPOINT, "GlidePoint" }, { MOUSE_MODEL_THINK, "ThinkingMouse" }, { MOUSE_MODEL_INTELLI, "IntelliMouse" }, { MOUSE_MODEL_MOUSEMANPLUS, "MouseMan+" }, { MOUSE_MODEL_GENERIC, "Generic PS/2 mouse" }, { MOUSE_MODEL_UNKNOWN, NULL }, }; int i; for (i = 0; models[i].model_code != MOUSE_MODEL_UNKNOWN; ++i) { if (models[i].model_code == model) return models[i].model_name; } return "Unknown"; } static void recover_from_error(KBDC kbdc) { /* discard anything left in the output buffer */ empty_both_buffers(kbdc, 10); #if 0 /* * NOTE: KBDC_RESET_KBD may not restore the communication between the * keyboard and the controller. */ reset_kbd(kbdc); #else /* * NOTE: somehow diagnostic and keyboard port test commands bring the * keyboard back. */ if (!test_controller(kbdc)) log(LOG_ERR, "psm: keyboard controller failed.\n"); /* if there isn't a keyboard in the system, the following error is OK */ if (test_kbd_port(kbdc) != 0) { if (verbose) log(LOG_ERR, "psm: keyboard port failed.\n"); } #endif } static int restore_controller(KBDC kbdc, int command_byte) { empty_both_buffers(kbdc, 10); if (!set_controller_command_byte(kbdc, 0xff, command_byte)) { log(LOG_ERR, "psm: failed to restore the keyboard controller " "command byte.\n"); return FALSE; } else { return TRUE; } } /* * Re-initialize the aux port and device. The aux port must be enabled * and its interrupt must be disabled before calling this routine. * The aux device will be disabled before returning. * The keyboard controller must be locked via `kbdc_lock()' before * calling this routine. */ static int reinitialize(int unit, mousemode_t *mode) { struct psm_softc *sc = PSM_SOFTC(unit); KBDC kbdc = sc->kbdc; int stat[3]; int i; switch((i = test_aux_port(kbdc))) { case 1: /* ignore this error */ case PSM_ACK: if (verbose) log(LOG_DEBUG, "psm%d: strange result for test aux port (%d).\n", unit, i); /* fall though */ case 0: /* no error */ break; case -1: /* time out */ default: /* error */ recover_from_error(kbdc); log(LOG_ERR, "psm%d: the aux port is not functioning (%d).\n", unit, i); return FALSE; } /* * NOTE: some controllers appears to hang the `keyboard' when * the aux port doesn't exist and `PSMC_RESET_DEV' is issued. */ if (!reset_aux_dev(kbdc)) { recover_from_error(kbdc); log(LOG_ERR, "psm%d: failed to reset the aux device.\n", unit); return FALSE; } /* * both the aux port and the aux device is functioning, see * if the device can be enabled. */ if (!enable_aux_dev(kbdc) || !disable_aux_dev(kbdc)) { log(LOG_ERR, "psm%d: failed to enable the aux device.\n", unit); return FALSE; } empty_both_buffers(kbdc, 10); /* remove stray data if any */ /* FIXME: hardware ID, mouse buttons? */ /* other parameters */ for (i = 0; vendortype[i].probefunc != NULL; ++i) { if ((*vendortype[i].probefunc)(sc)) { if (verbose >= 2) log(LOG_ERR, "psm%d: found %s\n", unit, model_name(vendortype[i].model)); break; } } sc->hw.model = vendortype[i].model; sc->mode.packetsize = vendortype[i].packetsize; /* set mouse parameters */ if (mode != (mousemode_t *)NULL) { if (mode->rate > 0) mode->rate = set_mouse_sampling_rate(kbdc, mode->rate); if (mode->resolution >= 0) mode->resolution = set_mouse_resolution(kbdc, mode->resolution); set_mouse_scaling(kbdc, 1); set_mouse_mode(kbdc); } /* request a data packet and extract sync. bits */ if (get_mouse_status(kbdc, stat, 1, 3) < 3) { log(LOG_DEBUG, "psm%d: failed to get data (reinitialize).\n", unit); sc->mode.syncmask[0] = 0; } else { sc->mode.syncmask[1] = stat[0] & sc->mode.syncmask[0]; /* syncbits */ /* the NetScroll Mouse will send three more bytes... Ignore them */ empty_aux_buffer(kbdc, 5); } /* just check the status of the mouse */ if (get_mouse_status(kbdc, stat, 0, 3) < 3) log(LOG_DEBUG, "psm%d: failed to get status (reinitialize).\n", unit); return TRUE; } static int doopen(int unit, int command_byte) { struct psm_softc *sc = PSM_SOFTC(unit); int stat[3]; /* enable the mouse device */ if (!enable_aux_dev(sc->kbdc)) { /* MOUSE ERROR: failed to enable the mouse because: * 1) the mouse is faulty, * 2) the mouse has been removed(!?) * In the latter case, the keyboard may have hung, and need * recovery procedure... */ recover_from_error(sc->kbdc); #if 0 /* FIXME: we could reset the mouse here and try to enable * it again. But it will take long time and it's not a good * idea to disable the keyboard that long... */ if (!reinitialize(unit, &sc->mode) || !enable_aux_dev(sc->kbdc)) { recover_from_error(sc->kbdc); #else { #endif restore_controller(sc->kbdc, command_byte); /* mark this device is no longer available */ sc->state &= ~PSM_VALID; log(LOG_ERR, "psm%d: failed to enable the device (doopen).\n", unit); return (EIO); } } if (get_mouse_status(sc->kbdc, stat, 0, 3) < 3) log(LOG_DEBUG, "psm%d: failed to get status (doopen).\n", unit); /* enable the aux port and interrupt */ if (!set_controller_command_byte(sc->kbdc, kbdc_get_device_mask(sc->kbdc), (command_byte & KBD_KBD_CONTROL_BITS) | KBD_ENABLE_AUX_PORT | KBD_ENABLE_AUX_INT)) { /* CONTROLLER ERROR */ disable_aux_dev(sc->kbdc); restore_controller(sc->kbdc, command_byte); log(LOG_ERR, "psm%d: failed to enable the aux interrupt (doopen).\n", unit); return (EIO); } return (0); } /* psm driver entry points */ #define endprobe(v) { if (bootverbose) \ --verbose; \ kbdc_set_device_mask(sc->kbdc, mask); \ kbdc_lock(sc->kbdc, FALSE); \ free(sc, M_DEVBUF); \ return (v); \ } static int psmprobe(device_t dev) { int unit = device_get_unit(dev); struct psm_softc *sc = device_get_softc(dev); - u_long port; - u_long flags; + uintptr_t port; + uintptr_t flags; int stat[3]; int command_byte; int mask; int i; #if 0 kbdc_debug(TRUE); #endif BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_PORT, &port); BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_FLAGS, &flags); sc->addr = port; sc->kbdc = kbdc_open(sc->addr); sc->config = flags & PSM_CONFIG_FLAGS; sc->flags = 0; if (bootverbose) ++verbose; device_set_desc(dev, "PS/2 Mouse"); if (!kbdc_lock(sc->kbdc, TRUE)) { printf("psm%d: unable to lock the controller.\n", unit); if (bootverbose) --verbose; return (ENXIO); } /* * NOTE: two bits in the command byte controls the operation of the * aux port (mouse port): the aux port disable bit (bit 5) and the aux * port interrupt (IRQ 12) enable bit (bit 2). */ /* discard anything left after the keyboard initialization */ empty_both_buffers(sc->kbdc, 10); /* save the current command byte; it will be used later */ mask = kbdc_get_device_mask(sc->kbdc) & ~KBD_AUX_CONTROL_BITS; command_byte = get_controller_command_byte(sc->kbdc); if (verbose) printf("psm%d: current command byte:%04x\n", unit, command_byte); if (command_byte == -1) { /* CONTROLLER ERROR */ printf("psm%d: unable to get the current command byte value.\n", unit); endprobe(ENXIO); } /* * disable the keyboard port while probing the aux port, which must be * enabled during this routine */ if (!set_controller_command_byte(sc->kbdc, KBD_KBD_CONTROL_BITS | KBD_AUX_CONTROL_BITS, KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT | KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { /* * this is CONTROLLER ERROR; I don't know how to recover * from this error... */ restore_controller(sc->kbdc, command_byte); printf("psm%d: unable to set the command byte.\n", unit); endprobe(ENXIO); } /* * NOTE: `test_aux_port()' is designed to return with zero if the aux * port exists and is functioning. However, some controllers appears * to respond with zero even when the aux port doesn't exist. (It may * be that this is only the case when the controller DOES have the aux * port but the port is not wired on the motherboard.) The keyboard * controllers without the port, such as the original AT, are * supporsed to return with an error code or simply time out. In any * case, we have to continue probing the port even when the controller * passes this test. * * XXX: some controllers erroneously return the error code 1 when * it has the perfectly functional aux port. We have to ignore this * error code. Even if the controller HAS error with the aux port, * it will be detected later... * XXX: another incompatible controller returns PSM_ACK (0xfa)... */ switch ((i = test_aux_port(sc->kbdc))) { case 1: /* ignore this error */ case PSM_ACK: if (verbose) printf("psm%d: strange result for test aux port (%d).\n", unit, i); /* fall though */ case 0: /* no error */ break; case -1: /* time out */ default: /* error */ recover_from_error(sc->kbdc); restore_controller(sc->kbdc, command_byte); if (verbose) printf("psm%d: the aux port is not functioning (%d).\n", unit, i); endprobe(ENXIO); } /* * NOTE: some controllers appears to hang the `keyboard' when the aux * port doesn't exist and `PSMC_RESET_DEV' is issued. */ if (!reset_aux_dev(sc->kbdc)) { recover_from_error(sc->kbdc); restore_controller(sc->kbdc, command_byte); if (verbose) printf("psm%d: failed to reset the aux device.\n", unit); endprobe(ENXIO); } /* * both the aux port and the aux device is functioning, see if the * device can be enabled. NOTE: when enabled, the device will start * sending data; we shall immediately disable the device once we know * the device can be enabled. */ if (!enable_aux_dev(sc->kbdc) || !disable_aux_dev(sc->kbdc)) { /* MOUSE ERROR */ restore_controller(sc->kbdc, command_byte); if (verbose) printf("psm%d: failed to enable the aux device.\n", unit); endprobe(ENXIO); } /* save the default values after reset */ if (get_mouse_status(sc->kbdc, stat, 0, 3) >= 3) { sc->dflt_mode.rate = sc->mode.rate = stat[2]; sc->dflt_mode.resolution = sc->mode.resolution = stat[1]; } else { sc->dflt_mode.rate = sc->mode.rate = -1; sc->dflt_mode.resolution = sc->mode.resolution = -1; } /* hardware information */ sc->hw.iftype = MOUSE_IF_PS2; /* verify the device is a mouse */ sc->hw.hwid = get_aux_id(sc->kbdc); if (!is_a_mouse(sc->hw.hwid)) { restore_controller(sc->kbdc, command_byte); if (verbose) printf("psm%d: unknown device type (%d).\n", unit, sc->hw.hwid); endprobe(ENXIO); } switch (sc->hw.hwid) { case PSM_BALLPOINT_ID: sc->hw.type = MOUSE_TRACKBALL; break; case PSM_MOUSE_ID: case PSM_INTELLI_ID: sc->hw.type = MOUSE_MOUSE; break; default: sc->hw.type = MOUSE_UNKNOWN; break; } /* # of buttons */ sc->hw.buttons = get_mouse_buttons(sc->kbdc); /* other parameters */ for (i = 0; vendortype[i].probefunc != NULL; ++i) { if ((*vendortype[i].probefunc)(sc)) { if (verbose >= 2) printf("psm%d: found %s\n", unit, model_name(vendortype[i].model)); break; } } sc->hw.model = vendortype[i].model; sc->dflt_mode.level = PSM_LEVEL_BASE; sc->dflt_mode.packetsize = MOUSE_PS2_PACKETSIZE; sc->dflt_mode.accelfactor = (sc->config & PSM_CONFIG_ACCEL) >> 4; if (sc->config & PSM_CONFIG_NOCHECKSYNC) sc->dflt_mode.syncmask[0] = 0; else sc->dflt_mode.syncmask[0] = vendortype[i].syncmask; sc->dflt_mode.syncmask[1] = 0; /* syncbits */ sc->mode = sc->dflt_mode; sc->mode.packetsize = vendortype[i].packetsize; /* set mouse parameters */ i = send_aux_command(sc->kbdc, PSMC_SET_DEFAULTS); if (verbose >= 2) printf("psm%d: SET_DEFAULTS return code:%04x\n", unit, i); if (sc->config & PSM_CONFIG_RESOLUTION) { sc->mode.resolution = set_mouse_resolution(sc->kbdc, (sc->config & PSM_CONFIG_RESOLUTION) - 1); } /* request a data packet and extract sync. bits */ if (get_mouse_status(sc->kbdc, stat, 1, 3) < 3) { printf("psm%d: failed to get data.\n", unit); sc->mode.syncmask[0] = 0; } else { sc->mode.syncmask[1] = stat[0] & sc->mode.syncmask[0]; /* syncbits */ /* the NetScroll Mouse will send three more bytes... Ignore them */ empty_aux_buffer(sc->kbdc, 5); } /* just check the status of the mouse */ /* * NOTE: XXX there are some arcane controller/mouse combinations out * there, which hung the controller unless there is data transmission * after ACK from the mouse. */ if (get_mouse_status(sc->kbdc, stat, 0, 3) < 3) { printf("psm%d: failed to get status.\n", unit); } else { /* * When in its native mode, some mice operate with different * default parameters than in the PS/2 compatible mode. */ sc->dflt_mode.rate = sc->mode.rate = stat[2]; sc->dflt_mode.resolution = sc->mode.resolution = stat[1]; } /* disable the aux port for now... */ if (!set_controller_command_byte(sc->kbdc, KBD_KBD_CONTROL_BITS | KBD_AUX_CONTROL_BITS, (command_byte & KBD_KBD_CONTROL_BITS) | KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { /* * this is CONTROLLER ERROR; I don't know the proper way to * recover from this error... */ restore_controller(sc->kbdc, command_byte); printf("psm%d: unable to set the command byte.\n", unit); endprobe(ENXIO); } /* done */ kbdc_set_device_mask(sc->kbdc, mask | KBD_AUX_CONTROL_BITS); kbdc_lock(sc->kbdc, FALSE); return (0); } static int psmattach(device_t dev) { int unit = device_get_unit(dev); struct psm_softc *sc = device_get_softc(dev); void *ih; struct resource *res; - u_long irq; + uintptr_t irq; int zero = 0; if (sc == NULL) /* shouldn't happen */ return (ENXIO); /* Setup initial state */ sc->state = PSM_VALID; /* Done */ #ifdef DEVFS sc->devfs_token = devfs_add_devswf(&psm_cdevsw, PSM_MKMINOR(unit, FALSE), DV_CHR, 0, 0, 0666, "psm%d", unit); sc->b_devfs_token = devfs_add_devswf(&psm_cdevsw, PSM_MKMINOR(unit, TRUE), DV_CHR, 0, 0, 0666, "bpsm%d", unit); #endif /* DEVFS */ #ifdef PSM_HOOKAPM sc->resumehook.ah_name = "PS/2 mouse"; sc->resumehook.ah_fun = psmresume; sc->resumehook.ah_arg = (void *)unit; sc->resumehook.ah_order = APM_MID_ORDER; apm_hook_establish(APM_HOOK_RESUME , &sc->resumehook); if (verbose) printf("psm%d: APM hooks installed.\n", unit); #endif /* PSM_HOOKAPM */ if (!verbose) { printf("psm%d: model %s, device ID %d\n", unit, model_name(sc->hw.model), sc->hw.hwid); } else { printf("psm%d: model %s, device ID %d, %d buttons\n", unit, model_name(sc->hw.model), sc->hw.hwid, sc->hw.buttons); printf("psm%d: config:%08x, flags:%08x, packet size:%d\n", unit, sc->config, sc->flags, sc->mode.packetsize); printf("psm%d: syncmask:%02x, syncbits:%02x\n", unit, sc->mode.syncmask[0], sc->mode.syncmask[1]); } if (bootverbose) --verbose; BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_IRQ, &irq); res = bus_alloc_resource(dev, SYS_RES_IRQ, &zero, irq, irq, 1, RF_SHAREABLE | RF_ACTIVE); BUS_SETUP_INTR(device_get_parent(dev), dev, res, psmintr, sc, &ih); return (0); } static int psmopen(dev_t dev, int flag, int fmt, struct proc *p) { int unit = PSM_UNIT(dev); struct psm_softc *sc; int command_byte; int err; int s; /* Validate unit number */ if (unit >= NPSM) return (ENXIO); /* Get device data */ sc = PSM_SOFTC(unit); if ((sc == NULL) || (sc->state & PSM_VALID) == 0) /* the device is no longer valid/functioning */ return (ENXIO); /* Disallow multiple opens */ if (sc->state & PSM_OPEN) return (EBUSY); device_busy(devclass_get_device(psm_devclass, unit)); /* Initialize state */ sc->rsel.si_flags = 0; sc->rsel.si_pid = 0; sc->mode.level = sc->dflt_mode.level; sc->mode.protocol = sc->dflt_mode.protocol; /* flush the event queue */ sc->queue.count = 0; sc->queue.head = 0; sc->queue.tail = 0; sc->status.flags = 0; sc->status.button = 0; sc->status.obutton = 0; sc->status.dx = 0; sc->status.dy = 0; sc->status.dz = 0; sc->button = 0; /* empty input buffer */ bzero(sc->ipacket, sizeof(sc->ipacket)); sc->inputbytes = 0; /* don't let timeout routines in the keyboard driver to poll the kbdc */ if (!kbdc_lock(sc->kbdc, TRUE)) return (EIO); /* save the current controller command byte */ s = spltty(); command_byte = get_controller_command_byte(sc->kbdc); /* enable the aux port and temporalily disable the keyboard */ if ((command_byte == -1) || !set_controller_command_byte(sc->kbdc, kbdc_get_device_mask(sc->kbdc), KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT | KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { /* CONTROLLER ERROR; do you know how to get out of this? */ kbdc_lock(sc->kbdc, FALSE); splx(s); log(LOG_ERR, "psm%d: unable to set the command byte (psmopen).\n", unit); return (EIO); } /* * Now that the keyboard controller is told not to generate * the keyboard and mouse interrupts, call `splx()' to allow * the other tty interrupts. The clock interrupt may also occur, * but timeout routines will be blocked by the poll flag set * via `kbdc_lock()' */ splx(s); /* enable the mouse device */ err = doopen(unit, command_byte); /* done */ if (err == 0) sc->state |= PSM_OPEN; kbdc_lock(sc->kbdc, FALSE); return (err); } static int psmclose(dev_t dev, int flag, int fmt, struct proc *p) { int unit = PSM_UNIT(dev); struct psm_softc *sc = PSM_SOFTC(unit); int stat[3]; int command_byte; int s; /* don't let timeout routines in the keyboard driver to poll the kbdc */ if (!kbdc_lock(sc->kbdc, TRUE)) return (EIO); /* save the current controller command byte */ s = spltty(); command_byte = get_controller_command_byte(sc->kbdc); if (command_byte == -1) { kbdc_lock(sc->kbdc, FALSE); splx(s); return (EIO); } /* disable the aux interrupt and temporalily disable the keyboard */ if (!set_controller_command_byte(sc->kbdc, kbdc_get_device_mask(sc->kbdc), KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT | KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { log(LOG_ERR, "psm%d: failed to disable the aux int (psmclose).\n", PSM_UNIT(dev)); /* CONTROLLER ERROR; * NOTE: we shall force our way through. Because the only * ill effect we shall see is that we may not be able * to read ACK from the mouse, and it doesn't matter much * so long as the mouse will accept the DISABLE command. */ } splx(s); /* remove anything left in the output buffer */ empty_aux_buffer(sc->kbdc, 10); /* disable the aux device, port and interrupt */ if (sc->state & PSM_VALID) { if (!disable_aux_dev(sc->kbdc)) { /* MOUSE ERROR; * NOTE: we don't return error and continue, pretending * we have successfully disabled the device. It's OK because * the interrupt routine will discard any data from the mouse * hereafter. */ log(LOG_ERR, "psm%d: failed to disable the device (psmclose).\n", PSM_UNIT(dev)); } if (get_mouse_status(sc->kbdc, stat, 0, 3) < 3) log(LOG_DEBUG, "psm%d: failed to get status (psmclose).\n", PSM_UNIT(dev)); } if (!set_controller_command_byte(sc->kbdc, kbdc_get_device_mask(sc->kbdc), (command_byte & KBD_KBD_CONTROL_BITS) | KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { /* CONTROLLER ERROR; * we shall ignore this error; see the above comment. */ log(LOG_ERR, "psm%d: failed to disable the aux port (psmclose).\n", PSM_UNIT(dev)); } /* remove anything left in the output buffer */ empty_aux_buffer(sc->kbdc, 10); /* close is almost always successful */ sc->state &= ~PSM_OPEN; kbdc_lock(sc->kbdc, FALSE); device_unbusy(devclass_get_device(psm_devclass, unit)); return (0); } static int tame_mouse(struct psm_softc *sc, mousestatus_t *status, unsigned char *buf) { static unsigned char butmapps2[8] = { 0, MOUSE_PS2_BUTTON1DOWN, MOUSE_PS2_BUTTON2DOWN, MOUSE_PS2_BUTTON1DOWN | MOUSE_PS2_BUTTON2DOWN, MOUSE_PS2_BUTTON3DOWN, MOUSE_PS2_BUTTON1DOWN | MOUSE_PS2_BUTTON3DOWN, MOUSE_PS2_BUTTON2DOWN | MOUSE_PS2_BUTTON3DOWN, MOUSE_PS2_BUTTON1DOWN | MOUSE_PS2_BUTTON2DOWN | MOUSE_PS2_BUTTON3DOWN, }; static unsigned char butmapmsc[8] = { MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON2UP | MOUSE_MSC_BUTTON3UP, MOUSE_MSC_BUTTON2UP | MOUSE_MSC_BUTTON3UP, MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON3UP, MOUSE_MSC_BUTTON3UP, MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON2UP, MOUSE_MSC_BUTTON2UP, MOUSE_MSC_BUTTON1UP, 0, }; int mapped; int i; if (sc->mode.level == PSM_LEVEL_BASE) { mapped = status->button & ~MOUSE_BUTTON4DOWN; if (status->button & MOUSE_BUTTON4DOWN) mapped |= MOUSE_BUTTON1DOWN; status->button = mapped; buf[0] = MOUSE_PS2_SYNC | butmapps2[mapped & MOUSE_STDBUTTONS]; i = max(min(status->dx, 255), -256); if (i < 0) buf[0] |= MOUSE_PS2_XNEG; buf[1] = i; i = max(min(status->dy, 255), -256); if (i < 0) buf[0] |= MOUSE_PS2_YNEG; buf[2] = i; return MOUSE_PS2_PACKETSIZE; } else if (sc->mode.level == PSM_LEVEL_STANDARD) { buf[0] = MOUSE_MSC_SYNC | butmapmsc[status->button & MOUSE_STDBUTTONS]; i = max(min(status->dx, 255), -256); buf[1] = i >> 1; buf[3] = i - buf[1]; i = max(min(status->dy, 255), -256); buf[2] = i >> 1; buf[4] = i - buf[2]; i = max(min(status->dz, 127), -128); buf[5] = (i >> 1) & 0x7f; buf[6] = (i - (i >> 1)) & 0x7f; buf[7] = (~status->button >> 3) & 0x7f; return MOUSE_SYS_PACKETSIZE; } return sc->inputbytes;; } static int psmread(dev_t dev, struct uio *uio, int flag) { register struct psm_softc *sc = PSM_SOFTC(PSM_UNIT(dev)); unsigned char buf[PSM_SMALLBUFSIZE]; int error = 0; int s; int l; if ((sc->state & PSM_VALID) == 0) return EIO; /* block until mouse activity occured */ s = spltty(); while (sc->queue.count <= 0) { if (PSM_NBLOCKIO(dev)) { splx(s); return EWOULDBLOCK; } sc->state |= PSM_ASLP; error = tsleep((caddr_t) sc, PZERO | PCATCH, "psmrea", 0); sc->state &= ~PSM_ASLP; if (error) { splx(s); return error; } else if ((sc->state & PSM_VALID) == 0) { /* the device disappeared! */ splx(s); return EIO; } } splx(s); /* copy data to the user land */ while ((sc->queue.count > 0) && (uio->uio_resid > 0)) { s = spltty(); l = min(sc->queue.count, uio->uio_resid); if (l > sizeof(buf)) l = sizeof(buf); if (l > sizeof(sc->queue.buf) - sc->queue.head) { bcopy(&sc->queue.buf[sc->queue.head], &buf[0], sizeof(sc->queue.buf) - sc->queue.head); bcopy(&sc->queue.buf[0], &buf[sizeof(sc->queue.buf) - sc->queue.head], l - (sizeof(sc->queue.buf) - sc->queue.head)); } else { bcopy(&sc->queue.buf[sc->queue.head], &buf[0], l); } sc->queue.count -= l; sc->queue.head = (sc->queue.head + l) % sizeof(sc->queue.buf); splx(s); error = uiomove(buf, l, uio); if (error) break; } return error; } static int block_mouse_data(struct psm_softc *sc, int *c) { int s; if (!kbdc_lock(sc->kbdc, TRUE)) return EIO; s = spltty(); *c = get_controller_command_byte(sc->kbdc); if ((*c == -1) || !set_controller_command_byte(sc->kbdc, kbdc_get_device_mask(sc->kbdc), KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT | KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { /* this is CONTROLLER ERROR */ splx(s); kbdc_lock(sc->kbdc, FALSE); return EIO; } /* * The device may be in the middle of status data transmission. * The transmission will be interrupted, thus, incomplete status * data must be discarded. Although the aux interrupt is disabled * at the keyboard controller level, at most one aux interrupt * may have already been pending and a data byte is in the * output buffer; throw it away. Note that the second argument * to `empty_aux_buffer()' is zero, so that the call will just * flush the internal queue. * `psmintr()' will be invoked after `splx()' if an interrupt is * pending; it will see no data and returns immediately. */ empty_aux_buffer(sc->kbdc, 0); /* flush the queue */ read_aux_data_no_wait(sc->kbdc); /* throw away data if any */ sc->inputbytes = 0; splx(s); return 0; } static int unblock_mouse_data(struct psm_softc *sc, int c) { int error = 0; /* * We may have seen a part of status data during `set_mouse_XXX()'. * they have been queued; flush it. */ empty_aux_buffer(sc->kbdc, 0); /* restore ports and interrupt */ if (!set_controller_command_byte(sc->kbdc, kbdc_get_device_mask(sc->kbdc), c & (KBD_KBD_CONTROL_BITS | KBD_AUX_CONTROL_BITS))) { /* CONTROLLER ERROR; this is serious, we may have * been left with the inaccessible keyboard and * the disabled mouse interrupt. */ error = EIO; } kbdc_lock(sc->kbdc, FALSE); return error; } static int psmioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p) { struct psm_softc *sc = PSM_SOFTC(PSM_UNIT(dev)); mousemode_t mode; mousestatus_t status; #if (defined(MOUSE_GETVARS)) mousevar_t *var; #endif mousedata_t *data; int stat[3]; int command_byte; int error = 0; int s; /* Perform IOCTL command */ switch (cmd) { case OLD_MOUSE_GETHWINFO: s = spltty(); ((old_mousehw_t *)addr)->buttons = sc->hw.buttons; ((old_mousehw_t *)addr)->iftype = sc->hw.iftype; ((old_mousehw_t *)addr)->type = sc->hw.type; ((old_mousehw_t *)addr)->hwid = sc->hw.hwid; splx(s); break; case MOUSE_GETHWINFO: s = spltty(); *(mousehw_t *)addr = sc->hw; if (sc->mode.level == PSM_LEVEL_BASE) ((mousehw_t *)addr)->model = MOUSE_MODEL_GENERIC; splx(s); break; case OLD_MOUSE_GETMODE: s = spltty(); switch (sc->mode.level) { case PSM_LEVEL_BASE: ((old_mousemode_t *)addr)->protocol = MOUSE_PROTO_PS2; break; case PSM_LEVEL_STANDARD: ((old_mousemode_t *)addr)->protocol = MOUSE_PROTO_SYSMOUSE; break; case PSM_LEVEL_NATIVE: ((old_mousemode_t *)addr)->protocol = MOUSE_PROTO_PS2; break; } ((old_mousemode_t *)addr)->rate = sc->mode.rate; ((old_mousemode_t *)addr)->resolution = sc->mode.resolution; ((old_mousemode_t *)addr)->accelfactor = sc->mode.accelfactor; splx(s); break; case MOUSE_GETMODE: s = spltty(); *(mousemode_t *)addr = sc->mode; ((mousemode_t *)addr)->resolution = MOUSE_RES_LOW - sc->mode.resolution; switch (sc->mode.level) { case PSM_LEVEL_BASE: ((mousemode_t *)addr)->protocol = MOUSE_PROTO_PS2; ((mousemode_t *)addr)->packetsize = MOUSE_PS2_PACKETSIZE; break; case PSM_LEVEL_STANDARD: ((mousemode_t *)addr)->protocol = MOUSE_PROTO_SYSMOUSE; ((mousemode_t *)addr)->packetsize = MOUSE_SYS_PACKETSIZE; ((mousemode_t *)addr)->syncmask[0] = MOUSE_SYS_SYNCMASK; ((mousemode_t *)addr)->syncmask[1] = MOUSE_SYS_SYNC; break; case PSM_LEVEL_NATIVE: /* FIXME: this isn't quite correct... XXX */ ((mousemode_t *)addr)->protocol = MOUSE_PROTO_PS2; break; } splx(s); break; case OLD_MOUSE_SETMODE: case MOUSE_SETMODE: if (cmd == OLD_MOUSE_SETMODE) { mode.rate = ((old_mousemode_t *)addr)->rate; /* * resolution old I/F new I/F * default 0 0 * low 1 -2 * medium low 2 -3 * medium high 3 -4 * high 4 -5 */ if (((old_mousemode_t *)addr)->resolution > 0) mode.resolution = -((old_mousemode_t *)addr)->resolution - 1; mode.accelfactor = ((old_mousemode_t *)addr)->accelfactor; mode.level = -1; } else { mode = *(mousemode_t *)addr; } /* adjust and validate parameters. */ if (mode.rate > UCHAR_MAX) return EINVAL; if (mode.rate == 0) mode.rate = sc->dflt_mode.rate; else if (mode.rate == -1) /* don't change the current setting */ ; else if (mode.rate < 0) return EINVAL; if (mode.resolution >= UCHAR_MAX) return EINVAL; if (mode.resolution >= 200) mode.resolution = MOUSE_RES_HIGH; else if (mode.resolution >= 100) mode.resolution = MOUSE_RES_MEDIUMHIGH; else if (mode.resolution >= 50) mode.resolution = MOUSE_RES_MEDIUMLOW; else if (mode.resolution > 0) mode.resolution = MOUSE_RES_LOW; if (mode.resolution == MOUSE_RES_DEFAULT) mode.resolution = sc->dflt_mode.resolution; else if (mode.resolution == -1) /* don't change the current setting */ ; else if (mode.resolution < 0) /* MOUSE_RES_LOW/MEDIUM/HIGH */ mode.resolution = MOUSE_RES_LOW - mode.resolution; if (mode.level == -1) /* don't change the current setting */ mode.level = sc->mode.level; else if ((mode.level < PSM_LEVEL_MIN) || (mode.level > PSM_LEVEL_MAX)) return EINVAL; if (mode.accelfactor == -1) /* don't change the current setting */ mode.accelfactor = sc->mode.accelfactor; else if (mode.accelfactor < 0) return EINVAL; /* don't allow anybody to poll the keyboard controller */ error = block_mouse_data(sc, &command_byte); if (error) return error; /* set mouse parameters */ if (mode.rate > 0) mode.rate = set_mouse_sampling_rate(sc->kbdc, mode.rate); if (mode.resolution >= 0) mode.resolution = set_mouse_resolution(sc->kbdc, mode.resolution); set_mouse_scaling(sc->kbdc, 1); get_mouse_status(sc->kbdc, stat, 0, 3); s = spltty(); sc->mode.rate = mode.rate; sc->mode.resolution = mode.resolution; sc->mode.accelfactor = mode.accelfactor; sc->mode.level = mode.level; splx(s); unblock_mouse_data(sc, command_byte); break; case MOUSE_GETLEVEL: *(int *)addr = sc->mode.level; break; case MOUSE_SETLEVEL: if ((*(int *)addr < PSM_LEVEL_MIN) || (*(int *)addr > PSM_LEVEL_MAX)) return EINVAL; sc->mode.level = *(int *)addr; 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; #if (defined(MOUSE_GETVARS)) case MOUSE_GETVARS: var = (mousevar_t *)addr; bzero(var, sizeof(*var)); s = spltty(); var->var[0] = MOUSE_VARS_PS2_SIG; var->var[1] = sc->config; var->var[2] = sc->flags; splx(s); break; case MOUSE_SETVARS: return ENODEV; #endif /* MOUSE_GETVARS */ case MOUSE_READSTATE: case MOUSE_READDATA: data = (mousedata_t *)addr; if (data->len > sizeof(data->buf)/sizeof(data->buf[0])) return EINVAL; error = block_mouse_data(sc, &command_byte); if (error) return error; if ((data->len = get_mouse_status(sc->kbdc, data->buf, (cmd == MOUSE_READDATA) ? 1 : 0, data->len)) <= 0) error = EIO; unblock_mouse_data(sc, command_byte); break; #if (defined(MOUSE_SETRESOLUTION)) case MOUSE_SETRESOLUTION: mode.resolution = *(int *)addr; if (mode.resolution >= UCHAR_MAX) return EINVAL; else if (mode.resolution >= 200) mode.resolution = MOUSE_RES_HIGH; else if (mode.resolution >= 100) mode.resolution = MOUSE_RES_MEDIUMHIGH; else if (mode.resolution >= 50) mode.resolution = MOUSE_RES_MEDIUMLOW; else if (mode.resolution > 0) mode.resolution = MOUSE_RES_LOW; if (mode.resolution == MOUSE_RES_DEFAULT) mode.resolution = sc->dflt_mode.resolution; else if (mode.resolution == -1) mode.resolution = sc->mode.resolution; else if (mode.resolution < 0) /* MOUSE_RES_LOW/MEDIUM/HIGH */ mode.resolution = MOUSE_RES_LOW - mode.resolution; error = block_mouse_data(sc, &command_byte); if (error) return error; sc->mode.resolution = set_mouse_resolution(sc->kbdc, mode.resolution); if (sc->mode.resolution != mode.resolution) error = EIO; unblock_mouse_data(sc, command_byte); break; #endif /* MOUSE_SETRESOLUTION */ #if (defined(MOUSE_SETRATE)) case MOUSE_SETRATE: mode.rate = *(int *)addr; if (mode.rate > UCHAR_MAX) return EINVAL; if (mode.rate == 0) mode.rate = sc->dflt_mode.rate; else if (mode.rate < 0) mode.rate = sc->mode.rate; error = block_mouse_data(sc, &command_byte); if (error) return error; sc->mode.rate = set_mouse_sampling_rate(sc->kbdc, mode.rate); if (sc->mode.rate != mode.rate) error = EIO; unblock_mouse_data(sc, command_byte); break; #endif /* MOUSE_SETRATE */ #if (defined(MOUSE_SETSCALING)) case MOUSE_SETSCALING: if ((*(int *)addr <= 0) || (*(int *)addr > 2)) return EINVAL; error = block_mouse_data(sc, &command_byte); if (error) return error; if (!set_mouse_scaling(sc->kbdc, *(int *)addr)) error = EIO; unblock_mouse_data(sc, command_byte); break; #endif /* MOUSE_SETSCALING */ #if (defined(MOUSE_GETHWID)) case MOUSE_GETHWID: error = block_mouse_data(sc, &command_byte); if (error) return error; sc->hw.hwid = get_aux_id(sc->kbdc); *(int *)addr = sc->hw.hwid; unblock_mouse_data(sc, command_byte); break; #endif /* MOUSE_GETHWID */ default: return ENOTTY; } return error; } static void psmintr(void *arg) { /* * the table to turn PS/2 mouse button bits (MOUSE_PS2_BUTTON?DOWN) * into `mousestatus' button bits (MOUSE_BUTTON?DOWN). */ static int butmap[8] = { 0, MOUSE_BUTTON1DOWN, MOUSE_BUTTON3DOWN, MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN, MOUSE_BUTTON2DOWN, MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN, MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN, MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN }; register struct psm_softc *sc = arg; mousestatus_t ms; int x, y, z; int c; int l; /* read until there is nothing to read */ while((c = read_aux_data_no_wait(sc->kbdc)) != -1) { /* discard the byte if the device is not open */ if ((sc->state & PSM_OPEN) == 0) continue; /* * Check sync bits. We check for overflow bits and the bit 3 * for most mice. True, the code doesn't work if overflow * condition occurs. But we expect it rarely happens... */ if ((sc->inputbytes == 0) && ((c & sc->mode.syncmask[0]) != sc->mode.syncmask[1])) { log(LOG_DEBUG, "psmintr: out of sync (%04x != %04x).\n", c & sc->mode.syncmask[0], sc->mode.syncmask[1]); continue; } sc->ipacket[sc->inputbytes++] = c; if (sc->inputbytes < sc->mode.packetsize) continue; #if 0 log(LOG_DEBUG, "psmintr: %02x %02x %02x %02x %02x %02x\n", sc->ipacket[0], sc->ipacket[1], sc->ipacket[2], sc->ipacket[3], sc->ipacket[4], sc->ipacket[5]); #endif c = sc->ipacket[0]; /* * A kludge for Kensington device! * The MSB of the horizontal count appears to be stored in * a strange place. This kludge doesn't affect other mice * because the bit is the overflow bit which is, in most cases, * expected to be zero when we reach here. XXX */ sc->ipacket[1] |= (c & MOUSE_PS2_XOVERFLOW) ? 0x80 : 0; /* ignore the overflow bits... */ x = (c & MOUSE_PS2_XNEG) ? sc->ipacket[1] - 256 : sc->ipacket[1]; y = (c & MOUSE_PS2_YNEG) ? sc->ipacket[2] - 256 : sc->ipacket[2]; z = 0; ms.obutton = sc->button; /* previous button state */ ms.button = butmap[c & MOUSE_PS2_BUTTONS]; switch (sc->hw.model) { case MOUSE_MODEL_INTELLI: case MOUSE_MODEL_NET: /* wheel data is in the fourth byte */ z = (char)sc->ipacket[3]; break; case MOUSE_MODEL_MOUSEMANPLUS: if ((c & ~MOUSE_PS2_BUTTONS) == 0xc8) { /* the extended data packet encodes button and wheel events */ x = y = 0; z = (sc->ipacket[1] & MOUSE_PS2PLUS_ZNEG) ? (sc->ipacket[2] & 0x0f) - 16 : (sc->ipacket[2] & 0x0f); ms.button |= (sc->ipacket[2] & MOUSE_PS2PLUS_BUTTON4DOWN) ? MOUSE_BUTTON4DOWN : 0; } else { /* preserve button states */ ms.button |= ms.obutton & MOUSE_EXTBUTTONS; } break; case MOUSE_MODEL_GLIDEPOINT: /* `tapping' action */ ms.button |= ((c & MOUSE_PS2_TAP)) ? 0 : MOUSE_BUTTON4DOWN; break; case MOUSE_MODEL_NETSCROLL: /* three addtional bytes encode button and wheel events */ ms.button |= (sc->ipacket[3] & MOUSE_PS2_BUTTON3DOWN) ? MOUSE_BUTTON4DOWN : 0; z = (sc->ipacket[3] & MOUSE_PS2_XNEG) ? sc->ipacket[4] - 256 : sc->ipacket[4]; break; case MOUSE_MODEL_THINK: /* the fourth button state in the first byte */ ms.button |= (c & MOUSE_PS2_TAP) ? MOUSE_BUTTON4DOWN : 0; break; case MOUSE_MODEL_GENERIC: default: break; } /* scale values */ if (sc->mode.accelfactor >= 1) { if (x != 0) { x = x * x / sc->mode.accelfactor; if (x == 0) x = 1; if (c & MOUSE_PS2_XNEG) x = -x; } if (y != 0) { y = y * y / sc->mode.accelfactor; if (y == 0) y = 1; if (c & MOUSE_PS2_YNEG) y = -y; } } ms.dx = x; ms.dy = y; ms.dz = z; ms.flags = ((x || y || z) ? MOUSE_POSCHANGED : 0) | (ms.obutton ^ ms.button); if (sc->mode.level < PSM_LEVEL_NATIVE) sc->inputbytes = tame_mouse(sc, &ms, sc->ipacket); sc->status.flags |= ms.flags; sc->status.dx += ms.dx; sc->status.dy += ms.dy; sc->status.dz += ms.dz; sc->status.button = ms.button; sc->button = ms.button; /* queue data */ if (sc->queue.count + sc->inputbytes < sizeof(sc->queue.buf)) { l = min(sc->inputbytes, sizeof(sc->queue.buf) - sc->queue.tail); bcopy(&sc->ipacket[0], &sc->queue.buf[sc->queue.tail], l); if (sc->inputbytes > l) bcopy(&sc->ipacket[l], &sc->queue.buf[0], sc->inputbytes - l); sc->queue.tail = (sc->queue.tail + sc->inputbytes) % sizeof(sc->queue.buf); sc->queue.count += sc->inputbytes; } sc->inputbytes = 0; if (sc->state & PSM_ASLP) { sc->state &= ~PSM_ASLP; wakeup((caddr_t) sc); } selwakeup(&sc->rsel); } } static int psmpoll(dev_t dev, int events, struct proc *p) { struct psm_softc *sc = PSM_SOFTC(PSM_UNIT(dev)); int s; int revents = 0; /* Return true if a mouse event available */ s = spltty(); if (events & (POLLIN | POLLRDNORM)) if (sc->queue.count > 0) revents |= events & (POLLIN | POLLRDNORM); else selrecord(p, &sc->rsel); splx(s); return (revents); } /* vendor/model specific routines */ static int mouse_id_proc1(KBDC kbdc, int res, int scale, int *status) { if (set_mouse_resolution(kbdc, res) != res) return FALSE; if (set_mouse_scaling(kbdc, scale) && set_mouse_scaling(kbdc, scale) && set_mouse_scaling(kbdc, scale) && (get_mouse_status(kbdc, status, 0, 3) >= 3)) return TRUE; return FALSE; } #if notyet /* Logitech MouseMan Cordless II */ static int enable_lcordless(struct psm_softc *sc) { int status[3]; int ch; if (!mouse_id_proc1(sc->kbdc, PSMD_RES_HIGH, 2, status)) return FALSE; if (status[1] == PSMD_RES_HIGH) return FALSE; ch = (status[0] & 0x07) - 1; /* channel # */ if ((ch <= 0) || (ch > 4)) return FALSE; /* * status[1]: always one? * status[2]: battery status? (0-100) */ return TRUE; } #endif /* notyet */ /* Genius NetScroll Mouse */ static int enable_groller(struct psm_softc *sc) { int status[3]; /* * The special sequence to enable the fourth button and the * roller. Immediately after this sequence check status bytes. * if the mouse is NetScroll, the second and the third bytes are * '3' and 'D'. */ /* * If the mouse is an ordinary PS/2 mouse, the status bytes should * look like the following. * * byte 1 bit 7 always 0 * bit 6 stream mode (0) * bit 5 disabled (0) * bit 4 1:1 scaling (0) * bit 3 always 0 * bit 0-2 button status * byte 2 resolution (PSMD_RES_HIGH) * byte 3 report rate (?) */ if (!mouse_id_proc1(sc->kbdc, PSMD_RES_HIGH, 1, status)) return FALSE; if ((status[1] != '3') || (status[2] != 'D')) return FALSE; /* FIXME!! */ sc->hw.buttons = get_mouse_buttons(sc->kbdc); sc->hw.buttons = 4; return TRUE; } /* Genius NetMouse/NetMouse Pro */ static int enable_gmouse(struct psm_softc *sc) { int status[3]; /* * The special sequence to enable the middle, "rubber" button. * Immediately after this sequence check status bytes. * if the mouse is NetMouse, NetMouse Pro, or ASCII MIE Mouse, * the second and the third bytes are '3' and 'U'. * NOTE: NetMouse reports that it has three buttons although it has * two buttons and a rubber button. NetMouse Pro and MIE Mouse * say they have three buttons too and they do have a button on the * side... */ if (!mouse_id_proc1(sc->kbdc, PSMD_RES_HIGH, 1, status)) return FALSE; if ((status[1] != '3') || (status[2] != 'U')) return FALSE; return TRUE; } /* ALPS GlidePoint */ static int enable_aglide(struct psm_softc *sc) { int status[3]; /* * The special sequence to obtain ALPS GlidePoint specific * information. Immediately after this sequence, status bytes will * contain something interesting. * NOTE: ALPS produces several models of GlidePoint. Some of those * do not respond to this sequence, thus, cannot be detected this way. */ if (!mouse_id_proc1(sc->kbdc, PSMD_RES_LOW, 2, status)) return FALSE; if ((status[0] & 0x10) || (status[1] == PSMD_RES_LOW)) return FALSE; return TRUE; } /* Kensington ThinkingMouse/Trackball */ static int enable_kmouse(struct psm_softc *sc) { static unsigned char rate[] = { 20, 60, 40, 20, 20, 60, 40, 20, 20 }; KBDC kbdc = sc->kbdc; int status[3]; int id1; int id2; int i; id1 = get_aux_id(kbdc); if (set_mouse_sampling_rate(kbdc, 10) != 10) return FALSE; /* * The device is now in the native mode? It returns a different * ID value... */ id2 = get_aux_id(kbdc); if ((id1 == id2) || (id2 != 2)) return FALSE; if (set_mouse_resolution(kbdc, PSMD_RES_LOW) != PSMD_RES_LOW) return FALSE; #if PSM_DEBUG >= 2 /* at this point, resolution is LOW, sampling rate is 10/sec */ if (get_mouse_status(kbdc, status, 0, 3) < 3) return FALSE; #endif /* * The special sequence to enable the third and fourth buttons. * Otherwise they behave like the first and second buttons. */ for (i = 0; i < sizeof(rate)/sizeof(rate[0]); ++i) { if (set_mouse_sampling_rate(kbdc, rate[i]) != rate[i]) return FALSE; } /* * At this point, the device is using default resolution and * sampling rate for the native mode. */ if (get_mouse_status(kbdc, status, 0, 3) < 3) return FALSE; if ((status[1] == PSMD_RES_LOW) || (status[2] == rate[i - 1])) return FALSE; /* the device appears be enabled by this sequence, diable it for now */ disable_aux_dev(kbdc); empty_aux_buffer(kbdc, 5); return TRUE; } /* Logitech MouseMan+/FirstMouse+ */ static int enable_mmanplus(struct psm_softc *sc) { static char res[] = { -1, PSMD_RES_LOW, PSMD_RES_HIGH, PSMD_RES_MEDIUM_HIGH, PSMD_RES_MEDIUM_LOW, -1, PSMD_RES_HIGH, PSMD_RES_MEDIUM_LOW, PSMD_RES_MEDIUM_HIGH, PSMD_RES_HIGH, }; KBDC kbdc = sc->kbdc; int data[3]; int i; /* the special sequence to enable the fourth button and the roller. */ for (i = 0; i < sizeof(res)/sizeof(res[0]); ++i) { if (res[i] < 0) { if (!set_mouse_scaling(kbdc, 1)) return FALSE; } else { if (set_mouse_resolution(kbdc, res[i]) != res[i]) return FALSE; } } if (get_mouse_status(kbdc, data, 1, 3) < 3) return FALSE; /* * MouseMan+ and FirstMouse+ return following data. * * byte 1 0xc8 * byte 2 ?? (MouseMan+:0xc2, FirstMouse+:0xc6) * byte 3 model ID? MouseMan+:0x50, FirstMouse+:0x51 */ if ((data[0] & ~MOUSE_PS2_BUTTONS) != 0xc8) return FALSE; /* * MouseMan+ (or FirstMouse+) is now in its native mode, in which * the wheel and the fourth button events are encoded in the * special data packet. The mouse may be put in the IntelliMouse mode * if it is initialized by the IntelliMouse's method. */ return TRUE; } /* MS IntelliMouse */ static int enable_msintelli(struct psm_softc *sc) { /* * Logitech MouseMan+ and FirstMouse+ will also respond to this * probe routine and act like IntelliMouse. */ static unsigned char rate[] = { 200, 100, 80, }; KBDC kbdc = sc->kbdc; int id; int i; /* the special sequence to enable the third button and the roller. */ for (i = 0; i < sizeof(rate)/sizeof(rate[0]); ++i) { if (set_mouse_sampling_rate(kbdc, rate[i]) != rate[i]) return FALSE; } /* the device will give the genuine ID only after the above sequence */ id = get_aux_id(kbdc); if (id != PSM_INTELLI_ID) return FALSE; sc->hw.hwid = id; sc->hw.buttons = 3; return TRUE; } #ifdef PSM_HOOKAPM static int psmresume(void *dummy) { struct psm_softc *sc = psm_softc[(int)dummy]; int unit = (int)dummy; int err = 0; int s; int c; if (verbose >= 2) log(LOG_NOTICE, "psm%d: APM resume hook called.\n", unit); /* don't let anybody mess with the aux device */ if (!kbdc_lock(sc->kbdc, TRUE)) return (EIO); s = spltty(); /* save the current controller command byte */ empty_both_buffers(sc->kbdc, 10); c = get_controller_command_byte(sc->kbdc); if (verbose >= 2) log(LOG_DEBUG, "psm%d: current command byte: %04x (psmresume).\n", unit, c); /* enable the aux port but disable the aux interrupt and the keyboard */ if ((c == -1) || !set_controller_command_byte(sc->kbdc, kbdc_get_device_mask(sc->kbdc), KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT | KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { /* CONTROLLER ERROR */ splx(s); kbdc_lock(sc->kbdc, FALSE); log(LOG_ERR, "psm%d: unable to set the command byte (psmresume).\n", unit); return (EIO); } /* flush any data */ if (sc->state & PSM_VALID) { disable_aux_dev(sc->kbdc); /* this may fail; but never mind... */ empty_aux_buffer(sc->kbdc, 10); } sc->inputbytes = 0; #ifdef PSM_RESETAFTERSUSPEND /* try to detect the aux device; are you still there? */ if (reinitialize(unit, &sc->mode)) { /* yes */ sc->state |= PSM_VALID; } else { /* the device has gone! */ restore_controller(sc->kbdc, c); sc->state &= ~PSM_VALID; log(LOG_ERR, "psm%d: the aux device has gone! (psmresume).\n", unit); err = ENXIO; } #endif /* PSM_RESETAFTERSUSPEND */ splx(s); /* restore the driver state */ if ((sc->state & PSM_OPEN) && (err == 0)) { /* enable the aux device and the port again */ err = doopen(unit, c); if (err != 0) log(LOG_ERR, "psm%d: failed to enable the device (psmresume).\n", unit); } else { /* restore the keyboard port and disable the aux port */ if (!set_controller_command_byte(sc->kbdc, kbdc_get_device_mask(sc->kbdc), (c & KBD_KBD_CONTROL_BITS) | KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { /* CONTROLLER ERROR */ log(LOG_ERR, "psm%d: failed to disable the aux port (psmresume).\n", unit); err = EIO; } } /* done */ kbdc_lock(sc->kbdc, FALSE); if ((sc->state & PSM_ASLP) && !(sc->state & PSM_VALID)) { /* * Release the blocked process; it must be notified that the device * cannot be accessed anymore. */ sc->state &= ~PSM_ASLP; wakeup((caddr_t)sc); } if (verbose >= 2) log(LOG_DEBUG, "psm%d: APM resume hook exiting.\n", unit); return (err); } #endif /* PSM_HOOKAPM */ CDEV_DRIVER_MODULE(psm, atkbdc, psm_driver, psm_devclass, CDEV_MAJOR, psm_cdevsw, 0, 0); #endif /* NPSM > 0 */ Index: head/sys/dev/cs/if_cs.c =================================================================== --- head/sys/dev/cs/if_cs.c (revision 45719) +++ head/sys/dev/cs/if_cs.c (revision 45720) @@ -1,1399 +1,1396 @@ /* * Copyright (c) 1997,1998 Maxim Bolotin and Oleg Sharoiko. * 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 unmodified, 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. * */ /* - * $Id: if_cs.c,v 1.8 1999/01/12 00:27:43 eivind Exp $ + * $Id: if_cs.c,v 1.9 1999/01/28 01:59:53 dillon Exp $ * * Device driver for Crystal Semiconductor CS8920 based ethernet * adapters. By Maxim Bolotin and Oleg Sharoiko, 27-April-1997 */ /* #define CS_DEBUG */ #include "cs.h" #include "bpfilter.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #if NBPFILTER > 0 #include #endif #include #include #include #include "pnp.h" #if NPNP > 0 #include #endif #ifdef CS_USE_64K_DMA #define CS_DMA_BUFFER_SIZE 65536 #else #define CS_DMA_BUFFER_SIZE 16384 #endif /* * cs_softc: per line info and status */ static struct cs_softc { /* Ethernet common code */ struct arpcom arpcom; /* Configuration words from EEPROM */ int auto_neg_cnf; /* AutoNegotitation configuration */ int adapter_cnf; /* Adapter configuration */ int isa_config; /* ISA configuration */ int chip_type; /* Type of chip */ struct ifmedia media; /* Media information */ int nic_addr; /* Base IO address of card */ int send_cmd; int line_ctl; /* */ int send_underrun; void *recv_ring; unsigned char *buffer; int buf_len; } cs_softc[NCS]; #if NPNP > 0 static u_long cs_unit = NCS; #endif static int cs_recv_delay = 570; SYSCTL_INT(_machdep, OID_AUTO, cs_recv_delay, CTLFLAG_RW, &cs_recv_delay, 0, ""); static int cs_attach __P((struct cs_softc *, int, int)); static int cs_attach_isa __P((struct isa_device *)); static void cs_init __P((void *)); static ointhand2_t csintr; static int cs_ioctl __P((struct ifnet *, u_long, caddr_t)); static int cs_probe __P((struct isa_device *)); static int cs_cs89x0_probe __P((struct cs_softc *, u_int *, int *, int, int, int)); static void cs_start __P((struct ifnet *)); static void cs_stop __P((struct cs_softc *)); static void cs_reset __P((struct cs_softc *)); static void cs_watchdog __P((struct ifnet *)); static int cs_mediachange __P((struct ifnet *)); static void cs_mediastatus __P((struct ifnet *, struct ifmediareq *)); static int cs_mediaset __P((struct cs_softc *, int)); static void cs_write_mbufs(struct cs_softc*, struct mbuf*); static void cs_xmit_buf(struct cs_softc*); static int cs_get_packet(struct cs_softc*); static void cs_setmode(struct cs_softc*); static int get_eeprom_data(struct cs_softc *sc, int, int, int *); static int get_eeprom_cksum(int, int, int *); static int wait_eeprom_ready( struct cs_softc *); static void control_dc_dc( struct cs_softc *, int ); static int send_test_pkt( struct cs_softc * ); static int enable_tp(struct cs_softc *); static int enable_aui(struct cs_softc *); static int enable_bnc(struct cs_softc *); static int cs_duplex_auto(struct cs_softc *); struct isa_driver csdriver = { cs_probe, cs_attach_isa, CS_NAME, 0 }; static int get_eeprom_data( struct cs_softc *sc, int off, int len, int *buffer) { int i; #ifdef CS_DEBUG printf(CS_NAME":EEPROM data from %x for %x:\n", off,len); #endif for (i=0;inic_addr, PP_EECMD, (off+i)|EEPROM_READ_CMD ); if (wait_eeprom_ready(sc)<0) return -1; buffer[i] = cs_readreg (sc->nic_addr, PP_EEData); #ifdef CS_DEBUG printf("%02x %02x ",(unsigned char)buffer[i], (unsigned char)buffer[i+1]); #endif } #ifdef CS_DEBUG printf("\n"); #endif return 0; } static int get_eeprom_cksum(int off, int len, int *buffer) { int i,cksum=0; for (i=0;iadapter_cnf & A_CNF_DC_DC_POLARITY)!=0) ^ on_not_off) self_control |= HCB1; else self_control &= ~HCB1; cs_writereg( sc->nic_addr, PP_SelfCTL, self_control ); DELAY( 500000 ); } static int cs_duplex_auto(struct cs_softc *sc) { int i, error=0, unit=sc->arpcom.ac_if.if_unit; cs_writereg(sc->nic_addr, PP_AutoNegCTL, RE_NEG_NOW | ALLOW_FDX | AUTO_NEG_ENABLE ); for (i=0; cs_readreg(sc->nic_addr,PP_AutoNegST)&AUTO_NEG_BUSY; i++) { if (i > 40000) { printf(CS_NAME"%1d: full/half duplex " "auto negotiation timeout\n", unit); error = ETIMEDOUT; break; } DELAY(1000); } DELAY( 1000000 ); return error; } static int enable_tp(struct cs_softc *sc) { int unit = sc->arpcom.ac_if.if_unit; cs_writereg(sc->nic_addr, PP_LineCTL, sc->line_ctl & ~AUI_ONLY); control_dc_dc(sc, 0); DELAY( 150000 ); if ((cs_readreg(sc->nic_addr, PP_LineST) & LINK_OK)==0) { printf(CS_NAME"%1d: failed to enable TP\n", unit); return EINVAL; } return 0; } /* * XXX This was rewritten from Linux driver without any tests. */ static int send_test_pkt(struct cs_softc *sc) { char test_packet[] = { 0,0,0,0,0,0, 0,0,0,0,0,0, 0, 46, /* A 46 in network order */ 0, 0, /* DSAP=0 & SSAP=0 fields */ 0xf3, 0 /* Control (Test Req + P bit set) */ }; int i; u_char ether_address_backup[ETHER_ADDR_LEN]; for (i = 0; i < ETHER_ADDR_LEN; i++) { ether_address_backup[i] = sc->arpcom.ac_enaddr[i]; } cs_writereg(sc->nic_addr, PP_LineCTL, cs_readreg(sc->nic_addr, PP_LineCTL) | SERIAL_TX_ON ); bcopy(test_packet, sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); bcopy(test_packet+ETHER_ADDR_LEN, sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); outw(sc->nic_addr + TX_CMD_PORT, sc->send_cmd); outw(sc->nic_addr + TX_LEN_PORT, sizeof(test_packet)); /* Wait for chip to allocate memory */ DELAY(50000); if (!(cs_readreg(sc->nic_addr, PP_BusST) & READY_FOR_TX_NOW)) { for (i = 0; i < ETHER_ADDR_LEN; i++) { sc->arpcom.ac_enaddr[i] = ether_address_backup[i]; } return 0; } outsw(sc->nic_addr + TX_FRAME_PORT, test_packet, sizeof(test_packet)); DELAY(30000); if ((cs_readreg(sc->nic_addr,PP_TxEvent) & TX_SEND_OK_BITS) == TX_OK) { for (i = 0; i < ETHER_ADDR_LEN; i++) { sc->arpcom.ac_enaddr[i] = ether_address_backup[i]; } return 1; } for (i = 0; i < ETHER_ADDR_LEN; i++) { sc->arpcom.ac_enaddr[i] = ether_address_backup[i]; } return 0; } /* * XXX This was rewritten from Linux driver without any tests. */ static int enable_aui(struct cs_softc *sc) { int unit = sc->arpcom.ac_if.if_unit; control_dc_dc(sc, 0); cs_writereg(sc->nic_addr, PP_LineCTL, (sc->line_ctl & ~AUTO_AUI_10BASET) | AUI_ONLY); if (!send_test_pkt(sc)) { printf(CS_NAME"%1d failed to enable AUI\n", unit); return EINVAL; } return 0; } /* * XXX This was rewritten from Linux driver without any tests. */ static int enable_bnc(struct cs_softc *sc) { int unit = sc->arpcom.ac_if.if_unit; control_dc_dc(sc, 1); cs_writereg(sc->nic_addr, PP_LineCTL, (sc->line_ctl & ~AUTO_AUI_10BASET) | AUI_ONLY); if (!send_test_pkt(sc)) { printf(CS_NAME"%1d failed to enable BNC\n", unit); return EINVAL; } return 0; } static int cs_cs89x0_probe(struct cs_softc *sc, u_int *dev_irq, int *dev_drq, int iobase, int unit, int flags) { unsigned rev_type = 0; int i, irq=0; int eeprom_buff[CHKSUM_LEN]; int chip_type, pp_isaint, pp_isadma; char chip_revision; if ((inw(iobase+ADD_PORT) & ADD_MASK) != ADD_SIG) { /* Chip not detected. Let's try to reset it */ if (bootverbose) printf(CS_NAME"%1d: trying to reset the chip.\n", unit); outw(iobase+ADD_PORT, PP_SelfCTL); i = inw(iobase+DATA_PORT); outw(iobase+ADD_PORT, PP_SelfCTL); outw(iobase+DATA_PORT, i | POWER_ON_RESET); if ((inw(iobase+ADD_PORT) & ADD_MASK) != ADD_SIG) return 0; } outw(iobase+ADD_PORT, PP_ChipID); if (inw(iobase+DATA_PORT) != CHIP_EISA_ID_SIG) return 0; rev_type = cs_readreg(iobase, PRODUCT_ID_ADD); chip_type = rev_type & ~REVISON_BITS; chip_revision = ((rev_type & REVISON_BITS) >> 8) + 'A'; sc->nic_addr = iobase; sc->chip_type = chip_type; if(chip_type==CS8900) { pp_isaint = PP_CS8900_ISAINT; pp_isadma = PP_CS8900_ISADMA; sc->send_cmd = TX_CS8900_AFTER_ALL; } else { pp_isaint = PP_CS8920_ISAINT; pp_isadma = PP_CS8920_ISADMA; sc->send_cmd = TX_CS8920_AFTER_ALL; } /* * Clear some fields so that fail of EEPROM will left them clean */ sc->auto_neg_cnf = 0; sc->adapter_cnf = 0; sc->isa_config = 0; /* * EEPROM */ if((cs_readreg(iobase, PP_SelfST) & EEPROM_PRESENT) == 0) { printf(CS_NAME"%1d: No EEPROM, assuming defaults.\n", unit); } else { if (get_eeprom_data(sc,START_EEPROM_DATA,CHKSUM_LEN, eeprom_buff)<0) { printf(CS_NAME"%1d: EEPROM read failed, " "assuming defaults..\n", unit); } else { if (get_eeprom_cksum(START_EEPROM_DATA,CHKSUM_LEN, eeprom_buff)<0) { printf( CS_NAME"%1d: EEPROM cheksum bad, " "assuming defaults..\n", unit ); } else { sc->auto_neg_cnf = eeprom_buff[AUTO_NEG_CNF_OFFSET/2]; sc->adapter_cnf = eeprom_buff[ADAPTER_CNF_OFFSET/2]; sc->isa_config = eeprom_buff[ISA_CNF_OFFSET/2]; for (i=0; iarpcom.ac_enaddr[i*2]= eeprom_buff[i]; sc->arpcom.ac_enaddr[i*2+1]= eeprom_buff[i] >> 8; } /* * If no interrupt specified (or "?"), * use what the board tells us. */ if (*dev_irq <= 0) { irq = sc->isa_config & INT_NO_MASK; if (chip_type==CS8900) { switch(irq) { case 0: irq=10; break; case 1: irq=11; break; case 2: irq=12; break; case 3: irq=5; break; default: printf(CS_NAME"%1d: invalid irq in EEPROM.\n",unit); } if (irq!=0) *dev_irq=(u_short)(1< CS8920_NO_INTS) { printf(CS_NAME"%1d: invalid irq\n", unit); return 0; } } cs_writereg(iobase, pp_isaint, irq); } else { printf(CS_NAME"%1d: invalid irq\n", unit); return 0; } /* * Temporary disabled * if (drq>0) cs_writereg(iobase, pp_isadma, drq); else { printf( CS_NAME"%1d: incorrect drq\n", unit ); return 0; } */ if (bootverbose) printf(CS_NAME"%1d: model CS89%c0%s rev %c\n" CS_NAME"%1d: media%s%s%s\n" CS_NAME"%1d: irq %d drq %d\n", unit, chip_type==CS8900 ? '0' : '2', chip_type==CS8920M ? "M" : "", chip_revision, unit, (sc->adapter_cnf & A_CNF_10B_T) ? " TP" : "", (sc->adapter_cnf & A_CNF_AUI) ? " AUI" : "", (sc->adapter_cnf & A_CNF_10B_2) ? " BNC" : "", unit, (int)*dev_irq, (int)*dev_drq); if ((sc->adapter_cnf & A_CNF_EXTND_10B_2) && (sc->adapter_cnf & A_CNF_LOW_RX_SQUELCH)) sc->line_ctl = LOW_RX_SQUELCH; else sc->line_ctl = 0; return PP_ISAIOB; } /* * Determine if the device is present * * on entry: * a pointer to an isa_device struct * on exit: * NULL if device not found * or # of i/o addresses used (if found) */ static int cs_probe(struct isa_device *dev) { int nports; struct cs_softc *sc=&cs_softc[dev->id_unit]; nports=cs_cs89x0_probe(sc, &(dev->id_irq), &(dev->id_drq), (dev->id_iobase), (dev->id_unit), (dev->id_flags)); if (nports) return (nports); return (0); } /* * Install the interface into kernel networking data structures */ static int cs_attach(struct cs_softc *sc, int unit, int flags) { int media=0; /* struct cs_softc *sc = &cs_softc[dev->id_unit]; */ struct ifnet *ifp = &(sc->arpcom.ac_if); if (!ifp->if_name) { ifp->if_softc=sc; ifp->if_unit=unit; ifp->if_name=csdriver.name; ifp->if_output=ether_output; ifp->if_start=cs_start; ifp->if_ioctl=cs_ioctl; ifp->if_watchdog=cs_watchdog; ifp->if_init=cs_init; ifp->if_snd.ifq_maxlen= IFQ_MAXLEN; /* * MIB DATA */ /* ifp->if_linkmib=&sc->mibdata; ifp->if_linkmiblen=sizeof sc->mibdata; */ ifp->if_flags=(IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST ); /* * this code still in progress (DMA support) * sc->recv_ring=malloc(CS_DMA_BUFFER_SIZE<<1, M_DEVBUF, M_NOWAIT); if (sc->recv_ring == NULL) { log(LOG_ERR,CS_NAME "%d: Couldn't allocate memory for NIC\n", unit); return(0); } if ((sc->recv_ring-(sc->recv_ring & 0x1FFFF)) < (128*1024-CS_DMA_BUFFER_SIZE)) sc->recv_ring+=16*1024; */ sc->buffer=malloc(ETHER_MAX_LEN-ETHER_CRC_LEN,M_DEVBUF,M_NOWAIT); if (sc->buffer == NULL) { printf(CS_NAME"%d: Couldn't allocate memory for NIC\n", unit); return(0); } /* * Initialize the media structures. */ ifmedia_init(&sc->media, 0, cs_mediachange, cs_mediastatus); if (sc->adapter_cnf & A_CNF_10B_T) { ifmedia_add(&sc->media, IFM_ETHER|IFM_10_T, 0, NULL); if (sc->chip_type != CS8900) { ifmedia_add(&sc->media, IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL); ifmedia_add(&sc->media, IFM_ETHER|IFM_10_T|IFM_HDX, 0, NULL); } } if (sc->adapter_cnf & A_CNF_10B_2) ifmedia_add(&sc->media, IFM_ETHER|IFM_10_2, 0, NULL); if (sc->adapter_cnf & A_CNF_AUI) ifmedia_add(&sc->media, IFM_ETHER|IFM_10_5, 0, NULL); if (sc->adapter_cnf & A_CNF_MEDIA) ifmedia_add(&sc->media, IFM_ETHER|IFM_AUTO, 0, NULL); /* Set default media from EEPROM */ switch (sc->adapter_cnf & A_CNF_MEDIA_TYPE) { case A_CNF_MEDIA_AUTO: media = IFM_ETHER|IFM_AUTO; break; case A_CNF_MEDIA_10B_T: media = IFM_ETHER|IFM_10_T; break; case A_CNF_MEDIA_10B_2: media = IFM_ETHER|IFM_10_2; break; case A_CNF_MEDIA_AUI: media = IFM_ETHER|IFM_10_5; break; default: printf(CS_NAME"%d: adapter has no media\n", unit); } ifmedia_set(&sc->media, media); cs_mediaset(sc, media); if_attach(ifp); cs_stop( sc ); ether_ifattach(ifp); } if (bootverbose) printf(CS_NAME"%d: ethernet address %6D\n", ifp->if_unit, sc->arpcom.ac_enaddr, ":"); #if NBPFILTER > 0 bpfattach(ifp, DLT_EN10MB, sizeof (struct ether_header)); #endif return 1; } static int cs_attach_isa(struct isa_device *dev) { int unit=dev->id_unit; struct cs_softc *sc=&cs_softc[unit]; int flags=dev->id_flags; dev->id_ointr = csintr; return cs_attach(sc, unit, flags); } /* * Initialize the board */ static void cs_init(void *xsc) { struct cs_softc *sc=(struct cs_softc *)xsc; struct ifnet *ifp = &sc->arpcom.ac_if; int i, s, rx_cfg; /* address not known */ if (TAILQ_EMPTY(&ifp->if_addrhead)) /* unlikely? XXX */ return; /* * reset whatchdog timer */ ifp->if_timer=0; sc->buf_len = 0; s=splimp(); /* * Hardware initialization of cs */ /* Enable receiver and transmitter */ cs_writereg(sc->nic_addr, PP_LineCTL, cs_readreg( sc->nic_addr, PP_LineCTL ) | SERIAL_RX_ON | SERIAL_TX_ON); /* Configure the receiver mode */ cs_setmode(sc); /* * This defines what type of frames will cause interrupts * Bad frames should generate interrupts so that the driver * could track statistics of discarded packets */ rx_cfg = RX_OK_ENBL | RX_CRC_ERROR_ENBL | RX_RUNT_ENBL | RX_EXTRA_DATA_ENBL; if (sc->isa_config & STREAM_TRANSFER) rx_cfg |= RX_STREAM_ENBL; cs_writereg(sc->nic_addr, PP_RxCFG, rx_cfg); cs_writereg(sc->nic_addr, PP_TxCFG, TX_LOST_CRS_ENBL | TX_SQE_ERROR_ENBL | TX_OK_ENBL | TX_LATE_COL_ENBL | TX_JBR_ENBL | TX_ANY_COL_ENBL | TX_16_COL_ENBL); cs_writereg(sc->nic_addr, PP_BufCFG, READY_FOR_TX_ENBL | RX_MISS_COUNT_OVRFLOW_ENBL | TX_COL_COUNT_OVRFLOW_ENBL | TX_UNDERRUN_ENBL /*| RX_DMA_ENBL*/); /* Write MAC address into IA filter */ for (i=0; inic_addr, PP_IA+i*2, sc->arpcom.ac_enaddr[i*2] | (sc->arpcom.ac_enaddr[i*2+1] << 8) ); /* * Now enable everything */ /* #ifdef CS_USE_64K_DMA cs_writereg(sc->nic_addr, PP_BusCTL, ENABLE_IRQ | RX_DMA_SIZE_64K); #else cs_writereg(sc->nic_addr, PP_BusCTL, ENABLE_IRQ); #endif */ cs_writereg(sc->nic_addr, PP_BusCTL, ENABLE_IRQ); /* * Set running and clear output active flags */ sc->arpcom.ac_if.if_flags |= IFF_RUNNING; sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE; /* * Start sending process */ cs_start(ifp); (void) splx(s); } /* * Get the packet from the board and send it to the upper layer * via ether_input(). */ static int cs_get_packet(struct cs_softc *sc) { struct ifnet *ifp = &(sc->arpcom.ac_if); int iobase = sc->nic_addr, status, length; struct ether_header *eh; struct mbuf *m; #ifdef CS_DEBUG int i; #endif status = inw(iobase + RX_FRAME_PORT); length = inw(iobase + RX_FRAME_PORT); #ifdef CS_DEBUG printf(CS_NAME"%1d: rcvd: stat %x, len %d\n", ifp->if_unit, status, length); #endif if (!(status & RX_OK)) { #ifdef CS_DEBUG printf(CS_NAME"%1d: bad pkt stat %x\n", ifp->if_unit, status); #endif ifp->if_ierrors++; return -1; } MGETHDR(m, M_DONTWAIT, MT_DATA); if (m==NULL) return -1; if (length > MHLEN) { MCLGET(m, M_DONTWAIT); if (!(m->m_flags & M_EXT)) { m_freem(m); return -1; } } /* Initialize packet's header info */ m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = length; m->m_len = length; /* Get the data */ insw(iobase + RX_FRAME_PORT, m->m_data, (length+1)>>1); eh = mtod(m, struct ether_header *); #if NBPFILTER > 0 if (ifp->if_bpf) bpf_mtap(ifp, m); #endif #ifdef CS_DEBUG for (i=0;im_data+i))); printf( "\n" ); #endif if (status & (RX_IA | RX_BROADCAST) || (ifp->if_flags & IFF_MULTICAST && status & RX_HASHED)) { m->m_pkthdr.len -= sizeof(struct ether_header); m->m_len -= sizeof(struct ether_header); m->m_data += sizeof(struct ether_header); /* Feed the packet to the upper layer */ ether_input(ifp, eh, m); ifp->if_ipackets++; if (length==ETHER_MAX_LEN-ETHER_CRC_LEN) DELAY( cs_recv_delay ); } else { m_freem(m); } return 0; } /* * Software calls interrupt handler */ static void csintr_sc(struct cs_softc *sc, int unit) { struct ifnet *ifp = &(sc->arpcom.ac_if); int status; #ifdef CS_DEBUG printf(CS_NAME"%1d: Interrupt.\n", unit); #endif while ((status=cs_readword(sc->nic_addr, ISQ_PORT))) { #ifdef CS_DEBUG printf( CS_NAME"%1d:from ISQ: %04x\n", unit, status ); #endif switch (status & ISQ_EVENT_MASK) { case ISQ_RECEIVER_EVENT: cs_get_packet(sc); break; case ISQ_TRANSMITTER_EVENT: if (status & TX_OK) ifp->if_opackets++; else ifp->if_oerrors++; ifp->if_flags &= ~IFF_OACTIVE; ifp->if_timer = 0; break; case ISQ_BUFFER_EVENT: if (status & READY_FOR_TX) { ifp->if_flags &= ~IFF_OACTIVE; ifp->if_timer = 0; } if (status & TX_UNDERRUN) { ifp->if_flags &= ~IFF_OACTIVE; ifp->if_timer = 0; ifp->if_oerrors++; } break; case ISQ_RX_MISS_EVENT: ifp->if_ierrors+=(status>>6); break; case ISQ_TX_COL_EVENT: ifp->if_collisions+=(status>>6); break; } } if (!(ifp->if_flags & IFF_OACTIVE)) { cs_start(ifp); } } /* * Handle interrupts */ static void csintr(int unit) { struct cs_softc *sc = &cs_softc[unit]; csintr_sc(sc, unit); } /* * Save the data in buffer */ static void cs_write_mbufs( struct cs_softc *sc, struct mbuf *m ) { int len; struct mbuf *mp; unsigned char *data, *buf; for (mp=m, buf=sc->buffer, sc->buf_len=0; mp != NULL; mp=mp->m_next) { len = mp->m_len; /* * Ignore empty parts */ if (!len) continue; /* * Find actual data address */ data = mtod(mp, caddr_t); bcopy((caddr_t) data, (caddr_t) buf, len); buf += len; sc->buf_len += len; } } static void cs_xmit_buf( struct cs_softc *sc ) { outsw(sc->nic_addr+TX_FRAME_PORT, sc->buffer, (sc->buf_len+1)>>1); sc->buf_len = 0; } static void cs_start(struct ifnet *ifp) { int s, length; struct mbuf *m, *mp; struct cs_softc *sc = ifp->if_softc; s = splimp(); for (;;) { if (sc->buf_len) length = sc->buf_len; else { IF_DEQUEUE( &ifp->if_snd, m ); if (m==NULL) { (void) splx(s); return; } for (length=0, mp=m; mp != NULL; mp=mp->m_next) length += mp->m_len; /* Skip zero-length packets */ if (length == 0) { m_freem(m); continue; } cs_write_mbufs(sc, m); #if NBPFILTER > 0 if (ifp->if_bpf) { bpf_mtap(ifp, m); } #endif m_freem(m); } /* * Issue a SEND command */ outw(sc->nic_addr+TX_CMD_PORT, sc->send_cmd); outw(sc->nic_addr+TX_LEN_PORT, length ); /* * If there's no free space in the buffer then leave * this packet for the next time: indicate output active * and return. */ if (!(cs_readreg(sc->nic_addr, PP_BusST) & READY_FOR_TX_NOW)) { ifp->if_timer = sc->buf_len; (void) splx(s); ifp->if_flags |= IFF_OACTIVE; return; } cs_xmit_buf(sc); /* * Set the watchdog timer in case we never hear * from board again. (I don't know about correct * value for this timeout) */ ifp->if_timer = length; (void) splx(s); ifp->if_flags |= IFF_OACTIVE; return; } } /* * Stop everything on the interface */ static void cs_stop(struct cs_softc *sc) { int s = splimp(); cs_writereg(sc->nic_addr, PP_RxCFG, 0); cs_writereg(sc->nic_addr, PP_TxCFG, 0); cs_writereg(sc->nic_addr, PP_BufCFG, 0); cs_writereg(sc->nic_addr, PP_BusCTL, 0); sc->arpcom.ac_if.if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); sc->arpcom.ac_if.if_timer = 0; (void) splx(s); } /* * Reset the interface */ static void cs_reset(struct cs_softc *sc) { cs_stop(sc); cs_init(sc); } static void cs_setmode(struct cs_softc *sc) { struct ifnet *ifp = &(sc->arpcom.ac_if); int rx_ctl; /* Stop the receiver while changing filters */ cs_writereg(sc->nic_addr, PP_LineCTL, cs_readreg(sc->nic_addr, PP_LineCTL) & ~SERIAL_RX_ON); if (ifp->if_flags & IFF_PROMISC) { /* Turn on promiscuous mode. */ rx_ctl = RX_OK_ACCEPT | RX_PROM_ACCEPT; } else { if (ifp->if_flags & IFF_MULTICAST) { /* Allow receiving frames with multicast addresses */ rx_ctl = RX_IA_ACCEPT | RX_BROADCAST_ACCEPT | RX_OK_ACCEPT | RX_MULTCAST_ACCEPT; /* * Here the reconfiguration of chip's multicast * filters should be done but I've no idea about * hash transformation in this chip. If you can * add this code or describe me the transformation * I'd be very glad. */ } else { /* * Receive only good frames addressed for us and * good broadcasts. */ rx_ctl = RX_IA_ACCEPT | RX_BROADCAST_ACCEPT | RX_OK_ACCEPT; } } /* Set up the filter */ cs_writereg(sc->nic_addr, PP_RxCTL, RX_DEF_ACCEPT | rx_ctl); /* Turn on receiver */ cs_writereg(sc->nic_addr, PP_LineCTL, cs_readreg(sc->nic_addr, PP_LineCTL) | SERIAL_RX_ON); } static int cs_ioctl(register struct ifnet *ifp, u_long command, caddr_t data) { struct cs_softc *sc=ifp->if_softc; struct ifreq *ifr = (struct ifreq *)data; int s,error=0; #ifdef CS_DEBUG printf(CS_NAME"%d: ioctl(%x)\n",sc->arpcom.ac_if.if_unit,command); #endif s=splimp(); switch (command) { case SIOCSIFADDR: case SIOCGIFADDR: case SIOCSIFMTU: ether_ioctl(ifp, command, data); break; case SIOCSIFFLAGS: /* * Switch interface state between "running" and * "stopped", reflecting the UP flag. */ if (sc->arpcom.ac_if.if_flags & IFF_UP) { if ((sc->arpcom.ac_if.if_flags & IFF_RUNNING)==0) { cs_init(sc); } } else { if ((sc->arpcom.ac_if.if_flags & IFF_RUNNING)!=0) { cs_stop(sc); } } /* * Promiscuous and/or multicast flags may have changed, * so reprogram the multicast filter and/or receive mode. * * See note about multicasts in cs_setmode */ cs_setmode(sc); break; case SIOCADDMULTI: case SIOCDELMULTI: /* * Multicast list has changed; set the hardware filter * accordingly. * * See note about multicasts in cs_setmode */ cs_setmode(sc); error = 0; break; case SIOCSIFMEDIA: case SIOCGIFMEDIA: error = ifmedia_ioctl(ifp, ifr, &sc->media, command); break; default: error = EINVAL; } (void) splx(s); return error; } /* * Device timeout/watchdog routine. Entered if the device neglects to * generate an interrupt after a transmit has been started on it. */ static void cs_watchdog(struct ifnet *ifp) { struct cs_softc *sc = &cs_softc[ifp->if_unit]; ifp->if_oerrors++; log(LOG_ERR, CS_NAME"%d: device timeout\n", ifp->if_unit); /* Reset the interface */ if (ifp->if_flags & IFF_UP) cs_reset(sc); else cs_stop(sc); } static int cs_mediachange(struct ifnet *ifp) { struct cs_softc *sc = ifp->if_softc; struct ifmedia *ifm = &sc->media; if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER) return EINVAL; return cs_mediaset(sc, ifm->ifm_media); } static void cs_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr) { int line_status; struct cs_softc *sc = ifp->if_softc; ifmr->ifm_active = IFM_ETHER; line_status = cs_readreg(sc->nic_addr, PP_LineST); if (line_status & TENBASET_ON) { ifmr->ifm_active |= IFM_10_T; if (sc->chip_type != CS8900) { if (cs_readreg(sc->nic_addr, PP_AutoNegST) & FDX_ACTIVE) ifmr->ifm_active |= IFM_FDX; if (cs_readreg(sc->nic_addr, PP_AutoNegST) & HDX_ACTIVE) ifmr->ifm_active |= IFM_HDX; } ifmr->ifm_status = IFM_AVALID; if (line_status & LINK_OK) ifmr->ifm_status |= IFM_ACTIVE; } else { if (line_status & AUI_ON) { cs_writereg(sc->nic_addr, PP_SelfCTL, cs_readreg(sc->nic_addr, PP_SelfCTL) | HCB1_ENBL); if (((sc->adapter_cnf & A_CNF_DC_DC_POLARITY)!=0)^ (cs_readreg(sc->nic_addr, PP_SelfCTL)&HCB1)) ifmr->ifm_active |= IFM_10_2; else ifmr->ifm_active |= IFM_10_5; } } } static int cs_mediaset(struct cs_softc *sc, int media) { int error; /* Stop the receiver & transmitter */ cs_writereg(sc->nic_addr, PP_LineCTL, cs_readreg(sc->nic_addr, PP_LineCTL) & ~(SERIAL_RX_ON | SERIAL_TX_ON)); #ifdef CS_DEBUG printf(CS_NAME"%d: cs_setmedia(%x)\n",sc->arpcom.ac_if.if_unit,media); #endif switch (IFM_SUBTYPE(media)) { default: case IFM_AUTO: if ((error=enable_tp(sc))==0) error = cs_duplex_auto(sc); else if ((error=enable_bnc(sc)) != 0) error = enable_aui(sc); break; case IFM_10_T: if ((error=enable_tp(sc)) != 0) break; if (media & IFM_FDX) cs_duplex_full(sc); else if (media & IFM_HDX) cs_duplex_half(sc); else error = cs_duplex_auto(sc); break; case IFM_10_2: error = enable_bnc(sc); break; case IFM_10_5: error = enable_aui(sc); break; } /* * Turn the transmitter & receiver back on */ cs_writereg(sc->nic_addr, PP_LineCTL, cs_readreg( sc->nic_addr, PP_LineCTL ) | SERIAL_RX_ON | SERIAL_TX_ON); return error; } #if NPNP > 0 static struct cspnp_ids { u_long vend_id; char *id_str; } cspnp_ids[]= { { 0x4060630e, "CSC6040" }, { 0x10104d24, "IBM EtherJet" }, { 0 } }; static char *cs_pnp_probe(u_long, u_long); static void cs_pnp_attach(u_long, u_long, char *, struct isa_device *); struct pnp_device cs_pnp = { "CS8920 based PnP Ethernet", cs_pnp_probe, cs_pnp_attach, &cs_unit, &net_imask /* imask */ }; DATA_SET (pnpdevice_set, cs_pnp); struct csintr_list { struct cs_softc *sc; int unit; struct csintr_list *next; }; static struct csintr_list *csintr_head; static void csintr_pnp_add(struct cs_softc *sc, int unit); static void csintr_pnp(int unit); static void csintr_pnp_add(struct cs_softc *sc, int unit) { struct csintr_list *intr; if (!sc) return; intr = malloc (sizeof (*intr), M_DEVBUF, M_WAITOK); if (!intr) return; intr->sc = sc; intr->unit = unit; intr->next = csintr_head; csintr_head = intr; } /* * Interrupt handler for PNP installed card * We have to find the number of the card. */ static void csintr_pnp(int unit) { struct csintr_list *intr; for (intr=csintr_head; intr; intr=intr->next) { if (intr->unit == unit) csintr_sc(intr->sc, unit); break; } } static char * cs_pnp_probe(u_long csn, u_long vend_id) { struct cspnp_ids *ids; char *s=NULL; for(ids = cspnp_ids; ids->vend_id != 0; ids++) { if (vend_id == ids->vend_id) { s = ids->id_str; break; } } if (s) { struct pnp_cinfo d; int ldn = 0; read_pnp_parms(&d, ldn); if (d.enable == 0) { printf("This is a %s, but LDN %d is disabled\n", s, ldn); return NULL ; } return s; } return NULL ; } static void cs_pnp_attach(u_long csn, u_long vend_id, char *name, struct isa_device *dev) { struct pnp_cinfo d; int ldn = 0; int iobase, unit, flags; u_int irq; int drq; - struct isa_device *dvp; struct cs_softc *sc = malloc(sizeof *sc, M_DEVBUF, M_NOWAIT); if (read_pnp_parms ( &d , ldn ) == 0 ) { printf("failed to read pnp parms\n"); return; } write_pnp_parms( &d, ldn ); enable_pnp_card(); iobase = dev->id_iobase = d.port[0]; irq = dev->id_irq = (1 << d.irq[0] ); drq = dev->id_drq = d.drq[0]; dev->id_maddr = 0; dev->id_ointr = csintr_pnp; flags = dev->id_flags = 0; unit = dev->id_unit; if (dev->id_driver == NULL) { dev->id_driver = &csdriver; - dvp = find_isadev(isa_devtab_net, &csdriver, 0); - if (dvp != NULL) - dev->id_id = dvp->id_id; + dev->id_id = isa_compat_nextid(); } if (!sc) return; bzero(sc, sizeof *sc); if (cs_cs89x0_probe(sc, &irq, &drq, iobase, unit, flags) == 0 || cs_attach(sc, unit, flags) == 0) { free(sc, M_DEVBUF); } else { if ((irq != dev->id_irq) || (drq != dev->id_drq) || (iobase != dev->id_iobase) || (unit != dev->id_unit) || (flags != dev->id_flags) ) { printf("failed to pnp card parametars\n"); } } csintr_pnp_add(sc, dev->id_unit); } #endif /* NPNP */ Index: head/sys/dev/ed/if_ed.c =================================================================== --- head/sys/dev/ed/if_ed.c (revision 45719) +++ head/sys/dev/ed/if_ed.c (revision 45720) @@ -1,3522 +1,3519 @@ /* * Copyright (c) 1995, David Greenman * 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 unmodified, 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. * - * $Id: if_ed.c,v 1.149 1999/01/28 01:59:53 dillon Exp $ + * $Id: if_ed.c,v 1.150 1999/03/17 16:44:51 luigi Exp $ */ /* * Device driver for National Semiconductor DS8390/WD83C690 based ethernet * adapters. By David Greenman, 29-April-1993 * * Currently supports the Western Digital/SMC 8003 and 8013 series, * the SMC Elite Ultra (8216), the 3Com 3c503, the NE1000 and NE2000, * and a variety of similar clones. * */ #include "ed.h" #include "bpfilter.h" #include "pnp.h" #ifndef EXTRA_ED # if NPNP > 0 # define EXTRA_ED 8 # else # define EXTRA_ED 0 # endif #endif #define NEDTOT (NED + EXTRA_ED) #include #include #include #include #include #include #include #include #include #include #include #include #if NBPFILTER > 0 #include #endif #include "opt_bdg.h" #ifdef BRIDGE #include #endif #include #include #include #include #include #if NPNP > 0 #include #endif /* * ed_softc: per line info and status */ struct ed_softc { struct arpcom arpcom; /* ethernet common */ char *type_str; /* pointer to type string */ u_char vendor; /* interface vendor */ u_char type; /* interface type code */ u_char gone; /* HW missing, presumed having a good time */ u_short asic_addr; /* ASIC I/O bus address */ u_short nic_addr; /* NIC (DS8390) I/O bus address */ /* * The following 'proto' variable is part of a work-around for 8013EBT asics * being write-only. It's sort of a prototype/shadow of the real thing. */ u_char wd_laar_proto; u_char cr_proto; u_char isa16bit; /* width of access to card 0=8 or 1=16 */ int is790; /* set by the probe code if the card is 790 * based */ /* * HP PC LAN PLUS card support. */ u_short hpp_options; /* flags controlling behaviour of the HP card */ u_short hpp_id; /* software revision and other fields */ caddr_t hpp_mem_start; /* Memory-mapped IO register address */ caddr_t mem_start; /* NIC memory start address */ caddr_t mem_end; /* NIC memory end address */ u_long mem_size; /* total NIC memory size */ caddr_t mem_ring; /* start of RX ring-buffer (in NIC mem) */ u_char mem_shared; /* NIC memory is shared with host */ u_char xmit_busy; /* transmitter is busy */ u_char txb_cnt; /* number of transmit buffers */ u_char txb_inuse; /* number of TX buffers currently in-use */ u_char txb_new; /* pointer to where new buffer will be added */ u_char txb_next_tx; /* pointer to next buffer ready to xmit */ u_short txb_len[8]; /* buffered xmit buffer lengths */ u_char tx_page_start; /* first page of TX buffer area */ u_char rec_page_start; /* first page of RX ring-buffer */ u_char rec_page_stop; /* last page of RX ring-buffer */ u_char next_packet; /* pointer to next unread RX packet */ struct ifmib_iso_8802_3 mibdata; /* stuff for network mgmt */ }; static struct ed_softc ed_softc[NEDTOT]; static int ed_attach __P((struct ed_softc *, int, int)); static int ed_attach_isa __P((struct isa_device *)); static void ed_init __P((void *)); static ointhand2_t edintr; static int ed_ioctl __P((struct ifnet *, u_long, caddr_t)); static int ed_probe __P((struct isa_device *)); static void ed_start __P((struct ifnet *)); static void ed_reset __P((struct ifnet *)); static void ed_watchdog __P((struct ifnet *)); static void ed_stop __P((struct ed_softc *)); static int ed_probe_generic8390 __P((struct ed_softc *)); static int ed_probe_WD80x3 __P((struct isa_device *)); static int ed_probe_3Com __P((struct isa_device *)); static int ed_probe_Novell __P((struct isa_device *)); static int ed_probe_Novell_generic __P((struct ed_softc *, int, int, int)); static int ed_probe_HP_pclanp __P((struct isa_device *)); #include "pci.h" #if NPCI > 0 void *ed_attach_NE2000_pci __P((int, int)); #endif #include "card.h" #if NCARD > 0 static int ed_probe_pccard __P((struct isa_device *, u_char *)); #endif static void ds_getmcaf __P((struct ed_softc *, u_long *)); static void ed_get_packet __P((struct ed_softc *, char *, /* u_short */ int, int)); static __inline void ed_rint __P((struct ed_softc *)); static __inline void ed_xmit __P((struct ed_softc *)); static __inline char * ed_ring_copy __P((struct ed_softc *, char *, char *, /* u_short */ int)); static void ed_hpp_set_physical_link __P((struct ed_softc *)); static void ed_hpp_readmem __P((struct ed_softc *, int, unsigned char *, /* u_short */ int)); static u_short ed_hpp_write_mbufs __P((struct ed_softc *, struct mbuf *, int)); static void ed_pio_readmem __P((struct ed_softc *, int, unsigned char *, /* u_short */ int)); static void ed_pio_writemem __P((struct ed_softc *, char *, /* u_short */ int, /* u_short */ int)); static u_short ed_pio_write_mbufs __P((struct ed_softc *, struct mbuf *, int)); void edintr_sc __P((struct ed_softc *)); static void ed_setrcr __P((struct ed_softc *)); static u_long ds_crc __P((u_char *ep)); #if (NCARD > 0) || (NPNP > 0) #include #endif #if NCARD > 0 #include #include #include #include /* * PC-Card (PCMCIA) specific code. */ static int edinit __P((struct pccard_devinfo *)); static void edunload __P((struct pccard_devinfo *)); static int card_intr __P((struct pccard_devinfo *)); PCCARD_MODULE(ed, edinit, edunload, card_intr, 0, net_imask); /* * Initialize the device - called from Slot manager. */ static int edinit(struct pccard_devinfo *devi) { int i; u_char e; struct ed_softc *sc = &ed_softc[devi->isahd.id_unit]; /* validate unit number. */ if (devi->isahd.id_unit >= NEDTOT) return(ENODEV); /* * Probe the device. If a value is returned, the * device was found at the location. */ sc->gone = 0; if (ed_probe_pccard(&devi->isahd, devi->misc) == 0) return(ENXIO); e = 0; for (i = 0; i < ETHER_ADDR_LEN; ++i) e |= devi->misc[i]; if (e) for (i = 0; i < ETHER_ADDR_LEN; ++i) sc->arpcom.ac_enaddr[i] = devi->misc[i]; if (ed_attach_isa(&devi->isahd) == 0) return(ENXIO); return(0); } /* * edunload - unload the driver and clear the table. * XXX TODO: * This is usually called when the card is ejected, but * can be caused by a modunload of a controller driver. * The idea is to reset the driver's view of the device * and ensure that any driver entry points such as * read and write do not hang. */ static void edunload(struct pccard_devinfo *devi) { struct ed_softc *sc = &ed_softc[devi->isahd.id_unit]; struct ifnet *ifp = &sc->arpcom.ac_if; if (sc->gone) { printf("ed%d: already unloaded\n", devi->isahd.id_unit); return; } ifp->if_flags &= ~IFF_RUNNING; if_down(ifp); sc->gone = 1; printf("ed%d: unload\n", devi->isahd.id_unit); } /* * card_intr - Shared interrupt called from * front end of PC-Card handler. */ static int card_intr(struct pccard_devinfo *devi) { edintr_sc(&ed_softc[devi->isahd.id_unit]); return(1); } #endif /* NCARD > 0 */ struct isa_driver eddriver = { ed_probe, ed_attach_isa, "ed", 1 /* We are ultra sensitive */ }; /* * Interrupt conversion table for WD/SMC ASIC/83C584 * (IRQ* are defined in icu.h) */ static unsigned short ed_intr_mask[] = { IRQ9, IRQ3, IRQ5, IRQ7, IRQ10, IRQ11, IRQ15, IRQ4 }; /* * Interrupt conversion table for 83C790 */ static unsigned short ed_790_intr_mask[] = { 0, IRQ9, IRQ3, IRQ5, IRQ7, IRQ10, IRQ11, IRQ15 }; /* * Interrupt conversion table for the HP PC LAN+ */ static unsigned short ed_hpp_intr_mask[] = { 0, /* 0 */ 0, /* 1 */ 0, /* 2 */ IRQ3, /* 3 */ IRQ4, /* 4 */ IRQ5, /* 5 */ IRQ6, /* 6 */ IRQ7, /* 7 */ 0, /* 8 */ IRQ9, /* 9 */ IRQ10, /* 10 */ IRQ11, /* 11 */ IRQ12, /* 12 */ 0, /* 13 */ 0, /* 14 */ IRQ15 /* 15 */ }; /* * Determine if the device is present * * on entry: * a pointer to an isa_device struct * on exit: * NULL if device not found * or # of i/o addresses used (if found) */ static int ed_probe(isa_dev) struct isa_device *isa_dev; { int nports; nports = ed_probe_WD80x3(isa_dev); if (nports) return (nports); nports = ed_probe_3Com(isa_dev); if (nports) return (nports); nports = ed_probe_Novell(isa_dev); if (nports) return (nports); nports = ed_probe_HP_pclanp(isa_dev); if (nports) return (nports); return (0); } /* * Generic probe routine for testing for the existance of a DS8390. * Must be called after the NIC has just been reset. This routine * works by looking at certain register values that are guaranteed * to be initialized a certain way after power-up or reset. Seems * not to currently work on the 83C690. * * Specifically: * * Register reset bits set bits * Command Register (CR) TXP, STA RD2, STP * Interrupt Status (ISR) RST * Interrupt Mask (IMR) All bits * Data Control (DCR) LAS * Transmit Config. (TCR) LB1, LB0 * * We only look at the CR and ISR registers, however, because looking at * the others would require changing register pages (which would be * intrusive if this isn't an 8390). * * Return 1 if 8390 was found, 0 if not. */ static int ed_probe_generic8390(sc) struct ed_softc *sc; { if ((inb(sc->nic_addr + ED_P0_CR) & (ED_CR_RD2 | ED_CR_TXP | ED_CR_STA | ED_CR_STP)) != (ED_CR_RD2 | ED_CR_STP)) return (0); if ((inb(sc->nic_addr + ED_P0_ISR) & ED_ISR_RST) != ED_ISR_RST) return (0); return (1); } /* * Probe and vendor-specific initialization routine for SMC/WD80x3 boards */ static int ed_probe_WD80x3(isa_dev) struct isa_device *isa_dev; { struct ed_softc *sc = &ed_softc[isa_dev->id_unit]; int i; u_int memsize, maddr; u_char iptr, isa16bit, sum; sc->asic_addr = isa_dev->id_iobase; sc->nic_addr = sc->asic_addr + ED_WD_NIC_OFFSET; sc->is790 = 0; #ifdef TOSH_ETHER outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_POW); DELAY(10000); #endif /* * Attempt to do a checksum over the station address PROM. If it * fails, it's probably not a SMC/WD board. There is a problem with * this, though: some clone WD boards don't pass the checksum test. * Danpex boards for one. */ for (sum = 0, i = 0; i < 8; ++i) sum += inb(sc->asic_addr + ED_WD_PROM + i); if (sum != ED_WD_ROM_CHECKSUM_TOTAL) { /* * Checksum is invalid. This often happens with cheap WD8003E * clones. In this case, the checksum byte (the eighth byte) * seems to always be zero. */ if (inb(sc->asic_addr + ED_WD_CARD_ID) != ED_TYPE_WD8003E || inb(sc->asic_addr + ED_WD_PROM + 7) != 0) return (0); } /* reset card to force it into a known state. */ #ifdef TOSH_ETHER outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_RST | ED_WD_MSR_POW); #else outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_RST); #endif DELAY(100); outb(sc->asic_addr + ED_WD_MSR, inb(sc->asic_addr + ED_WD_MSR) & ~ED_WD_MSR_RST); /* wait in the case this card is reading its EEROM */ DELAY(5000); sc->vendor = ED_VENDOR_WD_SMC; sc->type = inb(sc->asic_addr + ED_WD_CARD_ID); /* * Set initial values for width/size. */ memsize = 8192; isa16bit = 0; switch (sc->type) { case ED_TYPE_WD8003S: sc->type_str = "WD8003S"; break; case ED_TYPE_WD8003E: sc->type_str = "WD8003E"; break; case ED_TYPE_WD8003EB: sc->type_str = "WD8003EB"; break; case ED_TYPE_WD8003W: sc->type_str = "WD8003W"; break; case ED_TYPE_WD8013EBT: sc->type_str = "WD8013EBT"; memsize = 16384; isa16bit = 1; break; case ED_TYPE_WD8013W: sc->type_str = "WD8013W"; memsize = 16384; isa16bit = 1; break; case ED_TYPE_WD8013EP: /* also WD8003EP */ if (inb(sc->asic_addr + ED_WD_ICR) & ED_WD_ICR_16BIT) { isa16bit = 1; memsize = 16384; sc->type_str = "WD8013EP"; } else { sc->type_str = "WD8003EP"; } break; case ED_TYPE_WD8013WC: sc->type_str = "WD8013WC"; memsize = 16384; isa16bit = 1; break; case ED_TYPE_WD8013EBP: sc->type_str = "WD8013EBP"; memsize = 16384; isa16bit = 1; break; case ED_TYPE_WD8013EPC: sc->type_str = "WD8013EPC"; memsize = 16384; isa16bit = 1; break; case ED_TYPE_SMC8216C: /* 8216 has 16K shared mem -- 8416 has 8K */ case ED_TYPE_SMC8216T: if (sc->type == ED_TYPE_SMC8216C) { sc->type_str = "SMC8216/SMC8216C"; } else { sc->type_str = "SMC8216T"; } outb(sc->asic_addr + ED_WD790_HWR, inb(sc->asic_addr + ED_WD790_HWR) | ED_WD790_HWR_SWH); switch (inb(sc->asic_addr + ED_WD790_RAR) & ED_WD790_RAR_SZ64) { case ED_WD790_RAR_SZ64: memsize = 65536; break; case ED_WD790_RAR_SZ32: memsize = 32768; break; case ED_WD790_RAR_SZ16: memsize = 16384; break; case ED_WD790_RAR_SZ8: /* 8216 has 16K shared mem -- 8416 has 8K */ if (sc->type == ED_TYPE_SMC8216C) { sc->type_str = "SMC8416C/SMC8416BT"; } else { sc->type_str = "SMC8416T"; } memsize = 8192; break; } outb(sc->asic_addr + ED_WD790_HWR, inb(sc->asic_addr + ED_WD790_HWR) & ~ED_WD790_HWR_SWH); isa16bit = 1; sc->is790 = 1; break; #ifdef TOSH_ETHER case ED_TYPE_TOSHIBA1: sc->type_str = "Toshiba1"; memsize = 32768; isa16bit = 1; break; case ED_TYPE_TOSHIBA4: sc->type_str = "Toshiba4"; memsize = 32768; isa16bit = 1; break; #endif default: sc->type_str = ""; break; } /* * Make some adjustments to initial values depending on what is found * in the ICR. */ if (isa16bit && (sc->type != ED_TYPE_WD8013EBT) #ifdef TOSH_ETHER && (sc->type != ED_TYPE_TOSHIBA1) && (sc->type != ED_TYPE_TOSHIBA4) #endif && ((inb(sc->asic_addr + ED_WD_ICR) & ED_WD_ICR_16BIT) == 0)) { isa16bit = 0; memsize = 8192; } #if ED_DEBUG printf("type = %x type_str=%s isa16bit=%d memsize=%d id_msize=%d\n", sc->type, sc->type_str, isa16bit, memsize, isa_dev->id_msize); for (i = 0; i < 8; i++) printf("%x -> %x\n", i, inb(sc->asic_addr + i)); #endif /* * Allow the user to override the autoconfiguration */ if (isa_dev->id_msize) memsize = isa_dev->id_msize; maddr = (u_int) isa_dev->id_maddr & 0xffffff; if (maddr < 0xa0000 || maddr + memsize > 0x1000000) { printf("ed%d: Invalid ISA memory address range configured: 0x%x - 0x%x\n", isa_dev->id_unit, maddr, maddr + memsize); return 0; } /* * (note that if the user specifies both of the following flags that * '8bit' mode intentionally has precedence) */ if (isa_dev->id_flags & ED_FLAGS_FORCE_16BIT_MODE) isa16bit = 1; if (isa_dev->id_flags & ED_FLAGS_FORCE_8BIT_MODE) isa16bit = 0; /* * If possible, get the assigned interrupt number from the card and * use it. */ if ((sc->type & ED_WD_SOFTCONFIG) && (!sc->is790)) { /* * Assemble together the encoded interrupt number. */ iptr = (inb(isa_dev->id_iobase + ED_WD_ICR) & ED_WD_ICR_IR2) | ((inb(isa_dev->id_iobase + ED_WD_IRR) & (ED_WD_IRR_IR0 | ED_WD_IRR_IR1)) >> 5); /* * If no interrupt specified (or "?"), use what the board tells us. */ if (isa_dev->id_irq <= 0) isa_dev->id_irq = ed_intr_mask[iptr]; /* * Enable the interrupt. */ outb(isa_dev->id_iobase + ED_WD_IRR, inb(isa_dev->id_iobase + ED_WD_IRR) | ED_WD_IRR_IEN); } if (sc->is790) { outb(isa_dev->id_iobase + ED_WD790_HWR, inb(isa_dev->id_iobase + ED_WD790_HWR) | ED_WD790_HWR_SWH); iptr = (((inb(isa_dev->id_iobase + ED_WD790_GCR) & ED_WD790_GCR_IR2) >> 4) | (inb(isa_dev->id_iobase + ED_WD790_GCR) & (ED_WD790_GCR_IR1 | ED_WD790_GCR_IR0)) >> 2); outb(isa_dev->id_iobase + ED_WD790_HWR, inb(isa_dev->id_iobase + ED_WD790_HWR) & ~ED_WD790_HWR_SWH); /* * If no interrupt specified (or "?"), use what the board tells us. */ if (isa_dev->id_irq <= 0) isa_dev->id_irq = ed_790_intr_mask[iptr]; /* * Enable interrupts. */ outb(isa_dev->id_iobase + ED_WD790_ICR, inb(isa_dev->id_iobase + ED_WD790_ICR) | ED_WD790_ICR_EIL); } if (isa_dev->id_irq <= 0) { printf("ed%d: %s cards don't support auto-detected/assigned interrupts.\n", isa_dev->id_unit, sc->type_str); return (0); } sc->isa16bit = isa16bit; sc->mem_shared = 1; isa_dev->id_msize = memsize; sc->mem_start = (caddr_t) isa_dev->id_maddr; /* * allocate one xmit buffer if < 16k, two buffers otherwise */ if ((memsize < 16384) || (isa_dev->id_flags & ED_FLAGS_NO_MULTI_BUFFERING)) { sc->txb_cnt = 1; } else { sc->txb_cnt = 2; } sc->tx_page_start = ED_WD_PAGE_OFFSET; sc->rec_page_start = ED_WD_PAGE_OFFSET + ED_TXBUF_SIZE * sc->txb_cnt; sc->rec_page_stop = ED_WD_PAGE_OFFSET + memsize / ED_PAGE_SIZE; sc->mem_ring = sc->mem_start + (ED_PAGE_SIZE * sc->rec_page_start); sc->mem_size = memsize; sc->mem_end = sc->mem_start + memsize; /* * Get station address from on-board ROM */ for (i = 0; i < ETHER_ADDR_LEN; ++i) sc->arpcom.ac_enaddr[i] = inb(sc->asic_addr + ED_WD_PROM + i); /* * Set upper address bits and 8/16 bit access to shared memory. */ if (isa16bit) { if (sc->is790) { sc->wd_laar_proto = inb(sc->asic_addr + ED_WD_LAAR); } else { sc->wd_laar_proto = ED_WD_LAAR_L16EN | ((kvtop(sc->mem_start) >> 19) & ED_WD_LAAR_ADDRHI); } /* * Enable 16bit access */ outb(sc->asic_addr + ED_WD_LAAR, sc->wd_laar_proto | ED_WD_LAAR_M16EN); } else { if (((sc->type & ED_WD_SOFTCONFIG) || #ifdef TOSH_ETHER (sc->type == ED_TYPE_TOSHIBA1) || (sc->type == ED_TYPE_TOSHIBA4) || #endif (sc->type == ED_TYPE_WD8013EBT)) && (!sc->is790)) { sc->wd_laar_proto = (kvtop(sc->mem_start) >> 19) & ED_WD_LAAR_ADDRHI; outb(sc->asic_addr + ED_WD_LAAR, sc->wd_laar_proto); } } /* * Set address and enable interface shared memory. */ if (!sc->is790) { #ifdef TOSH_ETHER outb(sc->asic_addr + ED_WD_MSR + 1, ((kvtop(sc->mem_start) >> 8) & 0xe0) | 4); outb(sc->asic_addr + ED_WD_MSR + 2, ((kvtop(sc->mem_start) >> 16) & 0x0f)); outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_MENB | ED_WD_MSR_POW); #else outb(sc->asic_addr + ED_WD_MSR, ((kvtop(sc->mem_start) >> 13) & ED_WD_MSR_ADDR) | ED_WD_MSR_MENB); #endif sc->cr_proto = ED_CR_RD2; } else { outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_MENB); outb(sc->asic_addr + ED_WD790_HWR, (inb(sc->asic_addr + ED_WD790_HWR) | ED_WD790_HWR_SWH)); outb(sc->asic_addr + ED_WD790_RAR, ((kvtop(sc->mem_start) >> 13) & 0x0f) | ((kvtop(sc->mem_start) >> 11) & 0x40) | (inb(sc->asic_addr + ED_WD790_RAR) & 0xb0)); outb(sc->asic_addr + ED_WD790_HWR, (inb(sc->asic_addr + ED_WD790_HWR) & ~ED_WD790_HWR_SWH)); sc->cr_proto = 0; } #if 0 printf("starting memory performance test at 0x%x, size %d...\n", sc->mem_start, memsize*16384); for (i = 0; i < 16384; i++) bzero(sc->mem_start, memsize); printf("***DONE***\n"); #endif /* * Now zero memory and verify that it is clear */ bzero(sc->mem_start, memsize); for (i = 0; i < memsize; ++i) { if (sc->mem_start[i]) { printf("ed%d: failed to clear shared memory at %lx - check configuration\n", isa_dev->id_unit, kvtop(sc->mem_start + i)); /* * Disable 16 bit access to shared memory */ if (isa16bit) { if (sc->is790) { outb(sc->asic_addr + ED_WD_MSR, 0x00); } outb(sc->asic_addr + ED_WD_LAAR, sc->wd_laar_proto & ~ED_WD_LAAR_M16EN); } return (0); } } /* * Disable 16bit access to shared memory - we leave it * disabled so that 1) machines reboot properly when the board * is set 16 bit mode and there are conflicting 8bit * devices/ROMS in the same 128k address space as this boards * shared memory. and 2) so that other 8 bit devices with * shared memory can be used in this 128k region, too. */ if (isa16bit) { if (sc->is790) { outb(sc->asic_addr + ED_WD_MSR, 0x00); } outb(sc->asic_addr + ED_WD_LAAR, sc->wd_laar_proto & ~ED_WD_LAAR_M16EN); } return (ED_WD_IO_PORTS); } /* * Probe and vendor-specific initialization routine for 3Com 3c503 boards */ static int ed_probe_3Com(isa_dev) struct isa_device *isa_dev; { struct ed_softc *sc = &ed_softc[isa_dev->id_unit]; int i; u_int memsize; u_char isa16bit; sc->asic_addr = isa_dev->id_iobase + ED_3COM_ASIC_OFFSET; sc->nic_addr = isa_dev->id_iobase + ED_3COM_NIC_OFFSET; /* * Verify that the kernel configured I/O address matches the board * configured address */ switch (inb(sc->asic_addr + ED_3COM_BCFR)) { case ED_3COM_BCFR_300: if (isa_dev->id_iobase != 0x300) return (0); break; case ED_3COM_BCFR_310: if (isa_dev->id_iobase != 0x310) return (0); break; case ED_3COM_BCFR_330: if (isa_dev->id_iobase != 0x330) return (0); break; case ED_3COM_BCFR_350: if (isa_dev->id_iobase != 0x350) return (0); break; case ED_3COM_BCFR_250: if (isa_dev->id_iobase != 0x250) return (0); break; case ED_3COM_BCFR_280: if (isa_dev->id_iobase != 0x280) return (0); break; case ED_3COM_BCFR_2A0: if (isa_dev->id_iobase != 0x2a0) return (0); break; case ED_3COM_BCFR_2E0: if (isa_dev->id_iobase != 0x2e0) return (0); break; default: return (0); } /* * Verify that the kernel shared memory address matches the board * configured address. */ switch (inb(sc->asic_addr + ED_3COM_PCFR)) { case ED_3COM_PCFR_DC000: if (kvtop(isa_dev->id_maddr) != 0xdc000) return (0); break; case ED_3COM_PCFR_D8000: if (kvtop(isa_dev->id_maddr) != 0xd8000) return (0); break; case ED_3COM_PCFR_CC000: if (kvtop(isa_dev->id_maddr) != 0xcc000) return (0); break; case ED_3COM_PCFR_C8000: if (kvtop(isa_dev->id_maddr) != 0xc8000) return (0); break; default: return (0); } /* * Reset NIC and ASIC. Enable on-board transceiver throughout reset * sequence because it'll lock up if the cable isn't connected if we * don't. */ outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_RST | ED_3COM_CR_XSEL); /* * Wait for a while, then un-reset it */ DELAY(50); /* * The 3Com ASIC defaults to rather strange settings for the CR after * a reset - it's important to set it again after the following outb * (this is done when we map the PROM below). */ outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_XSEL); /* * Wait a bit for the NIC to recover from the reset */ DELAY(5000); sc->vendor = ED_VENDOR_3COM; sc->type_str = "3c503"; sc->mem_shared = 1; sc->cr_proto = ED_CR_RD2; /* * Hmmm...a 16bit 3Com board has 16k of memory, but only an 8k window * to it. */ memsize = 8192; /* * Get station address from on-board ROM */ /* * First, map ethernet address PROM over the top of where the NIC * registers normally appear. */ outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_EALO | ED_3COM_CR_XSEL); for (i = 0; i < ETHER_ADDR_LEN; ++i) sc->arpcom.ac_enaddr[i] = inb(sc->nic_addr + i); /* * Unmap PROM - select NIC registers. The proper setting of the * tranceiver is set in ed_init so that the attach code is given a * chance to set the default based on a compile-time config option */ outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_XSEL); /* * Determine if this is an 8bit or 16bit board */ /* * select page 0 registers */ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_STP); /* * Attempt to clear WTS bit. If it doesn't clear, then this is a 16bit * board. */ outb(sc->nic_addr + ED_P0_DCR, 0); /* * select page 2 registers */ outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_2 | ED_CR_RD2 | ED_CR_STP); /* * The 3c503 forces the WTS bit to a one if this is a 16bit board */ if (inb(sc->nic_addr + ED_P2_DCR) & ED_DCR_WTS) isa16bit = 1; else isa16bit = 0; /* * select page 0 registers */ outb(sc->nic_addr + ED_P2_CR, ED_CR_RD2 | ED_CR_STP); sc->mem_start = (caddr_t) isa_dev->id_maddr; sc->mem_size = memsize; sc->mem_end = sc->mem_start + memsize; /* * We have an entire 8k window to put the transmit buffers on the * 16bit boards. But since the 16bit 3c503's shared memory is only * fast enough to overlap the loading of one full-size packet, trying * to load more than 2 buffers can actually leave the transmitter idle * during the load. So 2 seems the best value. (Although a mix of * variable-sized packets might change this assumption. Nonetheless, * we optimize for linear transfers of same-size packets.) */ if (isa16bit) { if (isa_dev->id_flags & ED_FLAGS_NO_MULTI_BUFFERING) sc->txb_cnt = 1; else sc->txb_cnt = 2; sc->tx_page_start = ED_3COM_TX_PAGE_OFFSET_16BIT; sc->rec_page_start = ED_3COM_RX_PAGE_OFFSET_16BIT; sc->rec_page_stop = memsize / ED_PAGE_SIZE + ED_3COM_RX_PAGE_OFFSET_16BIT; sc->mem_ring = sc->mem_start; } else { sc->txb_cnt = 1; sc->tx_page_start = ED_3COM_TX_PAGE_OFFSET_8BIT; sc->rec_page_start = ED_TXBUF_SIZE + ED_3COM_TX_PAGE_OFFSET_8BIT; sc->rec_page_stop = memsize / ED_PAGE_SIZE + ED_3COM_TX_PAGE_OFFSET_8BIT; sc->mem_ring = sc->mem_start + (ED_PAGE_SIZE * ED_TXBUF_SIZE); } sc->isa16bit = isa16bit; /* * Initialize GA page start/stop registers. Probably only needed if * doing DMA, but what the hell. */ outb(sc->asic_addr + ED_3COM_PSTR, sc->rec_page_start); outb(sc->asic_addr + ED_3COM_PSPR, sc->rec_page_stop); /* * Set IRQ. 3c503 only allows a choice of irq 2-5. */ switch (isa_dev->id_irq) { case IRQ2: outb(sc->asic_addr + ED_3COM_IDCFR, ED_3COM_IDCFR_IRQ2); break; case IRQ3: outb(sc->asic_addr + ED_3COM_IDCFR, ED_3COM_IDCFR_IRQ3); break; case IRQ4: outb(sc->asic_addr + ED_3COM_IDCFR, ED_3COM_IDCFR_IRQ4); break; case IRQ5: outb(sc->asic_addr + ED_3COM_IDCFR, ED_3COM_IDCFR_IRQ5); break; default: printf("ed%d: Invalid irq configuration (%d) must be 3-5,9 for 3c503\n", isa_dev->id_unit, ffs(isa_dev->id_irq) - 1); return (0); } /* * Initialize GA configuration register. Set bank and enable shared * mem. */ outb(sc->asic_addr + ED_3COM_GACFR, ED_3COM_GACFR_RSEL | ED_3COM_GACFR_MBS0); /* * Initialize "Vector Pointer" registers. These gawd-awful things are * compared to 20 bits of the address on ISA, and if they match, the * shared memory is disabled. We set them to 0xffff0...allegedly the * reset vector. */ outb(sc->asic_addr + ED_3COM_VPTR2, 0xff); outb(sc->asic_addr + ED_3COM_VPTR1, 0xff); outb(sc->asic_addr + ED_3COM_VPTR0, 0x00); /* * Zero memory and verify that it is clear */ bzero(sc->mem_start, memsize); for (i = 0; i < memsize; ++i) if (sc->mem_start[i]) { printf("ed%d: failed to clear shared memory at %lx - check configuration\n", isa_dev->id_unit, kvtop(sc->mem_start + i)); return (0); } isa_dev->id_msize = memsize; return (ED_3COM_IO_PORTS); } /* * Probe and vendor-specific initialization routine for NE1000/2000 boards */ static int ed_probe_Novell_generic(sc, port, unit, flags) struct ed_softc *sc; int port; int unit; int flags; { u_int memsize, n; u_char romdata[16], tmp; static char test_pattern[32] = "THIS is A memory TEST pattern"; char test_buffer[32]; sc->asic_addr = port + ED_NOVELL_ASIC_OFFSET; sc->nic_addr = port + ED_NOVELL_NIC_OFFSET; /* XXX - do Novell-specific probe here */ /* Reset the board */ #ifdef GWETHER outb(sc->asic_addr + ED_NOVELL_RESET, 0); DELAY(200); #endif /* GWETHER */ tmp = inb(sc->asic_addr + ED_NOVELL_RESET); /* * I don't know if this is necessary; probably cruft leftover from * Clarkson packet driver code. Doesn't do a thing on the boards I've * tested. -DG [note that a outb(0x84, 0) seems to work here, and is * non-invasive...but some boards don't seem to reset and I don't have * complete documentation on what the 'right' thing to do is...so we * do the invasive thing for now. Yuck.] */ outb(sc->asic_addr + ED_NOVELL_RESET, tmp); DELAY(5000); /* * This is needed because some NE clones apparently don't reset the * NIC properly (or the NIC chip doesn't reset fully on power-up) XXX * - this makes the probe invasive! ...Done against my better * judgement. -DLG */ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_STP); DELAY(5000); /* Make sure that we really have an 8390 based board */ if (!ed_probe_generic8390(sc)) return (0); sc->vendor = ED_VENDOR_NOVELL; sc->mem_shared = 0; sc->cr_proto = ED_CR_RD2; /* * Test the ability to read and write to the NIC memory. This has the * side affect of determining if this is an NE1000 or an NE2000. */ /* * This prevents packets from being stored in the NIC memory when the * readmem routine turns on the start bit in the CR. */ outb(sc->nic_addr + ED_P0_RCR, ED_RCR_MON); /* Temporarily initialize DCR for byte operations */ outb(sc->nic_addr + ED_P0_DCR, ED_DCR_FT1 | ED_DCR_LS); outb(sc->nic_addr + ED_P0_PSTART, 8192 / ED_PAGE_SIZE); outb(sc->nic_addr + ED_P0_PSTOP, 16384 / ED_PAGE_SIZE); sc->isa16bit = 0; /* * Write a test pattern in byte mode. If this fails, then there * probably isn't any memory at 8k - which likely means that the board * is an NE2000. */ ed_pio_writemem(sc, test_pattern, 8192, sizeof(test_pattern)); ed_pio_readmem(sc, 8192, test_buffer, sizeof(test_pattern)); if (bcmp(test_pattern, test_buffer, sizeof(test_pattern))) { /* not an NE1000 - try NE2000 */ outb(sc->nic_addr + ED_P0_DCR, ED_DCR_WTS | ED_DCR_FT1 | ED_DCR_LS); outb(sc->nic_addr + ED_P0_PSTART, 16384 / ED_PAGE_SIZE); outb(sc->nic_addr + ED_P0_PSTOP, 32768 / ED_PAGE_SIZE); sc->isa16bit = 1; /* * Write a test pattern in word mode. If this also fails, then * we don't know what this board is. */ ed_pio_writemem(sc, test_pattern, 16384, sizeof(test_pattern)); ed_pio_readmem(sc, 16384, test_buffer, sizeof(test_pattern)); if (bcmp(test_pattern, test_buffer, sizeof(test_pattern))) return (0); /* not an NE2000 either */ sc->type = ED_TYPE_NE2000; sc->type_str = "NE2000"; } else { sc->type = ED_TYPE_NE1000; sc->type_str = "NE1000"; } /* 8k of memory plus an additional 8k if 16bit */ memsize = 8192 + sc->isa16bit * 8192; #if 0 /* probably not useful - NE boards only come two ways */ /* allow kernel config file overrides */ if (isa_dev->id_msize) memsize = isa_dev->id_msize; #endif sc->mem_size = memsize; /* NIC memory doesn't start at zero on an NE board */ /* The start address is tied to the bus width */ sc->mem_start = (char *) 8192 + sc->isa16bit * 8192; sc->mem_end = sc->mem_start + memsize; sc->tx_page_start = memsize / ED_PAGE_SIZE; #ifdef GWETHER { int x, i, mstart = 0, msize = 0; char pbuf0[ED_PAGE_SIZE], pbuf[ED_PAGE_SIZE], tbuf[ED_PAGE_SIZE]; for (i = 0; i < ED_PAGE_SIZE; i++) pbuf0[i] = 0; /* Clear all the memory. */ for (x = 1; x < 256; x++) ed_pio_writemem(sc, pbuf0, x * 256, ED_PAGE_SIZE); /* Search for the start of RAM. */ for (x = 1; x < 256; x++) { ed_pio_readmem(sc, x * 256, tbuf, ED_PAGE_SIZE); if (bcmp(pbuf0, tbuf, ED_PAGE_SIZE) == 0) { for (i = 0; i < ED_PAGE_SIZE; i++) pbuf[i] = 255 - x; ed_pio_writemem(sc, pbuf, x * 256, ED_PAGE_SIZE); ed_pio_readmem(sc, x * 256, tbuf, ED_PAGE_SIZE); if (bcmp(pbuf, tbuf, ED_PAGE_SIZE) == 0) { mstart = x * ED_PAGE_SIZE; msize = ED_PAGE_SIZE; break; } } } if (mstart == 0) { printf("ed%d: Cannot find start of RAM.\n", unit); return 0; } /* Search for the start of RAM. */ for (x = (mstart / ED_PAGE_SIZE) + 1; x < 256; x++) { ed_pio_readmem(sc, x * 256, tbuf, ED_PAGE_SIZE); if (bcmp(pbuf0, tbuf, ED_PAGE_SIZE) == 0) { for (i = 0; i < ED_PAGE_SIZE; i++) pbuf[i] = 255 - x; ed_pio_writemem(sc, pbuf, x * 256, ED_PAGE_SIZE); ed_pio_readmem(sc, x * 256, tbuf, ED_PAGE_SIZE); if (bcmp(pbuf, tbuf, ED_PAGE_SIZE) == 0) msize += ED_PAGE_SIZE; else { break; } } else { break; } } if (msize == 0) { printf("ed%d: Cannot find any RAM, start : %d, x = %d.\n", unit, mstart, x); return 0; } printf("ed%d: RAM start at %d, size : %d.\n", unit, mstart, msize); sc->mem_size = msize; sc->mem_start = (char *) mstart; sc->mem_end = (char *) (msize + mstart); sc->tx_page_start = mstart / ED_PAGE_SIZE; } #endif /* GWETHER */ /* * Use one xmit buffer if < 16k, two buffers otherwise (if not told * otherwise). */ if ((memsize < 16384) || (flags & ED_FLAGS_NO_MULTI_BUFFERING)) sc->txb_cnt = 1; else sc->txb_cnt = 2; sc->rec_page_start = sc->tx_page_start + sc->txb_cnt * ED_TXBUF_SIZE; sc->rec_page_stop = sc->tx_page_start + memsize / ED_PAGE_SIZE; sc->mem_ring = sc->mem_start + sc->txb_cnt * ED_PAGE_SIZE * ED_TXBUF_SIZE; ed_pio_readmem(sc, 0, romdata, 16); for (n = 0; n < ETHER_ADDR_LEN; n++) sc->arpcom.ac_enaddr[n] = romdata[n * (sc->isa16bit + 1)]; #ifdef GWETHER if (sc->arpcom.ac_enaddr[2] == 0x86) { sc->type_str = "Gateway AT"; } #endif /* GWETHER */ /* clear any pending interrupts that might have occurred above */ outb(sc->nic_addr + ED_P0_ISR, 0xff); return (ED_NOVELL_IO_PORTS); } static int ed_probe_Novell(isa_dev) struct isa_device *isa_dev; { struct ed_softc *sc = &ed_softc[isa_dev->id_unit]; isa_dev->id_maddr = 0; return ed_probe_Novell_generic(sc, isa_dev->id_iobase, isa_dev->id_unit, isa_dev->id_flags); } #if NCARD > 0 /* * Probe framework for pccards. Replicates the standard framework, * minus the pccard driver registration and ignores the ether address * supplied (from the CIS), relying on the probe to find it instead. */ static int ed_probe_pccard(isa_dev, ether) struct isa_device *isa_dev; u_char *ether; { int nports; nports = ed_probe_WD80x3(isa_dev); if (nports) return (nports); nports = ed_probe_Novell(isa_dev); if (nports) return (nports); return (0); } #endif /* NCARD > 0 */ #define ED_HPP_TEST_SIZE 16 /* * Probe and vendor specific initialization for the HP PC Lan+ Cards. * (HP Part nos: 27247B and 27252A). * * The card has an asic wrapper around a DS8390 core. The asic handles * host accesses and offers both standard register IO and memory mapped * IO. Memory mapped I/O allows better performance at the expense of greater * chance of an incompatibility with existing ISA cards. * * The card has a few caveats: it isn't tolerant of byte wide accesses, only * short (16 bit) or word (32 bit) accesses are allowed. Some card revisions * don't allow 32 bit accesses; these are indicated by a bit in the software * ID register (see if_edreg.h). * * Other caveats are: we should read the MAC address only when the card * is inactive. * * For more information; please consult the CRYNWR packet driver. * * The AUI port is turned on using the "link2" option on the ifconfig * command line. */ static int ed_probe_HP_pclanp(isa_dev) struct isa_device *isa_dev; { struct ed_softc *sc = &ed_softc[isa_dev->id_unit]; int n; /* temp var */ int memsize; /* mem on board */ u_char checksum; /* checksum of board address */ u_char irq; /* board configured IRQ */ char test_pattern[ED_HPP_TEST_SIZE]; /* read/write areas for */ char test_buffer[ED_HPP_TEST_SIZE]; /* probing card */ /* Fill in basic information */ sc->asic_addr = isa_dev->id_iobase + ED_HPP_ASIC_OFFSET; sc->nic_addr = isa_dev->id_iobase + ED_HPP_NIC_OFFSET; sc->is790 = 0; sc->isa16bit = 0; /* the 8390 core needs to be in byte mode */ /* * Look for the HP PCLAN+ signature: "0x50,0x48,0x00,0x53" */ if ((inb(sc->asic_addr + ED_HPP_ID) != 0x50) || (inb(sc->asic_addr + ED_HPP_ID + 1) != 0x48) || ((inb(sc->asic_addr + ED_HPP_ID + 2) & 0xF0) != 0) || (inb(sc->asic_addr + ED_HPP_ID + 3) != 0x53)) return 0; /* * Read the MAC address and verify checksum on the address. */ outw(sc->asic_addr + ED_HPP_PAGING, ED_HPP_PAGE_MAC); for (n = 0, checksum = 0; n < ETHER_ADDR_LEN; n++) checksum += (sc->arpcom.ac_enaddr[n] = inb(sc->asic_addr + ED_HPP_MAC_ADDR + n)); checksum += inb(sc->asic_addr + ED_HPP_MAC_ADDR + ETHER_ADDR_LEN); if (checksum != 0xFF) return 0; /* * Verify that the software model number is 0. */ outw(sc->asic_addr + ED_HPP_PAGING, ED_HPP_PAGE_ID); if (((sc->hpp_id = inw(sc->asic_addr + ED_HPP_PAGE_4)) & ED_HPP_ID_SOFT_MODEL_MASK) != 0x0000) return 0; /* * Read in and save the current options configured on card. */ sc->hpp_options = inw(sc->asic_addr + ED_HPP_OPTION); sc->hpp_options |= (ED_HPP_OPTION_NIC_RESET | ED_HPP_OPTION_CHIP_RESET | ED_HPP_OPTION_ENABLE_IRQ); /* * Reset the chip. This requires writing to the option register * so take care to preserve the other bits. */ outw(sc->asic_addr + ED_HPP_OPTION, (sc->hpp_options & ~(ED_HPP_OPTION_NIC_RESET | ED_HPP_OPTION_CHIP_RESET))); DELAY(5000); /* wait for chip reset to complete */ outw(sc->asic_addr + ED_HPP_OPTION, (sc->hpp_options | (ED_HPP_OPTION_NIC_RESET | ED_HPP_OPTION_CHIP_RESET | ED_HPP_OPTION_ENABLE_IRQ))); DELAY(5000); if (!(inb(sc->nic_addr + ED_P0_ISR) & ED_ISR_RST)) return 0; /* reset did not complete */ /* * Read out configuration information. */ outw(sc->asic_addr + ED_HPP_PAGING, ED_HPP_PAGE_HW); irq = inb(sc->asic_addr + ED_HPP_HW_IRQ); /* * Check for impossible IRQ. */ if (irq >= (sizeof(ed_hpp_intr_mask) / sizeof(ed_hpp_intr_mask[0]))) return 0; /* * If the kernel IRQ was specified with a '?' use the cards idea * of the IRQ. If the kernel IRQ was explicitly specified, it * should match that of the hardware. */ if (isa_dev->id_irq <= 0) isa_dev->id_irq = ed_hpp_intr_mask[irq]; else if (isa_dev->id_irq != ed_hpp_intr_mask[irq]) return 0; /* * Fill in softconfig info. */ sc->vendor = ED_VENDOR_HP; sc->type = ED_TYPE_HP_PCLANPLUS; sc->type_str = "HP-PCLAN+"; sc->mem_shared = 0; /* we DON'T have dual ported RAM */ sc->mem_start = 0; /* we use offsets inside the card RAM */ sc->hpp_mem_start = NULL;/* no memory mapped I/O by default */ /* * Check if memory mapping of the I/O registers possible. */ if (sc->hpp_options & ED_HPP_OPTION_MEM_ENABLE) { u_long mem_addr; /* * determine the memory address from the board. */ outw(sc->asic_addr + ED_HPP_PAGING, ED_HPP_PAGE_HW); mem_addr = (inw(sc->asic_addr + ED_HPP_HW_MEM_MAP) << 8); /* * Check that the kernel specified start of memory and * hardware's idea of it match. */ if (mem_addr != kvtop(isa_dev->id_maddr)) return 0; sc->hpp_mem_start = isa_dev->id_maddr; } /* * The board has 32KB of memory. Is there a way to determine * this programmatically? */ memsize = 32768; /* * Fill in the rest of the soft config structure. */ /* * The transmit page index. */ sc->tx_page_start = ED_HPP_TX_PAGE_OFFSET; if (isa_dev->id_flags & ED_FLAGS_NO_MULTI_BUFFERING) sc->txb_cnt = 1; else sc->txb_cnt = 2; /* * Memory description */ sc->mem_size = memsize; sc->mem_ring = sc->mem_start + (sc->txb_cnt * ED_PAGE_SIZE * ED_TXBUF_SIZE); sc->mem_end = sc->mem_start + sc->mem_size; /* * Receive area starts after the transmit area and * continues till the end of memory. */ sc->rec_page_start = sc->tx_page_start + (sc->txb_cnt * ED_TXBUF_SIZE); sc->rec_page_stop = (sc->mem_size / ED_PAGE_SIZE); sc->cr_proto = 0; /* value works */ /* * Set the wrap registers for string I/O reads. */ outw(sc->asic_addr + ED_HPP_PAGING, ED_HPP_PAGE_HW); outw(sc->asic_addr + ED_HPP_HW_WRAP, ((sc->rec_page_start / ED_PAGE_SIZE) | (((sc->rec_page_stop / ED_PAGE_SIZE) - 1) << 8))); /* * Reset the register page to normal operation. */ outw(sc->asic_addr + ED_HPP_PAGING, ED_HPP_PAGE_PERF); /* * Verify that we can read/write from adapter memory. * Create test pattern. */ for (n = 0; n < ED_HPP_TEST_SIZE; n++) { test_pattern[n] = (n*n) ^ ~n; } #undef ED_HPP_TEST_SIZE /* * Check that the memory is accessible thru the I/O ports. * Write out the contents of "test_pattern", read back * into "test_buffer" and compare the two for any * mismatch. */ for (n = 0; n < (32768 / ED_PAGE_SIZE); n ++) { ed_pio_writemem(sc, test_pattern, (n * ED_PAGE_SIZE), sizeof(test_pattern)); ed_pio_readmem(sc, (n * ED_PAGE_SIZE), test_buffer, sizeof(test_pattern)); if (bcmp(test_pattern, test_buffer, sizeof(test_pattern))) return 0; } return (ED_HPP_IO_PORTS); } /* * HP PC Lan+ : Set the physical link to use AUI or TP/TL. */ void ed_hpp_set_physical_link(struct ed_softc *sc) { struct ifnet *ifp = &sc->arpcom.ac_if; int lan_page; outw(sc->asic_addr + ED_HPP_PAGING, ED_HPP_PAGE_LAN); lan_page = inw(sc->asic_addr + ED_HPP_PAGE_0); if (ifp->if_flags & IFF_ALTPHYS) { /* * Use the AUI port. */ lan_page |= ED_HPP_LAN_AUI; outw(sc->asic_addr + ED_HPP_PAGING, ED_HPP_PAGE_LAN); outw(sc->asic_addr + ED_HPP_PAGE_0, lan_page); } else { /* * Use the ThinLan interface */ lan_page &= ~ED_HPP_LAN_AUI; outw(sc->asic_addr + ED_HPP_PAGING, ED_HPP_PAGE_LAN); outw(sc->asic_addr + ED_HPP_PAGE_0, lan_page); } /* * Wait for the lan card to re-initialize itself */ DELAY(150000); /* wait 150 ms */ /* * Restore normal pages. */ outw(sc->asic_addr + ED_HPP_PAGING, ED_HPP_PAGE_PERF); } /* * Install interface into kernel networking data structures */ static int ed_attach(sc, unit, flags) struct ed_softc *sc; int unit; int flags; { struct ifnet *ifp = &sc->arpcom.ac_if; /* * Set interface to stopped condition (reset) */ ed_stop(sc); if (!ifp->if_name) { /* * Initialize ifnet structure */ ifp->if_softc = sc; ifp->if_unit = unit; ifp->if_name = "ed"; ifp->if_output = ether_output; ifp->if_start = ed_start; ifp->if_ioctl = ed_ioctl; ifp->if_watchdog = ed_watchdog; ifp->if_init = ed_init; ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; ifp->if_linkmib = &sc->mibdata; ifp->if_linkmiblen = sizeof sc->mibdata; /* * XXX - should do a better job. */ if (sc->is790) sc->mibdata.dot3StatsEtherChipSet = DOT3CHIPSET(dot3VendorWesternDigital, dot3ChipSetWesternDigital83C790); else sc->mibdata.dot3StatsEtherChipSet = DOT3CHIPSET(dot3VendorNational, dot3ChipSetNational8390); sc->mibdata.dot3Compliance = DOT3COMPLIANCE_COLLS; /* * Set default state for ALTPHYS flag (used to disable the * tranceiver for AUI operation), based on compile-time * config option. */ if (flags & ED_FLAGS_DISABLE_TRANCEIVER) ifp->if_flags = (IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST | IFF_ALTPHYS); else ifp->if_flags = (IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST); /* * Attach the interface */ if_attach(ifp); ether_ifattach(ifp); } /* device attach does transition from UNCONFIGURED to IDLE state */ /* * Print additional info when attached */ printf("%s%d: address %6D, ", ifp->if_name, ifp->if_unit, sc->arpcom.ac_enaddr, ":"); if (sc->type_str && (*sc->type_str != 0)) printf("type %s ", sc->type_str); else printf("type unknown (0x%x) ", sc->type); if (sc->vendor == ED_VENDOR_HP) printf("(%s %s IO)", (sc->hpp_id & ED_HPP_ID_16_BIT_ACCESS) ? "16-bit" : "32-bit", sc->hpp_mem_start ? "memory mapped" : "regular"); else printf("%s ", sc->isa16bit ? "(16 bit)" : "(8 bit)"); printf("%s\n", (((sc->vendor == ED_VENDOR_3COM) || (sc->vendor == ED_VENDOR_HP)) && (ifp->if_flags & IFF_ALTPHYS)) ? " tranceiver disabled" : ""); /* * If BPF is in the kernel, call the attach for it */ #if NBPFILTER > 0 bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); #endif return 1; } static int ed_attach_isa(isa_dev) struct isa_device *isa_dev; { int unit = isa_dev->id_unit; struct ed_softc *sc = &ed_softc[unit]; int flags = isa_dev->id_flags; isa_dev->id_ointr = edintr; return ed_attach(sc, unit, flags); } #if NPCI > 0 void * ed_attach_NE2000_pci(unit, port) int unit; int port; { struct ed_softc *sc = malloc(sizeof *sc, M_DEVBUF, M_NOWAIT); int isa_flags = 0; if (!sc) return sc; bzero(sc, sizeof *sc); if (ed_probe_Novell_generic(sc, port, unit, isa_flags) == 0 || ed_attach(sc, unit, isa_flags) == 0) { free(sc, M_DEVBUF); return NULL; } return sc; } #endif /* * Reset interface. */ static void ed_reset(ifp) struct ifnet *ifp; { struct ed_softc *sc = ifp->if_softc; int s; if (sc->gone) return; s = splimp(); /* * Stop interface and re-initialize. */ ed_stop(sc); ed_init(sc); (void) splx(s); } /* * Take interface offline. */ static void ed_stop(sc) struct ed_softc *sc; { int n = 5000; if (sc->gone) return; /* * Stop everything on the interface, and select page 0 registers. */ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_STP); /* * Wait for interface to enter stopped state, but limit # of checks to * 'n' (about 5ms). It shouldn't even take 5us on modern DS8390's, but * just in case it's an old one. */ while (((inb(sc->nic_addr + ED_P0_ISR) & ED_ISR_RST) == 0) && --n); } /* * Device timeout/watchdog routine. Entered if the device neglects to * generate an interrupt after a transmit has been started on it. */ static void ed_watchdog(ifp) struct ifnet *ifp; { struct ed_softc *sc = ifp->if_softc; if (sc->gone) return; log(LOG_ERR, "ed%d: device timeout\n", ifp->if_unit); ifp->if_oerrors++; ed_reset(ifp); } /* * Initialize device. */ static void ed_init(xsc) void *xsc; { struct ed_softc *sc = xsc; struct ifnet *ifp = &sc->arpcom.ac_if; int i, s; if (sc->gone) return; /* address not known */ if (TAILQ_EMPTY(&ifp->if_addrhead)) /* unlikely? XXX */ return; /* * Initialize the NIC in the exact order outlined in the NS manual. * This init procedure is "mandatory"...don't change what or when * things happen. */ s = splimp(); /* reset transmitter flags */ sc->xmit_busy = 0; ifp->if_timer = 0; sc->txb_inuse = 0; sc->txb_new = 0; sc->txb_next_tx = 0; /* This variable is used below - don't move this assignment */ sc->next_packet = sc->rec_page_start + 1; /* * Set interface for page 0, Remote DMA complete, Stopped */ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_STP); if (sc->isa16bit) { /* * Set FIFO threshold to 8, No auto-init Remote DMA, byte * order=80x86, word-wide DMA xfers, */ outb(sc->nic_addr + ED_P0_DCR, ED_DCR_FT1 | ED_DCR_WTS | ED_DCR_LS); } else { /* * Same as above, but byte-wide DMA xfers */ outb(sc->nic_addr + ED_P0_DCR, ED_DCR_FT1 | ED_DCR_LS); } /* * Clear Remote Byte Count Registers */ outb(sc->nic_addr + ED_P0_RBCR0, 0); outb(sc->nic_addr + ED_P0_RBCR1, 0); /* * For the moment, don't store incoming packets in memory. */ outb(sc->nic_addr + ED_P0_RCR, ED_RCR_MON); /* * Place NIC in internal loopback mode */ outb(sc->nic_addr + ED_P0_TCR, ED_TCR_LB0); /* * Initialize transmit/receive (ring-buffer) Page Start */ outb(sc->nic_addr + ED_P0_TPSR, sc->tx_page_start); outb(sc->nic_addr + ED_P0_PSTART, sc->rec_page_start); /* Set lower bits of byte addressable framing to 0 */ if (sc->is790) outb(sc->nic_addr + 0x09, 0); /* * Initialize Receiver (ring-buffer) Page Stop and Boundry */ outb(sc->nic_addr + ED_P0_PSTOP, sc->rec_page_stop); outb(sc->nic_addr + ED_P0_BNRY, sc->rec_page_start); /* * Clear all interrupts. A '1' in each bit position clears the * corresponding flag. */ outb(sc->nic_addr + ED_P0_ISR, 0xff); /* * Enable the following interrupts: receive/transmit complete, * receive/transmit error, and Receiver OverWrite. * * Counter overflow and Remote DMA complete are *not* enabled. */ outb(sc->nic_addr + ED_P0_IMR, ED_IMR_PRXE | ED_IMR_PTXE | ED_IMR_RXEE | ED_IMR_TXEE | ED_IMR_OVWE); /* * Program Command Register for page 1 */ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_PAGE_1 | ED_CR_STP); /* * Copy out our station address */ for (i = 0; i < ETHER_ADDR_LEN; ++i) outb(sc->nic_addr + ED_P1_PAR0 + i, sc->arpcom.ac_enaddr[i]); /* * Set Current Page pointer to next_packet (initialized above) */ outb(sc->nic_addr + ED_P1_CURR, sc->next_packet); /* * Program Receiver Configuration Register and multicast filter. CR is * set to page 0 on return. */ ed_setrcr(sc); /* * Take interface out of loopback */ outb(sc->nic_addr + ED_P0_TCR, 0); /* * If this is a 3Com board, the tranceiver must be software enabled * (there is no settable hardware default). */ if (sc->vendor == ED_VENDOR_3COM) { if (ifp->if_flags & IFF_ALTPHYS) { outb(sc->asic_addr + ED_3COM_CR, 0); } else { outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_XSEL); } } /* * Set 'running' flag, and clear output active flag. */ ifp->if_flags |= IFF_RUNNING; ifp->if_flags &= ~IFF_OACTIVE; /* * ...and attempt to start output */ ed_start(ifp); (void) splx(s); } /* * This routine actually starts the transmission on the interface */ static __inline void ed_xmit(sc) struct ed_softc *sc; { struct ifnet *ifp = (struct ifnet *)sc; unsigned short len; if (sc->gone) return; len = sc->txb_len[sc->txb_next_tx]; /* * Set NIC for page 0 register access */ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_STA); /* * Set TX buffer start page */ outb(sc->nic_addr + ED_P0_TPSR, sc->tx_page_start + sc->txb_next_tx * ED_TXBUF_SIZE); /* * Set TX length */ outb(sc->nic_addr + ED_P0_TBCR0, len); outb(sc->nic_addr + ED_P0_TBCR1, len >> 8); /* * Set page 0, Remote DMA complete, Transmit Packet, and *Start* */ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_TXP | ED_CR_STA); sc->xmit_busy = 1; /* * Point to next transmit buffer slot and wrap if necessary. */ sc->txb_next_tx++; if (sc->txb_next_tx == sc->txb_cnt) sc->txb_next_tx = 0; /* * Set a timer just in case we never hear from the board again */ ifp->if_timer = 2; } /* * Start output on interface. * We make two assumptions here: * 1) that the current priority is set to splimp _before_ this code * is called *and* is returned to the appropriate priority after * return * 2) that the IFF_OACTIVE flag is checked before this code is called * (i.e. that the output part of the interface is idle) */ static void ed_start(ifp) struct ifnet *ifp; { struct ed_softc *sc = ifp->if_softc; struct mbuf *m0, *m; caddr_t buffer; int len; if (sc->gone) { printf("ed_start(%p) GONE\n",ifp); return; } outloop: /* * First, see if there are buffered packets and an idle transmitter - * should never happen at this point. */ if (sc->txb_inuse && (sc->xmit_busy == 0)) { printf("ed: packets buffered, but transmitter idle\n"); ed_xmit(sc); } /* * See if there is room to put another packet in the buffer. */ if (sc->txb_inuse == sc->txb_cnt) { /* * No room. Indicate this to the outside world and exit. */ ifp->if_flags |= IFF_OACTIVE; return; } IF_DEQUEUE(&ifp->if_snd, m); if (m == 0) { /* * We are using the !OACTIVE flag to indicate to the outside * world that we can accept an additional packet rather than * that the transmitter is _actually_ active. Indeed, the * transmitter may be active, but if we haven't filled all the * buffers with data then we still want to accept more. */ ifp->if_flags &= ~IFF_OACTIVE; return; } /* * Copy the mbuf chain into the transmit buffer */ m0 = m; /* txb_new points to next open buffer slot */ buffer = sc->mem_start + (sc->txb_new * ED_TXBUF_SIZE * ED_PAGE_SIZE); if (sc->mem_shared) { /* * Special case setup for 16 bit boards... */ if (sc->isa16bit) { switch (sc->vendor) { /* * For 16bit 3Com boards (which have 16k of * memory), we have the xmit buffers in a * different page of memory ('page 0') - so * change pages. */ case ED_VENDOR_3COM: outb(sc->asic_addr + ED_3COM_GACFR, ED_3COM_GACFR_RSEL); break; /* * Enable 16bit access to shared memory on * WD/SMC boards. */ case ED_VENDOR_WD_SMC:{ outb(sc->asic_addr + ED_WD_LAAR, sc->wd_laar_proto | ED_WD_LAAR_M16EN); if (sc->is790) { outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_MENB); } break; } } } for (len = 0; m != 0; m = m->m_next) { bcopy(mtod(m, caddr_t), buffer, m->m_len); buffer += m->m_len; len += m->m_len; } /* * Restore previous shared memory access */ if (sc->isa16bit) { switch (sc->vendor) { case ED_VENDOR_3COM: outb(sc->asic_addr + ED_3COM_GACFR, ED_3COM_GACFR_RSEL | ED_3COM_GACFR_MBS0); break; case ED_VENDOR_WD_SMC:{ if (sc->is790) { outb(sc->asic_addr + ED_WD_MSR, 0x00); } outb(sc->asic_addr + ED_WD_LAAR, sc->wd_laar_proto & ~ED_WD_LAAR_M16EN); break; } } } } else { len = ed_pio_write_mbufs(sc, m, (int)buffer); if (len == 0) goto outloop; } sc->txb_len[sc->txb_new] = max(len, (ETHER_MIN_LEN-ETHER_CRC_LEN)); sc->txb_inuse++; /* * Point to next buffer slot and wrap if necessary. */ sc->txb_new++; if (sc->txb_new == sc->txb_cnt) sc->txb_new = 0; if (sc->xmit_busy == 0) ed_xmit(sc); /* * Tap off here if there is a bpf listener. */ #if NBPFILTER > 0 if (ifp->if_bpf) { bpf_mtap(ifp, m0); } #endif m_freem(m0); /* * Loop back to the top to possibly buffer more packets */ goto outloop; } /* * Ethernet interface receiver interrupt. */ static __inline void ed_rint(sc) struct ed_softc *sc; { struct ifnet *ifp = &sc->arpcom.ac_if; u_char boundry; u_short len; struct ed_ring packet_hdr; char *packet_ptr; if (sc->gone) return; /* * Set NIC to page 1 registers to get 'current' pointer */ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_PAGE_1 | ED_CR_STA); /* * 'sc->next_packet' is the logical beginning of the ring-buffer - * i.e. it points to where new data has been buffered. The 'CURR' * (current) register points to the logical end of the ring-buffer - * i.e. it points to where additional new data will be added. We loop * here until the logical beginning equals the logical end (or in * other words, until the ring-buffer is empty). */ while (sc->next_packet != inb(sc->nic_addr + ED_P1_CURR)) { /* get pointer to this buffer's header structure */ packet_ptr = sc->mem_ring + (sc->next_packet - sc->rec_page_start) * ED_PAGE_SIZE; /* * The byte count includes a 4 byte header that was added by * the NIC. */ if (sc->mem_shared) packet_hdr = *(struct ed_ring *) packet_ptr; else ed_pio_readmem(sc, (int)packet_ptr, (char *) &packet_hdr, sizeof(packet_hdr)); len = packet_hdr.count; if (len > (ETHER_MAX_LEN - ETHER_CRC_LEN + sizeof(struct ed_ring)) || len < (ETHER_MIN_LEN - ETHER_CRC_LEN + sizeof(struct ed_ring))) { /* * Length is a wild value. There's a good chance that * this was caused by the NIC being old and buggy. * The bug is that the length low byte is duplicated in * the high byte. Try to recalculate the length based on * the pointer to the next packet. */ /* * NOTE: sc->next_packet is pointing at the current packet. */ len &= ED_PAGE_SIZE - 1; /* preserve offset into page */ if (packet_hdr.next_packet >= sc->next_packet) { len += (packet_hdr.next_packet - sc->next_packet) * ED_PAGE_SIZE; } else { len += ((packet_hdr.next_packet - sc->rec_page_start) + (sc->rec_page_stop - sc->next_packet)) * ED_PAGE_SIZE; } /* * because buffers are aligned on 256-byte boundary, * the length computed above is off by 256 in almost * all cases. Fix it... */ if (len & 0xff) len -= 256 ; if (len > (ETHER_MAX_LEN - ETHER_CRC_LEN + sizeof(struct ed_ring))) sc->mibdata.dot3StatsFrameTooLongs++; } /* * Be fairly liberal about what we allow as a "reasonable" length * so that a [crufty] packet will make it to BPF (and can thus * be analyzed). Note that all that is really important is that * we have a length that will fit into one mbuf cluster or less; * the upper layer protocols can then figure out the length from * their own length field(s). */ if ((len > sizeof(struct ed_ring)) && (len <= MCLBYTES) && (packet_hdr.next_packet >= sc->rec_page_start) && (packet_hdr.next_packet < sc->rec_page_stop)) { /* * Go get packet. */ ed_get_packet(sc, packet_ptr + sizeof(struct ed_ring), len - sizeof(struct ed_ring), packet_hdr.rsr & ED_RSR_PHY); ifp->if_ipackets++; } else { /* * Really BAD. The ring pointers are corrupted. */ log(LOG_ERR, "ed%d: NIC memory corrupt - invalid packet length %d\n", ifp->if_unit, len); ifp->if_ierrors++; ed_reset(ifp); return; } /* * Update next packet pointer */ sc->next_packet = packet_hdr.next_packet; /* * Update NIC boundry pointer - being careful to keep it one * buffer behind. (as recommended by NS databook) */ boundry = sc->next_packet - 1; if (boundry < sc->rec_page_start) boundry = sc->rec_page_stop - 1; /* * Set NIC to page 0 registers to update boundry register */ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_STA); outb(sc->nic_addr + ED_P0_BNRY, boundry); /* * Set NIC to page 1 registers before looping to top (prepare * to get 'CURR' current pointer) */ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_PAGE_1 | ED_CR_STA); } } /* * Ethernet interface interrupt processor */ void edintr_sc(sc) struct ed_softc *sc; { struct ifnet *ifp = (struct ifnet *)sc; u_char isr; if (sc->gone) return; /* * Set NIC to page 0 registers */ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_STA); /* * loop until there are no more new interrupts */ while ((isr = inb(sc->nic_addr + ED_P0_ISR)) != 0) { /* * reset all the bits that we are 'acknowledging' by writing a * '1' to each bit position that was set (writing a '1' * *clears* the bit) */ outb(sc->nic_addr + ED_P0_ISR, isr); /* * Handle transmitter interrupts. Handle these first because * the receiver will reset the board under some conditions. */ if (isr & (ED_ISR_PTX | ED_ISR_TXE)) { u_char collisions = inb(sc->nic_addr + ED_P0_NCR) & 0x0f; /* * Check for transmit error. If a TX completed with an * error, we end up throwing the packet away. Really * the only error that is possible is excessive * collisions, and in this case it is best to allow * the automatic mechanisms of TCP to backoff the * flow. Of course, with UDP we're screwed, but this * is expected when a network is heavily loaded. */ (void) inb(sc->nic_addr + ED_P0_TSR); if (isr & ED_ISR_TXE) { u_char tsr; /* * Excessive collisions (16) */ tsr = inb(sc->nic_addr + ED_P0_TSR); if ((tsr & ED_TSR_ABT) && (collisions == 0)) { /* * When collisions total 16, the * P0_NCR will indicate 0, and the * TSR_ABT is set. */ collisions = 16; sc->mibdata.dot3StatsExcessiveCollisions++; sc->mibdata.dot3StatsCollFrequencies[15]++; } if (tsr & ED_TSR_OWC) sc->mibdata.dot3StatsLateCollisions++; if (tsr & ED_TSR_CDH) sc->mibdata.dot3StatsSQETestErrors++; if (tsr & ED_TSR_CRS) sc->mibdata.dot3StatsCarrierSenseErrors++; if (tsr & ED_TSR_FU) sc->mibdata.dot3StatsInternalMacTransmitErrors++; /* * update output errors counter */ ifp->if_oerrors++; } else { /* * Update total number of successfully * transmitted packets. */ ifp->if_opackets++; } /* * reset tx busy and output active flags */ sc->xmit_busy = 0; ifp->if_flags &= ~IFF_OACTIVE; /* * clear watchdog timer */ ifp->if_timer = 0; /* * Add in total number of collisions on last * transmission. */ ifp->if_collisions += collisions; switch(collisions) { case 0: case 16: break; case 1: sc->mibdata.dot3StatsSingleCollisionFrames++; sc->mibdata.dot3StatsCollFrequencies[0]++; break; default: sc->mibdata.dot3StatsMultipleCollisionFrames++; sc->mibdata. dot3StatsCollFrequencies[collisions-1] ++; break; } /* * Decrement buffer in-use count if not zero (can only * be zero if a transmitter interrupt occured while * not actually transmitting). If data is ready to * transmit, start it transmitting, otherwise defer * until after handling receiver */ if (sc->txb_inuse && --sc->txb_inuse) ed_xmit(sc); } /* * Handle receiver interrupts */ if (isr & (ED_ISR_PRX | ED_ISR_RXE | ED_ISR_OVW)) { /* * Overwrite warning. In order to make sure that a * lockup of the local DMA hasn't occurred, we reset * and re-init the NIC. The NSC manual suggests only a * partial reset/re-init is necessary - but some chips * seem to want more. The DMA lockup has been seen * only with early rev chips - Methinks this bug was * fixed in later revs. -DG */ if (isr & ED_ISR_OVW) { ifp->if_ierrors++; #ifdef DIAGNOSTIC log(LOG_WARNING, "ed%d: warning - receiver ring buffer overrun\n", ifp->if_unit); #endif /* * Stop/reset/re-init NIC */ ed_reset(ifp); } else { /* * Receiver Error. One or more of: CRC error, * frame alignment error FIFO overrun, or * missed packet. */ if (isr & ED_ISR_RXE) { u_char rsr; rsr = inb(sc->nic_addr + ED_P0_RSR); if (rsr & ED_RSR_CRC) sc->mibdata.dot3StatsFCSErrors++; if (rsr & ED_RSR_FAE) sc->mibdata.dot3StatsAlignmentErrors++; if (rsr & ED_RSR_FO) sc->mibdata.dot3StatsInternalMacReceiveErrors++; ifp->if_ierrors++; #ifdef ED_DEBUG printf("ed%d: receive error %x\n", ifp->if_unit, inb(sc->nic_addr + ED_P0_RSR)); #endif } /* * Go get the packet(s) XXX - Doing this on an * error is dubious because there shouldn't be * any data to get (we've configured the * interface to not accept packets with * errors). */ /* * Enable 16bit access to shared memory first * on WD/SMC boards. */ if (sc->isa16bit && (sc->vendor == ED_VENDOR_WD_SMC)) { outb(sc->asic_addr + ED_WD_LAAR, sc->wd_laar_proto | ED_WD_LAAR_M16EN); if (sc->is790) { outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_MENB); } } ed_rint(sc); /* disable 16bit access */ if (sc->isa16bit && (sc->vendor == ED_VENDOR_WD_SMC)) { if (sc->is790) { outb(sc->asic_addr + ED_WD_MSR, 0x00); } outb(sc->asic_addr + ED_WD_LAAR, sc->wd_laar_proto & ~ED_WD_LAAR_M16EN); } } } /* * If it looks like the transmitter can take more data, * attempt to start output on the interface. This is done * after handling the receiver to give the receiver priority. */ if ((ifp->if_flags & IFF_OACTIVE) == 0) ed_start(ifp); /* * return NIC CR to standard state: page 0, remote DMA * complete, start (toggling the TXP bit off, even if was just * set in the transmit routine, is *okay* - it is 'edge' * triggered from low to high) */ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_STA); /* * If the Network Talley Counters overflow, read them to reset * them. It appears that old 8390's won't clear the ISR flag * otherwise - resulting in an infinite loop. */ if (isr & ED_ISR_CNT) { (void) inb(sc->nic_addr + ED_P0_CNTR0); (void) inb(sc->nic_addr + ED_P0_CNTR1); (void) inb(sc->nic_addr + ED_P0_CNTR2); } } } static void edintr(unit) int unit; { edintr_sc (&ed_softc[unit]); } /* * Process an ioctl request. This code needs some work - it looks * pretty ugly. */ static int ed_ioctl(ifp, command, data) register struct ifnet *ifp; u_long command; caddr_t data; { struct ed_softc *sc = ifp->if_softc; int s, error = 0; if (sc->gone) { ifp->if_flags &= ~IFF_RUNNING; return ENXIO; } s = splimp(); switch (command) { case SIOCSIFADDR: case SIOCGIFADDR: case SIOCSIFMTU: error = ether_ioctl(ifp, command, data); break; case SIOCSIFFLAGS: /* * If the interface is marked up and stopped, then start it. * If it is marked down and running, then stop it. */ if (ifp->if_flags & IFF_UP) { if ((ifp->if_flags & IFF_RUNNING) == 0) ed_init(sc); } else { if (ifp->if_flags & IFF_RUNNING) { ed_stop(sc); ifp->if_flags &= ~IFF_RUNNING; } } #if NBPFILTER > 0 /* * Promiscuous flag may have changed, so reprogram the RCR. */ ed_setrcr(sc); #endif /* * An unfortunate hack to provide the (required) software * control of the tranceiver for 3Com boards. The ALTPHYS flag * disables the tranceiver if set. */ if (sc->vendor == ED_VENDOR_3COM) { if (ifp->if_flags & IFF_ALTPHYS) { outb(sc->asic_addr + ED_3COM_CR, 0); } else { outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_XSEL); } } else if (sc->vendor == ED_VENDOR_HP) ed_hpp_set_physical_link(sc); break; case SIOCADDMULTI: case SIOCDELMULTI: /* * Multicast list has changed; set the hardware filter * accordingly. */ ed_setrcr(sc); error = 0; break; default: error = EINVAL; } (void) splx(s); return (error); } /* * Given a source and destination address, copy 'amount' of a packet from * the ring buffer into a linear destination buffer. Takes into account * ring-wrap. */ static __inline char * ed_ring_copy(sc, src, dst, amount) struct ed_softc *sc; char *src; char *dst; u_short amount; { u_short tmp_amount; /* does copy wrap to lower addr in ring buffer? */ if (src + amount > sc->mem_end) { tmp_amount = sc->mem_end - src; /* copy amount up to end of NIC memory */ if (sc->mem_shared) bcopy(src, dst, tmp_amount); else ed_pio_readmem(sc, (int)src, dst, tmp_amount); amount -= tmp_amount; src = sc->mem_ring; dst += tmp_amount; } if (sc->mem_shared) bcopy(src, dst, amount); else ed_pio_readmem(sc, (int)src, dst, amount); return (src + amount); } /* * Retreive packet from shared memory and send to the next level up via * ether_input(). If there is a BPF listener, give a copy to BPF, too. */ static void ed_get_packet(sc, buf, len, multicast) struct ed_softc *sc; char *buf; u_short len; int multicast; { struct ether_header *eh; struct mbuf *m; /* Allocate a header mbuf */ MGETHDR(m, M_DONTWAIT, MT_DATA); if (m == NULL) return; m->m_pkthdr.rcvif = &sc->arpcom.ac_if; m->m_pkthdr.len = m->m_len = len; /* * We always put the received packet in a single buffer - * either with just an mbuf header or in a cluster attached * to the header. The +2 is to compensate for the alignment * fixup below. */ if ((len + 2) > MHLEN) { /* Attach an mbuf cluster */ MCLGET(m, M_DONTWAIT); /* Insist on getting a cluster */ if ((m->m_flags & M_EXT) == 0) { m_freem(m); return; } } /* * The +2 is to longword align the start of the real packet. * This is important for NFS. */ m->m_data += 2; eh = mtod(m, struct ether_header *); #ifdef BRIDGE /* * Get link layer header, invoke brige_in, then * depending on the outcome of the test fetch the rest of the * packet and either pass up or call bdg_forward. */ if (do_bridge) { struct ifnet *ifp ; int need_more = 1 ; /* in case not bpf */ #if NBPFILTER > 0 if (sc->arpcom.ac_if.if_bpf) { need_more = 0 ; ed_ring_copy(sc, buf, (char *)eh, len); bpf_mtap(&sc->arpcom.ac_if, m); } else #endif ed_ring_copy(sc, buf, (char *)eh, 14); ifp = bridge_in(m); if (ifp == BDG_DROP) { m_freem(m); return ; } /* else fetch rest of pkt and continue */ if (need_more && len > 14) ed_ring_copy(sc, buf+14, (char *)(eh+1), len - 14); if (ifp != BDG_LOCAL ) bdg_forward(&m, ifp); /* not local, need forwarding */ if (ifp == BDG_LOCAL || ifp == BDG_BCAST || ifp == BDG_MCAST) goto getit ; /* not local and not multicast, just drop it */ if (m) m_freem(m); return ; } #endif /* * Get packet, including link layer address, from interface. */ ed_ring_copy(sc, buf, (char *)eh, len); #if NBPFILTER > 0 /* * Check if there's a BPF listener on this interface. If so, hand off * the raw packet to bpf. */ if (sc->arpcom.ac_if.if_bpf) bpf_mtap(&sc->arpcom.ac_if, m); #endif /* * If we are in promiscuous mode, we have to check whether * this packet is really for us. */ if ((sc->arpcom.ac_if.if_flags & IFF_PROMISC) && bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr, sizeof(eh->ether_dhost)) != 0 && multicast == 0) { m_freem(m); return; } getit: /* * Remove link layer address. */ m->m_pkthdr.len = m->m_len = len - sizeof(struct ether_header); m->m_data += sizeof(struct ether_header); ether_input(&sc->arpcom.ac_if, eh, m); return; } /* * Supporting routines */ /* * Given a NIC memory source address and a host memory destination * address, copy 'amount' from NIC to host using Programmed I/O. * The 'amount' is rounded up to a word - okay as long as mbufs * are word sized. * This routine is currently Novell-specific. */ static void ed_pio_readmem(sc, src, dst, amount) struct ed_softc *sc; int src; unsigned char *dst; unsigned short amount; { /* HP cards need special handling */ if (sc->vendor == ED_VENDOR_HP && sc->type == ED_TYPE_HP_PCLANPLUS) { ed_hpp_readmem(sc, src, dst, amount); return; } /* Regular Novell cards */ /* select page 0 registers */ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_STA); /* round up to a word */ if (amount & 1) ++amount; /* set up DMA byte count */ outb(sc->nic_addr + ED_P0_RBCR0, amount); outb(sc->nic_addr + ED_P0_RBCR1, amount >> 8); /* set up source address in NIC mem */ outb(sc->nic_addr + ED_P0_RSAR0, src); outb(sc->nic_addr + ED_P0_RSAR1, src >> 8); outb(sc->nic_addr + ED_P0_CR, ED_CR_RD0 | ED_CR_STA); if (sc->isa16bit) { insw(sc->asic_addr + ED_NOVELL_DATA, dst, amount / 2); } else insb(sc->asic_addr + ED_NOVELL_DATA, dst, amount); } /* * Stripped down routine for writing a linear buffer to NIC memory. * Only used in the probe routine to test the memory. 'len' must * be even. */ static void ed_pio_writemem(sc, src, dst, len) struct ed_softc *sc; char *src; unsigned short dst; unsigned short len; { int maxwait = 200; /* about 240us */ if (sc->vendor == ED_VENDOR_NOVELL) { /* select page 0 registers */ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_STA); /* reset remote DMA complete flag */ outb(sc->nic_addr + ED_P0_ISR, ED_ISR_RDC); /* set up DMA byte count */ outb(sc->nic_addr + ED_P0_RBCR0, len); outb(sc->nic_addr + ED_P0_RBCR1, len >> 8); /* set up destination address in NIC mem */ outb(sc->nic_addr + ED_P0_RSAR0, dst); outb(sc->nic_addr + ED_P0_RSAR1, dst >> 8); /* set remote DMA write */ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD1 | ED_CR_STA); if (sc->isa16bit) outsw(sc->asic_addr + ED_NOVELL_DATA, src, len / 2); else outsb(sc->asic_addr + ED_NOVELL_DATA, src, len); /* * Wait for remote DMA complete. This is necessary because on the * transmit side, data is handled internally by the NIC in bursts and * we can't start another remote DMA until this one completes. Not * waiting causes really bad things to happen - like the NIC * irrecoverably jamming the ISA bus. */ while (((inb(sc->nic_addr + ED_P0_ISR) & ED_ISR_RDC) != ED_ISR_RDC) && --maxwait); } else if ((sc->vendor == ED_VENDOR_HP) && (sc->type == ED_TYPE_HP_PCLANPLUS)) { /* HP PCLAN+ */ /* reset remote DMA complete flag */ outb(sc->nic_addr + ED_P0_ISR, ED_ISR_RDC); /* program the write address in RAM */ outw(sc->asic_addr + ED_HPP_PAGE_0, dst); if (sc->hpp_mem_start) { u_short *s = (u_short *) src; volatile u_short *d = (u_short *) sc->hpp_mem_start; u_short *const fence = s + (len >> 1); /* * Enable memory mapped access. */ outw(sc->asic_addr + ED_HPP_OPTION, sc->hpp_options & ~(ED_HPP_OPTION_MEM_DISABLE | ED_HPP_OPTION_BOOT_ROM_ENB)); /* * Copy to NIC memory. */ while (s < fence) *d = *s++; /* * Restore Boot ROM access. */ outw(sc->asic_addr + ED_HPP_OPTION, sc->hpp_options); } else { /* write data using I/O writes */ outsw(sc->asic_addr + ED_HPP_PAGE_4, src, len / 2); } } } /* * Write an mbuf chain to the destination NIC memory address using * programmed I/O. */ static u_short ed_pio_write_mbufs(sc, m, dst) struct ed_softc *sc; struct mbuf *m; int dst; { struct ifnet *ifp = (struct ifnet *)sc; unsigned short total_len, dma_len; struct mbuf *mp; int maxwait = 200; /* about 240us */ /* HP PC Lan+ cards need special handling */ if ((sc->vendor == ED_VENDOR_HP) && (sc->type == ED_TYPE_HP_PCLANPLUS)) { return ed_hpp_write_mbufs(sc, m, dst); } /* First, count up the total number of bytes to copy */ for (total_len = 0, mp = m; mp; mp = mp->m_next) total_len += mp->m_len; dma_len = total_len; if (sc->isa16bit && (dma_len & 1)) dma_len++; /* select page 0 registers */ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_STA); /* reset remote DMA complete flag */ outb(sc->nic_addr + ED_P0_ISR, ED_ISR_RDC); /* set up DMA byte count */ outb(sc->nic_addr + ED_P0_RBCR0, dma_len); outb(sc->nic_addr + ED_P0_RBCR1, dma_len >> 8); /* set up destination address in NIC mem */ outb(sc->nic_addr + ED_P0_RSAR0, dst); outb(sc->nic_addr + ED_P0_RSAR1, dst >> 8); /* set remote DMA write */ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD1 | ED_CR_STA); /* * Transfer the mbuf chain to the NIC memory. * 16-bit cards require that data be transferred as words, and only words. * So that case requires some extra code to patch over odd-length mbufs. */ if (!sc->isa16bit) { /* NE1000s are easy */ while (m) { if (m->m_len) { outsb(sc->asic_addr + ED_NOVELL_DATA, m->m_data, m->m_len); } m = m->m_next; } } else { /* NE2000s are a pain */ unsigned char *data; int len, wantbyte; unsigned char savebyte[2]; wantbyte = 0; while (m) { len = m->m_len; if (len) { data = mtod(m, caddr_t); /* finish the last word */ if (wantbyte) { savebyte[1] = *data; outw(sc->asic_addr + ED_NOVELL_DATA, *(u_short *)savebyte); data++; len--; wantbyte = 0; } /* output contiguous words */ if (len > 1) { outsw(sc->asic_addr + ED_NOVELL_DATA, data, len >> 1); data += len & ~1; len &= 1; } /* save last byte, if necessary */ if (len == 1) { savebyte[0] = *data; wantbyte = 1; } } m = m->m_next; } /* spit last byte */ if (wantbyte) { outw(sc->asic_addr + ED_NOVELL_DATA, *(u_short *)savebyte); } } /* * Wait for remote DMA complete. This is necessary because on the * transmit side, data is handled internally by the NIC in bursts and * we can't start another remote DMA until this one completes. Not * waiting causes really bad things to happen - like the NIC * irrecoverably jamming the ISA bus. */ while (((inb(sc->nic_addr + ED_P0_ISR) & ED_ISR_RDC) != ED_ISR_RDC) && --maxwait); if (!maxwait) { log(LOG_WARNING, "ed%d: remote transmit DMA failed to complete\n", ifp->if_unit); ed_reset(ifp); return(0); } return (total_len); } /* * Support routines to handle the HP PC Lan+ card. */ /* * HP PC Lan+: Read from NIC memory, using either PIO or memory mapped * IO. */ static void ed_hpp_readmem(sc, src, dst, amount) struct ed_softc *sc; unsigned short src; unsigned char *dst; unsigned short amount; { int use_32bit_access = !(sc->hpp_id & ED_HPP_ID_16_BIT_ACCESS); /* Program the source address in RAM */ outw(sc->asic_addr + ED_HPP_PAGE_2, src); /* * The HP PC Lan+ card supports word reads as well as * a memory mapped i/o port that is aliased to every * even address on the board. */ if (sc->hpp_mem_start) { /* Enable memory mapped access. */ outw(sc->asic_addr + ED_HPP_OPTION, sc->hpp_options & ~(ED_HPP_OPTION_MEM_DISABLE | ED_HPP_OPTION_BOOT_ROM_ENB)); if (use_32bit_access && (amount > 3)) { u_long *dl = (u_long *) dst; volatile u_long *const sl = (u_long *) sc->hpp_mem_start; u_long *const fence = dl + (amount >> 2); /* Copy out NIC data. We could probably write this as a `movsl'. The currently generated code is lousy. */ while (dl < fence) *dl++ = *sl; dst += (amount & ~3); amount &= 3; } /* Finish off any words left, as a series of short reads */ if (amount > 1) { u_short *d = (u_short *) dst; volatile u_short *const s = (u_short *) sc->hpp_mem_start; u_short *const fence = d + (amount >> 1); /* Copy out NIC data. */ while (d < fence) *d++ = *s; dst += (amount & ~1); amount &= 1; } /* * read in a byte; however we need to always read 16 bits * at a time or the hardware gets into a funny state */ if (amount == 1) { /* need to read in a short and copy LSB */ volatile u_short *const s = (volatile u_short *) sc->hpp_mem_start; *dst = (*s) & 0xFF; } /* Restore Boot ROM access. */ outw(sc->asic_addr + ED_HPP_OPTION, sc->hpp_options); } else { /* Read in data using the I/O port */ if (use_32bit_access && (amount > 3)) { insl(sc->asic_addr + ED_HPP_PAGE_4, dst, amount >> 2); dst += (amount & ~3); amount &= 3; } if (amount > 1) { insw(sc->asic_addr + ED_HPP_PAGE_4, dst, amount >> 1); dst += (amount & ~1); amount &= 1; } if (amount == 1) { /* read in a short and keep the LSB */ *dst = inw(sc->asic_addr + ED_HPP_PAGE_4) & 0xFF; } } } /* * Write to HP PC Lan+ NIC memory. Access to the NIC can be by using * outsw() or via the memory mapped interface to the same register. * Writes have to be in word units; byte accesses won't work and may cause * the NIC to behave wierdly. Long word accesses are permitted if the ASIC * allows it. */ static u_short ed_hpp_write_mbufs(struct ed_softc *sc, struct mbuf *m, int dst) { int len, wantbyte; unsigned short total_len; unsigned char savebyte[2]; volatile u_short * const d = (volatile u_short *) sc->hpp_mem_start; int use_32bit_accesses = !(sc->hpp_id & ED_HPP_ID_16_BIT_ACCESS); /* select page 0 registers */ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_STA); /* reset remote DMA complete flag */ outb(sc->nic_addr + ED_P0_ISR, ED_ISR_RDC); /* program the write address in RAM */ outw(sc->asic_addr + ED_HPP_PAGE_0, dst); if (sc->hpp_mem_start) /* enable memory mapped I/O */ outw(sc->asic_addr + ED_HPP_OPTION, sc->hpp_options & ~(ED_HPP_OPTION_MEM_DISABLE | ED_HPP_OPTION_BOOT_ROM_ENB)); wantbyte = 0; total_len = 0; if (sc->hpp_mem_start) { /* Memory mapped I/O port */ while (m) { total_len += (len = m->m_len); if (len) { caddr_t data = mtod(m, caddr_t); /* finish the last word of the previous mbuf */ if (wantbyte) { savebyte[1] = *data; *d = *((ushort *) savebyte); data++; len--; wantbyte = 0; } /* output contiguous words */ if ((len > 3) && (use_32bit_accesses)) { volatile u_long *const dl = (volatile u_long *) d; u_long *sl = (u_long *) data; u_long *fence = sl + (len >> 2); while (sl < fence) *dl = *sl++; data += (len & ~3); len &= 3; } /* finish off remain 16 bit writes */ if (len > 1) { u_short *s = (u_short *) data; u_short *fence = s + (len >> 1); while (s < fence) *d = *s++; data += (len & ~1); len &= 1; } /* save last byte if needed */ if ((wantbyte = (len == 1)) != 0) savebyte[0] = *data; } m = m->m_next; /* to next mbuf */ } if (wantbyte) /* write last byte */ *d = *((u_short *) savebyte); } else { /* use programmed I/O */ while (m) { total_len += (len = m->m_len); if (len) { caddr_t data = mtod(m, caddr_t); /* finish the last word of the previous mbuf */ if (wantbyte) { savebyte[1] = *data; outw(sc->asic_addr + ED_HPP_PAGE_4, *((u_short *)savebyte)); data++; len--; wantbyte = 0; } /* output contiguous words */ if ((len > 3) && use_32bit_accesses) { outsl(sc->asic_addr + ED_HPP_PAGE_4, data, len >> 2); data += (len & ~3); len &= 3; } /* finish off remaining 16 bit accesses */ if (len > 1) { outsw(sc->asic_addr + ED_HPP_PAGE_4, data, len >> 1); data += (len & ~1); len &= 1; } if ((wantbyte = (len == 1)) != 0) savebyte[0] = *data; } /* if len != 0 */ m = m->m_next; } if (wantbyte) /* spit last byte */ outw(sc->asic_addr + ED_HPP_PAGE_4, *(u_short *)savebyte); } if (sc->hpp_mem_start) /* turn off memory mapped i/o */ outw(sc->asic_addr + ED_HPP_OPTION, sc->hpp_options); return (total_len); } static void ed_setrcr(sc) struct ed_softc *sc; { struct ifnet *ifp = (struct ifnet *)sc; int i; /* set page 1 registers */ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_PAGE_1 | ED_CR_STP); if (ifp->if_flags & IFF_PROMISC) { /* * Reconfigure the multicast filter. */ for (i = 0; i < 8; i++) outb(sc->nic_addr + ED_P1_MAR0 + i, 0xff); /* * And turn on promiscuous mode. Also enable reception of * runts and packets with CRC & alignment errors. */ /* Set page 0 registers */ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_STP); outb(sc->nic_addr + ED_P0_RCR, ED_RCR_PRO | ED_RCR_AM | ED_RCR_AB | ED_RCR_AR | ED_RCR_SEP); } else { /* set up multicast addresses and filter modes */ if (ifp->if_flags & IFF_MULTICAST) { u_long mcaf[2]; if (ifp->if_flags & IFF_ALLMULTI) { mcaf[0] = 0xffffffff; mcaf[1] = 0xffffffff; } else ds_getmcaf(sc, mcaf); /* * Set multicast filter on chip. */ for (i = 0; i < 8; i++) outb(sc->nic_addr + ED_P1_MAR0 + i, ((u_char *) mcaf)[i]); /* Set page 0 registers */ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_STP); outb(sc->nic_addr + ED_P0_RCR, ED_RCR_AM | ED_RCR_AB); } else { /* * Initialize multicast address hashing registers to * not accept multicasts. */ for (i = 0; i < 8; ++i) outb(sc->nic_addr + ED_P1_MAR0 + i, 0x00); /* Set page 0 registers */ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_STP); outb(sc->nic_addr + ED_P0_RCR, ED_RCR_AB); } } /* * Start interface. */ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_STA); } /* * Compute crc for ethernet address */ static u_long ds_crc(ep) u_char *ep; { #define POLYNOMIAL 0x04c11db6 register u_long crc = 0xffffffffL; register int carry, i, j; register u_char b; for (i = 6; --i >= 0;) { b = *ep++; for (j = 8; --j >= 0;) { carry = ((crc & 0x80000000L) ? 1 : 0) ^ (b & 0x01); crc <<= 1; b >>= 1; if (carry) crc = ((crc ^ POLYNOMIAL) | carry); } } return crc; #undef POLYNOMIAL } /* * Compute the multicast address filter from the * list of multicast addresses we need to listen to. */ static void ds_getmcaf(sc, mcaf) struct ed_softc *sc; u_long *mcaf; { register u_int index; register u_char *af = (u_char *) mcaf; struct ifmultiaddr *ifma; mcaf[0] = 0; mcaf[1] = 0; for (ifma = sc->arpcom.ac_if.if_multiaddrs.lh_first; ifma; ifma = ifma->ifma_link.le_next) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; index = ds_crc(LLADDR((struct sockaddr_dl *)ifma->ifma_addr)) >> 26; af[index >> 3] |= 1 << (index & 7); } } /* * support PnP cards if we are using 'em */ #if NPNP > 0 static pnpid_t edpnp_ids[] = { { 0xd680d041, "NE2000"}, { 0 } }; static char *edpnp_probe(u_long csn, u_long vend_id); static void edpnp_attach(u_long csn, u_long vend_id, char *name, struct isa_device *dev); static u_long nedpnp = NED; static struct pnp_device edpnp = { "edpnp", edpnp_probe, edpnp_attach, &nedpnp, &net_imask }; DATA_SET (pnpdevice_set, edpnp); static char * edpnp_probe(u_long csn, u_long vend_id) { pnpid_t *id; char *s = NULL; for(id = edpnp_ids; id->vend_id != 0; id++) { if (vend_id == id->vend_id) { s = id->id_str; break; } } if (s) { struct pnp_cinfo d; read_pnp_parms(&d, 0); if (d.enable == 0 || d.flags & 1) { printf("CSN %lu is disabled.\n", csn); return (NULL); } } return (s); } static void edpnp_attach(u_long csn, u_long vend_id, char *name, struct isa_device *dev) { struct pnp_cinfo d; - struct isa_device *dvp; if (dev->id_unit >= NEDTOT) return; if (read_pnp_parms(&d, 0) == 0) { printf("failed to read pnp parms\n"); return; } write_pnp_parms(&d, 0); enable_pnp_card(); dev->id_iobase = d.port[0]; dev->id_irq = (1 << d.irq[0]); dev->id_ointr = edintr; dev->id_drq = -1; if (dev->id_driver == NULL) { dev->id_driver = &eddriver; - dvp = find_isadev(isa_devtab_net, &eddriver, 0); - if (dvp != NULL) - dev->id_id = dvp->id_id; + dev->id_id = isa_compat_nextid(); } if ((dev->id_alive = ed_probe(dev)) != 0) ed_attach_isa(dev); else printf("ed%d: probe failed\n", dev->id_unit); } #endif Index: head/sys/dev/fdc/fdc.c =================================================================== --- head/sys/dev/fdc/fdc.c (revision 45719) +++ head/sys/dev/fdc/fdc.c (revision 45720) @@ -1,2297 +1,2394 @@ /* * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Don Ahn. * * Libretto PCMCIA floppy support by David Horwitt (dhorwitt@ucsd.edu) * aided by the Linux floppy driver modifications from David Bateman * (dbateman@eng.uts.edu.au). * * Copyright (c) 1993, 1994 by * jc@irbs.UUCP (John Capo) * vak@zebub.msk.su (Serge Vakulenko) * ache@astral.msk.su (Andrew A. Chernov) * * Copyright (c) 1993, 1994, 1995 by * joerg_wunsch@uriah.sax.de (Joerg Wunsch) * dufault@hda.com (Peter Dufault) * * 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: @(#)fd.c 7.4 (Berkeley) 5/25/91 - * $Id: fd.c,v 1.133 1999/02/10 00:03:32 ken Exp $ + * $Id: fd.c,v 1.134 1999/04/06 03:06:51 peter Exp $ * */ #include "fd.h" #include "opt_devfs.h" #include "opt_fdc.h" #if NFDC > 0 #include #include #include +#include +#include #include -#include -#include -#include #include -#include #include +#include #include +#include #include #include -#include -#include -#include -#include -#include + +#include +#include +#include + +#include +#include +#include #include + #ifdef DEVFS #include #endif /* DEVFS */ +#include +#include +#include +#include +#include +#include + /* misuse a flag to identify format operation */ #define B_FORMAT B_XXX /* configuration flags */ #define FDC_PRETEND_D0 (1 << 0) /* pretend drive 0 to be there */ #ifdef FDC_YE #define FDC_IS_PCMCIA (1 << 1) /* if successful probe, then it's a PCMCIA device */ #endif /* internally used only, not really from CMOS: */ #define RTCFDT_144M_PRETENDED 0x1000 /* * this biotab field doubles as a field for the physical unit number * on the controller */ #define id_physid id_scsiid /* error returns for fd_cmd() */ #define FD_FAILED -1 #define FD_NOT_VALID -2 #define FDC_ERRMAX 100 /* do not log more */ #define NUMTYPES 14 #define NUMDENS (NUMTYPES - 6) /* These defines (-1) must match index for fd_types */ #define F_TAPE_TYPE 0x020 /* bit for fd_types to indicate tape */ #define NO_TYPE 0 /* must match NO_TYPE in ft.c */ #define FD_1720 1 #define FD_1480 2 #define FD_1440 3 #define FD_1200 4 #define FD_820 5 #define FD_800 6 #define FD_720 7 #define FD_360 8 #define FD_1480in5_25 9 #define FD_1440in5_25 10 #define FD_820in5_25 11 #define FD_800in5_25 12 #define FD_720in5_25 13 #define FD_360in5_25 14 static struct fd_type fd_types[NUMTYPES] = { { 21,2,0xFF,0x04,82,3444,1,FDC_500KBPS,2,0x0C,2 }, /* 1.72M in HD 3.5in */ { 18,2,0xFF,0x1B,82,2952,1,FDC_500KBPS,2,0x6C,1 }, /* 1.48M in HD 3.5in */ { 18,2,0xFF,0x1B,80,2880,1,FDC_500KBPS,2,0x6C,1 }, /* 1.44M in HD 3.5in */ { 15,2,0xFF,0x1B,80,2400,1,FDC_500KBPS,2,0x54,1 }, /* 1.2M in HD 5.25/3.5 */ { 10,2,0xFF,0x10,82,1640,1,FDC_250KBPS,2,0x2E,1 }, /* 820K in HD 3.5in */ { 10,2,0xFF,0x10,80,1600,1,FDC_250KBPS,2,0x2E,1 }, /* 800K in HD 3.5in */ { 9,2,0xFF,0x20,80,1440,1,FDC_250KBPS,2,0x50,1 }, /* 720K in HD 3.5in */ { 9,2,0xFF,0x2A,40, 720,1,FDC_250KBPS,2,0x50,1 }, /* 360K in DD 5.25in */ { 18,2,0xFF,0x02,82,2952,1,FDC_500KBPS,2,0x02,2 }, /* 1.48M in HD 5.25in */ { 18,2,0xFF,0x02,80,2880,1,FDC_500KBPS,2,0x02,2 }, /* 1.44M in HD 5.25in */ { 10,2,0xFF,0x10,82,1640,1,FDC_300KBPS,2,0x2E,1 }, /* 820K in HD 5.25in */ { 10,2,0xFF,0x10,80,1600,1,FDC_300KBPS,2,0x2E,1 }, /* 800K in HD 5.25in */ { 9,2,0xFF,0x20,80,1440,1,FDC_300KBPS,2,0x50,1 }, /* 720K in HD 5.25in */ { 9,2,0xFF,0x23,40, 720,2,FDC_300KBPS,2,0x50,1 }, /* 360K in HD 5.25in */ }; #define DRVS_PER_CTLR 2 /* 2 floppies */ /***********************************************************************\ * Per controller structure. * \***********************************************************************/ -struct fdc_data fdc_data[NFDC]; +static devclass_t fdc_devclass; /***********************************************************************\ * Per drive structure. * * N per controller (DRVS_PER_CTLR) * \***********************************************************************/ -static struct fd_data { +struct fd_data { struct fdc_data *fdc; /* pointer to controller structure */ int fdsu; /* this units number on this controller */ int type; /* Drive type (FD_1440...) */ struct fd_type *ft; /* pointer to the type descriptor */ int flags; #define FD_OPEN 0x01 /* it's open */ #define FD_ACTIVE 0x02 /* it's active */ #define FD_MOTOR 0x04 /* motor should be on */ #define FD_MOTOR_WAIT 0x08 /* motor coming up */ int skip; int hddrv; #define FD_NO_TRACK -2 int track; /* where we think the head is */ int options; /* user configurable options, see ioctl_fd.h */ struct callout_handle toffhandle; struct callout_handle tohandle; struct devstat device_stats; #ifdef DEVFS void *bdevs[1 + NUMDENS + MAXPARTITIONS]; void *cdevs[1 + NUMDENS + MAXPARTITIONS]; #endif -} fd_data[NFD]; + device_t dev; + fdu_t fdu; +}; +static devclass_t fd_devclass; /***********************************************************************\ * Throughout this file the following conventions will be used: * * fd is a pointer to the fd_data struct for the drive in question * * fdc is a pointer to the fdc_data struct for the controller * * fdu is the floppy drive unit number * * fdcu is the floppy controller unit number * * fdsu is the floppy drive unit number on that controller. (sub-unit) * \***********************************************************************/ #ifdef FDC_YE #include "card.h" static int yeattach(struct isa_device *); #endif -/* autoconfig functions */ -static int fdprobe(struct isa_device *); -static int fdattach(struct isa_device *); - /* needed for ft driver, thus exported */ -int in_fdc(fdcu_t); -int out_fdc(fdcu_t, int); +int in_fdc(struct fdc_data *); +int out_fdc(struct fdc_data *, int); /* internal functions */ -static void set_motor(fdcu_t, int, int); +static void fdc_add_device(device_t, const char *, int); +static void fdc_intr(void *); +static void set_motor(struct fdc_data *, int, int); # define TURNON 1 # define TURNOFF 0 static timeout_t fd_turnoff; static timeout_t fd_motor_on; -static void fd_turnon(fdu_t); +static void fd_turnon(struct fd_data *); static void fdc_reset(fdc_p); -static int fd_in(fdcu_t, int *); -static void fdstart(fdcu_t); +static int fd_in(struct fdc_data *, int *); +static void fdstart(struct fdc_data *); static timeout_t fd_iotimeout; static timeout_t fd_pseudointr; -static ointhand2_t fdintr; -static int fdstate(fdcu_t, fdc_p); -static int retrier(fdcu_t); +static int fdstate(struct fdc_data *); +static int retrier(struct fdc_data *); static int fdformat(dev_t, struct fd_formb *, struct proc *); static int enable_fifo(fdc_p fdc); static int fifo_threshold = 8; /* XXX: should be accessible via sysctl */ #define DEVIDLE 0 #define FINDWORK 1 #define DOSEEK 2 #define SEEKCOMPLETE 3 #define IOCOMPLETE 4 #define RECALCOMPLETE 5 #define STARTRECAL 6 #define RESETCTLR 7 #define SEEKWAIT 8 #define RECALWAIT 9 #define MOTORWAIT 10 #define IOTIMEDOUT 11 #define RESETCOMPLETE 12 #ifdef FDC_YE #define PIOREAD 13 #endif #ifdef FDC_DEBUG static char const * const fdstates[] = { "DEVIDLE", "FINDWORK", "DOSEEK", "SEEKCOMPLETE", "IOCOMPLETE", "RECALCOMPLETE", "STARTRECAL", "RESETCTLR", "SEEKWAIT", "RECALWAIT", "MOTORWAIT", "IOTIMEDOUT", "RESETCOMPLETE", #ifdef FDC_YE "PIOREAD", #endif }; /* CAUTION: fd_debug causes huge amounts of logging output */ static int volatile fd_debug = 0; #define TRACE0(arg) if(fd_debug) printf(arg) #define TRACE1(arg1, arg2) if(fd_debug) printf(arg1, arg2) #else /* FDC_DEBUG */ #define TRACE0(arg) #define TRACE1(arg1, arg2) #endif /* FDC_DEBUG */ #ifdef FDC_YE #if NCARD > 0 #include #include #include #include #include /* * PC-Card (PCMCIA) specific code. */ static int yeinit(struct pccard_devinfo *); /* init device */ static void yeunload(struct pccard_devinfo *); /* Disable driver */ static int yeintr(struct pccard_devinfo *); /* Interrupt handler */ PCCARD_MODULE(fdc, yeinit, yeunload, yeintr, 0, bio_imask); /* * this is the secret PIO data port (offset from base) */ #define FDC_YE_DATAPORT 6 /* * Initialize the device - called from Slot manager. */ static int yeinit(struct pccard_devinfo *devi) { fdc_p fdc = &fdc_data[devi->isahd.id_unit]; /* validate unit number. */ if (devi->isahd.id_unit >= NFDC) return(ENODEV); fdc->baseport = devi->isahd.id_iobase; /* * reset controller */ outb(fdc->baseport+FDOUT, 0); DELAY(100); outb(fdc->baseport+FDOUT, FDO_FRST); /* * wire into system */ if (yeattach(&devi->isahd) == 0) return(ENXIO); return(0); } /* * yeunload - unload the driver and clear the table. * XXX TODO: * This is usually called when the card is ejected, but * can be caused by a modunload of a controller driver. * The idea is to reset the driver's view of the device * and ensure that any driver entry points such as * read and write do not hang. */ static void yeunload(struct pccard_devinfo *devi) { if (fd_data[devi->isahd.id_unit].type == NO_TYPE) return; /* * this prevents Fdopen() and fdstrategy() from attempting * to access unloaded controller */ fd_data[devi->isahd.id_unit].type = NO_TYPE; printf("fdc%d: unload\n", devi->isahd.id_unit); } /* * yeintr - Shared interrupt called from * front end of PC-Card handler. */ static int yeintr(struct pccard_devinfo *devi) { fdintr((fdcu_t)devi->isahd.id_unit); return(1); } #endif /* NCARD > 0 */ #endif /* FDC_YE */ - -/* autoconfig structure */ - -struct isa_driver fdcdriver = { - fdprobe, fdattach, "fdc", -}; - static d_open_t Fdopen; /* NOTE, not fdopen */ static d_read_t fdread; static d_write_t fdwrite; static d_close_t fdclose; static d_ioctl_t fdioctl; static d_strategy_t fdstrategy; /* even if SLICE defined, these are needed for the ft support. */ #define CDEV_MAJOR 9 #define BDEV_MAJOR 2 - -static struct cdevsw fd_cdevsw = { - Fdopen, fdclose, fdread, fdwrite, - fdioctl, nostop, nullreset, nodevtotty, - seltrue, nommap, fdstrategy, "fd", - NULL, -1, nodump, nopsize, - D_DISK, 0, -1 }; - - -static struct isa_device *fdcdevs[NFDC]; - - static int -fdc_err(fdcu_t fdcu, const char *s) +fdc_err(struct fdc_data *fdc, const char *s) { - fdc_data[fdcu].fdc_errs++; - if(s) { - if(fdc_data[fdcu].fdc_errs < FDC_ERRMAX) - printf("fdc%d: %s", fdcu, s); - else if(fdc_data[fdcu].fdc_errs == FDC_ERRMAX) - printf("fdc%d: too many errors, not logging any more\n", - fdcu); + fdc->fdc_errs++; + if (s) { + if (fdc->fdc_errs < FDC_ERRMAX) { + device_print_prettyname(fdc->fdc_dev); + printf("%s", s); + } else if (fdc->fdc_errs == FDC_ERRMAX) { + device_print_prettyname(fdc->fdc_dev); + printf("too many errors, not logging any more\n"); + } } return FD_FAILED; } /* * fd_cmd: Send a command to the chip. Takes a varargs with this structure: * Unit number, * # of output bytes, output bytes as ints ..., * # of input bytes, input bytes as ints ... */ - static int -fd_cmd(fdcu_t fdcu, int n_out, ...) +fd_cmd(struct fdc_data *fdc, int n_out, ...) { u_char cmd; int n_in; int n; va_list ap; va_start(ap, n_out); cmd = (u_char)(va_arg(ap, int)); va_end(ap); va_start(ap, n_out); for (n = 0; n < n_out; n++) { - if (out_fdc(fdcu, va_arg(ap, int)) < 0) + if (out_fdc(fdc, va_arg(ap, int)) < 0) { char msg[50]; snprintf(msg, sizeof(msg), "cmd %x failed at out byte %d of %d\n", cmd, n + 1, n_out); - return fdc_err(fdcu, msg); + return fdc_err(fdc, msg); } } n_in = va_arg(ap, int); for (n = 0; n < n_in; n++) { int *ptr = va_arg(ap, int *); - if (fd_in(fdcu, ptr) < 0) + if (fd_in(fdc, ptr) < 0) { char msg[50]; snprintf(msg, sizeof(msg), "cmd %02x failed at in byte %d of %d\n", cmd, n + 1, n_in); - return fdc_err(fdcu, msg); + return fdc_err(fdc, msg); } } return 0; } static int enable_fifo(fdc_p fdc) { int i, j; if ((fdc->flags & FDC_HAS_FIFO) == 0) { /* * XXX: * Cannot use fd_cmd the normal way here, since * this might be an invalid command. Thus we send the * first byte, and check for an early turn of data directon. */ - if (out_fdc(fdc->fdcu, I8207X_CONFIGURE) < 0) - return fdc_err(fdc->fdcu, "Enable FIFO failed\n"); + if (out_fdc(fdc, I8207X_CONFIGURE) < 0) + return fdc_err(fdc, "Enable FIFO failed\n"); /* If command is invalid, return */ j = 100000; while ((i = inb(fdc->baseport + FDSTS) & (NE7_DIO | NE7_RQM)) != NE7_RQM && j-- > 0) if (i == (NE7_DIO | NE7_RQM)) { fdc_reset(fdc); return FD_FAILED; } if (j<0 || - fd_cmd(fdc->fdcu, 3, + fd_cmd(fdc, 3, 0, (fifo_threshold - 1) & 0xf, 0, 0) < 0) { fdc_reset(fdc); - return fdc_err(fdc->fdcu, "Enable FIFO failed\n"); + return fdc_err(fdc, "Enable FIFO failed\n"); } fdc->flags |= FDC_HAS_FIFO; return 0; } - if (fd_cmd(fdc->fdcu, 4, + if (fd_cmd(fdc, 4, I8207X_CONFIGURE, 0, (fifo_threshold - 1) & 0xf, 0, 0) < 0) - return fdc_err(fdc->fdcu, "Re-enable FIFO failed\n"); + return fdc_err(fdc, "Re-enable FIFO failed\n"); return 0; } static int fd_sense_drive_status(fdc_p fdc, int *st3p) { int st3; - if (fd_cmd(fdc->fdcu, 2, NE7CMD_SENSED, fdc->fdu, 1, &st3)) + if (fd_cmd(fdc, 2, NE7CMD_SENSED, fdc->fdu, 1, &st3)) { - return fdc_err(fdc->fdcu, "Sense Drive Status failed\n"); + return fdc_err(fdc, "Sense Drive Status failed\n"); } if (st3p) *st3p = st3; return 0; } static int fd_sense_int(fdc_p fdc, int *st0p, int *cylp) { - int st0, cyl; + int cyl, st0, ret; - int ret = fd_cmd(fdc->fdcu, 1, NE7CMD_SENSEI, 1, &st0); - - if (ret) - { - (void)fdc_err(fdc->fdcu, + ret = fd_cmd(fdc, 1, NE7CMD_SENSEI, 1, &st0); + if (ret) { + (void)fdc_err(fdc, "sense intr err reading stat reg 0\n"); return ret; } if (st0p) *st0p = st0; - if ((st0 & NE7_ST0_IC) == NE7_ST0_IC_IV) - { + if ((st0 & NE7_ST0_IC) == NE7_ST0_IC_IV) { /* * There doesn't seem to have been an interrupt. */ return FD_NOT_VALID; } - if (fd_in(fdc->fdcu, &cyl) < 0) - { - return fdc_err(fdc->fdcu, "can't get cyl num\n"); + if (fd_in(fdc, &cyl) < 0) { + return fdc_err(fdc, "can't get cyl num\n"); } if (cylp) *cylp = cyl; return 0; } static int fd_read_status(fdc_p fdc, int fdsu) { int i, ret; - for (i = 0; i < 7; i++) - { + for (i = 0; i < 7; i++) { /* * XXX types are poorly chosen. Only bytes can by read * from the hardware, but fdc->status[] wants u_ints and * fd_in() gives ints. */ int status; - ret = fd_in(fdc->fdcu, &status); + ret = fd_in(fdc, &status); fdc->status[i] = status; if (ret != 0) break; } if (ret == 0) fdc->flags |= FDC_STAT_VALID; else fdc->flags &= ~FDC_STAT_VALID; return ret; } /****************************************************************************/ /* autoconfiguration stuff */ /****************************************************************************/ -/* - * probe for existance of controller - */ static int -fdprobe(struct isa_device *dev) +fdc_probe(device_t dev) { - fdcu_t fdcu = dev->id_unit; - if(fdc_data[fdcu].flags & FDC_ATTACHED) - { - printf("fdc%d: unit used multiple times\n", fdcu); - return 0; + int error, i, ic_type; + struct fdc_data *fdc; + char myname[8]; /* better be long enough */ + + fdc = device_get_softc(dev); + bzero(fdc, sizeof *fdc); + fdc->fdc_dev = dev; + fdc->rid_ioport = fdc->rid_irq = fdc->rid_drq = 0; + fdc->res_ioport = fdc->res_irq = fdc->res_drq = 0; + + fdc->res_ioport = bus_alloc_resource(dev, SYS_RES_IOPORT, + &fdc->rid_ioport, 0ul, ~0ul, + IO_FDCSIZE, RF_ACTIVE); + if (fdc->res_ioport == 0) { + device_print_prettyname(dev); + printf("cannot reserve I/O port range\n"); + error = ENXIO; + goto out; } + fdc->baseport = fdc->res_ioport->r_start; - fdcdevs[fdcu] = dev; - fdc_data[fdcu].baseport = dev->id_iobase; + fdc->res_irq = bus_alloc_resource(dev, SYS_RES_IRQ, + &fdc->rid_irq, 0ul, ~0ul, 1, + RF_ACTIVE); + if (fdc->res_irq == 0) { + device_print_prettyname(dev); + printf("cannot reserve interrupt line\n"); + error = ENXIO; + goto out; + } + fdc->res_drq = bus_alloc_resource(dev, SYS_RES_DRQ, + &fdc->rid_drq, 0ul, ~0ul, 1, + RF_ACTIVE); + if (fdc->res_drq == 0) { + device_print_prettyname(dev); + printf("cannot reserve DMA request line\n"); + error = ENXIO; + goto out; + } + fdc->dmachan = fdc->res_drq->r_start; + error = BUS_SETUP_INTR(device_get_parent(dev), dev, + fdc->res_irq, fdc_intr, fdc, &fdc->fdc_intr); /* First - lets reset the floppy controller */ - outb(dev->id_iobase+FDOUT, 0); + outb(fdc->baseport + FDOUT, 0); DELAY(100); - outb(dev->id_iobase+FDOUT, FDO_FRST); + outb(fdc->baseport + FDOUT, FDO_FRST); /* see if it can handle a command */ - if (fd_cmd(fdcu, - 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), - 0)) - { - return(0); + if (fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), + NE7_SPEC_2(2, 0), 0)) { + error = ENXIO; + goto out; } + + if (fd_cmd(fdc, 1, NE7CMD_VERSION, 1, &ic_type) == 0) { + ic_type = (u_char)ic_type; + switch (ic_type) { + case 0x80: + device_set_desc(dev, "NEC 765 or clone"); + fdc->fdct = FDC_NE765; + break; + case 0x81: + device_set_desc(dev, "Intel 82077 or clone"); + fdc->fdct = FDC_I82077; + break; + case 0x90: + device_set_desc(dev, "NEC 72065B or clone"); + fdc->fdct = FDC_NE72065; + break; + default: + device_set_desc(dev, "generic floppy controller"); + fdc->fdct = FDC_UNKNOWN; + break; + } + } + + snprintf(myname, sizeof(myname), "%s%d", device_get_name(dev), + device_get_unit(dev)); + for (i = resource_query_string(-1, "at", myname); i != -1; + i = resource_query_string(i, "at", myname)) + fdc_add_device(dev, resource_query_name(i), + resource_query_unit(i)); #ifdef FDC_YE /* * don't succeed on probe; wait * for PCCARD subsystem to do it */ if (dev->id_flags & FDC_IS_PCMCIA) return(0); #endif - return (IO_FDCSIZE); + return (0); + +out: + if (fdc->fdc_intr) + BUS_TEARDOWN_INTR(device_get_parent(dev), dev, fdc->res_irq, + fdc->fdc_intr); + if (fdc->res_irq != 0) { + bus_deactivate_resource(dev, SYS_RES_IRQ, fdc->rid_irq, + fdc->res_irq); + bus_release_resource(dev, SYS_RES_IRQ, fdc->rid_irq, + fdc->res_irq); + } + if (fdc->res_ioport != 0) { + bus_deactivate_resource(dev, SYS_RES_IOPORT, fdc->rid_ioport, + fdc->res_ioport); + bus_release_resource(dev, SYS_RES_IOPORT, fdc->rid_ioport, + fdc->res_ioport); + } + if (fdc->res_drq != 0) { + bus_deactivate_resource(dev, SYS_RES_DRQ, fdc->rid_drq, + fdc->res_drq); + bus_release_resource(dev, SYS_RES_DRQ, fdc->rid_drq, + fdc->res_drq); + } + return (error); } /* - * wire controller into system, look for floppy units + * Aped dfr@freebsd.org's isa_add_device(). */ +static void +fdc_add_device(device_t dev, const char *name, int unit) +{ + int disabled, *ivar; + device_t child; + + ivar = malloc(sizeof *ivar, M_DEVBUF /* XXX */, M_NOWAIT); + if (ivar == 0) + return; + if (resource_int_value(name, unit, "drive", ivar) == 0) + *ivar = 0; + child = device_add_child(dev, name, unit, ivar); + if (child == 0) + return; + if (resource_int_value(name, unit, "disabled", &disabled) == 0) + device_disable(child); +} + static int -fdattach(struct isa_device *dev) +fdc_attach(device_t dev) { - unsigned fdt; - fdu_t fdu; - fdcu_t fdcu = dev->id_unit; - fdc_p fdc = fdc_data + fdcu; - fd_p fd; - int fdsu, st0, st3, i; - struct isa_device *fdup; - int ic_type = 0; -#ifdef DEVFS - int mynor; - int typemynor; - int typesize; -#endif + struct fdc_data *fdc = device_get_softc(dev); + fdcu_t fdcu = device_get_unit(dev); - dev->id_ointr = fdintr; fdc->fdcu = fdcu; fdc->flags |= FDC_ATTACHED; - fdc->dmachan = dev->id_drq; + /* Acquire the DMA channel forever, The driver will do the rest */ + /* XXX should integrate with rman */ isa_dma_acquire(fdc->dmachan); isa_dmainit(fdc->dmachan, 128 << 3 /* XXX max secsize */); fdc->state = DEVIDLE; + /* reset controller, turn motor off, clear fdout mirror reg */ outb(fdc->baseport + FDOUT, ((fdc->fdout = 0))); bufq_init(&fdc->head); - /* check for each floppy drive */ - for (fdup = isa_biotab_fdc; fdup->id_driver != 0; fdup++) { - if (fdup->id_iobase != dev->id_iobase) - continue; - fdu = fdup->id_unit; - fd = &fd_data[fdu]; - if (fdu >= (NFD)) - continue; - fdsu = fdup->id_physid; - /* look up what bios thinks we have */ - switch (fdu) { - case 0: if (dev->id_flags & FDC_PRETEND_D0) - fdt = RTCFDT_144M | RTCFDT_144M_PRETENDED; - else - fdt = (rtcin(RTC_FDISKETTE) & 0xf0); - break; - case 1: fdt = ((rtcin(RTC_FDISKETTE) << 4) & 0xf0); - break; - default: fdt = RTCFDT_NONE; - break; - } - /* is there a unit? */ - if ((fdt == RTCFDT_NONE) - ) { - fd->type = NO_TYPE; - continue; - } +#ifdef FIFO_BEFORE_MOTORON + /* Hmm, this doesn't work here - is set_motor() magic? -Peter */ + if (fdc->fdct != FDC_NE765 && fdc->fdct != FDC_UNKNOWN + && enable_fifo(fdc) == 0) { + device_print_prettyname(dev); + printf("FIFO enabled, %d bytes threshold\n", fifo_threshold); + } +#endif + /* + * Probe and attach any children as were configured above. + */ + return (bus_generic_attach(dev)); +} - /* select it */ - set_motor(fdcu, fdsu, TURNON); - DELAY(1000000); /* 1 sec */ +static void +fdc_print_child(device_t me, device_t child) +{ + printf(" at %s%d drive %d", device_get_name(me), device_get_unit(me), + *(int *)device_get_ivars(child)); +} - if (ic_type == 0 && - fd_cmd(fdcu, 1, NE7CMD_VERSION, 1, &ic_type) == 0) - { -#ifdef FDC_PRINT_BOGUS_CHIPTYPE - printf("fdc%d: ", fdcu); +static int +fd_probe(device_t dev) +{ + int i; + u_int fdt, st0, st3; + struct fd_data *fd; + struct fdc_data *fdc; + fdsu_t fdsu; +#ifndef FIFO_BEFORE_MOTORON + static int fd_fifo = 0; #endif - ic_type = (u_char)ic_type; - switch( ic_type ) { - case 0x80: -#ifdef FDC_PRINT_BOGUS_CHIPTYPE - printf("NEC 765\n"); + + fdsu = *(int *)device_get_ivars(dev); /* xxx cheat a bit... */ + fd = device_get_softc(dev); + fdc = device_get_softc(device_get_parent(dev)); + + bzero(fd, sizeof *fd); + fd->dev = dev; + fd->fdc = fdc; + fd->fdsu = fdsu; + fd->fdu = device_get_unit(dev); + + /* look up what bios thinks we have */ + switch (fd->fdu) { + case 0: + if (isa_get_flags(fdc->fdc_dev) & FDC_PRETEND_D0) + fdt = RTCFDT_144M | RTCFDT_144M_PRETENDED; + else + fdt = (rtcin(RTC_FDISKETTE) & 0xf0); + break; + case 1: + fdt = ((rtcin(RTC_FDISKETTE) << 4) & 0xf0); + break; + default: + fdt = RTCFDT_NONE; + break; + } + + /* is there a unit? */ + if (fdt == RTCFDT_NONE) + return (ENXIO); + + /* select it */ + set_motor(fdc, fdsu, TURNON); + DELAY(1000000); /* 1 sec */ + +#ifndef FIFO_BEFORE_MOTORON + if (fd_fifo == 0 && fdc->fdct != FDC_NE765 && fdc->fdct != FDC_UNKNOWN + && enable_fifo(fdc) == 0) { + device_print_prettyname(device_get_parent(dev)); + printf("FIFO enabled, %d bytes threshold\n", fifo_threshold); + } + fd_fifo = 1; #endif - fdc->fdct = FDC_NE765; - break; - case 0x81: -#ifdef FDC_PRINT_BOGUS_CHIPTYPE - printf("Intel 82077\n"); -#endif - fdc->fdct = FDC_I82077; - break; - case 0x90: -#ifdef FDC_PRINT_BOGUS_CHIPTYPE - printf("NEC 72065B\n"); -#endif - fdc->fdct = FDC_NE72065; - break; - default: -#ifdef FDC_PRINT_BOGUS_CHIPTYPE - printf("unknown IC type %02x\n", ic_type); -#endif - fdc->fdct = FDC_UNKNOWN; - break; - } - if (fdc->fdct != FDC_NE765 && - fdc->fdct != FDC_UNKNOWN && - enable_fifo(fdc) == 0) { - printf("fdc%d: FIFO enabled", fdcu); - printf(", %d bytes threshold\n", - fifo_threshold); - } - } - if ((fd_cmd(fdcu, 2, NE7CMD_SENSED, fdsu, 1, &st3) == 0) && - (st3 & NE7_ST3_T0)) { - /* if at track 0, first seek inwards */ - /* seek some steps: */ - (void)fd_cmd(fdcu, 3, NE7CMD_SEEK, fdsu, 10, 0); - DELAY(300000); /* ...wait a moment... */ - (void)fd_sense_int(fdc, 0, 0); /* make ctrlr happy */ - } - /* If we're at track 0 first seek inwards. */ - if ((fd_sense_drive_status(fdc, &st3) == 0) && - (st3 & NE7_ST3_T0)) { - /* Seek some steps... */ - if (fd_cmd(fdcu, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) { - /* ...wait a moment... */ - DELAY(300000); - /* make ctrlr happy: */ - (void)fd_sense_int(fdc, 0, 0); - } + if ((fd_cmd(fdc, 2, NE7CMD_SENSED, fdsu, 1, &st3) == 0) + && (st3 & NE7_ST3_T0)) { + /* if at track 0, first seek inwards */ + /* seek some steps: */ + fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0); + DELAY(300000); /* ...wait a moment... */ + fd_sense_int(fdc, 0, 0); /* make ctrlr happy */ + } + + /* If we're at track 0 first seek inwards. */ + if ((fd_sense_drive_status(fdc, &st3) == 0) && (st3 & NE7_ST3_T0)) { + /* Seek some steps... */ + if (fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) { + /* ...wait a moment... */ + DELAY(300000); + /* make ctrlr happy: */ + fd_sense_int(fdc, 0, 0); } + } - for(i = 0; i < 2; i++) { - /* - * we must recalibrate twice, just in case the - * heads have been beyond cylinder 76, since most - * FDCs still barf when attempting to recalibrate - * more than 77 steps - */ - /* go back to 0: */ - if (fd_cmd(fdcu, 2, NE7CMD_RECAL, fdsu, 0) == 0) { - /* a second being enough for full stroke seek*/ - DELAY(i == 0? 1000000: 300000); + for (i = 0; i < 2; i++) { + /* + * we must recalibrate twice, just in case the + * heads have been beyond cylinder 76, since most + * FDCs still barf when attempting to recalibrate + * more than 77 steps + */ + /* go back to 0: */ + if (fd_cmd(fdc, 2, NE7CMD_RECAL, fdsu, 0) == 0) { + /* a second being enough for full stroke seek*/ + DELAY(i == 0 ? 1000000 : 300000); - /* anything responding? */ - if (fd_sense_int(fdc, &st0, 0) == 0 && - (st0 & NE7_ST0_EC) == 0) - break; /* already probed succesfully */ - } + /* anything responding? */ + if (fd_sense_int(fdc, &st0, 0) == 0 && + (st0 & NE7_ST0_EC) == 0) + break; /* already probed succesfully */ } + } - set_motor(fdcu, fdsu, TURNOFF); + set_motor(fdc, fdsu, TURNOFF); - if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */ - continue; + if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */ + return (ENXIO); - fd->track = FD_NO_TRACK; - fd->fdc = fdc; - fd->fdsu = fdsu; - fd->options = 0; - callout_handle_init(&fd->toffhandle); - callout_handle_init(&fd->tohandle); - printf("fd%d: ", fdu); + fd->track = FD_NO_TRACK; + fd->fdc = fdc; + fd->fdsu = fdsu; + fd->options = 0; + callout_handle_init(&fd->toffhandle); + callout_handle_init(&fd->tohandle); - switch (fdt) { - case RTCFDT_12M: - printf("1.2MB 5.25in\n"); - fd->type = FD_1200; + switch (fdt) { + case RTCFDT_12M: + device_set_desc(dev, "1200-KB 5.25\" drive"); + fd->type = FD_1200; + break; + case RTCFDT_144M | RTCFDT_144M_PRETENDED: + device_set_desc(dev, "config-pretended 1440-MB 3.5\" drive"); + fdt = RTCFDT_144M; + fd->type = FD_1440; + case RTCFDT_144M: + device_set_desc(dev, "1440-KB 3.5\" drive"); + fd->type = FD_1440; + break; + case RTCFDT_288M: + case RTCFDT_288M_1: + device_set_desc(dev, "2880-KB 3.5\" drive (in 1440-KB mode)"); + fd->type = FD_1440; + break; + case RTCFDT_360K: + device_set_desc(dev, "360-KB 5.25\" drive"); + fd->type = FD_360; + break; + case RTCFDT_720K: + printf("720-KB 3.5\" drive"); + fd->type = FD_720; + break; + default: + return (ENXIO); + } + return (0); +} + +static int +fd_attach(device_t dev) +{ + struct fd_data *fd; + + fd = device_get_softc(dev); + +#ifdef DEVFS /* XXX bitrot */ + mynor = fdu << 6; + fd->bdevs[0] = devfs_add_devswf(&fd_cdevsw, mynor, DV_BLK, + UID_ROOT, GID_OPERATOR, 0640, + "fd%d", fdu); + fd->cdevs[0] = devfs_add_devswf(&fd_cdevsw, mynor, DV_CHR, + UID_ROOT, GID_OPERATOR, 0640, + "rfd%d", fdu); + for (i = 1; i < 1 + NUMDENS; i++) { + /* + * XXX this and the lookup in Fdopen() should be + * data driven. + */ + switch (fd->type) { + case FD_360: + if (i != FD_360) + continue; break; - case RTCFDT_144M | RTCFDT_144M_PRETENDED: - printf("config-pretended "); - fdt = RTCFDT_144M; - /* fallthrough */ - case RTCFDT_144M: - printf("1.44MB 3.5in\n"); - fd->type = FD_1440; + case FD_720: + if (i != FD_720 && i != FD_800 && i != FD_820) + continue; break; - case RTCFDT_288M: - case RTCFDT_288M_1: - printf("2.88MB 3.5in - 1.44MB mode\n"); - fd->type = FD_1440; + case FD_1200: + if (i != FD_360 && i != FD_720 && i != FD_800 + && i != FD_820 && i != FD_1200 + && i != FD_1440 && i != FD_1480) + continue; break; - case RTCFDT_360K: - printf("360KB 5.25in\n"); - fd->type = FD_360; + case FD_1440: + if (i != FD_720 && i != FD_800 && i != FD_820 + && i != FD_1200 && i != FD_1440 + && i != FD_1480 && i != FD_1720) + continue; break; - case RTCFDT_720K: - printf("720KB 3.5in\n"); - fd->type = FD_720; - break; - default: - printf("unknown\n"); - fd->type = NO_TYPE; - continue; } -#ifdef DEVFS - mynor = fdu << 6; - fd->bdevs[0] = devfs_add_devswf(&fd_cdevsw, mynor, DV_BLK, - UID_ROOT, GID_OPERATOR, 0640, - "fd%d", fdu); - fd->cdevs[0] = devfs_add_devswf(&fd_cdevsw, mynor, DV_CHR, - UID_ROOT, GID_OPERATOR, 0640, - "rfd%d", fdu); - for (i = 1; i < 1 + NUMDENS; i++) { - /* - * XXX this and the lookup in Fdopen() should be - * data driven. - */ - switch (fd->type) { - case FD_360: - if (i != FD_360) - continue; - break; - case FD_720: - if (i != FD_720 && i != FD_800 && i != FD_820) - continue; - break; - case FD_1200: - if (i != FD_360 && i != FD_720 && i != FD_800 - && i != FD_820 && i != FD_1200 - && i != FD_1440 && i != FD_1480) - continue; - break; - case FD_1440: - if (i != FD_720 && i != FD_800 && i != FD_820 - && i != FD_1200 && i != FD_1440 - && i != FD_1480 && i != FD_1720) - continue; - break; - } - typesize = fd_types[i - 1].size / 2; - /* - * XXX all these conversions give bloated code and - * confusing names. - */ - if (typesize == 1476) - typesize = 1480; - if (typesize == 1722) - typesize = 1720; - typemynor = mynor | i; - fd->bdevs[i] = - devfs_add_devswf(&fd_cdevsw, typemynor, DV_BLK, - UID_ROOT, GID_OPERATOR, 0640, - "fd%d.%d", fdu, typesize); - fd->cdevs[i] = - devfs_add_devswf(&fd_cdevsw, typemynor, DV_CHR, - UID_ROOT, GID_OPERATOR, 0640, - "rfd%d.%d", fdu, typesize); - } - - for (i = 0; i < MAXPARTITIONS; i++) { - fd->bdevs[1 + NUMDENS + i] = devfs_makelink(fd->bdevs[0], - "fd%d%c", fdu, 'a' + i); - fd->cdevs[1 + NUMDENS + i] = - devfs_makelink(fd->cdevs[0], - "rfd%d%c", fdu, 'a' + i); - } -#endif /* DEVFS */ + typesize = fd_types[i - 1].size / 2; /* - * Export the drive to the devstat interface. + * XXX all these conversions give bloated code and + * confusing names. */ - devstat_add_entry(&fd->device_stats, "fd", - fdu, 512, - DEVSTAT_NO_ORDERED_TAGS, - DEVSTAT_TYPE_FLOPPY | DEVSTAT_TYPE_IF_OTHER, - DEVSTAT_PRIORITY_FD); - + if (typesize == 1476) + typesize = 1480; + if (typesize == 1722) + typesize = 1720; + typemynor = mynor | i; + fd->bdevs[i] = + devfs_add_devswf(&fd_cdevsw, typemynor, DV_BLK, + UID_ROOT, GID_OPERATOR, 0640, + "fd%d.%d", fdu, typesize); + fd->cdevs[i] = + devfs_add_devswf(&fd_cdevsw, typemynor, DV_CHR, + UID_ROOT, GID_OPERATOR, 0640, + "rfd%d.%d", fdu, typesize); } - return (1); + for (i = 0; i < MAXPARTITIONS; i++) { + fd->bdevs[1 + NUMDENS + i] = devfs_makelink(fd->bdevs[0], + "fd%d%c", fdu, 'a' + i); + fd->cdevs[1 + NUMDENS + i] = + devfs_makelink(fd->cdevs[0], + "rfd%d%c", fdu, 'a' + i); + } +#endif /* DEVFS */ + /* + * Export the drive to the devstat interface. + */ + devstat_add_entry(&fd->device_stats, device_get_name(dev), + device_get_unit(dev), 512, DEVSTAT_NO_ORDERED_TAGS, + DEVSTAT_TYPE_FLOPPY | DEVSTAT_TYPE_IF_OTHER, + DEVSTAT_PRIORITY_FD); + return (0); } - - #ifdef FDC_YE /* * this is a subset of fdattach() optimized for the Y-E Data * PCMCIA floppy drive. */ static int yeattach(struct isa_device *dev) { fdcu_t fdcu = dev->id_unit; fdc_p fdc = fdc_data + fdcu; fdsu_t fdsu = 0; /* assume 1 drive per YE controller */ fdu_t fdu; fd_p fd; int st0, st3, i; #ifdef DEVFS int mynor; int typemynor; int typesize; #endif fdc->fdcu = fdcu; /* * the FDC_PCMCIA flag is used to to indicate special PIO is used * instead of DMA */ fdc->flags = FDC_ATTACHED|FDC_PCMCIA; fdc->state = DEVIDLE; /* reset controller, turn motor off, clear fdout mirror reg */ outb(fdc->baseport + FDOUT, ((fdc->fdout = 0))); bufq_init(&fdc->head); /* * assume 2 drives/ "normal" controller */ fdu = fdcu * 2; if (fdu >= NFD) { printf("fdu %d >= NFD\n",fdu); return(0); }; fd = &fd_data[fdu]; set_motor(fdcu, fdsu, TURNON); DELAY(1000000); /* 1 sec */ fdc->fdct = FDC_NE765; if ((fd_cmd(fdcu, 2, NE7CMD_SENSED, fdsu, 1, &st3) == 0) && (st3 & NE7_ST3_T0)) { /* if at track 0, first seek inwards */ /* seek some steps: */ (void)fd_cmd(fdcu, 3, NE7CMD_SEEK, fdsu, 10, 0); DELAY(300000); /* ...wait a moment... */ (void)fd_sense_int(fdc, 0, 0); /* make ctrlr happy */ } /* If we're at track 0 first seek inwards. */ if ((fd_sense_drive_status(fdc, &st3) == 0) && (st3 & NE7_ST3_T0)) { /* Seek some steps... */ if (fd_cmd(fdcu, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) { /* ...wait a moment... */ DELAY(300000); /* make ctrlr happy: */ (void)fd_sense_int(fdc, 0, 0); } } for(i = 0; i < 2; i++) { /* * we must recalibrate twice, just in case the * heads have been beyond cylinder 76, since most * FDCs still barf when attempting to recalibrate * more than 77 steps */ /* go back to 0: */ if (fd_cmd(fdcu, 2, NE7CMD_RECAL, fdsu, 0) == 0) { /* a second being enough for full stroke seek*/ DELAY(i == 0? 1000000: 300000); /* anything responding? */ if (fd_sense_int(fdc, &st0, 0) == 0 && (st0 & NE7_ST0_EC) == 0) break; /* already probed succesfully */ } } set_motor(fdcu, fdsu, TURNOFF); if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */ return(0); fd->track = FD_NO_TRACK; fd->fdc = fdc; fd->fdsu = fdsu; fd->options = 0; printf("fdc%d: 1.44MB 3.5in PCMCIA\n", fdcu); fd->type = FD_1440; #ifdef DEVFS mynor = fdcu << 6; fd->bdevs[0] = devfs_add_devswf(&fd_cdevsw, mynor, DV_BLK, UID_ROOT, GID_OPERATOR, 0640, "fd%d", fdu); fd->cdevs[0] = devfs_add_devswf(&fd_cdevsw, mynor, DV_CHR, UID_ROOT, GID_OPERATOR, 0640, "rfd%d", fdu); /* * XXX this and the lookup in Fdopen() should be * data driven. */ typemynor = mynor | FD_1440; typesize = fd_types[FD_1440 - 1].size / 2; /* * XXX all these conversions give bloated code and * confusing names. */ if (typesize == 1476) typesize = 1480; if (typesize == 1722) typesize = 1720; fd->bdevs[FD_1440] = devfs_add_devswf(&fd_cdevsw, typemynor, DV_BLK, UID_ROOT, GID_OPERATOR, 0640, "fd%d.%d", fdu, typesize); fd->cdevs[FD_1440] = devfs_add_devswf(&fd_cdevsw, typemynor, DV_CHR, UID_ROOT, GID_OPERATOR, 0640,"rfd%d.%d", fdu, typesize); for (i = 0; i < MAXPARTITIONS; i++) { fd->bdevs[1 + NUMDENS + i] = devfs_makelink(fd->bdevs[0], "fd%d%c", fdu, 'a' + i); fd->cdevs[1 + NUMDENS + i] = devfs_makelink(fd->cdevs[0], "rfd%d%c", fdu, 'a' + i); } #endif /* DEVFS */ return (1); } #endif /****************************************************************************/ /* motor control stuff */ /* remember to not deselect the drive we're working on */ /****************************************************************************/ static void -set_motor(fdcu_t fdcu, int fdsu, int turnon) +set_motor(struct fdc_data *fdc, int fdsu, int turnon) { - int fdout = fdc_data[fdcu].fdout; + int fdout = fdc->fdout; int needspecify = 0; if(turnon) { fdout &= ~FDO_FDSEL; fdout |= (FDO_MOEN0 << fdsu) + fdsu; } else fdout &= ~(FDO_MOEN0 << fdsu); if(!turnon && (fdout & (FDO_MOEN0+FDO_MOEN1+FDO_MOEN2+FDO_MOEN3)) == 0) /* gonna turn off the last drive, put FDC to bed */ fdout &= ~ (FDO_FRST|FDO_FDMAEN); else { /* make sure controller is selected and specified */ if((fdout & (FDO_FRST|FDO_FDMAEN)) == 0) needspecify = 1; fdout |= (FDO_FRST|FDO_FDMAEN); } - outb(fdc_data[fdcu].baseport+FDOUT, fdout); - fdc_data[fdcu].fdout = fdout; + outb(fdc->baseport+FDOUT, fdout); + fdc->fdout = fdout; TRACE1("[0x%x->FDOUT]", fdout); - if(needspecify) { + if (needspecify) { /* * XXX * special case: since we have just woken up the FDC * from its sleep, we silently assume the command will * be accepted, and do not test for a timeout */ - (void)fd_cmd(fdcu, 3, NE7CMD_SPECIFY, + (void)fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), 0); - if (fdc_data[fdcu].flags & FDC_HAS_FIFO) - (void) enable_fifo(&fdc_data[fdcu]); + if (fdc->flags & FDC_HAS_FIFO) + (void) enable_fifo(fdc); } } static void -fd_turnoff(void *arg1) +fd_turnoff(void *xfd) { - fdu_t fdu = (fdu_t)arg1; int s; - fd_p fd = fd_data + fdu; + fd_p fd = xfd; - TRACE1("[fd%d: turnoff]", fdu); + TRACE1("[fd%d: turnoff]", fd->fdu); /* * Don't turn off the motor yet if the drive is active. * XXX shouldn't even schedule turnoff until drive is inactive * and nothing is queued on it. */ - if (fd->fdc->state != DEVIDLE && fd->fdc->fdu == fdu) { - fd->toffhandle = timeout(fd_turnoff, arg1, 4 * hz); + if (fd->fdc->state != DEVIDLE && fd->fdc->fdu == fd->fdu) { + fd->toffhandle = timeout(fd_turnoff, fd, 4 * hz); return; } s = splbio(); fd->flags &= ~FD_MOTOR; - set_motor(fd->fdc->fdcu, fd->fdsu, TURNOFF); + set_motor(fd->fdc, fd->fdsu, TURNOFF); splx(s); } static void -fd_motor_on(void *arg1) +fd_motor_on(void *xfd) { - fdu_t fdu = (fdu_t)arg1; int s; + fd_p fd = xfd; - fd_p fd = fd_data + fdu; s = splbio(); fd->flags &= ~FD_MOTOR_WAIT; if((fd->fdc->fd == fd) && (fd->fdc->state == MOTORWAIT)) { - fdintr(fd->fdc->fdcu); + fdc_intr(fd->fdc); } splx(s); } static void -fd_turnon(fdu_t fdu) +fd_turnon(fd_p fd) { - fd_p fd = fd_data + fdu; if(!(fd->flags & FD_MOTOR)) { fd->flags |= (FD_MOTOR + FD_MOTOR_WAIT); - set_motor(fd->fdc->fdcu, fd->fdsu, TURNON); - timeout(fd_motor_on, (caddr_t)fdu, hz); /* in 1 sec its ok */ + set_motor(fd->fdc, fd->fdsu, TURNON); + timeout(fd_motor_on, fd, hz); /* in 1 sec its ok */ } } static void fdc_reset(fdc_p fdc) { - fdcu_t fdcu = fdc->fdcu; - /* Try a reset, keep motor on */ outb(fdc->baseport + FDOUT, fdc->fdout & ~(FDO_FRST|FDO_FDMAEN)); TRACE1("[0x%x->FDOUT]", fdc->fdout & ~(FDO_FRST|FDO_FDMAEN)); DELAY(100); /* enable FDC, but defer interrupts a moment */ outb(fdc->baseport + FDOUT, fdc->fdout & ~FDO_FDMAEN); TRACE1("[0x%x->FDOUT]", fdc->fdout & ~FDO_FDMAEN); DELAY(100); outb(fdc->baseport + FDOUT, fdc->fdout); TRACE1("[0x%x->FDOUT]", fdc->fdout); /* XXX after a reset, silently believe the FDC will accept commands */ - (void)fd_cmd(fdcu, 3, NE7CMD_SPECIFY, + (void)fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), 0); if (fdc->flags & FDC_HAS_FIFO) (void) enable_fifo(fdc); } /****************************************************************************/ /* fdc in/out */ /****************************************************************************/ int -in_fdc(fdcu_t fdcu) +in_fdc(struct fdc_data *fdc) { - int baseport = fdc_data[fdcu].baseport; + int baseport = fdc->baseport; int i, j = 100000; while ((i = inb(baseport+FDSTS) & (NE7_DIO|NE7_RQM)) != (NE7_DIO|NE7_RQM) && j-- > 0) if (i == NE7_RQM) - return fdc_err(fdcu, "ready for output in input\n"); + return fdc_err(fdc, "ready for output in input\n"); if (j <= 0) - return fdc_err(fdcu, bootverbose? "input ready timeout\n": 0); + return fdc_err(fdc, bootverbose? "input ready timeout\n": 0); #ifdef FDC_DEBUG i = inb(baseport+FDDATA); TRACE1("[FDDATA->0x%x]", (unsigned char)i); return(i); #else /* !FDC_DEBUG */ return inb(baseport+FDDATA); #endif /* FDC_DEBUG */ } /* * fd_in: Like in_fdc, but allows you to see if it worked. */ static int -fd_in(fdcu_t fdcu, int *ptr) +fd_in(struct fdc_data *fdc, int *ptr) { - int baseport = fdc_data[fdcu].baseport; + int baseport = fdc->baseport; int i, j = 100000; while ((i = inb(baseport+FDSTS) & (NE7_DIO|NE7_RQM)) != (NE7_DIO|NE7_RQM) && j-- > 0) if (i == NE7_RQM) - return fdc_err(fdcu, "ready for output in input\n"); + return fdc_err(fdc, "ready for output in input\n"); if (j <= 0) - return fdc_err(fdcu, bootverbose? "input ready timeout\n": 0); + return fdc_err(fdc, bootverbose? "input ready timeout\n": 0); #ifdef FDC_DEBUG i = inb(baseport+FDDATA); TRACE1("[FDDATA->0x%x]", (unsigned char)i); *ptr = i; return 0; #else /* !FDC_DEBUG */ i = inb(baseport+FDDATA); if (ptr) *ptr = i; return 0; #endif /* FDC_DEBUG */ } int -out_fdc(fdcu_t fdcu, int x) +out_fdc(struct fdc_data *fdc, int x) { - int baseport = fdc_data[fdcu].baseport; + int baseport = fdc->baseport; int i; /* Check that the direction bit is set */ i = 100000; while ((inb(baseport+FDSTS) & NE7_DIO) && i-- > 0); - if (i <= 0) return fdc_err(fdcu, "direction bit not set\n"); + if (i <= 0) return fdc_err(fdc, "direction bit not set\n"); /* Check that the floppy controller is ready for a command */ i = 100000; while ((inb(baseport+FDSTS) & NE7_RQM) == 0 && i-- > 0); if (i <= 0) - return fdc_err(fdcu, bootverbose? "output ready timeout\n": 0); + return fdc_err(fdc, bootverbose? "output ready timeout\n": 0); /* Send the command and return */ outb(baseport+FDDATA, x); TRACE1("[0x%x->FDDATA]", x); return (0); } /****************************************************************************/ /* fdopen/fdclose */ /****************************************************************************/ int Fdopen(dev_t dev, int flags, int mode, struct proc *p) { fdu_t fdu = FDUNIT(minor(dev)); int type = FDTYPE(minor(dev)); + fd_p fd; fdc_p fdc; /* check bounds */ - if (fdu >= NFD) - return(ENXIO); - fdc = fd_data[fdu].fdc; - if ((fdc == NULL) || (fd_data[fdu].type == NO_TYPE)) - return(ENXIO); + if ((fd = devclass_get_softc(fd_devclass, fdu)) == 0) + return (ENXIO); + fdc = fd->fdc; + if ((fdc == NULL) || (fd->type == NO_TYPE)) + return (ENXIO); if (type > NUMDENS) - return(ENXIO); + return (ENXIO); if (type == 0) - type = fd_data[fdu].type; + type = fd->type; else { /* * For each type of basic drive, make sure we are trying * to open a type it can do, */ - if (type != fd_data[fdu].type) { - switch (fd_data[fdu].type) { + if (type != fd->type) { + switch (fd->type) { case FD_360: - return(ENXIO); + return (ENXIO); case FD_720: if ( type != FD_820 && type != FD_800 ) - return(ENXIO); + return (ENXIO); break; case FD_1200: switch (type) { case FD_1480: type = FD_1480in5_25; break; case FD_1440: type = FD_1440in5_25; break; case FD_820: type = FD_820in5_25; break; case FD_800: type = FD_800in5_25; break; case FD_720: type = FD_720in5_25; break; case FD_360: type = FD_360in5_25; break; default: return(ENXIO); } break; case FD_1440: if ( type != FD_1720 && type != FD_1480 && type != FD_1200 && type != FD_820 && type != FD_800 && type != FD_720 ) return(ENXIO); break; } } } - fd_data[fdu].ft = fd_types + type - 1; - fd_data[fdu].flags |= FD_OPEN; - + fd->ft = fd_types + type - 1; + fd->flags |= FD_OPEN; + device_busy(fd->dev); + device_busy(fd->fdc->fdc_dev); return 0; } int fdclose(dev_t dev, int flags, int mode, struct proc *p) { fdu_t fdu = FDUNIT(minor(dev)); + struct fd_data *fd; - fd_data[fdu].flags &= ~FD_OPEN; - fd_data[fdu].options &= ~FDOPT_NORETRY; + fd = devclass_get_softc(fd_devclass, fdu); + fd->flags &= ~FD_OPEN; + fd->options &= ~FDOPT_NORETRY; - return(0); + return (0); } static int fdread(dev_t dev, struct uio *uio, int ioflag) { return (physio(fdstrategy, NULL, dev, 1, minphys, uio)); } static int fdwrite(dev_t dev, struct uio *uio, int ioflag) { return (physio(fdstrategy, NULL, dev, 0, minphys, uio)); } /****************************************************************************/ /* fdstrategy */ /****************************************************************************/ void fdstrategy(struct buf *bp) { unsigned nblocks, blknum, cando; int s; - fdcu_t fdcu; fdu_t fdu; fdc_p fdc; fd_p fd; size_t fdblk; fdu = FDUNIT(minor(bp->b_dev)); - fd = &fd_data[fdu]; + fd = devclass_get_softc(fd_devclass, fdu); + if (fd == 0) + panic("fdstrategy: buf for nonexistent device (%#lx, %#lx)", + (u_long)major(bp->b_dev), (u_long)minor(bp->b_dev)); fdc = fd->fdc; - fdcu = fdc->fdcu; #ifdef FDC_YE if (fd->type == NO_TYPE) { bp->b_error = ENXIO; bp->b_flags |= B_ERROR; /* * I _refuse_ to use a goto */ biodone(bp); return; }; #endif fdblk = 128 << (fd->ft->secsize); if (!(bp->b_flags & B_FORMAT)) { - if ((fdu >= NFD) || (bp->b_blkno < 0)) { + if (bp->b_blkno < 0) { printf( "fd%d: fdstrat: bad request blkno = %lu, bcount = %ld\n", fdu, (u_long)bp->b_blkno, bp->b_bcount); bp->b_error = EINVAL; bp->b_flags |= B_ERROR; goto bad; } if ((bp->b_bcount % fdblk) != 0) { bp->b_error = EINVAL; bp->b_flags |= B_ERROR; goto bad; } } /* * Set up block calculations. */ if (bp->b_blkno > 20000000) { /* * Reject unreasonably high block number, prevent the * multiplication below from overflowing. */ bp->b_error = EINVAL; bp->b_flags |= B_ERROR; goto bad; } blknum = (unsigned) bp->b_blkno * DEV_BSIZE/fdblk; nblocks = fd->ft->size; bp->b_resid = 0; if (blknum + (bp->b_bcount / fdblk) > nblocks) { if (blknum <= nblocks) { cando = (nblocks - blknum) * fdblk; bp->b_resid = bp->b_bcount - cando; if (cando == 0) goto bad; /* not actually bad but EOF */ } else { bp->b_error = EINVAL; bp->b_flags |= B_ERROR; goto bad; } } bp->b_pblkno = bp->b_blkno; s = splbio(); bufqdisksort(&fdc->head, bp); - untimeout(fd_turnoff, (caddr_t)fdu, fd->toffhandle); /* a good idea */ + untimeout(fd_turnoff, fd, fd->toffhandle); /* a good idea */ /* Tell devstat we are starting on the transaction */ devstat_start_transaction(&fd->device_stats); - fdstart(fdcu); + fdstart(fdc); splx(s); return; bad: biodone(bp); } /***************************************************************\ * fdstart * * We have just queued something.. if the controller is not busy * * then simulate the case where it has just finished a command * * So that it (the interrupt routine) looks on the queue for more* * work to do and picks up what we just added. * * If the controller is already busy, we need do nothing, as it * * will pick up our work when the present work completes * \***************************************************************/ static void -fdstart(fdcu_t fdcu) +fdstart(struct fdc_data *fdc) { int s; s = splbio(); - if(fdc_data[fdcu].state == DEVIDLE) + if(fdc->state == DEVIDLE) { - fdintr(fdcu); + fdc_intr(fdc); } splx(s); } static void -fd_iotimeout(void *arg1) +fd_iotimeout(void *xfdc) { fdc_p fdc; - fdcu_t fdcu; int s; - fdcu = (fdcu_t)arg1; - fdc = fdc_data + fdcu; + fdc = xfdc; TRACE1("fd%d[fd_iotimeout()]", fdc->fdu); /* * Due to IBM's brain-dead design, the FDC has a faked ready * signal, hardwired to ready == true. Thus, any command * issued if there's no diskette in the drive will _never_ * complete, and must be aborted by resetting the FDC. * Many thanks, Big Blue! * The FDC must not be reset directly, since that would * interfere with the state machine. Instead, pretend that * the command completed but was invalid. The state machine * will reset the FDC and retry once. */ s = splbio(); fdc->status[0] = NE7_ST0_IC_IV; fdc->flags &= ~FDC_STAT_VALID; fdc->state = IOTIMEDOUT; - fdintr(fdcu); + fdc_intr(fdc); splx(s); } /* just ensure it has the right spl */ static void -fd_pseudointr(void *arg1) +fd_pseudointr(void *xfdc) { - fdcu_t fdcu = (fdcu_t)arg1; int s; s = splbio(); - fdintr(fdcu); + fdc_intr(xfdc); splx(s); } /***********************************************************************\ * fdintr * * keep calling the state machine until it returns a 0 * * ALWAYS called at SPLBIO * \***********************************************************************/ static void -fdintr(fdcu_t fdcu) +fdc_intr(void *xfdc) { - fdc_p fdc = fdc_data + fdcu; - while(fdstate(fdcu, fdc)) - ; + fdc_p fdc = xfdc; + while(fdstate(fdc)) + ; } #ifdef FDC_YE /* * magic pseudo-DMA initialization for YE FDC. Sets count and * direction */ #define SET_BCDR(wr,cnt,port) outb(port,(((cnt)-1) & 0xff)); \ outb(port+1,((wr ? 0x80 : 0) | ((((cnt)-1) >> 8) & 0x7f))) /* * fdcpio(): perform programmed IO read/write for YE PCMCIA floppy */ static int fdcpio(fdcu_t fdcu, long flags, caddr_t addr, u_int count) { u_char *cptr = (u_char *)addr; fdc_p fdc = &fdc_data[fdcu]; int io = fdc->baseport; if (flags & B_READ) { if (fdc->state != PIOREAD) { fdc->state = PIOREAD; return(0); }; SET_BCDR(0,count,io); insb(io+FDC_YE_DATAPORT,cptr,count); } else { outsb(io+FDC_YE_DATAPORT,cptr,count); SET_BCDR(0,count,io); }; return(1); } #endif /* FDC_YE */ /***********************************************************************\ * The controller state machine. * * if it returns a non zero value, it should be called again immediatly * \***********************************************************************/ static int -fdstate(fdcu_t fdcu, fdc_p fdc) +fdstate(fdc_p fdc) { int read, format, head, i, sec = 0, sectrac, st0, cyl, st3; unsigned blknum = 0, b_cylinder = 0; fdu_t fdu = fdc->fdu; fd_p fd; register struct buf *bp; struct fd_formb *finfo = NULL; size_t fdblk; bp = fdc->bp; if (bp == NULL) { bp = bufq_first(&fdc->head); if (bp != NULL) { bufq_remove(&fdc->head, bp); fdc->bp = bp; } } if (bp == NULL) { /***********************************************\ * nothing left for this controller to do * * Force into the IDLE state, * \***********************************************/ fdc->state = DEVIDLE; - if(fdc->fd) - { - printf("fd%d: unexpected valid fd pointer\n", - fdc->fdu); + if (fdc->fd) { + device_print_prettyname(fdc->fdc_dev); + printf("unexpected valid fd pointer\n"); fdc->fd = (fd_p) 0; fdc->fdu = -1; } - TRACE1("[fdc%d IDLE]", fdcu); - return(0); + TRACE1("[fdc%d IDLE]", fdc->fdcu); + return (0); } fdu = FDUNIT(minor(bp->b_dev)); - fd = fd_data + fdu; + fd = devclass_get_softc(fd_devclass, fdu); fdblk = 128 << fd->ft->secsize; - if (fdc->fd && (fd != fdc->fd)) - { - printf("fd%d: confused fd pointers\n", fdu); + if (fdc->fd && (fd != fdc->fd)) { + device_print_prettyname(fd->dev); + printf("confused fd pointers\n"); } read = bp->b_flags & B_READ; format = bp->b_flags & B_FORMAT; - if(format) { + if (format) { finfo = (struct fd_formb *)bp->b_data; fd->skip = (char *)&(finfo->fd_formb_cylno(0)) - (char *)finfo; } if (fdc->state == DOSEEK || fdc->state == SEEKCOMPLETE) { blknum = (unsigned) bp->b_pblkno * DEV_BSIZE/fdblk + fd->skip/fdblk; b_cylinder = blknum / (fd->ft->sectrac * fd->ft->heads); } TRACE1("fd%d", fdu); TRACE1("[%s]", fdstates[fdc->state]); TRACE1("(0x%x)", fd->flags); - untimeout(fd_turnoff, (caddr_t)fdu, fd->toffhandle); - fd->toffhandle = timeout(fd_turnoff, (caddr_t)fdu, 4 * hz); + untimeout(fd_turnoff, fd, fd->toffhandle); + fd->toffhandle = timeout(fd_turnoff, fd, 4 * hz); switch (fdc->state) { case DEVIDLE: case FINDWORK: /* we have found new work */ fdc->retry = 0; fd->skip = 0; fdc->fd = fd; fdc->fdu = fdu; outb(fdc->baseport+FDCTL, fd->ft->trans); TRACE1("[0x%x->FDCTL]", fd->ft->trans); /*******************************************************\ * If the next drive has a motor startup pending, then * * it will start up in its own good time * \*******************************************************/ - if(fd->flags & FD_MOTOR_WAIT) - { + if(fd->flags & FD_MOTOR_WAIT) { fdc->state = MOTORWAIT; - return(0); /* come back later */ + return (0); /* come back later */ } /*******************************************************\ * Maybe if it's not starting, it SHOULD be starting * \*******************************************************/ if (!(fd->flags & FD_MOTOR)) { fdc->state = MOTORWAIT; - fd_turnon(fdu); - return(0); + fd_turnon(fd); + return (0); } else /* at least make sure we are selected */ { - set_motor(fdcu, fd->fdsu, TURNON); + set_motor(fdc, fd->fdsu, TURNON); } if (fdc->flags & FDC_NEEDS_RESET) { fdc->state = RESETCTLR; fdc->flags &= ~FDC_NEEDS_RESET; } else fdc->state = DOSEEK; break; case DOSEEK: if (b_cylinder == (unsigned)fd->track) { fdc->state = SEEKCOMPLETE; break; } - if (fd_cmd(fdcu, 3, NE7CMD_SEEK, + if (fd_cmd(fdc, 3, NE7CMD_SEEK, fd->fdsu, b_cylinder * fd->ft->steptrac, 0)) { /* * seek command not accepted, looks like * the FDC went off to the Saints... */ fdc->retry = 6; /* try a reset */ - return(retrier(fdcu)); + return(retrier(fdc)); } fd->track = FD_NO_TRACK; fdc->state = SEEKWAIT; return(0); /* will return later */ case SEEKWAIT: /* allow heads to settle */ - timeout(fd_pseudointr, (caddr_t)fdcu, hz / 16); + timeout(fd_pseudointr, fdc, hz / 16); fdc->state = SEEKCOMPLETE; return(0); /* will return later */ case SEEKCOMPLETE : /* SEEK DONE, START DMA */ /* Make sure seek really happened*/ - if(fd->track == FD_NO_TRACK) - { + if(fd->track == FD_NO_TRACK) { int descyl = b_cylinder * fd->ft->steptrac; do { /* * This might be a "ready changed" interrupt, * which cannot really happen since the * RDY pin is hardwired to + 5 volts. This * generally indicates a "bouncing" intr * line, so do one of the following: * * When running on an enhanced FDC that is * known to not go stuck after responding * with INVALID, fetch all interrupt states * until seeing either an INVALID or a * real interrupt condition. * * When running on a dumb old NE765, give * up immediately. The controller will * provide up to four dummy RC interrupt * conditions right after reset (for the * corresponding four drives), so this is * our only chance to get notice that it * was not the FDC that caused the interrupt. */ if (fd_sense_int(fdc, &st0, &cyl) == FD_NOT_VALID) return 0; if(fdc->fdct == FDC_NE765 && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC) return 0; /* hope for a real intr */ } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC); - if (0 == descyl) - { + if (0 == descyl) { int failed = 0; /* * seek to cyl 0 requested; make sure we are * really there */ if (fd_sense_drive_status(fdc, &st3)) failed = 1; if ((st3 & NE7_ST3_T0) == 0) { printf( "fd%d: Seek to cyl 0, but not really there (ST3 = %b)\n", fdu, st3, NE7_ST3BITS); failed = 1; } - if (failed) - { + if (failed) { if(fdc->retry < 3) fdc->retry = 3; - return(retrier(fdcu)); + return (retrier(fdc)); } } - if (cyl != descyl) - { + if (cyl != descyl) { printf( "fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n", fdu, descyl, cyl, st0); if (fdc->retry < 3) fdc->retry = 3; - return(retrier(fdcu)); + return (retrier(fdc)); } } fd->track = b_cylinder; #ifdef FDC_YE if (!(fdc->flags & FDC_PCMCIA)) #endif isa_dmastart(bp->b_flags, bp->b_data+fd->skip, format ? bp->b_bcount : fdblk, fdc->dmachan); sectrac = fd->ft->sectrac; sec = blknum % (sectrac * fd->ft->heads); head = sec / sectrac; sec = sec % sectrac + 1; fd->hddrv = ((head&1)<<2)+fdu; if(format || !read) { /* make sure the drive is writable */ if(fd_sense_drive_status(fdc, &st3) != 0) { /* stuck controller? */ isa_dmadone(bp->b_flags, bp->b_data + fd->skip, format ? bp->b_bcount : fdblk, fdc->dmachan); fdc->retry = 6; /* reset the beast */ - return(retrier(fdcu)); + return (retrier(fdc)); } if(st3 & NE7_ST3_WP) { /* * XXX YES! this is ugly. * in order to force the current operation * to fail, we will have to fake an FDC * error - all error handling is done * by the retrier() */ fdc->status[0] = NE7_ST0_IC_AT; fdc->status[1] = NE7_ST1_NW; fdc->status[2] = 0; fdc->status[3] = fd->track; fdc->status[4] = head; fdc->status[5] = sec; fdc->retry = 8; /* break out immediately */ fdc->state = IOTIMEDOUT; /* not really... */ return (1); } } - if(format) - { + if (format) { #ifdef FDC_YE if (fdc->flags & FDC_PCMCIA) (void)fdcpio(fdcu,bp->b_flags, bp->b_data+fd->skip, bp->b_bcount); #endif /* formatting */ - if(fd_cmd(fdcu, 6, - NE7CMD_FORMAT, - head << 2 | fdu, + if(fd_cmd(fdc, 6, NE7CMD_FORMAT, head << 2 | fdu, finfo->fd_formb_secshift, finfo->fd_formb_nsecs, finfo->fd_formb_gaplen, - finfo->fd_formb_fillbyte, - 0)) - { + finfo->fd_formb_fillbyte, 0)) { /* controller fell over */ isa_dmadone(bp->b_flags, bp->b_data + fd->skip, format ? bp->b_bcount : fdblk, fdc->dmachan); fdc->retry = 6; - return(retrier(fdcu)); + return (retrier(fdc)); } - } - else - { + } else { #ifdef FDC_YE if (fdc->flags & FDC_PCMCIA) { /* * this seems to be necessary even when * reading data */ SET_BCDR(1,fdblk,fdc->baseport); /* * perform the write pseudo-DMA before * the WRITE command is sent */ if (!read) (void)fdcpio(fdcu,bp->b_flags, bp->b_data+fd->skip, fdblk); } #endif - if (fd_cmd(fdcu, 9, + if (fd_cmd(fdc, 9, (read ? NE7CMD_READ : NE7CMD_WRITE), head << 2 | fdu, /* head & unit */ fd->track, /* track */ head, sec, /* sector + 1 */ fd->ft->secsize, /* sector size */ sectrac, /* sectors/track */ fd->ft->gap, /* gap size */ fd->ft->datalen, /* data length */ - 0)) - { + 0)) { /* the beast is sleeping again */ isa_dmadone(bp->b_flags, bp->b_data + fd->skip, format ? bp->b_bcount : fdblk, fdc->dmachan); fdc->retry = 6; - return(retrier(fdcu)); + return (retrier(fdc)); } } #ifdef FDC_YE if (fdc->flags & FDC_PCMCIA) /* * if this is a read, then simply await interrupt * before performing PIO */ if (read && !fdcpio(fdcu,bp->b_flags, bp->b_data+fd->skip,fdblk)) { fd->tohandle = timeout(fd_iotimeout, (caddr_t)fdcu, hz); return(0); /* will return later */ }; /* * write (or format) operation will fall through and * await completion interrupt */ #endif fdc->state = IOCOMPLETE; - fd->tohandle = timeout(fd_iotimeout, (caddr_t)fdcu, hz); - return(0); /* will return later */ + fd->tohandle = timeout(fd_iotimeout, fdc, hz); + return (0); /* will return later */ #ifdef FDC_YE case PIOREAD: /* * actually perform the PIO read. The IOCOMPLETE case * removes the timeout for us. */ (void)fdcpio(fdcu,bp->b_flags,bp->b_data+fd->skip,fdblk); fdc->state = IOCOMPLETE; /* FALLTHROUGH */ #endif case IOCOMPLETE: /* IO DONE, post-analyze */ - untimeout(fd_iotimeout, (caddr_t)fdcu, fd->tohandle); + untimeout(fd_iotimeout, fdc, fd->tohandle); - if (fd_read_status(fdc, fd->fdsu)) - { + if (fd_read_status(fdc, fd->fdsu)) { isa_dmadone(bp->b_flags, bp->b_data + fd->skip, format ? bp->b_bcount : fdblk, fdc->dmachan); if (fdc->retry < 6) fdc->retry = 6; /* force a reset */ - return retrier(fdcu); + return (retrier(fdc)); } fdc->state = IOTIMEDOUT; /* FALLTHROUGH */ case IOTIMEDOUT: #ifdef FDC_YE if (!(fdc->flags & FDC_PCMCIA)) #endif isa_dmadone(bp->b_flags, bp->b_data + fd->skip, format ? bp->b_bcount : fdblk, fdc->dmachan); - if (fdc->status[0] & NE7_ST0_IC) - { + if (fdc->status[0] & NE7_ST0_IC) { if ((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT && fdc->status[1] & NE7_ST1_OR) { /* * DMA overrun. Someone hogged the bus * and didn't release it in time for the * next FDC transfer. * Just restart it, don't increment retry * count. (vak) */ fdc->state = SEEKCOMPLETE; return (1); } else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_IV && fdc->retry < 6) fdc->retry = 6; /* force a reset */ else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT && fdc->status[2] & NE7_ST2_WC && fdc->retry < 3) fdc->retry = 3; /* force recalibrate */ - return(retrier(fdcu)); + return (retrier(fdc)); } /* All OK */ fd->skip += fdblk; - if (!format && fd->skip < bp->b_bcount - bp->b_resid) - { + if (!format && fd->skip < bp->b_bcount - bp->b_resid) { /* set up next transfer */ fdc->state = DOSEEK; - } - else - { + } else { /* ALL DONE */ fd->skip = 0; fdc->bp = NULL; /* Tell devstat we have finished with the transaction */ devstat_end_transaction(&fd->device_stats, bp->b_bcount - bp->b_resid, DEVSTAT_TAG_NONE, (bp->b_flags & B_READ) ? DEVSTAT_READ : DEVSTAT_WRITE); biodone(bp); fdc->fd = (fd_p) 0; fdc->fdu = -1; fdc->state = FINDWORK; } - return(1); + return (1); case RESETCTLR: fdc_reset(fdc); fdc->retry++; fdc->state = RESETCOMPLETE; return (0); case RESETCOMPLETE: /* * Discard all the results from the reset so that they * can't cause an unexpected interrupt later. */ for (i = 0; i < 4; i++) (void)fd_sense_int(fdc, &st0, &cyl); fdc->state = STARTRECAL; /* Fall through. */ case STARTRECAL: - if(fd_cmd(fdcu, - 2, NE7CMD_RECAL, fdu, - 0)) /* Recalibrate Function */ - { + if(fd_cmd(fdc, 2, NE7CMD_RECAL, fdu, 0)) { /* arrgl */ fdc->retry = 6; - return(retrier(fdcu)); + return (retrier(fdc)); } fdc->state = RECALWAIT; - return(0); /* will return later */ + return (0); /* will return later */ case RECALWAIT: /* allow heads to settle */ - timeout(fd_pseudointr, (caddr_t)fdcu, hz / 8); + timeout(fd_pseudointr, fdc, hz / 8); fdc->state = RECALCOMPLETE; - return(0); /* will return later */ + return (0); /* will return later */ case RECALCOMPLETE: do { /* * See SEEKCOMPLETE for a comment on this: */ if (fd_sense_int(fdc, &st0, &cyl) == FD_NOT_VALID) return 0; if(fdc->fdct == FDC_NE765 && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC) return 0; /* hope for a real intr */ } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC); if ((st0 & NE7_ST0_IC) != NE7_ST0_IC_NT || cyl != 0) { if(fdc->retry > 3) /* * a recalibrate from beyond cylinder 77 * will "fail" due to the FDC limitations; * since people used to complain much about * the failure message, try not logging * this one if it seems to be the first * time in a line */ printf("fd%d: recal failed ST0 %b cyl %d\n", fdu, st0, NE7_ST0BITS, cyl); if(fdc->retry < 3) fdc->retry = 3; - return(retrier(fdcu)); + return (retrier(fdc)); } fd->track = 0; /* Seek (probably) necessary */ fdc->state = DOSEEK; - return(1); /* will return immediatly */ + return (1); /* will return immediatly */ case MOTORWAIT: if(fd->flags & FD_MOTOR_WAIT) { - return(0); /* time's not up yet */ + return (0); /* time's not up yet */ } if (fdc->flags & FDC_NEEDS_RESET) { fdc->state = RESETCTLR; fdc->flags &= ~FDC_NEEDS_RESET; } else { /* * If all motors were off, then the controller was * reset, so it has lost track of the current * cylinder. Recalibrate to handle this case. */ fdc->state = STARTRECAL; } - return(1); /* will return immediatly */ + return (1); /* will return immediatly */ default: - printf("fdc%d: Unexpected FD int->", fdcu); + device_print_prettyname(fdc->fdc_dev); + printf("unexpected FD int->"); if (fd_read_status(fdc, fd->fdsu) == 0) printf("FDC status :%x %x %x %x %x %x %x ", fdc->status[0], fdc->status[1], fdc->status[2], fdc->status[3], fdc->status[4], fdc->status[5], fdc->status[6] ); else printf("No status available "); if (fd_sense_int(fdc, &st0, &cyl) != 0) { printf("[controller is dead now]\n"); - return(0); + return (0); } printf("ST0 = %x, PCN = %x\n", st0, cyl); - return(0); + return (0); } /*XXX confusing: some branches return immediately, others end up here*/ - return(1); /* Come back immediatly to new state */ + return (1); /* Come back immediatly to new state */ } static int -retrier(fdcu) - fdcu_t fdcu; +retrier(struct fdc_data *fdc) { - fdc_p fdc = fdc_data + fdcu; register struct buf *bp; + struct fd_data *fd; + int fdu; bp = fdc->bp; - if(fd_data[FDUNIT(minor(bp->b_dev))].options & FDOPT_NORETRY) + /* XXX shouldn't this be cached somewhere? */ + fdu = FDUNIT(minor(bp->b_dev)); + fd = devclass_get_softc(fd_devclass, fdu); + if (fd->options & FDOPT_NORETRY) goto fail; - switch(fdc->retry) - { + + switch (fdc->retry) { case 0: case 1: case 2: fdc->state = SEEKCOMPLETE; break; case 3: case 4: case 5: fdc->state = STARTRECAL; break; case 6: fdc->state = RESETCTLR; break; case 7: break; default: fail: { dev_t sav_b_dev = bp->b_dev; /* Trick diskerr */ bp->b_dev = makedev(major(bp->b_dev), (FDUNIT(minor(bp->b_dev))<<3)|RAW_PART); diskerr(bp, "fd", "hard error", LOG_PRINTF, fdc->fd->skip / DEV_BSIZE, (struct disklabel *)NULL); bp->b_dev = sav_b_dev; if (fdc->flags & FDC_STAT_VALID) { printf( " (ST0 %b ST1 %b ST2 %b cyl %u hd %u sec %u)\n", fdc->status[0], NE7_ST0BITS, fdc->status[1], NE7_ST1BITS, fdc->status[2], NE7_ST2BITS, fdc->status[3], fdc->status[4], fdc->status[5]); } else printf(" (No status)\n"); } bp->b_flags |= B_ERROR; bp->b_error = EIO; bp->b_resid += bp->b_bcount - fdc->fd->skip; fdc->bp = NULL; /* Tell devstat we have finished with the transaction */ devstat_end_transaction(&fdc->fd->device_stats, bp->b_bcount - bp->b_resid, DEVSTAT_TAG_NONE, (bp->b_flags & B_READ) ? DEVSTAT_READ : DEVSTAT_WRITE); fdc->fd->skip = 0; biodone(bp); fdc->state = FINDWORK; fdc->flags |= FDC_NEEDS_RESET; fdc->fd = (fd_p) 0; fdc->fdu = -1; - return(1); + return (1); } fdc->retry++; - return(1); + return (1); } static int fdformat(dev, finfo, p) dev_t dev; struct fd_formb *finfo; struct proc *p; { fdu_t fdu; fd_p fd; struct buf *bp; int rv = 0, s; size_t fdblk; fdu = FDUNIT(minor(dev)); - fd = &fd_data[fdu]; + fd = devclass_get_softc(fd_devclass, fdu); fdblk = 128 << fd->ft->secsize; /* set up a buffer header for fdstrategy() */ bp = (struct buf *)malloc(sizeof(struct buf), M_TEMP, M_NOWAIT); if(bp == 0) return ENOBUFS; /* * keep the process from being swapped */ PHOLD(p); bzero((void *)bp, sizeof(struct buf)); bp->b_flags = B_BUSY | B_PHYS | B_FORMAT; bp->b_proc = p; /* * calculate a fake blkno, so fdstrategy() would initiate a * seek to the requested cylinder */ bp->b_blkno = (finfo->cyl * (fd->ft->sectrac * fd->ft->heads) + finfo->head * fd->ft->sectrac) * fdblk / DEV_BSIZE; bp->b_bcount = sizeof(struct fd_idfield_data) * finfo->fd_formb_nsecs; bp->b_data = (caddr_t)finfo; /* now do the format */ bp->b_dev = dev; fdstrategy(bp); /* ...and wait for it to complete */ s = splbio(); - while(!(bp->b_flags & B_DONE)) - { + while(!(bp->b_flags & B_DONE)) { rv = tsleep((caddr_t)bp, PRIBIO, "fdform", 20 * hz); - if(rv == EWOULDBLOCK) + if (rv == EWOULDBLOCK) break; } splx(s); - if(rv == EWOULDBLOCK) { + if (rv == EWOULDBLOCK) { /* timed out */ rv = EIO; biodone(bp); } - if(bp->b_flags & B_ERROR) + if (bp->b_flags & B_ERROR) rv = bp->b_error; /* * allow the process to be swapped */ PRELE(p); free(bp, M_TEMP); return rv; } /* * TODO: don't allocate buffer on stack. */ static int fdioctl(dev, cmd, addr, flag, p) dev_t dev; u_long cmd; caddr_t addr; int flag; struct proc *p; { fdu_t fdu = FDUNIT(minor(dev)); - fd_p fd = &fd_data[fdu]; + fd_p fd = devclass_get_softc(fd_devclass, fdu); size_t fdblk; struct fd_type *fdt; struct disklabel *dl; char buffer[DEV_BSIZE]; int error = 0; fdblk = 128 << fd->ft->secsize; - switch (cmd) - { + switch (cmd) { case DIOCGDINFO: bzero(buffer, sizeof (buffer)); dl = (struct disklabel *)buffer; dl->d_secsize = fdblk; - fdt = fd_data[FDUNIT(minor(dev))].ft; + fdt = fd->ft; dl->d_secpercyl = fdt->size / fdt->tracks; dl->d_type = DTYPE_FLOPPY; if (readdisklabel(dkmodpart(dev, RAW_PART), fdstrategy, dl) == NULL) error = 0; else error = EINVAL; *(struct disklabel *)addr = *dl; break; case DIOCSDINFO: if ((flag & FWRITE) == 0) error = EBADF; break; case DIOCWLABEL: if ((flag & FWRITE) == 0) error = EBADF; break; case DIOCWDINFO: - if ((flag & FWRITE) == 0) - { + if ((flag & FWRITE) == 0) { error = EBADF; break; } dl = (struct disklabel *)addr; if ((error = setdisklabel((struct disklabel *)buffer, dl, (u_long)0)) != 0) break; error = writedisklabel(dev, fdstrategy, (struct disklabel *)buffer); break; case FD_FORM: - if((flag & FWRITE) == 0) + if ((flag & FWRITE) == 0) error = EBADF; /* must be opened for writing */ - else if(((struct fd_formb *)addr)->format_version != + else if (((struct fd_formb *)addr)->format_version != FD_FORMAT_VERSION) error = EINVAL; /* wrong version of formatting prog */ else error = fdformat(dev, (struct fd_formb *)addr, p); break; case FD_GTYPE: /* get drive type */ *(struct fd_type *)addr = *fd->ft; break; case FD_STYPE: /* set drive type */ /* this is considered harmful; only allow for superuser */ - if(suser(p->p_ucred, &p->p_acflag) != 0) + if (suser(p->p_ucred, &p->p_acflag) != 0) return EPERM; *fd->ft = *(struct fd_type *)addr; break; case FD_GOPTS: /* get drive options */ *(int *)addr = fd->options; break; case FD_SOPTS: /* set drive options */ fd->options = *(int *)addr; break; default: error = ENOTTY; break; } return (error); } +static device_method_t fdc_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, fdc_probe), + DEVMETHOD(device_attach, fdc_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), -static fd_devsw_installed = 0; + /* Bus interface */ + DEVMETHOD(bus_print_child, fdc_print_child), + /* Our children never use any other bus interface methods. */ -static void fd_drvinit(void *notused ) -{ + { 0, 0 } +}; - if( ! fd_devsw_installed ) { - cdevsw_add_generic(BDEV_MAJOR,CDEV_MAJOR, &fd_cdevsw); - fd_devsw_installed = 1; - } -} +static driver_t fdc_driver = { + "fdc", + fdc_methods, + DRIVER_TYPE_BIO, + sizeof(struct fdc_data) +}; -SYSINIT(fddev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,fd_drvinit,NULL) +DRIVER_MODULE(fdc, isa, fdc_driver, fdc_devclass, 0, 0); +static device_method_t fd_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, fd_probe), + DEVMETHOD(device_attach, fd_attach), + DEVMETHOD(device_detach, bus_generic_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + DEVMETHOD(device_suspend, bus_generic_suspend), /* XXX */ + DEVMETHOD(device_resume, bus_generic_resume), /* XXX */ -#endif + { 0, 0 } +}; + +static driver_t fd_driver = { + "fd", + fd_methods, + DRIVER_TYPE_BIO, + sizeof(struct fd_data) +}; + +static struct cdevsw fd_cdevsw = { + Fdopen, fdclose, fdread, fdwrite, + fdioctl, nostop, nullreset, nodevtotty, + seltrue, nommap, fdstrategy, "fd", + NULL, -1, nodump, nopsize, + D_DISK, 0, -1 +}; + +BDEV_DRIVER_MODULE(fd, fdc, fd_driver, fd_devclass, BDEV_MAJOR, CDEV_MAJOR, + fd_cdevsw, 0, 0); + +#endif /* NFDC > 0 */ /* * Hello emacs, these are the * Local Variables: * c-indent-level: 8 * c-continued-statement-offset: 8 * c-continued-brace-offset: 0 * c-brace-offset: -8 * c-brace-imaginary-offset: 0 * c-argdecl-indent: 8 * c-label-offset: -8 * c++-hanging-braces: 1 * c++-access-specifier-offset: -8 * c++-empty-arglist-indent: 8 * c++-friend-offset: 0 * End: */ Index: head/sys/dev/fxp/if_fxp.c =================================================================== --- head/sys/dev/fxp/if_fxp.c (revision 45719) +++ head/sys/dev/fxp/if_fxp.c (revision 45720) @@ -1,1925 +1,1995 @@ /* * Copyright (c) 1995, David Greenman * All rights reserved. * * Modifications to support NetBSD and media selection: * Copyright (c) 1997 Jason R. Thorpe. 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 unmodified, 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. * - * $Id: if_fxp.c,v 1.65 1999/03/17 16:44:53 luigi Exp $ + * $Id: if_fxp.c,v 1.66 1999/03/20 04:51:25 wes Exp $ */ /* * Intel EtherExpress Pro/100B PCI Fast Ethernet driver */ #include "bpfilter.h" #include #include #include #include #include #include #include #include #include #ifdef NS #include #include #endif #if NBPFILTER > 0 #include #endif #if defined(__NetBSD__) #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __alpha__ /* XXX */ /* XXX XXX NEED REAL DMA MAPPING SUPPORT XXX XXX */ #undef vtophys #define vtophys(va) alpha_XXX_dmamap((vm_offset_t)(va)) #endif /* __alpha__ */ #else /* __FreeBSD__ */ #include +#include +#include +#include +#include #include #include #include /* for vtophys */ #include /* for vtophys */ #include /* for DELAY */ #include #include /* for PCIM_CMD_xxx */ #include #include #endif /* __NetBSD__ */ #include "opt_bdg.h" #ifdef BRIDGE #include #include #endif /* * NOTE! On the Alpha, we have an alignment constraint. The * card DMAs the packet immediately following the RFA. However, * the first thing in the packet is a 14-byte Ethernet header. * This means that the packet is misaligned. To compensate, * we actually offset the RFA 2 bytes into the cluster. This * alignes the packet after the Ethernet header at a 32-bit * boundary. HOWEVER! This means that the RFA is misaligned! */ #define RFA_ALIGNMENT_FUDGE 2 /* * Inline function to copy a 16-bit aligned 32-bit quantity. */ static __inline void fxp_lwcopy __P((volatile u_int32_t *, volatile u_int32_t *)); static __inline void fxp_lwcopy(src, dst) volatile u_int32_t *src, *dst; { volatile u_int16_t *a = (volatile u_int16_t *)src; volatile u_int16_t *b = (volatile u_int16_t *)dst; b[0] = a[0]; b[1] = a[1]; } /* * Template for default configuration parameters. * See struct fxp_cb_config for the bit definitions. */ static u_char fxp_cb_config_template[] = { 0x0, 0x0, /* cb_status */ 0x80, 0x2, /* cb_command */ 0xff, 0xff, 0xff, 0xff, /* link_addr */ 0x16, /* 0 */ 0x8, /* 1 */ 0x0, /* 2 */ 0x0, /* 3 */ 0x0, /* 4 */ 0x80, /* 5 */ 0xb2, /* 6 */ 0x3, /* 7 */ 0x1, /* 8 */ 0x0, /* 9 */ 0x26, /* 10 */ 0x0, /* 11 */ 0x60, /* 12 */ 0x0, /* 13 */ 0xf2, /* 14 */ 0x48, /* 15 */ 0x0, /* 16 */ 0x40, /* 17 */ 0xf3, /* 18 */ 0x0, /* 19 */ 0x3f, /* 20 */ 0x5 /* 21 */ }; /* Supported media types. */ struct fxp_supported_media { const int fsm_phy; /* PHY type */ const int *fsm_media; /* the media array */ const int fsm_nmedia; /* the number of supported media */ const int fsm_defmedia; /* default media for this PHY */ }; static const int fxp_media_standard[] = { IFM_ETHER|IFM_10_T, IFM_ETHER|IFM_10_T|IFM_FDX, IFM_ETHER|IFM_100_TX, IFM_ETHER|IFM_100_TX|IFM_FDX, IFM_ETHER|IFM_AUTO, }; #define FXP_MEDIA_STANDARD_DEFMEDIA (IFM_ETHER|IFM_AUTO) static const int fxp_media_default[] = { IFM_ETHER|IFM_MANUAL, /* XXX IFM_AUTO ? */ }; #define FXP_MEDIA_DEFAULT_DEFMEDIA (IFM_ETHER|IFM_MANUAL) static const struct fxp_supported_media fxp_media[] = { { FXP_PHY_DP83840, fxp_media_standard, sizeof(fxp_media_standard) / sizeof(fxp_media_standard[0]), FXP_MEDIA_STANDARD_DEFMEDIA }, { FXP_PHY_DP83840A, fxp_media_standard, sizeof(fxp_media_standard) / sizeof(fxp_media_standard[0]), FXP_MEDIA_STANDARD_DEFMEDIA }, { FXP_PHY_82553A, fxp_media_standard, sizeof(fxp_media_standard) / sizeof(fxp_media_standard[0]), FXP_MEDIA_STANDARD_DEFMEDIA }, { FXP_PHY_82553C, fxp_media_standard, sizeof(fxp_media_standard) / sizeof(fxp_media_standard[0]), FXP_MEDIA_STANDARD_DEFMEDIA }, { FXP_PHY_82555, fxp_media_standard, sizeof(fxp_media_standard) / sizeof(fxp_media_standard[0]), FXP_MEDIA_STANDARD_DEFMEDIA }, { FXP_PHY_82555B, fxp_media_standard, sizeof(fxp_media_standard) / sizeof(fxp_media_standard[0]), FXP_MEDIA_STANDARD_DEFMEDIA }, { FXP_PHY_80C24, fxp_media_default, sizeof(fxp_media_default) / sizeof(fxp_media_default[0]), FXP_MEDIA_DEFAULT_DEFMEDIA }, }; #define NFXPMEDIA (sizeof(fxp_media) / sizeof(fxp_media[0])) static int fxp_mediachange __P((struct ifnet *)); static void fxp_mediastatus __P((struct ifnet *, struct ifmediareq *)); static void fxp_set_media __P((struct fxp_softc *, int)); static __inline void fxp_scb_wait __P((struct fxp_softc *)); static FXP_INTR_TYPE fxp_intr __P((void *)); static void fxp_start __P((struct ifnet *)); static int fxp_ioctl __P((struct ifnet *, FXP_IOCTLCMD_TYPE, caddr_t)); static void fxp_init __P((void *)); static void fxp_stop __P((struct fxp_softc *)); static void fxp_watchdog __P((struct ifnet *)); static int fxp_add_rfabuf __P((struct fxp_softc *, struct mbuf *)); static int fxp_mdi_read __P((struct fxp_softc *, int, int)); static void fxp_mdi_write __P((struct fxp_softc *, int, int, int)); static void fxp_read_eeprom __P((struct fxp_softc *, u_int16_t *, int, int)); static int fxp_attach_common __P((struct fxp_softc *, u_int8_t *)); static void fxp_stats_update __P((void *)); static void fxp_mc_setup __P((struct fxp_softc *)); /* * Set initial transmit threshold at 64 (512 bytes). This is * increased by 64 (512 bytes) at a time, to maximum of 192 * (1536 bytes), if an underrun occurs. */ static int tx_threshold = 64; /* * Number of transmit control blocks. This determines the number * of transmit buffers that can be chained in the CB list. * This must be a power of two. */ #define FXP_NTXCB 128 /* * Number of completed TX commands at which point an interrupt * will be generated to garbage collect the attached buffers. * Must be at least one less than FXP_NTXCB, and should be * enough less so that the transmitter doesn't becomes idle * during the buffer rundown (which would reduce performance). */ #define FXP_CXINT_THRESH 120 /* * TxCB list index mask. This is used to do list wrap-around. */ #define FXP_TXCB_MASK (FXP_NTXCB - 1) /* * Number of receive frame area buffers. These are large so chose * wisely. */ #define FXP_NRFABUFS 64 /* * Maximum number of seconds that the receiver can be idle before we * assume it's dead and attempt to reset it by reprogramming the * multicast filter. This is part of a work-around for a bug in the * NIC. See fxp_stats_update(). */ #define FXP_MAX_RX_IDLE 15 /* * Wait for the previous command to be accepted (but not necessarily * completed). */ static __inline void fxp_scb_wait(sc) struct fxp_softc *sc; { int i = 10000; while (CSR_READ_1(sc, FXP_CSR_SCB_COMMAND) && --i); } /************************************************************* * Operating system-specific autoconfiguration glue *************************************************************/ #if defined(__NetBSD__) #ifdef __BROKEN_INDIRECT_CONFIG static int fxp_match __P((struct device *, void *, void *)); #else static int fxp_match __P((struct device *, struct cfdata *, void *)); #endif static void fxp_attach __P((struct device *, struct device *, void *)); static void fxp_shutdown __P((void *)); /* Compensate for lack of a generic ether_ioctl() */ static int fxp_ether_ioctl __P((struct ifnet *, FXP_IOCTLCMD_TYPE, caddr_t)); #define ether_ioctl fxp_ether_ioctl struct cfattach fxp_ca = { sizeof(struct fxp_softc), fxp_match, fxp_attach }; struct cfdriver fxp_cd = { NULL, "fxp", DV_IFNET }; /* * Check if a device is an 82557. */ static int fxp_match(parent, match, aux) struct device *parent; #ifdef __BROKEN_INDIRECT_CONFIG void *match; #else struct cfdata *match; #endif void *aux; { struct pci_attach_args *pa = aux; if (PCI_VENDOR(pa->pa_id) != PCI_VENDOR_INTEL) return (0); switch (PCI_PRODUCT(pa->pa_id)) { case PCI_PRODUCT_INTEL_82557: return (1); } return (0); } static void fxp_attach(parent, self, aux) struct device *parent, *self; void *aux; { struct fxp_softc *sc = (struct fxp_softc *)self; struct pci_attach_args *pa = aux; pci_chipset_tag_t pc = pa->pa_pc; pci_intr_handle_t ih; const char *intrstr = NULL; u_int8_t enaddr[6]; struct ifnet *ifp; /* * Map control/status registers. */ if (pci_mapreg_map(pa, FXP_PCI_MMBA, PCI_MAPREG_TYPE_MEM, 0, &sc->sc_st, &sc->sc_sh, NULL, NULL)) { printf(": can't map registers\n"); return; } printf(": Intel EtherExpress Pro 10/100B Ethernet\n"); /* * Allocate our interrupt. */ if (pci_intr_map(pc, pa->pa_intrtag, pa->pa_intrpin, pa->pa_intrline, &ih)) { printf("%s: couldn't map interrupt\n", sc->sc_dev.dv_xname); return; } intrstr = pci_intr_string(pc, ih); sc->sc_ih = pci_intr_establish(pc, ih, IPL_NET, fxp_intr, sc); if (sc->sc_ih == NULL) { printf("%s: couldn't establish interrupt", sc->sc_dev.dv_xname); if (intrstr != NULL) printf(" at %s", intrstr); printf("\n"); return; } printf("%s: interrupting at %s\n", sc->sc_dev.dv_xname, intrstr); /* Do generic parts of attach. */ if (fxp_attach_common(sc, enaddr)) { /* Failed! */ return; } printf("%s: Ethernet address %s%s\n", sc->sc_dev.dv_xname, ether_sprintf(enaddr), sc->phy_10Mbps_only ? ", 10Mbps" : ""); ifp = &sc->sc_ethercom.ec_if; bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ); ifp->if_softc = sc; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = fxp_ioctl; ifp->if_start = fxp_start; ifp->if_watchdog = fxp_watchdog; /* * Attach the interface. */ if_attach(ifp); /* * Let the system queue as many packets as we have available * TX descriptors. */ ifp->if_snd.ifq_maxlen = FXP_NTXCB - 1; ether_ifattach(ifp, enaddr); #if NBPFILTER > 0 bpfattach(&sc->sc_ethercom.ec_if.if_bpf, ifp, DLT_EN10MB, sizeof(struct ether_header)); #endif /* * Add shutdown hook so that DMA is disabled prior to reboot. Not * doing do could allow DMA to corrupt kernel memory during the * reboot before the driver initializes. */ shutdownhook_establish(fxp_shutdown, sc); } /* * Device shutdown routine. Called at system shutdown after sync. The * main purpose of this routine is to shut off receiver DMA so that * kernel memory doesn't get clobbered during warmboot. */ static void fxp_shutdown(sc) void *sc; { fxp_stop((struct fxp_softc *) sc); } static int fxp_ether_ioctl(ifp, cmd, data) struct ifnet *ifp; FXP_IOCTLCMD_TYPE cmd; caddr_t data; { struct ifaddr *ifa = (struct ifaddr *) data; struct fxp_softc *sc = ifp->if_softc; switch (cmd) { case SIOCSIFADDR: ifp->if_flags |= IFF_UP; switch (ifa->ifa_addr->sa_family) { #ifdef INET case AF_INET: fxp_init(sc); arp_ifinit(ifp, ifa); break; #endif #ifdef NS case AF_NS: { register struct ns_addr *ina = &IA_SNS(ifa)->sns_addr; if (ns_nullhost(*ina)) ina->x_host = *(union ns_host *) LLADDR(ifp->if_sadl); else bcopy(ina->x_host.c_host, LLADDR(ifp->if_sadl), ifp->if_addrlen); /* Set new address. */ fxp_init(sc); break; } #endif default: fxp_init(sc); break; } break; default: return (EINVAL); } return (0); } #else /* __FreeBSD__ */ -static u_long fxp_count; -static const char *fxp_probe __P((pcici_t, pcidi_t)); -static void fxp_attach __P((pcici_t, int)); - -static void fxp_shutdown __P((int, void *)); - -static struct pci_device fxp_device = { - "fxp", - fxp_probe, - fxp_attach, - &fxp_count, - NULL -}; -DATA_SET(pcidevice_set, fxp_device); - /* * Return identification string if this is device is ours. */ -static const char * -fxp_probe(config_id, device_id) - pcici_t config_id; - pcidi_t device_id; +static int +fxp_probe(device_t dev) { - if (((device_id & 0xffff) == FXP_VENDORID_INTEL) && - ((device_id >> 16) & 0xffff) == FXP_DEVICEID_i82557) - return ("Intel EtherExpress Pro 10/100B Ethernet"); + if ((pci_get_vendor(dev) == FXP_VENDORID_INTEL) && + (pci_get_device(dev) == FXP_DEVICEID_i82557)) { + device_set_desc(dev, "Intel EtherExpress Pro 10/100B Ethernet"); + return 0; + } - return NULL; + return ENXIO; } -static void -fxp_attach(config_id, unit) - pcici_t config_id; - int unit; +static int +fxp_attach(device_t dev) { - struct fxp_softc *sc; - vm_offset_t pbase; + int error = 0; + struct fxp_softc *sc = device_get_softc(dev); struct ifnet *ifp; int s; u_long val; + int rid; - sc = malloc(sizeof(struct fxp_softc), M_DEVBUF, M_NOWAIT); - if (sc == NULL) - return; - bzero(sc, sizeof(struct fxp_softc)); callout_handle_init(&sc->stat_ch); s = splimp(); /* * Enable bus mastering. */ - val = pci_conf_read(config_id, PCI_COMMAND_STATUS_REG); + val = pci_read_config(dev, PCIR_COMMAND, 2); val |= (PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); - pci_conf_write(config_id, PCI_COMMAND_STATUS_REG, val); + pci_write_config(dev, PCIR_COMMAND, val, 2); /* * Map control/status registers. */ - if (!pci_map_mem(config_id, FXP_PCI_MMBA, - (vm_offset_t *)&sc->csr, &pbase)) { - printf("fxp%d: couldn't map memory\n", unit); + rid = FXP_PCI_MMBA; + sc->mem = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, + 0, ~0, 1, RF_ACTIVE); + if (!sc->mem) { + device_printf(dev, "could not map memory\n"); + error = ENXIO; goto fail; - } + } + sc->csr = rman_get_virtual(sc->mem); /* XXX use bus_space */ /* * Allocate our interrupt. */ - if (!pci_map_int(config_id, fxp_intr, sc, &net_imask)) { - printf("fxp%d: couldn't map interrupt\n", unit); + rid = 0; + sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, + RF_SHAREABLE | RF_ACTIVE); + if (sc->irq == NULL) { + device_printf(dev, "could not map interrupt\n"); + error = ENXIO; goto fail; } + error = bus_setup_intr(dev, sc->irq, fxp_intr, sc, &sc->ih); + if (error) { + device_printf(dev, "could not setup irq\n"); + goto fail; + } + /* Do generic parts of attach. */ if (fxp_attach_common(sc, sc->arpcom.ac_enaddr)) { /* Failed! */ - (void) pci_unmap_int(config_id); + bus_teardown_intr(dev, sc->irq, sc->ih); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq); + bus_release_resource(dev, SYS_RES_MEMORY, FXP_PCI_MMBA, sc->mem); + error = ENXIO; goto fail; } - printf("fxp%d: Ethernet address %6D%s\n", unit, + device_printf(dev, "Ethernet address %6D%s\n", sc->arpcom.ac_enaddr, ":", sc->phy_10Mbps_only ? ", 10Mbps" : ""); ifp = &sc->arpcom.ac_if; - ifp->if_unit = unit; + ifp->if_unit = device_get_unit(dev); ifp->if_name = "fxp"; ifp->if_output = ether_output; ifp->if_baudrate = 100000000; ifp->if_init = fxp_init; ifp->if_softc = sc; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = fxp_ioctl; ifp->if_start = fxp_start; ifp->if_watchdog = fxp_watchdog; /* * Attach the interface. */ if_attach(ifp); /* * Let the system queue as many packets as we have available * TX descriptors. */ ifp->if_snd.ifq_maxlen = FXP_NTXCB - 1; ether_ifattach(ifp); #if NBPFILTER > 0 bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); #endif + splx(s); + return 0; + + fail: + splx(s); + return error; +} + +/* + * Detach interface. + */ +static int +fxp_detach(device_t dev) +{ + struct fxp_softc *sc = device_get_softc(dev); + int s; + + s = splimp(); + /* - * Add shutdown hook so that DMA is disabled prior to reboot. Not - * doing do could allow DMA to corrupt kernel memory during the - * reboot before the driver initializes. + * Close down routes etc. */ - at_shutdown(fxp_shutdown, sc, SHUTDOWN_POST_SYNC); + if_detach(&sc->arpcom.ac_if); - splx(s); - return; + /* + * Stop DMA and drop transmit queue. + */ + fxp_stop(sc); - fail: - free(sc, M_DEVBUF); + /* + * Deallocate resources. + */ + bus_teardown_intr(dev, sc->irq, sc->ih); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq); + bus_release_resource(dev, SYS_RES_MEMORY, FXP_PCI_MMBA, sc->mem); + + /* + * Free all the receive buffers. + */ + if (sc->rfa_headm != NULL) + m_freem(sc->rfa_headm); + + /* + * Free all media structures. + */ + ifmedia_removeall(&sc->sc_media); + + /* + * Free anciliary structures. + */ + free(sc->cbl_base, M_DEVBUF); + free(sc->fxp_stats, M_DEVBUF); + free(sc->mcsp, M_DEVBUF); + splx(s); + + return 0; } /* * Device shutdown routine. Called at system shutdown after sync. The * main purpose of this routine is to shut off receiver DMA so that * kernel memory doesn't get clobbered during warmboot. */ -static void -fxp_shutdown(howto, sc) - int howto; - void *sc; +static int +fxp_shutdown(device_t dev) { - fxp_stop((struct fxp_softc *) sc); + /* + * Make sure that DMA is disabled prior to reboot. Not doing + * do could allow DMA to corrupt kernel memory during the + * reboot before the driver initializes. + */ + fxp_stop((struct fxp_softc *) device_get_softc(dev)); + return 0; } + +static device_method_t fxp_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, fxp_probe), + DEVMETHOD(device_attach, fxp_attach), + DEVMETHOD(device_detach, fxp_detach), + DEVMETHOD(device_shutdown, fxp_shutdown), + + { 0, 0 } +}; + +static driver_t fxp_driver = { + "fxp", + fxp_methods, + DRIVER_TYPE_NET, + sizeof(struct fxp_softc), +}; + +static devclass_t fxp_devclass; + +DRIVER_MODULE(fxp, pci, fxp_driver, fxp_devclass, 0, 0); #endif /* __NetBSD__ */ /************************************************************* * End of operating system-specific autoconfiguration glue *************************************************************/ /* * Do generic parts of attach. */ static int fxp_attach_common(sc, enaddr) struct fxp_softc *sc; u_int8_t *enaddr; { u_int16_t data; int i, nmedia, defmedia; const int *media; /* * Reset to a stable state. */ CSR_WRITE_4(sc, FXP_CSR_PORT, FXP_PORT_SELECTIVE_RESET); DELAY(10); sc->cbl_base = malloc(sizeof(struct fxp_cb_tx) * FXP_NTXCB, M_DEVBUF, M_NOWAIT); if (sc->cbl_base == NULL) goto fail; bzero(sc->cbl_base, sizeof(struct fxp_cb_tx) * FXP_NTXCB); sc->fxp_stats = malloc(sizeof(struct fxp_stats), M_DEVBUF, M_NOWAIT); if (sc->fxp_stats == NULL) goto fail; bzero(sc->fxp_stats, sizeof(struct fxp_stats)); sc->mcsp = malloc(sizeof(struct fxp_cb_mcs), M_DEVBUF, M_NOWAIT); if (sc->mcsp == NULL) goto fail; /* * Pre-allocate our receive buffers. */ for (i = 0; i < FXP_NRFABUFS; i++) { if (fxp_add_rfabuf(sc, NULL) != 0) { goto fail; } } /* * Get info about the primary PHY */ fxp_read_eeprom(sc, (u_int16_t *)&data, 6, 1); sc->phy_primary_addr = data & 0xff; sc->phy_primary_device = (data >> 8) & 0x3f; sc->phy_10Mbps_only = data >> 15; /* * Read MAC address. */ fxp_read_eeprom(sc, (u_int16_t *)enaddr, 0, 3); /* * Initialize the media structures. */ media = fxp_media_default; nmedia = sizeof(fxp_media_default) / sizeof(fxp_media_default[0]); defmedia = FXP_MEDIA_DEFAULT_DEFMEDIA; for (i = 0; i < NFXPMEDIA; i++) { if (sc->phy_primary_device == fxp_media[i].fsm_phy) { media = fxp_media[i].fsm_media; nmedia = fxp_media[i].fsm_nmedia; defmedia = fxp_media[i].fsm_defmedia; } } ifmedia_init(&sc->sc_media, 0, fxp_mediachange, fxp_mediastatus); for (i = 0; i < nmedia; i++) { if (IFM_SUBTYPE(media[i]) == IFM_100_TX && sc->phy_10Mbps_only) continue; ifmedia_add(&sc->sc_media, media[i], 0, NULL); } ifmedia_set(&sc->sc_media, defmedia); return (0); fail: printf(FXP_FORMAT ": Failed to malloc memory\n", FXP_ARGS(sc)); if (sc->cbl_base) free(sc->cbl_base, M_DEVBUF); if (sc->fxp_stats) free(sc->fxp_stats, M_DEVBUF); if (sc->mcsp) free(sc->mcsp, M_DEVBUF); /* frees entire chain */ if (sc->rfa_headm) m_freem(sc->rfa_headm); return (ENOMEM); } /* * Read from the serial EEPROM. Basically, you manually shift in * the read opcode (one bit at a time) and then shift in the address, * and then you shift out the data (all of this one bit at a time). * The word size is 16 bits, so you have to provide the address for * every 16 bits of data. */ static void fxp_read_eeprom(sc, data, offset, words) struct fxp_softc *sc; u_short *data; int offset; int words; { u_int16_t reg; int i, x; for (i = 0; i < words; i++) { CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, FXP_EEPROM_EECS); /* * Shift in read opcode. */ for (x = 3; x > 0; x--) { if (FXP_EEPROM_OPC_READ & (1 << (x - 1))) { reg = FXP_EEPROM_EECS | FXP_EEPROM_EEDI; } else { reg = FXP_EEPROM_EECS; } CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg); CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg | FXP_EEPROM_EESK); DELAY(1); CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg); DELAY(1); } /* * Shift in address. */ for (x = 6; x > 0; x--) { if ((i + offset) & (1 << (x - 1))) { reg = FXP_EEPROM_EECS | FXP_EEPROM_EEDI; } else { reg = FXP_EEPROM_EECS; } CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg); CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg | FXP_EEPROM_EESK); DELAY(1); CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg); DELAY(1); } reg = FXP_EEPROM_EECS; data[i] = 0; /* * Shift out data. */ for (x = 16; x > 0; x--) { CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg | FXP_EEPROM_EESK); DELAY(1); if (CSR_READ_2(sc, FXP_CSR_EEPROMCONTROL) & FXP_EEPROM_EEDO) data[i] |= (1 << (x - 1)); CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg); DELAY(1); } CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, 0); DELAY(1); } } /* * Start packet transmission on the interface. */ static void fxp_start(ifp) struct ifnet *ifp; { struct fxp_softc *sc = ifp->if_softc; struct fxp_cb_tx *txp; /* * See if we need to suspend xmit until the multicast filter * has been reprogrammed (which can only be done at the head * of the command chain). */ if (sc->need_mcsetup) return; txp = NULL; /* * We're finished if there is nothing more to add to the list or if * we're all filled up with buffers to transmit. * NOTE: One TxCB is reserved to guarantee that fxp_mc_setup() can add * a NOP command when needed. */ while (ifp->if_snd.ifq_head != NULL && sc->tx_queued < FXP_NTXCB - 1) { struct mbuf *m, *mb_head; int segment; /* * Grab a packet to transmit. */ IF_DEQUEUE(&ifp->if_snd, mb_head); /* * Get pointer to next available tx desc. */ txp = sc->cbl_last->next; /* * Go through each of the mbufs in the chain and initialize * the transmit buffer descriptors with the physical address * and size of the mbuf. */ tbdinit: for (m = mb_head, segment = 0; m != NULL; m = m->m_next) { if (m->m_len != 0) { if (segment == FXP_NTXSEG) break; txp->tbd[segment].tb_addr = vtophys(mtod(m, vm_offset_t)); txp->tbd[segment].tb_size = m->m_len; segment++; } } if (m != NULL) { struct mbuf *mn; /* * We ran out of segments. We have to recopy this mbuf * chain first. Bail out if we can't get the new buffers. */ MGETHDR(mn, M_DONTWAIT, MT_DATA); if (mn == NULL) { m_freem(mb_head); break; } if (mb_head->m_pkthdr.len > MHLEN) { MCLGET(mn, M_DONTWAIT); if ((mn->m_flags & M_EXT) == 0) { m_freem(mn); m_freem(mb_head); break; } } m_copydata(mb_head, 0, mb_head->m_pkthdr.len, mtod(mn, caddr_t)); mn->m_pkthdr.len = mn->m_len = mb_head->m_pkthdr.len; m_freem(mb_head); mb_head = mn; goto tbdinit; } txp->tbd_number = segment; txp->mb_head = mb_head; txp->cb_status = 0; if (sc->tx_queued != FXP_CXINT_THRESH - 1) { txp->cb_command = FXP_CB_COMMAND_XMIT | FXP_CB_COMMAND_SF | FXP_CB_COMMAND_S; } else { txp->cb_command = FXP_CB_COMMAND_XMIT | FXP_CB_COMMAND_SF | FXP_CB_COMMAND_S | FXP_CB_COMMAND_I; /* * Set a 5 second timer just in case we don't hear from the * card again. */ ifp->if_timer = 5; } txp->tx_threshold = tx_threshold; /* * Advance the end of list forward. */ sc->cbl_last->cb_command &= ~FXP_CB_COMMAND_S; sc->cbl_last = txp; /* * Advance the beginning of the list forward if there are * no other packets queued (when nothing is queued, cbl_first * sits on the last TxCB that was sent out). */ if (sc->tx_queued == 0) sc->cbl_first = txp; sc->tx_queued++; #if NBPFILTER > 0 /* * Pass packet to bpf if there is a listener. */ if (ifp->if_bpf) bpf_mtap(FXP_BPFTAP_ARG(ifp), mb_head); #endif } /* * We're finished. If we added to the list, issue a RESUME to get DMA * going again if suspended. */ if (txp != NULL) { fxp_scb_wait(sc); CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_RESUME); } } /* * Process interface interrupts. */ static FXP_INTR_TYPE fxp_intr(arg) void *arg; { struct fxp_softc *sc = arg; struct ifnet *ifp = &sc->sc_if; u_int8_t statack; #if defined(__NetBSD__) int claimed = 0; #endif while ((statack = CSR_READ_1(sc, FXP_CSR_SCB_STATACK)) != 0) { #if defined(__NetBSD__) claimed = 1; #endif /* * First ACK all the interrupts in this pass. */ CSR_WRITE_1(sc, FXP_CSR_SCB_STATACK, statack); /* * Free any finished transmit mbuf chains. */ if (statack & FXP_SCB_STATACK_CXTNO) { struct fxp_cb_tx *txp; for (txp = sc->cbl_first; sc->tx_queued && (txp->cb_status & FXP_CB_STATUS_C) != 0; txp = txp->next) { if (txp->mb_head != NULL) { m_freem(txp->mb_head); txp->mb_head = NULL; } sc->tx_queued--; } sc->cbl_first = txp; ifp->if_timer = 0; if (sc->tx_queued == 0) { if (sc->need_mcsetup) fxp_mc_setup(sc); } /* * Try to start more packets transmitting. */ if (ifp->if_snd.ifq_head != NULL) fxp_start(ifp); } /* * Process receiver interrupts. If a no-resource (RNR) * condition exists, get whatever packets we can and * re-start the receiver. */ if (statack & (FXP_SCB_STATACK_FR | FXP_SCB_STATACK_RNR)) { struct mbuf *m; struct fxp_rfa *rfa; rcvloop: m = sc->rfa_headm; rfa = (struct fxp_rfa *)(m->m_ext.ext_buf + RFA_ALIGNMENT_FUDGE); if (rfa->rfa_status & FXP_RFA_STATUS_C) { /* * Remove first packet from the chain. */ sc->rfa_headm = m->m_next; m->m_next = NULL; /* * Add a new buffer to the receive chain. * If this fails, the old buffer is recycled * instead. */ if (fxp_add_rfabuf(sc, m) == 0) { struct ether_header *eh; u_int16_t total_len; total_len = rfa->actual_size & (MCLBYTES - 1); if (total_len < sizeof(struct ether_header)) { m_freem(m); goto rcvloop; } m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = m->m_len = total_len ; eh = mtod(m, struct ether_header *); #if NBPFILTER > 0 if (ifp->if_bpf) bpf_tap(FXP_BPFTAP_ARG(ifp), mtod(m, caddr_t), total_len); #endif /* NBPFILTER > 0 */ #ifdef BRIDGE if (do_bridge) { struct ifnet *bdg_ifp ; bdg_ifp = bridge_in(m); if (bdg_ifp == BDG_DROP) goto dropit ; if (bdg_ifp != BDG_LOCAL) bdg_forward(&m, bdg_ifp); if (bdg_ifp != BDG_LOCAL && bdg_ifp != BDG_BCAST && bdg_ifp != BDG_MCAST) goto dropit ; goto getit ; } #endif /* * Only pass this packet up * if it is for us. */ if ((ifp->if_flags & IFF_PROMISC) && (rfa->rfa_status & FXP_RFA_STATUS_IAMATCH) && (eh->ether_dhost[0] & 1) == 0) { dropit: if (m) m_freem(m); goto rcvloop; } getit: m->m_data += sizeof(struct ether_header); m->m_len -= sizeof(struct ether_header); m->m_pkthdr.len = m->m_len ; ether_input(ifp, eh, m); } goto rcvloop; } if (statack & FXP_SCB_STATACK_RNR) { fxp_scb_wait(sc); CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, vtophys(sc->rfa_headm->m_ext.ext_buf) + RFA_ALIGNMENT_FUDGE); CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_RU_START); } } } #if defined(__NetBSD__) return (claimed); #endif } /* * Update packet in/out/collision statistics. The i82557 doesn't * allow you to access these counters without doing a fairly * expensive DMA to get _all_ of the statistics it maintains, so * we do this operation here only once per second. The statistics * counters in the kernel are updated from the previous dump-stats * DMA and then a new dump-stats DMA is started. The on-chip * counters are zeroed when the DMA completes. If we can't start * the DMA immediately, we don't wait - we just prepare to read * them again next time. */ static void fxp_stats_update(arg) void *arg; { struct fxp_softc *sc = arg; struct ifnet *ifp = &sc->sc_if; struct fxp_stats *sp = sc->fxp_stats; struct fxp_cb_tx *txp; int s; ifp->if_opackets += sp->tx_good; ifp->if_collisions += sp->tx_total_collisions; if (sp->rx_good) { ifp->if_ipackets += sp->rx_good; sc->rx_idle_secs = 0; } else { /* * Receiver's been idle for another second. */ sc->rx_idle_secs++; } ifp->if_ierrors += sp->rx_crc_errors + sp->rx_alignment_errors + sp->rx_rnr_errors + sp->rx_overrun_errors; /* * If any transmit underruns occured, bump up the transmit * threshold by another 512 bytes (64 * 8). */ if (sp->tx_underruns) { ifp->if_oerrors += sp->tx_underruns; if (tx_threshold < 192) tx_threshold += 64; } s = splimp(); /* * Release any xmit buffers that have completed DMA. This isn't * strictly necessary to do here, but it's advantagous for mbufs * with external storage to be released in a timely manner rather * than being defered for a potentially long time. This limits * the delay to a maximum of one second. */ for (txp = sc->cbl_first; sc->tx_queued && (txp->cb_status & FXP_CB_STATUS_C) != 0; txp = txp->next) { if (txp->mb_head != NULL) { m_freem(txp->mb_head); txp->mb_head = NULL; } sc->tx_queued--; } sc->cbl_first = txp; /* * If we haven't received any packets in FXP_MAC_RX_IDLE seconds, * then assume the receiver has locked up and attempt to clear * the condition by reprogramming the multicast filter. This is * a work-around for a bug in the 82557 where the receiver locks * up if it gets certain types of garbage in the syncronization * bits prior to the packet header. This bug is supposed to only * occur in 10Mbps mode, but has been seen to occur in 100Mbps * mode as well (perhaps due to a 10/100 speed transition). */ if (sc->rx_idle_secs > FXP_MAX_RX_IDLE) { sc->rx_idle_secs = 0; fxp_mc_setup(sc); } /* * If there is no pending command, start another stats * dump. Otherwise punt for now. */ if (CSR_READ_1(sc, FXP_CSR_SCB_COMMAND) == 0) { /* * Start another stats dump. */ CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_DUMPRESET); } else { /* * A previous command is still waiting to be accepted. * Just zero our copy of the stats and wait for the * next timer event to update them. */ sp->tx_good = 0; sp->tx_underruns = 0; sp->tx_total_collisions = 0; sp->rx_good = 0; sp->rx_crc_errors = 0; sp->rx_alignment_errors = 0; sp->rx_rnr_errors = 0; sp->rx_overrun_errors = 0; } splx(s); /* * Schedule another timeout one second from now. */ sc->stat_ch = timeout(fxp_stats_update, sc, hz); } /* * Stop the interface. Cancels the statistics updater and resets * the interface. */ static void fxp_stop(sc) struct fxp_softc *sc; { struct ifnet *ifp = &sc->sc_if; struct fxp_cb_tx *txp; int i; /* * Cancel stats updater. */ untimeout(fxp_stats_update, sc, sc->stat_ch); /* * Issue software reset */ CSR_WRITE_4(sc, FXP_CSR_PORT, FXP_PORT_SELECTIVE_RESET); DELAY(10); /* * Release any xmit buffers. */ txp = sc->cbl_base; if (txp != NULL) { for (i = 0; i < FXP_NTXCB; i++) { if (txp[i].mb_head != NULL) { m_freem(txp[i].mb_head); txp[i].mb_head = NULL; } } } sc->tx_queued = 0; /* * Free all the receive buffers then reallocate/reinitialize */ if (sc->rfa_headm != NULL) m_freem(sc->rfa_headm); sc->rfa_headm = NULL; sc->rfa_tailm = NULL; for (i = 0; i < FXP_NRFABUFS; i++) { if (fxp_add_rfabuf(sc, NULL) != 0) { /* * This "can't happen" - we're at splimp() * and we just freed all the buffers we need * above. */ panic("fxp_stop: no buffers!"); } } ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); ifp->if_timer = 0; } /* * Watchdog/transmission transmit timeout handler. Called when a * transmission is started on the interface, but no interrupt is * received before the timeout. This usually indicates that the * card has wedged for some reason. */ static void fxp_watchdog(ifp) struct ifnet *ifp; { struct fxp_softc *sc = ifp->if_softc; printf(FXP_FORMAT ": device timeout\n", FXP_ARGS(sc)); ifp->if_oerrors++; fxp_init(sc); } static void fxp_init(xsc) void *xsc; { struct fxp_softc *sc = xsc; struct ifnet *ifp = &sc->sc_if; struct fxp_cb_config *cbp; struct fxp_cb_ias *cb_ias; struct fxp_cb_tx *txp; int i, s, prm; s = splimp(); /* * Cancel any pending I/O */ fxp_stop(sc); prm = (ifp->if_flags & IFF_PROMISC) ? 1 : 0; /* * Initialize base of CBL and RFA memory. Loading with zero * sets it up for regular linear addressing. */ CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, 0); CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_BASE); fxp_scb_wait(sc); CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_RU_BASE); /* * Initialize base of dump-stats buffer. */ fxp_scb_wait(sc); CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, vtophys(sc->fxp_stats)); CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_DUMP_ADR); /* * We temporarily use memory that contains the TxCB list to * construct the config CB. The TxCB list memory is rebuilt * later. */ cbp = (struct fxp_cb_config *) sc->cbl_base; /* * This bcopy is kind of disgusting, but there are a bunch of must be * zero and must be one bits in this structure and this is the easiest * way to initialize them all to proper values. */ bcopy(fxp_cb_config_template, (volatile void *)&cbp->cb_status, sizeof(fxp_cb_config_template)); cbp->cb_status = 0; cbp->cb_command = FXP_CB_COMMAND_CONFIG | FXP_CB_COMMAND_EL; cbp->link_addr = -1; /* (no) next command */ cbp->byte_count = 22; /* (22) bytes to config */ cbp->rx_fifo_limit = 8; /* rx fifo threshold (32 bytes) */ cbp->tx_fifo_limit = 0; /* tx fifo threshold (0 bytes) */ cbp->adaptive_ifs = 0; /* (no) adaptive interframe spacing */ cbp->rx_dma_bytecount = 0; /* (no) rx DMA max */ cbp->tx_dma_bytecount = 0; /* (no) tx DMA max */ cbp->dma_bce = 0; /* (disable) dma max counters */ cbp->late_scb = 0; /* (don't) defer SCB update */ cbp->tno_int = 0; /* (disable) tx not okay interrupt */ cbp->ci_int = 1; /* interrupt on CU idle */ cbp->save_bf = prm; /* save bad frames */ cbp->disc_short_rx = !prm; /* discard short packets */ cbp->underrun_retry = 1; /* retry mode (1) on DMA underrun */ cbp->mediatype = !sc->phy_10Mbps_only; /* interface mode */ cbp->nsai = 1; /* (don't) disable source addr insert */ cbp->preamble_length = 2; /* (7 byte) preamble */ cbp->loopback = 0; /* (don't) loopback */ cbp->linear_priority = 0; /* (normal CSMA/CD operation) */ cbp->linear_pri_mode = 0; /* (wait after xmit only) */ cbp->interfrm_spacing = 6; /* (96 bits of) interframe spacing */ cbp->promiscuous = prm; /* promiscuous mode */ cbp->bcast_disable = 0; /* (don't) disable broadcasts */ cbp->crscdt = 0; /* (CRS only) */ cbp->stripping = !prm; /* truncate rx packet to byte count */ cbp->padding = 1; /* (do) pad short tx packets */ cbp->rcv_crc_xfer = 0; /* (don't) xfer CRC to host */ cbp->force_fdx = 0; /* (don't) force full duplex */ cbp->fdx_pin_en = 1; /* (enable) FDX# pin */ cbp->multi_ia = 0; /* (don't) accept multiple IAs */ cbp->mc_all = sc->all_mcasts;/* accept all multicasts */ /* * Start the config command/DMA. */ fxp_scb_wait(sc); CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, vtophys(&cbp->cb_status)); CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_START); /* ...and wait for it to complete. */ while (!(cbp->cb_status & FXP_CB_STATUS_C)); /* * Now initialize the station address. Temporarily use the TxCB * memory area like we did above for the config CB. */ cb_ias = (struct fxp_cb_ias *) sc->cbl_base; cb_ias->cb_status = 0; cb_ias->cb_command = FXP_CB_COMMAND_IAS | FXP_CB_COMMAND_EL; cb_ias->link_addr = -1; #if defined(__NetBSD__) bcopy(LLADDR(ifp->if_sadl), (void *)cb_ias->macaddr, 6); #else bcopy(sc->arpcom.ac_enaddr, (volatile void *)cb_ias->macaddr, sizeof(sc->arpcom.ac_enaddr)); #endif /* __NetBSD__ */ /* * Start the IAS (Individual Address Setup) command/DMA. */ fxp_scb_wait(sc); CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_START); /* ...and wait for it to complete. */ while (!(cb_ias->cb_status & FXP_CB_STATUS_C)); /* * Initialize transmit control block (TxCB) list. */ txp = sc->cbl_base; bzero(txp, sizeof(struct fxp_cb_tx) * FXP_NTXCB); for (i = 0; i < FXP_NTXCB; i++) { txp[i].cb_status = FXP_CB_STATUS_C | FXP_CB_STATUS_OK; txp[i].cb_command = FXP_CB_COMMAND_NOP; txp[i].link_addr = vtophys(&txp[(i + 1) & FXP_TXCB_MASK].cb_status); txp[i].tbd_array_addr = vtophys(&txp[i].tbd[0]); txp[i].next = &txp[(i + 1) & FXP_TXCB_MASK]; } /* * Set the suspend flag on the first TxCB and start the control * unit. It will execute the NOP and then suspend. */ txp->cb_command = FXP_CB_COMMAND_NOP | FXP_CB_COMMAND_S; sc->cbl_first = sc->cbl_last = txp; sc->tx_queued = 1; fxp_scb_wait(sc); CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_START); /* * Initialize receiver buffer area - RFA. */ fxp_scb_wait(sc); CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, vtophys(sc->rfa_headm->m_ext.ext_buf) + RFA_ALIGNMENT_FUDGE); CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_RU_START); /* * Set current media. */ fxp_set_media(sc, sc->sc_media.ifm_cur->ifm_media); ifp->if_flags |= IFF_RUNNING; ifp->if_flags &= ~IFF_OACTIVE; splx(s); /* * Start stats updater. */ sc->stat_ch = timeout(fxp_stats_update, sc, hz); } static void fxp_set_media(sc, media) struct fxp_softc *sc; int media; { switch (sc->phy_primary_device) { case FXP_PHY_DP83840: case FXP_PHY_DP83840A: fxp_mdi_write(sc, sc->phy_primary_addr, FXP_DP83840_PCR, fxp_mdi_read(sc, sc->phy_primary_addr, FXP_DP83840_PCR) | FXP_DP83840_PCR_LED4_MODE | /* LED4 always indicates duplex */ FXP_DP83840_PCR_F_CONNECT | /* force link disconnect bypass */ FXP_DP83840_PCR_BIT10); /* XXX I have no idea */ /* fall through */ case FXP_PHY_82553A: case FXP_PHY_82553C: /* untested */ case FXP_PHY_82555: case FXP_PHY_82555B: if (IFM_SUBTYPE(media) != IFM_AUTO) { int flags; flags = (IFM_SUBTYPE(media) == IFM_100_TX) ? FXP_PHY_BMCR_SPEED_100M : 0; flags |= (media & IFM_FDX) ? FXP_PHY_BMCR_FULLDUPLEX : 0; fxp_mdi_write(sc, sc->phy_primary_addr, FXP_PHY_BMCR, (fxp_mdi_read(sc, sc->phy_primary_addr, FXP_PHY_BMCR) & ~(FXP_PHY_BMCR_AUTOEN | FXP_PHY_BMCR_SPEED_100M | FXP_PHY_BMCR_FULLDUPLEX)) | flags); } else { fxp_mdi_write(sc, sc->phy_primary_addr, FXP_PHY_BMCR, (fxp_mdi_read(sc, sc->phy_primary_addr, FXP_PHY_BMCR) | FXP_PHY_BMCR_AUTOEN)); } break; /* * The Seeq 80c24 doesn't have a PHY programming interface, so do * nothing. */ case FXP_PHY_80C24: break; default: printf(FXP_FORMAT ": warning: unsupported PHY, type = %d, addr = %d\n", FXP_ARGS(sc), sc->phy_primary_device, sc->phy_primary_addr); } } /* * Change media according to request. */ int fxp_mediachange(ifp) struct ifnet *ifp; { struct fxp_softc *sc = ifp->if_softc; struct ifmedia *ifm = &sc->sc_media; if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER) return (EINVAL); fxp_set_media(sc, ifm->ifm_media); return (0); } /* * Notify the world which media we're using. */ void fxp_mediastatus(ifp, ifmr) struct ifnet *ifp; struct ifmediareq *ifmr; { struct fxp_softc *sc = ifp->if_softc; int flags, stsflags; switch (sc->phy_primary_device) { case FXP_PHY_82555: case FXP_PHY_82555B: case FXP_PHY_DP83840: case FXP_PHY_DP83840A: ifmr->ifm_status = IFM_AVALID; /* IFM_ACTIVE will be valid */ ifmr->ifm_active = IFM_ETHER; /* * the following is not an error. * You need to read this register twice to get current * status. This is correct documented behaviour, the * first read gets latched values. */ stsflags = fxp_mdi_read(sc, sc->phy_primary_addr, FXP_PHY_STS); stsflags = fxp_mdi_read(sc, sc->phy_primary_addr, FXP_PHY_STS); if (stsflags & FXP_PHY_STS_LINK_STS) ifmr->ifm_status |= IFM_ACTIVE; /* * If we are in auto mode, then try report the result. */ flags = fxp_mdi_read(sc, sc->phy_primary_addr, FXP_PHY_BMCR); if (flags & FXP_PHY_BMCR_AUTOEN) { ifmr->ifm_active |= IFM_AUTO; /* XXX presently 0 */ if (stsflags & FXP_PHY_STS_AUTO_DONE) { /* * Intel and National parts report * differently on what they found. */ if ((sc->phy_primary_device == FXP_PHY_82555) || (sc->phy_primary_device == FXP_PHY_82555B)) { flags = fxp_mdi_read(sc, sc->phy_primary_addr, FXP_PHY_USC); if (flags & FXP_PHY_USC_SPEED) ifmr->ifm_active |= IFM_100_TX; else ifmr->ifm_active |= IFM_10_T; if (flags & FXP_PHY_USC_DUPLEX) ifmr->ifm_active |= IFM_FDX; } else { /* it's National. only know speed */ flags = fxp_mdi_read(sc, sc->phy_primary_addr, FXP_DP83840_PAR); if (flags & FXP_DP83840_PAR_SPEED_10) ifmr->ifm_active |= IFM_10_T; else ifmr->ifm_active |= IFM_100_TX; } } } else { /* in manual mode.. just report what we were set to */ if (flags & FXP_PHY_BMCR_SPEED_100M) ifmr->ifm_active |= IFM_100_TX; else ifmr->ifm_active |= IFM_10_T; if (flags & FXP_PHY_BMCR_FULLDUPLEX) ifmr->ifm_active |= IFM_FDX; } break; case FXP_PHY_80C24: default: ifmr->ifm_active = IFM_ETHER|IFM_MANUAL; /* XXX IFM_AUTO ? */ } } /* * Add a buffer to the end of the RFA buffer list. * Return 0 if successful, 1 for failure. A failure results in * adding the 'oldm' (if non-NULL) on to the end of the list - * tossing out its old contents and recycling it. * The RFA struct is stuck at the beginning of mbuf cluster and the * data pointer is fixed up to point just past it. */ static int fxp_add_rfabuf(sc, oldm) struct fxp_softc *sc; struct mbuf *oldm; { u_int32_t v; struct mbuf *m; struct fxp_rfa *rfa, *p_rfa; MGETHDR(m, M_DONTWAIT, MT_DATA); if (m != NULL) { MCLGET(m, M_DONTWAIT); if ((m->m_flags & M_EXT) == 0) { m_freem(m); if (oldm == NULL) return 1; m = oldm; m->m_data = m->m_ext.ext_buf; } } else { if (oldm == NULL) return 1; m = oldm; m->m_data = m->m_ext.ext_buf; } /* * Move the data pointer up so that the incoming data packet * will be 32-bit aligned. */ m->m_data += RFA_ALIGNMENT_FUDGE; /* * Get a pointer to the base of the mbuf cluster and move * data start past it. */ rfa = mtod(m, struct fxp_rfa *); m->m_data += sizeof(struct fxp_rfa); rfa->size = MCLBYTES - sizeof(struct fxp_rfa) - RFA_ALIGNMENT_FUDGE; /* * Initialize the rest of the RFA. Note that since the RFA * is misaligned, we cannot store values directly. Instead, * we use an optimized, inline copy. */ rfa->rfa_status = 0; rfa->rfa_control = FXP_RFA_CONTROL_EL; rfa->actual_size = 0; v = -1; fxp_lwcopy(&v, &rfa->link_addr); fxp_lwcopy(&v, &rfa->rbd_addr); /* * If there are other buffers already on the list, attach this * one to the end by fixing up the tail to point to this one. */ if (sc->rfa_headm != NULL) { p_rfa = (struct fxp_rfa *) (sc->rfa_tailm->m_ext.ext_buf + RFA_ALIGNMENT_FUDGE); sc->rfa_tailm->m_next = m; v = vtophys(rfa); fxp_lwcopy(&v, &p_rfa->link_addr); p_rfa->rfa_control &= ~FXP_RFA_CONTROL_EL; } else { sc->rfa_headm = m; } sc->rfa_tailm = m; return (m == oldm); } static volatile int fxp_mdi_read(sc, phy, reg) struct fxp_softc *sc; int phy; int reg; { int count = 10000; int value; CSR_WRITE_4(sc, FXP_CSR_MDICONTROL, (FXP_MDI_READ << 26) | (reg << 16) | (phy << 21)); while (((value = CSR_READ_4(sc, FXP_CSR_MDICONTROL)) & 0x10000000) == 0 && count--) DELAY(10); if (count <= 0) printf(FXP_FORMAT ": fxp_mdi_read: timed out\n", FXP_ARGS(sc)); return (value & 0xffff); } static void fxp_mdi_write(sc, phy, reg, value) struct fxp_softc *sc; int phy; int reg; int value; { int count = 10000; CSR_WRITE_4(sc, FXP_CSR_MDICONTROL, (FXP_MDI_WRITE << 26) | (reg << 16) | (phy << 21) | (value & 0xffff)); while((CSR_READ_4(sc, FXP_CSR_MDICONTROL) & 0x10000000) == 0 && count--) DELAY(10); if (count <= 0) printf(FXP_FORMAT ": fxp_mdi_write: timed out\n", FXP_ARGS(sc)); } static int fxp_ioctl(ifp, command, data) struct ifnet *ifp; FXP_IOCTLCMD_TYPE command; caddr_t data; { struct fxp_softc *sc = ifp->if_softc; struct ifreq *ifr = (struct ifreq *)data; int s, error = 0; s = splimp(); switch (command) { case SIOCSIFADDR: #if !defined(__NetBSD__) case SIOCGIFADDR: case SIOCSIFMTU: #endif error = ether_ioctl(ifp, command, data); break; case SIOCSIFFLAGS: sc->all_mcasts = (ifp->if_flags & IFF_ALLMULTI) ? 1 : 0; /* * If interface is marked up and not running, then start it. * If it is marked down and running, stop it. * XXX If it's up then re-initialize it. This is so flags * such as IFF_PROMISC are handled. */ if (ifp->if_flags & IFF_UP) { fxp_init(sc); } else { if (ifp->if_flags & IFF_RUNNING) fxp_stop(sc); } break; case SIOCADDMULTI: case SIOCDELMULTI: sc->all_mcasts = (ifp->if_flags & IFF_ALLMULTI) ? 1 : 0; #if defined(__NetBSD__) error = (command == SIOCADDMULTI) ? ether_addmulti(ifr, &sc->sc_ethercom) : ether_delmulti(ifr, &sc->sc_ethercom); if (error == ENETRESET) { /* * Multicast list has changed; set the hardware * filter accordingly. */ if (!sc->all_mcasts) fxp_mc_setup(sc); /* * fxp_mc_setup() can turn on all_mcasts if we run * out of space, so check it again rather than else {}. */ if (sc->all_mcasts) fxp_init(sc); error = 0; } #else /* __FreeBSD__ */ /* * Multicast list has changed; set the hardware filter * accordingly. */ if (!sc->all_mcasts) fxp_mc_setup(sc); /* * fxp_mc_setup() can turn on sc->all_mcasts, so check it * again rather than else {}. */ if (sc->all_mcasts) fxp_init(sc); error = 0; #endif /* __NetBSD__ */ break; case SIOCSIFMEDIA: case SIOCGIFMEDIA: error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, command); break; default: error = EINVAL; } (void) splx(s); return (error); } /* * Program the multicast filter. * * We have an artificial restriction that the multicast setup command * must be the first command in the chain, so we take steps to ensure * this. By requiring this, it allows us to keep up the performance of * the pre-initialized command ring (esp. link pointers) by not actually * inserting the mcsetup command in the ring - i.e. its link pointer * points to the TxCB ring, but the mcsetup descriptor itself is not part * of it. We then can do 'CU_START' on the mcsetup descriptor and have it * lead into the regular TxCB ring when it completes. * * This function must be called at splimp. */ static void fxp_mc_setup(sc) struct fxp_softc *sc; { struct fxp_cb_mcs *mcsp = sc->mcsp; struct ifnet *ifp = &sc->sc_if; struct ifmultiaddr *ifma; int nmcasts; /* * If there are queued commands, we must wait until they are all * completed. If we are already waiting, then add a NOP command * with interrupt option so that we're notified when all commands * have been completed - fxp_start() ensures that no additional * TX commands will be added when need_mcsetup is true. */ if (sc->tx_queued) { struct fxp_cb_tx *txp; /* * need_mcsetup will be true if we are already waiting for the * NOP command to be completed (see below). In this case, bail. */ if (sc->need_mcsetup) return; sc->need_mcsetup = 1; /* * Add a NOP command with interrupt so that we are notified when all * TX commands have been processed. */ txp = sc->cbl_last->next; txp->mb_head = NULL; txp->cb_status = 0; txp->cb_command = FXP_CB_COMMAND_NOP | FXP_CB_COMMAND_S | FXP_CB_COMMAND_I; /* * Advance the end of list forward. */ sc->cbl_last->cb_command &= ~FXP_CB_COMMAND_S; sc->cbl_last = txp; sc->tx_queued++; /* * Issue a resume in case the CU has just suspended. */ fxp_scb_wait(sc); CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_RESUME); /* * Set a 5 second timer just in case we don't hear from the * card again. */ ifp->if_timer = 5; return; } sc->need_mcsetup = 0; /* * Initialize multicast setup descriptor. */ mcsp->next = sc->cbl_base; mcsp->mb_head = NULL; mcsp->cb_status = 0; mcsp->cb_command = FXP_CB_COMMAND_MCAS | FXP_CB_COMMAND_S | FXP_CB_COMMAND_I; mcsp->link_addr = vtophys(&sc->cbl_base->cb_status); nmcasts = 0; if (!sc->all_mcasts) { for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL; ifma = ifma->ifma_link.le_next) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; if (nmcasts >= MAXMCADDR) { sc->all_mcasts = 1; nmcasts = 0; break; } bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), (volatile void *) &sc->mcsp->mc_addr[nmcasts][0], 6); nmcasts++; } } mcsp->mc_cnt = nmcasts * 6; sc->cbl_first = sc->cbl_last = (struct fxp_cb_tx *) mcsp; sc->tx_queued = 1; /* * Wait until command unit is not active. This should never * be the case when nothing is queued, but make sure anyway. */ while ((CSR_READ_1(sc, FXP_CSR_SCB_RUSCUS) >> 6) == FXP_SCB_CUS_ACTIVE) ; /* * Start the multicast setup command. */ fxp_scb_wait(sc); CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, vtophys(&mcsp->cb_status)); CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_START); ifp->if_timer = 2; return; } Index: head/sys/dev/fxp/if_fxpvar.h =================================================================== --- head/sys/dev/fxp/if_fxpvar.h (revision 45719) +++ head/sys/dev/fxp/if_fxpvar.h (revision 45720) @@ -1,114 +1,117 @@ /* * Copyright (c) 1995, David Greenman * All rights reserved. * * Modifications to support NetBSD: * Copyright (c) 1997 Jason R. Thorpe. 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 unmodified, 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. * - * $Id: if_fxpvar.h,v 1.5 1998/06/07 17:12:38 dfr Exp $ + * $Id: if_fxpvar.h,v 1.6 1998/08/02 00:29:15 dg Exp $ */ /* * Misc. defintions for the Intel EtherExpress Pro/100B PCI Fast * Ethernet driver */ /* * NOTE: Elements are ordered for optimal cacheline behavior, and NOT * for functional grouping. */ struct fxp_softc { #if defined(__NetBSD__) struct device sc_dev; /* generic device structures */ void *sc_ih; /* interrupt handler cookie */ bus_space_tag_t sc_st; /* bus space tag */ bus_space_handle_t sc_sh; /* bus space handle */ struct ethercom sc_ethercom; /* ethernet common part */ #else struct arpcom arpcom; /* per-interface network data */ caddr_t csr; /* control/status registers */ + struct resource *mem; /* resource descriptor for registers */ + struct resource *irq; /* resource descriptor for interrupt */ + void *ih; /* interrupt handler cookie */ #endif /* __NetBSD__ */ struct mbuf *rfa_headm; /* first mbuf in receive frame area */ struct mbuf *rfa_tailm; /* last mbuf in receive frame area */ struct fxp_cb_tx *cbl_first; /* first active TxCB in list */ int tx_queued; /* # of active TxCB's */ int need_mcsetup; /* multicast filter needs programming */ struct fxp_cb_tx *cbl_last; /* last active TxCB in list */ struct fxp_stats *fxp_stats; /* Pointer to interface stats */ int rx_idle_secs; /* # of seconds RX has been idle */ struct callout_handle stat_ch; /* Handle for canceling our stat timeout */ struct fxp_cb_tx *cbl_base; /* base of TxCB list */ struct fxp_cb_mcs *mcsp; /* Pointer to mcast setup descriptor */ int all_mcasts; /* receive all multicasts */ struct ifmedia sc_media; /* media information */ int phy_primary_addr; /* address of primary PHY */ int phy_primary_device; /* device type of primary PHY */ int phy_10Mbps_only; /* PHY is 10Mbps-only device */ }; /* Macros to ease CSR access. */ #if defined(__NetBSD__) #define CSR_READ_1(sc, reg) \ bus_space_read_1((sc)->sc_st, (sc)->sc_sh, (reg)) #define CSR_READ_2(sc, reg) \ bus_space_read_2((sc)->sc_st, (sc)->sc_sh, (reg)) #define CSR_READ_4(sc, reg) \ bus_space_read_4((sc)->sc_st, (sc)->sc_sh, (reg)) #define CSR_WRITE_1(sc, reg, val) \ bus_space_write_1((sc)->sc_st, (sc)->sc_sh, (reg), (val)) #define CSR_WRITE_2(sc, reg, val) \ bus_space_write_2((sc)->sc_st, (sc)->sc_sh, (reg), (val)) #define CSR_WRITE_4(sc, reg, val) \ bus_space_write_4((sc)->sc_st, (sc)->sc_sh, (reg), (val)) #else #define CSR_READ_1(sc, reg) \ (*((u_int8_t *)((sc)->csr + (reg)))) #define CSR_READ_2(sc, reg) \ (*((u_int16_t *)((sc)->csr + (reg)))) #define CSR_READ_4(sc, reg) \ (*((u_int32_t *)((sc)->csr + (reg)))) #define CSR_WRITE_1(sc, reg, val) \ (*((u_int8_t *)((sc)->csr + (reg)))) = (val) #define CSR_WRITE_2(sc, reg, val) \ (*((u_int16_t *)((sc)->csr + (reg)))) = (val) #define CSR_WRITE_4(sc, reg, val) \ (*((u_int32_t *)((sc)->csr + (reg)))) = (val) #endif /* __NetBSD__ */ /* Deal with slight differences in software interfaces. */ #if defined(__NetBSD__) #define sc_if sc_ethercom.ec_if #define FXP_FORMAT "%s" #define FXP_ARGS(sc) (sc)->sc_dev.dv_xname #define FXP_INTR_TYPE int #define FXP_IOCTLCMD_TYPE u_long #define FXP_BPFTAP_ARG(ifp) (ifp)->if_bpf #else /* __FreeBSD__ */ #define sc_if arpcom.ac_if #define FXP_FORMAT "fxp%d" #define FXP_ARGS(sc) (sc)->arpcom.ac_if.if_unit #define FXP_INTR_TYPE void #define FXP_IOCTLCMD_TYPE u_long #define FXP_BPFTAP_ARG(ifp) ifp #endif /* __NetBSD__ */ Index: head/sys/dev/kbd/atkbd.c =================================================================== --- head/sys/dev/kbd/atkbd.c (revision 45719) +++ head/sys/dev/kbd/atkbd.c (revision 45720) @@ -1,1422 +1,1421 @@ /*- * Copyright (c) 1999 Kazutaka YOKOTA * 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 as * the first lines of this file unmodified. * 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 AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS BE LIABLE 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. * - * $Id: atkbd.c,v 1.4 1999/01/28 10:55:55 yokota Exp $ + * $Id: atkbd.c,v 1.5 1999/03/10 10:36:52 yokota Exp $ */ #include "atkbd.h" #include "opt_kbd.h" #include "opt_atkbd.h" #include "opt_devfs.h" #if NATKBD > 0 #include #include #include #include #include #include #include #include #include #include #include -#ifndef __i386__ +#if 1 #include #include extern devclass_t atkbd_devclass; #define ATKBD_SOFTC(unit) \ ((atkbd_softc_t *)devclass_get_softc(atkbd_devclass, unit)) #else /* __i386__ */ #include #include extern struct isa_driver atkbddriver; /* XXX: a kludge; see below */ static atkbd_softc_t *atkbd_softc[NATKBD]; #define ATKBD_SOFTC(unit) \ (((unit) >= NATKBD) ? NULL : atkbd_softc[(unit)]) #endif /* __i386__ */ static timeout_t atkbd_timeout; #ifdef KBD_INSTALL_CDEV static d_open_t atkbdopen; static d_close_t atkbdclose; static d_read_t atkbdread; static d_ioctl_t atkbdioctl; static d_poll_t atkbdpoll; static struct cdevsw atkbd_cdevsw = { atkbdopen, atkbdclose, atkbdread, nowrite, atkbdioctl, nostop, nullreset, nodevtotty, atkbdpoll, nommap, NULL, ATKBD_DRIVER_NAME, NULL, -1, }; #endif /* KBD_INSTALL_CDEV */ +#if 0 #ifdef __i386__ atkbd_softc_t *atkbd_get_softc(int unit) { atkbd_softc_t *sc; if (unit >= sizeof(atkbd_softc)/sizeof(atkbd_softc[0])) return NULL; sc = atkbd_softc[unit]; if (sc == NULL) { sc = atkbd_softc[unit] = malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT); if (sc == NULL) return NULL; bzero(sc, sizeof(*sc)); } return sc; } #endif /* __i386__ */ +#endif int atkbd_probe_unit(int unit, int port, int irq, int flags) { keyboard_switch_t *sw; int args[2]; int error; sw = kbd_get_switch(ATKBD_DRIVER_NAME); if (sw == NULL) return ENXIO; args[0] = port; args[1] = irq; error = (*sw->probe)(unit, args, flags); if (error) return error; return 0; } int atkbd_attach_unit(int unit, atkbd_softc_t *sc, int port, int irq, int flags) { keyboard_switch_t *sw; int args[2]; int error; if (sc->flags & ATKBD_ATTACHED) return 0; sw = kbd_get_switch(ATKBD_DRIVER_NAME); if (sw == NULL) return ENXIO; /* reset, initialize and enable the device */ args[0] = port; args[1] = irq; sc->kbd = NULL; error = (*sw->probe)(unit, args, flags); if (error) return error; error = (*sw->init)(unit, &sc->kbd, args, flags); if (error) return error; (*sw->enable)(sc->kbd); #ifdef KBD_INSTALL_CDEV /* attach a virtual keyboard cdev */ error = kbd_attach(makedev(0, ATKBD_MKMINOR(unit)), sc->kbd, &atkbd_cdevsw); if (error) return error; #endif /* * This is a kludge to compensate for lost keyboard interrupts. * A similar code used to be in syscons. See below. XXX */ atkbd_timeout(sc->kbd); if (bootverbose) (*sw->diag)(sc->kbd, bootverbose); sc->flags |= ATKBD_ATTACHED; return 0; } static void atkbd_timeout(void *arg) { keyboard_t *kbd; int s; /* The following comments are extracted from syscons.c (1.287) */ /* * With release 2.1 of the Xaccel server, the keyboard is left * hanging pretty often. Apparently an interrupt from the * keyboard is lost, and I don't know why (yet). * This ugly hack calls scintr if input is ready for the keyboard * and conveniently hides the problem. XXX */ /* * Try removing anything stuck in the keyboard controller; whether * it's a keyboard scan code or mouse data. `scintr()' doesn't * read the mouse data directly, but `kbdio' routines will, as a * side effect. */ s = spltty(); kbd = (keyboard_t *)arg; if ((*kbdsw[kbd->kb_index]->lock)(kbd, TRUE)) { /* * We have seen the lock flag is not set. Let's reset * the flag early, otherwise the LED update routine fails * which may want the lock during the interrupt routine. */ (*kbdsw[kbd->kb_index]->lock)(kbd, FALSE); if ((*kbdsw[kbd->kb_index]->check_char)(kbd)) (*kbdsw[kbd->kb_index]->intr)(kbd, NULL); } splx(s); timeout(atkbd_timeout, arg, hz/10); } /* cdev driver functions */ #ifdef KBD_INSTALL_CDEV static int atkbdopen(dev_t dev, int flag, int mode, struct proc *p) { atkbd_softc_t *sc; sc = ATKBD_SOFTC(ATKBD_UNIT(dev)); if (sc == NULL) return ENXIO; if (mode & (FWRITE | O_CREAT | O_APPEND | O_TRUNC)) return ENODEV; /* FIXME: set the initial input mode (K_XLATE?) and lock state? */ return genkbdopen(&sc->gensc, sc->kbd, flag, mode, p); } static int atkbdclose(dev_t dev, int flag, int mode, struct proc *p) { atkbd_softc_t *sc; sc = ATKBD_SOFTC(ATKBD_UNIT(dev)); return genkbdclose(&sc->gensc, sc->kbd, flag, mode, p); } static int atkbdread(dev_t dev, struct uio *uio, int flag) { atkbd_softc_t *sc; sc = ATKBD_SOFTC(ATKBD_UNIT(dev)); return genkbdread(&sc->gensc, sc->kbd, uio, flag); } static int atkbdioctl(dev_t dev, u_long cmd, caddr_t arg, int flag, struct proc *p) { atkbd_softc_t *sc; sc = ATKBD_SOFTC(ATKBD_UNIT(dev)); return genkbdioctl(&sc->gensc, sc->kbd, cmd, arg, flag, p); } static int atkbdpoll(dev_t dev, int event, struct proc *p) { atkbd_softc_t *sc; sc = ATKBD_SOFTC(ATKBD_UNIT(dev)); return genkbdpoll(&sc->gensc, sc->kbd, event, p); } #endif /* KBD_INSTALL_CDEV */ /* LOW-LEVEL */ #include #include #include #define ATKBD_DEFAULT 0 typedef struct atkbd_state { KBDC kbdc; /* keyboard controller */ /* XXX: don't move this field; pcvt * expects `kbdc' to be the first * field in this structure. */ int ks_mode; /* input mode (K_XLATE,K_RAW,K_CODE) */ int ks_flags; /* flags */ #define COMPOSE (1 << 0) int ks_polling; int ks_state; /* shift/lock key state */ int ks_accents; /* accent key index (> 0) */ u_int ks_composed_char; /* composed char code (> 0) */ u_char ks_prefix; /* AT scan code prefix */ } atkbd_state_t; /* keyboard driver declaration */ static int atkbd_configure(int flags); static kbd_probe_t atkbd_probe; static kbd_init_t atkbd_init; static kbd_term_t atkbd_term; static kbd_intr_t atkbd_intr; static kbd_test_if_t atkbd_test_if; static kbd_enable_t atkbd_enable; static kbd_disable_t atkbd_disable; static kbd_read_t atkbd_read; static kbd_check_t atkbd_check; static kbd_read_char_t atkbd_read_char; static kbd_check_char_t atkbd_check_char; static kbd_ioctl_t atkbd_ioctl; static kbd_lock_t atkbd_lock; static kbd_clear_state_t atkbd_clear_state; static kbd_get_state_t atkbd_get_state; static kbd_set_state_t atkbd_set_state; static kbd_poll_mode_t atkbd_poll; keyboard_switch_t atkbdsw = { atkbd_probe, atkbd_init, atkbd_term, atkbd_intr, atkbd_test_if, atkbd_enable, atkbd_disable, atkbd_read, atkbd_check, atkbd_read_char, atkbd_check_char, atkbd_ioctl, atkbd_lock, atkbd_clear_state, atkbd_get_state, atkbd_set_state, genkbd_get_fkeystr, atkbd_poll, genkbd_diag, }; KEYBOARD_DRIVER(atkbd, atkbdsw, atkbd_configure); /* local functions */ static int setup_kbd_port(KBDC kbdc, int port, int intr); static int get_kbd_echo(KBDC kbdc); static int probe_keyboard(KBDC kbdc, int flags); static int init_keyboard(KBDC kbdc, int *type, int flags); static int write_kbd(KBDC kbdc, int command, int data); static int get_kbd_id(KBDC kbdc); static int typematic(int delay, int rate); /* local variables */ /* the initial key map, accent map and fkey strings */ #ifdef ATKBD_DFLT_KEYMAP #define KBD_DFLT_KEYMAP #include "atkbdmap.h" #endif #include /* structures for the default keyboard */ static keyboard_t default_kbd; static atkbd_state_t default_kbd_state; static keymap_t default_keymap; static accentmap_t default_accentmap; static fkeytab_t default_fkeytab[NUM_FKEYS]; /* * The back door to the keyboard driver! * This function is called by the console driver, via the kbdio module, * to tickle keyboard drivers when the low-level console is being initialized. * Almost nothing in the kernel has been initialied yet. Try to probe * keyboards if possible. * NOTE: because of the way the low-level conole is initialized, this routine * may be called more than once!! */ static int atkbd_configure(int flags) { keyboard_t *kbd; int arg[2]; -#ifdef __i386__ - struct isa_device *dev; int i; /* XXX: a kludge to obtain the device configuration flags */ - dev = find_isadev(isa_devtab_tty, &atkbddriver, 0); - if (dev != NULL) { - flags |= dev->id_flags; + if (resource_int_value("atkbd", 0, "flags", &i) == 0) { + flags |= i; /* if the driver is disabled, unregister the keyboard if any */ - if (!dev->id_enabled) { + if (resource_int_value("atkbd", 0, "disabled", &i) == 0 + && i != 0) { i = kbd_find_keyboard(ATKBD_DRIVER_NAME, ATKBD_DEFAULT); if (i >= 0) { kbd = kbd_get_keyboard(i); kbd_unregister(kbd); kbd->kb_flags &= ~KB_REGISTERED; return 0; } } } -#endif - + /* probe the keyboard controller */ atkbdc_configure(); /* probe the default keyboard */ arg[0] = -1; arg[1] = -1; kbd = NULL; if (atkbd_probe(ATKBD_DEFAULT, arg, flags)) return 0; if (atkbd_init(ATKBD_DEFAULT, &kbd, arg, flags)) return 0; /* return the number of found keyboards */ return 1; } /* low-level functions */ /* detect a keyboard */ static int atkbd_probe(int unit, void *arg, int flags) { KBDC kbdc; int *data = (int *)arg; /* XXX */ if (unit == ATKBD_DEFAULT) { if (KBD_IS_PROBED(&default_kbd)) return 0; } kbdc = kbdc_open(data[0]); if (kbdc == NULL) return ENXIO; if (probe_keyboard(kbdc, flags)) { if (flags & KB_CONF_FAIL_IF_NO_KBD) return ENXIO; } return 0; } /* reset and initialize the device */ static int atkbd_init(int unit, keyboard_t **kbdp, void *arg, int flags) { keyboard_t *kbd; atkbd_state_t *state; keymap_t *keymap; accentmap_t *accmap; fkeytab_t *fkeymap; int fkeymap_size; int *data = (int *)arg; /* XXX */ if (unit == ATKBD_DEFAULT) { *kbdp = kbd = &default_kbd; if (KBD_IS_INITIALIZED(kbd) && KBD_IS_CONFIGURED(kbd)) return 0; state = &default_kbd_state; keymap = &default_keymap; accmap = &default_accentmap; fkeymap = default_fkeytab; fkeymap_size = sizeof(default_fkeytab)/sizeof(default_fkeytab[0]); } else if (*kbdp == NULL) { *kbdp = kbd = malloc(sizeof(*kbd), M_DEVBUF, M_NOWAIT); if (kbd == NULL) return ENOMEM; bzero(kbd, sizeof(*kbd)); state = malloc(sizeof(*state), M_DEVBUF, M_NOWAIT); keymap = malloc(sizeof(key_map), M_DEVBUF, M_NOWAIT); accmap = malloc(sizeof(accent_map), M_DEVBUF, M_NOWAIT); fkeymap = malloc(sizeof(fkey_tab), M_DEVBUF, M_NOWAIT); fkeymap_size = sizeof(fkey_tab)/sizeof(fkey_tab[0]); if ((state == NULL) || (keymap == NULL) || (accmap == NULL) || (fkeymap == NULL)) { if (state != NULL) free(state, M_DEVBUF); if (keymap != NULL) free(keymap, M_DEVBUF); if (accmap != NULL) free(accmap, M_DEVBUF); if (fkeymap != NULL) free(fkeymap, M_DEVBUF); free(kbd, M_DEVBUF); return ENOMEM; } bzero(state, sizeof(*state)); } else if (KBD_IS_INITIALIZED(*kbdp) && KBD_IS_CONFIGURED(*kbdp)) { return 0; } else { kbd = *kbdp; state = (atkbd_state_t *)kbd->kb_data; bzero(state, sizeof(*state)); keymap = kbd->kb_keymap; accmap = kbd->kb_accentmap; fkeymap = kbd->kb_fkeytab; fkeymap_size = kbd->kb_fkeytab_size; } if (!KBD_IS_PROBED(kbd)) { state->kbdc = kbdc_open(data[0]); if (state->kbdc == NULL) return ENXIO; kbd_init_struct(kbd, ATKBD_DRIVER_NAME, KB_OTHER, unit, flags, data[0], IO_KBDSIZE); bcopy(&key_map, keymap, sizeof(key_map)); bcopy(&accent_map, accmap, sizeof(accent_map)); bcopy(fkey_tab, fkeymap, imin(fkeymap_size*sizeof(fkeymap[0]), sizeof(fkey_tab))); kbd_set_maps(kbd, keymap, accmap, fkeymap, fkeymap_size); kbd->kb_data = (void *)state; if (probe_keyboard(state->kbdc, flags)) { /* shouldn't happen */ if (flags & KB_CONF_FAIL_IF_NO_KBD) return ENXIO; } else { KBD_FOUND_DEVICE(kbd); } atkbd_clear_state(kbd); state->ks_mode = K_XLATE; /* * FIXME: set the initial value for lock keys in ks_state * according to the BIOS data? */ KBD_PROBE_DONE(kbd); } if (!KBD_IS_INITIALIZED(kbd) && !(flags & KB_CONF_PROBE_ONLY)) { if (KBD_HAS_DEVICE(kbd) && init_keyboard(state->kbdc, &kbd->kb_type, kbd->kb_config) && (kbd->kb_config & KB_CONF_FAIL_IF_NO_KBD)) return ENXIO; atkbd_ioctl(kbd, KDSETLED, (caddr_t)&state->ks_state); KBD_INIT_DONE(kbd); } if (!KBD_IS_CONFIGURED(kbd)) { if (kbd_register(kbd) < 0) return ENXIO; KBD_CONFIG_DONE(kbd); } return 0; } /* finish using this keyboard */ static int atkbd_term(keyboard_t *kbd) { kbd_unregister(kbd); return 0; } /* keyboard interrupt routine */ static int atkbd_intr(keyboard_t *kbd, void *arg) { atkbd_state_t *state; int c; if (KBD_IS_ACTIVE(kbd) && KBD_IS_BUSY(kbd)) { /* let the callback function to process the input */ (*kbd->kb_callback.kc_func)(kbd, KBDIO_KEYINPUT, kbd->kb_callback.kc_arg); } else { /* read and discard the input; no one is waiting for input */ do { c = atkbd_read_char(kbd, FALSE); } while (c != NOKEY); if (!KBD_HAS_DEVICE(kbd)) { /* * The keyboard was not detected before; * it must have been reconnected! */ state = (atkbd_state_t *)kbd->kb_data; init_keyboard(state->kbdc, &kbd->kb_type, kbd->kb_config); atkbd_ioctl(kbd, KDSETLED, (caddr_t)&state->ks_state); KBD_FOUND_DEVICE(kbd); } } return 0; } /* test the interface to the device */ static int atkbd_test_if(keyboard_t *kbd) { int error; int s; error = 0; empty_both_buffers(((atkbd_state_t *)kbd->kb_data)->kbdc, 10); s = spltty(); if (!test_controller(((atkbd_state_t *)kbd->kb_data)->kbdc)) error = EIO; else if (test_kbd_port(((atkbd_state_t *)kbd->kb_data)->kbdc) != 0) error = EIO; splx(s); return error; } /* * Enable the access to the device; until this function is called, * the client cannot read from the keyboard. */ static int atkbd_enable(keyboard_t *kbd) { int s; s = spltty(); KBD_ACTIVATE(kbd); splx(s); return 0; } /* disallow the access to the device */ static int atkbd_disable(keyboard_t *kbd) { int s; s = spltty(); KBD_DEACTIVATE(kbd); splx(s); return 0; } /* read one byte from the keyboard if it's allowed */ static int atkbd_read(keyboard_t *kbd, int wait) { int c; if (wait) c = read_kbd_data(((atkbd_state_t *)kbd->kb_data)->kbdc); else c = read_kbd_data_no_wait(((atkbd_state_t *)kbd->kb_data)->kbdc); return (KBD_IS_ACTIVE(kbd) ? c : -1); } /* check if data is waiting */ static int atkbd_check(keyboard_t *kbd) { if (!KBD_IS_ACTIVE(kbd)) return FALSE; return kbdc_data_ready(((atkbd_state_t *)kbd->kb_data)->kbdc); } /* read char from the keyboard */ static u_int atkbd_read_char(keyboard_t *kbd, int wait) { atkbd_state_t *state; u_int action; int scancode; int keycode; state = (atkbd_state_t *)kbd->kb_data; next_code: /* do we have a composed char to return? */ if (!(state->ks_flags & COMPOSE) && (state->ks_composed_char > 0)) { action = state->ks_composed_char; state->ks_composed_char = 0; if (action > UCHAR_MAX) return ERRKEY; return action; } /* see if there is something in the keyboard port */ if (wait) { do { scancode = read_kbd_data(state->kbdc); } while (scancode == -1); } else { scancode = read_kbd_data_no_wait(state->kbdc); if (scancode == -1) return NOKEY; } /* return the byte as is for the K_RAW mode */ if (state->ks_mode == K_RAW) return scancode; /* translate the scan code into a keycode */ keycode = scancode & 0x7F; switch (state->ks_prefix) { case 0x00: /* normal scancode */ switch(scancode) { case 0xB8: /* left alt (compose key) released */ if (state->ks_flags & COMPOSE) { state->ks_flags &= ~COMPOSE; if (state->ks_composed_char > UCHAR_MAX) state->ks_composed_char = 0; } break; case 0x38: /* left alt (compose key) pressed */ if (!(state->ks_flags & COMPOSE)) { state->ks_flags |= COMPOSE; state->ks_composed_char = 0; } break; case 0xE0: case 0xE1: state->ks_prefix = scancode; goto next_code; } break; case 0xE0: /* 0xE0 prefix */ state->ks_prefix = 0; switch (keycode) { case 0x1C: /* right enter key */ keycode = 0x59; break; case 0x1D: /* right ctrl key */ keycode = 0x5A; break; case 0x35: /* keypad divide key */ keycode = 0x5B; break; case 0x37: /* print scrn key */ keycode = 0x5C; break; case 0x38: /* right alt key (alt gr) */ keycode = 0x5D; break; case 0x46: /* ctrl-pause/break on AT 101 (see below) */ keycode = 0x68; break; case 0x47: /* grey home key */ keycode = 0x5E; break; case 0x48: /* grey up arrow key */ keycode = 0x5F; break; case 0x49: /* grey page up key */ keycode = 0x60; break; case 0x4B: /* grey left arrow key */ keycode = 0x61; break; case 0x4D: /* grey right arrow key */ keycode = 0x62; break; case 0x4F: /* grey end key */ keycode = 0x63; break; case 0x50: /* grey down arrow key */ keycode = 0x64; break; case 0x51: /* grey page down key */ keycode = 0x65; break; case 0x52: /* grey insert key */ keycode = 0x66; break; case 0x53: /* grey delete key */ keycode = 0x67; break; /* the following 3 are only used on the MS "Natural" keyboard */ case 0x5b: /* left Window key */ keycode = 0x69; break; case 0x5c: /* right Window key */ keycode = 0x6a; break; case 0x5d: /* menu key */ keycode = 0x6b; break; default: /* ignore everything else */ goto next_code; } break; case 0xE1: /* 0xE1 prefix */ /* * The pause/break key on the 101 keyboard produces: * E1-1D-45 E1-9D-C5 * Ctrl-pause/break produces: * E0-46 E0-C6 (See above.) */ state->ks_prefix = 0; if (keycode == 0x1D) state->ks_prefix = 0x1D; goto next_code; /* NOT REACHED */ case 0x1D: /* pause / break */ state->ks_prefix = 0; if (keycode != 0x45) goto next_code; keycode = 0x68; break; } if (kbd->kb_type == KB_84) { switch (keycode) { case 0x37: /* *(numpad)/print screen */ if (state->ks_flags & SHIFTS) keycode = 0x5c; /* print screen */ break; case 0x45: /* num lock/pause */ if (state->ks_flags & CTLS) keycode = 0x68; /* pause */ break; case 0x46: /* scroll lock/break */ if (state->ks_flags & CTLS) keycode = 0x6c; /* break */ break; } } else if (kbd->kb_type == KB_101) { switch (keycode) { case 0x5c: /* print screen */ if (state->ks_flags & ALTS) keycode = 0x54; /* sysrq */ break; case 0x68: /* pause/break */ if (state->ks_flags & CTLS) keycode = 0x6c; /* break */ break; } } /* return the key code in the K_CODE mode */ if (state->ks_mode == K_CODE) return (keycode | (scancode & 0x80)); /* compose a character code */ if (state->ks_flags & COMPOSE) { switch (scancode) { /* key pressed, process it */ case 0x47: case 0x48: case 0x49: /* keypad 7,8,9 */ state->ks_composed_char *= 10; state->ks_composed_char += scancode - 0x40; if (state->ks_composed_char > UCHAR_MAX) return ERRKEY; goto next_code; case 0x4B: case 0x4C: case 0x4D: /* keypad 4,5,6 */ state->ks_composed_char *= 10; state->ks_composed_char += scancode - 0x47; if (state->ks_composed_char > UCHAR_MAX) return ERRKEY; goto next_code; case 0x4F: case 0x50: case 0x51: /* keypad 1,2,3 */ state->ks_composed_char *= 10; state->ks_composed_char += scancode - 0x4E; if (state->ks_composed_char > UCHAR_MAX) return ERRKEY; goto next_code; case 0x52: /* keypad 0 */ state->ks_composed_char *= 10; if (state->ks_composed_char > UCHAR_MAX) return ERRKEY; goto next_code; /* key released, no interest here */ case 0xC7: case 0xC8: case 0xC9: /* keypad 7,8,9 */ case 0xCB: case 0xCC: case 0xCD: /* keypad 4,5,6 */ case 0xCF: case 0xD0: case 0xD1: /* keypad 1,2,3 */ case 0xD2: /* keypad 0 */ goto next_code; case 0x38: /* left alt key */ break; default: if (state->ks_composed_char > 0) { state->ks_flags &= ~COMPOSE; state->ks_composed_char = 0; return ERRKEY; } break; } } /* keycode to key action */ action = genkbd_keyaction(kbd, keycode, scancode & 0x80, &state->ks_state, &state->ks_accents); if (action == NOKEY) goto next_code; else return action; } /* check if char is waiting */ static int atkbd_check_char(keyboard_t *kbd) { atkbd_state_t *state; if (!KBD_IS_ACTIVE(kbd)) return FALSE; state = (atkbd_state_t *)kbd->kb_data; if (!(state->ks_flags & COMPOSE) && (state->ks_composed_char > 0)) return TRUE; return kbdc_data_ready(state->kbdc); } /* some useful control functions */ static int atkbd_ioctl(keyboard_t *kbd, u_long cmd, caddr_t arg) { /* trasnlate LED_XXX bits into the device specific bits */ static u_char ledmap[8] = { 0, 4, 2, 6, 1, 5, 3, 7, }; atkbd_state_t *state = kbd->kb_data; int error; int s; int i; s = spltty(); switch (cmd) { case KDGKBMODE: /* get keyboard mode */ *(int *)arg = state->ks_mode; break; case KDSKBMODE: /* set keyboard mode */ switch (*(int *)arg) { case K_XLATE: if (state->ks_mode != K_XLATE) { /* make lock key state and LED state match */ state->ks_state &= ~LOCK_MASK; state->ks_state |= KBD_LED_VAL(kbd); } /* FALL THROUGH */ case K_RAW: case K_CODE: if (state->ks_mode != *(int *)arg) { atkbd_clear_state(kbd); state->ks_mode = *(int *)arg; } break; default: splx(s); return EINVAL; } break; case KDGETLED: /* get keyboard LED */ *(int *)arg = KBD_LED_VAL(kbd); break; case KDSETLED: /* set keyboard LED */ /* NOTE: lock key state in ks_state won't be changed */ if (*(int *)arg & ~LOCK_MASK) { splx(s); return EINVAL; } i = *(int *)arg; /* replace CAPS LED with ALTGR LED for ALTGR keyboards */ if (kbd->kb_keymap->n_keys > ALTGR_OFFSET) { if (i & ALKED) i |= CLKED; else i &= ~CLKED; } if (KBD_HAS_DEVICE(kbd)) { error = write_kbd(state->kbdc, KBDC_SET_LEDS, ledmap[i & LED_MASK]); if (error) { splx(s); return error; } } KBD_LED_VAL(kbd) = *(int *)arg; break; case KDGKBSTATE: /* get lock key state */ *(int *)arg = state->ks_state & LOCK_MASK; break; case KDSKBSTATE: /* set lock key state */ if (*(int *)arg & ~LOCK_MASK) { splx(s); return EINVAL; } state->ks_state &= ~LOCK_MASK; state->ks_state |= *(int *)arg; splx(s); /* set LEDs and quit */ return atkbd_ioctl(kbd, KDSETLED, arg); case KDSETREPEAT: /* set keyboard repeat rate (new interface) */ splx(s); if (!KBD_HAS_DEVICE(kbd)) return 0; i = typematic(((int *)arg)[0], ((int *)arg)[1]); return write_kbd(state->kbdc, KBDC_SET_TYPEMATIC, i); case KDSETRAD: /* set keyboard repeat rate (old interface) */ splx(s); if (!KBD_HAS_DEVICE(kbd)) return 0; return write_kbd(state->kbdc, KBDC_SET_TYPEMATIC, *(int *)arg); case PIO_KEYMAP: /* set keyboard translation table */ case PIO_KEYMAPENT: /* set keyboard translation table entry */ case PIO_DEADKEYMAP: /* set accent key translation table */ state->ks_accents = 0; /* FALL THROUGH */ default: splx(s); return genkbd_commonioctl(kbd, cmd, arg); } splx(s); return 0; } /* lock the access to the keyboard */ static int atkbd_lock(keyboard_t *kbd, int lock) { return kbdc_lock(((atkbd_state_t *)kbd->kb_data)->kbdc, lock); } /* clear the internal state of the keyboard */ static void atkbd_clear_state(keyboard_t *kbd) { atkbd_state_t *state; state = (atkbd_state_t *)kbd->kb_data; state->ks_flags = 0; state->ks_polling = 0; state->ks_state &= LOCK_MASK; /* preserve locking key state */ state->ks_accents = 0; state->ks_composed_char = 0; #if 0 state->ks_prefix = 0; /* XXX */ #endif } /* save the internal state */ static int atkbd_get_state(keyboard_t *kbd, void *buf, size_t len) { if (len == 0) return sizeof(atkbd_state_t); if (len < sizeof(atkbd_state_t)) return -1; bcopy(kbd->kb_data, buf, sizeof(atkbd_state_t)); return 0; } /* set the internal state */ static int atkbd_set_state(keyboard_t *kbd, void *buf, size_t len) { if (len < sizeof(atkbd_state_t)) return ENOMEM; if (((atkbd_state_t *)kbd->kb_data)->kbdc != ((atkbd_state_t *)buf)->kbdc) return ENOMEM; bcopy(buf, kbd->kb_data, sizeof(atkbd_state_t)); return 0; } static int atkbd_poll(keyboard_t *kbd, int on) { atkbd_state_t *state; int s; state = (atkbd_state_t *)kbd->kb_data; s = spltty(); if (on) ++state->ks_polling; else --state->ks_polling; splx(s); return 0; } /* local functions */ static int setup_kbd_port(KBDC kbdc, int port, int intr) { if (!set_controller_command_byte(kbdc, KBD_KBD_CONTROL_BITS, ((port) ? KBD_ENABLE_KBD_PORT : KBD_DISABLE_KBD_PORT) | ((intr) ? KBD_ENABLE_KBD_INT : KBD_DISABLE_KBD_INT))) return 1; return 0; } static int get_kbd_echo(KBDC kbdc) { /* enable the keyboard port, but disable the keyboard intr. */ if (setup_kbd_port(kbdc, TRUE, FALSE)) /* CONTROLLER ERROR: there is very little we can do... */ return ENXIO; /* see if something is present */ write_kbd_command(kbdc, KBDC_ECHO); if (read_kbd_data(kbdc) != KBD_ECHO) { empty_both_buffers(kbdc, 10); test_controller(kbdc); test_kbd_port(kbdc); return ENXIO; } /* enable the keyboard port and intr. */ if (setup_kbd_port(kbdc, TRUE, TRUE)) { /* * CONTROLLER ERROR * This is serious; the keyboard intr is left disabled! */ return ENXIO; } return 0; } static int probe_keyboard(KBDC kbdc, int flags) { /* * Don't try to print anything in this function. The low-level * console may not have been initialized yet... */ int err; int c; int m; if (!kbdc_lock(kbdc, TRUE)) { /* driver error? */ return ENXIO; } /* flush any noise in the buffer */ empty_both_buffers(kbdc, 10); /* save the current keyboard controller command byte */ m = kbdc_get_device_mask(kbdc) & ~KBD_KBD_CONTROL_BITS; c = get_controller_command_byte(kbdc); if (c == -1) { /* CONTROLLER ERROR */ kbdc_set_device_mask(kbdc, m); kbdc_lock(kbdc, FALSE); return ENXIO; } /* * The keyboard may have been screwed up by the boot block. * We may just be able to recover from error by testing the controller * and the keyboard port. The controller command byte needs to be * saved before this recovery operation, as some controllers seem * to set the command byte to particular values. */ test_controller(kbdc); test_kbd_port(kbdc); err = get_kbd_echo(kbdc); if (err == 0) { kbdc_set_device_mask(kbdc, m | KBD_KBD_CONTROL_BITS); } else { if (c != -1) /* try to restore the command byte as before */ set_controller_command_byte(kbdc, 0xff, c); kbdc_set_device_mask(kbdc, m); } kbdc_lock(kbdc, FALSE); return err; } static int init_keyboard(KBDC kbdc, int *type, int flags) { int codeset; int id; int c; if (!kbdc_lock(kbdc, TRUE)) { /* driver error? */ return EIO; } /* save the current controller command byte */ empty_both_buffers(kbdc, 10); c = get_controller_command_byte(kbdc); if (c == -1) { /* CONTROLLER ERROR */ kbdc_lock(kbdc, FALSE); printf("atkbd: unable to get the current command byte value.\n"); return EIO; } if (bootverbose) printf("atkbd: the current kbd controller command byte %04x\n", c); #if 0 /* override the keyboard lock switch */ c |= KBD_OVERRIDE_KBD_LOCK; #endif /* enable the keyboard port, but disable the keyboard intr. */ if (setup_kbd_port(kbdc, TRUE, FALSE)) { /* CONTROLLER ERROR: there is very little we can do... */ printf("atkbd: unable to set the command byte.\n"); kbdc_lock(kbdc, FALSE); return EIO; } /* * Check if we have an XT keyboard before we attempt to reset it. * The procedure assumes that the keyboard and the controller have * been set up properly by BIOS and have not been messed up * during the boot process. */ codeset = -1; if (flags & KB_CONF_ALT_SCANCODESET) /* the user says there is a XT keyboard */ codeset = 1; #ifdef KBD_DETECT_XT_KEYBOARD else if ((c & KBD_TRANSLATION) == 0) { /* SET_SCANCODE_SET is not always supported; ignore error */ if (send_kbd_command_and_data(kbdc, KBDC_SET_SCANCODE_SET, 0) == KBD_ACK) codeset = read_kbd_data(kbdc); } if (bootverbose) printf("atkbd: scancode set %d\n", codeset); #endif /* KBD_DETECT_XT_KEYBOARD */ *type = KB_OTHER; id = get_kbd_id(kbdc); switch(id) { case 0x41ab: case 0x83ab: *type = KB_101; break; case -1: /* AT 84 keyboard doesn't return ID */ *type = KB_84; break; default: break; } if (bootverbose) printf("atkbd: keyboard ID 0x%x (%d)\n", id, *type); /* reset keyboard hardware */ if (!(flags & KB_CONF_NO_RESET) && !reset_kbd(kbdc)) { /* * KEYBOARD ERROR * Keyboard reset may fail either because the keyboard * doen't exist, or because the keyboard doesn't pass * the self-test, or the keyboard controller on the * motherboard and the keyboard somehow fail to shake hands. * It is just possible, particularly in the last case, * that the keyoard controller may be left in a hung state. * test_controller() and test_kbd_port() appear to bring * the keyboard controller back (I don't know why and how, * though.) */ empty_both_buffers(kbdc, 10); test_controller(kbdc); test_kbd_port(kbdc); /* * We could disable the keyboard port and interrupt... but, * the keyboard may still exist (see above). */ set_controller_command_byte(kbdc, 0xff, c); kbdc_lock(kbdc, FALSE); if (bootverbose) printf("atkbd: failed to reset the keyboard.\n"); return EIO; } /* * Allow us to set the XT_KEYBD flag in UserConfig so that keyboards * such as those on the IBM ThinkPad laptop computers can be used * with the standard console driver. */ if (codeset == 1) { if (send_kbd_command_and_data(kbdc, KBDC_SET_SCANCODE_SET, codeset) == KBD_ACK) { /* XT kbd doesn't need scan code translation */ c &= ~KBD_TRANSLATION; } else { /* * KEYBOARD ERROR * The XT kbd isn't usable unless the proper scan * code set is selected. */ set_controller_command_byte(kbdc, 0xff, c); kbdc_lock(kbdc, FALSE); printf("atkbd: unable to set the XT keyboard mode.\n"); return EIO; } } #ifdef __alpha__ if (send_kbd_command_and_data( kbdc, KBDC_SET_SCANCODE_SET, 2) != KBD_ACK) { printf("atkbd: can't set translation.\n"); } c |= KBD_TRANSLATION; #endif /* enable the keyboard port and intr. */ if (!set_controller_command_byte(kbdc, KBD_KBD_CONTROL_BITS | KBD_TRANSLATION | KBD_OVERRIDE_KBD_LOCK, (c & (KBD_TRANSLATION | KBD_OVERRIDE_KBD_LOCK)) | KBD_ENABLE_KBD_PORT | KBD_ENABLE_KBD_INT)) { /* * CONTROLLER ERROR * This is serious; we are left with the disabled * keyboard intr. */ set_controller_command_byte(kbdc, 0xff, c); kbdc_lock(kbdc, FALSE); printf("atkbd: unable to enable the keyboard port and intr.\n"); return EIO; } kbdc_lock(kbdc, FALSE); return 0; } static int write_kbd(KBDC kbdc, int command, int data) { int s; /* prevent the timeout routine from polling the keyboard */ if (!kbdc_lock(kbdc, TRUE)) return EBUSY; /* disable the keyboard and mouse interrupt */ s = spltty(); #if 0 c = get_controller_command_byte(kbdc); if ((c == -1) || !set_controller_command_byte(kbdc, kbdc_get_device_mask(kbdc), KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT | KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { /* CONTROLLER ERROR */ kbdc_lock(kbdc, FALSE); splx(s); return EIO; } /* * Now that the keyboard controller is told not to generate * the keyboard and mouse interrupts, call `splx()' to allow * the other tty interrupts. The clock interrupt may also occur, * but the timeout routine (`scrn_timer()') will be blocked * by the lock flag set via `kbdc_lock()' */ splx(s); #endif if (send_kbd_command_and_data(kbdc, command, data) != KBD_ACK) send_kbd_command(kbdc, KBDC_ENABLE_KBD); #if 0 /* restore the interrupts */ if (!set_controller_command_byte(kbdc, kbdc_get_device_mask(kbdc), c & (KBD_KBD_CONTROL_BITS | KBD_AUX_CONTROL_BITS))) { /* CONTROLLER ERROR */ } #else splx(s); #endif kbdc_lock(kbdc, FALSE); return 0; } static int get_kbd_id(KBDC kbdc) { int id1, id2; empty_both_buffers(kbdc, 10); id1 = id2 = -1; if (send_kbd_command(kbdc, KBDC_SEND_DEV_ID) != KBD_ACK) return -1; DELAY(10000); /* 10 msec delay */ id1 = read_kbd_data(kbdc); if (id1 != -1) id2 = read_kbd_data(kbdc); if ((id1 == -1) || (id2 == -1)) { empty_both_buffers(kbdc, 10); test_controller(kbdc); test_kbd_port(kbdc); return -1; } return ((id2 << 8) | id1); } static int typematic(int delay, int rate) { static int delays[] = { 250, 500, 750, 1000 }; static int rates[] = { 34, 38, 42, 46, 50, 55, 59, 63, 68, 76, 84, 92, 100, 110, 118, 126, 136, 152, 168, 184, 200, 220, 236, 252, 272, 304, 336, 368, 400, 440, 472, 504 }; int value; int i; for (i = sizeof(delays)/sizeof(delays[0]) - 1; i > 0; --i) { if (delay >= delays[i]) break; } value = i << 5; for (i = sizeof(rates)/sizeof(rates[0]) - 1; i > 0; --i) { if (rate >= rates[i]) break; } value |= i; return value; } #endif /* NATKBD > 0 */ Index: head/sys/dev/pci/pci.c =================================================================== --- head/sys/dev/pci/pci.c (revision 45719) +++ head/sys/dev/pci/pci.c (revision 45720) @@ -1,948 +1,1377 @@ /* * Copyright (c) 1997, Stefan Esser * 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 unmodified, 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 ``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. * - * $Id: pci.c,v 1.93 1999/01/19 23:29:18 se Exp $ + * $Id: pci.c,v 1.94 1999/04/11 02:47:31 eivind Exp $ * */ +#include "opt_bus.h" + #include "pci.h" #if NPCI > 0 #include "opt_devfs.h" #include "opt_simos.h" #include #include #include #include #include #include #include #include #include #ifdef DEVFS #include #endif /* DEVFS */ #include #include #include +#include +#include +#include +#include + #include #include #include #ifdef APIC_IO #include #endif /* APIC_IO */ static STAILQ_HEAD(devlist, pci_devinfo) pci_devq; u_int32_t pci_numdevs = 0; static u_int32_t pci_generation = 0; /* return highest PCI bus number known to be used, or -1 if none */ static int pci_bushigh(void) { if (pci_cfgopen() == 0) return (-1); return (0); } /* return base address of memory or port map */ static int pci_mapbase(unsigned mapreg) { int mask = 0x03; if ((mapreg & 0x01) == 0) mask = 0x0f; return (mapreg & ~mask); } /* return map type of memory or port map */ static int pci_maptype(unsigned mapreg) { static u_int8_t maptype[0x10] = { PCI_MAPMEM, PCI_MAPPORT, PCI_MAPMEM, 0, PCI_MAPMEM, PCI_MAPPORT, 0, 0, PCI_MAPMEM|PCI_MAPMEMP, PCI_MAPPORT, PCI_MAPMEM|PCI_MAPMEMP, 0, PCI_MAPMEM|PCI_MAPMEMP, PCI_MAPPORT, 0, 0, }; return maptype[mapreg & 0x0f]; } /* return log2 of map size decoded for memory or port map */ static int pci_mapsize(unsigned testval) { int ln2size; testval = pci_mapbase(testval); ln2size = 0; if (testval != 0) { while ((testval & 1) == 0) { ln2size++; testval >>= 1; } } return (ln2size); } /* return log2 of address range supported by map register */ static int pci_maprange(unsigned mapreg) { int ln2range = 0; switch (mapreg & 0x07) { case 0x00: case 0x01: case 0x05: ln2range = 32; break; case 0x02: ln2range = 20; break; case 0x04: ln2range = 64; break; } return (ln2range); } /* extract map parameters into newly allocated array of pcimap structures */ static pcimap * pci_readmaps(pcicfgregs *cfg, int maxmaps) { int i, j = 0; pcimap *map; int map64 = 0; int reg = PCIR_MAPS; for (i = 0; i < maxmaps; i++) { int reg = PCIR_MAPS + i*4; u_int32_t base; u_int32_t ln2range; base = pci_cfgread(cfg, reg, 4); ln2range = pci_maprange(base); if (base == 0 || ln2range == 0 || base == 0xffffffff) continue; /* skip invalid entry */ else { j++; if (ln2range > 32) { i++; j++; } } } map = malloc(j * sizeof (pcimap), M_DEVBUF, M_WAITOK); if (map != NULL) { bzero(map, sizeof(pcimap) * j); cfg->nummaps = j; for (i = 0, j = 0; i < maxmaps; i++, reg += 4) { u_int32_t base; u_int32_t testval; base = pci_cfgread(cfg, reg, 4); if (map64 == 0) { if (base == 0 || base == 0xffffffff) continue; /* skip invalid entry */ pci_cfgwrite(cfg, reg, 0xffffffff, 4); testval = pci_cfgread(cfg, reg, 4); pci_cfgwrite(cfg, reg, base, 4); map[j].reg = reg; map[j].base = pci_mapbase(base); map[j].type = pci_maptype(base); map[j].ln2size = pci_mapsize(testval); map[j].ln2range = pci_maprange(testval); map64 = map[j].ln2range == 64; } else { /* only fill in base, other fields are 0 */ map[j].base = base; map64 = 0; } j++; } } return (map); } /* adjust some values from PCI 1.0 devices to match 2.0 standards ... */ static void pci_fixancient(pcicfgregs *cfg) { if (cfg->hdrtype != 0) return; /* PCI to PCI bridges use header type 1 */ if (cfg->baseclass == PCIC_BRIDGE && cfg->subclass == PCIS_BRIDGE_PCI) cfg->hdrtype = 1; } /* read config data specific to header type 1 device (PCI to PCI bridge) */ static void * pci_readppb(pcicfgregs *cfg) { pcih1cfgregs *p; p = malloc(sizeof (pcih1cfgregs), M_DEVBUF, M_WAITOK); if (p == NULL) return (NULL); bzero(p, sizeof *p); p->secstat = pci_cfgread(cfg, PCIR_SECSTAT_1, 2); p->bridgectl = pci_cfgread(cfg, PCIR_BRIDGECTL_1, 2); p->seclat = pci_cfgread(cfg, PCIR_SECLAT_1, 1); p->iobase = PCI_PPBIOBASE (pci_cfgread(cfg, PCIR_IOBASEH_1, 2), pci_cfgread(cfg, PCIR_IOBASEL_1, 1)); p->iolimit = PCI_PPBIOLIMIT (pci_cfgread(cfg, PCIR_IOLIMITH_1, 2), pci_cfgread(cfg, PCIR_IOLIMITL_1, 1)); p->membase = PCI_PPBMEMBASE (0, pci_cfgread(cfg, PCIR_MEMBASE_1, 2)); p->memlimit = PCI_PPBMEMLIMIT (0, pci_cfgread(cfg, PCIR_MEMLIMIT_1, 2)); p->pmembase = PCI_PPBMEMBASE ( (pci_addr_t)pci_cfgread(cfg, PCIR_PMBASEH_1, 4), pci_cfgread(cfg, PCIR_PMBASEL_1, 2)); p->pmemlimit = PCI_PPBMEMLIMIT ( (pci_addr_t)pci_cfgread(cfg, PCIR_PMLIMITH_1, 4), pci_cfgread(cfg, PCIR_PMLIMITL_1, 2)); return (p); } /* read config data specific to header type 2 device (PCI to CardBus bridge) */ static void * pci_readpcb(pcicfgregs *cfg) { pcih2cfgregs *p; p = malloc(sizeof (pcih2cfgregs), M_DEVBUF, M_WAITOK); if (p == NULL) return (NULL); bzero(p, sizeof *p); p->secstat = pci_cfgread(cfg, PCIR_SECSTAT_2, 2); p->bridgectl = pci_cfgread(cfg, PCIR_BRIDGECTL_2, 2); p->seclat = pci_cfgread(cfg, PCIR_SECLAT_2, 1); p->membase0 = pci_cfgread(cfg, PCIR_MEMBASE0_2, 4); p->memlimit0 = pci_cfgread(cfg, PCIR_MEMLIMIT0_2, 4); p->membase1 = pci_cfgread(cfg, PCIR_MEMBASE1_2, 4); p->memlimit1 = pci_cfgread(cfg, PCIR_MEMLIMIT1_2, 4); p->iobase0 = pci_cfgread(cfg, PCIR_IOBASE0_2, 4); p->iolimit0 = pci_cfgread(cfg, PCIR_IOLIMIT0_2, 4); p->iobase1 = pci_cfgread(cfg, PCIR_IOBASE1_2, 4); p->iolimit1 = pci_cfgread(cfg, PCIR_IOLIMIT1_2, 4); p->pccardif = pci_cfgread(cfg, PCIR_PCCARDIF_2, 4); return p; } /* extract header type specific config data */ static void pci_hdrtypedata(pcicfgregs *cfg) { switch (cfg->hdrtype) { case 0: cfg->subvendor = pci_cfgread(cfg, PCIR_SUBVEND_0, 2); cfg->subdevice = pci_cfgread(cfg, PCIR_SUBDEV_0, 2); cfg->map = pci_readmaps(cfg, PCI_MAXMAPS_0); break; case 1: cfg->subvendor = pci_cfgread(cfg, PCIR_SUBVEND_1, 2); cfg->subdevice = pci_cfgread(cfg, PCIR_SUBDEV_1, 2); cfg->secondarybus = pci_cfgread(cfg, PCIR_SECBUS_1, 1); cfg->subordinatebus = pci_cfgread(cfg, PCIR_SUBBUS_1, 1); cfg->map = pci_readmaps(cfg, PCI_MAXMAPS_1); cfg->hdrspec = pci_readppb(cfg); break; case 2: cfg->subvendor = pci_cfgread(cfg, PCIR_SUBVEND_2, 2); cfg->subdevice = pci_cfgread(cfg, PCIR_SUBDEV_2, 2); cfg->secondarybus = pci_cfgread(cfg, PCIR_SECBUS_2, 1); cfg->subordinatebus = pci_cfgread(cfg, PCIR_SUBBUS_2, 1); cfg->map = pci_readmaps(cfg, PCI_MAXMAPS_2); cfg->hdrspec = pci_readpcb(cfg); break; } } /* read configuration header into pcicfgrect structure */ static struct pci_devinfo * pci_readcfg(pcicfgregs *probe) { pcicfgregs *cfg = NULL; struct pci_devinfo *devlist_entry; struct devlist *devlist_head; devlist_head = &pci_devq; devlist_entry = NULL; if (pci_cfgread(probe, PCIR_DEVVENDOR, 4) != -1) { devlist_entry = malloc(sizeof(struct pci_devinfo), M_DEVBUF, M_WAITOK); if (devlist_entry == NULL) return (NULL); + bzero(devlist_entry, sizeof *devlist_entry); cfg = &devlist_entry->cfg; - bzero(cfg, sizeof *cfg); - cfg->bus = probe->bus; cfg->slot = probe->slot; cfg->func = probe->func; cfg->vendor = pci_cfgread(cfg, PCIR_VENDOR, 2); cfg->device = pci_cfgread(cfg, PCIR_DEVICE, 2); cfg->cmdreg = pci_cfgread(cfg, PCIR_COMMAND, 2); cfg->statreg = pci_cfgread(cfg, PCIR_STATUS, 2); cfg->baseclass = pci_cfgread(cfg, PCIR_CLASS, 1); cfg->subclass = pci_cfgread(cfg, PCIR_SUBCLASS, 1); cfg->progif = pci_cfgread(cfg, PCIR_PROGIF, 1); cfg->revid = pci_cfgread(cfg, PCIR_REVID, 1); cfg->hdrtype = pci_cfgread(cfg, PCIR_HEADERTYPE, 1); cfg->cachelnsz = pci_cfgread(cfg, PCIR_CACHELNSZ, 1); cfg->lattimer = pci_cfgread(cfg, PCIR_LATTIMER, 1); cfg->intpin = pci_cfgread(cfg, PCIR_INTPIN, 1); cfg->intline = pci_cfgread(cfg, PCIR_INTLINE, 1); #ifdef __alpha__ alpha_platform_assign_pciintr(cfg); #endif #ifdef APIC_IO if (cfg->intpin != 0) { int airq; airq = pci_apic_irq(cfg->bus, cfg->slot, cfg->intpin); if (airq >= 0) { /* PCI specific entry found in MP table */ if (airq != cfg->intline) { undirect_pci_irq(cfg->intline); cfg->intline = airq; } } else { /* * PCI interrupts might be redirected to the * ISA bus according to some MP tables. Use the * same methods as used by the ISA devices * devices to find the proper IOAPIC int pin. */ airq = isa_apic_irq(cfg->intline); if ((airq >= 0) && (airq != cfg->intline)) { /* XXX: undirect_pci_irq() ? */ undirect_isa_irq(cfg->intline); cfg->intline = airq; } } } #endif /* APIC_IO */ cfg->mingnt = pci_cfgread(cfg, PCIR_MINGNT, 1); cfg->maxlat = pci_cfgread(cfg, PCIR_MAXLAT, 1); cfg->mfdev = (cfg->hdrtype & PCIM_MFDEV) != 0; cfg->hdrtype &= ~PCIM_MFDEV; pci_fixancient(cfg); pci_hdrtypedata(cfg); STAILQ_INSERT_TAIL(devlist_head, devlist_entry, pci_links); devlist_entry->conf.pc_sel.pc_bus = cfg->bus; devlist_entry->conf.pc_sel.pc_dev = cfg->slot; devlist_entry->conf.pc_sel.pc_func = cfg->func; devlist_entry->conf.pc_hdr = cfg->hdrtype; devlist_entry->conf.pc_subvendor = cfg->subvendor; devlist_entry->conf.pc_subdevice = cfg->subdevice; devlist_entry->conf.pc_vendor = cfg->vendor; devlist_entry->conf.pc_device = cfg->device; devlist_entry->conf.pc_class = cfg->baseclass; devlist_entry->conf.pc_subclass = cfg->subclass; devlist_entry->conf.pc_progif = cfg->progif; devlist_entry->conf.pc_revid = cfg->revid; pci_numdevs++; pci_generation++; } return (devlist_entry); } #if 0 /* free pcicfgregs structure and all depending data structures */ static int pci_freecfg(struct pci_devinfo *dinfo) { struct devlist *devlist_head; devlist_head = &pci_devq; if (dinfo->cfg.hdrspec != NULL) free(dinfo->cfg.hdrspec, M_DEVBUF); if (dinfo->cfg.map != NULL) free(dinfo->cfg.map, M_DEVBUF); /* XXX this hasn't been tested */ STAILQ_REMOVE(devlist_head, dinfo, pci_devinfo, pci_links); free(dinfo, M_DEVBUF); /* increment the generation count */ pci_generation++; /* we're losing one device */ pci_numdevs--; return (0); } #endif -static void -pci_addcfg(struct pci_devinfo *dinfo) -{ - if (bootverbose) { - int i; - pcicfgregs *cfg = &dinfo->cfg; - - printf("found->\tvendor=0x%04x, dev=0x%04x, revid=0x%02x\n", - cfg->vendor, cfg->device, cfg->revid); - printf("\tclass=%02x-%02x-%02x, hdrtype=0x%02x, mfdev=%d\n", - cfg->baseclass, cfg->subclass, cfg->progif, - cfg->hdrtype, cfg->mfdev); - printf("\tsubordinatebus=%x \tsecondarybus=%x\n", - cfg->subordinatebus, cfg->secondarybus); -#ifdef PCI_DEBUG - printf("\tcmdreg=0x%04x, statreg=0x%04x, cachelnsz=%d (dwords)\n", - cfg->cmdreg, cfg->statreg, cfg->cachelnsz); - printf("\tlattimer=0x%02x (%d ns), mingnt=0x%02x (%d ns), maxlat=0x%02x (%d ns)\n", - cfg->lattimer, cfg->lattimer * 30, - cfg->mingnt, cfg->mingnt * 250, cfg->maxlat, cfg->maxlat * 250); -#endif /* PCI_DEBUG */ - if (cfg->intpin > 0) - printf("\tintpin=%c, irq=%d\n", cfg->intpin +'a' -1, cfg->intline); - - for (i = 0; i < cfg->nummaps; i++) { - pcimap *m = &cfg->map[i]; - printf("\tmap[%d]: type %x, range %2d, base %08x, size %2d\n", - i, m->type, m->ln2range, m->base, m->ln2size); - } - } - pci_drvattach(dinfo); /* XXX currently defined in pci_compat.c */ -} - -/* scan one PCI bus for devices */ - -static int -pci_probebus(int bus) -{ - pcicfgregs probe; - int bushigh = bus; - -#ifdef SIMOS -#undef PCI_SLOTMAX -#define PCI_SLOTMAX 0 -#endif - - bzero(&probe, sizeof probe); - /* XXX KDM */ - /* probe.parent = pci_bridgeto(bus); */ - probe.bus = bus; - for (probe.slot = 0; probe.slot <= PCI_SLOTMAX; probe.slot++) { - int pcifunchigh = 0; - for (probe.func = 0; probe.func <= pcifunchigh; probe.func++) { - struct pci_devinfo *dinfo = pci_readcfg(&probe); - if (dinfo != NULL) { - if (dinfo->cfg.mfdev) - pcifunchigh = 7; - /* - * XXX: Temporarily move pci_addcfg() up before - * the use of cfg->subordinatebus. This is - * necessary, since pci_addcfg() calls the - * device's probe(), which may read the bus# - * from some device dependent register of - * some host to PCI bridges. The probe will - * eventually be moved to pci_readcfg(), and - * pci_addcfg() will then be moved back down - * below the conditional statement ... - */ - pci_addcfg(dinfo); - - if (bushigh < dinfo->cfg.subordinatebus) - bushigh = dinfo->cfg.subordinatebus; - if (bushigh < dinfo->cfg.secondarybus) - bushigh = dinfo->cfg.secondarybus; - - /* XXX KDM */ - /* cfg = NULL; we don't own this anymore ... */ - } - } - } - return (bushigh); -} - -/* scan a PCI bus tree reached through one PCI attachment point */ - -int -pci_probe(pciattach *parent) -{ - int bushigh; - int bus = 0; - - STAILQ_INIT(&pci_devq); - - bushigh = pci_bushigh(); - while (bus <= bushigh) { - int newbushigh; - - printf("Probing for devices on PCI bus %d:\n", bus); - newbushigh = pci_probebus(bus); - - if (bushigh < newbushigh) - bushigh = newbushigh; - bus++; - } - return (bushigh); -} - /* * This is the user interface to PCI configuration space. */ static int pci_open(dev_t dev, int oflags, int devtype, struct proc *p) { if ((oflags & FWRITE) && securelevel > 0) { return EPERM; } return 0; } static int pci_close(dev_t dev, int flag, int devtype, struct proc *p) { return 0; } /* * Match a single pci_conf structure against an array of pci_match_conf * structures. The first argument, 'matches', is an array of num_matches * pci_match_conf structures. match_buf is a pointer to the pci_conf * structure that will be compared to every entry in the matches array. * This function returns 1 on failure, 0 on success. */ static int pci_conf_match(struct pci_match_conf *matches, int num_matches, struct pci_conf *match_buf) { int i; if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0)) return(1); for (i = 0; i < num_matches; i++) { /* * I'm not sure why someone would do this...but... */ if (matches[i].flags == PCI_GETCONF_NO_MATCH) continue; /* * Look at each of the match flags. If it's set, do the * comparison. If the comparison fails, we don't have a * match, go on to the next item if there is one. */ if (((matches[i].flags & PCI_GETCONF_MATCH_BUS) != 0) && (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus)) continue; if (((matches[i].flags & PCI_GETCONF_MATCH_DEV) != 0) && (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev)) continue; if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC) != 0) && (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func)) continue; if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR) != 0) && (match_buf->pc_vendor != matches[i].pc_vendor)) continue; if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE) != 0) && (match_buf->pc_device != matches[i].pc_device)) continue; if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS) != 0) && (match_buf->pc_class != matches[i].pc_class)) continue; if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT) != 0) && (match_buf->pd_unit != matches[i].pd_unit)) continue; if (((matches[i].flags & PCI_GETCONF_MATCH_NAME) != 0) && (strncmp(matches[i].pd_name, match_buf->pd_name, sizeof(match_buf->pd_name)) != 0)) continue; return(0); } return(1); } static int pci_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) { struct pci_io *io; int error; if (!(flag & FWRITE)) return EPERM; switch(cmd) { case PCIOCGETCONF: { struct pci_devinfo *dinfo; struct pci_conf_io *cio; struct devlist *devlist_head; struct pci_match_conf *pattern_buf; int num_patterns; size_t iolen; int ionum, i; cio = (struct pci_conf_io *)data; num_patterns = 0; dinfo = NULL; /* * Hopefully the user won't pass in a null pointer, but it * can't hurt to check. */ if (cio == NULL) { error = EINVAL; break; } /* * If the user specified an offset into the device list, * but the list has changed since they last called this * ioctl, tell them that the list has changed. They will * have to get the list from the beginning. */ if ((cio->offset != 0) && (cio->generation != pci_generation)){ cio->num_matches = 0; cio->status = PCI_GETCONF_LIST_CHANGED; error = 0; break; } /* * Check to see whether the user has asked for an offset * past the end of our list. */ if (cio->offset >= pci_numdevs) { cio->num_matches = 0; cio->status = PCI_GETCONF_LAST_DEVICE; error = 0; break; } /* get the head of the device queue */ devlist_head = &pci_devq; /* * Determine how much room we have for pci_conf structures. * Round the user's buffer size down to the nearest * multiple of sizeof(struct pci_conf) in case the user * didn't specify a multiple of that size. */ iolen = min(cio->match_buf_len - (cio->match_buf_len % sizeof(struct pci_conf)), pci_numdevs * sizeof(struct pci_conf)); /* * Since we know that iolen is a multiple of the size of * the pciconf union, it's okay to do this. */ ionum = iolen / sizeof(struct pci_conf); /* * If this test is true, the user wants the pci_conf * structures returned to match the supplied entries. */ if ((cio->num_patterns > 0) && (cio->pat_buf_len > 0)) { /* * pat_buf_len needs to be: * num_patterns * sizeof(struct pci_match_conf) * While it is certainly possible the user just * allocated a large buffer, but set the number of * matches correctly, it is far more likely that * their kernel doesn't match the userland utility * they're using. It's also possible that the user * forgot to initialize some variables. Yes, this * may be overly picky, but I hazard to guess that * it's far more likely to just catch folks that * updated their kernel but not their userland. */ if ((cio->num_patterns * sizeof(struct pci_match_conf)) != cio->pat_buf_len){ /* The user made a mistake, return an error*/ cio->status = PCI_GETCONF_ERROR; printf("pci_ioctl: pat_buf_len %d != " "num_patterns (%d) * sizeof(struct " "pci_match_conf) (%d)\npci_ioctl: " "pat_buf_len should be = %d\n", cio->pat_buf_len, cio->num_patterns, - sizeof(struct pci_match_conf), - sizeof(struct pci_match_conf) * + (int)sizeof(struct pci_match_conf), + (int)sizeof(struct pci_match_conf) * cio->num_patterns); printf("pci_ioctl: do your headers match your " "kernel?\n"); cio->num_matches = 0; error = EINVAL; break; } /* * Check the user's buffer to make sure it's readable. */ if ((error = useracc((caddr_t)cio->patterns, cio->pat_buf_len, B_READ)) != 1){ printf("pci_ioctl: pattern buffer %p, " "length %u isn't user accessible for" " READ\n", cio->patterns, cio->pat_buf_len); error = EACCES; break; } /* * Allocate a buffer to hold the patterns. */ pattern_buf = malloc(cio->pat_buf_len, M_TEMP, M_WAITOK); error = copyin(cio->patterns, pattern_buf, cio->pat_buf_len); if (error != 0) break; num_patterns = cio->num_patterns; } else if ((cio->num_patterns > 0) || (cio->pat_buf_len > 0)) { /* * The user made a mistake, spit out an error. */ cio->status = PCI_GETCONF_ERROR; cio->num_matches = 0; printf("pci_ioctl: invalid GETCONF arguments\n"); error = EINVAL; break; } else pattern_buf = NULL; /* * Make sure we can write to the match buffer. */ if ((error = useracc((caddr_t)cio->matches, cio->match_buf_len, B_WRITE)) != 1) { printf("pci_ioctl: match buffer %p, length %u " "isn't user accessible for WRITE\n", cio->matches, cio->match_buf_len); error = EACCES; break; } /* * Go through the list of devices and copy out the devices * that match the user's criteria. */ for (cio->num_matches = 0, error = 0, i = 0, dinfo = STAILQ_FIRST(devlist_head); (dinfo != NULL) && (cio->num_matches < ionum) && (error == 0) && (i < pci_numdevs); dinfo = STAILQ_NEXT(dinfo, pci_links), i++) { if (i < cio->offset) continue; if ((pattern_buf == NULL) || (pci_conf_match(pattern_buf, num_patterns, &dinfo->conf) == 0)) { /* * If we've filled up the user's buffer, * break out at this point. Since we've * got a match here, we'll pick right back * up at the matching entry. We can also * tell the user that there are more matches * left. */ if (cio->num_matches >= ionum) break; error = copyout(&dinfo->conf, &cio->matches[cio->num_matches], sizeof(struct pci_conf)); cio->num_matches++; } } /* * Set the pointer into the list, so if the user is getting * n records at a time, where n < pci_numdevs, */ cio->offset = i; /* * Set the generation, the user will need this if they make * another ioctl call with offset != 0. */ cio->generation = pci_generation; /* * If this is the last device, inform the user so he won't * bother asking for more devices. If dinfo isn't NULL, we * know that there are more matches in the list because of * the way the traversal is done. */ if (dinfo == NULL) cio->status = PCI_GETCONF_LAST_DEVICE; else cio->status = PCI_GETCONF_MORE_DEVS; if (pattern_buf != NULL) free(pattern_buf, M_TEMP); break; } case PCIOCREAD: io = (struct pci_io *)data; switch(io->pi_width) { pcicfgregs probe; case 4: case 2: case 1: probe.bus = io->pi_sel.pc_bus; probe.slot = io->pi_sel.pc_dev; probe.func = io->pi_sel.pc_func; io->pi_data = pci_cfgread(&probe, io->pi_reg, io->pi_width); error = 0; break; default: error = ENODEV; break; } break; case PCIOCWRITE: io = (struct pci_io *)data; switch(io->pi_width) { pcicfgregs probe; case 4: case 2: case 1: probe.bus = io->pi_sel.pc_bus; probe.slot = io->pi_sel.pc_dev; probe.func = io->pi_sel.pc_func; pci_cfgwrite(&probe, io->pi_reg, io->pi_data, io->pi_width); error = 0; break; default: error = ENODEV; break; } break; default: error = ENOTTY; break; } return (error); } #define PCI_CDEV 78 static struct cdevsw pcicdev = { pci_open, pci_close, noread, nowrite, pci_ioctl, nostop, noreset, nodevtotty, seltrue, nommap, nostrategy, "pci", 0, PCI_CDEV }; #ifdef DEVFS static void *pci_devfs_token; #endif static void pci_cdevinit(void *dummy) { dev_t dev; dev = makedev(PCI_CDEV, 0); cdevsw_add(&dev, &pcicdev, NULL); #ifdef DEVFS pci_devfs_token = devfs_add_devswf(&pcicdev, 0, DV_CHR, UID_ROOT, GID_WHEEL, 0644, "pci"); #endif } SYSINIT(pcidev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE+PCI_CDEV, pci_cdevinit, NULL); + +#include "pci_if.h" + +/* + * A simple driver to wrap the old pci driver mechanism for back-compat. + */ + +static int +pci_compat_probe(device_t dev) +{ + struct pci_device *dvp; + struct pci_devinfo *dinfo; + pcicfgregs *cfg; + const char *name; + int error; + + dinfo = device_get_ivars(dev); + cfg = &dinfo->cfg; + dvp = device_get_driver(dev)->priv; + + /* + * Do the wrapped probe. + */ + error = ENXIO; + if (dvp && dvp->pd_probe) { + name = dvp->pd_probe(cfg, (cfg->device << 16) + cfg->vendor); + if (name) { + device_set_desc_copy(dev, name); + error = 0; + } + } + + return error; +} + +static int +pci_compat_attach(device_t dev) +{ + struct pci_device *dvp; + struct pci_devinfo *dinfo; + pcicfgregs *cfg; + int unit; + + dinfo = device_get_ivars(dev); + cfg = &dinfo->cfg; + dvp = device_get_driver(dev)->priv; + + unit = device_get_unit(dev); + if (unit > *dvp->pd_count) + *dvp->pd_count = unit; + if (dvp->pd_attach) + dvp->pd_attach(cfg, unit); + + /* + * XXX KDM for some devices, dvp->pd_name winds up NULL. + * I haven't investigated enough to figure out why this + * would happen. + */ + if (dvp->pd_name != NULL) + strncpy(dinfo->conf.pd_name, dvp->pd_name, + sizeof(dinfo->conf.pd_name)); + else + strncpy(dinfo->conf.pd_name, "????", + sizeof(dinfo->conf.pd_name)); + dinfo->conf.pd_name[sizeof(dinfo->conf.pd_name) - 1] = 0; + dinfo->conf.pd_unit = unit; + + return 0; +} + +static device_method_t pci_compat_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, pci_compat_probe), + DEVMETHOD(device_attach, pci_compat_attach), + + { 0, 0 } +}; + +static devclass_t pci_devclass; + +/* + * Create a new style driver around each old pci driver. + */ +static void +pci_wrap_old_drivers(void) +{ + struct pci_device **dvpp, *dvp; + + dvpp = (struct pci_device **)pcidevice_set.ls_items; + while ((dvp = *dvpp++) != NULL) { + driver_t *driver; + driver = malloc(sizeof(driver_t), M_DEVBUF, M_NOWAIT); + if (!driver) + continue; + bzero(driver, sizeof(driver_t)); + driver->name = dvp->pd_name; + driver->methods = pci_compat_methods; + driver->type = 0; /* XXX fixup in pci_map_int() */ + driver->softc = sizeof(struct pci_devinfo *); + driver->priv = dvp; + devclass_add_driver(pci_devclass, driver); + } +} + +/* + * New style pci driver. Parent device is either a pci-host-bridge or a + * pci-pci-bridge. Both kinds are represented by instances of pcib. + */ + +static void +pci_print_verbose(struct pci_devinfo *dinfo) +{ + if (bootverbose) { + int i; + pcicfgregs *cfg = &dinfo->cfg; + + printf("found->\tvendor=0x%04x, dev=0x%04x, revid=0x%02x\n", + cfg->vendor, cfg->device, cfg->revid); + printf("\tclass=%02x-%02x-%02x, hdrtype=0x%02x, mfdev=%d\n", + cfg->baseclass, cfg->subclass, cfg->progif, + cfg->hdrtype, cfg->mfdev); + printf("\tsubordinatebus=%x \tsecondarybus=%x\n", + cfg->subordinatebus, cfg->secondarybus); +#ifdef PCI_DEBUG + printf("\tcmdreg=0x%04x, statreg=0x%04x, cachelnsz=%d (dwords)\n", + cfg->cmdreg, cfg->statreg, cfg->cachelnsz); + printf("\tlattimer=0x%02x (%d ns), mingnt=0x%02x (%d ns), maxlat=0x%02x (%d ns)\n", + cfg->lattimer, cfg->lattimer * 30, + cfg->mingnt, cfg->mingnt * 250, cfg->maxlat, cfg->maxlat * 250); +#endif /* PCI_DEBUG */ + if (cfg->intpin > 0) + printf("\tintpin=%c, irq=%d\n", cfg->intpin +'a' -1, cfg->intline); + + for (i = 0; i < cfg->nummaps; i++) { + pcimap *m = &cfg->map[i]; + printf("\tmap[%d]: type %x, range %2d, base %08x, size %2d\n", + i, m->type, m->ln2range, m->base, m->ln2size); + } + } +} + +static int +pci_add_children(device_t dev, int busno) +{ + pcicfgregs probe; + int bushigh = busno; + +#ifdef SIMOS +#undef PCI_SLOTMAX +#define PCI_SLOTMAX 0 +#endif + + bzero(&probe, sizeof probe); + /* XXX KDM */ + /* probe.parent = pci_bridgeto(bus); */ + probe.bus = busno; + for (probe.slot = 0; probe.slot <= PCI_SLOTMAX; probe.slot++) { + int pcifunchigh = 0; + for (probe.func = 0; probe.func <= pcifunchigh; probe.func++) { + struct pci_devinfo *dinfo = pci_readcfg(&probe); + if (dinfo != NULL) { + if (dinfo->cfg.mfdev) + pcifunchigh = 7; + + pci_print_verbose(dinfo); + dinfo->cfg.dev = + device_add_child(dev, NULL, -1, dinfo); + + if (bushigh < dinfo->cfg.subordinatebus) + bushigh = dinfo->cfg.subordinatebus; + if (bushigh < dinfo->cfg.secondarybus) + bushigh = dinfo->cfg.secondarybus; + } + } + } + + return bushigh; +} + +static int +pci_new_probe(device_t dev) +{ + STAILQ_INIT(&pci_devq); + device_set_desc(dev, "PCI bus"); + + pci_add_children(dev, device_get_unit(dev)); + + return 0; +} + +static void +pci_print_child(device_t dev, device_t child) +{ + printf(" at device %d.%d", pci_get_slot(child), pci_get_function(child)); + printf(" on %s%d", device_get_name(dev), device_get_unit(dev)); +} + +static int +pci_read_ivar(device_t dev, device_t child, int which, u_long *result) +{ + struct pci_devinfo *dinfo; + pcicfgregs *cfg; + + dinfo = device_get_ivars(child); + cfg = &dinfo->cfg; + + switch (which) { + case PCI_IVAR_SUBVENDOR: + *result = cfg->subvendor; + break; + case PCI_IVAR_SUBDEVICE: + *result = cfg->subdevice; + break; + case PCI_IVAR_VENDOR: + *result = cfg->vendor; + break; + case PCI_IVAR_DEVICE: + *result = cfg->device; + break; + case PCI_IVAR_DEVID: + *result = (cfg->device << 16) | cfg->vendor; + break; + case PCI_IVAR_CLASS: + *result = cfg->baseclass; + break; + case PCI_IVAR_SUBCLASS: + *result = cfg->subclass; + break; + case PCI_IVAR_PROGIF: + *result = cfg->progif; + break; + case PCI_IVAR_REVID: + *result = cfg->revid; + break; + case PCI_IVAR_INTPIN: + *result = cfg->intpin; + break; + case PCI_IVAR_IRQ: + *result = cfg->intline; + break; + case PCI_IVAR_BUS: + *result = cfg->bus; + break; + case PCI_IVAR_SLOT: + *result = cfg->slot; + break; + case PCI_IVAR_FUNCTION: + *result = cfg->func; + break; + case PCI_IVAR_SECONDARYBUS: + *result = cfg->secondarybus; + break; + case PCI_IVAR_SUBORDINATEBUS: + *result = cfg->subordinatebus; + break; + default: + return ENOENT; + } + return 0; +} + +static int +pci_write_ivar(device_t dev, device_t child, int which, uintptr_t value) +{ + struct pci_devinfo *dinfo; + pcicfgregs *cfg; + + dinfo = device_get_ivars(child); + cfg = &dinfo->cfg; + + switch (which) { + case PCI_IVAR_SUBVENDOR: + case PCI_IVAR_SUBDEVICE: + case PCI_IVAR_VENDOR: + case PCI_IVAR_DEVICE: + case PCI_IVAR_DEVID: + case PCI_IVAR_CLASS: + case PCI_IVAR_SUBCLASS: + case PCI_IVAR_PROGIF: + case PCI_IVAR_REVID: + case PCI_IVAR_INTPIN: + case PCI_IVAR_IRQ: + case PCI_IVAR_BUS: + case PCI_IVAR_SLOT: + case PCI_IVAR_FUNCTION: + return EINVAL; /* disallow for now */ + + case PCI_IVAR_SECONDARYBUS: + cfg->secondarybus = value; + break; + case PCI_IVAR_SUBORDINATEBUS: + cfg->subordinatebus = value; + break; + default: + return ENOENT; + } + return 0; +} + +static int +pci_mapno(pcicfgregs *cfg, int reg) +{ + int i, nummaps; + pcimap *map; + + nummaps = cfg->nummaps; + map = cfg->map; + + for (i = 0; i < nummaps; i++) + if (map[i].reg == reg) + return (i); + return (-1); +} + +static int +pci_porten(pcicfgregs *cfg) +{ + return ((cfg->cmdreg & PCIM_CMD_PORTEN) != 0); +} + +static int +pci_isportmap(pcicfgregs *cfg, int map) + +{ + return ((unsigned)map < cfg->nummaps + && (cfg->map[map].type & PCI_MAPPORT) != 0); +} + +static int +pci_memen(pcicfgregs *cfg) +{ + return ((cfg->cmdreg & PCIM_CMD_MEMEN) != 0); +} + +static int +pci_ismemmap(pcicfgregs *cfg, int map) +{ + return ((unsigned)map < cfg->nummaps + && (cfg->map[map].type & PCI_MAPMEM) != 0); +} + +static struct resource * +pci_alloc_resource(device_t dev, device_t child, int type, int *rid, + u_long start, u_long end, u_long count, u_int flags) +{ + int isdefault; + struct pci_devinfo *dinfo = device_get_ivars(child); + pcicfgregs *cfg = &dinfo->cfg; + struct resource *rv, **rvp = 0; + int map; + + isdefault = (device_get_parent(child) == dev + && start == 0UL && end == ~0UL && count == 1); + + switch (type) { + case SYS_RES_IRQ: + if (*rid != 0) + return 0; + if (isdefault && cfg->intline != 255) { + start = cfg->intline; + end = cfg->intline; + count = 1; + } + break; + + case SYS_RES_DRQ: /* passthru for child isa */ + break; + + case SYS_RES_MEMORY: + if (isdefault) { + map = pci_mapno(cfg, *rid); + if (pci_memen(cfg) && pci_ismemmap(cfg, map)) { + start = cfg->map[map].base; + count = 1 << cfg->map[map].ln2size; + end = start + count; + rvp = &cfg->map[map].res; + } else + return 0; + } + break; + + case SYS_RES_IOPORT: + if (isdefault) { + map = pci_mapno(cfg, *rid); + if (pci_porten(cfg) && pci_isportmap(cfg, map)) { + start = cfg->map[map].base; + count = 1 << cfg->map[map].ln2size; + end = start + count; + rvp = &cfg->map[map].res; + } else + return 0; + } + break; + + default: + return 0; + } + + rv = BUS_ALLOC_RESOURCE(device_get_parent(dev), child, + type, rid, start, end, count, flags); + if (rvp) + *rvp = rv; + + return rv; +} + +static int +pci_release_resource(device_t dev, device_t child, int type, int rid, + struct resource *r) +{ + int rv; + struct pci_devinfo *dinfo = device_get_ivars(child); + pcicfgregs *cfg = &dinfo->cfg; + int map = 0; + + switch (type) { + case SYS_RES_IRQ: + if (rid != 0) + return EINVAL; + break; + + case SYS_RES_DRQ: /* passthru for child isa */ + break; + + case SYS_RES_MEMORY: + case SYS_RES_IOPORT: + /* + * Only check the map registers if this is a direct + * descendant. + */ + if (device_get_parent(child) == dev) + map = pci_mapno(cfg, rid); + else + map = -1; + break; + + default: + return (ENOENT); + } + + rv = BUS_RELEASE_RESOURCE(device_get_parent(dev), child, type, rid, r); + + if (rv == 0) { + switch (type) { + case SYS_RES_IRQ: + cfg->irqres = 0; + break; + + case SYS_RES_DRQ: /* passthru for child isa */ + break; + + case SYS_RES_MEMORY: + case SYS_RES_IOPORT: + if (map != -1) + cfg->map[map].res = 0; + break; + + default: + return ENOENT; + } + } + + return rv; +} + +static u_int32_t +pci_read_config_method(device_t dev, device_t child, int reg, int width) +{ + struct pci_devinfo *dinfo = device_get_ivars(child); + pcicfgregs *cfg = &dinfo->cfg; + return pci_cfgread(cfg, reg, width); +} + +static void +pci_write_config_method(device_t dev, device_t child, int reg, + u_int32_t val, int width) +{ + struct pci_devinfo *dinfo = device_get_ivars(child); + pcicfgregs *cfg = &dinfo->cfg; + pci_cfgwrite(cfg, reg, val, width); +} + +static int +pci_modevent(module_t mod, int what, void *arg) +{ + switch (what) { + case MOD_LOAD: + pci_wrap_old_drivers(); + break; + + case MOD_UNLOAD: + break; + } + + return 0; +} + +static device_method_t pci_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, pci_new_probe), + DEVMETHOD(device_attach, bus_generic_attach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + + /* Bus interface */ + DEVMETHOD(bus_print_child, pci_print_child), + DEVMETHOD(bus_read_ivar, pci_read_ivar), + DEVMETHOD(bus_write_ivar, pci_write_ivar), + DEVMETHOD(bus_driver_added, bus_generic_driver_added), + DEVMETHOD(bus_alloc_resource, pci_alloc_resource), + DEVMETHOD(bus_release_resource, pci_release_resource), + DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), + DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), + DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), + DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), + + /* PCI interface */ + DEVMETHOD(pci_read_config, pci_read_config_method), + DEVMETHOD(pci_write_config, pci_write_config_method), + + { 0, 0 } +}; + +static driver_t pci_driver = { + "pci", + pci_methods, + DRIVER_TYPE_MISC, + 1, /* no softc */ +}; + +DRIVER_MODULE(pci, pcib, pci_driver, pci_devclass, pci_modevent, 0); #endif /* NPCI > 0 */ Index: head/sys/dev/pci/pci_if.m =================================================================== --- head/sys/dev/pci/pci_if.m (nonexistent) +++ head/sys/dev/pci/pci_if.m (revision 45720) @@ -0,0 +1,44 @@ +# +# Copyright (c) 1998 Doug Rabson +# 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. +# +# $Id$ +# + +INTERFACE pci; + +METHOD u_int32_t read_config { + device_t dev; + device_t child; + int reg; + int width; +}; + +METHOD void write_config { + device_t dev; + device_t child; + int reg; + u_int32_t val; + int width; +}; Property changes on: head/sys/dev/pci/pci_if.m ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: head/sys/dev/pci/pcireg.h =================================================================== --- head/sys/dev/pci/pcireg.h (revision 45719) +++ head/sys/dev/pci/pcireg.h (revision 45720) @@ -1,257 +1,257 @@ #ifndef PCI_COMPAT #define PCI_COMPAT #endif /* * Copyright (c) 1997, Stefan Esser * 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 unmodified, 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 ``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. * - * $Id: pcireg.h,v 1.19 1997/09/20 07:41:58 dyson Exp $ + * $Id: pcireg.h,v 1.20 1998/10/07 03:40:51 gibbs Exp $ * */ /* * PCIM_xxx: mask to locate subfield in register * PCIR_xxx: config register offset * PCIC_xxx: device class * PCIS_xxx: device subclass * PCIP_xxx: device programming interface * PCIV_xxx: PCI vendor ID (only required to fixup ancient devices) * PCID_xxx: device ID */ /* some PCI bus constants */ #define PCI_BUSMAX 255 #define PCI_SLOTMAX 31 #define PCI_FUNCMAX 7 #define PCI_REGMAX 255 /* PCI config header registers for all devices */ #define PCIR_DEVVENDOR 0x00 #define PCIR_VENDOR 0x00 #define PCIR_DEVICE 0x02 #define PCIR_COMMAND 0x04 #define PCIM_CMD_PORTEN 0x0001 #define PCIM_CMD_MEMEN 0x0002 #define PCIM_CMD_BUSMASTEREN 0x0004 #define PCIM_CMD_PERRESPEN 0x0040 #define PCIR_STATUS 0x06 #define PCIR_REVID 0x08 #define PCIR_PROGIF 0x09 #define PCIR_SUBCLASS 0x0a #define PCIR_CLASS 0x0b #define PCIR_CACHELNSZ 0x0c #define PCIR_LATTIMER 0x0d #define PCIR_HEADERTYPE 0x0e #define PCIM_MFDEV 0x80 #define PCIR_BIST 0x0f /* config registers for header type 0 devices */ #define PCIR_MAPS 0x10 #define PCIR_CARDBUSCIS 0x28 #define PCIR_SUBVEND_0 0x2c #define PCIR_SUBDEV_0 0x2e #define PCIR_INTLINE 0x3c #define PCIR_INTPIN 0x3d #define PCIR_MINGNT 0x3e #define PCIR_MAXLAT 0x3f /* config registers for header type 1 devices */ #define PCIR_SECSTAT_1 0 /**/ #define PCIR_PRIBUS_1 0x18 #define PCIR_SECBUS_1 0x19 #define PCIR_SUBBUS_1 0x1a #define PCIR_SECLAT_1 0x1b #define PCIR_IOBASEL_1 0x1c #define PCIR_IOLIMITL_1 0x1d #define PCIR_IOBASEH_1 0 /**/ #define PCIR_IOLIMITH_1 0 /**/ #define PCIR_MEMBASE_1 0x20 #define PCIR_MEMLIMIT_1 0x22 #define PCIR_PMBASEL_1 0x24 #define PCIR_PMLIMITL_1 0x26 #define PCIR_PMBASEH_1 0 /**/ #define PCIR_PMLIMITH_1 0 /**/ #define PCIR_BRIDGECTL_1 0 /**/ #define PCIR_SUBVEND_1 0x34 #define PCIR_SUBDEV_1 0x36 /* config registers for header type 2 devices */ #define PCIR_SECSTAT_2 0x16 #define PCIR_PRIBUS_2 0x18 #define PCIR_SECBUS_2 0x19 #define PCIR_SUBBUS_2 0x1a #define PCIR_SECLAT_2 0x1b #define PCIR_MEMBASE0_2 0x1c #define PCIR_MEMLIMIT0_2 0x20 #define PCIR_MEMBASE1_2 0x24 #define PCIR_MEMLIMIT1_2 0x28 #define PCIR_IOBASE0_2 0x2c #define PCIR_IOLIMIT0_2 0x30 #define PCIR_IOBASE1_2 0x34 #define PCIR_IOLIMIT1_2 0x38 #define PCIR_BRIDGECTL_2 0x3e #define PCIR_SUBVEND_2 0x40 #define PCIR_SUBDEV_2 0x42 #define PCIR_PCCARDIF_2 0x44 /* PCI device class, subclass and programming interface definitions */ #define PCIC_OLD 0x00 #define PCIS_OLD_NONVGA 0x00 #define PCIS_OLD_VGA 0x01 #define PCIC_STORAGE 0x01 #define PCIS_STORAGE_SCSI 0x00 #define PCIS_STORAGE_IDE 0x01 #define PCIP_STORAGE_IDE_MODEPRIM 0x01 #define PCIP_STORAGE_IDE_PROGINDPRIM 0x02 #define PCIP_STORAGE_IDE_MODESEC 0x04 #define PCIP_STORAGE_IDE_PROGINDSEC 0x08 #define PCIP_STORAGE_IDE_MASTERDEV 0x80 #define PCIS_STORAGE_FLOPPY 0x02 #define PCIS_STORAGE_IPI 0x03 #define PCIS_STORAGE_RAID 0x04 #define PCIS_STORAGE_OTHER 0x80 #define PCIC_NETWORK 0x02 #define PCIS_NETWORK_ETHERNET 0x00 #define PCIS_NETWORK_TOKENRING 0x01 #define PCIS_NETWORK_FDDI 0x02 #define PCIS_NETWORK_ATM 0x03 #define PCIS_NETWORK_OTHER 0x80 #define PCIC_DISPLAY 0x03 #define PCIS_DISPLAY_VGA 0x00 #define PCIS_DISPLAY_XGA 0x01 #define PCIS_DISPLAY_OTHER 0x80 #define PCIC_MULTIMEDIA 0x04 #define PCIS_MULTIMEDIA_VIDEO 0x00 #define PCIS_MULTIMEDIA_AUDIO 0x01 #define PCIS_MULTIMEDIA_OTHER 0x80 #define PCIC_MEMORY 0x05 #define PCIS_MEMORY_RAM 0x00 #define PCIS_MEMORY_FLASH 0x01 #define PCIS_MEMORY_OTHER 0x80 #define PCIC_BRIDGE 0x06 -#define PCIS_BRDIGE_HOST 0x00 +#define PCIS_BRIDGE_HOST 0x00 #define PCIS_BRIDGE_ISA 0x01 #define PCIS_BRIDGE_EISA 0x02 #define PCIS_BRIDGE_MCA 0x03 #define PCIS_BRIDGE_PCI 0x04 #define PCIS_BRIDGE_PCMCIA 0x05 #define PCIS_BRIDGE_NUBUS 0x06 #define PCIS_BRIDGE_CARDBUS 0x07 #define PCIS_BRIDGE_OTHER 0x80 #define PCIC_SIMPLECOMM 0x07 #define PCIS_SIMPLECOMM_UART 0x00 #define PCIS_SIMPLECOMM_PAR 0x01 #define PCIS_SIMPLECOMM_OTHER 0x80 #define PCIC_BASEPERIPH 0x08 #define PCIS_BASEPERIPH_PIC 0x00 #define PCIS_BASEPERIPH_DMA 0x01 #define PCIS_BASEPERIPH_TIMER 0x02 #define PCIS_BASEPERIPH_RTC 0x03 #define PCIS_BASEPERIPH_OTHER 0x80 #define PCIC_INPUTDEV 0x09 #define PCIS_INPUTDEV_KEYBOARD 0x00 #define PCIS_INPUTDEV_DIGITIZER 0x01 #define PCIS_INPUTDEV_MOUSE 0x02 #define PCIS_INPUTDEV_OTHER 0x80 #define PCIC_DOCKING 0x0a #define PCIS_DOCKING_GENERIC 0x00 #define PCIS_DOCKING_OTHER 0x80 #define PCIC_PROCESSOR 0x0b #define PCIS_PROCESSOR_386 0x00 #define PCIS_PROCESSOR_486 0x01 #define PCIS_PROCESSOR_PENTIUM 0x02 #define PCIS_PROCESSOR_ALPHA 0x10 #define PCIS_PROCESSOR_POWERPC 0x20 #define PCIS_PROCESSOR_COPROC 0x40 #define PCIC_SERIALBUS 0x0c #define PCIS_SERIALBUS_FW 0x00 #define PCIS_SERIALBUS_ACCESS 0x01 #define PCIS_SERIALBUS_SSA 0x02 #define PCIS_SERIALBUS_USB 0x03 #define PCIS_SERIALBUS_FC 0x04 #define PCIS_SERIALBUS #define PCIS_SERIALBUS #define PCIC_OTHER 0xff /* some PCI vendor definitions (only used to identify ancient devices !!! */ #define PCIV_INTEL 0x8086 #define PCID_INTEL_SATURN 0x0483 #define PCID_INTEL_ORION 0x84c4 /* for compatibility to FreeBSD-2.2 version of PCI code */ #ifdef PCI_COMPAT #define PCI_ID_REG 0x00 #define PCI_COMMAND_STATUS_REG 0x04 #define PCI_COMMAND_IO_ENABLE 0x00000001 #define PCI_COMMAND_MEM_ENABLE 0x00000002 #define PCI_CLASS_REG 0x08 #define PCI_CLASS_MASK 0xff000000 #define PCI_SUBCLASS_MASK 0x00ff0000 #define PCI_REVISION_MASK 0x000000ff #define PCI_CLASS_PREHISTORIC 0x00000000 #define PCI_SUBCLASS_PREHISTORIC_VGA 0x00010000 #define PCI_CLASS_MASS_STORAGE 0x01000000 #define PCI_CLASS_DISPLAY 0x03000000 #define PCI_SUBCLASS_DISPLAY_VGA 0x00000000 #define PCI_CLASS_BRIDGE 0x06000000 #define PCI_MAP_REG_START 0x10 #define PCI_MAP_REG_END 0x28 #define PCI_MAP_IO 0x00000001 #define PCI_INTERRUPT_REG 0x3c #endif /* PCI_COMPAT */ Index: head/sys/dev/pci/pcivar.h =================================================================== --- head/sys/dev/pci/pcivar.h (revision 45719) +++ head/sys/dev/pci/pcivar.h (revision 45720) @@ -1,234 +1,311 @@ /* * Copyright (c) 1997, Stefan Esser * 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 unmodified, 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 ``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. * - * $Id: pcivar.h,v 1.24 1999/01/13 04:59:19 bde Exp $ + * $Id: pcivar.h,v 1.25 1999/01/19 23:29:20 se Exp $ * */ #ifndef _PCIVAR_H_ #define _PCIVAR_H_ #ifndef PCI_COMPAT #define PCI_COMPAT #endif #include /* XXX KDM */ #include /* some PCI bus constants */ #define PCI_BUSMAX 255 /* highest supported bus number */ #define PCI_SLOTMAX 31 /* highest supported slot number */ #define PCI_FUNCMAX 7 /* highest supported function number */ #define PCI_REGMAX 255 /* highest supported config register addr. */ #define PCI_MAXMAPS_0 6 /* max. no. of memory/port maps */ #define PCI_MAXMAPS_1 2 /* max. no. of maps for PCI to PCI bridge */ #define PCI_MAXMAPS_2 1 /* max. no. of maps for CardBus bridge */ /* pci_addr_t covers this system's PCI bus address space: 32 or 64 bit */ #ifdef PCI_A64 typedef u_int64_t pci_addr_t; /* u_int64_t for system with 64bit addresses */ #else typedef u_int32_t pci_addr_t; /* u_int64_t for system with 64bit addresses */ #endif /* map register information */ typedef struct { u_int32_t base; u_int8_t type; #define PCI_MAPMEM 0x01 /* memory map */ #define PCI_MAPMEMP 0x02 /* prefetchable memory map */ #define PCI_MAPPORT 0x04 /* port map */ u_int8_t ln2size; u_int8_t ln2range; u_int8_t reg; /* offset of map register in config space */ +/* u_int8_t dummy;*/ + struct resource *res; /* handle from resource manager */ } pcimap; /* config header information common to all header types */ typedef struct pcicfg { + struct device *dev; /* device which owns this */ pcimap *map; /* pointer to array of PCI maps */ void *hdrspec; /* pointer to header type specific data */ + struct resource *irqres; /* resource descriptor for interrupt mapping */ u_int16_t subvendor; /* card vendor ID */ u_int16_t subdevice; /* card device ID, assigned by card vendor */ u_int16_t vendor; /* chip vendor ID */ u_int16_t device; /* chip device ID, assigned by chip vendor */ u_int16_t cmdreg; /* disable/enable chip and PCI options */ u_int16_t statreg; /* supported PCI features and error state */ u_int8_t baseclass; /* chip PCI class */ u_int8_t subclass; /* chip PCI subclass */ u_int8_t progif; /* chip PCI programming interface */ u_int8_t revid; /* chip revision ID */ u_int8_t hdrtype; /* chip config header type */ u_int8_t cachelnsz; /* cache line size in 4byte units */ u_int8_t intpin; /* PCI interrupt pin */ u_int8_t intline; /* interrupt line (IRQ for PC arch) */ u_int8_t mingnt; /* min. useful bus grant time in 250ns units */ u_int8_t maxlat; /* max. tolerated bus grant latency in 250ns */ u_int8_t lattimer; /* latency timer in units of 30ns bus cycles */ u_int8_t mfdev; /* multi-function device (from hdrtype reg) */ u_int8_t nummaps; /* actual number of PCI maps used */ u_int8_t bus; /* config space bus address */ u_int8_t slot; /* config space slot address */ u_int8_t func; /* config space function number */ u_int8_t secondarybus; /* bus on secondary side of bridge, if any */ u_int8_t subordinatebus; /* topmost bus number behind bridge, if any */ } pcicfgregs; /* additional type 1 device config header information (PCI to PCI bridge) */ #ifdef PCI_A64 #define PCI_PPBMEMBASE(h,l) ((((pci_addr_t)(h) << 32) + ((l)<<16)) & ~0xfffff) #define PCI_PPBMEMLIMIT(h,l) ((((pci_addr_t)(h) << 32) + ((l)<<16)) | 0xfffff) #else #define PCI_PPBMEMBASE(h,l) (((l)<<16) & ~0xfffff) #define PCI_PPBMEMLIMIT(h,l) (((l)<<16) | 0xfffff) #endif /* PCI_A64 */ #define PCI_PPBIOBASE(h,l) ((((h)<<16) + ((l)<<8)) & ~0xfff) #define PCI_PPBIOLIMIT(h,l) ((((h)<<16) + ((l)<<8)) | 0xfff) typedef struct { pci_addr_t pmembase; /* base address of prefetchable memory */ pci_addr_t pmemlimit; /* topmost address of prefetchable memory */ u_int32_t membase; /* base address of memory window */ u_int32_t memlimit; /* topmost address of memory window */ u_int32_t iobase; /* base address of port window */ u_int32_t iolimit; /* topmost address of port window */ u_int16_t secstat; /* secondary bus status register */ u_int16_t bridgectl; /* bridge control register */ u_int8_t seclat; /* CardBus latency timer */ } pcih1cfgregs; /* additional type 2 device config header information (CardBus bridge) */ typedef struct { u_int32_t membase0; /* base address of memory window */ u_int32_t memlimit0; /* topmost address of memory window */ u_int32_t membase1; /* base address of memory window */ u_int32_t memlimit1; /* topmost address of memory window */ u_int32_t iobase0; /* base address of port window */ u_int32_t iolimit0; /* topmost address of port window */ u_int32_t iobase1; /* base address of port window */ u_int32_t iolimit1; /* topmost address of port window */ u_int32_t pccardif; /* PC Card 16bit IF legacy more base addr. */ u_int16_t secstat; /* secondary bus status register */ u_int16_t bridgectl; /* bridge control register */ u_int8_t seclat; /* CardBus latency timer */ } pcih2cfgregs; /* PCI bus attach definitions (there could be multiple PCI bus *trees* ... */ typedef struct pciattach { int unit; int pcibushigh; struct pciattach *next; } pciattach; struct pci_devinfo { STAILQ_ENTRY(pci_devinfo) pci_links; struct pci_device *device; /* should this be ifdefed? */ pcicfgregs cfg; struct pci_conf conf; }; extern u_int32_t pci_numdevs; /* externally visible functions */ int pci_probe (pciattach *attach); void pci_drvattach(struct pci_devinfo *dinfo); /* low level PCI config register functions provided by pcibus.c */ int pci_cfgopen (void); int pci_cfgread (pcicfgregs *cfg, int reg, int bytes); void pci_cfgwrite (pcicfgregs *cfg, int reg, int data, int bytes); #ifdef __alpha__ vm_offset_t pci_cvt_to_dense (vm_offset_t); vm_offset_t pci_cvt_to_bwx (vm_offset_t); #endif /* __alpha__ */ + +#ifdef _SYS_BUS_H_ + +#include "pci_if.h" + +enum pci_device_ivars { + PCI_IVAR_SUBVENDOR, + PCI_IVAR_SUBDEVICE, + PCI_IVAR_VENDOR, + PCI_IVAR_DEVICE, + PCI_IVAR_DEVID, + PCI_IVAR_CLASS, + PCI_IVAR_SUBCLASS, + PCI_IVAR_PROGIF, + PCI_IVAR_REVID, + PCI_IVAR_INTPIN, + PCI_IVAR_IRQ, + PCI_IVAR_BUS, + PCI_IVAR_SLOT, + PCI_IVAR_FUNCTION, + PCI_IVAR_SECONDARYBUS, + PCI_IVAR_SUBORDINATEBUS, +}; + +/* + * Simplified accessors for pci devices + */ +#define PCI_ACCESSOR(A, B, T) \ + \ +static __inline T pci_get_ ## A(device_t dev) \ +{ \ + uintptr_t v; \ + BUS_READ_IVAR(device_get_parent(dev), dev, PCI_IVAR_ ## B, &v); \ + return (T) v; \ +} \ + \ +static __inline void pci_set_ ## A(device_t dev, T t) \ +{ \ + u_long v = (u_long) t; \ + BUS_WRITE_IVAR(device_get_parent(dev), dev, PCI_IVAR_ ## B, v); \ +} + +PCI_ACCESSOR(subvendor, SUBVENDOR, u_int16_t) +PCI_ACCESSOR(subdevice, SUBDEVICE, u_int16_t) +PCI_ACCESSOR(vendor, VENDOR, u_int16_t) +PCI_ACCESSOR(device, DEVICE, u_int16_t) +PCI_ACCESSOR(devid, DEVID, u_int32_t) +PCI_ACCESSOR(class, CLASS, u_int8_t) +PCI_ACCESSOR(subclass, SUBCLASS, u_int8_t) +PCI_ACCESSOR(progif, PROGIF, u_int8_t) +PCI_ACCESSOR(revid, REVID, u_int8_t) +PCI_ACCESSOR(intpin, INTPIN, u_int8_t) +PCI_ACCESSOR(irq, IRQ, u_int8_t) +PCI_ACCESSOR(bus, BUS, u_int8_t) +PCI_ACCESSOR(slot, SLOT, u_int8_t) +PCI_ACCESSOR(function, FUNCTION, u_int8_t) +PCI_ACCESSOR(secondarybus, SECONDARYBUS, u_int8_t) +PCI_ACCESSOR(subordinatebus, SUBORDINATEBUS, u_int8_t) + +static __inline u_int32_t +pci_read_config(device_t dev, int reg, int width) +{ + return PCI_READ_CONFIG(device_get_parent(dev), dev, reg, width); +} + +static __inline void +pci_write_config(device_t dev, int reg, u_int32_t val, int width) +{ + PCI_WRITE_CONFIG(device_get_parent(dev), dev, reg, val, width); +} + +#endif + /* for compatibility to FreeBSD-2.2 version of PCI code */ #ifdef PCI_COMPAT typedef pcicfgregs *pcici_t; typedef unsigned pcidi_t; typedef void pci_inthand_t(void *arg); #define pci_max_burst_len (3) /* just copied from old PCI code for now ... */ extern struct linker_set pcidevice_set; extern int pci_mechanism; struct pci_device { char* pd_name; const char* (*pd_probe ) (pcici_t tag, pcidi_t type); void (*pd_attach) (pcici_t tag, int unit); u_long *pd_count; int (*pd_shutdown) (int, int); }; struct pci_lkm { struct pci_device *dvp; struct pci_lkm *next; }; #ifdef __i386__ typedef u_short pci_port_t; #else typedef u_int pci_port_t; #endif u_long pci_conf_read (pcici_t tag, u_long reg); void pci_conf_write (pcici_t tag, u_long reg, u_long data); void pci_configure (void); int pci_map_port (pcici_t tag, u_long reg, pci_port_t* pa); int pci_map_mem (pcici_t tag, u_long reg, vm_offset_t* va, vm_offset_t* pa); int pci_map_dense (pcici_t tag, u_long reg, vm_offset_t* va, vm_offset_t* pa); int pci_map_bwx (pcici_t tag, u_long reg, vm_offset_t* va, vm_offset_t* pa); int pci_map_int (pcici_t tag, pci_inthand_t *handler, void *arg, intrmask_t *maskptr); int pci_map_int_right(pcici_t cfg, pci_inthand_t *handler, void *arg, intrmask_t *maskptr, u_int flags); int pci_unmap_int (pcici_t tag); int pci_register_lkm (struct pci_device *dvp, int if_revision); #endif /* PCI_COMPAT */ #endif /* _PCIVAR_H_ */ Index: head/sys/dev/rp/rp.c =================================================================== --- head/sys/dev/rp/rp.c (revision 45719) +++ head/sys/dev/rp/rp.c (revision 45720) @@ -1,2053 +1,2045 @@ /* * Copyright (c) Comtrol Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted prodived that the follwoing conditions * are met. * 1. Redistributions of source code must retain the above copyright * notive, this list of conditions and the following disclainer. * 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 prodided 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 Comtrol Corporation. * 4. The name of Comtrol Corporation may not be used to endorse or * promote products derived from this software without specific * prior written permission. * * THIS SOFTWARE IS PROVIDED BY COMTROL CORPORATION ``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 COMTROL CORPORATION 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, LIFE 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. */ /* * rp.c - for RocketPort FreeBSD */ #include "opt_compat.h" #include #include #include #include #include #include #include #include #include #include #define ROCKET_C #include #include #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif static Byte_t RData[RDATASIZE] = { 0x00, 0x09, 0xf6, 0x82, 0x02, 0x09, 0x86, 0xfb, 0x04, 0x09, 0x00, 0x0a, 0x06, 0x09, 0x01, 0x0a, 0x08, 0x09, 0x8a, 0x13, 0x0a, 0x09, 0xc5, 0x11, 0x0c, 0x09, 0x86, 0x85, 0x0e, 0x09, 0x20, 0x0a, 0x10, 0x09, 0x21, 0x0a, 0x12, 0x09, 0x41, 0xff, 0x14, 0x09, 0x82, 0x00, 0x16, 0x09, 0x82, 0x7b, 0x18, 0x09, 0x8a, 0x7d, 0x1a, 0x09, 0x88, 0x81, 0x1c, 0x09, 0x86, 0x7a, 0x1e, 0x09, 0x84, 0x81, 0x20, 0x09, 0x82, 0x7c, 0x22, 0x09, 0x0a, 0x0a }; static Byte_t RRegData[RREGDATASIZE]= { 0x00, 0x09, 0xf6, 0x82, /* 00: Stop Rx processor */ 0x08, 0x09, 0x8a, 0x13, /* 04: Tx software flow control */ 0x0a, 0x09, 0xc5, 0x11, /* 08: XON char */ 0x0c, 0x09, 0x86, 0x85, /* 0c: XANY */ 0x12, 0x09, 0x41, 0xff, /* 10: Rx mask char */ 0x14, 0x09, 0x82, 0x00, /* 14: Compare/Ignore #0 */ 0x16, 0x09, 0x82, 0x7b, /* 18: Compare #1 */ 0x18, 0x09, 0x8a, 0x7d, /* 1c: Compare #2 */ 0x1a, 0x09, 0x88, 0x81, /* 20: Interrupt #1 */ 0x1c, 0x09, 0x86, 0x7a, /* 24: Ignore/Replace #1 */ 0x1e, 0x09, 0x84, 0x81, /* 28: Interrupt #2 */ 0x20, 0x09, 0x82, 0x7c, /* 2c: Ignore/Replace #2 */ 0x22, 0x09, 0x0a, 0x0a /* 30: Rx FIFO Enable */ }; static CONTROLLER_T sController[CTL_SIZE] = { {-1,-1,0,0,0,0,0,0,0,0,0,{0,0,0,0},{0,0,0,0},{-1,-1,-1,-1},{0,0,0,0}}, {-1,-1,0,0,0,0,0,0,0,0,0,{0,0,0,0},{0,0,0,0},{-1,-1,-1,-1},{0,0,0,0}}, {-1,-1,0,0,0,0,0,0,0,0,0,{0,0,0,0},{0,0,0,0},{-1,-1,-1,-1},{0,0,0,0}}, {-1,-1,0,0,0,0,0,0,0,0,0,{0,0,0,0},{0,0,0,0},{-1,-1,-1,-1},{0,0,0,0}} }; #if 0 /* IRQ number to MUDBAC register 2 mapping */ Byte_t sIRQMap[16] = { 0,0,0,0x10,0x20,0x30,0,0,0,0x40,0x50,0x60,0x70,0,0,0x80 }; #endif static Byte_t sBitMapClrTbl[8] = { 0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f }; static Byte_t sBitMapSetTbl[8] = { 0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80 }; /*************************************************************************** Function: sInitController Purpose: Initialization of controller global registers and controller structure. Call: sInitController(CtlP,CtlNum,MudbacIO,AiopIOList,AiopIOListSize, IRQNum,Frequency,PeriodicOnly) CONTROLLER_T *CtlP; Ptr to controller structure int CtlNum; Controller number ByteIO_t MudbacIO; Mudbac base I/O address. ByteIO_t *AiopIOList; List of I/O addresses for each AIOP. This list must be in the order the AIOPs will be found on the controller. Once an AIOP in the list is not found, it is assumed that there are no more AIOPs on the controller. int AiopIOListSize; Number of addresses in AiopIOList int IRQNum; Interrupt Request number. Can be any of the following: 0: Disable global interrupts 3: IRQ 3 4: IRQ 4 5: IRQ 5 9: IRQ 9 10: IRQ 10 11: IRQ 11 12: IRQ 12 15: IRQ 15 Byte_t Frequency: A flag identifying the frequency of the periodic interrupt, can be any one of the following: FREQ_DIS - periodic interrupt disabled FREQ_137HZ - 137 Hertz FREQ_69HZ - 69 Hertz FREQ_34HZ - 34 Hertz FREQ_17HZ - 17 Hertz FREQ_9HZ - 9 Hertz FREQ_4HZ - 4 Hertz If IRQNum is set to 0 the Frequency parameter is overidden, it is forced to a value of FREQ_DIS. int PeriodicOnly: TRUE if all interrupts except the periodic interrupt are to be blocked. FALSE is both the periodic interrupt and other channel interrupts are allowed. If IRQNum is set to 0 the PeriodicOnly parameter is overidden, it is forced to a value of FALSE. Return: int: Number of AIOPs on the controller, or CTLID_NULL if controller initialization failed. Comments: If periodic interrupts are to be disabled but AIOP interrupts are allowed, set Frequency to FREQ_DIS and PeriodicOnly to FALSE. If interrupts are to be completely disabled set IRQNum to 0. Setting Frequency to FREQ_DIS and PeriodicOnly to TRUE is an invalid combination. This function performs initialization of global interrupt modes, but it does not actually enable global interrupts. To enable and disable global interrupts use functions sEnGlobalInt() and sDisGlobalInt(). Enabling of global interrupts is normally not done until all other initializations are complete. Even if interrupts are globally enabled, they must also be individually enabled for each channel that is to generate interrupts. Warnings: No range checking on any of the parameters is done. No context switches are allowed while executing this function. After this function all AIOPs on the controller are disabled, they can be enabled with sEnAiop(). */ int sInitController( CONTROLLER_T *CtlP, int CtlNum, ByteIO_t MudbacIO, ByteIO_t *AiopIOList, int AiopIOListSize, int IRQNum, Byte_t Frequency, int PeriodicOnly) { int i; ByteIO_t io; CtlP->CtlNum = CtlNum; CtlP->BusType = isISA; CtlP->CtlID = CTLID_0001; /* controller release 1 */ CtlP->MBaseIO = MudbacIO; CtlP->MReg1IO = MudbacIO + 1; CtlP->MReg2IO = MudbacIO + 2; CtlP->MReg3IO = MudbacIO + 3; #if 1 CtlP->MReg2 = 0; /* interrupt disable */ CtlP->MReg3 = 0; /* no periodic interrupts */ #else if(sIRQMap[IRQNum] == 0) /* interrupts globally disabled */ { CtlP->MReg2 = 0; /* interrupt disable */ CtlP->MReg3 = 0; /* no periodic interrupts */ } else { CtlP->MReg2 = sIRQMap[IRQNum]; /* set IRQ number */ CtlP->MReg3 = Frequency; /* set frequency */ if(PeriodicOnly) /* periodic interrupt only */ { CtlP->MReg3 |= PERIODIC_ONLY; } } #endif sOutB(CtlP->MReg2IO,CtlP->MReg2); sOutB(CtlP->MReg3IO,CtlP->MReg3); sControllerEOI(CtlP); /* clear EOI if warm init */ /* Init AIOPs */ CtlP->NumAiop = 0; for(i=0; i < AiopIOListSize; i++) { io = AiopIOList[i]; CtlP->AiopIO[i] = (WordIO_t)io; CtlP->AiopIntChanIO[i] = io + _INT_CHAN; sOutB(CtlP->MReg2IO,CtlP->MReg2 | (i & 0x03)); /* AIOP index */ sOutB(MudbacIO,(Byte_t)(io >> 6)); /* set up AIOP I/O in MUDBAC */ sEnAiop(CtlP,i); /* enable the AIOP */ CtlP->AiopID[i] = sReadAiopID(io); /* read AIOP ID */ if(CtlP->AiopID[i] == AIOPID_NULL) /* if AIOP does not exist */ { sDisAiop(CtlP,i); /* disable AIOP */ break; /* done looking for AIOPs */ } CtlP->AiopNumChan[i] = sReadAiopNumChan((WordIO_t)io); /* num channels in AIOP */ sOutW((WordIO_t)io + _INDX_ADDR,_CLK_PRE); /* clock prescaler */ sOutB(io + _INDX_DATA,CLOCK_PRESC); CtlP->NumAiop++; /* bump count of AIOPs */ sDisAiop(CtlP,i); /* disable AIOP */ } if(CtlP->NumAiop == 0) return(-1); else return(CtlP->NumAiop); } int sPCIInitController( CONTROLLER_T *CtlP, int CtlNum, ByteIO_t *AiopIOList, int AiopIOListSize, int IRQNum, Byte_t Frequency, int PeriodicOnly) { int i; ByteIO_t io; CtlP->CtlNum = CtlNum; CtlP->BusType = isPCI; CtlP->CtlID = CTLID_0001; /* controller release 1 */ CtlP->PCIIO = (WordIO_t)((ByteIO_t)AiopIOList[0] + _PCI_INT_FUNC); sPCIControllerEOI(CtlP); /* Init AIOPs */ CtlP->NumAiop = 0; for(i=0; i < AiopIOListSize; i++) { io = AiopIOList[i]; CtlP->AiopIO[i] = (WordIO_t)io; CtlP->AiopIntChanIO[i] = io + _INT_CHAN; CtlP->AiopID[i] = sReadAiopID(io); /* read AIOP ID */ if(CtlP->AiopID[i] == AIOPID_NULL) /* if AIOP does not exist */ { break; /* done looking for AIOPs */ } CtlP->AiopNumChan[i] = sReadAiopNumChan((WordIO_t)io); /* num channels in AIOP */ sOutW((WordIO_t)io + _INDX_ADDR,_CLK_PRE); /* clock prescaler */ sOutB(io + _INDX_DATA,CLOCK_PRESC); CtlP->NumAiop++; /* bump count of AIOPs */ } if(CtlP->NumAiop == 0) return(-1); else return(CtlP->NumAiop); } /*************************************************************************** Function: sReadAiopID Purpose: Read the AIOP idenfication number directly from an AIOP. Call: sReadAiopID(io) ByteIO_t io: AIOP base I/O address Return: int: Flag AIOPID_XXXX if a valid AIOP is found, where X is replace by an identifying number. Flag AIOPID_NULL if no valid AIOP is found Warnings: No context switches are allowed while executing this function. */ int sReadAiopID(ByteIO_t io) { Byte_t AiopID; /* ID byte from AIOP */ sOutB(io + _CMD_REG,RESET_ALL); /* reset AIOP */ sOutB(io + _CMD_REG,0x0); AiopID = sInB(io + _CHN_STAT0) & 0x07; if(AiopID == 0x06) return(1); else /* AIOP does not exist */ return(-1); } /*************************************************************************** Function: sReadAiopNumChan Purpose: Read the number of channels available in an AIOP directly from an AIOP. Call: sReadAiopNumChan(io) WordIO_t io: AIOP base I/O address Return: int: The number of channels available Comments: The number of channels is determined by write/reads from identical offsets within the SRAM address spaces for channels 0 and 4. If the channel 4 space is mirrored to channel 0 it is a 4 channel AIOP, otherwise it is an 8 channel. Warnings: No context switches are allowed while executing this function. */ int sReadAiopNumChan(WordIO_t io) { Word_t x; sOutDW((DWordIO_t)io + _INDX_ADDR,0x12340000L); /* write to chan 0 SRAM */ sOutW(io + _INDX_ADDR,0); /* read from SRAM, chan 0 */ x = sInW(io + _INDX_DATA); sOutW(io + _INDX_ADDR,0x4000); /* read from SRAM, chan 4 */ if(x != sInW(io + _INDX_DATA)) /* if different must be 8 chan */ return(8); else return(4); } /*************************************************************************** Function: sInitChan Purpose: Initialization of a channel and channel structure Call: sInitChan(CtlP,ChP,AiopNum,ChanNum) CONTROLLER_T *CtlP; Ptr to controller structure CHANNEL_T *ChP; Ptr to channel structure int AiopNum; AIOP number within controller int ChanNum; Channel number within AIOP Return: int: TRUE if initialization succeeded, FALSE if it fails because channel number exceeds number of channels available in AIOP. Comments: This function must be called before a channel can be used. Warnings: No range checking on any of the parameters is done. No context switches are allowed while executing this function. */ int sInitChan( CONTROLLER_T *CtlP, CHANNEL_T *ChP, int AiopNum, int ChanNum) { int i; WordIO_t AiopIO; WordIO_t ChIOOff; Byte_t *ChR; Word_t ChOff; static Byte_t R[4]; if(ChanNum >= CtlP->AiopNumChan[AiopNum]) return(FALSE); /* exceeds num chans in AIOP */ /* Channel, AIOP, and controller identifiers */ ChP->CtlP = CtlP; ChP->ChanID = CtlP->AiopID[AiopNum]; ChP->AiopNum = AiopNum; ChP->ChanNum = ChanNum; /* Global direct addresses */ AiopIO = CtlP->AiopIO[AiopNum]; ChP->Cmd = (ByteIO_t)AiopIO + _CMD_REG; ChP->IntChan = (ByteIO_t)AiopIO + _INT_CHAN; ChP->IntMask = (ByteIO_t)AiopIO + _INT_MASK; ChP->IndexAddr = (DWordIO_t)AiopIO + _INDX_ADDR; ChP->IndexData = AiopIO + _INDX_DATA; /* Channel direct addresses */ ChIOOff = AiopIO + ChP->ChanNum * 2; ChP->TxRxData = ChIOOff + _TD0; ChP->ChanStat = ChIOOff + _CHN_STAT0; ChP->TxRxCount = ChIOOff + _FIFO_CNT0; ChP->IntID = (ByteIO_t)AiopIO + ChP->ChanNum + _INT_ID0; /* Initialize the channel from the RData array */ for(i=0; i < RDATASIZE; i+=4) { R[0] = RData[i]; R[1] = RData[i+1] + 0x10 * ChanNum; R[2] = RData[i+2]; R[3] = RData[i+3]; sOutDW(ChP->IndexAddr,*((DWord_t *)&R[0])); } ChR = ChP->R; for(i=0; i < RREGDATASIZE; i+=4) { ChR[i] = RRegData[i]; ChR[i+1] = RRegData[i+1] + 0x10 * ChanNum; ChR[i+2] = RRegData[i+2]; ChR[i+3] = RRegData[i+3]; } /* Indexed registers */ ChOff = (Word_t)ChanNum * 0x1000; ChP->BaudDiv[0] = (Byte_t)(ChOff + _BAUD); ChP->BaudDiv[1] = (Byte_t)((ChOff + _BAUD) >> 8); ChP->BaudDiv[2] = (Byte_t)BRD9600; ChP->BaudDiv[3] = (Byte_t)(BRD9600 >> 8); sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->BaudDiv[0]); ChP->TxControl[0] = (Byte_t)(ChOff + _TX_CTRL); ChP->TxControl[1] = (Byte_t)((ChOff + _TX_CTRL) >> 8); ChP->TxControl[2] = 0; ChP->TxControl[3] = 0; sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->TxControl[0]); ChP->RxControl[0] = (Byte_t)(ChOff + _RX_CTRL); ChP->RxControl[1] = (Byte_t)((ChOff + _RX_CTRL) >> 8); ChP->RxControl[2] = 0; ChP->RxControl[3] = 0; sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->RxControl[0]); ChP->TxEnables[0] = (Byte_t)(ChOff + _TX_ENBLS); ChP->TxEnables[1] = (Byte_t)((ChOff + _TX_ENBLS) >> 8); ChP->TxEnables[2] = 0; ChP->TxEnables[3] = 0; sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->TxEnables[0]); ChP->TxCompare[0] = (Byte_t)(ChOff + _TXCMP1); ChP->TxCompare[1] = (Byte_t)((ChOff + _TXCMP1) >> 8); ChP->TxCompare[2] = 0; ChP->TxCompare[3] = 0; sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->TxCompare[0]); ChP->TxReplace1[0] = (Byte_t)(ChOff + _TXREP1B1); ChP->TxReplace1[1] = (Byte_t)((ChOff + _TXREP1B1) >> 8); ChP->TxReplace1[2] = 0; ChP->TxReplace1[3] = 0; sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->TxReplace1[0]); ChP->TxReplace2[0] = (Byte_t)(ChOff + _TXREP2); ChP->TxReplace2[1] = (Byte_t)((ChOff + _TXREP2) >> 8); ChP->TxReplace2[2] = 0; ChP->TxReplace2[3] = 0; sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->TxReplace2[0]); ChP->TxFIFOPtrs = ChOff + _TXF_OUTP; ChP->TxFIFO = ChOff + _TX_FIFO; sOutB(ChP->Cmd,(Byte_t)ChanNum | RESTXFCNT); /* apply reset Tx FIFO count */ sOutB(ChP->Cmd,(Byte_t)ChanNum); /* remove reset Tx FIFO count */ sOutW((WordIO_t)ChP->IndexAddr,ChP->TxFIFOPtrs); /* clear Tx in/out ptrs */ sOutW(ChP->IndexData,0); ChP->RxFIFOPtrs = ChOff + _RXF_OUTP; ChP->RxFIFO = ChOff + _RX_FIFO; sOutB(ChP->Cmd,(Byte_t)ChanNum | RESRXFCNT); /* apply reset Rx FIFO count */ sOutB(ChP->Cmd,(Byte_t)ChanNum); /* remove reset Rx FIFO count */ sOutW((WordIO_t)ChP->IndexAddr,ChP->RxFIFOPtrs); /* clear Rx out ptr */ sOutW(ChP->IndexData,0); sOutW((WordIO_t)ChP->IndexAddr,ChP->RxFIFOPtrs + 2); /* clear Rx in ptr */ sOutW(ChP->IndexData,0); ChP->TxPrioCnt = ChOff + _TXP_CNT; sOutW((WordIO_t)ChP->IndexAddr,ChP->TxPrioCnt); sOutB(ChP->IndexData,0); ChP->TxPrioPtr = ChOff + _TXP_PNTR; sOutW((WordIO_t)ChP->IndexAddr,ChP->TxPrioPtr); sOutB(ChP->IndexData,0); ChP->TxPrioBuf = ChOff + _TXP_BUF; sEnRxProcessor(ChP); /* start the Rx processor */ return(TRUE); } /*************************************************************************** Function: sStopRxProcessor Purpose: Stop the receive processor from processing a channel. Call: sStopRxProcessor(ChP) CHANNEL_T *ChP; Ptr to channel structure Comments: The receive processor can be started again with sStartRxProcessor(). This function causes the receive processor to skip over the stopped channel. It does not stop it from processing other channels. Warnings: No context switches are allowed while executing this function. Do not leave the receive processor stopped for more than one character time. After calling this function a delay of 4 uS is required to ensure that the receive processor is no longer processing this channel. */ void sStopRxProcessor(CHANNEL_T *ChP) { Byte_t R[4]; R[0] = ChP->R[0]; R[1] = ChP->R[1]; R[2] = 0x0a; R[3] = ChP->R[3]; sOutDW(ChP->IndexAddr,*(DWord_t *)&R[0]); } /*************************************************************************** Function: sFlushRxFIFO Purpose: Flush the Rx FIFO Call: sFlushRxFIFO(ChP) CHANNEL_T *ChP; Ptr to channel structure Return: void Comments: To prevent data from being enqueued or dequeued in the Tx FIFO while it is being flushed the receive processor is stopped and the transmitter is disabled. After these operations a 4 uS delay is done before clearing the pointers to allow the receive processor to stop. These items are handled inside this function. Warnings: No context switches are allowed while executing this function. */ void sFlushRxFIFO(CHANNEL_T *ChP) { int i; Byte_t Ch; /* channel number within AIOP */ int RxFIFOEnabled; /* TRUE if Rx FIFO enabled */ if(sGetRxCnt(ChP) == 0) /* Rx FIFO empty */ return; /* don't need to flush */ RxFIFOEnabled = FALSE; if(ChP->R[0x32] == 0x08) /* Rx FIFO is enabled */ { RxFIFOEnabled = TRUE; sDisRxFIFO(ChP); /* disable it */ for(i=0; i < 2000/200; i++) /* delay 2 uS to allow proc to disable FIFO*/ sInB(ChP->IntChan); /* depends on bus i/o timing */ } sGetChanStatus(ChP); /* clear any pending Rx errors in chan stat */ Ch = (Byte_t)sGetChanNum(ChP); sOutB(ChP->Cmd,Ch | RESRXFCNT); /* apply reset Rx FIFO count */ sOutB(ChP->Cmd,Ch); /* remove reset Rx FIFO count */ sOutW((WordIO_t)ChP->IndexAddr,ChP->RxFIFOPtrs); /* clear Rx out ptr */ sOutW(ChP->IndexData,0); sOutW((WordIO_t)ChP->IndexAddr,ChP->RxFIFOPtrs + 2); /* clear Rx in ptr */ sOutW(ChP->IndexData,0); if(RxFIFOEnabled) sEnRxFIFO(ChP); /* enable Rx FIFO */ } /*************************************************************************** Function: sFlushTxFIFO Purpose: Flush the Tx FIFO Call: sFlushTxFIFO(ChP) CHANNEL_T *ChP; Ptr to channel structure Return: void Comments: To prevent data from being enqueued or dequeued in the Tx FIFO while it is being flushed the receive processor is stopped and the transmitter is disabled. After these operations a 4 uS delay is done before clearing the pointers to allow the receive processor to stop. These items are handled inside this function. Warnings: No context switches are allowed while executing this function. */ void sFlushTxFIFO(CHANNEL_T *ChP) { int i; Byte_t Ch; /* channel number within AIOP */ int TxEnabled; /* TRUE if transmitter enabled */ if(sGetTxCnt(ChP) == 0) /* Tx FIFO empty */ return; /* don't need to flush */ TxEnabled = FALSE; if(ChP->TxControl[3] & TX_ENABLE) { TxEnabled = TRUE; sDisTransmit(ChP); /* disable transmitter */ } sStopRxProcessor(ChP); /* stop Rx processor */ for(i = 0; i < 4000/200; i++) /* delay 4 uS to allow proc to stop */ sInB(ChP->IntChan); /* depends on bus i/o timing */ Ch = (Byte_t)sGetChanNum(ChP); sOutB(ChP->Cmd,Ch | RESTXFCNT); /* apply reset Tx FIFO count */ sOutB(ChP->Cmd,Ch); /* remove reset Tx FIFO count */ sOutW((WordIO_t)ChP->IndexAddr,ChP->TxFIFOPtrs); /* clear Tx in/out ptrs */ sOutW(ChP->IndexData,0); if(TxEnabled) sEnTransmit(ChP); /* enable transmitter */ sStartRxProcessor(ChP); /* restart Rx processor */ } /*************************************************************************** Function: sWriteTxPrioByte Purpose: Write a byte of priority transmit data to a channel Call: sWriteTxPrioByte(ChP,Data) CHANNEL_T *ChP; Ptr to channel structure Byte_t Data; The transmit data byte Return: int: 1 if the bytes is successfully written, otherwise 0. Comments: The priority byte is transmitted before any data in the Tx FIFO. Warnings: No context switches are allowed while executing this function. */ int sWriteTxPrioByte(CHANNEL_T *ChP, Byte_t Data) { Byte_t DWBuf[4]; /* buffer for double word writes */ Word_t *WordPtr; /* must be far because Win SS != DS */ register DWordIO_t IndexAddr; if(sGetTxCnt(ChP) > 1) /* write it to Tx priority buffer */ { IndexAddr = ChP->IndexAddr; sOutW((WordIO_t)IndexAddr,ChP->TxPrioCnt); /* get priority buffer status */ if(sInB((ByteIO_t)ChP->IndexData) & PRI_PEND) /* priority buffer busy */ return(0); /* nothing sent */ WordPtr = (Word_t *)(&DWBuf[0]); *WordPtr = ChP->TxPrioBuf; /* data byte address */ DWBuf[2] = Data; /* data byte value */ sOutDW(IndexAddr,*((DWord_t *)(&DWBuf[0]))); /* write it out */ *WordPtr = ChP->TxPrioCnt; /* Tx priority count address */ DWBuf[2] = PRI_PEND + 1; /* indicate 1 byte pending */ DWBuf[3] = 0; /* priority buffer pointer */ sOutDW(IndexAddr,*((DWord_t *)(&DWBuf[0]))); /* write it out */ } else /* write it to Tx FIFO */ { sWriteTxByte(sGetTxRxDataIO(ChP),Data); } return(1); /* 1 byte sent */ } /*************************************************************************** Function: sEnInterrupts Purpose: Enable one or more interrupts for a channel Call: sEnInterrupts(ChP,Flags) CHANNEL_T *ChP; Ptr to channel structure Word_t Flags: Interrupt enable flags, can be any combination of the following flags: TXINT_EN: Interrupt on Tx FIFO empty RXINT_EN: Interrupt on Rx FIFO at trigger level (see sSetRxTrigger()) SRCINT_EN: Interrupt on SRC (Special Rx Condition) MCINT_EN: Interrupt on modem input change CHANINT_EN: Allow channel interrupt signal to the AIOP's Interrupt Channel Register. Return: void Comments: If an interrupt enable flag is set in Flags, that interrupt will be enabled. If an interrupt enable flag is not set in Flags, that interrupt will not be changed. Interrupts can be disabled with function sDisInterrupts(). This function sets the appropriate bit for the channel in the AIOP's Interrupt Mask Register if the CHANINT_EN flag is set. This allows this channel's bit to be set in the AIOP's Interrupt Channel Register. Interrupts must also be globally enabled before channel interrupts will be passed on to the host. This is done with function sEnGlobalInt(). In some cases it may be desirable to disable interrupts globally but enable channel interrupts. This would allow the global interrupt status register to be used to determine which AIOPs need service. */ void sEnInterrupts(CHANNEL_T *ChP,Word_t Flags) { Byte_t Mask; /* Interrupt Mask Register */ ChP->RxControl[2] |= ((Byte_t)Flags & (RXINT_EN | SRCINT_EN | MCINT_EN)); sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->RxControl[0]); ChP->TxControl[2] |= ((Byte_t)Flags & TXINT_EN); sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->TxControl[0]); if(Flags & CHANINT_EN) { Mask = sInB(ChP->IntMask) | sBitMapSetTbl[ChP->ChanNum]; sOutB(ChP->IntMask,Mask); } } /*************************************************************************** Function: sDisInterrupts Purpose: Disable one or more interrupts for a channel Call: sDisInterrupts(ChP,Flags) CHANNEL_T *ChP; Ptr to channel structure Word_t Flags: Interrupt flags, can be any combination of the following flags: TXINT_EN: Interrupt on Tx FIFO empty RXINT_EN: Interrupt on Rx FIFO at trigger level (see sSetRxTrigger()) SRCINT_EN: Interrupt on SRC (Special Rx Condition) MCINT_EN: Interrupt on modem input change CHANINT_EN: Disable channel interrupt signal to the AIOP's Interrupt Channel Register. Return: void Comments: If an interrupt flag is set in Flags, that interrupt will be disabled. If an interrupt flag is not set in Flags, that interrupt will not be changed. Interrupts can be enabled with function sEnInterrupts(). This function clears the appropriate bit for the channel in the AIOP's Interrupt Mask Register if the CHANINT_EN flag is set. This blocks this channel's bit from being set in the AIOP's Interrupt Channel Register. */ void sDisInterrupts(CHANNEL_T *ChP,Word_t Flags) { Byte_t Mask; /* Interrupt Mask Register */ ChP->RxControl[2] &= ~((Byte_t)Flags & (RXINT_EN | SRCINT_EN | MCINT_EN)); sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->RxControl[0]); ChP->TxControl[2] &= ~((Byte_t)Flags & TXINT_EN); sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->TxControl[0]); if(Flags & CHANINT_EN) { Mask = sInB(ChP->IntMask) & sBitMapClrTbl[ChP->ChanNum]; sOutB(ChP->IntMask,Mask); } } /********************************************************************* Begin FreeBsd-specific driver code **********************************************************************/ static int rpprobe __P((struct isa_device *)); static int rpattach __P((struct isa_device *)); static const char* rp_pciprobe(pcici_t tag, pcidi_t type); static void rp_pciattach(pcici_t tag, int unit); static u_long rp_pcicount; static struct pci_device rp_pcidevice = { "rp", rp_pciprobe, rp_pciattach, &rp_pcicount, NULL }; DATA_SET (pcidevice_set, rp_pcidevice); static timeout_t rpdtrwakeup; struct isa_driver rpdriver = { rpprobe, rpattach, "rp" }; static char driver_name[] = "rp"; static d_open_t rpopen; static d_close_t rpclose; static d_read_t rpread; static d_write_t rpwrite; static d_ioctl_t rpioctl; static d_stop_t rpstop; static d_devtotty_t rpdevtotty; #define CDEV_MAJOR 81 static struct cdevsw rp_cdevsw = { rpopen, rpclose, rpread, rpwrite, rpioctl, rpstop, noreset, rpdevtotty, ttpoll, nommap, NULL, driver_name, NULL, -1, nodump, nopsize, D_TTY, }; static int rp_controller_port = 0; static int rp_num_ports_open = 0; static int ndevs = 0; static int minor_to_unit[128]; #if 0 static struct tty rp_tty[128]; #endif static int rp_num_ports[4]; /* Number of ports on each controller */ #define _INLINE_ __inline #define POLL_INTERVAL 1 #define CALLOUT_MASK 0x80 #define CONTROL_MASK 0x60 #define CONTROL_INIT_STATE 0x20 #define CONTROL_LOCK_STATE 0x40 #define DEV_UNIT(dev) (MINOR_TO_UNIT(minor(dev)) #define MINOR_MAGIC_MASK (CALLOUT_MASK | CONTROL_MASK) #define MINOR_MAGIC(dev) ((minor(dev)) & ~MINOR_MAGIC_MASK) #define IS_CALLOUT(dev) (minor(dev) & CALLOUT_MASK) #define IS_CONTROL(dev) (minor(dev) & CONTROL_MASK) #define RP_ISMULTIPORT(dev) ((dev)->id_flags & 0x1) #define RP_MPMASTER(dev) (((dev)->id_flags >> 8) & 0xff) #define RP_NOTAST4(dev) ((dev)->id_flags & 0x04) static struct rp_port *p_rp_addr[4]; static struct rp_port *p_rp_table[MAX_RP_PORTS]; #define rp_addr(unit) (p_rp_addr[unit]) #define rp_table(port) (p_rp_table[port]) /* * The top-level routines begin here */ int rpselect __P((dev_t, int, struct proc *)); static int rpparam __P((struct tty *, struct termios *)); static void rpstart __P((struct tty *)); static void rphardclose __P((struct rp_port *)); #define rpmap nomap #define rpreset noreset #define rpstrategy nostrategy static void rp_disc_optim __P((struct tty *tp, struct termios *t, struct rp_port *rp)); static _INLINE_ void rp_do_receive(struct rp_port *rp, struct tty *tp, CHANNEL_t *cp, unsigned int ChanStatus) { int spl; unsigned int CharNStat; int ToRecv, ch; ToRecv = sGetRxCnt(cp); if(ToRecv == 0) return; /* If status indicates there are errored characters in the FIFO, then enter status mode (a word in FIFO holds characters and status) */ if(ChanStatus & (RXFOVERFL | RXBREAK | RXFRAME | RXPARITY)) { if(!(ChanStatus & STATMODE)) { ChanStatus |= STATMODE; sEnRxStatusMode(cp); } } /* if we previously entered status mode then read down the FIFO one word at a time, pulling apart the character and the status. Update error counters depending on status. */ if(ChanStatus & STATMODE) { while(ToRecv) { if(tp->t_state & TS_TBLOCK) { break; } CharNStat = sInW(sGetTxRxDataIO(cp)); ch = CharNStat & 0xff; if((CharNStat & STMBREAK) || (CharNStat & STMFRAMEH)) ch |= TTY_FE; else if (CharNStat & STMPARITYH) ch |= TTY_PE; else if (CharNStat & STMRCVROVRH) rp->rp_overflows++; (*linesw[tp->t_line].l_rint)(ch, tp); ToRecv--; } /* After emtying FIFO in status mode, turn off status mode */ if(sGetRxCnt(cp) == 0) sDisRxStatusMode(cp); } else { while (ToRecv) { if(tp->t_state & TS_TBLOCK) { break; } ch = (u_char) sInB(sGetTxRxDataIO(cp)); spl = spltty(); (*linesw[tp->t_line].l_rint)(ch, tp); splx(spl); ToRecv--; } } } static _INLINE_ void rp_handle_port(struct rp_port *rp) { CHANNEL_t *cp; struct tty *tp; unsigned int IntMask, ChanStatus; /* int oldcts; */ if(!rp) return; cp = &rp->rp_channel; tp = rp->rp_tty; IntMask = sGetChanIntID(cp); IntMask = IntMask & rp->rp_intmask; ChanStatus = sGetChanStatus(cp); if(IntMask & RXF_TRIG) if(!(tp->t_state & TS_TBLOCK) && (tp->t_state & TS_CARR_ON) && (tp->t_state & TS_ISOPEN)) { rp_do_receive(rp, tp, cp, ChanStatus); } if(IntMask & DELTA_CD) { if(ChanStatus & CD_ACT) { if(!(tp->t_state & TS_CARR_ON) ) { (void)(*linesw[tp->t_line].l_modem)(tp, 1); } } else { if((tp->t_state & TS_CARR_ON)) { (void)(*linesw[tp->t_line].l_modem)(tp, 0); if((*linesw[tp->t_line].l_modem)(tp, 0) == 0) { rphardclose(rp); } } } } /* oldcts = rp->rp_cts; rp->rp_cts = ((ChanStatus & CTS_ACT) != 0); if(oldcts != rp->rp_cts) { printf("CTS change (now %s)... on port %d\n", rp->rp_cts ? "on" : "off", rp->rp_port); } */ } static void rp_do_poll(void *not_used) { CONTROLLER_t *ctl; struct rp_port *rp; struct tty *tp; int unit, aiop, ch, line, count; unsigned char CtlMask, AiopMask; for(unit = 0; unit <= ndevs; unit++) { rp = rp_addr(unit); ctl = rp->rp_ctlp; if(ctl->BusType == isPCI) CtlMask = sPCIGetControllerIntStatus(ctl); else CtlMask = sGetControllerIntStatus(ctl); for(aiop=0; CtlMask; CtlMask >>=1, aiop++) { if(CtlMask & 1) { AiopMask = sGetAiopIntStatus(ctl, aiop); for(ch = 0; AiopMask; AiopMask >>=1, ch++) { if(AiopMask & 1) { line = (unit << 5) | (aiop << 3) | ch; rp = rp_table(line); rp_handle_port(rp); } } } } for(line = 0, rp = rp_addr(unit); line < rp_num_ports[unit]; line++, rp++) { tp = rp->rp_tty; if((tp->t_state & TS_BUSY) && (tp->t_state & TS_ISOPEN)) { count = sGetTxCnt(&rp->rp_channel); if(count == 0) tp->t_state &= ~(TS_BUSY); if(!(tp->t_state & TS_TTSTOP) && (count <= rp->rp_restart)) { (*linesw[tp->t_line].l_start)(tp); } } } } if(rp_num_ports_open) timeout(rp_do_poll, (void *)NULL, POLL_INTERVAL); } static const char* rp_pciprobe(pcici_t tag, pcidi_t type) { int vendor_id; vendor_id = type & 0xffff; switch(vendor_id) case 0x11fe: return("rp"); return(NULL); } static int rpprobe(dev) struct isa_device *dev; { int controller, unit; int aiop, num_aiops; unsigned int aiopio[MAX_AIOPS_PER_BOARD]; CONTROLLER_t *ctlp; unit = dev->id_unit; if (dev->id_unit >= 4) { printf("rpprobe: unit number %d invalid.\n", dev->id_unit); return 1; } printf("probing for RocketPort(ISA) unit %d\n", unit); if (rp_controller_port) controller = rp_controller_port; else { controller = dev->id_iobase + 0x40; } for (aiop=0; aiopid_iobase + (aiop * 0x400); ctlp = sCtlNumToCtlPtr(dev->id_unit); num_aiops = sInitController(ctlp, dev->id_unit, controller + ((unit-rp_pcicount)*0x400), aiopio, MAX_AIOPS_PER_BOARD, 0, FREQ_DIS, 0); if (num_aiops <= 0) { printf("board%d init failed\n", unit); return 0; } if (rp_controller_port) { dev->id_msize = 64; } else { dev->id_msize = 68; rp_controller_port = controller; } dev->id_irq = 0; return 1; } static void rp_pciattach(pcici_t tag, int unit) { dev_t rp_dev; int success, oldspl; u_short iobase; int num_ports, num_chan, num_aiops; int aiop, chan, port; int ChanStatus, line, i, count; unsigned int aiopio[MAX_AIOPS_PER_BOARD]; struct rp_port *rp; struct tty *tty; CONTROLLER_t *ctlp; success = pci_map_port(tag, 0x10, &iobase); if(!success) printf("ioaddr mapping failed for RocketPort(PCI)\n"); for(aiop=0; aiop < MAX_AIOPS_PER_BOARD; aiop++) aiopio[aiop] = iobase + (aiop * 0x40); ctlp = sCtlNumToCtlPtr(unit); num_aiops = sPCIInitController(ctlp, unit, aiopio, MAX_AIOPS_PER_BOARD, 0, FREQ_DIS, 0); num_ports = 0; for(aiop=0; aiop < num_aiops; aiop++) { sResetAiopByNum(ctlp, aiop); num_ports += sGetAiopNumChan(ctlp, aiop); } printf("RocketPort%d = %d ports\n", unit, num_ports); rp_num_ports[unit] = num_ports; rp = (struct rp_port *) malloc(sizeof(struct rp_port) * num_ports, M_TTYS, M_NOWAIT); if(rp == 0) { printf("rp_attach: Could not malloc rp_ports structures\n"); return; } count = unit * 32; /* board times max ports per card SG */ for(i=count;i < (count + rp_num_ports[unit]);i++) minor_to_unit[i] = unit; bzero(rp, sizeof(struct rp_port) * num_ports); tty = (struct tty *) malloc(sizeof(struct tty) * num_ports, M_TTYS, M_NOWAIT); if(tty == 0) { printf("rp_attach: Could not malloc tty structures\n"); return; } bzero(tty, sizeof(struct tty) * num_ports); oldspl = spltty(); rp_addr(unit) = rp; splx(oldspl); rp_dev = makedev(CDEV_MAJOR, unit); cdevsw_add(&rp_dev, &rp_cdevsw, NULL); port = 0; for(aiop=0; aiop < num_aiops; aiop++) { num_chan = sGetAiopNumChan(ctlp, aiop); for(chan=0; chan < num_chan; chan++, port++, rp++, tty++) { rp->rp_tty = tty; rp->rp_port = port; rp->rp_ctlp = ctlp; rp->rp_unit = unit; rp->rp_chan = chan; rp->rp_aiop = aiop; tty->t_line = 0; /* tty->t_termios = deftermios; */ rp->dtr_wait = 3 * hz; rp->it_in.c_iflag = 0; rp->it_in.c_oflag = 0; rp->it_in.c_cflag = TTYDEF_CFLAG; rp->it_in.c_lflag = 0; termioschars(&rp->it_in); /* termioschars(&tty->t_termios); */ rp->it_in.c_ispeed = rp->it_in.c_ospeed = TTYDEF_SPEED; rp->it_out = rp->it_in; rp->rp_intmask = RXF_TRIG | TXFIFO_MT | SRC_INT | DELTA_CD | DELTA_CTS | DELTA_DSR; ChanStatus = sGetChanStatus(&rp->rp_channel); if(sInitChan(ctlp, &rp->rp_channel, aiop, chan) == 0) { printf("RocketPort sInitChan(%d, %d, %d) failed \n", unit, aiop, chan); return; } ChanStatus = sGetChanStatus(&rp->rp_channel); rp->rp_cts = (ChanStatus & CTS_ACT) != 0; line = (unit << 5) | (aiop << 3) | chan; rp_table(line) = rp; /* devfs_add_devswf(&rp_cdevsw, port, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "ttyR%r", port); devfs_add_devswf(&rp_cdevsw, port | CONTROL_INIT_STATE, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "ttyRi%r", port); */ } } } static int rpattach(dev) struct isa_device *dev; { - struct isa_device *idev; dev_t rp_dev; int iobase, unit, /*rpmajor,*/ oldspl; int num_ports, num_chan, num_aiops; int aiop, chan, port; int ChanStatus, line, i, count; unsigned int aiopio[MAX_AIOPS_PER_BOARD]; struct rp_port *rp; struct tty *tty; CONTROLLER_t *ctlp; iobase = dev->id_iobase; unit = dev->id_unit; ndevs = unit; for(aiop=0; aiop < MAX_AIOPS_PER_BOARD; aiop++) aiopio[aiop] = iobase + (aiop * 0x400); ctlp = sCtlNumToCtlPtr(unit); num_aiops = sInitController(ctlp, unit, rp_controller_port + ((unit-rp_pcicount) * 0x400), aiopio, MAX_AIOPS_PER_BOARD, 0, FREQ_DIS, 0); num_ports = 0; for(aiop=0; aiop < num_aiops; aiop++) { sResetAiopByNum(ctlp, aiop); sEnAiop(ctlp, aiop); num_ports += sGetAiopNumChan(ctlp, aiop); } printf("RocketPort%d = %d ports\n", unit, num_ports); rp_num_ports[unit] = num_ports; rp = (struct rp_port *) malloc(sizeof(struct rp_port) * num_ports, M_TTYS, M_NOWAIT); if(rp == 0) { printf("rp_attach: Could not malloc rp_ports structures\n"); return(0); } count = unit * 32; /* board # times max ports per card SG */ for(i=count;i < (count + rp_num_ports[unit]);i++) minor_to_unit[i] = unit; bzero(rp, sizeof(struct rp_port) * num_ports); tty = (struct tty *) malloc(sizeof(struct tty) * num_ports, M_TTYS, M_NOWAIT); if(tty == 0) { printf("rp_attach: Could not malloc tty structures\n"); return(0); } bzero(tty, sizeof(struct tty) * num_ports); oldspl = spltty(); rp_addr(unit) = rp; splx(oldspl); rp_dev = makedev(CDEV_MAJOR, unit); cdevsw_add(&rp_dev, &rp_cdevsw, NULL); port = 0; for(aiop=0; aiop < num_aiops; aiop++) { num_chan = sGetAiopNumChan(ctlp, aiop); for(chan=0; chan < num_chan; chan++, port++, rp++, tty++) { rp->rp_tty = tty; rp->rp_port = port; rp->rp_ctlp = ctlp; rp->rp_unit = unit; rp->rp_chan = chan; rp->rp_aiop = aiop; tty->t_line = 0; /* tty->t_termios = deftermios; */ rp->dtr_wait = 3 * hz; rp->it_in.c_iflag = 0; rp->it_in.c_oflag = 0; rp->it_in.c_cflag = TTYDEF_CFLAG; rp->it_in.c_lflag = 0; termioschars(&rp->it_in); /* termioschars(&tty->t_termios); */ rp->it_in.c_ispeed = rp->it_in.c_ospeed = TTYDEF_SPEED; rp->it_out = rp->it_in; rp->rp_intmask = RXF_TRIG | TXFIFO_MT | SRC_INT | DELTA_CD | DELTA_CTS | DELTA_DSR; ChanStatus = sGetChanStatus(&rp->rp_channel); if(sInitChan(ctlp, &rp->rp_channel, aiop, chan) == 0) { printf("RocketPort sInitChan(%d, %d, %d) failed \n", unit, aiop, chan); return(0); } ChanStatus = sGetChanStatus(&rp->rp_channel); rp->rp_cts = (ChanStatus & CTS_ACT) != 0; line = (unit << 5) | (aiop << 3) | chan; rp_table(line) = rp; } } - idev = find_isadev(isa_devtab_tty, &rpdriver, - RP_MPMASTER(dev) + rp_pcicount); - if(idev == NULL) { - printf("rp%d: master device %d not configured\n", - dev->id_unit, RP_MPMASTER(dev)); - } -/* printf("COOL!! Device is found!!\n"); for(rpmajor=0;rpmajor> 16) -1) * 32); /* SG */ port = (minor(dev) & 0x1f); /* SG */ mynor = (port + umynor); /* SG */ unit = minor_to_unit[mynor]; if(IS_CONTROL(dev)) return(0); rp = rp_addr(unit) + port; /* rp->rp_tty = &rp_tty[rp->rp_port]; */ tp = rp->rp_tty; oldspl = spltty(); open_top: while(rp->state & ~SET_DTR) { error = tsleep(&rp->dtr_wait, TTIPRI | PCATCH, "rpdtr", 0); if(error != 0) goto out; } if(tp->t_state & TS_ISOPEN) { if(IS_CALLOUT(dev)) { if(!rp->active_out) { error = EBUSY; goto out; } } else { if(rp->active_out) { if(flag & O_NONBLOCK) { error = EBUSY; goto out; } error = tsleep(&rp->active_out, TTIPRI | PCATCH, "rpbi", 0); if(error != 0) goto out; goto open_top; } } if(tp->t_state & TS_XCLUDE && suser(p->p_ucred, &p->p_acflag)) { splx(oldspl); return(EBUSY); } } else { tp->t_dev = dev; tp->t_param = rpparam; tp->t_oproc = rpstart; tp->t_line = 0; tp->t_termios = IS_CALLOUT(dev) ? rp->it_out : rp->it_in; flags = 0; flags |= SET_RTS; flags |= SET_DTR; rp->rp_channel.TxControl[3] = ((rp->rp_channel.TxControl[3] & ~(SET_RTS | SET_DTR)) | flags); sOutDW(rp->rp_channel.IndexAddr, *(DWord_t *) &(rp->rp_channel.TxControl[0])); sSetRxTrigger(&rp->rp_channel, TRIG_1); sDisRxStatusMode(&rp->rp_channel); sFlushRxFIFO(&rp->rp_channel); sFlushTxFIFO(&rp->rp_channel); sEnInterrupts(&rp->rp_channel, (TXINT_EN|MCINT_EN|RXINT_EN|SRCINT_EN|CHANINT_EN)); sSetRxTrigger(&rp->rp_channel, TRIG_1); sDisRxStatusMode(&rp->rp_channel); sClrTxXOFF(&rp->rp_channel); /* sDisRTSFlowCtl(&rp->rp_channel); sDisCTSFlowCtl(&rp->rp_channel); */ sDisTxSoftFlowCtl(&rp->rp_channel); sStartRxProcessor(&rp->rp_channel); sEnRxFIFO(&rp->rp_channel); sEnTransmit(&rp->rp_channel); /* sSetDTR(&rp->rp_channel); sSetRTS(&rp->rp_channel); */ ++rp->wopeners; error = rpparam(tp, &tp->t_termios); --rp->wopeners; if(error != 0) { splx(oldspl); return(error); } rp_num_ports_open++; IntMask = sGetChanIntID(&rp->rp_channel); IntMask = IntMask & rp->rp_intmask; ChanStatus = sGetChanStatus(&rp->rp_channel); if((IntMask & DELTA_CD) || IS_CALLOUT(dev)) { if((ChanStatus & CD_ACT) || IS_CALLOUT(dev)) { (void)(*linesw[tp->t_line].l_modem)(tp, 1); } } if(rp_num_ports_open == 1) timeout(rp_do_poll, (void *)NULL, POLL_INTERVAL); } if(!(flag&O_NONBLOCK) && !(tp->t_cflag&CLOCAL) && !(tp->t_state & TS_CARR_ON) && !(IS_CALLOUT(dev))) { ++rp->wopeners; error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "rpdcd", 0); --rp->wopeners; if(error != 0) goto out; goto open_top; } error = (*linesw[tp->t_line].l_open)(dev, tp); rp_disc_optim(tp, &tp->t_termios, rp); if(tp->t_state & TS_ISOPEN && IS_CALLOUT(dev)) rp->active_out = TRUE; /* if(rp_num_ports_open == 1) timeout(rp_do_poll, (void *)NULL, POLL_INTERVAL); */ out: splx(oldspl); if(!(tp->t_state & TS_ISOPEN) && rp->wopeners == 0) { rphardclose(rp); } return(error); } int rpclose(dev, flag, mode, p) dev_t dev; int flag, mode; struct proc *p; { int oldspl, unit, mynor, umynor, port; /* SG */ struct rp_port *rp; struct tty *tp; CHANNEL_t *cp; umynor = (((minor(dev) >> 16) -1) * 32); /* SG */ port = (minor(dev) & 0x1f); /* SG */ mynor = (port + umynor); /* SG */ unit = minor_to_unit[mynor]; /* SG */ if(IS_CONTROL(dev)) return(0); rp = rp_addr(unit) + port; cp = &rp->rp_channel; tp = rp->rp_tty; oldspl = spltty(); (*linesw[tp->t_line].l_close)(tp, flag); rp_disc_optim(tp, &tp->t_termios, rp); rpstop(tp, FREAD | FWRITE); rphardclose(rp); tp->t_state &= ~TS_BUSY; ttyclose(tp); splx(oldspl); return(0); } static void rphardclose(struct rp_port *rp) { int mynor; struct tty *tp; CHANNEL_t *cp; cp = &rp->rp_channel; tp = rp->rp_tty; mynor = MINOR_MAGIC(tp->t_dev); sFlushRxFIFO(cp); sFlushTxFIFO(cp); sDisTransmit(cp); sDisInterrupts(cp, TXINT_EN|MCINT_EN|RXINT_EN|SRCINT_EN|CHANINT_EN); sDisRTSFlowCtl(cp); sDisCTSFlowCtl(cp); sDisTxSoftFlowCtl(cp); sClrTxXOFF(cp); if(tp->t_cflag&HUPCL || !(tp->t_state&TS_ISOPEN) || !rp->active_out) { sClrDTR(cp); } if(IS_CALLOUT(tp->t_dev)) { sClrDTR(cp); } if(rp->dtr_wait != 0) { timeout(rpdtrwakeup, rp, rp->dtr_wait); rp->state |= ~SET_DTR; } rp->active_out = FALSE; wakeup(&rp->active_out); wakeup(TSA_CARR_ON(tp)); } static int rpread(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { struct rp_port *rp; struct tty *tp; int unit, mynor, umynor, port, error = 0; /* SG */ umynor = (((minor(dev) >> 16) -1) * 32); /* SG */ port = (minor(dev) & 0x1f); /* SG */ mynor = (port + umynor); /* SG */ unit = minor_to_unit[mynor]; /* SG */ if(IS_CONTROL(dev)) return(ENODEV); rp = rp_addr(unit) + port; tp = rp->rp_tty; error = (*linesw[tp->t_line].l_read)(tp, uio, flag); return(error); } static int rpwrite(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { struct rp_port *rp; struct tty *tp; int unit, mynor, port, umynor, error = 0; /* SG */ umynor = (((minor(dev) >> 16) -1) * 32); /* SG */ port = (minor(dev) & 0x1f); /* SG */ mynor = (port + umynor); /* SG */ unit = minor_to_unit[mynor]; /* SG */ if(IS_CONTROL(dev)) return(ENODEV); rp = rp_addr(unit) + port; tp = rp->rp_tty; while(rp->rp_disable_writes) { rp->rp_waiting = 1; if(error = ttysleep(tp, (caddr_t)rp, TTOPRI|PCATCH, "rp_write", 0)) { return(error); } } error = (*linesw[tp->t_line].l_write)(tp, uio, flag); return error; } static void rpdtrwakeup(void *chan) { struct rp_port *rp; rp = (struct rp_port *)chan; rp->state &= SET_DTR; wakeup(&rp->dtr_wait); } int rpioctl(dev, cmd, data, flag, p) dev_t dev; u_long cmd; caddr_t data; int flag; struct proc *p; { struct rp_port *rp; CHANNEL_t *cp; struct tty *tp; int unit, mynor, port, umynor; /* SG */ int oldspl; int error = 0; int arg, flags, result, ChanStatus; int oldcmd; struct termios term, *t; umynor = (((minor(dev) >> 16) -1) * 32); /* SG */ port = (minor(dev) & 0x1f); /* SG */ mynor = (port + umynor); /* SG */ unit = minor_to_unit[mynor]; rp = rp_addr(unit) + port; if(IS_CONTROL(dev)) { struct termios *ct; switch (IS_CONTROL(dev)) { case CONTROL_INIT_STATE: ct = IS_CALLOUT(dev) ? &rp->it_out : &rp->it_in; break; case CONTROL_LOCK_STATE: ct = IS_CALLOUT(dev) ? &rp->lt_out : &rp->lt_in; break; default: return(ENODEV); /* /dev/nodev */ } switch (cmd) { case TIOCSETA: error = suser(p->p_ucred, &p->p_acflag); if(error != 0) return(error); *ct = *(struct termios *)data; return(0); case TIOCGETA: *(struct termios *)data = *ct; return(0); case TIOCGETD: *(int *)data = TTYDISC; return(0); case TIOCGWINSZ: bzero(data, sizeof(struct winsize)); return(0); default: return(ENOTTY); } } tp = rp->rp_tty; cp = &rp->rp_channel; #if defined(COMPAT_43) || defined(COMPAT_SUNOS) term = tp->t_termios; oldcmd = cmd; error = ttsetcompat(tp, &cmd, data, &term); if(error != 0) return(error); if(cmd != oldcmd) { data = (caddr_t)&term; } #endif if((cmd == TIOCSETA) || (cmd == TIOCSETAW) || (cmd == TIOCSETAF)) { int cc; struct termios *dt = (struct termios *)data; struct termios *lt = IS_CALLOUT(dev) ? &rp->lt_out : &rp->lt_in; dt->c_iflag = (tp->t_iflag & lt->c_iflag) | (dt->c_iflag & ~lt->c_iflag); dt->c_oflag = (tp->t_oflag & lt->c_oflag) | (dt->c_oflag & ~lt->c_oflag); dt->c_cflag = (tp->t_cflag & lt->c_cflag) | (dt->c_cflag & ~lt->c_cflag); dt->c_lflag = (tp->t_lflag & lt->c_lflag) | (dt->c_lflag & ~lt->c_lflag); for(cc = 0; cc < NCCS; ++cc) if(lt->c_cc[cc] = tp->t_cc[cc]) dt->c_cc[cc] = tp->t_cc[cc]; if(lt->c_ispeed != 0) dt->c_ispeed = tp->t_ispeed; if(lt->c_ospeed != 0) dt->c_ospeed = tp->t_ospeed; } t = &tp->t_termios; error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); if(error != ENOIOCTL) { return(error); } oldspl = spltty(); flags = rp->rp_channel.TxControl[3]; error = ttioctl(tp, cmd, data, flag); flags = rp->rp_channel.TxControl[3]; rp_disc_optim(tp, &tp->t_termios, rp); if(error != ENOIOCTL) { splx(oldspl); return(error); } switch(cmd) { case TIOCSBRK: sSendBreak(&rp->rp_channel); break; case TIOCCBRK: sClrBreak(&rp->rp_channel); break; case TIOCSDTR: sSetDTR(&rp->rp_channel); sSetRTS(&rp->rp_channel); break; case TIOCCDTR: sClrDTR(&rp->rp_channel); break; case TIOCMSET: arg = *(int *) data; flags = 0; if(arg & TIOCM_RTS) flags |= SET_RTS; if(arg & TIOCM_DTR) flags |= SET_DTR; rp->rp_channel.TxControl[3] = ((rp->rp_channel.TxControl[3] & ~(SET_RTS | SET_DTR)) | flags); sOutDW(rp->rp_channel.IndexAddr, *(DWord_t *) &(rp->rp_channel.TxControl[0])); break; case TIOCMBIS: arg = *(int *) data; flags = 0; if(arg & TIOCM_RTS) flags |= SET_RTS; if(arg & TIOCM_DTR) flags |= SET_DTR; rp->rp_channel.TxControl[3] |= flags; sOutDW(rp->rp_channel.IndexAddr, *(DWord_t *) &(rp->rp_channel.TxControl[0])); break; case TIOCMBIC: arg = *(int *) data; flags = 0; if(arg & TIOCM_RTS) flags |= SET_RTS; if(arg & TIOCM_DTR) flags |= SET_DTR; rp->rp_channel.TxControl[3] &= ~flags; sOutDW(rp->rp_channel.IndexAddr, *(DWord_t *) &(rp->rp_channel.TxControl[0])); break; case TIOCMGET: ChanStatus = sGetChanStatusLo(&rp->rp_channel); flags = rp->rp_channel.TxControl[3]; result = TIOCM_LE; /* always on while open for some reason */ result |= (((flags & SET_DTR) ? TIOCM_DTR : 0) | ((flags & SET_RTS) ? TIOCM_RTS : 0) | ((ChanStatus & CD_ACT) ? TIOCM_CAR : 0) | ((ChanStatus & DSR_ACT) ? TIOCM_DSR : 0) | ((ChanStatus & CTS_ACT) ? TIOCM_CTS : 0)); if(rp->rp_channel.RxControl[2] & RTSFC_EN) { result |= TIOCM_RTS; } *(int *)data = result; break; case TIOCMSDTRWAIT: error = suser(p->p_ucred, &p->p_acflag); if(error != 0) { splx(oldspl); return(error); } rp->dtr_wait = *(int *)data * hz/100; break; case TIOCMGDTRWAIT: *(int *)data = rp->dtr_wait * 100/hz; break; default: splx(oldspl); return ENOTTY; } splx(oldspl); return(0); } static struct speedtab baud_table[] = { B0, 0, B50, BRD50, B75, BRD75, B110, BRD110, B134, BRD134, B150, BRD150, B200, BRD200, B300, BRD300, B600, BRD600, B1200, BRD1200, B1800, BRD1800, B2400, BRD2400, B4800, BRD4800, B9600, BRD9600, B19200, BRD19200, B38400, BRD38400, B7200, BRD7200, B14400, BRD14400, B57600, BRD57600, B76800, BRD76800, B115200, BRD115200, B230400, BRD230400, -1, -1 }; static int rpparam(tp, t) struct tty *tp; struct termios *t; { struct rp_port *rp; CHANNEL_t *cp; int unit, mynor, port, umynor; /* SG */ int oldspl, cflag, iflag, oflag, lflag; int ospeed; umynor = (((minor(tp->t_dev) >> 16) -1) * 32); /* SG */ port = (minor(tp->t_dev) & 0x1f); /* SG */ mynor = (port + umynor); /* SG */ unit = minor_to_unit[mynor]; rp = rp_addr(unit) + port; cp = &rp->rp_channel; oldspl = spltty(); cflag = t->c_cflag; iflag = t->c_iflag; oflag = t->c_oflag; lflag = t->c_lflag; ospeed = ttspeedtab(t->c_ispeed, baud_table); if(ospeed < 0 || t->c_ispeed != t->c_ospeed) return(EINVAL); tp->t_ispeed = t->c_ispeed; tp->t_ospeed = t->c_ospeed; tp->t_cflag = cflag; tp->t_iflag = iflag; tp->t_oflag = oflag; tp->t_lflag = lflag; if(t->c_ospeed == 0) { sClrDTR(cp); return(0); } rp->rp_fifo_lw = ((t->c_ospeed*2) / 1000) +1; /* Set baud rate ----- we only pay attention to ispeed */ sSetDTR(cp); sSetRTS(cp); sSetBaud(cp, ospeed); if(cflag & CSTOPB) { sSetStop2(cp); } else { sSetStop1(cp); } if(cflag & PARENB) { sEnParity(cp); if(cflag & PARODD) { sSetOddParity(cp); } else { sSetEvenParity(cp); } } else { sDisParity(cp); } if((cflag & CSIZE) == CS8) { sSetData8(cp); rp->rp_imask = 0xFF; } else { sSetData7(cp); rp->rp_imask = 0x7F; } if(iflag & ISTRIP) { rp->rp_imask &= 0x7F; } if(cflag & CLOCAL) { rp->rp_intmask &= ~DELTA_CD; } else { rp->rp_intmask |= DELTA_CD; } /* Put flow control stuff here */ if(cflag & CCTS_OFLOW) { sEnCTSFlowCtl(cp); } else { sDisCTSFlowCtl(cp); } if(cflag & CRTS_IFLOW) { rp->rp_rts_iflow = 1; } else { rp->rp_rts_iflow = 0; } if(cflag & CRTS_IFLOW) { sEnRTSFlowCtl(cp); } else { sDisRTSFlowCtl(cp); } rp_disc_optim(tp, t, rp); if((cflag & CLOCAL) || (sGetChanStatusLo(cp) & CD_ACT)) { tp->t_state |= TS_CARR_ON; wakeup(TSA_CARR_ON(tp)); } /* tp->t_state |= TS_CAN_BYPASS_L_RINT; flags = rp->rp_channel.TxControl[3]; if(flags & SET_DTR) else if(flags & SET_RTS) else */ splx(oldspl); return(0); } static void rp_disc_optim(tp, t, rp) struct tty *tp; struct termios *t; struct rp_port *rp; { if(!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON)) &&(!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK)) &&(!(t->c_iflag & PARMRK) ||(t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK)) && !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN)) && linesw[tp->t_line].l_rint == ttyinput) tp->t_state |= TS_CAN_BYPASS_L_RINT; else tp->t_state &= ~TS_CAN_BYPASS_L_RINT; } static void rpstart(tp) struct tty *tp; { struct rp_port *rp; CHANNEL_t *cp; struct clist *qp; int unit, mynor, port, umynor; /* SG */ char ch, flags; int spl, xmit_fifo_room; int count; umynor = (((minor(tp->t_dev) >> 16) -1) * 32); /* SG */ port = (minor(tp->t_dev) & 0x1f); /* SG */ mynor = (port + umynor); /* SG */ unit = minor_to_unit[mynor]; rp = rp_addr(unit) + port; cp = &rp->rp_channel; flags = rp->rp_channel.TxControl[3]; spl = spltty(); if(tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) { ttwwakeup(tp); splx(spl); return; } if(rp->rp_xmit_stopped) { sEnTransmit(cp); rp->rp_xmit_stopped = 0; } count = sGetTxCnt(cp); if(tp->t_outq.c_cc == 0) { if((tp->t_state & TS_BUSY) && (count == 0)) { tp->t_state &= ~TS_BUSY; } ttwwakeup(tp); splx(spl); return; } xmit_fifo_room = TXFIFO_SIZE - sGetTxCnt(cp); qp = &tp->t_outq; count = 0; if(xmit_fifo_room > 0 && qp->c_cc > 0) { tp->t_state |= TS_BUSY; } while(xmit_fifo_room > 0 && qp->c_cc > 0) { ch = getc(qp); sOutB(sGetTxRxDataIO(cp), ch); xmit_fifo_room--; count++; } rp->rp_restart = (qp->c_cc > 0) ? rp->rp_fifo_lw : 0; ttwwakeup(tp); splx(spl); } static void rpstop(tp, flag) register struct tty *tp; int flag; { struct rp_port *rp; CHANNEL_t *cp; int unit, mynor, port, umynor; /* SG */ int spl; umynor = (((minor(tp->t_dev) >> 16) -1) * 32); /* SG */ port = (minor(tp->t_dev) & 0x1f); /* SG */ mynor = (port + umynor); /* SG */ unit = minor_to_unit[mynor]; rp = rp_addr(unit) + port; cp = &rp->rp_channel; spl = spltty(); if(tp->t_state & TS_BUSY) { if((tp->t_state&TS_TTSTOP) == 0) { sFlushTxFIFO(cp); } else { if(rp->rp_xmit_stopped == 0) { sDisTransmit(cp); rp->rp_xmit_stopped = 1; } } } splx(spl); rpstart(tp); } int rpselect(dev, flag, p) dev_t dev; int flag; struct proc *p; { return(0); } struct tty * rpdevtotty(dev_t dev) { struct rp_port *rp; int unit, port, mynor, umynor; /* SG */ umynor = (((minor(dev) >> 16) -1) * 32); /* SG */ port = (minor(dev) & 0x1f); /* SG */ mynor = (port + umynor); /* SG */ unit = minor_to_unit[mynor]; /* SG */ if(IS_CONTROL(dev)) return(NULL); rp = rp_addr(unit) + port; return(rp->rp_tty); } Index: head/sys/dev/sio/sio.c =================================================================== --- head/sys/dev/sio/sio.c (revision 45719) +++ head/sys/dev/sio/sio.c (revision 45720) @@ -1,3196 +1,3219 @@ /*- * 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. * - * $Id: sio.c,v 1.220 1999/01/19 00:21:47 peter Exp $ + * $Id: sio.c,v 1.221 1999/01/30 12:17:35 phk Exp $ * from: @(#)com.c 7.5 (Berkeley) 5/16/91 * from: i386/isa sio.c,v 1.215 */ #include "opt_comconsole.h" #include "opt_compat.h" #include "opt_ddb.h" #include "opt_devfs.h" /* #include "opt_sio.h" */ #include "sio.h" /* #include "pnp.h" */ #define NPNP 0 /* * Serial driver, based on 386BSD-0.1 com driver. * Mostly rewritten to use pseudo-DMA. * Works for National Semiconductor NS8250-NS16550AF UARTs. * COM driver, based on HP dca driver. * * Changes for PC-Card integration: * - Added PC-Card driver table and handlers */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include #include #ifdef DEVFS #include #endif #include #include #include #include #include #include #include #ifdef COM_ESP #include #endif #include #if 0 #include "card.h" #if NCARD > 0 #include #include #include #endif #if NPNP > 0 #include #endif #endif +#ifndef __i386__ #define disable_intr() 0 #define enable_intr() 0 +#endif #ifdef SMP #define disable_intr() COM_DISABLE_INTR() #define enable_intr() COM_ENABLE_INTR() #endif /* SMP */ #ifndef EXTRA_SIO #if NPNP > 0 #define EXTRA_SIO MAX_PNP_CARDS #else #define EXTRA_SIO 0 #endif #endif #define NSIOTOT (NSIO + EXTRA_SIO) #define LOTS_OF_EVENTS 64 /* helps separate urgent events from input */ #define RS_IBUFSIZE 256 #define CALLOUT_MASK 0x80 #define CONTROL_MASK 0x60 #define CONTROL_INIT_STATE 0x20 #define CONTROL_LOCK_STATE 0x40 #define DEV_TO_UNIT(dev) (MINOR_TO_UNIT(minor(dev))) #define MINOR_MAGIC_MASK (CALLOUT_MASK | CONTROL_MASK) #define MINOR_TO_UNIT(mynor) ((mynor) & ~MINOR_MAGIC_MASK) #ifdef COM_MULTIPORT /* checks in flags for multiport and which is multiport "master chip" * for a given card */ #define COM_ISMULTIPORT(flags) ((flags) & 0x01) #define COM_MPMASTER(flags) (((flags) >> 8) & 0x0ff) #define COM_NOTAST4(flags) ((flags) & 0x04) #endif /* COM_MULTIPORT */ #define COM_CONSOLE(flags) ((flags) & 0x10) #define COM_FORCECONSOLE(flags) ((flags) & 0x20) #define COM_LLCONSOLE(flags) ((flags) & 0x40) #define COM_LOSESOUTINTS(flags) ((flags) & 0x08) #define COM_NOFIFO(flags) ((flags) & 0x02) #define COM_ST16650A(flags) ((flags) & 0x20000) #define COM_C_NOPROBE (0x40000) #define COM_NOPROBE(flags) ((flags) & COM_C_NOPROBE) #define COM_C_IIR_TXRDYBUG (0x80000) #define COM_IIR_TXRDYBUG(flags) ((flags) & COM_C_IIR_TXRDYBUG) #define COM_FIFOSIZE(flags) (((flags) & 0xff000000) >> 24) #define com_scr 7 /* scratch register for 16450-16550 (R/W) */ /* * Input buffer watermarks. * The external device is asked to stop sending when the buffer exactly reaches * high water, or when the high level requests it. * The high level is notified immediately (rather than at a later clock tick) * when this watermark is reached. * The buffer size is chosen so the watermark should almost never be reached. * The low watermark is invisibly 0 since the buffer is always emptied all at * once. */ #define RS_IHIGHWATER (3 * RS_IBUFSIZE / 4) /* * com state bits. * (CS_BUSY | CS_TTGO) and (CS_BUSY | CS_TTGO | CS_ODEVREADY) must be higher * than the other bits so that they can be tested as a group without masking * off the low bits. * * The following com and tty flags correspond closely: * CS_BUSY = TS_BUSY (maintained by comstart(), siopoll() and * siostop()) * CS_TTGO = ~TS_TTSTOP (maintained by comparam() and comstart()) * CS_CTS_OFLOW = CCTS_OFLOW (maintained by comparam()) * CS_RTS_IFLOW = CRTS_IFLOW (maintained by comparam()) * TS_FLUSH is not used. * XXX I think TIOCSETA doesn't clear TS_TTSTOP when it clears IXON. * XXX CS_*FLOW should be CF_*FLOW in com->flags (control flags not state). */ #define CS_BUSY 0x80 /* output in progress */ #define CS_TTGO 0x40 /* output not stopped by XOFF */ #define CS_ODEVREADY 0x20 /* external device h/w ready (CTS) */ #define CS_CHECKMSR 1 /* check of MSR scheduled */ #define CS_CTS_OFLOW 2 /* use CTS output flow control */ #define CS_DTR_OFF 0x10 /* DTR held off */ #define CS_ODONE 4 /* output completed */ #define CS_RTS_IFLOW 8 /* use RTS input flow control */ #define CSE_BUSYCHECK 1 /* siobusycheck() scheduled */ static char const * const error_desc[] = { #define CE_OVERRUN 0 "silo overflow", #define CE_INTERRUPT_BUF_OVERFLOW 1 "interrupt-level buffer overflow", #define CE_TTY_BUF_OVERFLOW 2 "tty-level buffer overflow", }; #define CE_NTYPES 3 #define CE_RECORD(com, errnum) (++(com)->delta_error_counts[errnum]) /* types. XXX - should be elsewhere */ typedef u_int Port_t; /* hardware port */ typedef u_char bool_t; /* boolean */ /* queue of linear buffers */ struct lbq { u_char *l_head; /* next char to process */ u_char *l_tail; /* one past the last char to process */ struct lbq *l_next; /* next in queue */ bool_t l_queued; /* nonzero if queued */ }; /* com device structure */ struct com_s { u_int flags; /* Copy isa device flags */ u_char state; /* miscellaneous flag bits */ bool_t active_out; /* nonzero if the callout device is open */ u_char cfcr_image; /* copy of value written to CFCR */ #ifdef COM_ESP bool_t esp; /* is this unit a hayes esp board? */ #endif u_char extra_state; /* more flag bits, separate for order trick */ u_char fifo_image; /* copy of value written to FIFO */ bool_t hasfifo; /* nonzero for 16550 UARTs */ bool_t st16650a; /* Is a Startech 16650A or RTS/CTS compat */ bool_t loses_outints; /* nonzero if device loses output interrupts */ u_char mcr_image; /* copy of value written to MCR */ #ifdef COM_MULTIPORT bool_t multiport; /* is this unit part of a multiport device? */ #endif /* COM_MULTIPORT */ bool_t no_irq; /* nonzero if irq is not attached */ bool_t gone; /* hardware disappeared */ bool_t poll; /* nonzero if polling is required */ bool_t poll_output; /* nonzero if polling for output is required */ int unit; /* unit number */ int dtr_wait; /* time to hold DTR down on close (* 1/hz) */ u_int tx_fifo_size; u_int wopeners; /* # processes waiting for DCD in open() */ /* * The high level of the driver never reads status registers directly * because there would be too many side effects to handle conveniently. * Instead, it reads copies of the registers stored here by the * interrupt handler. */ u_char last_modem_status; /* last MSR read by intr handler */ u_char prev_modem_status; /* last MSR handled by high level */ u_char hotchar; /* ldisc-specific char to be handled ASAP */ u_char *ibuf; /* start of input buffer */ u_char *ibufend; /* end of input buffer */ u_char *ihighwater; /* threshold in input buffer */ u_char *iptr; /* next free spot in input buffer */ struct lbq obufq; /* head of queue of output buffers */ struct lbq obufs[2]; /* output buffers */ Port_t data_port; /* i/o ports */ #ifdef COM_ESP Port_t esp_port; #endif Port_t int_id_port; Port_t iobase; Port_t modem_ctl_port; Port_t line_status_port; Port_t modem_status_port; Port_t intr_ctl_port; /* Ports of IIR register */ struct tty *tp; /* cross reference */ /* Initial state. */ struct termios it_in; /* should be in struct tty */ struct termios it_out; /* Lock state. */ struct termios lt_in; /* should be in struct tty */ struct termios lt_out; bool_t do_timestamp; bool_t do_dcd_timestamp; struct timeval timestamp; struct timeval dcd_timestamp; u_long bytes_in; /* statistics */ u_long bytes_out; u_int delta_error_counts[CE_NTYPES]; u_long error_counts[CE_NTYPES]; /* * Ping-pong input buffers. The extra factor of 2 in the sizes is * to allow for an error byte for each input byte. */ #define CE_INPUT_OFFSET RS_IBUFSIZE u_char ibuf1[2 * RS_IBUFSIZE]; u_char ibuf2[2 * RS_IBUFSIZE]; /* * Data area for output buffers. Someday we should build the output * buffer queue without copying data. */ u_char obuf1[256]; u_char obuf2[256]; #ifdef DEVFS void *devfs_token_ttyd; void *devfs_token_ttyl; void *devfs_token_ttyi; void *devfs_token_cuaa; void *devfs_token_cual; void *devfs_token_cuai; #endif }; #ifdef COM_ESP static int espattach __P((struct isa_device *isdp, struct com_s *com, Port_t esp_port)); #endif static int sioattach __P((device_t dev)); static timeout_t siobusycheck; static timeout_t siodtrwakeup; static void comhardclose __P((struct com_s *com)); static void siointr1 __P((struct com_s *com)); static void siointr __P((void *arg)); static int commctl __P((struct com_s *com, int bits, int how)); static int comparam __P((struct tty *tp, struct termios *t)); static swihand_t siopoll; static int sioprobe __P((device_t dev)); static void siosettimeout __P((void)); static void comstart __P((struct tty *tp)); static timeout_t comwakeup; static void disc_optim __P((struct tty *tp, struct termios *t, struct com_s *com)); #ifdef DSI_SOFT_MODEM static int LoadSoftModem __P((int unit,int base_io, u_long size, u_char *ptr)); #endif /* DSI_SOFT_MODEM */ static char driver_name[] = "sio"; /* table and macro for fast conversion from a unit number to its com struct */ static devclass_t sio_devclass; #define com_addr(unit) ((struct com_s *) \ devclass_get_softc(sio_devclass, unit)) static device_method_t sio_methods[] = { /* Device interface */ DEVMETHOD(device_probe, sioprobe), DEVMETHOD(device_attach, sioattach), { 0, 0 } }; static driver_t sio_driver = { driver_name, sio_methods, DRIVER_TYPE_TTY, sizeof(struct com_s), }; static d_open_t sioopen; static d_close_t sioclose; static d_read_t sioread; static d_write_t siowrite; static d_ioctl_t sioioctl; static d_stop_t siostop; static d_devtotty_t siodevtotty; #define CDEV_MAJOR 28 static struct cdevsw sio_cdevsw = { sioopen, sioclose, sioread, siowrite, sioioctl, siostop, noreset, siodevtotty, ttpoll, nommap, NULL, driver_name, NULL, -1, nodump, nopsize, D_TTY, }; int comconsole = -1; static volatile speed_t comdefaultrate = CONSPEED; static volatile speed_t gdbdefaultrate = CONSPEED; static u_int com_events; /* input chars + weighted output completions */ static Port_t siocniobase; static Port_t siogdbiobase; static bool_t sio_registered; static int sio_timeout; static int sio_timeouts_until_log; static struct callout_handle sio_timeout_handle = CALLOUT_HANDLE_INITIALIZER(&sio_timeout_handle); #if 0 /* XXX */ static struct tty *sio_tty[NSIOTOT]; #else static struct tty sio_tty[NSIOTOT]; #endif static const int nsio_tty = NSIOTOT; static struct speedtab comspeedtab[] = { { 0, 0 }, { 50, COMBRD(50) }, { 75, COMBRD(75) }, { 110, COMBRD(110) }, { 134, COMBRD(134) }, { 150, COMBRD(150) }, { 200, COMBRD(200) }, { 300, COMBRD(300) }, { 600, COMBRD(600) }, { 1200, COMBRD(1200) }, { 1800, COMBRD(1800) }, { 2400, COMBRD(2400) }, { 4800, COMBRD(4800) }, { 9600, COMBRD(9600) }, { 19200, COMBRD(19200) }, { 38400, COMBRD(38400) }, { 57600, COMBRD(57600) }, { 115200, COMBRD(115200) }, { -1, -1 } }; #ifdef COM_ESP /* XXX configure this properly. */ static Port_t likely_com_ports[] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, }; static Port_t likely_esp_ports[] = { 0x140, 0x180, 0x280, 0 }; #endif /* * handle sysctl read/write requests for console speed * * In addition to setting comdefaultrate for I/O through /dev/console, * also set the initial and lock values for the /dev/ttyXX device * if there is one associated with the console. Finally, if the /dev/tty * device has already been open, change the speed on the open running port * itself. */ static int sysctl_machdep_comdefaultrate SYSCTL_HANDLER_ARGS { int error, s; speed_t newspeed; struct com_s *com; struct tty *tp; newspeed = comdefaultrate; error = sysctl_handle_opaque(oidp, &newspeed, sizeof newspeed, req); if (error || !req->newptr) return (error); comdefaultrate = newspeed; if (comconsole < 0) /* serial console not selected? */ return (0); com = com_addr(comconsole); if (!com) return (ENXIO); /* * set the initial and lock rates for /dev/ttydXX and /dev/cuaXX * (note, the lock rates really are boolean -- if non-zero, disallow * speed changes) */ com->it_in.c_ispeed = com->it_in.c_ospeed = com->lt_in.c_ispeed = com->lt_in.c_ospeed = com->it_out.c_ispeed = com->it_out.c_ospeed = com->lt_out.c_ispeed = com->lt_out.c_ospeed = comdefaultrate; /* * if we're open, change the running rate too */ tp = com->tp; if (tp && (tp->t_state & TS_ISOPEN)) { tp->t_termios.c_ispeed = tp->t_termios.c_ospeed = comdefaultrate; s = spltty(); error = comparam(tp, &tp->t_termios); splx(s); } return error; } SYSCTL_PROC(_machdep, OID_AUTO, conspeed, CTLTYPE_INT | CTLFLAG_RW, 0, 0, sysctl_machdep_comdefaultrate, "I", ""); #if NCARD > 0 /* * PC-Card (PCMCIA) specific code. */ static int sioinit __P((struct pccard_devinfo *)); static void siounload __P((struct pccard_devinfo *)); static int card_intr __P((struct pccard_devinfo *)); PCCARD_MODULE(sio, sioinit, siounload, card_intr, 0, tty_imask); /* * Initialize the device - called from Slot manager. */ int sioinit(struct pccard_devinfo *devi) { /* validate unit number. */ if (devi->isahd.id_unit >= (NSIOTOT)) return(ENODEV); /* Make sure it isn't already probed. */ if (com_addr(devi->isahd.id_unit)) return(EBUSY); /* It's already probed as serial by Upper */ devi->isahd.id_flags |= COM_C_NOPROBE; /* * Probe the device. If a value is returned, the * device was found at the location. */ if (sioprobe(&devi->isahd) == 0) return(ENXIO); if (sioattach(&devi->isahd) == 0) return(ENXIO); return(0); } /* * siounload - unload the driver and clear the table. * XXX TODO: * This is usually called when the card is ejected, but * can be caused by a modunload of a controller driver. * The idea is to reset the driver's view of the device * and ensure that any driver entry points such as * read and write do not hang. */ static void siounload(struct pccard_devinfo *devi) { struct com_s *com; if (!devi) { printf("NULL devi in siounload\n"); return; } com = com_addr(devi->isahd.id_unit); if (!com) { printf("NULL com in siounload\n"); return; } if (!com->iobase) { printf("sio%d already unloaded!\n",devi->isahd.id_unit); return; } if (com->tp && (com->tp->t_state & TS_ISOPEN)) { com->gone = 1; printf("sio%d: unload\n", devi->isahd.id_unit); com->tp->t_gen++; ttyclose(com->tp); ttwakeup(com->tp); ttwwakeup(com->tp); } else { com_addr(com->unit) = NULL; bzero(com, sizeof *com); free(com,M_TTYS); printf("sio%d: unload,gone\n", devi->isahd.id_unit); } } /* * card_intr - Shared interrupt called from * front end of PC-Card handler. */ static int card_intr(struct pccard_devinfo *devi) { struct com_s *com; COM_LOCK(); com = com_addr(devi->isahd.id_unit); if (com && !com->gone) siointr1(com_addr(devi->isahd.id_unit)); COM_UNLOCK(); return(1); } #endif /* NCARD > 0 */ #define SET_FLAG(dev, bit) isa_set_flags(dev, isa_get_flags(dev) | (bit)) #define CLR_FLAG(dev, bit) isa_set_flags(dev, isa_get_flags(dev) & ~(bit)) static int sioprobe(dev) device_t dev; { static bool_t already_init; bool_t failures[10]; int fn; device_t idev; Port_t iobase; intrmask_t irqmap[4]; intrmask_t irqs; u_char mcr_image; int result; device_t xdev; u_int flags = isa_get_flags(dev); if (!already_init) { /* * Turn off MCR_IENABLE for all likely serial ports. An unused * port with its MCR_IENABLE gate open will inhibit interrupts * from any used port that shares the interrupt vector. * XXX the gate enable is elsewhere for some multiports. */ device_t *devs; int count, i; devclass_get_devices(sio_devclass, &devs, &count); for (i = 0; i < count; i++) { xdev = devs[i]; outb(isa_get_port(xdev) + com_mcr, 0); } free(devs, M_TEMP); already_init = TRUE; } if (COM_LLCONSOLE(flags)) { printf("sio%d: reserved for low-level i/o\n", device_get_unit(dev)); return (ENXIO); } /* * If the device is on a multiport card and has an AST/4 * compatible interrupt control register, initialize this * register and prepare to leave MCR_IENABLE clear in the mcr. * Otherwise, prepare to set MCR_IENABLE in the mcr. * Point idev to the device struct giving the correct id_irq. * This is the struct for the master device if there is one. */ idev = dev; mcr_image = MCR_IENABLE; #ifdef COM_MULTIPORT if (COM_ISMULTIPORT(flags)) { idev = devclass_get_device(sio_devclass, COM_MPMASTER(flags)); if (idev == NULL) { printf("sio%d: master device %d not configured\n", device_get_unit(dev), COM_MPMASTER(flags)); isa_set_irq(dev, 0); idev = dev; } if (!COM_NOTAST4(flags)) { outb(isa_get_port(idev) + com_scr, isa_get_irq(idev) >= 0 ? 0x80 : 0); mcr_image = 0; } } #endif /* COM_MULTIPORT */ if (isa_get_irq(idev) < 0) mcr_image = 0; bzero(failures, sizeof failures); iobase = isa_get_port(dev); /* * We don't want to get actual interrupts, just masked ones. * Interrupts from this line should already be masked in the ICU, * but mask them in the processor as well in case there are some * (misconfigured) shared interrupts. */ disable_intr(); /* EXTRA DELAY? */ /* * Initialize the speed and the word size and wait long enough to * drain the maximum of 16 bytes of junk in device output queues. * The speed is undefined after a master reset and must be set * before relying on anything related to output. There may be * junk after a (very fast) soft reboot and (apparently) after * master reset. * XXX what about the UART bug avoided by waiting in comparam()? * We don't want to to wait long enough to drain at 2 bps. */ if (iobase == siocniobase) DELAY((16 + 1) * 1000000 / (comdefaultrate / 10)); else { outb(iobase + com_cfcr, CFCR_DLAB | CFCR_8BITS); outb(iobase + com_dlbl, COMBRD(SIO_TEST_SPEED) & 0xff); outb(iobase + com_dlbh, (u_int) COMBRD(SIO_TEST_SPEED) >> 8); outb(iobase + com_cfcr, CFCR_8BITS); DELAY((16 + 1) * 1000000 / (SIO_TEST_SPEED / 10)); } /* * Enable the interrupt gate and disable device interupts. This * should leave the device driving the interrupt line low and * guarantee an edge trigger if an interrupt can be generated. */ /* EXTRA DELAY? */ outb(iobase + com_mcr, mcr_image); outb(iobase + com_ier, 0); DELAY(1000); /* XXX */ irqmap[0] = isa_irq_pending(); /* * Attempt to set loopback mode so that we can send a null byte * without annoying any external device. */ /* EXTRA DELAY? */ outb(iobase + com_mcr, mcr_image | MCR_LOOPBACK); /* * Attempt to generate an output interrupt. On 8250's, setting * IER_ETXRDY generates an interrupt independent of the current * setting and independent of whether the THR is empty. On 16450's, * setting IER_ETXRDY generates an interrupt independent of the * current setting. On 16550A's, setting IER_ETXRDY only * generates an interrupt when IER_ETXRDY is not already set. */ outb(iobase + com_ier, IER_ETXRDY); /* * On some 16x50 incompatibles, setting IER_ETXRDY doesn't generate * an interrupt. They'd better generate one for actually doing * output. Loopback may be broken on the same incompatibles but * it's unlikely to do more than allow the null byte out. */ outb(iobase + com_data, 0); DELAY((1 + 2) * 1000000 / (SIO_TEST_SPEED / 10)); /* * Turn off loopback mode so that the interrupt gate works again * (MCR_IENABLE was hidden). This should leave the device driving * an interrupt line high. It doesn't matter if the interrupt * line oscillates while we are not looking at it, since interrupts * are disabled. */ /* EXTRA DELAY? */ outb(iobase + com_mcr, mcr_image); /* * It's a definitly Serial PCMCIA(16550A), but still be required * for IIR_TXRDY implementation ( Palido 321s, DC-1S... ) */ if ( COM_NOPROBE(flags) ) { /* Reading IIR register twice */ for ( fn = 0; fn < 2; fn ++ ) { DELAY(10000); failures[6] = inb(iobase + com_iir); } /* Check IIR_TXRDY clear ? */ isa_set_portsize(dev, IO_COMSIZE); result = 0; if ( failures[6] & IIR_TXRDY ) { /* Nop, Double check with clearing IER */ outb(iobase + com_ier, 0); if ( inb(iobase + com_iir) & IIR_NOPEND ) { /* Ok. we're familia this gang */ SET_FLAG(dev, COM_C_IIR_TXRDYBUG); /* Set IIR_TXRDYBUG */ } else { /* Unknow, Just omit this chip.. XXX*/ result = ENXIO; } } else { /* OK. this is well-known guys */ CLR_FLAG(dev, COM_C_IIR_TXRDYBUG); /*Clear IIR_TXRDYBUG*/ } outb(iobase + com_cfcr, CFCR_8BITS); enable_intr(); return (iobase == siocniobase ? 0 : result); } /* * Check that * o the CFCR, IER and MCR in UART hold the values written to them * (the values happen to be all distinct - this is good for * avoiding false positive tests from bus echoes). * o an output interrupt is generated and its vector is correct. * o the interrupt goes away when the IIR in the UART is read. */ /* EXTRA DELAY? */ failures[0] = inb(iobase + com_cfcr) - CFCR_8BITS; failures[1] = inb(iobase + com_ier) - IER_ETXRDY; failures[2] = inb(iobase + com_mcr) - mcr_image; DELAY(10000); /* Some internal modems need this time */ irqmap[1] = isa_irq_pending(); failures[4] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_TXRDY; DELAY(1000); /* XXX */ irqmap[2] = isa_irq_pending(); failures[6] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_NOPEND; /* * Turn off all device interrupts and check that they go off properly. * Leave MCR_IENABLE alone. For ports without a master port, it gates * the OUT2 output of the UART to * the ICU input. Closing the gate would give a floating ICU input * (unless there is another device driving at) and spurious interrupts. * (On the system that this was first tested on, the input floats high * and gives a (masked) interrupt as soon as the gate is closed.) */ outb(iobase + com_ier, 0); outb(iobase + com_cfcr, CFCR_8BITS); /* dummy to avoid bus echo */ failures[7] = inb(iobase + com_ier); DELAY(1000); /* XXX */ irqmap[3] = isa_irq_pending(); failures[9] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_NOPEND; enable_intr(); irqs = irqmap[1] & ~irqmap[0]; if (isa_get_irq(idev) >= 0 && ((1 << isa_get_irq(idev)) & irqs) == 0) printf( "sio%d: configured irq %d not in bitmap of probed irqs %#x\n", device_get_unit(dev), isa_get_irq(idev), irqs); if (bootverbose) printf("sio%d: irq maps: %#x %#x %#x %#x\n", device_get_unit(dev), irqmap[0], irqmap[1], irqmap[2], irqmap[3]); isa_set_portsize(dev, IO_COMSIZE); result = 0; for (fn = 0; fn < sizeof failures; ++fn) if (failures[fn]) { outb(iobase + com_mcr, 0); result = ENXIO; if (bootverbose) { printf("sio%d: probe failed test(s):", device_get_unit(dev)); for (fn = 0; fn < sizeof failures; ++fn) if (failures[fn]) printf(" %d", fn); printf("\n"); } break; } return (iobase == siocniobase ? 0 : result); } #ifdef COM_ESP static int espattach(isdp, com, esp_port) struct isa_device *isdp; struct com_s *com; Port_t esp_port; { u_char dips; u_char val; /* * Check the ESP-specific I/O port to see if we're an ESP * card. If not, return failure immediately. */ if ((inb(esp_port) & 0xf3) == 0) { printf(" port 0x%x is not an ESP board?\n", esp_port); return (0); } /* * We've got something that claims to be a Hayes ESP card. * Let's hope so. */ /* Get the dip-switch configuration */ outb(esp_port + ESP_CMD1, ESP_GETDIPS); dips = inb(esp_port + ESP_STATUS1); /* * Bits 0,1 of dips say which COM port we are. */ if (com->iobase == likely_com_ports[dips & 0x03]) printf(" : ESP"); else { printf(" esp_port has com %d\n", dips & 0x03); return (0); } /* * Check for ESP version 2.0 or later: bits 4,5,6 = 010. */ outb(esp_port + ESP_CMD1, ESP_GETTEST); val = inb(esp_port + ESP_STATUS1); /* clear reg 1 */ val = inb(esp_port + ESP_STATUS2); if ((val & 0x70) < 0x20) { printf("-old (%o)", val & 0x70); return (0); } /* * Check for ability to emulate 16550: bit 7 == 1 */ if ((dips & 0x80) == 0) { printf(" slave"); return (0); } /* * Okay, we seem to be a Hayes ESP card. Whee. */ com->esp = TRUE; com->esp_port = esp_port; return (1); } #endif /* COM_ESP */ static int sioattach(dev) device_t dev; { struct com_s *com; #ifdef COM_ESP Port_t *espp; #endif Port_t iobase; int s; int unit; void *ih; struct resource *res; int zero = 0; u_int flags = isa_get_flags(dev); #if 0 isdp->id_ri_flags |= RI_FAST; #endif iobase = isa_get_port(dev); unit = device_get_unit(dev); com = device_get_softc(dev); /* * sioprobe() has initialized the device registers as follows: * o cfcr = CFCR_8BITS. * It is most important that CFCR_DLAB is off, so that the * data port is not hidden when we enable interrupts. * o ier = 0. * Interrupts are only enabled when the line is open. * o mcr = MCR_IENABLE, or 0 if the port has AST/4 compatible * interrupt control register or the config specifies no irq. * Keeping MCR_DTR and MCR_RTS off might stop the external * device from sending before we are ready. */ bzero(com, sizeof *com); com->unit = unit; com->cfcr_image = CFCR_8BITS; com->dtr_wait = 3 * hz; com->loses_outints = COM_LOSESOUTINTS(flags) != 0; com->no_irq = isa_get_irq(dev) < 0; com->tx_fifo_size = 1; com->iptr = com->ibuf = com->ibuf1; com->ibufend = com->ibuf1 + RS_IBUFSIZE; com->ihighwater = com->ibuf1 + RS_IHIGHWATER; com->obufs[0].l_head = com->obuf1; com->obufs[1].l_head = com->obuf2; com->iobase = iobase; com->data_port = iobase + com_data; com->int_id_port = iobase + com_iir; com->modem_ctl_port = iobase + com_mcr; com->mcr_image = inb(com->modem_ctl_port); com->line_status_port = iobase + com_lsr; com->modem_status_port = iobase + com_msr; com->intr_ctl_port = iobase + com_ier; /* * We don't use all the flags from since they * are only relevant for logins. It's important to have echo off * initially so that the line doesn't start blathering before the * echo flag can be turned off. */ com->it_in.c_iflag = 0; com->it_in.c_oflag = 0; com->it_in.c_cflag = TTYDEF_CFLAG; com->it_in.c_lflag = 0; if (unit == comconsole) { com->it_in.c_iflag = TTYDEF_IFLAG; com->it_in.c_oflag = TTYDEF_OFLAG; com->it_in.c_cflag = TTYDEF_CFLAG | CLOCAL; com->it_in.c_lflag = TTYDEF_LFLAG; com->lt_out.c_cflag = com->lt_in.c_cflag = CLOCAL; com->lt_out.c_ispeed = com->lt_out.c_ospeed = com->lt_in.c_ispeed = com->lt_in.c_ospeed = com->it_in.c_ispeed = com->it_in.c_ospeed = comdefaultrate; } else com->it_in.c_ispeed = com->it_in.c_ospeed = TTYDEF_SPEED; termioschars(&com->it_in); com->it_out = com->it_in; /* attempt to determine UART type */ printf("sio%d: type", unit); #ifdef DSI_SOFT_MODEM if((inb(iobase+7) ^ inb(iobase+7)) & 0x80) { printf(" Digicom Systems, Inc. SoftModem"); goto determined_type; } #endif /* DSI_SOFT_MODEM */ #ifdef COM_MULTIPORT if (!COM_ISMULTIPORT(flags) && !COM_IIR_TXRDYBUG(flags)) #else if (!COM_IIR_TXRDYBUG(flags)) #endif { u_char scr; u_char scr1; u_char scr2; scr = inb(iobase + com_scr); outb(iobase + com_scr, 0xa5); scr1 = inb(iobase + com_scr); outb(iobase + com_scr, 0x5a); scr2 = inb(iobase + com_scr); outb(iobase + com_scr, scr); if (scr1 != 0xa5 || scr2 != 0x5a) { printf(" 8250"); goto determined_type; } } outb(iobase + com_fifo, FIFO_ENABLE | FIFO_RX_HIGH); DELAY(100); com->st16650a = 0; switch (inb(com->int_id_port) & IIR_FIFO_MASK) { case FIFO_RX_LOW: printf(" 16450"); break; case FIFO_RX_MEDL: printf(" 16450?"); break; case FIFO_RX_MEDH: printf(" 16550?"); break; case FIFO_RX_HIGH: if (COM_NOFIFO(flags)) { printf(" 16550A fifo disabled"); } else { com->hasfifo = TRUE; if (COM_ST16650A(flags)) { com->st16650a = 1; com->tx_fifo_size = 32; printf(" ST16650A"); } else { com->tx_fifo_size = COM_FIFOSIZE(flags); printf(" 16550A"); } } #ifdef COM_ESP for (espp = likely_esp_ports; *espp != 0; espp++) if (espattach(dev, com, *espp)) { com->tx_fifo_size = 1024; break; } #endif if (!com->st16650a) { if (!com->tx_fifo_size) com->tx_fifo_size = 16; else printf(" lookalike with %d bytes FIFO", com->tx_fifo_size); } break; } #ifdef COM_ESP if (com->esp) { /* * Set 16550 compatibility mode. * We don't use the ESP_MODE_SCALE bit to increase the * fifo trigger levels because we can't handle large * bursts of input. * XXX flow control should be set in comparam(), not here. */ outb(com->esp_port + ESP_CMD1, ESP_SETMODE); outb(com->esp_port + ESP_CMD2, ESP_MODE_RTS | ESP_MODE_FIFO); /* Set RTS/CTS flow control. */ outb(com->esp_port + ESP_CMD1, ESP_SETFLOWTYPE); outb(com->esp_port + ESP_CMD2, ESP_FLOW_RTS); outb(com->esp_port + ESP_CMD2, ESP_FLOW_CTS); /* Set flow-control levels. */ outb(com->esp_port + ESP_CMD1, ESP_SETRXFLOW); outb(com->esp_port + ESP_CMD2, HIBYTE(768)); outb(com->esp_port + ESP_CMD2, LOBYTE(768)); outb(com->esp_port + ESP_CMD2, HIBYTE(512)); outb(com->esp_port + ESP_CMD2, LOBYTE(512)); } #endif /* COM_ESP */ outb(iobase + com_fifo, 0); determined_type: ; #ifdef COM_MULTIPORT if (COM_ISMULTIPORT(flags)) { com->multiport = TRUE; printf(" (multiport"); if (unit == COM_MPMASTER(flags)) printf(" master"); printf(")"); com->no_irq = isa_get_irq(devclass_get_device (sio_devclass, COM_MPMASTER(flags))) < 0; } #endif /* COM_MULTIPORT */ if (unit == comconsole) printf(", console"); if ( COM_IIR_TXRDYBUG(flags) ) printf(" with a bogus IIR_TXRDY register"); printf("\n"); if (!sio_registered) { register_swi(SWI_TTY, siopoll); sio_registered = TRUE; } #ifdef DEVFS com->devfs_token_ttyd = devfs_add_devswf(&sio_cdevsw, unit, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "ttyd%r", unit); com->devfs_token_ttyi = devfs_add_devswf(&sio_cdevsw, unit | CONTROL_INIT_STATE, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "ttyid%r", unit); com->devfs_token_ttyl = devfs_add_devswf(&sio_cdevsw, unit | CONTROL_LOCK_STATE, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "ttyld%r", unit); com->devfs_token_cuaa = devfs_add_devswf(&sio_cdevsw, unit | CALLOUT_MASK, DV_CHR, UID_UUCP, GID_DIALER, 0660, "cuaa%r", unit); com->devfs_token_cuai = devfs_add_devswf(&sio_cdevsw, unit | CALLOUT_MASK | CONTROL_INIT_STATE, DV_CHR, UID_UUCP, GID_DIALER, 0660, "cuaia%r", unit); com->devfs_token_cual = devfs_add_devswf(&sio_cdevsw, unit | CALLOUT_MASK | CONTROL_LOCK_STATE, DV_CHR, UID_UUCP, GID_DIALER, 0660, "cuala%r", unit); #endif com->flags = isa_get_flags(dev); /* Heritate id_flags for later */ res = bus_alloc_resource(dev, SYS_RES_IRQ, &zero, 0ul, ~0ul, 1, RF_SHAREABLE | RF_ACTIVE); BUS_SETUP_INTR(device_get_parent(dev), dev, res, siointr, com, &ih); return (0); } static int sioopen(dev, flag, mode, p) dev_t dev; int flag; int mode; struct proc *p; { struct com_s *com; int error; Port_t iobase; int mynor; int s; struct tty *tp; int unit; mynor = minor(dev); unit = MINOR_TO_UNIT(mynor); if ((u_int) unit >= NSIOTOT || (com = com_addr(unit)) == NULL) return (ENXIO); if (com->gone) return (ENXIO); if (mynor & CONTROL_MASK) return (0); #if 0 /* XXX */ tp = com->tp = sio_tty[unit] = ttymalloc(sio_tty[unit]); #else tp = com->tp = &sio_tty[unit]; #endif s = spltty(); /* * We jump to this label after all non-interrupted sleeps to pick * up any changes of the device state. */ open_top: while (com->state & CS_DTR_OFF) { error = tsleep(&com->dtr_wait, TTIPRI | PCATCH, "siodtr", 0); if (com_addr(unit) == NULL) return (ENXIO); if (error != 0 || com->gone) goto out; } if (tp->t_state & TS_ISOPEN) { /* * The device is open, so everything has been initialized. * Handle conflicts. */ if (mynor & CALLOUT_MASK) { if (!com->active_out) { error = EBUSY; goto out; } } else { if (com->active_out) { if (flag & O_NONBLOCK) { error = EBUSY; goto out; } error = tsleep(&com->active_out, TTIPRI | PCATCH, "siobi", 0); if (com_addr(unit) == NULL) return (ENXIO); if (error != 0 || com->gone) goto out; goto open_top; } } if (tp->t_state & TS_XCLUDE && suser(p->p_ucred, &p->p_acflag)) { error = EBUSY; goto out; } } else { /* * The device isn't open, so there are no conflicts. * Initialize it. Initialization is done twice in many * cases: to preempt sleeping callin opens if we are * callout, and to complete a callin open after DCD rises. */ tp->t_oproc = comstart; tp->t_param = comparam; tp->t_dev = dev; tp->t_termios = mynor & CALLOUT_MASK ? com->it_out : com->it_in; tp->t_ififosize = 2 * RS_IBUFSIZE; tp->t_ispeedwat = (speed_t)-1; tp->t_ospeedwat = (speed_t)-1; (void)commctl(com, TIOCM_DTR | TIOCM_RTS, DMSET); com->poll = com->no_irq; com->poll_output = com->loses_outints; ++com->wopeners; error = comparam(tp, &tp->t_termios); --com->wopeners; if (error != 0) goto out; /* * XXX we should goto open_top if comparam() slept. */ iobase = com->iobase; if (com->hasfifo) { /* * (Re)enable and drain fifos. * * Certain SMC chips cause problems if the fifos * are enabled while input is ready. Turn off the * fifo if necessary to clear the input. We test * the input ready bit after enabling the fifos * since we've already enabled them in comparam() * and to handle races between enabling and fresh * input. */ while (TRUE) { outb(iobase + com_fifo, FIFO_RCV_RST | FIFO_XMT_RST | com->fifo_image); /* * XXX the delays are for superstitious * historical reasons. It must be less than * the character time at the maximum * supported speed (87 usec at 115200 bps * 8N1). Otherwise we might loop endlessly * if data is streaming in. We used to use * delays of 100. That usually worked * because DELAY(100) used to usually delay * for about 85 usec instead of 100. */ DELAY(50); if (!(inb(com->line_status_port) & LSR_RXRDY)) break; outb(iobase + com_fifo, 0); DELAY(50); (void) inb(com->data_port); } } disable_intr(); (void) inb(com->line_status_port); (void) inb(com->data_port); com->prev_modem_status = com->last_modem_status = inb(com->modem_status_port); if (COM_IIR_TXRDYBUG(com->flags)) { outb(com->intr_ctl_port, IER_ERXRDY | IER_ERLS | IER_EMSC); } else { outb(com->intr_ctl_port, IER_ERXRDY | IER_ETXRDY | IER_ERLS | IER_EMSC); } enable_intr(); /* * Handle initial DCD. Callout devices get a fake initial * DCD (trapdoor DCD). If we are callout, then any sleeping * callin opens get woken up and resume sleeping on "siobi" * instead of "siodcd". */ /* * XXX `mynor & CALLOUT_MASK' should be * `tp->t_cflag & (SOFT_CARRIER | TRAPDOOR_CARRIER) where * TRAPDOOR_CARRIER is the default initial state for callout * devices and SOFT_CARRIER is like CLOCAL except it hides * the true carrier. */ if (com->prev_modem_status & MSR_DCD || mynor & CALLOUT_MASK) (*linesw[tp->t_line].l_modem)(tp, 1); } /* * Wait for DCD if necessary. */ if (!(tp->t_state & TS_CARR_ON) && !(mynor & CALLOUT_MASK) && !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK)) { ++com->wopeners; error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "siodcd", 0); if (com_addr(unit) == NULL) return (ENXIO); --com->wopeners; if (error != 0 || com->gone) goto out; goto open_top; } error = (*linesw[tp->t_line].l_open)(dev, tp); disc_optim(tp, &tp->t_termios, com); if (tp->t_state & TS_ISOPEN && mynor & CALLOUT_MASK) com->active_out = TRUE; siosettimeout(); out: splx(s); if (!(tp->t_state & TS_ISOPEN) && com->wopeners == 0) comhardclose(com); return (error); } static int sioclose(dev, flag, mode, p) dev_t dev; int flag; int mode; struct proc *p; { struct com_s *com; int mynor; int s; struct tty *tp; mynor = minor(dev); if (mynor & CONTROL_MASK) return (0); com = com_addr(MINOR_TO_UNIT(mynor)); tp = com->tp; s = spltty(); (*linesw[tp->t_line].l_close)(tp, flag); disc_optim(tp, &tp->t_termios, com); siostop(tp, FREAD | FWRITE); comhardclose(com); ttyclose(tp); siosettimeout(); splx(s); if (com->gone) { printf("sio%d: gone\n", com->unit); s = spltty(); bzero(tp,sizeof *tp); bzero(com,sizeof *com); splx(s); } return (0); } static void comhardclose(com) struct com_s *com; { Port_t iobase; int s; struct tty *tp; int unit; unit = com->unit; iobase = com->iobase; s = spltty(); com->poll = FALSE; com->poll_output = FALSE; com->do_timestamp = FALSE; com->do_dcd_timestamp = FALSE; outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); { outb(iobase + com_ier, 0); tp = com->tp; if (tp->t_cflag & HUPCL /* * XXX we will miss any carrier drop between here and the * next open. Perhaps we should watch DCD even when the * port is closed; it is not sufficient to check it at * the next open because it might go up and down while * we're not watching. */ || !com->active_out && !(com->prev_modem_status & MSR_DCD) && !(com->it_in.c_cflag & CLOCAL) || !(tp->t_state & TS_ISOPEN)) { (void)commctl(com, TIOCM_DTR, DMBIC); if (com->dtr_wait != 0 && !(com->state & CS_DTR_OFF)) { timeout(siodtrwakeup, com, com->dtr_wait); com->state |= CS_DTR_OFF; } } } if (com->hasfifo) { /* * Disable fifos so that they are off after controlled * reboots. Some BIOSes fail to detect 16550s when the * fifos are enabled. */ outb(iobase + com_fifo, 0); } com->active_out = FALSE; wakeup(&com->active_out); wakeup(TSA_CARR_ON(tp)); /* restart any wopeners */ splx(s); } static int sioread(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { int mynor; int unit; struct tty *tp; mynor = minor(dev); if (mynor & CONTROL_MASK) return (ENODEV); unit = MINOR_TO_UNIT(mynor); if (com_addr(unit)->gone) return (ENODEV); tp = com_addr(unit)->tp; return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); } static int siowrite(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { int mynor; struct tty *tp; int unit; mynor = minor(dev); if (mynor & CONTROL_MASK) return (ENODEV); unit = MINOR_TO_UNIT(mynor); if (com_addr(unit)->gone) return (ENODEV); tp = com_addr(unit)->tp; /* * (XXX) We disallow virtual consoles if the physical console is * a serial port. This is in case there is a display attached that * is not the console. In that situation we don't need/want the X * server taking over the console. */ if (constty != NULL && unit == comconsole) constty = NULL; return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); } static void siobusycheck(chan) void *chan; { struct com_s *com; int s; com = (struct com_s *)chan; /* * Clear TS_BUSY if low-level output is complete. * spl locking is sufficient because siointr1() does not set CS_BUSY. * If siointr1() clears CS_BUSY after we look at it, then we'll get * called again. Reading the line status port outside of siointr1() * is safe because CS_BUSY is clear so there are no output interrupts * to lose. */ s = spltty(); if (com->state & CS_BUSY) com->extra_state &= ~CSE_BUSYCHECK; /* False alarm. */ else if ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY)) == (LSR_TSRE | LSR_TXRDY)) { com->tp->t_state &= ~TS_BUSY; ttwwakeup(com->tp); com->extra_state &= ~CSE_BUSYCHECK; } else timeout(siobusycheck, com, hz / 100); splx(s); } static void siodtrwakeup(chan) void *chan; { struct com_s *com; com = (struct com_s *)chan; com->state &= ~CS_DTR_OFF; wakeup(&com->dtr_wait); } void siointr(arg) void *arg; { #ifndef COM_MULTIPORT COM_LOCK(); siointr1((struct com_s *) arg); COM_UNLOCK(); #else /* COM_MULTIPORT */ bool_t possibly_more_intrs; /* * Loop until there is no activity on any port. This is necessary * to get an interrupt edge more than to avoid another interrupt. * If the IRQ signal is just an OR of the IRQ signals from several * devices, then the edge from one may be lost because another is * on. */ COM_LOCK(); do { possibly_more_intrs = FALSE; for (unit = 0; unit < NSIOTOT; ++unit) { com = com_addr(unit); /* * XXX COM_LOCK(); * would it work here, or be counter-productive? */ if (com != NULL && !com->gone && (inb(com->int_id_port) & IIR_IMASK) != IIR_NOPEND) { siointr1(com); possibly_more_intrs = TRUE; } /* XXX COM_UNLOCK(); */ } } while (possibly_more_intrs); COM_UNLOCK(); #endif /* COM_MULTIPORT */ } static void siointr1(com) struct com_s *com; { u_char line_status; u_char modem_status; u_char *ioptr; u_char recv_data; u_char int_ident; u_char int_ctl; u_char int_ctl_new; int_ctl = inb(com->intr_ctl_port); int_ctl_new = int_ctl; while (!com->gone) { line_status = inb(com->line_status_port); /* input event? (check first to help avoid overruns) */ while (line_status & LSR_RCV_MASK) { /* break/unnattached error bits or real input? */ if (!(line_status & LSR_RXRDY)) recv_data = 0; else recv_data = inb(com->data_port); if (line_status & (LSR_BI | LSR_FE | LSR_PE)) { /* * Don't store BI if IGNBRK or FE/PE if IGNPAR. * Otherwise, push the work to a higher level * (to handle PARMRK) if we're bypassing. * Otherwise, convert BI/FE and PE+INPCK to 0. * * This makes bypassing work right in the * usual "raw" case (IGNBRK set, and IGNPAR * and INPCK clear). * * Note: BI together with FE/PE means just BI. */ if (line_status & LSR_BI) { #if defined(DDB) && defined(BREAK_TO_DEBUGGER) if (com->unit == comconsole) { breakpoint(); goto cont; } #endif if (com->tp == NULL || com->tp->t_iflag & IGNBRK) goto cont; } else { if (com->tp == NULL || com->tp->t_iflag & IGNPAR) goto cont; } if (com->tp->t_state & TS_CAN_BYPASS_L_RINT && (line_status & (LSR_BI | LSR_FE) || com->tp->t_iflag & INPCK)) recv_data = 0; } ++com->bytes_in; if (com->hotchar != 0 && recv_data == com->hotchar) setsofttty(); ioptr = com->iptr; if (ioptr >= com->ibufend) CE_RECORD(com, CE_INTERRUPT_BUF_OVERFLOW); else { if (com->do_timestamp) microtime(&com->timestamp); ++com_events; schedsofttty(); #if 0 /* for testing input latency vs efficiency */ if (com->iptr - com->ibuf == 8) setsofttty(); #endif ioptr[0] = recv_data; ioptr[CE_INPUT_OFFSET] = line_status; com->iptr = ++ioptr; if (ioptr == com->ihighwater && com->state & CS_RTS_IFLOW) outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); if (line_status & LSR_OE) CE_RECORD(com, CE_OVERRUN); } cont: /* * "& 0x7F" is to avoid the gcc-1.40 generating a slow * jump from the top of the loop to here */ line_status = inb(com->line_status_port) & 0x7F; } /* modem status change? (always check before doing output) */ modem_status = inb(com->modem_status_port); if (modem_status != com->last_modem_status) { if (com->do_dcd_timestamp && !(com->last_modem_status & MSR_DCD) && modem_status & MSR_DCD) microtime(&com->dcd_timestamp); /* * Schedule high level to handle DCD changes. Note * that we don't use the delta bits anywhere. Some * UARTs mess them up, and it's easy to remember the * previous bits and calculate the delta. */ com->last_modem_status = modem_status; if (!(com->state & CS_CHECKMSR)) { com_events += LOTS_OF_EVENTS; com->state |= CS_CHECKMSR; setsofttty(); } /* handle CTS change immediately for crisp flow ctl */ if (com->state & CS_CTS_OFLOW) { if (modem_status & MSR_CTS) com->state |= CS_ODEVREADY; else com->state &= ~CS_ODEVREADY; } } /* output queued and everything ready? */ if (line_status & LSR_TXRDY && com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) { ioptr = com->obufq.l_head; if (com->tx_fifo_size > 1) { u_int ocount; ocount = com->obufq.l_tail - ioptr; if (ocount > com->tx_fifo_size) ocount = com->tx_fifo_size; com->bytes_out += ocount; do outb(com->data_port, *ioptr++); while (--ocount != 0); } else { outb(com->data_port, *ioptr++); ++com->bytes_out; } com->obufq.l_head = ioptr; if (COM_IIR_TXRDYBUG(com->flags)) { int_ctl_new = int_ctl | IER_ETXRDY; } if (ioptr >= com->obufq.l_tail) { struct lbq *qp; qp = com->obufq.l_next; qp->l_queued = FALSE; qp = qp->l_next; if (qp != NULL) { com->obufq.l_head = qp->l_head; com->obufq.l_tail = qp->l_tail; com->obufq.l_next = qp; } else { /* output just completed */ if ( COM_IIR_TXRDYBUG(com->flags) ) { int_ctl_new = int_ctl & ~IER_ETXRDY; } com->state &= ~CS_BUSY; } if (!(com->state & CS_ODONE)) { com_events += LOTS_OF_EVENTS; com->state |= CS_ODONE; setsofttty(); /* handle at high level ASAP */ } } if ( COM_IIR_TXRDYBUG(com->flags) && (int_ctl != int_ctl_new)) { outb(com->intr_ctl_port, int_ctl_new); } } /* finished? */ #ifndef COM_MULTIPORT if ((inb(com->int_id_port) & IIR_IMASK) == IIR_NOPEND) #endif /* COM_MULTIPORT */ return; } } static int sioioctl(dev, cmd, data, flag, p) dev_t dev; u_long cmd; caddr_t data; int flag; struct proc *p; { struct com_s *com; int error; Port_t iobase; int mynor; int s; struct tty *tp; #if defined(COMPAT_43) || defined(COMPAT_SUNOS) u_long oldcmd; struct termios term; #endif mynor = minor(dev); com = com_addr(MINOR_TO_UNIT(mynor)); if (com->gone) return (ENODEV); iobase = com->iobase; if (mynor & CONTROL_MASK) { struct termios *ct; switch (mynor & CONTROL_MASK) { case CONTROL_INIT_STATE: ct = mynor & CALLOUT_MASK ? &com->it_out : &com->it_in; break; case CONTROL_LOCK_STATE: ct = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in; break; default: return (ENODEV); /* /dev/nodev */ } switch (cmd) { case TIOCSETA: error = suser(p->p_ucred, &p->p_acflag); if (error != 0) return (error); *ct = *(struct termios *)data; return (0); case TIOCGETA: *(struct termios *)data = *ct; return (0); case TIOCGETD: *(int *)data = TTYDISC; return (0); case TIOCGWINSZ: bzero(data, sizeof(struct winsize)); return (0); #ifdef DSI_SOFT_MODEM /* * Download micro-code to Digicom modem. */ case TIOCDSIMICROCODE: { u_long l; u_char *p,*pi; pi = (u_char*)(*(caddr_t*)data); error = copyin(pi,&l,sizeof l); if(error) {return error;}; pi += sizeof l; p = malloc(l,M_TEMP,M_NOWAIT); if(!p) {return ENOBUFS;} error = copyin(pi,p,l); if(error) {free(p,M_TEMP); return error;}; if(error = LoadSoftModem( MINOR_TO_UNIT(mynor),iobase,l,p)) {free(p,M_TEMP); return error;} free(p,M_TEMP); return(0); } #endif /* DSI_SOFT_MODEM */ default: return (ENOTTY); } } tp = com->tp; #if defined(COMPAT_43) || defined(COMPAT_SUNOS) term = tp->t_termios; oldcmd = cmd; error = ttsetcompat(tp, &cmd, data, &term); if (error != 0) return (error); if (cmd != oldcmd) data = (caddr_t)&term; #endif if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { int cc; struct termios *dt = (struct termios *)data; struct termios *lt = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in; dt->c_iflag = (tp->t_iflag & lt->c_iflag) | (dt->c_iflag & ~lt->c_iflag); dt->c_oflag = (tp->t_oflag & lt->c_oflag) | (dt->c_oflag & ~lt->c_oflag); dt->c_cflag = (tp->t_cflag & lt->c_cflag) | (dt->c_cflag & ~lt->c_cflag); dt->c_lflag = (tp->t_lflag & lt->c_lflag) | (dt->c_lflag & ~lt->c_lflag); for (cc = 0; cc < NCCS; ++cc) if (lt->c_cc[cc] != 0) dt->c_cc[cc] = tp->t_cc[cc]; if (lt->c_ispeed != 0) dt->c_ispeed = tp->t_ispeed; if (lt->c_ospeed != 0) dt->c_ospeed = tp->t_ospeed; } error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); if (error != ENOIOCTL) return (error); s = spltty(); error = ttioctl(tp, cmd, data, flag); disc_optim(tp, &tp->t_termios, com); if (error != ENOIOCTL) { splx(s); return (error); } switch (cmd) { case TIOCSBRK: outb(iobase + com_cfcr, com->cfcr_image |= CFCR_SBREAK); break; case TIOCCBRK: outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); break; case TIOCSDTR: (void)commctl(com, TIOCM_DTR, DMBIS); break; case TIOCCDTR: (void)commctl(com, TIOCM_DTR, DMBIC); break; /* * XXX should disallow changing MCR_RTS if CS_RTS_IFLOW is set. The * changes get undone on the next call to comparam(). */ case TIOCMSET: (void)commctl(com, *(int *)data, DMSET); break; case TIOCMBIS: (void)commctl(com, *(int *)data, DMBIS); break; case TIOCMBIC: (void)commctl(com, *(int *)data, DMBIC); break; case TIOCMGET: *(int *)data = commctl(com, 0, DMGET); break; case TIOCMSDTRWAIT: /* must be root since the wait applies to following logins */ error = suser(p->p_ucred, &p->p_acflag); if (error != 0) { splx(s); return (error); } com->dtr_wait = *(int *)data * hz / 100; break; case TIOCMGDTRWAIT: *(int *)data = com->dtr_wait * 100 / hz; break; case TIOCTIMESTAMP: com->do_timestamp = TRUE; *(struct timeval *)data = com->timestamp; break; case TIOCDCDTIMESTAMP: com->do_dcd_timestamp = TRUE; *(struct timeval *)data = com->dcd_timestamp; break; default: splx(s); return (ENOTTY); } splx(s); return (0); } static void siopoll() { int unit; if (com_events == 0) return; repeat: for (unit = 0; unit < NSIOTOT; ++unit) { u_char *buf; struct com_s *com; u_char *ibuf; int incc; struct tty *tp; com = com_addr(unit); if (com == NULL) continue; tp = com->tp; if (tp == NULL || com->gone) { /* * Discard any events related to never-opened or * going-away devices. */ disable_intr(); incc = com->iptr - com->ibuf; com->iptr = com->ibuf; if (com->state & CS_CHECKMSR) { incc += LOTS_OF_EVENTS; com->state &= ~CS_CHECKMSR; } com_events -= incc; enable_intr(); continue; } /* switch the role of the low-level input buffers */ if (com->iptr == (ibuf = com->ibuf)) { buf = NULL; /* not used, but compiler can't tell */ incc = 0; } else { buf = ibuf; disable_intr(); incc = com->iptr - buf; com_events -= incc; if (ibuf == com->ibuf1) ibuf = com->ibuf2; else ibuf = com->ibuf1; com->ibufend = ibuf + RS_IBUFSIZE; com->ihighwater = ibuf + RS_IHIGHWATER; com->iptr = ibuf; /* * There is now room for another low-level buffer full * of input, so enable RTS if it is now disabled and * there is room in the high-level buffer. */ if ((com->state & CS_RTS_IFLOW) && !(com->mcr_image & MCR_RTS) && !(tp->t_state & TS_TBLOCK)) outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); enable_intr(); com->ibuf = ibuf; } if (com->state & CS_CHECKMSR) { u_char delta_modem_status; disable_intr(); delta_modem_status = com->last_modem_status ^ com->prev_modem_status; com->prev_modem_status = com->last_modem_status; com_events -= LOTS_OF_EVENTS; com->state &= ~CS_CHECKMSR; enable_intr(); if (delta_modem_status & MSR_DCD) (*linesw[tp->t_line].l_modem) (tp, com->prev_modem_status & MSR_DCD); } if (com->state & CS_ODONE) { disable_intr(); com_events -= LOTS_OF_EVENTS; com->state &= ~CS_ODONE; enable_intr(); if (!(com->state & CS_BUSY) && !(com->extra_state & CSE_BUSYCHECK)) { timeout(siobusycheck, com, hz / 100); com->extra_state |= CSE_BUSYCHECK; } (*linesw[tp->t_line].l_start)(tp); } if (incc <= 0 || !(tp->t_state & TS_ISOPEN) || !(tp->t_cflag & CREAD)) continue; /* * Avoid the grotesquely inefficient lineswitch routine * (ttyinput) in "raw" mode. It usually takes about 450 * instructions (that's without canonical processing or echo!). * slinput is reasonably fast (usually 40 instructions plus * call overhead). */ if (tp->t_state & TS_CAN_BYPASS_L_RINT) { if (tp->t_rawq.c_cc + incc > tp->t_ihiwat && (com->state & CS_RTS_IFLOW || tp->t_iflag & IXOFF) && !(tp->t_state & TS_TBLOCK)) ttyblock(tp); tk_nin += incc; tk_rawcc += incc; tp->t_rawcc += incc; com->delta_error_counts[CE_TTY_BUF_OVERFLOW] += b_to_q((char *)buf, incc, &tp->t_rawq); ttwakeup(tp); if (tp->t_state & TS_TTSTOP && (tp->t_iflag & IXANY || tp->t_cc[VSTART] == tp->t_cc[VSTOP])) { tp->t_state &= ~TS_TTSTOP; tp->t_lflag &= ~FLUSHO; comstart(tp); } } else { do { u_char line_status; int recv_data; line_status = (u_char) buf[CE_INPUT_OFFSET]; recv_data = (u_char) *buf++; if (line_status & (LSR_BI | LSR_FE | LSR_OE | LSR_PE)) { if (line_status & LSR_BI) recv_data |= TTY_BI; if (line_status & LSR_FE) recv_data |= TTY_FE; if (line_status & LSR_OE) recv_data |= TTY_OE; if (line_status & LSR_PE) recv_data |= TTY_PE; } (*linesw[tp->t_line].l_rint)(recv_data, tp); } while (--incc > 0); } if (com_events == 0) break; } if (com_events >= LOTS_OF_EVENTS) goto repeat; } static int comparam(tp, t) struct tty *tp; struct termios *t; { u_int cfcr; int cflag; struct com_s *com; int divisor; u_char dlbh; u_char dlbl; int error; Port_t iobase; int s; int unit; int txtimeout; /* do historical conversions */ if (t->c_ispeed == 0) t->c_ispeed = t->c_ospeed; /* check requested parameters */ divisor = ttspeedtab(t->c_ospeed, comspeedtab); if (divisor < 0 || divisor > 0 && t->c_ispeed != t->c_ospeed) return (EINVAL); /* parameters are OK, convert them to the com struct and the device */ unit = DEV_TO_UNIT(tp->t_dev); com = com_addr(unit); iobase = com->iobase; s = spltty(); if (divisor == 0) (void)commctl(com, TIOCM_DTR, DMBIC); /* hang up line */ else (void)commctl(com, TIOCM_DTR, DMBIS); cflag = t->c_cflag; switch (cflag & CSIZE) { case CS5: cfcr = CFCR_5BITS; break; case CS6: cfcr = CFCR_6BITS; break; case CS7: cfcr = CFCR_7BITS; break; default: cfcr = CFCR_8BITS; break; } if (cflag & PARENB) { cfcr |= CFCR_PENAB; if (!(cflag & PARODD)) cfcr |= CFCR_PEVEN; } if (cflag & CSTOPB) cfcr |= CFCR_STOPB; if (com->hasfifo && divisor != 0) { /* * Use a fifo trigger level low enough so that the input * latency from the fifo is less than about 16 msec and * the total latency is less than about 30 msec. These * latencies are reasonable for humans. Serial comms * protocols shouldn't expect anything better since modem * latencies are larger. */ com->fifo_image = t->c_ospeed <= 4800 ? FIFO_ENABLE : FIFO_ENABLE | FIFO_RX_HIGH; #ifdef COM_ESP /* * The Hayes ESP card needs the fifo DMA mode bit set * in compatibility mode. If not, it will interrupt * for each character received. */ if (com->esp) com->fifo_image |= FIFO_DMA_MODE; #endif outb(iobase + com_fifo, com->fifo_image); } /* * Some UARTs lock up if the divisor latch registers are selected * while the UART is doing output (they refuse to transmit anything * more until given a hard reset). Fix this by stopping filling * the device buffers and waiting for them to drain. Reading the * line status port outside of siointr1() might lose some receiver * error bits, but that is acceptable here. */ disable_intr(); retry: com->state &= ~CS_TTGO; txtimeout = tp->t_timeout; enable_intr(); while ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY)) != (LSR_TSRE | LSR_TXRDY)) { tp->t_state |= TS_SO_OCOMPLETE; error = ttysleep(tp, TSA_OCOMPLETE(tp), TTIPRI | PCATCH, "siotx", hz / 100); if ( txtimeout != 0 && (!error || error == EAGAIN) && (txtimeout -= hz / 100) <= 0 ) error = EIO; if (com->gone) error = ENODEV; if (error != 0 && error != EAGAIN) { if (!(tp->t_state & TS_TTSTOP)) { disable_intr(); com->state |= CS_TTGO; enable_intr(); } splx(s); return (error); } } disable_intr(); /* very important while com_data is hidden */ /* * XXX - clearing CS_TTGO is not sufficient to stop further output, * because siopoll() calls comstart() which usually sets it again * because TS_TTSTOP is clear. Setting TS_TTSTOP would not be * sufficient, for similar reasons. */ if ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY)) != (LSR_TSRE | LSR_TXRDY)) goto retry; if (divisor != 0) { outb(iobase + com_cfcr, cfcr | CFCR_DLAB); /* * Only set the divisor registers if they would change, * since on some 16550 incompatibles (UMC8669F), setting * them while input is arriving them loses sync until * data stops arriving. */ dlbl = divisor & 0xFF; if (inb(iobase + com_dlbl) != dlbl) outb(iobase + com_dlbl, dlbl); dlbh = (u_int) divisor >> 8; if (inb(iobase + com_dlbh) != dlbh) outb(iobase + com_dlbh, dlbh); } outb(iobase + com_cfcr, com->cfcr_image = cfcr); if (!(tp->t_state & TS_TTSTOP)) com->state |= CS_TTGO; if (cflag & CRTS_IFLOW) { if (com->st16650a) { outb(iobase + com_cfcr, 0xbf); outb(iobase + com_fifo, inb(iobase + com_fifo) | 0x40); } com->state |= CS_RTS_IFLOW; /* * If CS_RTS_IFLOW just changed from off to on, the change * needs to be propagated to MCR_RTS. This isn't urgent, * so do it later by calling comstart() instead of repeating * a lot of code from comstart() here. */ } else if (com->state & CS_RTS_IFLOW) { com->state &= ~CS_RTS_IFLOW; /* * CS_RTS_IFLOW just changed from on to off. Force MCR_RTS * on here, since comstart() won't do it later. */ outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); if (com->st16650a) { outb(iobase + com_cfcr, 0xbf); outb(iobase + com_fifo, inb(iobase + com_fifo) & ~0x40); } } /* * Set up state to handle output flow control. * XXX - worth handling MDMBUF (DCD) flow control at the lowest level? * Now has 10+ msec latency, while CTS flow has 50- usec latency. */ com->state |= CS_ODEVREADY; com->state &= ~CS_CTS_OFLOW; if (cflag & CCTS_OFLOW) { com->state |= CS_CTS_OFLOW; if (!(com->last_modem_status & MSR_CTS)) com->state &= ~CS_ODEVREADY; if (com->st16650a) { outb(iobase + com_cfcr, 0xbf); outb(iobase + com_fifo, inb(iobase + com_fifo) | 0x80); } } else { if (com->st16650a) { outb(iobase + com_cfcr, 0xbf); outb(iobase + com_fifo, inb(iobase + com_fifo) & ~0x80); } } outb(iobase + com_cfcr, com->cfcr_image); /* XXX shouldn't call functions while intrs are disabled. */ disc_optim(tp, t, com); /* * Recover from fiddling with CS_TTGO. We used to call siointr1() * unconditionally, but that defeated the careful discarding of * stale input in sioopen(). */ if (com->state >= (CS_BUSY | CS_TTGO)) siointr1(com); enable_intr(); splx(s); comstart(tp); return (0); } static void comstart(tp) struct tty *tp; { struct com_s *com; int s; int unit; unit = DEV_TO_UNIT(tp->t_dev); com = com_addr(unit); s = spltty(); disable_intr(); if (tp->t_state & TS_TTSTOP) com->state &= ~CS_TTGO; else com->state |= CS_TTGO; if (tp->t_state & TS_TBLOCK) { if (com->mcr_image & MCR_RTS && com->state & CS_RTS_IFLOW) outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); } else { if (!(com->mcr_image & MCR_RTS) && com->iptr < com->ihighwater && com->state & CS_RTS_IFLOW) outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); } enable_intr(); if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) { ttwwakeup(tp); splx(s); return; } if (tp->t_outq.c_cc != 0) { struct lbq *qp; struct lbq *next; if (!com->obufs[0].l_queued) { com->obufs[0].l_tail = com->obuf1 + q_to_b(&tp->t_outq, com->obuf1, sizeof com->obuf1); com->obufs[0].l_next = NULL; com->obufs[0].l_queued = TRUE; disable_intr(); if (com->state & CS_BUSY) { qp = com->obufq.l_next; while ((next = qp->l_next) != NULL) qp = next; qp->l_next = &com->obufs[0]; } else { com->obufq.l_head = com->obufs[0].l_head; com->obufq.l_tail = com->obufs[0].l_tail; com->obufq.l_next = &com->obufs[0]; com->state |= CS_BUSY; } enable_intr(); } if (tp->t_outq.c_cc != 0 && !com->obufs[1].l_queued) { com->obufs[1].l_tail = com->obuf2 + q_to_b(&tp->t_outq, com->obuf2, sizeof com->obuf2); com->obufs[1].l_next = NULL; com->obufs[1].l_queued = TRUE; disable_intr(); if (com->state & CS_BUSY) { qp = com->obufq.l_next; while ((next = qp->l_next) != NULL) qp = next; qp->l_next = &com->obufs[1]; } else { com->obufq.l_head = com->obufs[1].l_head; com->obufq.l_tail = com->obufs[1].l_tail; com->obufq.l_next = &com->obufs[1]; com->state |= CS_BUSY; } enable_intr(); } tp->t_state |= TS_BUSY; } disable_intr(); if (com->state >= (CS_BUSY | CS_TTGO)) siointr1(com); /* fake interrupt to start output */ enable_intr(); ttwwakeup(tp); splx(s); } static void siostop(tp, rw) struct tty *tp; int rw; { struct com_s *com; com = com_addr(DEV_TO_UNIT(tp->t_dev)); if (com->gone) return; disable_intr(); if (rw & FWRITE) { if (com->hasfifo) #ifdef COM_ESP /* XXX avoid h/w bug. */ if (!com->esp) #endif /* XXX does this flush everything? */ outb(com->iobase + com_fifo, FIFO_XMT_RST | com->fifo_image); com->obufs[0].l_queued = FALSE; com->obufs[1].l_queued = FALSE; if (com->state & CS_ODONE) com_events -= LOTS_OF_EVENTS; com->state &= ~(CS_ODONE | CS_BUSY); com->tp->t_state &= ~TS_BUSY; } if (rw & FREAD) { if (com->hasfifo) #ifdef COM_ESP /* XXX avoid h/w bug. */ if (!com->esp) #endif /* XXX does this flush everything? */ outb(com->iobase + com_fifo, FIFO_RCV_RST | com->fifo_image); com_events -= (com->iptr - com->ibuf); com->iptr = com->ibuf; } enable_intr(); comstart(tp); } static struct tty * siodevtotty(dev) dev_t dev; { int mynor; int unit; mynor = minor(dev); if (mynor & CONTROL_MASK) return (NULL); unit = MINOR_TO_UNIT(mynor); if ((u_int) unit >= NSIOTOT) return (NULL); return (&sio_tty[unit]); } static int commctl(com, bits, how) struct com_s *com; int bits; int how; { int mcr; int msr; if (how == DMGET) { bits = TIOCM_LE; /* XXX - always enabled while open */ mcr = com->mcr_image; if (mcr & MCR_DTR) bits |= TIOCM_DTR; if (mcr & MCR_RTS) bits |= TIOCM_RTS; msr = com->prev_modem_status; if (msr & MSR_CTS) bits |= TIOCM_CTS; if (msr & MSR_DCD) bits |= TIOCM_CD; if (msr & MSR_DSR) bits |= TIOCM_DSR; /* * XXX - MSR_RI is naturally volatile, and we make MSR_TERI * more volatile by reading the modem status a lot. Perhaps * we should latch both bits until the status is read here. */ if (msr & (MSR_RI | MSR_TERI)) bits |= TIOCM_RI; return (bits); } mcr = 0; if (bits & TIOCM_DTR) mcr |= MCR_DTR; if (bits & TIOCM_RTS) mcr |= MCR_RTS; if (com->gone) return(0); disable_intr(); switch (how) { case DMSET: outb(com->modem_ctl_port, com->mcr_image = mcr | (com->mcr_image & MCR_IENABLE)); break; case DMBIS: outb(com->modem_ctl_port, com->mcr_image |= mcr); break; case DMBIC: outb(com->modem_ctl_port, com->mcr_image &= ~mcr); break; } enable_intr(); return (0); } static void siosettimeout() { struct com_s *com; bool_t someopen; int unit; /* * Set our timeout period to 1 second if no polled devices are open. * Otherwise set it to max(1/200, 1/hz). * Enable timeouts iff some device is open. */ untimeout(comwakeup, (void *)NULL, sio_timeout_handle); sio_timeout = hz; someopen = FALSE; for (unit = 0; unit < NSIOTOT; ++unit) { com = com_addr(unit); if (com != NULL && com->tp != NULL && com->tp->t_state & TS_ISOPEN && !com->gone) { someopen = TRUE; if (com->poll || com->poll_output) { sio_timeout = hz > 200 ? hz / 200 : 1; break; } } } if (someopen) { sio_timeouts_until_log = hz / sio_timeout; sio_timeout_handle = timeout(comwakeup, (void *)NULL, sio_timeout); } else { /* Flush error messages, if any. */ sio_timeouts_until_log = 1; comwakeup((void *)NULL); untimeout(comwakeup, (void *)NULL, sio_timeout_handle); } } static void comwakeup(chan) void *chan; { struct com_s *com; int unit; sio_timeout_handle = timeout(comwakeup, (void *)NULL, sio_timeout); /* * Recover from lost output interrupts. * Poll any lines that don't use interrupts. */ for (unit = 0; unit < NSIOTOT; ++unit) { com = com_addr(unit); if (com != NULL && !com->gone && (com->state >= (CS_BUSY | CS_TTGO) || com->poll)) { disable_intr(); siointr1(com); enable_intr(); } } /* * Check for and log errors, but not too often. */ if (--sio_timeouts_until_log > 0) return; sio_timeouts_until_log = hz / sio_timeout; for (unit = 0; unit < NSIOTOT; ++unit) { int errnum; com = com_addr(unit); if (com == NULL) continue; if (com->gone) continue; for (errnum = 0; errnum < CE_NTYPES; ++errnum) { u_int delta; u_long total; disable_intr(); delta = com->delta_error_counts[errnum]; com->delta_error_counts[errnum] = 0; enable_intr(); if (delta == 0) continue; total = com->error_counts[errnum] += delta; log(LOG_ERR, "sio%d: %u more %s%s (total %lu)\n", unit, delta, error_desc[errnum], delta == 1 ? "" : "s", total); } } } static void disc_optim(tp, t, com) struct tty *tp; struct termios *t; struct com_s *com; { if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON)) && (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK)) && (!(t->c_iflag & PARMRK) || (t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK)) && !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN)) && linesw[tp->t_line].l_rint == ttyinput) tp->t_state |= TS_CAN_BYPASS_L_RINT; else tp->t_state &= ~TS_CAN_BYPASS_L_RINT; com->hotchar = linesw[tp->t_line].l_hotchar; } /* * Following are all routines needed for SIO to act as console */ #include struct siocnstate { u_char dlbl; u_char dlbh; u_char ier; u_char cfcr; u_char mcr; }; static speed_t siocngetspeed __P((Port_t, struct speedtab *)); static void siocnclose __P((struct siocnstate *sp, Port_t iobase)); static void siocnopen __P((struct siocnstate *sp, Port_t iobase, int speed)); static void siocntxwait __P((Port_t iobase)); +#ifdef __i386__ +/* + * XXX: sciocnget() and sciocnputc() are not declared static, as they are + * referred to from i386/i386/i386-gdbstub.c. + */ +static cn_probe_t siocnprobe; +static cn_init_t siocninit; +static cn_checkc_t siocncheckc; + cn_getc_t siocngetc; + cn_putc_t siocnputc; + +CONS_DRIVER(sio, siocnprobe, siocninit, siocngetc, siocncheckc, siocnputc); + +#endif + static void siocntxwait(iobase) Port_t iobase; { int timo; /* * Wait for any pending transmission to finish. Required to avoid * the UART lockup bug when the speed is changed, and for normal * transmits. */ timo = 100000; while ((inb(iobase + com_lsr) & (LSR_TSRE | LSR_TXRDY)) != (LSR_TSRE | LSR_TXRDY) && --timo != 0) ; } /* * Read the serial port specified and try to figure out what speed * it's currently running at. We're assuming the serial port has * been initialized and is basicly idle. This routine is only intended * to be run at system startup. * * If the value read from the serial port doesn't make sense, return 0. */ static speed_t siocngetspeed(iobase, table) Port_t iobase; struct speedtab *table; { int code; u_char dlbh; u_char dlbl; u_char cfcr; cfcr = inb(iobase + com_cfcr); outb(iobase + com_cfcr, CFCR_DLAB | cfcr); dlbl = inb(iobase + com_dlbl); dlbh = inb(iobase + com_dlbh); outb(iobase + com_cfcr, cfcr); code = dlbh << 8 | dlbl; for ( ; table->sp_speed != -1; table++) if (table->sp_code == code) return (table->sp_speed); return 0; /* didn't match anything sane */ } static void siocnopen(sp, iobase, speed) struct siocnstate *sp; Port_t iobase; int speed; { int divisor; u_char dlbh; u_char dlbl; /* * Save all the device control registers except the fifo register * and set our default ones (cs8 -parenb speed=comdefaultrate). * We can't save the fifo register since it is read-only. */ sp->ier = inb(iobase + com_ier); outb(iobase + com_ier, 0); /* spltty() doesn't stop siointr() */ siocntxwait(iobase); sp->cfcr = inb(iobase + com_cfcr); outb(iobase + com_cfcr, CFCR_DLAB | CFCR_8BITS); sp->dlbl = inb(iobase + com_dlbl); sp->dlbh = inb(iobase + com_dlbh); /* * Only set the divisor registers if they would change, since on * some 16550 incompatibles (Startech), setting them clears the * data input register. This also reduces the effects of the * UMC8669F bug. */ divisor = ttspeedtab(speed, comspeedtab); dlbl = divisor & 0xFF; if (sp->dlbl != dlbl) outb(iobase + com_dlbl, dlbl); dlbh = (u_int) divisor >> 8; if (sp->dlbh != dlbh) outb(iobase + com_dlbh, dlbh); outb(iobase + com_cfcr, CFCR_8BITS); sp->mcr = inb(iobase + com_mcr); /* * We don't want interrupts, but must be careful not to "disable" * them by clearing the MCR_IENABLE bit, since that might cause * an interrupt by floating the IRQ line. */ outb(iobase + com_mcr, (sp->mcr & MCR_IENABLE) | MCR_DTR | MCR_RTS); } static void siocnclose(sp, iobase) struct siocnstate *sp; Port_t iobase; { /* * Restore the device control registers. */ siocntxwait(iobase); outb(iobase + com_cfcr, CFCR_DLAB | CFCR_8BITS); if (sp->dlbl != inb(iobase + com_dlbl)) outb(iobase + com_dlbl, sp->dlbl); if (sp->dlbh != inb(iobase + com_dlbh)) outb(iobase + com_dlbh, sp->dlbh); outb(iobase + com_cfcr, sp->cfcr); /* * XXX damp oscillations of MCR_DTR and MCR_RTS by not restoring them. */ outb(iobase + com_mcr, sp->mcr | MCR_DTR | MCR_RTS); outb(iobase + com_ier, sp->ier); } void siocnprobe(cp) struct consdev *cp; { -#if 0 speed_t boot_speed; u_char cfcr; - struct isa_device *dvp; - int s; + int s, unit; struct siocnstate sp; /* * Find our first enabled console, if any. If it is a high-level * console device, then initialize it and return successfully. * If it is a low-level console device, then initialize it and * return unsuccessfully. It must be initialized in both cases * for early use by console drivers and debuggers. Initializing * the hardware is not necessary in all cases, since the i/o * routines initialize it on the fly, but it is necessary if * input might arrive while the hardware is switched back to an * uninitialized state. We can't handle multiple console devices * yet because our low-level routines don't take a device arg. * We trust the user to set the console flags properly so that we * don't need to probe. */ cp->cn_pri = CN_DEAD; - for (dvp = isa_devtab_tty; dvp->id_driver != NULL; dvp++) - if (dvp->id_driver == &siodriver && dvp->id_enabled - && COM_CONSOLE(dvp)) { - siocniobase = dvp->id_iobase; + + for (unit = 0; unit < 16; unit++) { /* XXX need to know how many */ + int flags; + if (resource_int_value("sio", unit, "flags", &flags)) + continue; + if (COM_CONSOLE(flags)) { + int port; + if (resource_int_value("sio", unit, "port", &port)) + continue; + siocniobase = port; s = spltty(); if (boothowto & RB_SERIAL) { boot_speed = siocngetspeed(siocniobase, comspeedtab); if (boot_speed) comdefaultrate = boot_speed; } /* * Initialize the divisor latch. We can't rely on * siocnopen() to do this the first time, since it * avoids writing to the latch if the latch appears * to have the correct value. Also, if we didn't * just read the speed from the hardware, then we * need to set the speed in hardware so that * switching it later is null. */ cfcr = inb(siocniobase + com_cfcr); outb(siocniobase + com_cfcr, CFCR_DLAB | cfcr); outb(siocniobase + com_dlbl, COMBRD(comdefaultrate) & 0xff); outb(siocniobase + com_dlbh, (u_int) COMBRD(comdefaultrate) >> 8); outb(siocniobase + com_cfcr, cfcr); siocnopen(&sp, siocniobase, comdefaultrate); splx(s); - if (!COM_LLCONSOLE(dvp)) { - cp->cn_dev = makedev(CDEV_MAJOR, dvp->id_unit); - cp->cn_pri = COM_FORCECONSOLE(dvp) + if (!COM_LLCONSOLE(flags)) { + cp->cn_dev = makedev(CDEV_MAJOR, unit); + cp->cn_pri = COM_FORCECONSOLE(flags) || boothowto & RB_SERIAL ? CN_REMOTE : CN_NORMAL; } break; } -#endif + } } +#ifdef __alpha__ + struct consdev siocons = { NULL, NULL, siocngetc, siocncheckc, siocnputc, NULL, makedev(CDEV_MAJOR, 0), CN_NORMAL, }; extern struct consdev *cn_tab; int siocnattach(port, speed) int port; int speed; { int s; u_char cfcr; struct siocnstate sp; siocniobase = port; comdefaultrate = speed; s = spltty(); /* * Initialize the divisor latch. We can't rely on * siocnopen() to do this the first time, since it * avoids writing to the latch if the latch appears * to have the correct value. Also, if we didn't * just read the speed from the hardware, then we * need to set the speed in hardware so that * switching it later is null. */ cfcr = inb(siocniobase + com_cfcr); outb(siocniobase + com_cfcr, CFCR_DLAB | cfcr); outb(siocniobase + com_dlbl, COMBRD(comdefaultrate) & 0xff); outb(siocniobase + com_dlbh, (u_int) COMBRD(comdefaultrate) >> 8); outb(siocniobase + com_cfcr, cfcr); siocnopen(&sp, siocniobase, comdefaultrate); splx(s); cn_tab = &siocons; return 0; } int siogdbattach(port, speed) int port; int speed; { int s; u_char cfcr; struct siocnstate sp; siogdbiobase = port; gdbdefaultrate = speed; s = spltty(); /* * Initialize the divisor latch. We can't rely on * siocnopen() to do this the first time, since it * avoids writing to the latch if the latch appears * to have the correct value. Also, if we didn't * just read the speed from the hardware, then we * need to set the speed in hardware so that * switching it later is null. */ cfcr = inb(siogdbiobase + com_cfcr); outb(siogdbiobase + com_cfcr, CFCR_DLAB | cfcr); outb(siogdbiobase + com_dlbl, COMBRD(gdbdefaultrate) & 0xff); outb(siogdbiobase + com_dlbh, (u_int) COMBRD(gdbdefaultrate) >> 8); outb(siogdbiobase + com_cfcr, cfcr); siocnopen(&sp, siogdbiobase, gdbdefaultrate); splx(s); return 0; } +#endif + void siocninit(cp) struct consdev *cp; { comconsole = DEV_TO_UNIT(cp->cn_dev); } int siocncheckc(dev) dev_t dev; { int c; Port_t iobase; int s; struct siocnstate sp; iobase = siocniobase; s = spltty(); siocnopen(&sp, iobase, comdefaultrate); if (inb(iobase + com_lsr) & LSR_RXRDY) c = inb(iobase + com_data); else c = -1; siocnclose(&sp, iobase); splx(s); return (c); } int siocngetc(dev) dev_t dev; { int c; Port_t iobase; int s; struct siocnstate sp; iobase = siocniobase; s = spltty(); siocnopen(&sp, iobase, comdefaultrate); while (!(inb(iobase + com_lsr) & LSR_RXRDY)) ; c = inb(iobase + com_data); siocnclose(&sp, iobase); splx(s); return (c); } void siocnputc(dev, c) dev_t dev; int c; { int s; struct siocnstate sp; s = spltty(); siocnopen(&sp, siocniobase, comdefaultrate); siocntxwait(siocniobase); outb(siocniobase + com_data, c); siocnclose(&sp, siocniobase); splx(s); } int siogdbgetc() { int c; Port_t iobase; int s; struct siocnstate sp; iobase = siogdbiobase; s = spltty(); siocnopen(&sp, iobase, gdbdefaultrate); while (!(inb(iobase + com_lsr) & LSR_RXRDY)) ; c = inb(iobase + com_data); siocnclose(&sp, iobase); splx(s); return (c); } void siogdbputc(c) int c; { int s; struct siocnstate sp; s = spltty(); siocnopen(&sp, siogdbiobase, gdbdefaultrate); siocntxwait(siogdbiobase); outb(siogdbiobase + com_data, c); siocnclose(&sp, siogdbiobase); splx(s); } #ifdef DSI_SOFT_MODEM /* * The magic code to download microcode to a "Connection 14.4+Fax" * modem from Digicom Systems Inc. Very magic. */ #define DSI_ERROR(str) { ptr = str; goto error; } static int LoadSoftModem(int unit, int base_io, u_long size, u_char *ptr) { int int_c,int_k; int data_0188, data_0187; /* * First see if it is a DSI SoftModem */ if(!((inb(base_io+7) ^ inb(base_io+7)) & 0x80)) return ENODEV; data_0188 = inb(base_io+4); data_0187 = inb(base_io+3); outb(base_io+3,0x80); outb(base_io+4,0x0C); outb(base_io+0,0x31); outb(base_io+1,0x8C); outb(base_io+7,0x10); outb(base_io+7,0x19); if(0x18 != (inb(base_io+7) & 0x1A)) DSI_ERROR("dsp bus not granted"); if(0x01 != (inb(base_io+7) & 0x01)) { outb(base_io+7,0x18); outb(base_io+7,0x19); if(0x01 != (inb(base_io+7) & 0x01)) DSI_ERROR("program mem not granted"); } int_c = 0; while(1) { if(int_c >= 7 || size <= 0x1800) break; for(int_k = 0 ; int_k < 0x800; int_k++) { outb(base_io+0,*ptr++); outb(base_io+1,*ptr++); outb(base_io+2,*ptr++); } size -= 0x1800; int_c++; } if(size > 0x1800) { outb(base_io+7,0x18); outb(base_io+7,0x19); if(0x00 != (inb(base_io+7) & 0x01)) DSI_ERROR("program data not granted"); for(int_k = 0 ; int_k < 0x800; int_k++) { outb(base_io+1,*ptr++); outb(base_io+2,0); outb(base_io+1,*ptr++); outb(base_io+2,*ptr++); } size -= 0x1800; while(size > 0x1800) { for(int_k = 0 ; int_k < 0xC00; int_k++) { outb(base_io+1,*ptr++); outb(base_io+2,*ptr++); } size -= 0x1800; } if(size < 0x1800) { for(int_k=0;int_k 0) { if(int_c == 7) { outb(base_io+7,0x18); outb(base_io+7,0x19); if(0x00 != (inb(base_io+7) & 0x01)) DSI_ERROR("program data not granted"); for(int_k = 0 ; int_k < size/3; int_k++) { outb(base_io+1,*ptr++); outb(base_io+2,0); outb(base_io+1,*ptr++); outb(base_io+2,*ptr++); } } else { for(int_k = 0 ; int_k < size/3; int_k++) { outb(base_io+0,*ptr++); outb(base_io+1,*ptr++); outb(base_io+2,*ptr++); } } } outb(base_io+7,0x11); outb(base_io+7,3); outb(base_io+4,data_0188 & 0xfb); outb(base_io+3,data_0187); return 0; error: printf("sio%d: DSI SoftModem microcode load failed: <%s>\n",unit,ptr); outb(base_io+7,0x00); \ outb(base_io+3,data_0187); \ outb(base_io+4,data_0188); \ return EIO; } #endif /* DSI_SOFT_MODEM */ /* * support PnP cards if we are using 'em */ #if NPNP > 0 static pnpid_t siopnp_ids[] = { { 0x5015f435, "MOT1550"}, { 0x8113b04e, "Supra1381"}, { 0x9012b04e, "Supra1290"}, { 0x7121b04e, "SupraExpress 56i Sp"}, { 0x11007256, "USR0011"}, { 0x30207256, "USR2030"}, { 0x31307256, "USR3031"}, { 0 } }; static char *siopnp_probe(u_long csn, u_long vend_id); static void siopnp_attach(u_long csn, u_long vend_id, char *name, struct isa_device *dev); static u_long nsiopnp = NSIO; static struct pnp_device siopnp = { "siopnp", siopnp_probe, siopnp_attach, &nsiopnp, &tty_imask }; DATA_SET (pnpdevice_set, siopnp); static char * siopnp_probe(u_long csn, u_long vend_id) { pnpid_t *id; char *s = NULL; for(id = siopnp_ids; id->vend_id != 0; id++) { if (vend_id == id->vend_id) { s = id->id_str; break; } } if (s) { struct pnp_cinfo d; read_pnp_parms(&d, 0); if (d.enable == 0 || d.flags & 1) { printf("CSN %lu is disabled.\n", csn); return (NULL); } } return (s); } static void siopnp_attach(u_long csn, u_long vend_id, char *name, struct isa_device *dev) { struct pnp_cinfo d; - struct isa_device *dvp; if (dev->id_unit >= NSIOTOT) return; if (read_pnp_parms(&d, 0) == 0) { printf("failed to read pnp parms\n"); return; } write_pnp_parms(&d, 0); enable_pnp_card(); dev->id_iobase = d.port[0]; dev->id_irq = (1 << d.irq[0]); dev->id_intr = siointr; dev->id_ri_flags = RI_FAST; dev->id_drq = -1; if (dev->id_driver == NULL) { dev->id_driver = &siodriver; - dvp = find_isadev(isa_devtab_tty, &siodriver, 0); - if (dvp != NULL) - dev->id_id = dvp->id_id; + dev->id_id = isa_compat_nextid(); } if ((dev->id_alive = sioprobe(dev)) != 0) sioattach(dev); else printf("sio%d: probe failed\n", dev->id_unit); } #endif CDEV_DRIVER_MODULE(sio, isa, sio_driver, sio_devclass, CDEV_MAJOR, sio_cdevsw, 0, 0); Index: head/sys/dev/syscons/syscons.c =================================================================== --- head/sys/dev/syscons/syscons.c (revision 45719) +++ head/sys/dev/syscons/syscons.c (revision 45720) @@ -1,4536 +1,4544 @@ /*- * Copyright (c) 1992-1998 Søren Schmidt * 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, * without modification, immediately at the beginning of 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. 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. * - * $Id: syscons.c,v 1.298 1999/02/05 11:52:11 yokota Exp $ + * $Id: syscons.c,v 1.299 1999/03/10 10:36:53 yokota Exp $ */ #include "sc.h" #include "splash.h" #ifdef __i386__ #include "apm.h" #endif #include "opt_ddb.h" #include "opt_devfs.h" #ifdef __i386__ #include "opt_vesa.h" #include "opt_vm86.h" #endif #include "opt_syscons.h" #if NSC > 0 #include #include #include #include #include #include #include #include #include #ifdef DEVFS #include #endif #include #include #include #include #include #include #include #include #include #ifdef __i386__ #include #include #include #endif #include #include #include #include #include #include #include #include #ifndef __i386__ #include #else #include #include #include #endif #if !defined(MAXCONS) #define MAXCONS 16 #endif #if !defined(SC_MAX_HISTORY_SIZE) #define SC_MAX_HISTORY_SIZE (1000 * MAXCONS) #endif #if !defined(SC_HISTORY_SIZE) #define SC_HISTORY_SIZE (ROW * 4) #endif #if (SC_HISTORY_SIZE * MAXCONS) > SC_MAX_HISTORY_SIZE #undef SC_MAX_HISTORY_SIZE #define SC_MAX_HISTORY_SIZE (SC_HISTORY_SIZE * MAXCONS) #endif #if !defined(SC_MOUSE_CHAR) #define SC_MOUSE_CHAR (0xd0) #endif #define COLD 0 #define WARM 1 #define DEFAULT_BLANKTIME (5*60) /* 5 minutes */ #define MAX_BLANKTIME (7*24*60*60) /* 7 days!? */ /* for backward compatibility */ #define OLD_CONS_MOUSECTL _IOWR('c', 10, old_mouse_info_t) typedef struct old_mouse_data { int x; int y; int buttons; } old_mouse_data_t; typedef struct old_mouse_info { int operation; union { struct old_mouse_data data; struct mouse_mode mode; } u; } old_mouse_info_t; static default_attr user_default = { (FG_LIGHTGREY | BG_BLACK) << 8, (FG_BLACK | BG_LIGHTGREY) << 8 }; static default_attr kernel_default = { (FG_WHITE | BG_BLACK) << 8, (FG_BLACK | BG_LIGHTGREY) << 8 }; static scr_stat main_console; static scr_stat *console[MAXCONS]; #ifdef DEVFS static void *sc_devfs_token[MAXCONS]; static void *sc_mouse_devfs_token; static void *sc_console_devfs_token; #endif scr_stat *cur_console; static scr_stat *new_scp, *old_scp; static term_stat kernel_console; static default_attr *current_default; static int sc_flags; static char init_done = COLD; static u_short sc_buffer[ROW*COL]; static char shutdown_in_progress = FALSE; static char font_loading_in_progress = FALSE; static char switch_in_progress = FALSE; static char write_in_progress = FALSE; static char blink_in_progress = FALSE; static int blinkrate = 0; static int adapter = -1; static int keyboard = -1; static keyboard_t *kbd; static int delayed_next_scr = FALSE; static long scrn_blank_time = 0; /* screen saver timeout value */ static int scrn_blanked = FALSE; /* screen saver active flag */ static long scrn_time_stamp; static int saver_mode = CONS_LKM_SAVER; /* LKM/user saver */ static int run_scrn_saver = FALSE; /* should run the saver? */ static int scrn_idle = FALSE; /* about to run the saver */ #if NSPLASH > 0 static int scrn_saver_failed; #endif u_char scr_map[256]; u_char scr_rmap[256]; static int initial_video_mode; /* initial video mode # */ int fonts_loaded = 0 #ifdef STD8X16FONT | FONT_16 #endif ; u_char font_8[256*8]; u_char font_14[256*14]; #ifdef STD8X16FONT extern #endif u_char font_16[256*16]; u_char palette[256*3]; static u_char *cut_buffer; static int cut_buffer_size; static int mouse_level; /* sysmouse protocol level */ static mousestatus_t mouse_status = { 0, 0, 0, 0, 0, 0 }; static u_short mouse_and_mask[16] = { 0xc000, 0xe000, 0xf000, 0xf800, 0xfc00, 0xfe00, 0xff00, 0xff80, 0xfe00, 0x1e00, 0x1f00, 0x0f00, 0x0f00, 0x0000, 0x0000, 0x0000 }; static u_short mouse_or_mask[16] = { 0x0000, 0x4000, 0x6000, 0x7000, 0x7800, 0x7c00, 0x7e00, 0x6800, 0x0c00, 0x0c00, 0x0600, 0x0600, 0x0000, 0x0000, 0x0000, 0x0000 }; int sc_history_size = SC_HISTORY_SIZE; static int extra_history_size = SC_MAX_HISTORY_SIZE - SC_HISTORY_SIZE * MAXCONS; static void none_saver(int blank) { } static void (*current_saver)(int blank) = none_saver; d_ioctl_t *sc_user_ioctl; static int sticky_splash = FALSE; static struct { u_int8_t cursor_start; u_int8_t cursor_end; u_int8_t shift_state; } bios_value; /* OS specific stuff */ #ifdef not_yet_done #define VIRTUAL_TTY(x) (sccons[x] = ttymalloc(sccons[x])) struct CONSOLE_TTY (sccons[MAXCONS] = ttymalloc(sccons[MAXCONS])) struct MOUSE_TTY (sccons[MAXCONS+1] = ttymalloc(sccons[MAXCONS+1])) struct tty *sccons[MAXCONS+2]; #else #define VIRTUAL_TTY(x) &sccons[x] #define CONSOLE_TTY &sccons[MAXCONS] #define MOUSE_TTY &sccons[MAXCONS+1] static struct tty sccons[MAXCONS+2]; #endif #define SC_MOUSE 128 #define SC_CONSOLE 255 vm_offset_t Crtat; static const int nsccons = MAXCONS+2; #define WRAPHIST(scp, pointer, offset)\ ((scp)->history + ((((pointer) - (scp)->history) + (scp)->history_size \ + (offset)) % (scp)->history_size)) #define ISSIGVALID(sig) ((sig) > 0 && (sig) < NSIG) /* some useful macros */ #define kbd_read_char(kbd, wait) \ (*kbdsw[(kbd)->kb_index]->read_char)((kbd), (wait)) #define kbd_check_char(kbd) \ (*kbdsw[(kbd)->kb_index]->check_char)((kbd)) #define kbd_enable(kbd) \ (*kbdsw[(kbd)->kb_index]->enable)((kbd)) #define kbd_disable(kbd) \ (*kbdsw[(kbd)->kb_index]->disable)((kbd)) #define kbd_lock(kbd, lockf) \ (*kbdsw[(kbd)->kb_index]->lock)((kbd), (lockf)) #define kbd_ioctl(kbd, cmd, arg) \ (((kbd) == NULL) ? \ ENODEV : (*kbdsw[(kbd)->kb_index]->ioctl)((kbd), (cmd), (arg))) #define kbd_clear_state(kbd) \ (*kbdsw[(kbd)->kb_index]->clear_state)((kbd)) #define kbd_get_fkeystr(kbd, fkey, len) \ (*kbdsw[(kbd)->kb_index]->get_fkeystr)((kbd), (fkey), (len)) #define kbd_poll(kbd, on) \ (*kbdsw[(kbd)->kb_index]->poll)((kbd), (on)) /* prototypes */ static kbd_callback_func_t sckbdevent; static int scparam(struct tty *tp, struct termios *t); static int scvidprobe(int unit, int flags, int cons); static int sckbdprobe(int unit, int flags, int cons); static void scstart(struct tty *tp); static void scmousestart(struct tty *tp); static void scinit(void); static void scshutdown(int howto, void *arg); static u_int scgetc(keyboard_t *kbd, u_int flags); #define SCGETC_CN 1 #define SCGETC_NONBLOCK 2 static int sccngetch(int flags); static void sccnupdate(scr_stat *scp); static scr_stat *alloc_scp(void); static void init_scp(scr_stat *scp); static void get_bios_values(void); static void sc_bcopy(scr_stat *scp, u_short *p, int from, int to, int mark); static int get_scr_num(void); static timeout_t scrn_timer; static void scrn_update(scr_stat *scp, int show_cursor); #if NSPLASH > 0 static int scsplash_callback(int); static void scsplash_saver(int show); static int add_scrn_saver(void (*this_saver)(int)); static int remove_scrn_saver(void (*this_saver)(int)); static int set_scrn_saver_mode(scr_stat *scp, int mode, u_char *pal, int border); static int restore_scrn_saver_mode(scr_stat *scp, int changemode); static void stop_scrn_saver(void (*saver)(int)); static int wait_scrn_saver_stop(void); #define scsplash_stick(stick) (sticky_splash = (stick)) #else /* !NSPLASH */ #define stop_scrn_saver(saver) #define wait_scrn_saver_stop() 0 #define scsplash_stick(stick) #endif /* NSPLASH */ static int switch_scr(scr_stat *scp, u_int next_scr); static void exchange_scr(void); static void scan_esc(scr_stat *scp, u_char c); static void ansi_put(scr_stat *scp, u_char *buf, int len); static void draw_cursor_image(scr_stat *scp); static void remove_cursor_image(scr_stat *scp); static void move_crsr(scr_stat *scp, int x, int y); static void history_to_screen(scr_stat *scp); static int history_up_line(scr_stat *scp); static int history_down_line(scr_stat *scp); static int mask2attr(struct term_stat *term); static int save_kbd_state(scr_stat *scp); static int update_kbd_state(int state, int mask); static int update_kbd_leds(int which); static void set_destructive_cursor(scr_stat *scp); static void set_mouse_pos(scr_stat *scp); static int skip_spc_right(scr_stat *scp, u_short *p); static int skip_spc_left(scr_stat *scp, u_short *p); static void mouse_cut(scr_stat *scp); static void mouse_cut_start(scr_stat *scp); static void mouse_cut_end(scr_stat *scp); static void mouse_cut_word(scr_stat *scp); static void mouse_cut_line(scr_stat *scp); static void mouse_cut_extend(scr_stat *scp); static void mouse_paste(scr_stat *scp); static void draw_mouse_image(scr_stat *scp); static void remove_mouse_image(scr_stat *scp); static void draw_cutmarking(scr_stat *scp); static void remove_cutmarking(scr_stat *scp); static void do_bell(scr_stat *scp, int pitch, int duration); static timeout_t blink_screen; #define CDEV_MAJOR 12 #ifdef __i386__ static cn_probe_t sccnprobe; static cn_init_t sccninit; static cn_getc_t sccngetc; static cn_checkc_t sccncheckc; static cn_putc_t sccnputc; CONS_DRIVER(sc, sccnprobe, sccninit, sccngetc, sccncheckc, sccnputc); #else /* !__i386__ */ static cn_getc_t sccngetc; static cn_checkc_t sccncheckc; static cn_putc_t sccnputc; struct consdev sc_cons = { NULL, NULL, sccngetc, sccncheckc, sccnputc, NULL, makedev(CDEV_MAJOR, 0), CN_NORMAL, }; #endif /* __i386__ */ static d_open_t scopen; static d_close_t scclose; static d_read_t scread; static d_write_t scwrite; static d_ioctl_t scioctl; static d_mmap_t scmmap; struct cdevsw sc_cdevsw = { scopen, scclose, scread, scwrite, scioctl, nullstop, noreset, scdevtotty, ttpoll, scmmap, nostrategy, "sc", NULL, -1, nodump, nopsize, D_TTY, }; #ifdef __i386__ #define fillw_io(p, b, c) fillw((p), (void *)(b), (c)) #endif #ifdef __alpha__ static void fillw(int pat, void *base, size_t cnt) { u_short *sp = base; while (cnt--) *sp++ = pat; } static void fillw_io(int pat, u_int32_t base, size_t cnt) { while (cnt--) { writew(base, pat); base += 2; } } #endif static void draw_cursor_image(scr_stat *scp) { u_short cursor_image; vm_offset_t ptr; u_short prev_image; if (ISPIXELSC(scp)) { sc_bcopy(scp, scp->scr_buf, scp->cursor_pos - scp->scr_buf, scp->cursor_pos - scp->scr_buf, 1); return; } ptr = scp->adp->va_window + 2*(scp->cursor_pos - scp->scr_buf); /* do we have a destructive cursor ? */ if (sc_flags & CHAR_CURSOR) { prev_image = scp->cursor_saveunder; cursor_image = readw(ptr) & 0x00ff; if (cursor_image == DEAD_CHAR) cursor_image = prev_image & 0x00ff; cursor_image |= *(scp->cursor_pos) & 0xff00; scp->cursor_saveunder = cursor_image; /* update the cursor bitmap if the char under the cursor has changed */ if (prev_image != cursor_image) set_destructive_cursor(scp); /* modify cursor_image */ if (!(sc_flags & BLINK_CURSOR)||((sc_flags & BLINK_CURSOR)&&(blinkrate & 4))){ /* * When the mouse pointer is at the same position as the cursor, * the cursor bitmap needs to be updated even if the char under * the cursor hasn't changed, because the mouse pionter may * have moved by a few dots within the cursor cel. */ if ((prev_image == cursor_image) && (cursor_image != *(scp->cursor_pos))) set_destructive_cursor(scp); cursor_image &= 0xff00; cursor_image |= DEAD_CHAR; } } else { cursor_image = (readw(ptr) & 0x00ff) | (*(scp->cursor_pos) & 0xff00); scp->cursor_saveunder = cursor_image; if (!(sc_flags & BLINK_CURSOR)||((sc_flags & BLINK_CURSOR)&&(blinkrate & 4))){ if ((cursor_image & 0x7000) == 0x7000) { cursor_image &= 0x8fff; if(!(cursor_image & 0x0700)) cursor_image |= 0x0700; } else { cursor_image |= 0x7000; if ((cursor_image & 0x0700) == 0x0700) cursor_image &= 0xf0ff; } } } writew(ptr, cursor_image); } static void remove_cursor_image(scr_stat *scp) { if (ISPIXELSC(scp)) sc_bcopy(scp, scp->scr_buf, scp->cursor_oldpos - scp->scr_buf, scp->cursor_oldpos - scp->scr_buf, 0); else writew(scp->adp->va_window + 2*(scp->cursor_oldpos - scp->scr_buf), scp->cursor_saveunder); } static void move_crsr(scr_stat *scp, int x, int y) { if (x < 0) x = 0; if (y < 0) y = 0; if (x >= scp->xsize) x = scp->xsize-1; if (y >= scp->ysize) y = scp->ysize-1; scp->xpos = x; scp->ypos = y; scp->cursor_pos = scp->scr_buf + scp->ypos * scp->xsize + scp->xpos; } int sc_probe_unit(int unit, int flags) { if (!scvidprobe(unit, flags, FALSE)) { if (bootverbose) printf("sc%d: no video adapter is found.\n", unit); return ENXIO; } return ((sckbdprobe(unit, flags, FALSE)) ? 0 : ENXIO); } /* probe video adapters, return TRUE if found */ static int scvidprobe(int unit, int flags, int cons) { video_adapter_t *adp; /* * Access the video adapter driver through the back door! * Video adapter drivers need to be configured before syscons. * However, when syscons is being probed as the low-level console, * they have not been initialized yet. We force them to initialize * themselves here. XXX */ vid_configure(cons ? VIO_PROBE_ONLY : 0); /* allocate a frame buffer */ if (adapter < 0) { adapter = vid_allocate("*", -1, (void *)&adapter); if (adapter < 0) return FALSE; } adp = vid_get_adapter(adapter); /* shouldn't fail */ Crtat = adp->va_window; initial_video_mode = adp->va_initial_mode; return TRUE; } /* probe the keyboard, return TRUE if found */ static int sckbdprobe(int unit, int flags, int cons) { /* access the keyboard driver through the backdoor! */ kbd_configure(cons ? KB_CONF_PROBE_ONLY : 0); /* allocate a keyboard and register the keyboard event handler */ if (keyboard < 0) { keyboard = kbd_allocate("*", -1, (void *)&keyboard, sckbdevent, NULL); if (keyboard < 0) return FALSE; } kbd = kbd_get_keyboard(keyboard); /* shouldn't fail */ return TRUE; } #if NAPM > 0 static int scresume(void *dummy) { if (kbd != NULL) kbd_clear_state(kbd); return 0; } #endif int sc_attach_unit(int unit, int flags) { scr_stat *scp; #if defined(VESA) && defined(VM86) video_info_t info; #endif dev_t cdev = makedev(CDEV_MAJOR, 0); #ifdef DEVFS int vc; #endif scinit(); scp = console[0]; sc_flags = flags; if (!ISFONTAVAIL(scp->adp->va_flags)) sc_flags &= ~CHAR_CURSOR; /* copy temporary buffer to final buffer */ scp->scr_buf = NULL; sc_alloc_scr_buffer(scp, FALSE, FALSE); bcopy(sc_buffer, scp->scr_buf, scp->xsize*scp->ysize*sizeof(u_short)); /* cut buffer is available only when the mouse pointer is used */ if (ISMOUSEAVAIL(scp->adp->va_flags)) sc_alloc_cut_buffer(scp, FALSE); /* initialize history buffer & pointers */ sc_alloc_history_buffer(scp, sc_history_size, 0, FALSE); #if defined(VESA) && defined(VM86) if ((sc_flags & VESA800X600) && ((*vidsw[scp->ad]->get_info)(scp->adp, M_VESA_800x600, &info) == 0)) { #if NSPLASH > 0 splash_term(scp->adp); #endif sc_set_graphics_mode(scp, NULL, M_VESA_800x600); sc_set_pixel_mode(scp, NULL, COL, ROW, 16); initial_video_mode = M_VESA_800x600; #if NSPLASH > 0 /* put up the splash again! */ splash_init(scp->adp, scsplash_callback); #endif } #endif /* VESA && VM86 */ /* initialize cursor stuff */ if (!ISGRAPHSC(scp)) draw_cursor_image(scp); /* get screen update going */ scrn_timer((void *)TRUE); /* set up the keyboard */ kbd_ioctl(kbd, KDSKBMODE, (caddr_t)&scp->kbd_mode); update_kbd_state(scp->status, LOCK_MASK); if (bootverbose) { printf("sc%d:", unit); if (adapter >= 0) printf(" fb%d", adapter); if (keyboard >= 0) printf(" kbd%d", keyboard); printf("\n"); } printf("sc%d: ", unit); switch(scp->adp->va_type) { case KD_VGA: printf("VGA %s", (scp->adp->va_flags & V_ADP_COLOR) ? "color" : "mono"); break; case KD_EGA: printf("EGA %s", (scp->adp->va_flags & V_ADP_COLOR) ? "color" : "mono"); break; case KD_CGA: printf("CGA"); break; case KD_MONO: case KD_HERCULES: default: printf("MDA/Hercules"); break; } printf(" <%d virtual consoles, flags=0x%x>\n", MAXCONS, sc_flags); #if NAPM > 0 scp->r_hook.ah_fun = scresume; scp->r_hook.ah_arg = NULL; scp->r_hook.ah_name = "system keyboard"; scp->r_hook.ah_order = APM_MID_ORDER; apm_hook_establish(APM_HOOK_RESUME , &scp->r_hook); #endif at_shutdown(scshutdown, NULL, SHUTDOWN_PRE_SYNC); cdevsw_add(&cdev, &sc_cdevsw, NULL); #ifdef DEVFS for (vc = 0; vc < MAXCONS; vc++) sc_devfs_token[vc] = devfs_add_devswf(&sc_cdevsw, vc, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "ttyv%r", vc); sc_mouse_devfs_token = devfs_add_devswf(&sc_cdevsw, SC_MOUSE, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "sysmouse"); sc_console_devfs_token = devfs_add_devswf(&sc_cdevsw, SC_CONSOLE, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "consolectl"); #endif return 0; } struct tty *scdevtotty(dev_t dev) { int unit = minor(dev); if (init_done == COLD) return(NULL); if (unit == SC_CONSOLE) return CONSOLE_TTY; if (unit == SC_MOUSE) return MOUSE_TTY; if (unit >= MAXCONS || unit < 0) return(NULL); return VIRTUAL_TTY(unit); } int scopen(dev_t dev, int flag, int mode, struct proc *p) { struct tty *tp = scdevtotty(dev); keyarg_t key; if (!tp) return(ENXIO); tp->t_oproc = (minor(dev) == SC_MOUSE) ? scmousestart : scstart; tp->t_param = scparam; tp->t_dev = dev; if (!(tp->t_state & TS_ISOPEN)) { ttychars(tp); /* Use the current setting of the <-- key as default VERASE. */ /* If the Delete key is preferable, an stty is necessary */ key.keynum = 0x0e; /* how do we know this magic number... XXX */ kbd_ioctl(kbd, GIO_KEYMAPENT, (caddr_t)&key); tp->t_cc[VERASE] = key.key.map[0]; tp->t_iflag = TTYDEF_IFLAG; tp->t_oflag = TTYDEF_OFLAG; tp->t_cflag = TTYDEF_CFLAG; tp->t_lflag = TTYDEF_LFLAG; tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; scparam(tp, &tp->t_termios); (*linesw[tp->t_line].l_modem)(tp, 1); if (minor(dev) == SC_MOUSE) mouse_level = 0; /* XXX */ } else if (tp->t_state & TS_XCLUDE && suser(p->p_ucred, &p->p_acflag)) return(EBUSY); if (minor(dev) < MAXCONS && !console[minor(dev)]) { console[minor(dev)] = alloc_scp(); if (ISGRAPHSC(console[minor(dev)])) sc_set_pixel_mode(console[minor(dev)], NULL, COL, ROW, 16); } if (minor(dev)t_winsize.ws_col && !tp->t_winsize.ws_row) { tp->t_winsize.ws_col = console[minor(dev)]->xsize; tp->t_winsize.ws_row = console[minor(dev)]->ysize; } return ((*linesw[tp->t_line].l_open)(dev, tp)); } int scclose(dev_t dev, int flag, int mode, struct proc *p) { struct tty *tp = scdevtotty(dev); struct scr_stat *scp; if (!tp) return(ENXIO); if (minor(dev) < MAXCONS) { scp = sc_get_scr_stat(tp->t_dev); if (scp->status & SWITCH_WAIT_ACQ) wakeup((caddr_t)&scp->smode); #if not_yet_done if (scp == &main_console) { scp->pid = 0; scp->proc = NULL; scp->smode.mode = VT_AUTO; } else { free(scp->scr_buf, M_DEVBUF); if (scp->history != NULL) { free(scp->history, M_DEVBUF); if (scp->history_size / scp->xsize > imax(sc_history_size, scp->ysize)) extra_history_size += scp->history_size / scp->xsize - imax(sc_history_size, scp->ysize); } free(scp, M_DEVBUF); console[minor(dev)] = NULL; } #else scp->pid = 0; scp->proc = NULL; scp->smode.mode = VT_AUTO; #endif } spltty(); (*linesw[tp->t_line].l_close)(tp, flag); ttyclose(tp); spl0(); return(0); } int scread(dev_t dev, struct uio *uio, int flag) { struct tty *tp = scdevtotty(dev); if (!tp) return(ENXIO); sc_touch_scrn_saver(); return((*linesw[tp->t_line].l_read)(tp, uio, flag)); } int scwrite(dev_t dev, struct uio *uio, int flag) { struct tty *tp = scdevtotty(dev); if (!tp) return(ENXIO); return((*linesw[tp->t_line].l_write)(tp, uio, flag)); } static int sckbdevent(keyboard_t *thiskbd, int event, void *arg) { static struct tty *cur_tty; int c; size_t len; u_char *cp; /* assert(thiskbd == kbd) */ switch (event) { case KBDIO_KEYINPUT: break; case KBDIO_UNLOADING: kbd = NULL; kbd_release(thiskbd, (void *)&keyboard); return 0; default: return EINVAL; } /* * Loop while there is still input to get from the keyboard. * I don't think this is nessesary, and it doesn't fix * the Xaccel-2.1 keyboard hang, but it can't hurt. XXX */ while ((c = scgetc(thiskbd, SCGETC_NONBLOCK)) != NOKEY) { cur_tty = VIRTUAL_TTY(get_scr_num()); if (!(cur_tty->t_state & TS_ISOPEN)) if (!((cur_tty = CONSOLE_TTY)->t_state & TS_ISOPEN)) continue; switch (KEYFLAGS(c)) { case 0x0000: /* normal key */ (*linesw[cur_tty->t_line].l_rint)(KEYCHAR(c), cur_tty); break; case FKEY: /* function key, return string */ cp = kbd_get_fkeystr(thiskbd, KEYCHAR(c), &len); if (cp != NULL) { while (len-- > 0) (*linesw[cur_tty->t_line].l_rint)(*cp++, cur_tty); } break; case MKEY: /* meta is active, prepend ESC */ (*linesw[cur_tty->t_line].l_rint)(0x1b, cur_tty); (*linesw[cur_tty->t_line].l_rint)(KEYCHAR(c), cur_tty); break; case BKEY: /* backtab fixed sequence (esc [ Z) */ (*linesw[cur_tty->t_line].l_rint)(0x1b, cur_tty); (*linesw[cur_tty->t_line].l_rint)('[', cur_tty); (*linesw[cur_tty->t_line].l_rint)('Z', cur_tty); break; } } if (cur_console->status & MOUSE_VISIBLE) { remove_mouse_image(cur_console); cur_console->status &= ~MOUSE_VISIBLE; } return 0; } static int scparam(struct tty *tp, struct termios *t) { tp->t_ispeed = t->c_ispeed; tp->t_ospeed = t->c_ospeed; tp->t_cflag = t->c_cflag; return 0; } int scioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) { u_int delta_ehs; int error; int i; struct tty *tp; scr_stat *scp; int s; tp = scdevtotty(dev); if (!tp) return ENXIO; scp = sc_get_scr_stat(tp->t_dev); /* If there is a user_ioctl function call that first */ if (sc_user_ioctl) { error = (*sc_user_ioctl)(dev, cmd, data, flag, p); if (error != ENOIOCTL) return error; } error = sc_vid_ioctl(tp, cmd, data, flag, p); if (error != ENOIOCTL) return error; switch (cmd) { /* process console hardware related ioctl's */ case GIO_ATTR: /* get current attributes */ *(int*)data = (scp->term.cur_attr >> 8) & 0xFF; return 0; case GIO_COLOR: /* is this a color console ? */ *(int *)data = (scp->adp->va_flags & V_ADP_COLOR) ? 1 : 0; return 0; case CONS_BLANKTIME: /* set screen saver timeout (0 = no saver) */ if (*(int *)data < 0 || *(int *)data > MAX_BLANKTIME) return EINVAL; s = spltty(); scrn_blank_time = *(int *)data; run_scrn_saver = (scrn_blank_time != 0); splx(s); return 0; case CONS_CURSORTYPE: /* set cursor type blink/noblink */ if ((*(int*)data) & 0x01) sc_flags |= BLINK_CURSOR; else sc_flags &= ~BLINK_CURSOR; if ((*(int*)data) & 0x02) { if (!ISFONTAVAIL(scp->adp->va_flags)) return ENXIO; sc_flags |= CHAR_CURSOR; } else sc_flags &= ~CHAR_CURSOR; /* * The cursor shape is global property; all virtual consoles * are affected. Update the cursor in the current console... */ if (!ISGRAPHSC(cur_console)) { s = spltty(); remove_cursor_image(cur_console); if (sc_flags & CHAR_CURSOR) set_destructive_cursor(cur_console); draw_cursor_image(cur_console); splx(s); } return 0; case CONS_BELLTYPE: /* set bell type sound/visual */ if ((*(int *)data) & 0x01) sc_flags |= VISUAL_BELL; else sc_flags &= ~VISUAL_BELL; if ((*(int *)data) & 0x02) sc_flags |= QUIET_BELL; else sc_flags &= ~QUIET_BELL; return 0; case CONS_HISTORY: /* set history size */ if (*(int *)data > 0) { int lines; /* buffer size to allocate */ int lines0; /* current buffer size */ lines = imax(*(int *)data, scp->ysize); lines0 = (scp->history != NULL) ? scp->history_size / scp->xsize : scp->ysize; if (lines0 > imax(sc_history_size, scp->ysize)) delta_ehs = lines0 - imax(sc_history_size, scp->ysize); else delta_ehs = 0; /* * syscons unconditionally allocates buffers upto SC_HISTORY_SIZE * lines or scp->ysize lines, whichever is larger. A value * greater than that is allowed, subject to extra_history_size. */ if (lines > imax(sc_history_size, scp->ysize)) if (lines - imax(sc_history_size, scp->ysize) > extra_history_size + delta_ehs) return EINVAL; if (cur_console->status & BUFFER_SAVED) return EBUSY; sc_alloc_history_buffer(scp, lines, delta_ehs, TRUE); return 0; } else return EINVAL; case CONS_MOUSECTL: /* control mouse arrow */ case OLD_CONS_MOUSECTL: { /* MOUSE_BUTTON?DOWN -> MOUSE_MSC_BUTTON?UP */ static int butmap[8] = { MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON2UP | MOUSE_MSC_BUTTON3UP, MOUSE_MSC_BUTTON2UP | MOUSE_MSC_BUTTON3UP, MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON3UP, MOUSE_MSC_BUTTON3UP, MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON2UP, MOUSE_MSC_BUTTON2UP, MOUSE_MSC_BUTTON1UP, 0, }; mouse_info_t *mouse = (mouse_info_t*)data; mouse_info_t buf; /* FIXME: */ if (!ISMOUSEAVAIL(scp->adp->va_flags)) return ENODEV; if (cmd == OLD_CONS_MOUSECTL) { static u_char swapb[] = { 0, 4, 2, 6, 1, 5, 3, 7 }; old_mouse_info_t *old_mouse = (old_mouse_info_t *)data; mouse = &buf; mouse->operation = old_mouse->operation; switch (mouse->operation) { case MOUSE_MODE: mouse->u.mode = old_mouse->u.mode; break; case MOUSE_SHOW: case MOUSE_HIDE: break; case MOUSE_MOVEABS: case MOUSE_MOVEREL: case MOUSE_ACTION: mouse->u.data.x = old_mouse->u.data.x; mouse->u.data.y = old_mouse->u.data.y; mouse->u.data.z = 0; mouse->u.data.buttons = swapb[old_mouse->u.data.buttons & 0x7]; break; case MOUSE_GETINFO: old_mouse->u.data.x = scp->mouse_xpos; old_mouse->u.data.y = scp->mouse_ypos; old_mouse->u.data.buttons = swapb[scp->mouse_buttons & 0x7]; break; default: return EINVAL; } } switch (mouse->operation) { case MOUSE_MODE: if (ISSIGVALID(mouse->u.mode.signal)) { scp->mouse_signal = mouse->u.mode.signal; scp->mouse_proc = p; scp->mouse_pid = p->p_pid; } else { scp->mouse_signal = 0; scp->mouse_proc = NULL; scp->mouse_pid = 0; } return 0; case MOUSE_SHOW: if (ISTEXTSC(scp) && !(scp->status & MOUSE_ENABLED)) { scp->status |= (MOUSE_ENABLED | MOUSE_VISIBLE); scp->mouse_oldpos = scp->mouse_pos; mark_all(scp); return 0; } else return EINVAL; break; case MOUSE_HIDE: if (ISTEXTSC(scp) && (scp->status & MOUSE_ENABLED)) { scp->status &= ~(MOUSE_ENABLED | MOUSE_VISIBLE); mark_all(scp); return 0; } else return EINVAL; break; case MOUSE_MOVEABS: scp->mouse_xpos = mouse->u.data.x; scp->mouse_ypos = mouse->u.data.y; set_mouse_pos(scp); break; case MOUSE_MOVEREL: scp->mouse_xpos += mouse->u.data.x; scp->mouse_ypos += mouse->u.data.y; set_mouse_pos(scp); break; case MOUSE_GETINFO: mouse->u.data.x = scp->mouse_xpos; mouse->u.data.y = scp->mouse_ypos; mouse->u.data.z = 0; mouse->u.data.buttons = scp->mouse_buttons; return 0; case MOUSE_ACTION: case MOUSE_MOTION_EVENT: /* this should maybe only be settable from /dev/consolectl SOS */ /* send out mouse event on /dev/sysmouse */ mouse_status.dx += mouse->u.data.x; mouse_status.dy += mouse->u.data.y; mouse_status.dz += mouse->u.data.z; if (mouse->operation == MOUSE_ACTION) mouse_status.button = mouse->u.data.buttons; mouse_status.flags |= ((mouse->u.data.x || mouse->u.data.y || mouse->u.data.z) ? MOUSE_POSCHANGED : 0) | (mouse_status.obutton ^ mouse_status.button); if (mouse_status.flags == 0) return 0; if (ISTEXTSC(cur_console) && (cur_console->status & MOUSE_ENABLED)) cur_console->status |= MOUSE_VISIBLE; if ((MOUSE_TTY)->t_state & TS_ISOPEN) { u_char buf[MOUSE_SYS_PACKETSIZE]; int j; /* the first five bytes are compatible with MouseSystems' */ buf[0] = MOUSE_MSC_SYNC | butmap[mouse_status.button & MOUSE_STDBUTTONS]; j = imax(imin(mouse->u.data.x, 255), -256); buf[1] = j >> 1; buf[3] = j - buf[1]; j = -imax(imin(mouse->u.data.y, 255), -256); buf[2] = j >> 1; buf[4] = j - buf[2]; for (j = 0; j < MOUSE_MSC_PACKETSIZE; j++) (*linesw[(MOUSE_TTY)->t_line].l_rint)(buf[j],MOUSE_TTY); if (mouse_level >= 1) { /* extended part */ j = imax(imin(mouse->u.data.z, 127), -128); buf[5] = (j >> 1) & 0x7f; buf[6] = (j - (j >> 1)) & 0x7f; /* buttons 4-10 */ buf[7] = (~mouse_status.button >> 3) & 0x7f; for (j = MOUSE_MSC_PACKETSIZE; j < MOUSE_SYS_PACKETSIZE; j++) (*linesw[(MOUSE_TTY)->t_line].l_rint)(buf[j],MOUSE_TTY); } } if (cur_console->mouse_signal) { cur_console->mouse_buttons = mouse->u.data.buttons; /* has controlling process died? */ if (cur_console->mouse_proc && (cur_console->mouse_proc != pfind(cur_console->mouse_pid))){ cur_console->mouse_signal = 0; cur_console->mouse_proc = NULL; cur_console->mouse_pid = 0; } else psignal(cur_console->mouse_proc, cur_console->mouse_signal); } else if (mouse->operation == MOUSE_ACTION && cut_buffer != NULL) { /* process button presses */ if ((cur_console->mouse_buttons ^ mouse->u.data.buttons) && ISTEXTSC(cur_console)) { cur_console->mouse_buttons = mouse->u.data.buttons; if (cur_console->mouse_buttons & MOUSE_BUTTON1DOWN) mouse_cut_start(cur_console); else mouse_cut_end(cur_console); if (cur_console->mouse_buttons & MOUSE_BUTTON2DOWN || cur_console->mouse_buttons & MOUSE_BUTTON3DOWN) mouse_paste(cur_console); } } if (mouse->u.data.x != 0 || mouse->u.data.y != 0) { cur_console->mouse_xpos += mouse->u.data.x; cur_console->mouse_ypos += mouse->u.data.y; set_mouse_pos(cur_console); } break; case MOUSE_BUTTON_EVENT: if ((mouse->u.event.id & MOUSE_BUTTONS) == 0) return EINVAL; if (mouse->u.event.value < 0) return EINVAL; if (mouse->u.event.value > 0) { cur_console->mouse_buttons |= mouse->u.event.id; mouse_status.button |= mouse->u.event.id; } else { cur_console->mouse_buttons &= ~mouse->u.event.id; mouse_status.button &= ~mouse->u.event.id; } mouse_status.flags |= mouse_status.obutton ^ mouse_status.button; if (mouse_status.flags == 0) return 0; if (ISTEXTSC(cur_console) && (cur_console->status & MOUSE_ENABLED)) cur_console->status |= MOUSE_VISIBLE; if ((MOUSE_TTY)->t_state & TS_ISOPEN) { u_char buf[8]; int i; buf[0] = MOUSE_MSC_SYNC | butmap[mouse_status.button & MOUSE_STDBUTTONS]; buf[7] = (~mouse_status.button >> 3) & 0x7f; buf[1] = buf[2] = buf[3] = buf[4] = buf[5] = buf[6] = 0; for (i = 0; i < ((mouse_level >= 1) ? MOUSE_SYS_PACKETSIZE : MOUSE_MSC_PACKETSIZE); i++) (*linesw[(MOUSE_TTY)->t_line].l_rint)(buf[i],MOUSE_TTY); } if (cur_console->mouse_signal) { if (cur_console->mouse_proc && (cur_console->mouse_proc != pfind(cur_console->mouse_pid))){ cur_console->mouse_signal = 0; cur_console->mouse_proc = NULL; cur_console->mouse_pid = 0; } else psignal(cur_console->mouse_proc, cur_console->mouse_signal); break; } if (!ISTEXTSC(cur_console) || (cut_buffer == NULL)) break; switch (mouse->u.event.id) { case MOUSE_BUTTON1DOWN: switch (mouse->u.event.value % 4) { case 0: /* up */ mouse_cut_end(cur_console); break; case 1: mouse_cut_start(cur_console); break; case 2: mouse_cut_word(cur_console); break; case 3: mouse_cut_line(cur_console); break; } break; case MOUSE_BUTTON2DOWN: switch (mouse->u.event.value) { case 0: /* up */ break; default: mouse_paste(cur_console); break; } break; case MOUSE_BUTTON3DOWN: switch (mouse->u.event.value) { case 0: /* up */ if (!(cur_console->mouse_buttons & MOUSE_BUTTON1DOWN)) mouse_cut_end(cur_console); break; default: mouse_cut_extend(cur_console); break; } break; } break; default: return EINVAL; } /* make screensaver happy */ sc_touch_scrn_saver(); return 0; } /* MOUSE_XXX: /dev/sysmouse ioctls */ case MOUSE_GETHWINFO: /* get device information */ { mousehw_t *hw = (mousehw_t *)data; if (tp != MOUSE_TTY) return ENOTTY; hw->buttons = 10; /* XXX unknown */ hw->iftype = MOUSE_IF_SYSMOUSE; hw->type = MOUSE_MOUSE; hw->model = MOUSE_MODEL_GENERIC; hw->hwid = 0; return 0; } case MOUSE_GETMODE: /* get protocol/mode */ { mousemode_t *mode = (mousemode_t *)data; if (tp != MOUSE_TTY) return ENOTTY; mode->level = mouse_level; switch (mode->level) { case 0: /* at this level, sysmouse emulates MouseSystems protocol */ mode->protocol = MOUSE_PROTO_MSC; mode->rate = -1; /* unknown */ mode->resolution = -1; /* unknown */ mode->accelfactor = 0; /* disabled */ mode->packetsize = MOUSE_MSC_PACKETSIZE; mode->syncmask[0] = MOUSE_MSC_SYNCMASK; mode->syncmask[1] = MOUSE_MSC_SYNC; break; case 1: /* at this level, sysmouse uses its own protocol */ mode->protocol = MOUSE_PROTO_SYSMOUSE; mode->rate = -1; mode->resolution = -1; mode->accelfactor = 0; mode->packetsize = MOUSE_SYS_PACKETSIZE; mode->syncmask[0] = MOUSE_SYS_SYNCMASK; mode->syncmask[1] = MOUSE_SYS_SYNC; break; } return 0; } case MOUSE_SETMODE: /* set protocol/mode */ { mousemode_t *mode = (mousemode_t *)data; if (tp != MOUSE_TTY) return ENOTTY; if ((mode->level < 0) || (mode->level > 1)) return EINVAL; mouse_level = mode->level; return 0; } case MOUSE_GETLEVEL: /* get operation level */ if (tp != MOUSE_TTY) return ENOTTY; *(int *)data = mouse_level; return 0; case MOUSE_SETLEVEL: /* set operation level */ if (tp != MOUSE_TTY) return ENOTTY; if ((*(int *)data < 0) || (*(int *)data > 1)) return EINVAL; mouse_level = *(int *)data; return 0; case MOUSE_GETSTATUS: /* get accumulated mouse events */ if (tp != MOUSE_TTY) return ENOTTY; s = spltty(); *(mousestatus_t *)data = mouse_status; mouse_status.flags = 0; mouse_status.obutton = mouse_status.button; mouse_status.dx = 0; mouse_status.dy = 0; mouse_status.dz = 0; splx(s); return 0; #if notyet case MOUSE_GETVARS: /* get internal mouse variables */ case MOUSE_SETVARS: /* set internal mouse variables */ if (tp != MOUSE_TTY) return ENOTTY; return ENODEV; #endif case MOUSE_READSTATE: /* read status from the device */ case MOUSE_READDATA: /* read data from the device */ if (tp != MOUSE_TTY) return ENOTTY; return ENODEV; case CONS_GETINFO: /* get current (virtual) console info */ { vid_info_t *ptr = (vid_info_t*)data; if (ptr->size == sizeof(struct vid_info)) { ptr->m_num = get_scr_num(); ptr->mv_col = scp->xpos; ptr->mv_row = scp->ypos; ptr->mv_csz = scp->xsize; ptr->mv_rsz = scp->ysize; ptr->mv_norm.fore = (scp->term.std_color & 0x0f00)>>8; ptr->mv_norm.back = (scp->term.std_color & 0xf000)>>12; ptr->mv_rev.fore = (scp->term.rev_color & 0x0f00)>>8; ptr->mv_rev.back = (scp->term.rev_color & 0xf000)>>12; ptr->mv_grfc.fore = 0; /* not supported */ ptr->mv_grfc.back = 0; /* not supported */ ptr->mv_ovscan = scp->border; if (scp == cur_console) save_kbd_state(scp); ptr->mk_keylock = scp->status & LOCK_MASK; return 0; } return EINVAL; } case CONS_GETVERS: /* get version number */ *(int*)data = 0x200; /* version 2.0 */ return 0; case CONS_IDLE: /* see if the screen has been idle */ /* * When the screen is in the GRAPHICS_MODE or UNKNOWN_MODE, * the user process may have been writing something on the * screen and syscons is not aware of it. Declare the screen * is NOT idle if it is in one of these modes. But there is * an exception to it; if a screen saver is running in the * graphics mode in the current screen, we should say that the * screen has been idle. */ *(int *)data = scrn_idle && (!ISGRAPHSC(cur_console) || (cur_console->status & SAVER_RUNNING)); return 0; case CONS_SAVERMODE: /* set saver mode */ switch(*(int *)data) { case CONS_USR_SAVER: /* if a LKM screen saver is running, stop it first. */ scsplash_stick(FALSE); saver_mode = *(int *)data; s = spltty(); if ((error = wait_scrn_saver_stop())) { splx(s); return error; } scp->status |= SAVER_RUNNING; scsplash_stick(TRUE); splx(s); break; case CONS_LKM_SAVER: s = spltty(); if ((saver_mode == CONS_USR_SAVER) && (scp->status & SAVER_RUNNING)) scp->status &= ~SAVER_RUNNING; saver_mode = *(int *)data; splx(s); break; default: return EINVAL; } return 0; case CONS_SAVERSTART: /* immediately start/stop the screen saver */ /* * Note that this ioctl does not guarantee the screen saver * actually starts or stops. It merely attempts to do so... */ s = spltty(); run_scrn_saver = (*(int *)data != 0); if (run_scrn_saver) scrn_time_stamp -= scrn_blank_time; splx(s); return 0; case VT_SETMODE: /* set screen switcher mode */ { struct vt_mode *mode; mode = (struct vt_mode *)data; if (ISSIGVALID(mode->relsig) && ISSIGVALID(mode->acqsig) && ISSIGVALID(mode->frsig)) { bcopy(data, &scp->smode, sizeof(struct vt_mode)); if (scp->smode.mode == VT_PROCESS) { scp->proc = p; scp->pid = scp->proc->p_pid; } return 0; } else return EINVAL; } case VT_GETMODE: /* get screen switcher mode */ bcopy(&scp->smode, data, sizeof(struct vt_mode)); return 0; case VT_RELDISP: /* screen switcher ioctl */ switch(*(int *)data) { case VT_FALSE: /* user refuses to release screen, abort */ if (scp == old_scp && (scp->status & SWITCH_WAIT_REL)) { old_scp->status &= ~SWITCH_WAIT_REL; switch_in_progress = FALSE; return 0; } return EINVAL; case VT_TRUE: /* user has released screen, go on */ if (scp == old_scp && (scp->status & SWITCH_WAIT_REL)) { scp->status &= ~SWITCH_WAIT_REL; exchange_scr(); if (new_scp->smode.mode == VT_PROCESS) { new_scp->status |= SWITCH_WAIT_ACQ; psignal(new_scp->proc, new_scp->smode.acqsig); } else switch_in_progress = FALSE; return 0; } return EINVAL; case VT_ACKACQ: /* acquire acknowledged, switch completed */ if (scp == new_scp && (scp->status & SWITCH_WAIT_ACQ)) { scp->status &= ~SWITCH_WAIT_ACQ; switch_in_progress = FALSE; return 0; } return EINVAL; default: return EINVAL; } /* NOT REACHED */ case VT_OPENQRY: /* return free virtual console */ for (i = 0; i < MAXCONS; i++) { tp = VIRTUAL_TTY(i); if (!(tp->t_state & TS_ISOPEN)) { *(int *)data = i + 1; return 0; } } return EINVAL; case VT_ACTIVATE: /* switch to screen *data */ s = spltty(); sc_clean_up(cur_console); splx(s); return switch_scr(scp, *(int *)data - 1); case VT_WAITACTIVE: /* wait for switch to occur */ if (*(int *)data > MAXCONS || *(int *)data < 0) return EINVAL; s = spltty(); error = sc_clean_up(cur_console); splx(s); if (error) return error; if (minor(dev) == *(int *)data - 1) return 0; if (*(int *)data == 0) { if (scp == cur_console) return 0; } else scp = console[*(int *)data - 1]; while ((error=tsleep((caddr_t)&scp->smode, PZERO|PCATCH, "waitvt", 0)) == ERESTART) ; return error; case VT_GETACTIVE: *(int *)data = get_scr_num()+1; return 0; case KDENABIO: /* allow io operations */ error = suser(p->p_ucred, &p->p_acflag); if (error != 0) return error; if (securelevel > 0) return EPERM; #ifdef __i386__ p->p_md.md_regs->tf_eflags |= PSL_IOPL; #endif return 0; case KDDISABIO: /* disallow io operations (default) */ #ifdef __i386__ p->p_md.md_regs->tf_eflags &= ~PSL_IOPL; #endif return 0; case KDSKBSTATE: /* set keyboard state (locks) */ if (*(int *)data & ~LOCK_MASK) return EINVAL; scp->status &= ~LOCK_MASK; scp->status |= *(int *)data; if (scp == cur_console) update_kbd_state(scp->status, LOCK_MASK); return 0; case KDGKBSTATE: /* get keyboard state (locks) */ if (scp == cur_console) save_kbd_state(scp); *(int *)data = scp->status & LOCK_MASK; return 0; case KDSETREPEAT: /* set keyboard repeat & delay rates (new) */ error = kbd_ioctl(kbd, cmd, data); if (error == ENOIOCTL) error = ENODEV; return error; case KDSETRAD: /* set keyboard repeat & delay rates (old) */ if (*(int *)data & ~0x7f) return EINVAL; error = kbd_ioctl(kbd, cmd, data); if (error == ENOIOCTL) error = ENODEV; return error; case KDSKBMODE: /* set keyboard mode */ switch (*(int *)data) { case K_XLATE: /* switch to XLT ascii mode */ case K_RAW: /* switch to RAW scancode mode */ case K_CODE: /* switch to CODE mode */ scp->kbd_mode = *(int *)data; if (scp == cur_console) kbd_ioctl(kbd, cmd, data); return 0; default: return EINVAL; } /* NOT REACHED */ case KDGKBMODE: /* get keyboard mode */ *(int *)data = scp->kbd_mode; return 0; case KDGKBINFO: error = kbd_ioctl(kbd, cmd, data); if (error == ENOIOCTL) error = ENODEV; return error; case KDMKTONE: /* sound the bell */ if (*(int*)data) do_bell(scp, (*(int*)data)&0xffff, (((*(int*)data)>>16)&0xffff)*hz/1000); else do_bell(scp, scp->bell_pitch, scp->bell_duration); return 0; case KIOCSOUND: /* make tone (*data) hz */ #ifdef __i386__ if (scp == cur_console) { if (*(int*)data) { int pitch = timer_freq / *(int*)data; /* set command for counter 2, 2 byte write */ if (acquire_timer2(TIMER_16BIT|TIMER_SQWAVE)) return EBUSY; /* set pitch */ outb(TIMER_CNTR2, pitch); outb(TIMER_CNTR2, (pitch>>8)); /* enable counter 2 output to speaker */ outb(IO_PPI, inb(IO_PPI) | 3); } else { /* disable counter 2 output to speaker */ outb(IO_PPI, inb(IO_PPI) & 0xFC); release_timer2(); } } #endif /* __i386__ */ return 0; case KDGKBTYPE: /* get keyboard type */ error = kbd_ioctl(kbd, cmd, data); if (error == ENOIOCTL) { /* always return something? XXX */ *(int *)data = 0; } return 0; case KDSETLED: /* set keyboard LED status */ if (*(int *)data & ~LED_MASK) /* FIXME: LOCK_MASK? */ return EINVAL; scp->status &= ~LED_MASK; scp->status |= *(int *)data; if (scp == cur_console) update_kbd_leds(scp->status); return 0; case KDGETLED: /* get keyboard LED status */ if (scp == cur_console) save_kbd_state(scp); *(int *)data = scp->status & LED_MASK; return 0; case CONS_SETKBD: /* set the new keyboard */ { keyboard_t *newkbd; s = spltty(); newkbd = kbd_get_keyboard(*(int *)data); if (newkbd == NULL) { splx(s); return EINVAL; } error = 0; if (kbd != newkbd) { i = kbd_allocate(newkbd->kb_name, newkbd->kb_unit, (void *)&keyboard, sckbdevent, NULL); /* i == newkbd->kb_index */ if (i >= 0) { if (kbd != NULL) { save_kbd_state(cur_console); kbd_release(kbd, (void *)&keyboard); } kbd = kbd_get_keyboard(i); /* kbd == newkbd */ keyboard = i; kbd_ioctl(kbd, KDSKBMODE, (caddr_t)&cur_console->kbd_mode); update_kbd_state(cur_console->status, LOCK_MASK); } else { error = EPERM; /* XXX */ } } splx(s); return error; } case CONS_RELKBD: /* release the current keyboard */ s = spltty(); error = 0; if (kbd != NULL) { save_kbd_state(cur_console); error = kbd_release(kbd, (void *)&keyboard); if (error == 0) { kbd = NULL; keyboard = -1; } } splx(s); return error; case GIO_SCRNMAP: /* get output translation table */ bcopy(&scr_map, data, sizeof(scr_map)); return 0; case PIO_SCRNMAP: /* set output translation table */ bcopy(data, &scr_map, sizeof(scr_map)); for (i=0; iadp->va_flags)) return ENXIO; bcopy(data, font_8, 8*256); fonts_loaded |= FONT_8; /* * FONT KLUDGE * Always use the font page #0. XXX * Don't load if the current font size is not 8x8. */ if (ISTEXTSC(cur_console) && (cur_console->font_size < 14)) copy_font(cur_console, LOAD, 8, font_8); return 0; case GIO_FONT8x8: /* get 8x8 dot font */ if (!ISFONTAVAIL(scp->adp->va_flags)) return ENXIO; if (fonts_loaded & FONT_8) { bcopy(font_8, data, 8*256); return 0; } else return ENXIO; case PIO_FONT8x14: /* set 8x14 dot font */ if (!ISFONTAVAIL(scp->adp->va_flags)) return ENXIO; bcopy(data, font_14, 14*256); fonts_loaded |= FONT_14; /* * FONT KLUDGE * Always use the font page #0. XXX * Don't load if the current font size is not 8x14. */ if (ISTEXTSC(cur_console) && (cur_console->font_size >= 14) && (cur_console->font_size < 16)) copy_font(cur_console, LOAD, 14, font_14); return 0; case GIO_FONT8x14: /* get 8x14 dot font */ if (!ISFONTAVAIL(scp->adp->va_flags)) return ENXIO; if (fonts_loaded & FONT_14) { bcopy(font_14, data, 14*256); return 0; } else return ENXIO; case PIO_FONT8x16: /* set 8x16 dot font */ if (!ISFONTAVAIL(scp->adp->va_flags)) return ENXIO; bcopy(data, font_16, 16*256); fonts_loaded |= FONT_16; /* * FONT KLUDGE * Always use the font page #0. XXX * Don't load if the current font size is not 8x16. */ if (ISTEXTSC(cur_console) && (cur_console->font_size >= 16)) copy_font(cur_console, LOAD, 16, font_16); return 0; case GIO_FONT8x16: /* get 8x16 dot font */ if (!ISFONTAVAIL(scp->adp->va_flags)) return ENXIO; if (fonts_loaded & FONT_16) { bcopy(font_16, data, 16*256); return 0; } else return ENXIO; default: break; } error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); if (error != ENOIOCTL) return(error); error = ttioctl(tp, cmd, data, flag); if (error != ENOIOCTL) return(error); return(ENOTTY); } static void scstart(struct tty *tp) { struct clist *rbp; int s, len; u_char buf[PCBURST]; scr_stat *scp = sc_get_scr_stat(tp->t_dev); if (scp->status & SLKED || blink_in_progress) return; /* XXX who repeats the call when the above flags are cleared? */ s = spltty(); if (!(tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP))) { tp->t_state |= TS_BUSY; rbp = &tp->t_outq; while (rbp->c_cc) { len = q_to_b(rbp, buf, PCBURST); splx(s); ansi_put(scp, buf, len); s = spltty(); } tp->t_state &= ~TS_BUSY; ttwwakeup(tp); } splx(s); } static void scmousestart(struct tty *tp) { struct clist *rbp; int s; u_char buf[PCBURST]; s = spltty(); if (!(tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP))) { tp->t_state |= TS_BUSY; rbp = &tp->t_outq; while (rbp->c_cc) { q_to_b(rbp, buf, PCBURST); } tp->t_state &= ~TS_BUSY; ttwwakeup(tp); } splx(s); } #if __i386__ /* XXX kludge! */ extern struct isa_driver scdriver; static void sccnprobe(struct consdev *cp) { +#if 0 struct isa_device *dvp; /* * Take control if we are the highest priority enabled display device. */ dvp = find_display(); if (dvp == NULL || dvp->id_driver != &scdriver) { cp->cn_pri = CN_DEAD; return; } if (!scvidprobe(dvp->id_unit, dvp->id_flags, TRUE)) { cp->cn_pri = CN_DEAD; return; } sckbdprobe(dvp->id_unit, dvp->id_flags, TRUE); +#else + if (!scvidprobe(0, 0, TRUE)) { + cp->cn_pri = CN_DEAD; + return; + } + sckbdprobe(0, 0, TRUE); +#endif /* initialize required fields */ cp->cn_dev = makedev(CDEV_MAJOR, SC_CONSOLE); cp->cn_pri = CN_INTERNAL; } static void sccninit(struct consdev *cp) { scinit(); } #else /* !__i386__ */ extern struct consdev *cn_tab; void sccnattach(void) { if (!scvidprobe(0, 0, TRUE) || !sckbdprobe(0, 0, TRUE)) { return; } scinit(); cn_tab = &sc_cons; } #endif /* __i386__ */ static void sccnputc(dev_t dev, int c) { u_char buf[1]; scr_stat *scp = console[0]; term_stat save = scp->term; u_short *p; int s; int i; if (scp == cur_console && scp->status & SLKED) { scp->status &= ~SLKED; update_kbd_state(scp->status, SLKED); if (cur_console->status & BUFFER_SAVED) { p = cur_console->history_save; for (i = 0; i < cur_console->ysize; ++i) { bcopy(p, cur_console->scr_buf + (cur_console->xsize*i), cur_console->xsize*sizeof(u_short)); p += cur_console->xsize; if (p + cur_console->xsize > cur_console->history + cur_console->history_size) p = cur_console->history; } cur_console->status &= ~BUFFER_SAVED; cur_console->history_head = cur_console->history_save; cur_console->status |= CURSOR_ENABLED; mark_all(cur_console); } #if 1 /* XXX */ scstart(VIRTUAL_TTY(get_scr_num())); #endif } scp->term = kernel_console; current_default = &kernel_default; if (scp == cur_console && !ISGRAPHSC(scp)) remove_cursor_image(scp); buf[0] = c; ansi_put(scp, buf, 1); kernel_console = scp->term; current_default = &user_default; scp->term = save; s = spltty(); /* block sckbdevent and scrn_timer */ sccnupdate(scp); splx(s); } static int sccngetc(dev_t dev) { return sccngetch(0); } static int sccncheckc(dev_t dev) { return sccngetch(SCGETC_NONBLOCK); } static int sccngetch(int flags) { int cur_mode; int s = spltty(); /* block sckbdevent and scrn_timer while we poll */ int c; /* * Stop the screen saver and update the screen if necessary. * What if we have been running in the screen saver code... XXX */ sc_touch_scrn_saver(); sccnupdate(cur_console); if (kbd == NULL) { splx(s); return -1; } /* * Make sure the keyboard is accessible even when the kbd device * driver is disabled. */ kbd_enable(kbd); /* we shall always use the keyboard in the XLATE mode here */ cur_mode = cur_console->kbd_mode; cur_console->kbd_mode = K_XLATE; kbd_ioctl(kbd, KDSKBMODE, (caddr_t)&cur_console->kbd_mode); kbd_poll(kbd, TRUE); c = scgetc(kbd, SCGETC_CN | flags); kbd_poll(kbd, FALSE); cur_console->kbd_mode = cur_mode; kbd_ioctl(kbd, KDSKBMODE, (caddr_t)&cur_console->kbd_mode); kbd_disable(kbd); splx(s); switch (KEYFLAGS(c)) { case 0: /* normal char */ return KEYCHAR(c); case FKEY: /* function key */ return c; /* XXX */ case NOKEY: case ERRKEY: default: return -1; } /* NOT REACHED */ } static void sccnupdate(scr_stat *scp) { /* this is a cut-down version of scrn_timer()... */ if (font_loading_in_progress) return; if (panicstr || shutdown_in_progress) { sc_touch_scrn_saver(); } else if (scp != cur_console) { return; } if (!run_scrn_saver) scrn_idle = FALSE; if ((saver_mode != CONS_LKM_SAVER) || !scrn_idle) if (scrn_blanked) stop_scrn_saver(current_saver); if (scp != cur_console || blink_in_progress || switch_in_progress) return; if (!ISGRAPHSC(scp) && !(scp->status & SAVER_RUNNING)) scrn_update(scp, TRUE); } scr_stat *sc_get_scr_stat(dev_t dev) { int unit = minor(dev); if (unit == SC_CONSOLE) return console[0]; if (unit >= MAXCONS || unit < 0) return(NULL); return console[unit]; } static int get_scr_num() { int i = 0; while ((i < MAXCONS) && (cur_console != console[i])) i++; return i < MAXCONS ? i : 0; } static void scrn_timer(void *arg) { static int kbd_interval = 0; struct timeval tv; scr_stat *scp; int s; /* don't do anything when we are touching font */ if (font_loading_in_progress) { if (arg) timeout(scrn_timer, (void *)TRUE, hz / 10); return; } s = spltty(); if ((kbd == NULL) && (sc_flags & AUTODETECT_KBD)) { /* try to allocate a keyboard automatically */ if (++kbd_interval >= 25) { keyboard = kbd_allocate("*", -1, (void *)&keyboard, sckbdevent, NULL); if (keyboard >= 0) { kbd = kbd_get_keyboard(keyboard); kbd_ioctl(kbd, KDSKBMODE, (caddr_t)&cur_console->kbd_mode); update_kbd_state(cur_console->status, LOCK_MASK); } kbd_interval = 0; } } /* should we stop the screen saver? */ getmicrouptime(&tv); if (panicstr || shutdown_in_progress) sc_touch_scrn_saver(); if (run_scrn_saver) { scrn_idle = (tv.tv_sec > scrn_time_stamp + scrn_blank_time); } else { scrn_time_stamp = tv.tv_sec; scrn_idle = FALSE; if (scrn_blank_time > 0) run_scrn_saver = TRUE; } if ((saver_mode != CONS_LKM_SAVER) || !scrn_idle) if (scrn_blanked) stop_scrn_saver(current_saver); /* should we just return ? */ if (blink_in_progress || switch_in_progress) { if (arg) timeout(scrn_timer, (void *)TRUE, hz / 10); splx(s); return; } /* Update the screen */ scp = cur_console; if (!ISGRAPHSC(scp) && !(scp->status & SAVER_RUNNING)) scrn_update(scp, TRUE); /* should we activate the screen saver? */ if ((saver_mode == CONS_LKM_SAVER) && scrn_idle) if (!ISGRAPHSC(scp) || scrn_blanked) (*current_saver)(TRUE); if (arg) timeout(scrn_timer, (void *)TRUE, hz / 25); splx(s); } static void scrn_update(scr_stat *scp, int show_cursor) { /* update screen image */ if (scp->start <= scp->end) sc_bcopy(scp, scp->scr_buf, scp->start, scp->end, 0); /* we are not to show the cursor and the mouse pointer... */ if (!show_cursor) { scp->end = 0; scp->start = scp->xsize*scp->ysize - 1; return; } /* update "pseudo" mouse pointer image */ if (scp->status & MOUSE_VISIBLE) { /* did mouse move since last time ? */ if (scp->status & MOUSE_MOVED) { /* do we need to remove old mouse pointer image ? */ if (scp->mouse_cut_start != NULL || (scp->mouse_pos-scp->scr_buf) <= scp->start || (scp->mouse_pos+scp->xsize + 1 - scp->scr_buf) >= scp->end) { remove_mouse_image(scp); } scp->status &= ~MOUSE_MOVED; draw_mouse_image(scp); } else { /* mouse didn't move, has it been overwritten ? */ if ((scp->mouse_pos+scp->xsize + 1 - scp->scr_buf) >= scp->start && (scp->mouse_pos - scp->scr_buf) <= scp->end) { draw_mouse_image(scp); } } } /* update cursor image */ if (scp->status & CURSOR_ENABLED) { /* did cursor move since last time ? */ if (scp->cursor_pos != scp->cursor_oldpos) { /* do we need to remove old cursor image ? */ if ((scp->cursor_oldpos - scp->scr_buf) < scp->start || ((scp->cursor_oldpos - scp->scr_buf) > scp->end)) { remove_cursor_image(scp); } scp->cursor_oldpos = scp->cursor_pos; draw_cursor_image(scp); } else { /* cursor didn't move, has it been overwritten ? */ if (scp->cursor_pos - scp->scr_buf >= scp->start && scp->cursor_pos - scp->scr_buf <= scp->end) { draw_cursor_image(scp); } else { /* if its a blinking cursor, we may have to update it */ if (sc_flags & BLINK_CURSOR) draw_cursor_image(scp); } } blinkrate++; } if (scp->mouse_cut_start != NULL) draw_cutmarking(scp); scp->end = 0; scp->start = scp->xsize*scp->ysize - 1; } #if NSPLASH > 0 static int scsplash_callback(int event) { int error; switch (event) { case SPLASH_INIT: scrn_saver_failed = FALSE; if (add_scrn_saver(scsplash_saver) == 0) { run_scrn_saver = TRUE; if (cold && !(boothowto & (RB_VERBOSE | RB_CONFIG))) { scsplash_stick(TRUE); (*current_saver)(TRUE); } } return 0; case SPLASH_TERM: if (current_saver == scsplash_saver) { scsplash_stick(FALSE); error = remove_scrn_saver(scsplash_saver); if (error) return error; } return 0; default: return EINVAL; } } static void scsplash_saver(int show) { static int busy = FALSE; scr_stat *scp; if (busy) return; busy = TRUE; scp = cur_console; if (show) { if (!scrn_saver_failed) { if (!scrn_blanked) set_scrn_saver_mode(scp, -1, NULL, 0); switch (splash(scp->adp, TRUE)) { case 0: /* succeeded */ scrn_blanked = TRUE; break; case EAGAIN: /* try later */ restore_scrn_saver_mode(scp, FALSE); break; default: scrn_saver_failed = TRUE; scsplash_stick(FALSE); printf("scsplash_saver(): failed to put up the image\n"); restore_scrn_saver_mode(scp, TRUE); break; } } } else if (!sticky_splash) { if (scrn_blanked && (splash(scp->adp, FALSE) == 0)) { restore_scrn_saver_mode(scp, TRUE); scrn_blanked = FALSE; } } busy = FALSE; } static int add_scrn_saver(void (*this_saver)(int)) { int error; if (current_saver != none_saver) { error = remove_scrn_saver(current_saver); if (error) return error; } run_scrn_saver = FALSE; saver_mode = CONS_LKM_SAVER; current_saver = this_saver; return 0; } static int remove_scrn_saver(void (*this_saver)(int)) { if (current_saver != this_saver) return EINVAL; /* * In order to prevent `current_saver' from being called by * the timeout routine `scrn_timer()' while we manipulate * the saver list, we shall set `current_saver' to `none_saver' * before stopping the current saver, rather than blocking by `splXX()'. */ current_saver = none_saver; if (scrn_blanked) stop_scrn_saver(this_saver); return (scrn_blanked ? EBUSY : 0); } static int set_scrn_saver_mode(scr_stat *scp, int mode, u_char *pal, int border) { int s; /* assert(scp == cur_console) */ s = spltty(); scp->splash_save_mode = scp->mode; scp->splash_save_status = scp->status & (GRAPHICS_MODE | PIXEL_MODE); scp->status &= ~(GRAPHICS_MODE | PIXEL_MODE); scp->status |= (UNKNOWN_MODE | SAVER_RUNNING); splx(s); if (mode < 0) return 0; scp->mode = mode; if (set_mode(scp) == 0) { if (scp->adp->va_info.vi_flags & V_INFO_GRAPHICS) scp->status |= GRAPHICS_MODE; if (pal != NULL) load_palette(scp->adp, pal); set_border(scp, border); return 0; } else { s = spltty(); scp->mode = scp->splash_save_mode; scp->status &= ~(UNKNOWN_MODE | SAVER_RUNNING); scp->status |= scp->splash_save_status; splx(s); return 1; } } static int restore_scrn_saver_mode(scr_stat *scp, int changemode) { int mode; int status; int s; /* assert(scp == cur_console) */ s = spltty(); mode = scp->mode; status = scp->status; scp->mode = scp->splash_save_mode; scp->status &= ~(UNKNOWN_MODE | SAVER_RUNNING); scp->status |= scp->splash_save_status; if (!changemode) { splx(s); return 0; } if (set_mode(scp) == 0) { load_palette(scp->adp, palette); splx(s); return 0; } else { scp->mode = mode; scp->status = status; splx(s); return 1; } } static void stop_scrn_saver(void (*saver)(int)) { (*saver)(FALSE); run_scrn_saver = FALSE; /* the screen saver may have chosen not to stop after all... */ if (scrn_blanked) return; mark_all(cur_console); if (delayed_next_scr) switch_scr(cur_console, delayed_next_scr - 1); wakeup((caddr_t)&scrn_blanked); } static int wait_scrn_saver_stop(void) { int error = 0; while (scrn_blanked) { run_scrn_saver = FALSE; error = tsleep((caddr_t)&scrn_blanked, PZERO | PCATCH, "scrsav", 0); run_scrn_saver = FALSE; if (error != ERESTART) break; } return error; } #endif /* NSPLASH */ void sc_touch_scrn_saver(void) { scsplash_stick(FALSE); run_scrn_saver = FALSE; } void sc_clear_screen(scr_stat *scp) { move_crsr(scp, 0, 0); scp->cursor_oldpos = scp->cursor_pos; fillw(scp->term.cur_color | scr_map[0x20], scp->scr_buf, scp->xsize * scp->ysize); mark_all(scp); remove_cutmarking(scp); } static int switch_scr(scr_stat *scp, u_int next_scr) { /* delay switch if actively updating screen */ if (scrn_blanked || write_in_progress || blink_in_progress) { delayed_next_scr = next_scr+1; sc_touch_scrn_saver(); return 0; } if (switch_in_progress && (cur_console->proc != pfind(cur_console->pid))) switch_in_progress = FALSE; if (next_scr >= MAXCONS || switch_in_progress || (cur_console->smode.mode == VT_AUTO && ISGRAPHSC(cur_console))) { do_bell(scp, BELL_PITCH, BELL_DURATION); return EINVAL; } /* is the wanted virtual console open ? */ if (next_scr) { struct tty *tp = VIRTUAL_TTY(next_scr); if (!(tp->t_state & TS_ISOPEN)) { do_bell(scp, BELL_PITCH, BELL_DURATION); return EINVAL; } } switch_in_progress = TRUE; old_scp = cur_console; new_scp = console[next_scr]; wakeup((caddr_t)&new_scp->smode); if (new_scp == old_scp) { switch_in_progress = FALSE; delayed_next_scr = FALSE; return 0; } /* has controlling process died? */ if (old_scp->proc && (old_scp->proc != pfind(old_scp->pid))) old_scp->smode.mode = VT_AUTO; if (new_scp->proc && (new_scp->proc != pfind(new_scp->pid))) new_scp->smode.mode = VT_AUTO; /* check the modes and switch appropriately */ if (old_scp->smode.mode == VT_PROCESS) { old_scp->status |= SWITCH_WAIT_REL; psignal(old_scp->proc, old_scp->smode.relsig); } else { exchange_scr(); if (new_scp->smode.mode == VT_PROCESS) { new_scp->status |= SWITCH_WAIT_ACQ; psignal(new_scp->proc, new_scp->smode.acqsig); } else switch_in_progress = FALSE; } return 0; } static void exchange_scr(void) { /* save the current state of video and keyboard */ move_crsr(old_scp, old_scp->xpos, old_scp->ypos); if (old_scp->kbd_mode == K_XLATE) save_kbd_state(old_scp); /* set up the video for the new screen */ cur_console = new_scp; if (old_scp->mode != new_scp->mode || ISUNKNOWNSC(old_scp)) set_mode(new_scp); move_crsr(new_scp, new_scp->xpos, new_scp->ypos); if (ISTEXTSC(new_scp) && (sc_flags & CHAR_CURSOR)) set_destructive_cursor(new_scp); if (ISGRAPHSC(old_scp)) load_palette(new_scp->adp, palette); set_border(new_scp, new_scp->border); /* set up the keyboard for the new screen */ if (old_scp->kbd_mode != new_scp->kbd_mode) kbd_ioctl(kbd, KDSKBMODE, (caddr_t)&new_scp->kbd_mode); update_kbd_state(new_scp->status, LOCK_MASK); delayed_next_scr = FALSE; mark_all(new_scp); } static void scan_esc(scr_stat *scp, u_char c) { static u_char ansi_col[16] = {0, 4, 2, 6, 1, 5, 3, 7, 8, 12, 10, 14, 9, 13, 11, 15}; int i, n; u_short *src, *dst, count; if (scp->term.esc == 1) { /* seen ESC */ switch (c) { case '7': /* Save cursor position */ scp->saved_xpos = scp->xpos; scp->saved_ypos = scp->ypos; break; case '8': /* Restore saved cursor position */ if (scp->saved_xpos >= 0 && scp->saved_ypos >= 0) move_crsr(scp, scp->saved_xpos, scp->saved_ypos); break; case '[': /* Start ESC [ sequence */ scp->term.esc = 2; scp->term.last_param = -1; for (i = scp->term.num_param; i < MAX_ESC_PAR; i++) scp->term.param[i] = 1; scp->term.num_param = 0; return; case 'M': /* Move cursor up 1 line, scroll if at top */ if (scp->ypos > 0) move_crsr(scp, scp->xpos, scp->ypos - 1); else { bcopy(scp->scr_buf, scp->scr_buf + scp->xsize, (scp->ysize - 1) * scp->xsize * sizeof(u_short)); fillw(scp->term.cur_color | scr_map[0x20], scp->scr_buf, scp->xsize); mark_all(scp); } break; #if notyet case 'Q': scp->term.esc = 4; return; #endif case 'c': /* Clear screen & home */ sc_clear_screen(scp); break; case '(': /* iso-2022: designate 94 character set to G0 */ scp->term.esc = 5; return; } } else if (scp->term.esc == 2) { /* seen ESC [ */ if (c >= '0' && c <= '9') { if (scp->term.num_param < MAX_ESC_PAR) { if (scp->term.last_param != scp->term.num_param) { scp->term.last_param = scp->term.num_param; scp->term.param[scp->term.num_param] = 0; } else scp->term.param[scp->term.num_param] *= 10; scp->term.param[scp->term.num_param] += c - '0'; return; } } scp->term.num_param = scp->term.last_param + 1; switch (c) { case ';': if (scp->term.num_param < MAX_ESC_PAR) return; break; case '=': scp->term.esc = 3; scp->term.last_param = -1; for (i = scp->term.num_param; i < MAX_ESC_PAR; i++) scp->term.param[i] = 1; scp->term.num_param = 0; return; case 'A': /* up n rows */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, scp->xpos, scp->ypos - n); break; case 'B': /* down n rows */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, scp->xpos, scp->ypos + n); break; case 'C': /* right n columns */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, scp->xpos + n, scp->ypos); break; case 'D': /* left n columns */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, scp->xpos - n, scp->ypos); break; case 'E': /* cursor to start of line n lines down */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, 0, scp->ypos + n); break; case 'F': /* cursor to start of line n lines up */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, 0, scp->ypos - n); break; case 'f': /* Cursor move */ case 'H': if (scp->term.num_param == 0) move_crsr(scp, 0, 0); else if (scp->term.num_param == 2) move_crsr(scp, scp->term.param[1] - 1, scp->term.param[0] - 1); break; case 'J': /* Clear all or part of display */ if (scp->term.num_param == 0) n = 0; else n = scp->term.param[0]; switch (n) { case 0: /* clear form cursor to end of display */ fillw(scp->term.cur_color | scr_map[0x20], scp->cursor_pos, scp->scr_buf + scp->xsize * scp->ysize - scp->cursor_pos); mark_for_update(scp, scp->cursor_pos - scp->scr_buf); mark_for_update(scp, scp->xsize * scp->ysize - 1); remove_cutmarking(scp); break; case 1: /* clear from beginning of display to cursor */ fillw(scp->term.cur_color | scr_map[0x20], scp->scr_buf, scp->cursor_pos - scp->scr_buf); mark_for_update(scp, 0); mark_for_update(scp, scp->cursor_pos - scp->scr_buf); remove_cutmarking(scp); break; case 2: /* clear entire display */ fillw(scp->term.cur_color | scr_map[0x20], scp->scr_buf, scp->xsize * scp->ysize); mark_all(scp); remove_cutmarking(scp); break; } break; case 'K': /* Clear all or part of line */ if (scp->term.num_param == 0) n = 0; else n = scp->term.param[0]; switch (n) { case 0: /* clear form cursor to end of line */ fillw(scp->term.cur_color | scr_map[0x20], scp->cursor_pos, scp->xsize - scp->xpos); mark_for_update(scp, scp->cursor_pos - scp->scr_buf); mark_for_update(scp, scp->cursor_pos - scp->scr_buf + scp->xsize - 1 - scp->xpos); break; case 1: /* clear from beginning of line to cursor */ fillw(scp->term.cur_color | scr_map[0x20], scp->cursor_pos - scp->xpos, scp->xpos + 1); mark_for_update(scp, scp->ypos * scp->xsize); mark_for_update(scp, scp->cursor_pos - scp->scr_buf); break; case 2: /* clear entire line */ fillw(scp->term.cur_color | scr_map[0x20], scp->cursor_pos - scp->xpos, scp->xsize); mark_for_update(scp, scp->ypos * scp->xsize); mark_for_update(scp, (scp->ypos + 1) * scp->xsize - 1); break; } break; case 'L': /* Insert n lines */ n = scp->term.param[0]; if (n < 1) n = 1; if (n > scp->ysize - scp->ypos) n = scp->ysize - scp->ypos; src = scp->scr_buf + scp->ypos * scp->xsize; dst = src + n * scp->xsize; count = scp->ysize - (scp->ypos + n); bcopy(src, dst, count * scp->xsize * sizeof(u_short)); fillw(scp->term.cur_color | scr_map[0x20], src, n * scp->xsize); mark_for_update(scp, scp->ypos * scp->xsize); mark_for_update(scp, scp->xsize * scp->ysize - 1); break; case 'M': /* Delete n lines */ n = scp->term.param[0]; if (n < 1) n = 1; if (n > scp->ysize - scp->ypos) n = scp->ysize - scp->ypos; dst = scp->scr_buf + scp->ypos * scp->xsize; src = dst + n * scp->xsize; count = scp->ysize - (scp->ypos + n); bcopy(src, dst, count * scp->xsize * sizeof(u_short)); src = dst + count * scp->xsize; fillw(scp->term.cur_color | scr_map[0x20], src, n * scp->xsize); mark_for_update(scp, scp->ypos * scp->xsize); mark_for_update(scp, scp->xsize * scp->ysize - 1); break; case 'P': /* Delete n chars */ n = scp->term.param[0]; if (n < 1) n = 1; if (n > scp->xsize - scp->xpos) n = scp->xsize - scp->xpos; dst = scp->cursor_pos; src = dst + n; count = scp->xsize - (scp->xpos + n); bcopy(src, dst, count * sizeof(u_short)); src = dst + count; fillw(scp->term.cur_color | scr_map[0x20], src, n); mark_for_update(scp, scp->cursor_pos - scp->scr_buf); mark_for_update(scp, scp->cursor_pos - scp->scr_buf + n + count - 1); break; case '@': /* Insert n chars */ n = scp->term.param[0]; if (n < 1) n = 1; if (n > scp->xsize - scp->xpos) n = scp->xsize - scp->xpos; src = scp->cursor_pos; dst = src + n; count = scp->xsize - (scp->xpos + n); bcopy(src, dst, count * sizeof(u_short)); fillw(scp->term.cur_color | scr_map[0x20], src, n); mark_for_update(scp, scp->cursor_pos - scp->scr_buf); mark_for_update(scp, scp->cursor_pos - scp->scr_buf + n + count - 1); break; case 'S': /* scroll up n lines */ n = scp->term.param[0]; if (n < 1) n = 1; if (n > scp->ysize) n = scp->ysize; bcopy(scp->scr_buf + (scp->xsize * n), scp->scr_buf, scp->xsize * (scp->ysize - n) * sizeof(u_short)); fillw(scp->term.cur_color | scr_map[0x20], scp->scr_buf + scp->xsize * (scp->ysize - n), scp->xsize * n); mark_all(scp); break; case 'T': /* scroll down n lines */ n = scp->term.param[0]; if (n < 1) n = 1; if (n > scp->ysize) n = scp->ysize; bcopy(scp->scr_buf, scp->scr_buf + (scp->xsize * n), scp->xsize * (scp->ysize - n) * sizeof(u_short)); fillw(scp->term.cur_color | scr_map[0x20], scp->scr_buf, scp->xsize * n); mark_all(scp); break; case 'X': /* erase n characters in line */ n = scp->term.param[0]; if (n < 1) n = 1; if (n > scp->xsize - scp->xpos) n = scp->xsize - scp->xpos; fillw(scp->term.cur_color | scr_map[0x20], scp->cursor_pos, n); mark_for_update(scp, scp->cursor_pos - scp->scr_buf); mark_for_update(scp, scp->cursor_pos - scp->scr_buf + n - 1); break; case 'Z': /* move n tabs backwards */ n = scp->term.param[0]; if (n < 1) n = 1; if ((i = scp->xpos & 0xf8) == scp->xpos) i -= 8*n; else i -= 8*(n-1); if (i < 0) i = 0; move_crsr(scp, i, scp->ypos); break; case '`': /* move cursor to column n */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, n - 1, scp->ypos); break; case 'a': /* move cursor n columns to the right */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, scp->xpos + n, scp->ypos); break; case 'd': /* move cursor to row n */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, scp->xpos, n - 1); break; case 'e': /* move cursor n rows down */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, scp->xpos, scp->ypos + n); break; case 'm': /* change attribute */ if (scp->term.num_param == 0) { scp->term.attr_mask = NORMAL_ATTR; scp->term.cur_attr = scp->term.cur_color = scp->term.std_color; break; } for (i = 0; i < scp->term.num_param; i++) { switch (n = scp->term.param[i]) { case 0: /* back to normal */ scp->term.attr_mask = NORMAL_ATTR; scp->term.cur_attr = scp->term.cur_color = scp->term.std_color; break; case 1: /* bold */ scp->term.attr_mask |= BOLD_ATTR; scp->term.cur_attr = mask2attr(&scp->term); break; case 4: /* underline */ scp->term.attr_mask |= UNDERLINE_ATTR; scp->term.cur_attr = mask2attr(&scp->term); break; case 5: /* blink */ scp->term.attr_mask |= BLINK_ATTR; scp->term.cur_attr = mask2attr(&scp->term); break; case 7: /* reverse video */ scp->term.attr_mask |= REVERSE_ATTR; scp->term.cur_attr = mask2attr(&scp->term); break; case 30: case 31: /* set fg color */ case 32: case 33: case 34: case 35: case 36: case 37: scp->term.attr_mask |= FOREGROUND_CHANGED; scp->term.cur_color = (scp->term.cur_color&0xF000) | (ansi_col[(n-30)&7]<<8); scp->term.cur_attr = mask2attr(&scp->term); break; case 40: case 41: /* set bg color */ case 42: case 43: case 44: case 45: case 46: case 47: scp->term.attr_mask |= BACKGROUND_CHANGED; scp->term.cur_color = (scp->term.cur_color&0x0F00) | (ansi_col[(n-40)&7]<<12); scp->term.cur_attr = mask2attr(&scp->term); break; } } break; case 's': /* Save cursor position */ scp->saved_xpos = scp->xpos; scp->saved_ypos = scp->ypos; break; case 'u': /* Restore saved cursor position */ if (scp->saved_xpos >= 0 && scp->saved_ypos >= 0) move_crsr(scp, scp->saved_xpos, scp->saved_ypos); break; case 'x': if (scp->term.num_param == 0) n = 0; else n = scp->term.param[0]; switch (n) { case 0: /* reset attributes */ scp->term.attr_mask = NORMAL_ATTR; scp->term.cur_attr = scp->term.cur_color = scp->term.std_color = current_default->std_color; scp->term.rev_color = current_default->rev_color; break; case 1: /* set ansi background */ scp->term.attr_mask &= ~BACKGROUND_CHANGED; scp->term.cur_color = scp->term.std_color = (scp->term.std_color & 0x0F00) | (ansi_col[(scp->term.param[1])&0x0F]<<12); scp->term.cur_attr = mask2attr(&scp->term); break; case 2: /* set ansi foreground */ scp->term.attr_mask &= ~FOREGROUND_CHANGED; scp->term.cur_color = scp->term.std_color = (scp->term.std_color & 0xF000) | (ansi_col[(scp->term.param[1])&0x0F]<<8); scp->term.cur_attr = mask2attr(&scp->term); break; case 3: /* set ansi attribute directly */ scp->term.attr_mask &= ~(FOREGROUND_CHANGED|BACKGROUND_CHANGED); scp->term.cur_color = scp->term.std_color = (scp->term.param[1]&0xFF)<<8; scp->term.cur_attr = mask2attr(&scp->term); break; case 5: /* set ansi reverse video background */ scp->term.rev_color = (scp->term.rev_color & 0x0F00) | (ansi_col[(scp->term.param[1])&0x0F]<<12); scp->term.cur_attr = mask2attr(&scp->term); break; case 6: /* set ansi reverse video foreground */ scp->term.rev_color = (scp->term.rev_color & 0xF000) | (ansi_col[(scp->term.param[1])&0x0F]<<8); scp->term.cur_attr = mask2attr(&scp->term); break; case 7: /* set ansi reverse video directly */ scp->term.rev_color = (scp->term.param[1]&0xFF)<<8; scp->term.cur_attr = mask2attr(&scp->term); break; } break; case 'z': /* switch to (virtual) console n */ if (scp->term.num_param == 1) switch_scr(scp, scp->term.param[0]); break; } } else if (scp->term.esc == 3) { /* seen ESC [0-9]+ = */ if (c >= '0' && c <= '9') { if (scp->term.num_param < MAX_ESC_PAR) { if (scp->term.last_param != scp->term.num_param) { scp->term.last_param = scp->term.num_param; scp->term.param[scp->term.num_param] = 0; } else scp->term.param[scp->term.num_param] *= 10; scp->term.param[scp->term.num_param] += c - '0'; return; } } scp->term.num_param = scp->term.last_param + 1; switch (c) { case ';': if (scp->term.num_param < MAX_ESC_PAR) return; break; case 'A': /* set display border color */ if (scp->term.num_param == 1) { scp->border=scp->term.param[0] & 0xff; if (scp == cur_console) set_border(cur_console, scp->border); } break; case 'B': /* set bell pitch and duration */ if (scp->term.num_param == 2) { scp->bell_pitch = scp->term.param[0]; scp->bell_duration = scp->term.param[1]; } break; case 'C': /* set cursor type & shape */ if (scp->term.num_param == 1) { if (scp->term.param[0] & 0x01) sc_flags |= BLINK_CURSOR; else sc_flags &= ~BLINK_CURSOR; if ((scp->term.param[0] & 0x02) && ISFONTAVAIL(scp->adp->va_flags)) sc_flags |= CHAR_CURSOR; else sc_flags &= ~CHAR_CURSOR; } else if (scp->term.num_param == 2) { scp->cursor_start = scp->term.param[0] & 0x1F; scp->cursor_end = scp->term.param[1] & 0x1F; } /* * The cursor shape is global property; all virtual consoles * are affected. Update the cursor in the current console... */ if (!ISGRAPHSC(cur_console)) { i = spltty(); remove_cursor_image(cur_console); if (sc_flags & CHAR_CURSOR) set_destructive_cursor(cur_console); draw_cursor_image(cur_console); splx(i); } break; case 'F': /* set ansi foreground */ if (scp->term.num_param == 1) { scp->term.attr_mask &= ~FOREGROUND_CHANGED; scp->term.cur_color = scp->term.std_color = (scp->term.std_color & 0xF000) | ((scp->term.param[0] & 0x0F) << 8); scp->term.cur_attr = mask2attr(&scp->term); } break; case 'G': /* set ansi background */ if (scp->term.num_param == 1) { scp->term.attr_mask &= ~BACKGROUND_CHANGED; scp->term.cur_color = scp->term.std_color = (scp->term.std_color & 0x0F00) | ((scp->term.param[0] & 0x0F) << 12); scp->term.cur_attr = mask2attr(&scp->term); } break; case 'H': /* set ansi reverse video foreground */ if (scp->term.num_param == 1) { scp->term.rev_color = (scp->term.rev_color & 0xF000) | ((scp->term.param[0] & 0x0F) << 8); scp->term.cur_attr = mask2attr(&scp->term); } break; case 'I': /* set ansi reverse video background */ if (scp->term.num_param == 1) { scp->term.rev_color = (scp->term.rev_color & 0x0F00) | ((scp->term.param[0] & 0x0F) << 12); scp->term.cur_attr = mask2attr(&scp->term); } break; } } #if notyet else if (scp->term.esc == 4) { /* seen ESC Q */ /* to be filled */ } #endif else if (scp->term.esc == 5) { /* seen ESC ( */ switch (c) { case 'B': /* iso-2022: desginate ASCII into G0 */ break; /* other items to be filled */ default: break; } } scp->term.esc = 0; } static void ansi_put(scr_stat *scp, u_char *buf, int len) { u_char *ptr = buf; /* make screensaver happy */ if (!sticky_splash && scp == cur_console) run_scrn_saver = FALSE; write_in_progress++; outloop: if (scp->term.esc) { scan_esc(scp, *ptr++); len--; } else if (PRINTABLE(*ptr)) { /* Print only printables */ int cnt = len <= (scp->xsize-scp->xpos) ? len : (scp->xsize-scp->xpos); u_short cur_attr = scp->term.cur_attr; u_short *cursor_pos = scp->cursor_pos; do { /* * gcc-2.6.3 generates poor (un)sign extension code. Casting the * pointers in the following to volatile should have no effect, * but in fact speeds up this inner loop from 26 to 18 cycles * (+ cache misses) on i486's. */ #define UCVP(ucp) ((u_char volatile *)(ucp)) *cursor_pos++ = UCVP(scr_map)[*UCVP(ptr)] | cur_attr; ptr++; cnt--; } while (cnt && PRINTABLE(*ptr)); len -= (cursor_pos - scp->cursor_pos); scp->xpos += (cursor_pos - scp->cursor_pos); mark_for_update(scp, scp->cursor_pos - scp->scr_buf); mark_for_update(scp, cursor_pos - scp->scr_buf); scp->cursor_pos = cursor_pos; if (scp->xpos >= scp->xsize) { scp->xpos = 0; scp->ypos++; } } else { switch(*ptr) { case 0x07: do_bell(scp, scp->bell_pitch, scp->bell_duration); break; case 0x08: /* non-destructive backspace */ if (scp->cursor_pos > scp->scr_buf) { mark_for_update(scp, scp->cursor_pos - scp->scr_buf); scp->cursor_pos--; mark_for_update(scp, scp->cursor_pos - scp->scr_buf); if (scp->xpos > 0) scp->xpos--; else { scp->xpos += scp->xsize - 1; scp->ypos--; } } break; case 0x09: /* non-destructive tab */ mark_for_update(scp, scp->cursor_pos - scp->scr_buf); scp->cursor_pos += (8 - scp->xpos % 8u); mark_for_update(scp, scp->cursor_pos - scp->scr_buf); if ((scp->xpos += (8 - scp->xpos % 8u)) >= scp->xsize) { scp->xpos = 0; scp->ypos++; } break; case 0x0a: /* newline, same pos */ mark_for_update(scp, scp->cursor_pos - scp->scr_buf); scp->cursor_pos += scp->xsize; mark_for_update(scp, scp->cursor_pos - scp->scr_buf); scp->ypos++; break; case 0x0c: /* form feed, clears screen */ sc_clear_screen(scp); break; case 0x0d: /* return, return to pos 0 */ mark_for_update(scp, scp->cursor_pos - scp->scr_buf); scp->cursor_pos -= scp->xpos; mark_for_update(scp, scp->cursor_pos - scp->scr_buf); scp->xpos = 0; break; case 0x1b: /* start escape sequence */ scp->term.esc = 1; scp->term.num_param = 0; break; } ptr++; len--; } /* do we have to scroll ?? */ if (scp->cursor_pos >= scp->scr_buf + scp->ysize * scp->xsize) { remove_cutmarking(scp); if (scp->history != NULL) { bcopy(scp->scr_buf, scp->history_head, scp->xsize * sizeof(u_short)); scp->history_head += scp->xsize; if (scp->history_head + scp->xsize > scp->history + scp->history_size) scp->history_head = scp->history; } bcopy(scp->scr_buf + scp->xsize, scp->scr_buf, scp->xsize * (scp->ysize - 1) * sizeof(u_short)); fillw(scp->term.cur_color | scr_map[0x20], scp->scr_buf + scp->xsize * (scp->ysize - 1), scp->xsize); scp->cursor_pos -= scp->xsize; scp->ypos--; mark_all(scp); } if (len) goto outloop; write_in_progress--; if (delayed_next_scr) switch_scr(scp, delayed_next_scr - 1); } static void scinit(void) { video_adapter_t *adp; int col; int row; u_int i; if (init_done != COLD) return; init_done = WARM; get_bios_values(); /* extract the hardware cursor location and hide the cursor for now */ adp = vid_get_adapter(adapter); (*vidsw[adapter]->read_hw_cursor)(adp, &col, &row); (*vidsw[adapter]->set_hw_cursor)(adp, -1, -1); /* set up the first console */ current_default = &user_default; console[0] = &main_console; init_scp(console[0]); cur_console = console[0]; /* copy screen to temporary buffer */ if (ISTEXTSC(console[0])) bcopy_fromio(console[0]->adp->va_window, sc_buffer, console[0]->xsize * console[0]->ysize * sizeof(u_short)); console[0]->scr_buf = console[0]->mouse_pos = console[0]->mouse_oldpos = sc_buffer; if (col >= console[0]->xsize) col = 0; if (row >= console[0]->ysize) row = console[0]->ysize - 1; console[0]->xpos = col; console[0]->ypos = row; console[0]->cursor_pos = console[0]->cursor_oldpos = sc_buffer + row*console[0]->xsize + col; console[0]->cursor_saveunder = *console[0]->cursor_pos; for (i=1; iadp->va_flags)) { if (fonts_loaded & FONT_16) { copy_font(cur_console, LOAD, 16, font_16); } else { copy_font(cur_console, SAVE, 16, font_16); fonts_loaded = FONT_16; set_destructive_cursor(cur_console); } /* * FONT KLUDGE * Always use the font page #0. XXX */ (*vidsw[cur_console->ad]->show_font)(cur_console->adp, 0); } save_palette(cur_console->adp, palette); #if NSPLASH > 0 /* we are ready to put up the splash image! */ splash_init(cur_console->adp, scsplash_callback); #endif } static void scshutdown(int howto, void *arg) { sc_touch_scrn_saver(); if (!cold && cur_console->smode.mode == VT_AUTO && console[0]->smode.mode == VT_AUTO) switch_scr(cur_console, 0); shutdown_in_progress = TRUE; } int sc_clean_up(scr_stat *scp) { int error; sc_touch_scrn_saver(); if ((error = wait_scrn_saver_stop())) return error; scp->status &= ~MOUSE_VISIBLE; remove_cutmarking(scp); return 0; } void sc_alloc_scr_buffer(scr_stat *scp, int wait, int clear) { if (scp->scr_buf) free(scp->scr_buf, M_DEVBUF); scp->scr_buf = (u_short *)malloc(scp->xsize*scp->ysize*sizeof(u_short), M_DEVBUF, (wait) ? M_WAITOK : M_NOWAIT); if (clear) { /* clear the screen and move the text cursor to the top-left position */ sc_clear_screen(scp); } else { /* retain the current cursor position, but adjust pointers */ move_crsr(scp, scp->xpos, scp->ypos); scp->cursor_oldpos = scp->cursor_pos; } /* move the mouse cursor at the center of the screen */ sc_move_mouse(scp, scp->xpixel / 2, scp->ypixel / 2); } void sc_alloc_cut_buffer(scr_stat *scp, int wait) { if ((cut_buffer == NULL) || (cut_buffer_size < scp->xsize * scp->ysize + 1)) { if (cut_buffer != NULL) free(cut_buffer, M_DEVBUF); cut_buffer_size = scp->xsize * scp->ysize + 1; cut_buffer = (u_char *)malloc(cut_buffer_size, M_DEVBUF, (wait) ? M_WAITOK : M_NOWAIT); if (cut_buffer != NULL) cut_buffer[0] = '\0'; } } void sc_alloc_history_buffer(scr_stat *scp, int lines, int extra, int wait) { u_short *usp; if (lines < scp->ysize) lines = scp->ysize; usp = scp->history; scp->history = NULL; if (usp != NULL) { free(usp, M_DEVBUF); if (extra > 0) extra_history_size += extra; } scp->history_size = lines * scp->xsize; if (lines > imax(sc_history_size, scp->ysize)) extra_history_size -= lines - imax(sc_history_size, scp->ysize); usp = (u_short *)malloc(scp->history_size * sizeof(u_short), M_DEVBUF, (wait) ? M_WAITOK : M_NOWAIT); if (usp != NULL) bzero(usp, scp->history_size * sizeof(u_short)); scp->history_head = scp->history_pos = usp; scp->history = usp; } static scr_stat *alloc_scp() { scr_stat *scp; scp = (scr_stat *)malloc(sizeof(scr_stat), M_DEVBUF, M_WAITOK); init_scp(scp); sc_alloc_scr_buffer(scp, TRUE, TRUE); if (ISMOUSEAVAIL(scp->adp->va_flags)) sc_alloc_cut_buffer(scp, TRUE); sc_alloc_history_buffer(scp, sc_history_size, 0, TRUE); /* SOS if (scp->adp->va_flags & V_ADP_MODECHANGE) set_mode(scp); */ sc_clear_screen(scp); scp->cursor_saveunder = *scp->cursor_pos; return scp; } static void init_scp(scr_stat *scp) { video_info_t info; scp->ad = adapter; scp->adp = vid_get_adapter(scp->ad); (*vidsw[scp->ad]->get_info)(scp->adp, initial_video_mode, &info); scp->status = 0; scp->mode = initial_video_mode; scp->scr_buf = NULL; if (info.vi_flags & V_INFO_GRAPHICS) { scp->status |= GRAPHICS_MODE; scp->xpixel = info.vi_width; scp->ypixel = info.vi_height; scp->xsize = info.vi_width/8; scp->ysize = info.vi_height/info.vi_cheight; scp->font_size = FONT_NONE; } else { scp->xsize = info.vi_width; scp->ysize = info.vi_height; scp->xpixel = scp->xsize*8; scp->ypixel = scp->ysize*info.vi_cheight; scp->font_size = info.vi_cheight; } scp->xoff = scp->yoff = 0; scp->xpos = scp->ypos = 0; scp->saved_xpos = scp->saved_ypos = -1; scp->start = scp->xsize * scp->ysize; scp->end = 0; scp->term.esc = 0; scp->term.attr_mask = NORMAL_ATTR; scp->term.cur_attr = scp->term.cur_color = scp->term.std_color = current_default->std_color; scp->term.rev_color = current_default->rev_color; scp->border = BG_BLACK; scp->cursor_start = bios_value.cursor_start; scp->cursor_end = bios_value.cursor_end; scp->mouse_xpos = scp->xsize*8/2; scp->mouse_ypos = scp->ysize*scp->font_size/2; scp->mouse_cut_start = scp->mouse_cut_end = NULL; scp->mouse_signal = 0; scp->mouse_pid = 0; scp->mouse_proc = NULL; scp->kbd_mode = K_XLATE; scp->bell_pitch = BELL_PITCH; scp->bell_duration = BELL_DURATION; scp->status |= (bios_value.shift_state & 0x20) ? NLKED : 0; scp->status |= CURSOR_ENABLED; scp->pid = 0; scp->proc = NULL; scp->smode.mode = VT_AUTO; scp->history_head = scp->history_pos = scp->history = NULL; scp->history_size = imax(sc_history_size, scp->ysize) * scp->xsize; } static void get_bios_values(void) { bios_value.cursor_start = *(u_int8_t *)pa_to_va(0x461); bios_value.cursor_end = *(u_int8_t *)pa_to_va(0x460); bios_value.shift_state = *(u_int8_t *)pa_to_va(0x417); } static void history_to_screen(scr_stat *scp) { int i; for (i=0; iysize; i++) bcopy(scp->history + (((scp->history_pos - scp->history) + scp->history_size-((i+1)*scp->xsize))%scp->history_size), scp->scr_buf + (scp->xsize * (scp->ysize-1 - i)), scp->xsize * sizeof(u_short)); mark_all(scp); } static int history_up_line(scr_stat *scp) { if (WRAPHIST(scp, scp->history_pos, -(scp->xsize*scp->ysize)) != scp->history_head) { scp->history_pos = WRAPHIST(scp, scp->history_pos, -scp->xsize); history_to_screen(scp); return 0; } else return -1; } static int history_down_line(scr_stat *scp) { if (scp->history_pos != scp->history_head) { scp->history_pos = WRAPHIST(scp, scp->history_pos, scp->xsize); history_to_screen(scp); return 0; } else return -1; } /* * scgetc(flags) - get character from keyboard. * If flags & SCGETC_CN, then avoid harmful side effects. * If flags & SCGETC_NONBLOCK, then wait until a key is pressed, else * return NOKEY if there is nothing there. */ static u_int scgetc(keyboard_t *kbd, u_int flags) { u_int c; int this_scr; int f; int i; if (kbd == NULL) return NOKEY; next_code: /* I don't like this, but... XXX */ if (flags & SCGETC_CN) sccnupdate(cur_console); /* first see if there is something in the keyboard port */ for (;;) { c = kbd_read_char(kbd, !(flags & SCGETC_NONBLOCK)); if (c == ERRKEY) { if (!(flags & SCGETC_CN)) do_bell(cur_console, BELL_PITCH, BELL_DURATION); } else if (c == NOKEY) return c; else break; } /* make screensaver happy */ if (!(c & RELKEY)) sc_touch_scrn_saver(); #ifdef __i386__ if (!(flags & SCGETC_CN)) /* do the /dev/random device a favour */ add_keyboard_randomness(c); #endif if (cur_console->kbd_mode != K_XLATE) return KEYCHAR(c); /* if scroll-lock pressed allow history browsing */ if (!ISGRAPHSC(cur_console) && cur_console->history && cur_console->status & SLKED) { cur_console->status &= ~CURSOR_ENABLED; if (!(cur_console->status & BUFFER_SAVED)) { cur_console->status |= BUFFER_SAVED; cur_console->history_save = cur_console->history_head; /* copy screen into top of history buffer */ for (i=0; iysize; i++) { bcopy(cur_console->scr_buf + (cur_console->xsize * i), cur_console->history_head, cur_console->xsize * sizeof(u_short)); cur_console->history_head += cur_console->xsize; if (cur_console->history_head + cur_console->xsize > cur_console->history + cur_console->history_size) cur_console->history_head=cur_console->history; } cur_console->history_pos = cur_console->history_head; history_to_screen(cur_console); } switch (c) { /* FIXME: key codes */ case SPCLKEY | FKEY | F(49): /* home key */ remove_cutmarking(cur_console); cur_console->history_pos = cur_console->history_head; history_to_screen(cur_console); goto next_code; case SPCLKEY | FKEY | F(57): /* end key */ remove_cutmarking(cur_console); cur_console->history_pos = WRAPHIST(cur_console, cur_console->history_head, cur_console->xsize*cur_console->ysize); history_to_screen(cur_console); goto next_code; case SPCLKEY | FKEY | F(50): /* up arrow key */ remove_cutmarking(cur_console); if (history_up_line(cur_console)) if (!(flags & SCGETC_CN)) do_bell(cur_console, BELL_PITCH, BELL_DURATION); goto next_code; case SPCLKEY | FKEY | F(58): /* down arrow key */ remove_cutmarking(cur_console); if (history_down_line(cur_console)) if (!(flags & SCGETC_CN)) do_bell(cur_console, BELL_PITCH, BELL_DURATION); goto next_code; case SPCLKEY | FKEY | F(51): /* page up key */ remove_cutmarking(cur_console); for (i=0; iysize; i++) if (history_up_line(cur_console)) { if (!(flags & SCGETC_CN)) do_bell(cur_console, BELL_PITCH, BELL_DURATION); break; } goto next_code; case SPCLKEY | FKEY | F(59): /* page down key */ remove_cutmarking(cur_console); for (i=0; iysize; i++) if (history_down_line(cur_console)) { if (!(flags & SCGETC_CN)) do_bell(cur_console, BELL_PITCH, BELL_DURATION); break; } goto next_code; } } /* * Process and consume special keys here. Return a plain char code * or a char code with the META flag or a function key code. */ if (c & RELKEY) { /* key released */ /* goto next_code */ } else { /* key pressed */ if (c & SPCLKEY) { c &= ~SPCLKEY; switch (KEYCHAR(c)) { /* LOCKING KEYS */ case NLK: case CLK: case ALK: break; case SLK: kbd_ioctl(kbd, KDGKBSTATE, (caddr_t)&f); if (f & SLKED) { cur_console->status |= SLKED; } else { if (cur_console->status & SLKED) { cur_console->status &= ~SLKED; if (cur_console->status & BUFFER_SAVED) { int i; u_short *ptr = cur_console->history_save; for (i=0; iysize; i++) { bcopy(ptr, cur_console->scr_buf + (cur_console->xsize*i), cur_console->xsize * sizeof(u_short)); ptr += cur_console->xsize; if (ptr + cur_console->xsize > cur_console->history + cur_console->history_size) ptr = cur_console->history; } cur_console->status &= ~BUFFER_SAVED; cur_console->history_head=cur_console->history_save; cur_console->status |= CURSOR_ENABLED; mark_all(cur_console); } scstart(VIRTUAL_TTY(get_scr_num())); } } break; /* NON-LOCKING KEYS */ case NOP: case LSH: case RSH: case LCTR: case RCTR: case LALT: case RALT: case ASH: case META: break; case BTAB: return c; case SPSC: /* force activatation/deactivation of the screen saver */ if (!scrn_blanked) { run_scrn_saver = TRUE; scrn_time_stamp -= scrn_blank_time; } #if NSPLASH > 0 if (cold) { /* * While devices are being probed, the screen saver need * to be invoked explictly. XXX */ if (scrn_blanked) { scsplash_stick(FALSE); stop_scrn_saver(current_saver); } else { if (!ISGRAPHSC(cur_console)) { scsplash_stick(TRUE); (*current_saver)(TRUE); } } } #endif /* NSPLASH */ break; case RBT: #ifndef SC_DISABLE_REBOOT shutdown_nice(); #endif break; #if NAPM > 0 case SUSP: apm_suspend(PMST_SUSPEND); break; case STBY: apm_suspend(PMST_STANDBY); break; #else case SUSP: case STBY: break; #endif case DBG: #ifdef DDB /* try to switch to console 0 */ /* * TRY to make sure the screen saver is stopped, * and the screen is updated before switching to * the vty0. */ scrn_timer((void *)FALSE); if (cur_console->smode.mode == VT_AUTO && console[0]->smode.mode == VT_AUTO) switch_scr(cur_console, 0); Debugger("manual escape to debugger"); #else printf("No debugger in kernel\n"); #endif break; case NEXT: this_scr = get_scr_num(); for (i = this_scr + 1; i != this_scr; i = (i + 1)%MAXCONS) { struct tty *tp = VIRTUAL_TTY(i); if (tp->t_state & TS_ISOPEN) { switch_scr(cur_console, i); break; } } break; default: if (KEYCHAR(c) >= F_SCR && KEYCHAR(c) <= L_SCR) { switch_scr(cur_console, KEYCHAR(c) - F_SCR); break; } /* assert(c & FKEY) */ return c; } /* goto next_code */ } else { /* regular keys (maybe MKEY is set) */ return c; } } goto next_code; } int scmmap(dev_t dev, vm_offset_t offset, int nprot) { struct tty *tp; struct scr_stat *scp; tp = scdevtotty(dev); if (!tp) return ENXIO; scp = sc_get_scr_stat(tp->t_dev); return (*vidsw[scp->ad]->mmap)(scp->adp, offset); } /* * Calculate hardware attributes word using logical attributes mask and * hardware colors */ static int mask2attr(struct term_stat *term) { int attr, mask = term->attr_mask; if (mask & REVERSE_ATTR) { attr = ((mask & FOREGROUND_CHANGED) ? ((term->cur_color & 0xF000) >> 4) : (term->rev_color & 0x0F00)) | ((mask & BACKGROUND_CHANGED) ? ((term->cur_color & 0x0F00) << 4) : (term->rev_color & 0xF000)); } else attr = term->cur_color; /* XXX: underline mapping for Hercules adapter can be better */ if (mask & (BOLD_ATTR | UNDERLINE_ATTR)) attr ^= 0x0800; if (mask & BLINK_ATTR) attr ^= 0x8000; return attr; } static int save_kbd_state(scr_stat *scp) { int state; int error; error = kbd_ioctl(kbd, KDGKBSTATE, (caddr_t)&state); if (error == ENOIOCTL) error = ENODEV; if (error == 0) { scp->status &= ~LOCK_MASK; scp->status |= state; } return error; } static int update_kbd_state(int new_bits, int mask) { int state; int error; if (mask != LOCK_MASK) { error = kbd_ioctl(kbd, KDGKBSTATE, (caddr_t)&state); if (error == ENOIOCTL) error = ENODEV; if (error) return error; state &= ~mask; state |= new_bits & mask; } else { state = new_bits & LOCK_MASK; } error = kbd_ioctl(kbd, KDSKBSTATE, (caddr_t)&state); if (error == ENOIOCTL) error = ENODEV; return error; } static int update_kbd_leds(int which) { int error; which &= LOCK_MASK; error = kbd_ioctl(kbd, KDSETLED, (caddr_t)&which); if (error == ENOIOCTL) error = ENODEV; return error; } int set_mode(scr_stat *scp) { video_info_t info; /* reject unsupported mode */ if ((*vidsw[scp->ad]->get_info)(scp->adp, scp->mode, &info)) return 1; /* if this vty is not currently showing, do nothing */ if (scp != cur_console) return 0; /* setup video hardware for the given mode */ (*vidsw[scp->ad]->set_mode)(scp->adp, scp->mode); Crtat = scp->adp->va_window; if (!(scp->status & GRAPHICS_MODE)) { /* load appropriate font */ if (!(scp->status & PIXEL_MODE) && ISFONTAVAIL(scp->adp->va_flags)) { if (scp->font_size < 14) { if (fonts_loaded & FONT_8) copy_font(scp, LOAD, 8, font_8); } else if (scp->font_size >= 16) { if (fonts_loaded & FONT_16) copy_font(scp, LOAD, 16, font_16); } else { if (fonts_loaded & FONT_14) copy_font(scp, LOAD, 14, font_14); } /* * FONT KLUDGE: * This is an interim kludge to display correct font. * Always use the font page #0 on the video plane 2. * Somehow we cannot show the font in other font pages on * some video cards... XXX */ (*vidsw[scp->ad]->show_font)(scp->adp, 0); } mark_all(scp); } if (scp->status & PIXEL_MODE) bzero_io(scp->adp->va_window, scp->xpixel*scp->ypixel/8); set_border(scp, scp->border); /* move hardware cursor out of the way */ (*vidsw[scp->ad]->set_hw_cursor)(scp->adp, -1, -1); return 0; } void set_border(scr_stat *scp, int color) { vm_offset_t p; int xoff; int yoff; int xlen; int ylen; int i; (*vidsw[scp->ad]->set_border)(scp->adp, color); if (scp->status & PIXEL_MODE) { outw(GDCIDX, 0x0005); /* read mode 0, write mode 0 */ outw(GDCIDX, 0x0003); /* data rotate/function select */ outw(GDCIDX, 0x0f01); /* set/reset enable */ outw(GDCIDX, 0xff08); /* bit mask */ outw(GDCIDX, (color << 8) | 0x00); /* set/reset */ p = scp->adp->va_window; xoff = scp->xoff; yoff = scp->yoff*scp->font_size; xlen = scp->xpixel/8; ylen = scp->ysize*scp->font_size; if (yoff > 0) { bzero_io(p, xlen*yoff); bzero_io(p + xlen*(yoff + ylen), xlen*scp->ypixel - xlen*(yoff + ylen)); } if (xoff > 0) { for (i = 0; i < ylen; ++i) { bzero_io(p + xlen*(yoff + i), xoff); bzero_io(p + xlen*(yoff + i) + xoff + scp->xsize, xlen - xoff - scp->xsize); } } outw(GDCIDX, 0x0000); /* set/reset */ outw(GDCIDX, 0x0001); /* set/reset enable */ } } void copy_font(scr_stat *scp, int operation, int font_size, u_char *buf) { /* * FONT KLUDGE: * This is an interim kludge to display correct font. * Always use the font page #0 on the video plane 2. * Somehow we cannot show the font in other font pages on * some video cards... XXX */ font_loading_in_progress = TRUE; if (operation == LOAD) { (*vidsw[scp->ad]->load_font)(scp->adp, 0, font_size, buf, 0, 256); if (sc_flags & CHAR_CURSOR) set_destructive_cursor(scp); } else if (operation == SAVE) { (*vidsw[scp->ad]->save_font)(scp->adp, 0, font_size, buf, 0, 256); } font_loading_in_progress = FALSE; } static void set_destructive_cursor(scr_stat *scp) { u_char cursor[32]; u_char *font_buffer; int font_size; int crtc_addr; int i; if (!ISFONTAVAIL(scp->adp->va_flags) || (scp->status & (GRAPHICS_MODE | PIXEL_MODE))) return; if (scp->font_size < 14) { font_buffer = font_8; font_size = 8; } else if (scp->font_size >= 16) { font_buffer = font_16; font_size = 16; } else { font_buffer = font_14; font_size = 14; } if (scp->status & MOUSE_VISIBLE) { if ((scp->cursor_saveunder & 0xff) == SC_MOUSE_CHAR) bcopy(&scp->mouse_cursor[0], cursor, scp->font_size); else if ((scp->cursor_saveunder & 0xff) == SC_MOUSE_CHAR + 1) bcopy(&scp->mouse_cursor[32], cursor, scp->font_size); else if ((scp->cursor_saveunder & 0xff) == SC_MOUSE_CHAR + 2) bcopy(&scp->mouse_cursor[64], cursor, scp->font_size); else if ((scp->cursor_saveunder & 0xff) == SC_MOUSE_CHAR + 3) bcopy(&scp->mouse_cursor[96], cursor, scp->font_size); else bcopy(font_buffer+((scp->cursor_saveunder & 0xff)*scp->font_size), cursor, scp->font_size); } else bcopy(font_buffer + ((scp->cursor_saveunder & 0xff) * scp->font_size), cursor, scp->font_size); for (i=0; i<32; i++) if ((i >= scp->cursor_start && i <= scp->cursor_end) || (scp->cursor_start >= scp->font_size && i == scp->font_size - 1)) cursor[i] |= 0xff; #if 1 crtc_addr = scp->adp->va_crtc_addr; while (!(inb(crtc_addr+6) & 0x08)) /* wait for vertical retrace */ ; #endif font_loading_in_progress = TRUE; (*vidsw[scp->ad]->load_font)(scp->adp, 0, font_size, cursor, DEAD_CHAR, 1); font_loading_in_progress = FALSE; } void sc_move_mouse(scr_stat *scp, int x, int y) { scp->mouse_xpos = x; scp->mouse_ypos = y; scp->mouse_pos = scp->mouse_oldpos = scp->scr_buf + (y / scp->font_size) * scp->xsize + x / 8; } static void set_mouse_pos(scr_stat *scp) { static int last_xpos = -1, last_ypos = -1; if (scp->mouse_xpos < 0) scp->mouse_xpos = 0; if (scp->mouse_ypos < 0) scp->mouse_ypos = 0; if (!ISTEXTSC(scp)) { if (scp->mouse_xpos > scp->xpixel-1) scp->mouse_xpos = scp->xpixel-1; if (scp->mouse_ypos > scp->ypixel-1) scp->mouse_ypos = scp->ypixel-1; return; } if (scp->mouse_xpos > (scp->xsize*8)-1) scp->mouse_xpos = (scp->xsize*8)-1; if (scp->mouse_ypos > (scp->ysize*scp->font_size)-1) scp->mouse_ypos = (scp->ysize*scp->font_size)-1; if (scp->mouse_xpos != last_xpos || scp->mouse_ypos != last_ypos) { scp->status |= MOUSE_MOVED; scp->mouse_pos = scp->scr_buf + ((scp->mouse_ypos/scp->font_size)*scp->xsize + scp->mouse_xpos/8); if ((scp->status & MOUSE_VISIBLE) && (scp->status & MOUSE_CUTTING)) mouse_cut(scp); } } #define isspace(c) (((c) & 0xff) == ' ') static int skip_spc_right(scr_stat *scp, u_short *p) { int i; for (i = (p - scp->scr_buf) % scp->xsize; i < scp->xsize; ++i) { if (!isspace(*p)) break; ++p; } return i; } static int skip_spc_left(scr_stat *scp, u_short *p) { int i; for (i = (p-- - scp->scr_buf) % scp->xsize - 1; i >= 0; --i) { if (!isspace(*p)) break; --p; } return i; } static void mouse_cut(scr_stat *scp) { u_short *end; u_short *p; int i = 0; int j = 0; scp->mouse_cut_end = (scp->mouse_pos >= scp->mouse_cut_start) ? scp->mouse_pos + 1 : scp->mouse_pos; end = (scp->mouse_cut_start > scp->mouse_cut_end) ? scp->mouse_cut_start : scp->mouse_cut_end; for (p = (scp->mouse_cut_start > scp->mouse_cut_end) ? scp->mouse_cut_end : scp->mouse_cut_start; p < end; ++p) { cut_buffer[i] = *p & 0xff; /* remember the position of the last non-space char */ if (!isspace(cut_buffer[i++])) j = i; /* trim trailing blank when crossing lines */ if (((p - scp->scr_buf) % scp->xsize) == (scp->xsize - 1)) { cut_buffer[j++] = '\r'; i = j; } } cut_buffer[i] = '\0'; /* scan towards the end of the last line */ --p; for (i = (p - scp->scr_buf) % scp->xsize; i < scp->xsize; ++i) { if (!isspace(*p)) break; ++p; } /* if there is nothing but blank chars, trim them, but mark towards eol */ if (i >= scp->xsize) { if (scp->mouse_cut_start > scp->mouse_cut_end) scp->mouse_cut_start = p; else scp->mouse_cut_end = p; cut_buffer[j++] = '\r'; cut_buffer[j] = '\0'; } mark_for_update(scp, scp->mouse_cut_start - scp->scr_buf); mark_for_update(scp, scp->mouse_cut_end - scp->scr_buf); } static void mouse_cut_start(scr_stat *scp) { int i; if (scp->status & MOUSE_VISIBLE) { if (scp->mouse_pos == scp->mouse_cut_start && scp->mouse_cut_start == scp->mouse_cut_end - 1) { cut_buffer[0] = '\0'; remove_cutmarking(scp); } else if (skip_spc_right(scp, scp->mouse_pos) >= scp->xsize) { /* if the pointer is on trailing blank chars, mark towards eol */ i = skip_spc_left(scp, scp->mouse_pos) + 1; scp->mouse_cut_start = scp->scr_buf + ((scp->mouse_pos - scp->scr_buf) / scp->xsize) * scp->xsize + i; scp->mouse_cut_end = scp->scr_buf + ((scp->mouse_pos - scp->scr_buf) / scp->xsize + 1) * scp->xsize; cut_buffer[0] = '\r'; cut_buffer[1] = '\0'; scp->status |= MOUSE_CUTTING; } else { scp->mouse_cut_start = scp->mouse_pos; scp->mouse_cut_end = scp->mouse_cut_start + 1; cut_buffer[0] = *scp->mouse_cut_start & 0xff; cut_buffer[1] = '\0'; scp->status |= MOUSE_CUTTING; } mark_all(scp); /* delete all other screens cut markings */ for (i=0; istatus & MOUSE_VISIBLE) { scp->status &= ~MOUSE_CUTTING; } } static void mouse_cut_word(scr_stat *scp) { u_short *p; u_short *sol; u_short *eol; int i; /* * Because we don't have locale information in the kernel, * we only distinguish space char and non-space chars. Punctuation * chars, symbols and other regular chars are all treated alike. */ if (scp->status & MOUSE_VISIBLE) { sol = scp->scr_buf + ((scp->mouse_pos - scp->scr_buf) / scp->xsize) * scp->xsize; eol = sol + scp->xsize; if (isspace(*scp->mouse_pos)) { for (p = scp->mouse_pos; p >= sol; --p) if (!isspace(*p)) break; scp->mouse_cut_start = ++p; for (p = scp->mouse_pos; p < eol; ++p) if (!isspace(*p)) break; scp->mouse_cut_end = p; } else { for (p = scp->mouse_pos; p >= sol; --p) if (isspace(*p)) break; scp->mouse_cut_start = ++p; for (p = scp->mouse_pos; p < eol; ++p) if (isspace(*p)) break; scp->mouse_cut_end = p; } for (i = 0, p = scp->mouse_cut_start; p < scp->mouse_cut_end; ++p) cut_buffer[i++] = *p & 0xff; cut_buffer[i] = '\0'; scp->status |= MOUSE_CUTTING; } } static void mouse_cut_line(scr_stat *scp) { u_short *p; int i; if (scp->status & MOUSE_VISIBLE) { scp->mouse_cut_start = scp->scr_buf + ((scp->mouse_pos - scp->scr_buf) / scp->xsize) * scp->xsize; scp->mouse_cut_end = scp->mouse_cut_start + scp->xsize; for (i = 0, p = scp->mouse_cut_start; p < scp->mouse_cut_end; ++p) cut_buffer[i++] = *p & 0xff; cut_buffer[i++] = '\r'; cut_buffer[i] = '\0'; scp->status |= MOUSE_CUTTING; } } static void mouse_cut_extend(scr_stat *scp) { if ((scp->status & MOUSE_VISIBLE) && !(scp->status & MOUSE_CUTTING) && (scp->mouse_cut_start != NULL)) { mouse_cut(scp); scp->status |= MOUSE_CUTTING; } } static void mouse_paste(scr_stat *scp) { if (scp->status & MOUSE_VISIBLE) { struct tty *tp; u_char *ptr = cut_buffer; tp = VIRTUAL_TTY(get_scr_num()); while (*ptr) (*linesw[tp->t_line].l_rint)(scr_rmap[*ptr++], tp); } } static void draw_mouse_image(scr_stat *scp) { u_short buffer[32]; u_short xoffset, yoffset; vm_offset_t crt_pos = scp->adp->va_window + 2*(scp->mouse_pos - scp->scr_buf); u_char *font_buffer; int font_size; int crtc_addr; int i; if (scp->font_size < 14) { font_buffer = font_8; font_size = 8; } else if (scp->font_size >= 16) { font_buffer = font_16; font_size = 16; } else { font_buffer = font_14; font_size = 14; } xoffset = scp->mouse_xpos % 8; yoffset = scp->mouse_ypos % scp->font_size; /* prepare mousepointer char's bitmaps */ bcopy(font_buffer + ((*(scp->mouse_pos) & 0xff) * font_size), &scp->mouse_cursor[0], font_size); bcopy(font_buffer + ((*(scp->mouse_pos+1) & 0xff) * font_size), &scp->mouse_cursor[32], font_size); bcopy(font_buffer + ((*(scp->mouse_pos+scp->xsize) & 0xff) * font_size), &scp->mouse_cursor[64], font_size); bcopy(font_buffer + ((*(scp->mouse_pos+scp->xsize+1) & 0xff) * font_size), &scp->mouse_cursor[96], font_size); for (i=0; imouse_cursor[i]<<8 | scp->mouse_cursor[i+32]; buffer[i+font_size]=scp->mouse_cursor[i+64]<<8|scp->mouse_cursor[i+96]; } /* now and-or in the mousepointer image */ for (i=0; i<16; i++) { buffer[i+yoffset] = ( buffer[i+yoffset] & ~(mouse_and_mask[i] >> xoffset)) | (mouse_or_mask[i] >> xoffset); } for (i=0; imouse_cursor[i] = (buffer[i] & 0xff00) >> 8; scp->mouse_cursor[i+32] = buffer[i] & 0xff; scp->mouse_cursor[i+64] = (buffer[i+font_size] & 0xff00) >> 8; scp->mouse_cursor[i+96] = buffer[i+font_size] & 0xff; } scp->mouse_oldpos = scp->mouse_pos; #if 1 /* wait for vertical retrace to avoid jitter on some videocards */ crtc_addr = scp->adp->va_crtc_addr; while (!(inb(crtc_addr+6) & 0x08)) /* idle */ ; #endif font_loading_in_progress = TRUE; (*vidsw[scp->ad]->load_font)(scp->adp, 0, 32, scp->mouse_cursor, SC_MOUSE_CHAR, 4); font_loading_in_progress = FALSE; writew(crt_pos, (*(scp->mouse_pos) & 0xff00) | SC_MOUSE_CHAR); writew(crt_pos+2*scp->xsize, (*(scp->mouse_pos + scp->xsize) & 0xff00) | (SC_MOUSE_CHAR + 2)); if (scp->mouse_xpos < (scp->xsize-1)*8) { writew(crt_pos + 2, (*(scp->mouse_pos + 1) & 0xff00) | (SC_MOUSE_CHAR + 1)); writew(crt_pos+2*scp->xsize + 2, (*(scp->mouse_pos + scp->xsize + 1) & 0xff00) | (SC_MOUSE_CHAR + 3)); } mark_for_update(scp, scp->mouse_pos - scp->scr_buf); mark_for_update(scp, scp->mouse_pos + scp->xsize + 1 - scp->scr_buf); } static void remove_mouse_image(scr_stat *scp) { vm_offset_t crt_pos; if (!ISTEXTSC(scp)) return; crt_pos = scp->adp->va_window + 2*(scp->mouse_oldpos - scp->scr_buf); writew(crt_pos, *(scp->mouse_oldpos)); writew(crt_pos+2, *(scp->mouse_oldpos+1)); writew(crt_pos+2*scp->xsize, *(scp->mouse_oldpos+scp->xsize)); writew(crt_pos+2*scp->xsize+2, *(scp->mouse_oldpos+scp->xsize+1)); mark_for_update(scp, scp->mouse_oldpos - scp->scr_buf); mark_for_update(scp, scp->mouse_oldpos + scp->xsize + 1 - scp->scr_buf); } static void draw_cutmarking(scr_stat *scp) { vm_offset_t crt_pos; u_short *ptr; u_short och, nch; crt_pos = scp->adp->va_window; for (ptr=scp->scr_buf; ptr<=(scp->scr_buf+(scp->xsize*scp->ysize)); ptr++) { nch = och = readw(crt_pos + 2*(ptr - scp->scr_buf)); /* are we outside the selected area ? */ if ( ptr < (scp->mouse_cut_start > scp->mouse_cut_end ? scp->mouse_cut_end : scp->mouse_cut_start) || ptr >= (scp->mouse_cut_start > scp->mouse_cut_end ? scp->mouse_cut_start : scp->mouse_cut_end)) { if (ptr != scp->cursor_pos) nch = (och & 0xff) | (*ptr & 0xff00); } else { /* are we clear of the cursor image ? */ if (ptr != scp->cursor_pos) nch = (och & 0x88ff) | (*ptr & 0x7000)>>4 | (*ptr & 0x0700)<<4; else { if (sc_flags & CHAR_CURSOR) nch = (och & 0x88ff)|(*ptr & 0x7000)>>4|(*ptr & 0x0700)<<4; else if (!(sc_flags & BLINK_CURSOR)) nch = (och & 0xff) | (*ptr & 0xff00); } } if (nch != och) writew(crt_pos + 2*(ptr - scp->scr_buf), nch); } } static void remove_cutmarking(scr_stat *scp) { scp->mouse_cut_start = scp->mouse_cut_end = NULL; scp->status &= ~MOUSE_CUTTING; mark_all(scp); } static void do_bell(scr_stat *scp, int pitch, int duration) { if (cold || shutdown_in_progress) return; if (scp != cur_console && (sc_flags & QUIET_BELL)) return; if (sc_flags & VISUAL_BELL) { if (blink_in_progress) return; blink_in_progress = 4; if (scp != cur_console) blink_in_progress += 2; blink_screen(cur_console); } else { if (scp != cur_console) pitch *= 2; sysbeep(pitch, duration); } } static void blink_screen(void *arg) { scr_stat *scp = arg; if (!ISTEXTSC(scp) || (blink_in_progress <= 1)) { blink_in_progress = FALSE; mark_all(scp); if (delayed_next_scr) switch_scr(scp, delayed_next_scr - 1); } else { if (blink_in_progress & 1) fillw_io(kernel_default.std_color | scr_map[0x20], scp->adp->va_window, scp->xsize * scp->ysize); else fillw_io(kernel_default.rev_color | scr_map[0x20], scp->adp->va_window, scp->xsize * scp->ysize); blink_in_progress--; timeout(blink_screen, scp, hz / 10); } } void sc_bcopy(scr_stat *scp, u_short *p, int from, int to, int mark) { u_char *font; vm_offset_t d; vm_offset_t e; u_char *f; int font_size; int line_length; int xsize; u_short bg; int i, j; u_char c; if (ISTEXTSC(scp)) { bcopy_toio(p + from, scp->adp->va_window + 2*from, (to - from + 1)*sizeof(u_short)); } else /* if ISPIXELSC(scp) */ { if (mark) mark = 255; font_size = scp->font_size; if (font_size < 14) font = font_8; else if (font_size >= 16) font = font_16; else font = font_14; line_length = scp->xpixel/8; xsize = scp->xsize; d = scp->adp->va_window + scp->xoff + scp->yoff*font_size*line_length + (from%xsize) + font_size*line_length*(from/xsize); outw(GDCIDX, 0x0005); /* read mode 0, write mode 0 */ outw(GDCIDX, 0x0003); /* data rotate/function select */ outw(GDCIDX, 0x0f01); /* set/reset enable */ bg = -1; for (i = from ; i <= to ; i++) { /* set background color in EGA/VGA latch */ if (bg != (p[i] & 0xf000)) { bg = (p[i] & 0xf000); outw(GDCIDX, (bg >> 4) | 0x00); /* set/reset */ outw(GDCIDX, 0xff08); /* bit mask */ writeb(d, 0); c = readb(d); /* set the background color in the latch */ } /* foreground color */ outw(GDCIDX, (p[i] & 0x0f00) | 0x00); /* set/reset */ e = d; f = &font[(p[i] & 0x00ff)*font_size]; for (j = 0 ; j < font_size; j++, f++) { outw(GDCIDX, ((*f^mark) << 8) | 0x08); /* bit mask */ writeb(e, 0); e += line_length; } d++; if ((i % xsize) == xsize - 1) d += scp->xoff*2 + (font_size - 1)*line_length; } outw(GDCIDX, 0x0000); /* set/reset */ outw(GDCIDX, 0x0001); /* set/reset enable */ outw(GDCIDX, 0xff08); /* bit mask */ #if 0 /* VGA only */ outw(GDCIDX, 0x0305); /* read mode 0, write mode 3 */ outw(GDCIDX, 0x0003); /* data rotate/function select */ outw(GDCIDX, 0x0f01); /* set/reset enable */ outw(GDCIDX, 0xff08); /* bit mask */ bg = -1; for (i = from ; i <= to ; i++) { /* set background color in EGA/VGA latch */ if (bg != (p[i] & 0xf000)) { bg = (p[i] & 0xf000); outw(GDCIDX, 0x0005); /* read mode 0, write mode 0 */ outw(GDCIDX, (bg >> 4) | 0x00); /* set/reset */ *d = 0; c = *d; /* set the background color in the latch */ outw(GDCIDX, 0x0305); /* read mode 0, write mode 3 */ } /* foreground color */ outw(GDCIDX, (p[i] & 0x0f00) | 0x00); /* set/reset */ e = (u_char *)d; f = &font[(p[i] & 0x00ff)*font_size]; for (j = 0 ; j < font_size; j++, f++) { *e = *f^mark; e += line_length; } d++; if ((i % xsize) == xsize - 1) d += scp->xoff*2 + (font_size - 1)*line_length; } outw(GDCIDX, 0x0005); /* read mode 0, write mode 0 */ outw(GDCIDX, 0x0000); /* set/reset */ outw(GDCIDX, 0x0001); /* set/reset enable */ #endif /* 0 */ } } #endif /* NSC */ Index: head/sys/dev/usb/ohci.c =================================================================== --- head/sys/dev/usb/ohci.c (revision 45719) +++ head/sys/dev/usb/ohci.c (revision 45720) @@ -1,2238 +1,2236 @@ /* $NetBSD: ohci.c,v 1.27 1999/01/13 10:33:53 augustss Exp $ */ /* $FreeBSD$ */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (augustss@carlstedt.se) at * Carlstedt Research & Technology. * * 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 NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */ /* * USB Open Host Controller driver. * * OHCI spec: http://www.intel.com/design/usb/ohci11d.pdf * USB spec: http://www.teleport.com/cgi-bin/mailmerge.cgi/~usb/cgiform.tpl */ #include #include #include #include #if defined(__NetBSD__) #include #elif defined(__FreeBSD__) #include #include #endif #include #include #include +#ifdef __FreeBSD__ +#include +#include +#endif #include #include #include #include #include #include #include #include #include #if defined(__FreeBSD__) #include #define delay(d) DELAY(d) #endif #ifdef OHCI_DEBUG #define DPRINTF(x) if (ohcidebug) logprintf x #define DPRINTFN(n,x) if (ohcidebug>(n)) logprintf x int ohcidebug = 1; #else #define DPRINTF(x) #define DPRINTFN(n,x) #endif /* * The OHCI controller is little endian, so on big endian machines * the data strored in memory needs to be swapped. */ #if BYTE_ORDER == BIG_ENDIAN #define LE(x) (bswap32(x)) #else #define LE(x) (x) #endif struct ohci_pipe; ohci_soft_ed_t *ohci_alloc_sed __P((ohci_softc_t *)); void ohci_free_sed __P((ohci_softc_t *, ohci_soft_ed_t *)); ohci_soft_td_t *ohci_alloc_std __P((ohci_softc_t *)); void ohci_free_std __P((ohci_softc_t *, ohci_soft_td_t *)); usbd_status ohci_open __P((usbd_pipe_handle)); void ohci_poll __P((struct usbd_bus *)); void ohci_waitintr __P((ohci_softc_t *, usbd_request_handle)); void ohci_rhsc __P((ohci_softc_t *, usbd_request_handle)); void ohci_process_done __P((ohci_softc_t *, ohci_physaddr_t)); void ohci_ii_done __P((ohci_softc_t *, usbd_request_handle)); void ohci_ctrl_done __P((ohci_softc_t *, usbd_request_handle)); void ohci_intr_done __P((ohci_softc_t *, usbd_request_handle)); void ohci_bulk_done __P((ohci_softc_t *, usbd_request_handle)); usbd_status ohci_device_request __P((usbd_request_handle reqh)); void ohci_add_ed __P((ohci_soft_ed_t *, ohci_soft_ed_t *)); void ohci_rem_ed __P((ohci_soft_ed_t *, ohci_soft_ed_t *)); void ohci_hash_add_td __P((ohci_softc_t *, ohci_soft_td_t *)); void ohci_hash_rem_td __P((ohci_softc_t *, ohci_soft_td_t *)); ohci_soft_td_t *ohci_hash_find_td __P((ohci_softc_t *, ohci_physaddr_t)); usbd_status ohci_root_ctrl_transfer __P((usbd_request_handle)); usbd_status ohci_root_ctrl_start __P((usbd_request_handle)); void ohci_root_ctrl_abort __P((usbd_request_handle)); void ohci_root_ctrl_close __P((usbd_pipe_handle)); usbd_status ohci_root_intr_transfer __P((usbd_request_handle)); usbd_status ohci_root_intr_start __P((usbd_request_handle)); void ohci_root_intr_abort __P((usbd_request_handle)); void ohci_root_intr_close __P((usbd_pipe_handle)); usbd_status ohci_device_ctrl_transfer __P((usbd_request_handle)); usbd_status ohci_device_ctrl_start __P((usbd_request_handle)); void ohci_device_ctrl_abort __P((usbd_request_handle)); void ohci_device_ctrl_close __P((usbd_pipe_handle)); usbd_status ohci_device_bulk_transfer __P((usbd_request_handle)); usbd_status ohci_device_bulk_start __P((usbd_request_handle)); void ohci_device_bulk_abort __P((usbd_request_handle)); void ohci_device_bulk_close __P((usbd_pipe_handle)); usbd_status ohci_device_intr_transfer __P((usbd_request_handle)); usbd_status ohci_device_intr_start __P((usbd_request_handle)); void ohci_device_intr_abort __P((usbd_request_handle)); void ohci_device_intr_close __P((usbd_pipe_handle)); usbd_status ohci_device_setintr __P((ohci_softc_t *sc, struct ohci_pipe *pipe, int ival)); int ohci_str __P((usb_string_descriptor_t *, int, char *)); void ohci_timeout __P((void *)); void ohci_rhsc_able __P((ohci_softc_t *, int)); #ifdef UHCI_DEBUG ohci_softc_t *thesc; void ohci_dumpregs __P((ohci_softc_t *)); void ohci_dump_tds __P((ohci_soft_td_t *)); void ohci_dump_td __P((ohci_soft_td_t *)); void ohci_dump_ed __P((ohci_soft_ed_t *)); #endif -#if defined(__NetBSD__) #define OWRITE4(sc, r, x) bus_space_write_4((sc)->iot, (sc)->ioh, (r), (x)) #define OREAD4(sc, r) bus_space_read_4((sc)->iot, (sc)->ioh, (r)) #define OREAD2(sc, r) bus_space_read_2((sc)->iot, (sc)->ioh, (r)) -#elif defined(__FreeBSD__) -#define OWRITE4(sc, r, x) *(u_int32_t *) ((sc)->sc_iobase + (r)) = x -#define OREAD4(sc, r) (*(u_int32_t *) ((sc)->sc_iobase + (r))) -#define OREAD2(sc, r) (*(u_int16_t *) ((sc)->sc_iobase + (r))) -#endif /* Reverse the bits in a value 0 .. 31 */ static u_int8_t revbits[OHCI_NO_INTRS] = { 0x00, 0x10, 0x08, 0x18, 0x04, 0x14, 0x0c, 0x1c, 0x02, 0x12, 0x0a, 0x1a, 0x06, 0x16, 0x0e, 0x1e, 0x01, 0x11, 0x09, 0x19, 0x05, 0x15, 0x0d, 0x1d, 0x03, 0x13, 0x0b, 0x1b, 0x07, 0x17, 0x0f, 0x1f }; struct ohci_pipe { struct usbd_pipe pipe; ohci_soft_ed_t *sed; ohci_soft_td_t *tail; /* Info needed for different pipe kinds. */ union { /* Control pipe */ struct { usb_dma_t datadma; usb_dma_t reqdma; u_int length; ohci_soft_td_t *setup, *xfer, *stat; } ctl; /* Interrupt pipe */ struct { usb_dma_t datadma; int nslots; int pos; } intr; /* Bulk pipe */ struct { usb_dma_t datadma; u_int length; } bulk; } u; }; #define OHCI_INTR_ENDPT 1 struct usbd_methods ohci_root_ctrl_methods = { ohci_root_ctrl_transfer, ohci_root_ctrl_start, ohci_root_ctrl_abort, ohci_root_ctrl_close, 0, }; struct usbd_methods ohci_root_intr_methods = { ohci_root_intr_transfer, ohci_root_intr_start, ohci_root_intr_abort, ohci_root_intr_close, 0, }; struct usbd_methods ohci_device_ctrl_methods = { ohci_device_ctrl_transfer, ohci_device_ctrl_start, ohci_device_ctrl_abort, ohci_device_ctrl_close, 0, }; struct usbd_methods ohci_device_intr_methods = { ohci_device_intr_transfer, ohci_device_intr_start, ohci_device_intr_abort, ohci_device_intr_close, }; struct usbd_methods ohci_device_bulk_methods = { ohci_device_bulk_transfer, ohci_device_bulk_start, ohci_device_bulk_abort, ohci_device_bulk_close, 0, }; ohci_soft_ed_t * ohci_alloc_sed(sc) ohci_softc_t *sc; { ohci_soft_ed_t *sed; usbd_status r; int i, offs; usb_dma_t dma; if (!sc->sc_freeeds) { DPRINTFN(2, ("ohci_alloc_sed: allocating chunk\n")); sed = malloc(sizeof(ohci_soft_ed_t) * OHCI_ED_CHUNK, M_USBDEV, M_NOWAIT); if (!sed) return 0; r = usb_allocmem(sc->sc_dmatag, OHCI_ED_SIZE * OHCI_ED_CHUNK, OHCI_ED_ALIGN, &dma); if (r != USBD_NORMAL_COMPLETION) { free(sed, M_USBDEV); return 0; } for(i = 0; i < OHCI_ED_CHUNK; i++, sed++) { offs = i * OHCI_ED_SIZE; sed->physaddr = DMAADDR(&dma) + offs; sed->ed = (ohci_ed_t *) ((char *)KERNADDR(&dma) + offs); sed->next = sc->sc_freeeds; sc->sc_freeeds = sed; } } sed = sc->sc_freeeds; sc->sc_freeeds = sed->next; memset(sed->ed, 0, OHCI_ED_SIZE); sed->next = 0; return sed; } void ohci_free_sed(sc, sed) ohci_softc_t *sc; ohci_soft_ed_t *sed; { sed->next = sc->sc_freeeds; sc->sc_freeeds = sed; } ohci_soft_td_t * ohci_alloc_std(sc) ohci_softc_t *sc; { ohci_soft_td_t *std; usbd_status r; int i, offs; usb_dma_t dma; if (!sc->sc_freetds) { DPRINTFN(2, ("ohci_alloc_std: allocating chunk\n")); std = malloc(sizeof(ohci_soft_td_t) * OHCI_TD_CHUNK, M_USBDEV, M_NOWAIT); if (!std) return 0; r = usb_allocmem(sc->sc_dmatag, OHCI_TD_SIZE * OHCI_TD_CHUNK, OHCI_TD_ALIGN, &dma); if (r != USBD_NORMAL_COMPLETION) { free(std, M_USBDEV); return 0; } for(i = 0; i < OHCI_TD_CHUNK; i++, std++) { offs = i * OHCI_TD_SIZE; std->physaddr = DMAADDR(&dma) + offs; std->td = (ohci_td_t *) ((char *)KERNADDR(&dma) + offs); std->nexttd = sc->sc_freetds; sc->sc_freetds = std; } } std = sc->sc_freetds; sc->sc_freetds = std->nexttd; memset(std->td, 0, OHCI_TD_SIZE); std->nexttd = 0; return (std); } void ohci_free_std(sc, std) ohci_softc_t *sc; ohci_soft_td_t *std; { std->nexttd = sc->sc_freetds; sc->sc_freetds = std; } usbd_status ohci_init(sc) ohci_softc_t *sc; { ohci_soft_ed_t *sed, *psed; usbd_status r; int rev; int i; u_int32_t s, ctl, ival, hcr, fm, per; DPRINTF(("ohci_init: start\n")); rev = OREAD4(sc, OHCI_REVISION); printf("%s: OHCI version %d.%d%s\n", USBDEVNAME(sc->sc_bus.bdev), OHCI_REV_HI(rev), OHCI_REV_LO(rev), OHCI_REV_LEGACY(rev) ? ", legacy support" : ""); if (OHCI_REV_HI(rev) != 1 || OHCI_REV_LO(rev) != 0) { printf("%s: unsupported OHCI revision\n", USBDEVNAME(sc->sc_bus.bdev)); return (USBD_INVAL); } for (i = 0; i < OHCI_HASH_SIZE; i++) LIST_INIT(&sc->sc_hash_tds[i]); /* Allocate the HCCA area. */ r = usb_allocmem(sc->sc_dmatag, OHCI_HCCA_SIZE, OHCI_HCCA_ALIGN, &sc->sc_hccadma); if (r != USBD_NORMAL_COMPLETION) return (r); sc->sc_hcca = (struct ohci_hcca *)KERNADDR(&sc->sc_hccadma); memset(sc->sc_hcca, 0, OHCI_HCCA_SIZE); sc->sc_eintrs = OHCI_NORMAL_INTRS; sc->sc_ctrl_head = ohci_alloc_sed(sc); if (!sc->sc_ctrl_head) { r = USBD_NOMEM; goto bad1; } sc->sc_ctrl_head->ed->ed_flags |= LE(OHCI_ED_SKIP); sc->sc_bulk_head = ohci_alloc_sed(sc); if (!sc->sc_bulk_head) { r = USBD_NOMEM; goto bad2; } sc->sc_bulk_head->ed->ed_flags |= LE(OHCI_ED_SKIP); /* Allocate all the dummy EDs that make up the interrupt tree. */ for (i = 0; i < OHCI_NO_EDS; i++) { sed = ohci_alloc_sed(sc); if (!sed) { while (--i >= 0) ohci_free_sed(sc, sc->sc_eds[i]); r = USBD_NOMEM; goto bad3; } /* All ED fields are set to 0. */ sc->sc_eds[i] = sed; sed->ed->ed_flags |= LE(OHCI_ED_SKIP); if (i != 0) { psed = sc->sc_eds[(i-1) / 2]; sed->next = psed; sed->ed->ed_nexted = LE(psed->physaddr); } } /* * Fill HCCA interrupt table. The bit reversal is to get * the tree set up properly to spread the interrupts. */ for (i = 0; i < OHCI_NO_INTRS; i++) sc->sc_hcca->hcca_interrupt_table[revbits[i]] = LE(sc->sc_eds[OHCI_NO_EDS-OHCI_NO_INTRS+i]->physaddr); /* Determine in what context we are running. */ ctl = OREAD4(sc, OHCI_CONTROL); if (ctl & OHCI_IR) { /* SMM active, request change */ DPRINTF(("ohci_init: SMM active, request owner change\n")); s = OREAD4(sc, OHCI_COMMAND_STATUS); OWRITE4(sc, OHCI_COMMAND_STATUS, s | OHCI_OCR); for (i = 0; i < 100 && (ctl & OHCI_IR); i++) { delay(1000); ctl = OREAD4(sc, OHCI_CONTROL); } if ((ctl & OHCI_IR) == 0) { printf("%s: SMM does not respond, resetting\n", USBDEVNAME(sc->sc_bus.bdev)); OWRITE4(sc, OHCI_CONTROL, OHCI_HCFS_RESET); goto reset; } } else if ((ctl & OHCI_HCFS_MASK) != OHCI_HCFS_RESET) { /* BIOS started controller. */ DPRINTF(("ohci_init: BIOS active\n")); if ((ctl & OHCI_HCFS_MASK) != OHCI_HCFS_OPERATIONAL) { OWRITE4(sc, OHCI_CONTROL, OHCI_HCFS_OPERATIONAL); delay(USB_RESUME_DELAY * 1000); } } else { DPRINTF(("ohci_init: cold started\n")); reset: /* Controller was cold started. */ delay(USB_BUS_RESET_DELAY * 1000); } /* * This reset should not be necessary according to the OHCI spec, but * without it some controllers do not start. */ DPRINTF(("%s: resetting\n", USBDEVNAME(sc->sc_bus.bdev))); OWRITE4(sc, OHCI_CONTROL, OHCI_HCFS_RESET); delay(USB_BUS_RESET_DELAY * 1000); /* We now own the host controller and the bus has been reset. */ ival = OHCI_GET_IVAL(OREAD4(sc, OHCI_FM_INTERVAL)); OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_HCR); /* Reset HC */ /* Nominal time for a reset is 10 us. */ for (i = 0; i < 10; i++) { delay(10); hcr = OREAD4(sc, OHCI_COMMAND_STATUS) & OHCI_HCR; if (!hcr) break; } if (hcr) { printf("%s: reset timeout\n", USBDEVNAME(sc->sc_bus.bdev)); r = USBD_IOERROR; goto bad3; } #ifdef UHCI_DEBUG thesc = sc; if (ohcidebug > 15) ohci_dumpregs(sc); #endif /* The controller is now in suspend state, we have 2ms to finish. */ /* Set up HC registers. */ OWRITE4(sc, OHCI_HCCA, DMAADDR(&sc->sc_hccadma)); OWRITE4(sc, OHCI_CONTROL_HEAD_ED, sc->sc_ctrl_head->physaddr); OWRITE4(sc, OHCI_BULK_HEAD_ED, sc->sc_bulk_head->physaddr); OWRITE4(sc, OHCI_INTERRUPT_DISABLE, OHCI_ALL_INTRS); OWRITE4(sc, OHCI_INTERRUPT_ENABLE, sc->sc_eintrs | OHCI_MIE); ctl = OREAD4(sc, OHCI_CONTROL); ctl &= ~(OHCI_CBSR_MASK | OHCI_LES | OHCI_HCFS_MASK | OHCI_IR); ctl |= OHCI_PLE | OHCI_IE | OHCI_CLE | OHCI_BLE | OHCI_RATIO_1_4 | OHCI_HCFS_OPERATIONAL; /* And finally start it! */ OWRITE4(sc, OHCI_CONTROL, ctl); /* * The controller is now OPERATIONAL. Set a some final * registers that should be set earlier, but that the * controller ignores when in the SUSPEND state. */ fm = (OREAD4(sc, OHCI_FM_INTERVAL) & OHCI_FIT) ^ OHCI_FIT; fm |= OHCI_FSMPS(ival) | ival; OWRITE4(sc, OHCI_FM_INTERVAL, fm); per = OHCI_PERIODIC(ival); /* 90% periodic */ OWRITE4(sc, OHCI_PERIODIC_START, per); OWRITE4(sc, OHCI_RH_STATUS, OHCI_LPSC); /* Enable port power */ sc->sc_noport = OHCI_GET_NDP(OREAD4(sc, OHCI_RH_DESCRIPTOR_A)); #ifdef UHCI_DEBUG if (ohcidebug > 5) ohci_dumpregs(sc); #endif /* Set up the bus struct. */ sc->sc_bus.open_pipe = ohci_open; sc->sc_bus.pipe_size = sizeof(struct ohci_pipe); sc->sc_bus.do_poll = ohci_poll; return (USBD_NORMAL_COMPLETION); bad3: ohci_free_sed(sc, sc->sc_ctrl_head); bad2: ohci_free_sed(sc, sc->sc_bulk_head); bad1: usb_freemem(sc->sc_dmatag, &sc->sc_hccadma); return (r); } #ifdef UHCI_DEBUG void ohcidump(void); void ohcidump(void) { ohci_dumpregs(thesc); } void ohci_dumpregs(sc) ohci_softc_t *sc; { printf("ohci_dumpregs: rev=0x%08x control=0x%08x command=0x%08x\n", OREAD4(sc, OHCI_REVISION), OREAD4(sc, OHCI_CONTROL), OREAD4(sc, OHCI_COMMAND_STATUS)); printf(" intrstat=0x%08x intre=0x%08x intrd=0x%08x\n", OREAD4(sc, OHCI_INTERRUPT_STATUS), OREAD4(sc, OHCI_INTERRUPT_ENABLE), OREAD4(sc, OHCI_INTERRUPT_DISABLE)); printf(" hcca=0x%08x percur=0x%08x ctrlhd=0x%08x\n", OREAD4(sc, OHCI_HCCA), OREAD4(sc, OHCI_PERIOD_CURRENT_ED), OREAD4(sc, OHCI_CONTROL_HEAD_ED)); printf(" ctrlcur=0x%08x bulkhd=0x%08x bulkcur=0x%08x\n", OREAD4(sc, OHCI_CONTROL_CURRENT_ED), OREAD4(sc, OHCI_BULK_HEAD_ED), OREAD4(sc, OHCI_BULK_CURRENT_ED)); printf(" done=0x%08x fmival=0x%08x fmrem=0x%08x\n", OREAD4(sc, OHCI_DONE_HEAD), OREAD4(sc, OHCI_FM_INTERVAL), OREAD4(sc, OHCI_FM_REMAINING)); printf(" fmnum=0x%08x perst=0x%08x lsthrs=0x%08x\n", OREAD4(sc, OHCI_FM_NUMBER), OREAD4(sc, OHCI_PERIODIC_START), OREAD4(sc, OHCI_LS_THRESHOLD)); printf(" desca=0x%08x descb=0x%08x stat=0x%08x\n", OREAD4(sc, OHCI_RH_DESCRIPTOR_A), OREAD4(sc, OHCI_RH_DESCRIPTOR_B), OREAD4(sc, OHCI_RH_STATUS)); printf(" port1=0x%08x port2=0x%08x\n", OREAD4(sc, OHCI_RH_PORT_STATUS(1)), OREAD4(sc, OHCI_RH_PORT_STATUS(2))); printf(" HCCA: frame_number=0x%04x done_head=0x%08x\n", LE(sc->sc_hcca->hcca_frame_number), LE(sc->sc_hcca->hcca_done_head)); } #endif int ohci_intr(p) void *p; { ohci_softc_t *sc = p; u_int32_t intrs, eintrs; ohci_physaddr_t done; /* In case the interrupt occurs before initialization has completed. */ if (sc == NULL || sc->sc_hcca == NULL) { /* NWH added sc==0 */ #ifdef DIAGNOSTIC printf("ohci_intr: sc->sc_hcca == NULL\n"); #endif return (0); } intrs = 0; done = LE(sc->sc_hcca->hcca_done_head); if (done != 0) { sc->sc_hcca->hcca_done_head = 0; if (done & ~OHCI_DONE_INTRS) intrs = OHCI_WDH; if (done & OHCI_DONE_INTRS) intrs |= OREAD4(sc, OHCI_INTERRUPT_STATUS); } else intrs = OREAD4(sc, OHCI_INTERRUPT_STATUS); if (!intrs) return (0); intrs &= ~OHCI_MIE; OWRITE4(sc, OHCI_INTERRUPT_STATUS, intrs); /* Acknowledge */ eintrs = intrs & sc->sc_eintrs; if (!eintrs) return (0); sc->sc_intrs++; DPRINTFN(7, ("ohci_intr: sc=%p intrs=%x(%x) eintr=%x\n", sc, (u_int)intrs, OREAD4(sc, OHCI_INTERRUPT_STATUS), (u_int)eintrs)); if (eintrs & OHCI_SO) { printf("%s: scheduling overrun\n",USBDEVNAME(sc->sc_bus.bdev)); /* XXX do what */ intrs &= ~OHCI_SO; } if (eintrs & OHCI_WDH) { ohci_process_done(sc, done &~ OHCI_DONE_INTRS); intrs &= ~OHCI_WDH; } if (eintrs & OHCI_RD) { /* XXX process resume detect */ } if (eintrs & OHCI_UE) { printf("%s: unrecoverable error, controller halted\n", USBDEVNAME(sc->sc_bus.bdev)); OWRITE4(sc, OHCI_CONTROL, OHCI_HCFS_RESET); /* XXX what else */ } if (eintrs & OHCI_RHSC) { ohci_rhsc(sc, sc->sc_intrreqh); intrs &= ~OHCI_RHSC; /* * Disable RHSC interrupt for now, because it will be * on until the port has been reset. */ ohci_rhsc_able(sc, 0); } /* Block unprocessed interrupts. XXX */ OWRITE4(sc, OHCI_INTERRUPT_DISABLE, intrs); sc->sc_eintrs &= ~intrs; return (1); } void ohci_rhsc_able(sc, on) ohci_softc_t *sc; int on; { DPRINTFN(4, ("ohci_rhsc_able: on=%d\n", on)); if (on) { sc->sc_eintrs |= OHCI_RHSC; OWRITE4(sc, OHCI_INTERRUPT_ENABLE, OHCI_RHSC); } else { sc->sc_eintrs &= ~OHCI_RHSC; OWRITE4(sc, OHCI_INTERRUPT_DISABLE, OHCI_RHSC); } } #ifdef UHCI_DEBUG char *ohci_cc_strs[] = { "NO_ERROR", "CRC", "BIT_STUFFING", "DATA_TOGGLE_MISMATCH", "STALL", "DEVICE_NOT_RESPONDING", "PID_CHECK_FAILURE", "UNEXPECTED_PID", "DATA_OVERRUN", "DATA_UNDERRUN", "BUFFER_OVERRUN", "BUFFER_UNDERRUN", "NOT_ACCESSED", }; #endif void ohci_process_done(sc, done) ohci_softc_t *sc; ohci_physaddr_t done; { ohci_soft_td_t *std, *sdone; usbd_request_handle reqh; int len, cc; DPRINTFN(10,("ohci_process_done: done=0x%08lx\n", (u_long)done)); /* Reverse the done list. */ for (sdone = 0; done; done = LE(std->td->td_nexttd)) { std = ohci_hash_find_td(sc, done); std->dnext = sdone; sdone = std; } #ifdef UHCI_DEBUG if (ohcidebug > 10) { printf("ohci_process_done: TD done:\n"); ohci_dump_tds(sdone); } #endif for (std = sdone; std; std = std->dnext) { reqh = std->reqh; DPRINTFN(10, ("ohci_process_done: std=%p reqh=%p hcpriv=%p\n", std, reqh, reqh->hcpriv)); cc = OHCI_TD_GET_CC(LE(std->td->td_flags)); if (cc == OHCI_CC_NO_ERROR) { if (std->td->td_cbp == 0) len = std->len; else len = LE(std->td->td_be) - LE(std->td->td_cbp) + 1; /* * Only do a callback on the last stage of a transfer. * Others have hcpriv = 0. */ if ((reqh->pipe->endpoint->edesc->bmAttributes & UE_XFERTYPE) == UE_CONTROL) { /* For a control transfer the length is in * the xfer stage */ if (reqh->hcpriv == std) { reqh->status = USBD_NORMAL_COMPLETION; ohci_ii_done(sc, reqh); } else reqh->actlen = len; } else { if (reqh->hcpriv == std) { reqh->actlen = len; reqh->status = USBD_NORMAL_COMPLETION; ohci_ii_done(sc, reqh); } } } else { ohci_soft_td_t *p, *n; struct ohci_pipe *opipe = (struct ohci_pipe *)reqh->pipe; DPRINTFN(-1,("ohci_process_done: error cc=%d (%s)\n", OHCI_TD_GET_CC(LE(std->td->td_flags)), ohci_cc_strs[OHCI_TD_GET_CC(LE(std->td->td_flags))])); /* * Endpoint is halted. First unlink all the TDs * belonging to the failed transfer, and then restart * the endpoint. */ for (p = std->nexttd; p->reqh == reqh; p = n) { n = p->nexttd; ohci_hash_rem_td(sc, p); ohci_free_std(sc, p); } /* clear halt */ opipe->sed->ed->ed_headp = LE(p->physaddr); OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_CLF); if (cc == OHCI_CC_STALL) reqh->status = USBD_STALLED; else reqh->status = USBD_IOERROR; ohci_ii_done(sc, reqh); } ohci_hash_rem_td(sc, std); ohci_free_std(sc, std); } } void ohci_ii_done(sc, reqh) ohci_softc_t *sc; usbd_request_handle reqh; { switch (reqh->pipe->endpoint->edesc->bmAttributes & UE_XFERTYPE) { case UE_CONTROL: ohci_ctrl_done(sc, reqh); usb_start_next(reqh->pipe); break; case UE_INTERRUPT: ohci_intr_done(sc, reqh); break; case UE_BULK: ohci_bulk_done(sc, reqh); usb_start_next(reqh->pipe); break; case UE_ISOCHRONOUS: printf("ohci_process_done: ISO done?\n"); usb_start_next(reqh->pipe); break; } /* And finally execute callback. */ reqh->xfercb(reqh); } void ohci_ctrl_done(sc, reqh) ohci_softc_t *sc; usbd_request_handle reqh; { struct ohci_pipe *opipe = (struct ohci_pipe *)reqh->pipe; u_int len = opipe->u.ctl.length; usb_dma_t *dma; DPRINTFN(10,("ohci_ctrl_done: reqh=%p\n", reqh)); if (!reqh->isreq) { panic("ohci_ctrl_done: not a request\n"); return; } if (len != 0) { dma = &opipe->u.ctl.datadma; if (reqh->request.bmRequestType & UT_READ) memcpy(reqh->buffer, KERNADDR(dma), len); usb_freemem(sc->sc_dmatag, dma); } usb_untimeout(ohci_timeout, reqh, reqh->timo_handle); } void ohci_intr_done(sc, reqh) ohci_softc_t *sc; usbd_request_handle reqh; { struct ohci_pipe *opipe = (struct ohci_pipe *)reqh->pipe; usb_dma_t *dma; ohci_soft_ed_t *sed = opipe->sed; ohci_soft_td_t *xfer, *tail; DPRINTFN(10,("ohci_intr_done: reqh=%p, actlen=%d\n", reqh, reqh->actlen)); dma = &opipe->u.intr.datadma; memcpy(reqh->buffer, KERNADDR(dma), reqh->actlen); if (reqh->pipe->intrreqh == reqh) { xfer = opipe->tail; tail = ohci_alloc_std(sc); /* XXX should reuse TD */ if (!tail) { reqh->status = USBD_NOMEM; return; } tail->reqh = 0; xfer->td->td_flags = LE( OHCI_TD_IN | OHCI_TD_NOCC | OHCI_TD_SET_DI(1) | OHCI_TD_TOGGLE_CARRY); if (reqh->flags & USBD_SHORT_XFER_OK) xfer->td->td_flags |= LE(OHCI_TD_R); xfer->td->td_cbp = LE(DMAADDR(dma)); xfer->nexttd = tail; xfer->td->td_nexttd = LE(tail->physaddr); xfer->td->td_be = LE(LE(xfer->td->td_cbp) + reqh->length - 1); xfer->len = reqh->length; xfer->reqh = reqh; reqh->hcpriv = xfer; ohci_hash_add_td(sc, xfer); sed->ed->ed_tailp = LE(tail->physaddr); opipe->tail = tail; } else { usb_freemem(sc->sc_dmatag, dma); usb_start_next(reqh->pipe); } } void ohci_bulk_done(sc, reqh) ohci_softc_t *sc; usbd_request_handle reqh; { struct ohci_pipe *opipe = (struct ohci_pipe *)reqh->pipe; usb_dma_t *dma; DPRINTFN(10,("ohci_bulk_done: reqh=%p, actlen=%d\n", reqh, reqh->actlen)); dma = &opipe->u.bulk.datadma; if (reqh->request.bmRequestType & UT_READ) memcpy(reqh->buffer, KERNADDR(dma), reqh->actlen); usb_freemem(sc->sc_dmatag, dma); usb_untimeout(ohci_timeout, reqh, reqh->timo_handle); } void ohci_rhsc(sc, reqh) ohci_softc_t *sc; usbd_request_handle reqh; { usbd_pipe_handle pipe; struct ohci_pipe *opipe; u_char *p; int i, m; int hstatus; hstatus = OREAD4(sc, OHCI_RH_STATUS); DPRINTF(("ohci_rhsc: sc=%p reqh=%p hstatus=0x%08x\n", sc, reqh, hstatus)); if (reqh == 0) { /* Just ignore the change. */ return; } pipe = reqh->pipe; opipe = (struct ohci_pipe *)pipe; p = KERNADDR(&opipe->u.intr.datadma); m = min(sc->sc_noport, reqh->length * 8 - 1); memset(p, 0, reqh->length); for (i = 1; i <= m; i++) { if (OREAD4(sc, OHCI_RH_PORT_STATUS(i)) >> 16) p[i/8] |= 1 << (i%8); } DPRINTF(("ohci_rhsc: change=0x%02x\n", *p)); reqh->actlen = reqh->length; reqh->status = USBD_NORMAL_COMPLETION; reqh->xfercb(reqh); if (reqh->pipe->intrreqh != reqh) { sc->sc_intrreqh = 0; usb_freemem(sc->sc_dmatag, &opipe->u.intr.datadma); usb_start_next(reqh->pipe); } } /* * Wait here until controller claims to have an interrupt. * Then call ohci_intr and return. Use timeout to avoid waiting * too long. */ void ohci_waitintr(sc, reqh) ohci_softc_t *sc; usbd_request_handle reqh; { int timo = reqh->timeout; int usecs; u_int32_t intrs; reqh->status = USBD_IN_PROGRESS; for (usecs = timo * 1000000 / hz; usecs > 0; usecs -= 1000) { usb_delay_ms(&sc->sc_bus, 1); intrs = OREAD4(sc, OHCI_INTERRUPT_STATUS) & sc->sc_eintrs; DPRINTFN(15,("ohci_waitintr: 0x%04x\n", intrs)); #ifdef UHCI_DEBUG if (ohcidebug > 15) ohci_dumpregs(sc); #endif if (intrs) { ohci_intr(sc); if (reqh->status != USBD_IN_PROGRESS) return; } } /* Timeout */ DPRINTF(("ohci_waitintr: timeout\n")); reqh->status = USBD_TIMEOUT; ohci_ii_done(sc, reqh); /* XXX should free TD */ } void ohci_poll(bus) struct usbd_bus *bus; { ohci_softc_t *sc = (ohci_softc_t *)bus; if (OREAD4(sc, OHCI_INTERRUPT_STATUS) & sc->sc_eintrs) ohci_intr(sc); } usbd_status ohci_device_request(reqh) usbd_request_handle reqh; { struct ohci_pipe *opipe = (struct ohci_pipe *)reqh->pipe; usb_device_request_t *req = &reqh->request; usbd_device_handle dev = opipe->pipe.device; ohci_softc_t *sc = (ohci_softc_t *)dev->bus; int addr = dev->address; ohci_soft_td_t *setup, *xfer = 0, *stat, *next, *tail; ohci_soft_ed_t *sed; usb_dma_t *dmap; int isread; int len; usbd_status r; int s; isread = req->bmRequestType & UT_READ; len = UGETW(req->wLength); DPRINTFN(3,("ohci_device_control type=0x%02x, request=0x%02x, " "wValue=0x%04x, wIndex=0x%04x len=%d, addr=%d, endpt=%d\n", req->bmRequestType, req->bRequest, UGETW(req->wValue), UGETW(req->wIndex), len, addr, opipe->pipe.endpoint->edesc->bEndpointAddress)); setup = opipe->tail; stat = ohci_alloc_std(sc); if (!stat) { r = USBD_NOMEM; goto bad1; } tail = ohci_alloc_std(sc); if (!tail) { r = USBD_NOMEM; goto bad2; } tail->reqh = 0; sed = opipe->sed; dmap = &opipe->u.ctl.datadma; opipe->u.ctl.length = len; /* Update device address and length since they may have changed. */ /* XXX This only needs to be done once, but it's too early in open. */ sed->ed->ed_flags = LE( (LE(sed->ed->ed_flags) & ~(OHCI_ED_ADDRMASK | OHCI_ED_MAXPMASK)) | OHCI_ED_SET_FA(addr) | OHCI_ED_SET_MAXP(UGETW(opipe->pipe.endpoint->edesc->wMaxPacketSize))); /* Set up data transaction */ if (len != 0) { xfer = ohci_alloc_std(sc); if (!xfer) { r = USBD_NOMEM; goto bad3; } r = usb_allocmem(sc->sc_dmatag, len, 0, dmap); if (r != USBD_NORMAL_COMPLETION) goto bad4; xfer->td->td_flags = LE( (isread ? OHCI_TD_IN : OHCI_TD_OUT) | OHCI_TD_NOCC | OHCI_TD_TOGGLE_1 | OHCI_TD_NOINTR | (reqh->flags & USBD_SHORT_XFER_OK ? OHCI_TD_R : 0)); xfer->td->td_cbp = LE(DMAADDR(dmap)); xfer->nexttd = stat; xfer->td->td_nexttd = LE(stat->physaddr); xfer->td->td_be = LE(LE(xfer->td->td_cbp) + len - 1); xfer->len = len; xfer->reqh = reqh; next = xfer; } else next = stat; memcpy(KERNADDR(&opipe->u.ctl.reqdma), req, sizeof *req); if (!isread && len != 0) memcpy(KERNADDR(dmap), reqh->buffer, len); setup->td->td_flags = LE(OHCI_TD_SETUP | OHCI_TD_NOCC | OHCI_TD_TOGGLE_0 | OHCI_TD_NOINTR); setup->td->td_cbp = LE(DMAADDR(&opipe->u.ctl.reqdma)); setup->nexttd = next; setup->td->td_nexttd = LE(next->physaddr); setup->td->td_be = LE(LE(setup->td->td_cbp) + sizeof *req - 1); setup->len = 0; /* XXX The number of byte we count */ setup->reqh = reqh; stat->td->td_flags = LE( (isread ? OHCI_TD_OUT : OHCI_TD_IN) | OHCI_TD_NOCC | OHCI_TD_TOGGLE_1 | OHCI_TD_SET_DI(1)); stat->td->td_cbp = 0; stat->nexttd = tail; stat->td->td_nexttd = LE(tail->physaddr); stat->td->td_be = 0; stat->len = 0; stat->reqh = reqh; reqh->hcpriv = stat; #if UHCI_DEBUG if (ohcidebug > 5) { printf("ohci_device_request:\n"); ohci_dump_ed(sed); ohci_dump_tds(setup); } #endif /* Insert ED in schedule */ s = splusb(); ohci_hash_add_td(sc, setup); if (len != 0) ohci_hash_add_td(sc, xfer); ohci_hash_add_td(sc, stat); sed->ed->ed_tailp = LE(tail->physaddr); opipe->tail = tail; OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_CLF); if (reqh->timeout && !sc->sc_bus.use_polling) { usb_timeout(ohci_timeout, reqh, MS_TO_TICKS(reqh->timeout), reqh->timo_handle); } splx(s); #if UHCI_DEBUG if (ohcidebug > 5) { delay(5000); printf("ohci_device_request: status=%x\n", OREAD4(sc, OHCI_COMMAND_STATUS)); ohci_dump_ed(sed); ohci_dump_tds(setup); } #endif return (USBD_NORMAL_COMPLETION); bad4: ohci_free_std(sc, xfer); bad3: ohci_free_std(sc, tail); bad2: ohci_free_std(sc, stat); bad1: return (r); } /* * Add an ED to the schedule. Called at splusb(). */ void ohci_add_ed(sed, head) ohci_soft_ed_t *sed; ohci_soft_ed_t *head; { sed->next = head->next; sed->ed->ed_nexted = head->ed->ed_nexted; head->next = sed; head->ed->ed_nexted = LE(sed->physaddr); } /* * Remove an ED from the schedule. Called at splusb(). */ void ohci_rem_ed(sed, head) ohci_soft_ed_t *sed; ohci_soft_ed_t *head; { ohci_soft_ed_t *p; /* XXX */ for (p = head; p && p->next != sed; p = p->next) ; if (!p) panic("ohci_rem_ed: ED not found\n"); p->next = sed->next; p->ed->ed_nexted = sed->ed->ed_nexted; } /* * When a transfer is completed the TD is added to the done queue by * the host controller. This queue is the processed by software. * Unfortunately the queue contains the physical address of the TD * and we have no simple way to translate this back to a kernel address. * To make the translation possible (and fast) we use a hash table of * TDs currently in the schedule. The physical address is used as the * hash value. */ #define HASH(a) (((a) >> 4) % OHCI_HASH_SIZE) /* Called at splusb() */ void ohci_hash_add_td(sc, std) ohci_softc_t *sc; ohci_soft_td_t *std; { int h = HASH(std->physaddr); LIST_INSERT_HEAD(&sc->sc_hash_tds[h], std, hnext); } /* Called at splusb() */ void ohci_hash_rem_td(sc, std) ohci_softc_t *sc; ohci_soft_td_t *std; { LIST_REMOVE(std, hnext); } ohci_soft_td_t * ohci_hash_find_td(sc, a) ohci_softc_t *sc; ohci_physaddr_t a; { int h = HASH(a); ohci_soft_td_t *std; for (std = LIST_FIRST(&sc->sc_hash_tds[h]); std != 0; std = LIST_NEXT(std, hnext)) if (std->physaddr == a) return (std); panic("ohci_hash_find_td: addr 0x%08lx not found\n", (u_long)a); } void ohci_timeout(addr) void *addr; { #if 0 usbd_request_handle *reqh = addr; int s; DPRINTF(("ohci_timeout: reqh=%p\n", reqh)); s = splusb(); /* XXX need to inactivate TD before calling interrupt routine */ ohci_XXX_done(reqh); splx(s); #endif } #ifdef UHCI_DEBUG void ohci_dump_tds(std) ohci_soft_td_t *std; { for (; std; std = std->nexttd) ohci_dump_td(std); } void ohci_dump_td(std) ohci_soft_td_t *std; { printf("TD(%p) at %08lx: %b delay=%d ec=%d cc=%d\ncbp=0x%08lx " "nexttd=0x%08lx be=0x%08lx\n", std, (u_long)std->physaddr, (int)LE(std->td->td_flags), "\20\23R\24OUT\25IN\31TOG1\32SETTOGGLE", OHCI_TD_GET_DI(LE(std->td->td_flags)), OHCI_TD_GET_EC(LE(std->td->td_flags)), OHCI_TD_GET_CC(LE(std->td->td_flags)), (u_long)LE(std->td->td_cbp), (u_long)LE(std->td->td_nexttd), (u_long)LE(std->td->td_be)); } void ohci_dump_ed(sed) ohci_soft_ed_t *sed; { printf("ED(%p) at %08lx: addr=%d endpt=%d maxp=%d %b\ntailp=0x%08lx " "headp=%b nexted=0x%08lx\n", sed, (u_long)sed->physaddr, OHCI_ED_GET_FA(LE(sed->ed->ed_flags)), OHCI_ED_GET_EN(LE(sed->ed->ed_flags)), OHCI_ED_GET_MAXP(LE(sed->ed->ed_flags)), (int)LE(sed->ed->ed_flags), "\20\14OUT\15IN\16LOWSPEED\17SKIP\20ISO", (u_long)LE(sed->ed->ed_tailp), (int)LE(sed->ed->ed_headp), "\20\1HALT\2CARRY", (u_long)LE(sed->ed->ed_nexted)); } #endif usbd_status ohci_open(pipe) usbd_pipe_handle pipe; { usbd_device_handle dev = pipe->device; ohci_softc_t *sc = (ohci_softc_t *)dev->bus; usb_endpoint_descriptor_t *ed = pipe->endpoint->edesc; struct ohci_pipe *opipe = (struct ohci_pipe *)pipe; u_int8_t addr = dev->address; ohci_soft_ed_t *sed; ohci_soft_td_t *std; usbd_status r; int s; DPRINTFN(1, ("ohci_open: pipe=%p, addr=%d, endpt=%d (%d)\n", pipe, addr, ed->bEndpointAddress, sc->sc_addr)); if (addr == sc->sc_addr) { switch (ed->bEndpointAddress) { case USB_CONTROL_ENDPOINT: pipe->methods = &ohci_root_ctrl_methods; break; case UE_IN | OHCI_INTR_ENDPT: pipe->methods = &ohci_root_intr_methods; break; default: return (USBD_INVAL); } } else { sed = ohci_alloc_sed(sc); if (sed == 0) goto bad0; std = ohci_alloc_std(sc); if (std == 0) goto bad1; opipe->sed = sed; opipe->tail = std; sed->ed->ed_flags = LE( OHCI_ED_SET_FA(addr) | OHCI_ED_SET_EN(ed->bEndpointAddress) | OHCI_ED_DIR_TD | (dev->lowspeed ? OHCI_ED_SPEED : 0) | ((ed->bmAttributes & UE_XFERTYPE) == UE_ISOCHRONOUS ? OHCI_ED_FORMAT_ISO : OHCI_ED_FORMAT_GEN) | OHCI_ED_SET_MAXP(UGETW(ed->wMaxPacketSize))); sed->ed->ed_headp = sed->ed->ed_tailp = LE(std->physaddr); switch (ed->bmAttributes & UE_XFERTYPE) { case UE_CONTROL: pipe->methods = &ohci_device_ctrl_methods; r = usb_allocmem(sc->sc_dmatag, sizeof(usb_device_request_t), 0, &opipe->u.ctl.reqdma); if (r != USBD_NORMAL_COMPLETION) goto bad; s = splusb(); ohci_add_ed(sed, sc->sc_ctrl_head); splx(s); break; case UE_INTERRUPT: pipe->methods = &ohci_device_intr_methods; return (ohci_device_setintr(sc, opipe, ed->bInterval)); case UE_ISOCHRONOUS: printf("ohci_open: open iso unimplemented\n"); return (USBD_XXX); case UE_BULK: pipe->methods = &ohci_device_bulk_methods; s = splusb(); ohci_add_ed(sed, sc->sc_bulk_head); splx(s); break; } } return (USBD_NORMAL_COMPLETION); bad: ohci_free_std(sc, std); bad1: ohci_free_sed(sc, sed); bad0: return (USBD_NOMEM); } /* * Data structures and routines to emulate the root hub. */ usb_device_descriptor_t ohci_devd = { USB_DEVICE_DESCRIPTOR_SIZE, UDESC_DEVICE, /* type */ {0x00, 0x01}, /* USB version */ UCLASS_HUB, /* class */ USUBCLASS_HUB, /* subclass */ 0, /* protocol */ 64, /* max packet */ {0},{0},{0x00,0x01}, /* device id */ 1,2,0, /* string indicies */ 1 /* # of configurations */ }; usb_config_descriptor_t ohci_confd = { USB_CONFIG_DESCRIPTOR_SIZE, UDESC_CONFIG, {USB_CONFIG_DESCRIPTOR_SIZE + USB_INTERFACE_DESCRIPTOR_SIZE + USB_ENDPOINT_DESCRIPTOR_SIZE}, 1, 1, 0, UC_SELF_POWERED, 0 /* max power */ }; usb_interface_descriptor_t ohci_ifcd = { USB_INTERFACE_DESCRIPTOR_SIZE, UDESC_INTERFACE, 0, 0, 1, UCLASS_HUB, USUBCLASS_HUB, 0, 0 }; usb_endpoint_descriptor_t ohci_endpd = { USB_ENDPOINT_DESCRIPTOR_SIZE, UDESC_ENDPOINT, UE_IN | OHCI_INTR_ENDPT, UE_INTERRUPT, {8, 0}, /* max packet */ 255 }; usb_hub_descriptor_t ohci_hubd = { USB_HUB_DESCRIPTOR_SIZE, UDESC_HUB, 0, {0,0}, 0, 0, {0}, }; int ohci_str(p, l, s) usb_string_descriptor_t *p; int l; char *s; { int i; if (l == 0) return (0); p->bLength = 2 * strlen(s) + 2; if (l == 1) return (1); p->bDescriptorType = UDESC_STRING; l -= 2; for (i = 0; s[i] && l > 1; i++, l -= 2) USETW2(p->bString[i], 0, s[i]); return (2*i+2); } /* * Simulate a hardware hub by handling all the necessary requests. */ usbd_status ohci_root_ctrl_transfer(reqh) usbd_request_handle reqh; { int s; usbd_status r; s = splusb(); r = usb_insert_transfer(reqh); splx(s); if (r != USBD_NORMAL_COMPLETION) return (r); else return (ohci_root_ctrl_start(reqh)); } usbd_status ohci_root_ctrl_start(reqh) usbd_request_handle reqh; { ohci_softc_t *sc = (ohci_softc_t *)reqh->pipe->device->bus; usb_device_request_t *req; void *buf; int port, i; int len, value, index, l, totlen = 0; usb_port_status_t ps; usb_hub_descriptor_t hubd; usbd_status r; u_int32_t v; if (!reqh->isreq) /* XXX panic */ return (USBD_INVAL); req = &reqh->request; buf = reqh->buffer; DPRINTFN(4,("ohci_root_ctrl_control type=0x%02x request=%02x\n", req->bmRequestType, req->bRequest)); len = UGETW(req->wLength); value = UGETW(req->wValue); index = UGETW(req->wIndex); #define C(x,y) ((x) | ((y) << 8)) switch(C(req->bRequest, req->bmRequestType)) { case C(UR_CLEAR_FEATURE, UT_WRITE_DEVICE): case C(UR_CLEAR_FEATURE, UT_WRITE_INTERFACE): case C(UR_CLEAR_FEATURE, UT_WRITE_ENDPOINT): /* * DEVICE_REMOTE_WAKEUP and ENDPOINT_HALT are no-ops * for the integrated root hub. */ break; case C(UR_GET_CONFIG, UT_READ_DEVICE): if (len > 0) { *(u_int8_t *)buf = sc->sc_conf; totlen = 1; } break; case C(UR_GET_DESCRIPTOR, UT_READ_DEVICE): DPRINTFN(8,("ohci_root_ctrl_control wValue=0x%04x\n", value)); switch(value >> 8) { case UDESC_DEVICE: if ((value & 0xff) != 0) { r = USBD_IOERROR; goto ret; } totlen = l = min(len, USB_DEVICE_DESCRIPTOR_SIZE); memcpy(buf, &ohci_devd, l); break; case UDESC_CONFIG: if ((value & 0xff) != 0) { r = USBD_IOERROR; goto ret; } totlen = l = min(len, USB_CONFIG_DESCRIPTOR_SIZE); memcpy(buf, &ohci_confd, l); buf = (char *)buf + l; len -= l; l = min(len, USB_INTERFACE_DESCRIPTOR_SIZE); totlen += l; memcpy(buf, &ohci_ifcd, l); buf = (char *)buf + l; len -= l; l = min(len, USB_ENDPOINT_DESCRIPTOR_SIZE); totlen += l; memcpy(buf, &ohci_endpd, l); break; case UDESC_STRING: if (len == 0) break; *(u_int8_t *)buf = 0; totlen = 1; switch (value & 0xff) { case 1: /* Vendor */ totlen = ohci_str(buf, len, sc->sc_vendor); break; case 2: /* Product */ totlen = ohci_str(buf, len, "OHCI root hub"); break; } break; default: r = USBD_IOERROR; goto ret; } break; case C(UR_GET_INTERFACE, UT_READ_INTERFACE): if (len > 0) { *(u_int8_t *)buf = 0; totlen = 1; } break; case C(UR_GET_STATUS, UT_READ_DEVICE): if (len > 1) { USETW(((usb_status_t *)buf)->wStatus,UDS_SELF_POWERED); totlen = 2; } break; case C(UR_GET_STATUS, UT_READ_INTERFACE): case C(UR_GET_STATUS, UT_READ_ENDPOINT): if (len > 1) { USETW(((usb_status_t *)buf)->wStatus, 0); totlen = 2; } break; case C(UR_SET_ADDRESS, UT_WRITE_DEVICE): if (value >= USB_MAX_DEVICES) { r = USBD_IOERROR; goto ret; } sc->sc_addr = value; break; case C(UR_SET_CONFIG, UT_WRITE_DEVICE): if (value != 0 && value != 1) { r = USBD_IOERROR; goto ret; } sc->sc_conf = value; break; case C(UR_SET_DESCRIPTOR, UT_WRITE_DEVICE): break; case C(UR_SET_FEATURE, UT_WRITE_DEVICE): case C(UR_SET_FEATURE, UT_WRITE_INTERFACE): case C(UR_SET_FEATURE, UT_WRITE_ENDPOINT): r = USBD_IOERROR; goto ret; case C(UR_SET_INTERFACE, UT_WRITE_INTERFACE): break; case C(UR_SYNCH_FRAME, UT_WRITE_ENDPOINT): break; /* Hub requests */ case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_DEVICE): break; case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_OTHER): DPRINTFN(8, ("ohci_root_ctrl_control: UR_CLEAR_PORT_FEATURE " "port=%d feature=%d\n", index, value)); if (index < 1 || index > sc->sc_noport) { r = USBD_IOERROR; goto ret; } port = OHCI_RH_PORT_STATUS(index); switch(value) { case UHF_PORT_ENABLE: OWRITE4(sc, port, UPS_CURRENT_CONNECT_STATUS); break; case UHF_PORT_SUSPEND: OWRITE4(sc, port, UPS_OVERCURRENT_INDICATOR); break; case UHF_PORT_POWER: OWRITE4(sc, port, UPS_LOW_SPEED); break; case UHF_C_PORT_CONNECTION: OWRITE4(sc, port, UPS_C_CONNECT_STATUS << 16); break; case UHF_C_PORT_ENABLE: OWRITE4(sc, port, UPS_C_PORT_ENABLED << 16); break; case UHF_C_PORT_SUSPEND: OWRITE4(sc, port, UPS_C_SUSPEND << 16); break; case UHF_C_PORT_OVER_CURRENT: OWRITE4(sc, port, UPS_C_OVERCURRENT_INDICATOR << 16); break; case UHF_C_PORT_RESET: OWRITE4(sc, port, UPS_C_PORT_RESET << 16); break; default: r = USBD_IOERROR; goto ret; } switch(value) { case UHF_C_PORT_CONNECTION: case UHF_C_PORT_ENABLE: case UHF_C_PORT_SUSPEND: case UHF_C_PORT_OVER_CURRENT: case UHF_C_PORT_RESET: /* Enable RHSC interrupt if condition is cleared. */ if ((OREAD4(sc, port) >> 16) == 0) ohci_rhsc_able(sc, 1); break; default: break; } break; case C(UR_GET_DESCRIPTOR, UT_READ_CLASS_DEVICE): if (value != 0) { r = USBD_IOERROR; goto ret; } v = OREAD4(sc, OHCI_RH_DESCRIPTOR_A); hubd = ohci_hubd; hubd.bNbrPorts = sc->sc_noport; USETW(hubd.wHubCharacteristics, (v & OHCI_NPS ? UHD_PWR_NO_SWITCH : v & OHCI_PSM ? UHD_PWR_GANGED : UHD_PWR_INDIVIDUAL) /* XXX overcurrent */ ); hubd.bPwrOn2PwrGood = OHCI_GET_POTPGT(v); v = OREAD4(sc, OHCI_RH_DESCRIPTOR_B); for (i = 0, l = sc->sc_noport; l > 0; i++, l -= 8, v >>= 8) hubd.DeviceRemovable[i++] = (u_int8_t)v; hubd.bDescLength = USB_HUB_DESCRIPTOR_SIZE + i; l = min(len, hubd.bDescLength); totlen = l; memcpy(buf, &hubd, l); break; case C(UR_GET_STATUS, UT_READ_CLASS_DEVICE): if (len != 4) { r = USBD_IOERROR; goto ret; } memset(buf, 0, len); /* ? XXX */ totlen = len; break; case C(UR_GET_STATUS, UT_READ_CLASS_OTHER): DPRINTFN(8,("ohci_root_ctrl_transfer: get port status i=%d\n", index)); if (index < 1 || index > sc->sc_noport) { r = USBD_IOERROR; goto ret; } if (len != 4) { r = USBD_IOERROR; goto ret; } v = OREAD4(sc, OHCI_RH_PORT_STATUS(index)); DPRINTFN(8,("ohci_root_ctrl_transfer: port status=0x%04x\n", v)); USETW(ps.wPortStatus, v); USETW(ps.wPortChange, v >> 16); l = min(len, sizeof ps); memcpy(buf, &ps, l); totlen = l; break; case C(UR_SET_DESCRIPTOR, UT_WRITE_CLASS_DEVICE): r = USBD_IOERROR; goto ret; case C(UR_SET_FEATURE, UT_WRITE_CLASS_DEVICE): break; case C(UR_SET_FEATURE, UT_WRITE_CLASS_OTHER): if (index < 1 || index > sc->sc_noport) { r = USBD_IOERROR; goto ret; } port = OHCI_RH_PORT_STATUS(index); switch(value) { case UHF_PORT_ENABLE: OWRITE4(sc, port, UPS_PORT_ENABLED); break; case UHF_PORT_SUSPEND: OWRITE4(sc, port, UPS_SUSPEND); break; case UHF_PORT_RESET: DPRINTFN(5,("ohci_root_ctrl_transfer: reset port %d\n", index)); OWRITE4(sc, port, UPS_RESET); for (i = 0; i < 10; i++) { usb_delay_ms(&sc->sc_bus, 10); if ((OREAD4(sc, port) & UPS_RESET) == 0) break; } DPRINTFN(8,("ohci port %d reset, status = 0x%04x\n", index, OREAD4(sc, port))); break; case UHF_PORT_POWER: DPRINTFN(2,("ohci_root_ctrl_transfer: set port power " "%d\n", index)); OWRITE4(sc, port, UPS_PORT_POWER); break; default: r = USBD_IOERROR; goto ret; } break; default: r = USBD_IOERROR; goto ret; } reqh->actlen = totlen; r = USBD_NORMAL_COMPLETION; ret: reqh->status = r; reqh->xfercb(reqh); usb_start_next(reqh->pipe); return (USBD_IN_PROGRESS); } /* Abort a root control request. */ void ohci_root_ctrl_abort(reqh) usbd_request_handle reqh; { /* Nothing to do, all transfers are synchronous. */ } /* Close the root pipe. */ void ohci_root_ctrl_close(pipe) usbd_pipe_handle pipe; { DPRINTF(("ohci_root_ctrl_close\n")); } usbd_status ohci_root_intr_transfer(reqh) usbd_request_handle reqh; { int s; usbd_status r; s = splusb(); r = usb_insert_transfer(reqh); splx(s); if (r != USBD_NORMAL_COMPLETION) return (r); else return (ohci_root_intr_start(reqh)); } usbd_status ohci_root_intr_start(reqh) usbd_request_handle reqh; { usbd_pipe_handle pipe = reqh->pipe; ohci_softc_t *sc = (ohci_softc_t *)pipe->device->bus; struct ohci_pipe *upipe = (struct ohci_pipe *)pipe; usb_dma_t *dmap; usbd_status r; int len; len = reqh->length; dmap = &upipe->u.intr.datadma; if (len == 0) return (USBD_INVAL); /* XXX should it be? */ r = usb_allocmem(sc->sc_dmatag, len, 0, dmap); if (r != USBD_NORMAL_COMPLETION) return (r); sc->sc_intrreqh = reqh; return (USBD_IN_PROGRESS); } /* Abort a root interrupt request. */ void ohci_root_intr_abort(reqh) usbd_request_handle reqh; { /* No need to abort. */ } /* Close the root pipe. */ void ohci_root_intr_close(pipe) usbd_pipe_handle pipe; { ohci_softc_t *sc = (ohci_softc_t *)pipe->device->bus; sc->sc_intrreqh = 0; DPRINTF(("ohci_root_intr_close\n")); } /************************/ usbd_status ohci_device_ctrl_transfer(reqh) usbd_request_handle reqh; { int s; usbd_status r; s = splusb(); r = usb_insert_transfer(reqh); splx(s); if (r != USBD_NORMAL_COMPLETION) return (r); else return (ohci_device_ctrl_start(reqh)); } usbd_status ohci_device_ctrl_start(reqh) usbd_request_handle reqh; { ohci_softc_t *sc = (ohci_softc_t *)reqh->pipe->device->bus; usbd_status r; if (!reqh->isreq) { /* XXX panic */ printf("ohci_device_ctrl_transfer: not a request\n"); return (USBD_INVAL); } r = ohci_device_request(reqh); if (r != USBD_NORMAL_COMPLETION) return (r); if (sc->sc_bus.use_polling) ohci_waitintr(sc, reqh); return (USBD_IN_PROGRESS); } /* Abort a device control request. */ void ohci_device_ctrl_abort(reqh) usbd_request_handle reqh; { /* XXX inactivate */ usb_delay_ms(reqh->pipe->device->bus, 1); /* make sure it is donw */ /* XXX call done */ } /* Close a device control pipe. */ void ohci_device_ctrl_close(pipe) usbd_pipe_handle pipe; { struct ohci_pipe *opipe = (struct ohci_pipe *)pipe; ohci_softc_t *sc = (ohci_softc_t *)pipe->device->bus; ohci_soft_ed_t *sed = opipe->sed; int s; s = splusb(); sed->ed->ed_flags |= LE(OHCI_ED_SKIP); if ((LE(sed->ed->ed_tailp) & OHCI_TAILMASK) != LE(sed->ed->ed_headp)) usb_delay_ms(&sc->sc_bus, 2); ohci_rem_ed(sed, sc->sc_ctrl_head); splx(s); ohci_free_std(sc, opipe->tail); ohci_free_sed(sc, opipe->sed); /* XXX free other resources */ } /************************/ usbd_status ohci_device_bulk_transfer(reqh) usbd_request_handle reqh; { int s; usbd_status r; s = splusb(); r = usb_insert_transfer(reqh); splx(s); if (r != USBD_NORMAL_COMPLETION) return (r); else return (ohci_device_bulk_start(reqh)); } usbd_status ohci_device_bulk_start(reqh) usbd_request_handle reqh; { struct ohci_pipe *opipe = (struct ohci_pipe *)reqh->pipe; usbd_device_handle dev = opipe->pipe.device; ohci_softc_t *sc = (ohci_softc_t *)dev->bus; int addr = dev->address; ohci_soft_td_t *xfer, *tail; ohci_soft_ed_t *sed; usb_dma_t *dmap; usbd_status r; int s, len, isread; if (reqh->isreq) { /* XXX panic */ printf("ohci_device_bulk_transfer: a request\n"); return (USBD_INVAL); } len = reqh->length; dmap = &opipe->u.bulk.datadma; isread = reqh->pipe->endpoint->edesc->bEndpointAddress & UE_IN; sed = opipe->sed; opipe->u.bulk.length = len; r = usb_allocmem(sc->sc_dmatag, len, 0, dmap); if (r != USBD_NORMAL_COMPLETION) goto ret1; tail = ohci_alloc_std(sc); if (!tail) { r = USBD_NOMEM; goto ret2; } tail->reqh = 0; /* Update device address */ sed->ed->ed_flags = LE( (LE(sed->ed->ed_flags) & ~OHCI_ED_ADDRMASK) | OHCI_ED_SET_FA(addr)); /* Set up data transaction */ xfer = opipe->tail; xfer->td->td_flags = LE( (isread ? OHCI_TD_IN : OHCI_TD_OUT) | OHCI_TD_NOCC | OHCI_TD_SET_DI(1) | OHCI_TD_TOGGLE_CARRY | (reqh->flags & USBD_SHORT_XFER_OK ? OHCI_TD_R : 0)); xfer->td->td_cbp = LE(DMAADDR(dmap)); xfer->nexttd = tail; xfer->td->td_nexttd = LE(tail->physaddr); xfer->td->td_be = LE(LE(xfer->td->td_cbp) + len - 1); xfer->len = len; xfer->reqh = reqh; reqh->hcpriv = xfer; if (!isread) memcpy(KERNADDR(dmap), reqh->buffer, len); /* Insert ED in schedule */ s = splusb(); ohci_hash_add_td(sc, xfer); sed->ed->ed_tailp = LE(tail->physaddr); opipe->tail = tail; OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_BLF); if (reqh->timeout && !sc->sc_bus.use_polling) { usb_timeout(ohci_timeout, reqh, MS_TO_TICKS(reqh->timeout), reqh->timo_handle); } splx(s); return (USBD_IN_PROGRESS); ret2: usb_freemem(sc->sc_dmatag, dmap); ret1: return (r); } /* Abort a device bulk request. */ void ohci_device_bulk_abort(reqh) usbd_request_handle reqh; { #if 0 sed->ed->ed_flags |= LE(OHCI_ED_SKIP); if ((LE(sed->ed->ed_tailp) & OHCI_TAILMASK) != LE(sed->ed->ed_headp)) usb_delay_ms(reqh->pipe->device->bus, 2); #endif /* XXX inactivate */ usb_delay_ms(reqh->pipe->device->bus, 1); /* make sure it is done */ /* XXX call done */ } /* Close a device bulk pipe. */ void ohci_device_bulk_close(pipe) usbd_pipe_handle pipe; { struct ohci_pipe *opipe = (struct ohci_pipe *)pipe; usbd_device_handle dev = opipe->pipe.device; ohci_softc_t *sc = (ohci_softc_t *)dev->bus; int s; s = splusb(); ohci_rem_ed(opipe->sed, sc->sc_bulk_head); splx(s); ohci_free_std(sc, opipe->tail); ohci_free_sed(sc, opipe->sed); /* XXX free other resources */ } /************************/ usbd_status ohci_device_intr_transfer(reqh) usbd_request_handle reqh; { int s; usbd_status r; s = splusb(); r = usb_insert_transfer(reqh); splx(s); if (r != USBD_NORMAL_COMPLETION) return (r); else return (ohci_device_intr_start(reqh)); } usbd_status ohci_device_intr_start(reqh) usbd_request_handle reqh; { struct ohci_pipe *opipe = (struct ohci_pipe *)reqh->pipe; usbd_device_handle dev = opipe->pipe.device; ohci_softc_t *sc = (ohci_softc_t *)dev->bus; ohci_soft_ed_t *sed = opipe->sed; ohci_soft_td_t *xfer, *tail; usb_dma_t *dmap; usbd_status r; int len; int s; DPRINTFN(3, ("ohci_device_intr_transfer: reqh=%p buf=%p len=%d " "flags=%d priv=%p\n", reqh, reqh->buffer, reqh->length, reqh->flags, reqh->priv)); if (reqh->isreq) panic("ohci_device_intr_transfer: a request\n"); len = reqh->length; dmap = &opipe->u.intr.datadma; if (len == 0) return (USBD_INVAL); /* XXX should it be? */ xfer = opipe->tail; tail = ohci_alloc_std(sc); if (!tail) { r = USBD_NOMEM; goto ret1; } tail->reqh = 0; r = usb_allocmem(sc->sc_dmatag, len, 0, dmap); if (r != USBD_NORMAL_COMPLETION) goto ret2; xfer->td->td_flags = LE( OHCI_TD_IN | OHCI_TD_NOCC | OHCI_TD_SET_DI(1) | OHCI_TD_TOGGLE_CARRY); if (reqh->flags & USBD_SHORT_XFER_OK) xfer->td->td_flags |= LE(OHCI_TD_R); xfer->td->td_cbp = LE(DMAADDR(dmap)); xfer->nexttd = tail; xfer->td->td_nexttd = LE(tail->physaddr); xfer->td->td_be = LE(LE(xfer->td->td_cbp) + len - 1); xfer->len = len; xfer->reqh = reqh; reqh->hcpriv = xfer; #if UHCI_DEBUG if (ohcidebug > 5) { printf("ohci_device_intr_transfer:\n"); ohci_dump_ed(sed); ohci_dump_tds(xfer); } #endif /* Insert ED in schedule */ s = splusb(); ohci_hash_add_td(sc, xfer); sed->ed->ed_tailp = LE(tail->physaddr); opipe->tail = tail; #if 0 if (reqh->timeout && !sc->sc_bus.use_polling) { usb_timeout(ohci_timeout, reqh, MS_TO_TICKS(reqh->timeout), reqh->timo_handle); } #endif sed->ed->ed_flags &= LE(~OHCI_ED_SKIP); #ifdef UHCI_DEBUG if (ohcidebug > 5) { delay(5000); printf("ohci_device_intr_transfer: status=%x\n", OREAD4(sc, OHCI_COMMAND_STATUS)); ohci_dump_ed(sed); ohci_dump_tds(xfer); } #endif /* moved splx(s) because of indefinite printing of TD's */ splx(s); return (USBD_IN_PROGRESS); ret2: ohci_free_std(sc, xfer); ret1: return (r); } /* Abort a device control request. */ void ohci_device_intr_abort(reqh) usbd_request_handle reqh; { /* XXX inactivate */ usb_delay_ms(reqh->pipe->device->bus, 1); /* make sure it is done */ if (reqh->pipe->intrreqh == reqh) { DPRINTF(("ohci_device_intr_abort: remove\n")); reqh->pipe->intrreqh = 0; ohci_intr_done((ohci_softc_t *)reqh->pipe->device->bus, reqh); } } /* Close a device interrupt pipe. */ void ohci_device_intr_close(pipe) usbd_pipe_handle pipe; { struct ohci_pipe *opipe = (struct ohci_pipe *)pipe; ohci_softc_t *sc = (ohci_softc_t *)pipe->device->bus; int nslots = opipe->u.intr.nslots; int pos = opipe->u.intr.pos; int j; ohci_soft_ed_t *p, *sed = opipe->sed; int s; DPRINTFN(1,("ohci_device_intr_close: pipe=%p nslots=%d pos=%d\n", pipe, nslots, pos)); s = splusb(); sed->ed->ed_flags |= LE(OHCI_ED_SKIP); if ((sed->ed->ed_tailp & LE(OHCI_TAILMASK)) != sed->ed->ed_headp) usb_delay_ms(&sc->sc_bus, 2); for (p = sc->sc_eds[pos]; p && p->next != sed; p = p->next) ; if (!p) panic("ohci_device_intr_close: ED not found\n"); p->next = sed->next; p->ed->ed_nexted = sed->ed->ed_nexted; splx(s); for (j = 0; j < nslots; j++) --sc->sc_bws[pos * nslots + j]; ohci_free_std(sc, opipe->tail); ohci_free_sed(sc, opipe->sed); /* XXX free other resources */ } usbd_status ohci_device_setintr(sc, opipe, ival) ohci_softc_t *sc; struct ohci_pipe *opipe; int ival; { int i, j, s, best; u_int npoll, slow, shigh, nslots; u_int bestbw, bw; ohci_soft_ed_t *hsed, *sed = opipe->sed; DPRINTFN(2, ("ohci_setintr: pipe=%p\n", opipe)); if (ival == 0) { printf("ohci_setintr: 0 interval\n"); return (USBD_INVAL); } npoll = OHCI_NO_INTRS; while (npoll > ival) npoll /= 2; DPRINTFN(2, ("ohci_setintr: ival=%d npoll=%d\n", ival, npoll)); /* * We now know which level in the tree the ED must go into. * Figure out which slot has most bandwidth left over. * Slots to examine: * npoll * 1 0 * 2 1 2 * 4 3 4 5 6 * 8 7 8 9 10 11 12 13 14 * N (N-1) .. (N-1+N-1) */ slow = npoll-1; shigh = slow + npoll; nslots = OHCI_NO_INTRS / npoll; for (best = i = slow, bestbw = ~0; i < shigh; i++) { bw = 0; for (j = 0; j < nslots; j++) bw += sc->sc_bws[i * nslots + j]; if (bw < bestbw) { best = i; bestbw = bw; } } DPRINTFN(2, ("ohci_setintr: best=%d(%d..%d) bestbw=%d\n", best, slow, shigh, bestbw)); s = splusb(); hsed = sc->sc_eds[best]; sed->next = hsed->next; sed->ed->ed_nexted = hsed->ed->ed_nexted; hsed->next = sed; hsed->ed->ed_nexted = LE(sed->physaddr); splx(s); for (j = 0; j < nslots; j++) ++sc->sc_bws[best * nslots + j]; opipe->u.intr.nslots = nslots; opipe->u.intr.pos = best; DPRINTFN(5, ("ohci_setintr: returns %p\n", opipe)); return (USBD_NORMAL_COMPLETION); } Index: head/sys/dev/usb/ohci_pci.c =================================================================== --- head/sys/dev/usb/ohci_pci.c (revision 45719) +++ head/sys/dev/usb/ohci_pci.c (revision 45720) @@ -1,218 +1,240 @@ /* $FreeBSD$ */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (augustss@carlstedt.se) at * Carlstedt Research & Technology. * * 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 NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */ /* * USB Open Host Controller driver. * * OHCI spec: http://www.intel.com/design/usb/ohci11d.pdf * USB spec: http://www.teleport.com/cgi-bin/mailmerge.cgi/~usb/cgiform.tpl */ +#include "opt_bus.h" + #include #include #include #include #include #include #include #include +#include +#include +#include #include #include -#define PCI_CLASS_SERIALBUS 0x0c000000 -#define PCI_SUBCLASS_COMMUNICATIONS_SERIAL 0x00000000 -#define PCI_SUBCLASS_SERIALBUS_FIREWIRE 0x00000000 -#define PCI_SUBCLASS_SERIALBUS_ACCESS 0x00010000 -#define PCI_SUBCLASS_SERIALBUS_SSA 0x00020000 -#define PCI_SUBCLASS_SERIALBUS_USB 0x00030000 -#define PCI_SUBCLASS_SERIALBUS_FIBER 0x00040000 - -#define PCI_INTERFACE(d) (((d) >> 8) & 0xff) -#define PCI_SUBCLASS(d) ((d) & PCI_SUBCLASS_MASK) -#define PCI_CLASS(d) ((d) & PCI_CLASS_MASK) - - #include #include #include #include #include #include - #define PCI_OHCI_VENDORID_ALI 0x10b9 #define PCI_OHCI_VENDORID_CMDTECH 0x1095 #define PCI_OHCI_VENDORID_COMPAQ 0x0e11 #define PCI_OHCI_VENDORID_NEC 0x1033 #define PCI_OHCI_VENDORID_OPTI 0x1045 #define PCI_OHCI_VENDORID_SIS 0x1039 #define PCI_OHCI_DEVICEID_ALADDIN_V 0x523710b9 static const char *ohci_device_aladdin_v = "AcerLabs M5237 (Aladdin-V) USB Host Controller"; #define PCI_OHCI_DEVICEID_FIRELINK 0xc8611045 static const char *ohci_device_firelink = "OPTi 82C861 (FireLink) USB Host Controller"; #define PCI_OHCI_DEVICEID_NEC 0x00351033 static const char *ohci_device_nec = "NEC uPD 9210 USB Host Controller"; #define PCI_OHCI_DEVICEID_USB0670 0x06701095 static const char *ohci_device_usb0670 = "CMD Tech 670 (USB0670) USB Host Controller"; #define PCI_OHCI_DEVICEID_USB0673 0x06731095 static const char *ohci_device_usb0673 = "CMD Tech 673 (USB0673) USB Host Controller"; static const char *ohci_device_generic = "OHCI (generic) USB Host Controller"; +#define PCI_OHCI_BASE_REG 0x10 -static const char *ohci_pci_probe __P((pcici_t, pcidi_t)); -static void ohci_pci_attach __P((pcici_t, int)); - -static u_long ohci_count = 0; - -static struct pci_device ohci_pci_device = { - "ohci", - ohci_pci_probe, - ohci_pci_attach, - &ohci_count, - NULL -}; - -DATA_SET(pcidevice_set, ohci_pci_device); - - static const char * -ohci_pci_probe(pcici_t config_id, pcidi_t device_id) +ohci_pci_match(device_t dev) { - u_int32_t class; + u_int32_t device_id = pci_get_devid(dev); switch(device_id) { case PCI_OHCI_DEVICEID_ALADDIN_V: return (ohci_device_aladdin_v); case PCI_OHCI_DEVICEID_USB0670: return (ohci_device_usb0670); case PCI_OHCI_DEVICEID_USB0673: return (ohci_device_usb0673); case PCI_OHCI_DEVICEID_FIRELINK: return (ohci_device_firelink); case PCI_OHCI_DEVICEID_NEC: return (ohci_device_nec); default: - class = pci_conf_read(config_id, PCI_CLASS_REG); - if ( (PCI_CLASS(class) == PCI_CLASS_SERIALBUS) - && (PCI_SUBCLASS(class) == PCI_SUBCLASS_SERIALBUS_USB) - && (PCI_INTERFACE(class) == PCI_INTERFACE_OHCI)) { - return(ohci_device_generic); + if ( pci_get_class(dev) == PCIC_SERIALBUS + && pci_get_subclass(dev) == PCIS_SERIALBUS_USB + && pci_get_progif(dev) == PCI_INTERFACE_OHCI) { + return (ohci_device_generic); } } return NULL; /* dunno */ } -static void -ohci_pci_attach(pcici_t config_id, int unit) +static int +ohci_pci_probe(device_t dev) { - vm_offset_t pbase; + const char *desc = ohci_pci_match(dev); + if (desc) { + device_set_desc(dev, desc); + return 0; + } else { + return ENXIO; + } +} + +static int +ohci_pci_attach(device_t dev) +{ + int unit = device_get_unit(dev); + ohci_softc_t *sc = device_get_softc(dev); device_t usbus; - ohci_softc_t *sc; usbd_status err; - int id; + int rid; + struct resource *res; + void *ih; + int error; - sc = malloc(sizeof(ohci_softc_t), M_DEVBUF, M_NOWAIT); - /* Do not free it below, intr might use the sc */ - if ( sc == NULL ) { - printf("ohci%d: could not allocate memory", unit); - return; - } - memset(sc, 0, sizeof(ohci_softc_t)); - - if(!pci_map_mem(config_id, PCI_CBMEM, - (vm_offset_t *)&sc->sc_iobase, &pbase)) { - printf("ohci%d: could not map memory\n", unit); - return; + rid = PCI_CBMEM; + res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, + 0, ~0, 1, RF_ACTIVE); + if (!res) { + device_printf(dev, "could not map memory\n"); + return ENXIO; } - if ( !pci_map_int(config_id, (pci_inthand_t *)ohci_intr, - (void *) sc, &bio_imask)) { - printf("ohci%d: could not map irq\n", unit); - return; + sc->iot = rman_get_bustag(res); + sc->ioh = rman_get_bushandle(res); + + rid = 0; + res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, + RF_SHAREABLE | RF_ACTIVE); + if (res == NULL) { + device_printf(dev, "could not allocate irq\n"); + return ENOMEM; } - usbus = device_add_child(root_bus, "usb", -1, sc); + error = bus_setup_intr(dev, res, (driver_intr_t *) ohci_intr, sc, &ih); + if (error) { + device_printf(dev, "could not setup irq\n"); + return error; + } + + usbus = device_add_child(dev, "usb", -1, sc); if (!usbus) { - printf("ohci%d: could not add USB device to root bus\n", unit); - return; + printf("ohci%d: could not add USB device\n", unit); + return ENOMEM; } - id = pci_conf_read(config_id, PCI_ID_REG); - switch(id) { + switch (pci_get_devid(dev)) { case PCI_OHCI_DEVICEID_ALADDIN_V: device_set_desc(usbus, ohci_device_aladdin_v); sprintf(sc->sc_vendor, "AcerLabs"); break; case PCI_OHCI_DEVICEID_FIRELINK: device_set_desc(usbus, ohci_device_firelink); sprintf(sc->sc_vendor, "OPTi"); break; case PCI_OHCI_DEVICEID_NEC: device_set_desc(usbus, ohci_device_nec); sprintf(sc->sc_vendor, "NEC"); break; case PCI_OHCI_DEVICEID_USB0670: device_set_desc(usbus, ohci_device_usb0670); sprintf(sc->sc_vendor, "CMDTECH"); break; case PCI_OHCI_DEVICEID_USB0673: device_set_desc(usbus, ohci_device_usb0673); sprintf(sc->sc_vendor, "CMDTECH"); break; default: if (bootverbose) - printf("(New OHCI DeviceId=0x%08x)\n", id); + printf("(New OHCI DeviceId=0x%08x)\n", pci_get_devid(dev)); device_set_desc(usbus, ohci_device_generic); sprintf(sc->sc_vendor, "(unknown)"); } sc->sc_bus.bdev = usbus; err = ohci_init(sc); if (err != USBD_NORMAL_COMPLETION) { printf("ohci%d: init failed, error=%d\n", unit, err); - device_delete_child(root_bus, usbus); + device_delete_child(dev, usbus); } - return; + return device_probe_and_attach(sc->sc_bus.bdev); } + +static device_method_t ohci_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ohci_pci_probe), + DEVMETHOD(device_attach, ohci_pci_attach), + + /* Bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), + DEVMETHOD(bus_release_resource, bus_generic_release_resource), + DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), + DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), + DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), + DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), + + { 0, 0 } +}; + +static driver_t ohci_driver = { + "ohci", + ohci_methods, + DRIVER_TYPE_BIO, + sizeof(ohci_softc_t), +}; + +static devclass_t ohci_devclass; + +DRIVER_MODULE(ohci, pci, ohci_driver, ohci_devclass, 0, 0); Index: head/sys/dev/usb/ohcivar.h =================================================================== --- head/sys/dev/usb/ohcivar.h (revision 45719) +++ head/sys/dev/usb/ohcivar.h (revision 45720) @@ -1,106 +1,107 @@ /* $NetBSD: ohcivar.h,v 1.4 1998/12/26 12:53:01 augustss Exp $ */ /* $FreeBSD$ */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (augustss@carlstedt.se) at * Carlstedt Research & Technology. * * 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 NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */ typedef struct ohci_soft_ed { ohci_ed_t *ed; struct ohci_soft_ed *next; ohci_physaddr_t physaddr; } ohci_soft_ed_t; #define OHCI_ED_CHUNK 256 typedef struct ohci_soft_td { ohci_td_t *td; struct ohci_soft_td *nexttd; /* mirrors nexttd in TD */ struct ohci_soft_td *dnext; /* next in done list */ ohci_physaddr_t physaddr; LIST_ENTRY(ohci_soft_td) hnext; /*ohci_soft_ed_t *sed;*/ usbd_request_handle reqh; u_int16_t len; } ohci_soft_td_t; #define OHCI_TD_CHUNK 256 #define OHCI_NO_EDS (2*OHCI_NO_INTRS-1) #define OHCI_HASH_SIZE 128 typedef struct ohci_softc { struct usbd_bus sc_bus; /* base device */ #if defined(__NetBSD__) void *sc_ih; /* interrupt vectoring */ bus_space_tag_t iot; bus_space_handle_t ioh; bus_dma_tag_t sc_dmatag; /* DMA tag */ /* XXX should keep track of all DMA memory */ #elif defined(__FreeBSD__) - int sc_iobase; + bus_space_tag_t iot; + bus_space_handle_t ioh; #endif /* __FreeBSD__ */ usb_dma_t sc_hccadma; struct ohci_hcca *sc_hcca; ohci_soft_ed_t *sc_eds[OHCI_NO_EDS]; u_int sc_bws[OHCI_NO_INTRS]; u_int32_t sc_eintrs; ohci_soft_ed_t *sc_ctrl_head; ohci_soft_ed_t *sc_bulk_head; LIST_HEAD(, ohci_soft_td) sc_hash_tds[OHCI_HASH_SIZE]; int sc_noport; u_int8_t sc_addr; /* device address */ u_int8_t sc_conf; /* device configuration */ ohci_soft_ed_t *sc_freeeds; ohci_soft_td_t *sc_freetds; usbd_request_handle sc_intrreqh; int sc_intrs; char sc_vendor[16]; } ohci_softc_t; usbd_status ohci_init __P((ohci_softc_t *)); int ohci_intr __P((void *)); #define MS_TO_TICKS(ms) ((ms) * hz / 1000) Index: head/sys/dev/usb/ucom.c =================================================================== --- head/sys/dev/usb/ucom.c (revision 45719) +++ head/sys/dev/usb/ucom.c (revision 45720) @@ -1,142 +1,137 @@ /* $NetBSD: ucom.c,v 1.6 1999/01/08 11:58:25 augustss Exp $ */ /* $FreeBSD$ */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (augustss@carlstedt.se) at * Carlstedt Research & Technology. * * 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 NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */ #include #include #include #include #include #if defined(__NetBSD__) #include #include #elif defined(__FreeBSD__) #include #include #include #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef UCOM_DEBUG #define DPRINTF(x) if (ucomdebug) logprintf x #define DPRINTFN(n,x) if (ucomdebug>(n)) logprintf x int ucomdebug = 1; #else #define DPRINTF(x) #define DPRINTFN(n,x) #endif struct ucom_softc { bdevice sc_dev; /* base device */ usbd_interface_handle sc_iface; /* interface */ }; void ucom_intr __P((usbd_request_handle, usbd_private_handle, usbd_status)); void ucom_disco __P((void *)); USB_DECLARE_DRIVER(ucom); USB_MATCH(ucom) { USB_MATCH_START(ucom, uaa); usb_interface_descriptor_t *id; if (!uaa->iface) return (UMATCH_NONE); id = usbd_get_interface_descriptor(uaa->iface); if ((id && id->bInterfaceClass == UCLASS_CDC) && id->bInterfaceSubClass == USUBCLASS_ABSTRACT_CONTROL_MODEL) return (UMATCH_IFACECLASS_IFACESUBCLASS); return (UMATCH_NONE); } USB_ATTACH(ucom) { USB_ATTACH_START(ucom, sc, uaa); usbd_interface_handle iface = uaa->iface; usb_interface_descriptor_t *id; char devinfo[1024]; sc->sc_iface = iface; id = usbd_get_interface_descriptor(iface); usbd_devinfo(uaa->device, 0, devinfo); USB_ATTACH_SETUP; printf("%s: %s, iclass %d/%d\n", USBDEVNAME(sc->sc_dev), devinfo, id->bInterfaceClass, id->bInterfaceSubClass); USB_ATTACH_SUCCESS_RETURN; } #if defined(__FreeBSD__) static int ucom_detach(device_t self) { - const char *devinfo = device_get_desc(self); - DPRINTF(("%s: disconnected\n", USBDEVNAME(self))); - if (devinfo) { - device_set_desc(self, NULL); - free((void *)devinfo, M_USB); - } + device_set_desc(self, NULL); return 0; } #endif #if defined(__FreeBSD__) DRIVER_MODULE(ucom, uhub, ucom_driver, ucom_devclass, usbd_driver_load, 0); #endif Index: head/sys/dev/usb/ugen.c =================================================================== --- head/sys/dev/usb/ugen.c (revision 45719) +++ head/sys/dev/usb/ugen.c (revision 45720) @@ -1,1007 +1,1002 @@ /* $NetBSD: ugen.c,v 1.11 1999/01/08 11:58:25 augustss Exp $ */ /* $FreeBSD$ */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (augustss@carlstedt.se) at * Carlstedt Research & Technology. * * 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 NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */ #include #include #include #include #if defined(__NetBSD__) #include #include #elif defined(__FreeBSD__) #include #include #include #include #include #include #endif #include #include #include #include #include #include #include #include #include #ifdef UGEN_DEBUG #define DPRINTF(x) if (ugendebug) logprintf x #define DPRINTFN(n,x) if (ugendebug>(n)) logprintf x int ugendebug = 1; #else #define DPRINTF(x) #define DPRINTFN(n,x) #endif struct ugen_endpoint { struct ugen_softc *sc; usb_endpoint_descriptor_t *edesc; usbd_interface_handle iface; int state; #define UGEN_OPEN 0x01 /* device is open */ #define UGEN_ASLP 0x02 /* waiting for data */ #define UGEN_SHORT_OK 0x04 /* short xfers are OK */ usbd_pipe_handle pipeh; struct clist q; struct selinfo rsel; void *ibuf; }; #define UGEN_CHUNK 128 /* chunk size for read */ #define UGEN_IBSIZE 1020 /* buffer size */ #define UGEN_BBSIZE 1024 struct ugen_softc { bdevice sc_dev; /* base device */ struct usbd_device *sc_udev; struct ugen_endpoint sc_endpoints[USB_MAX_ENDPOINTS][2]; #define OUT 0 /* index order is important, from UE_OUT */ #define IN 1 /* from UE_IN */ int sc_disconnected; /* device is gone */ }; #if defined(__NetBSD__) int ugenopen __P((dev_t, int, int, struct proc *)); int ugenclose __P((dev_t, int, int, struct proc *p)); int ugenread __P((dev_t, struct uio *uio, int)); int ugenwrite __P((dev_t, struct uio *uio, int)); int ugenioctl __P((dev_t, u_long, caddr_t, int, struct proc *)); int ugenpoll __P((dev_t, int, struct proc *)); #elif defined(__FreeBSD__) d_open_t ugenopen; d_close_t ugenclose; d_read_t ugenread; d_write_t ugenwrite; d_ioctl_t ugenioctl; d_poll_t ugenpoll; #define UGEN_CDEV_MAJOR 114 static struct cdevsw ugen_cdevsw = { ugenopen, ugenclose, ugenread, ugenwrite, ugenioctl, nostop, nullreset, nodevtotty, ugenpoll, nommap, nostrat, "ugen", NULL, -1 }; #endif void ugenintr __P((usbd_request_handle reqh, usbd_private_handle addr, usbd_status status)); void ugen_disco __P((void *)); int ugen_set_config __P((struct ugen_softc *sc, int configno)); usb_config_descriptor_t *ugen_get_cdesc __P((struct ugen_softc *sc, int index, int *lenp)); usbd_status ugen_set_interface __P((struct ugen_softc *, int, int)); int ugen_get_alt_index __P((struct ugen_softc *sc, int ifaceidx)); #define UGENUNIT(n) (((n) >> 4) & 0xf) #define UGENENDPOINT(n) ((n) & 0xf) USB_DECLARE_DRIVER(ugen); USB_MATCH(ugen) { USB_MATCH_START(ugen, uaa); if (uaa->usegeneric) return (UMATCH_GENERIC); else return (UMATCH_NONE); } USB_ATTACH(ugen) { USB_ATTACH_START(ugen, sc, uaa); char devinfo[1024]; usbd_status r; int conf; usbd_devinfo(uaa->device, 0, devinfo); USB_ATTACH_SETUP; printf("%s: %s\n", USBDEVNAME(sc->sc_dev), devinfo); sc->sc_udev = uaa->device; conf = 1; /* XXX should not hard code 1 */ r = ugen_set_config(sc, conf); if (r != USBD_NORMAL_COMPLETION) { printf("%s: setting configuration %d failed\n", USBDEVNAME(sc->sc_dev), conf); sc->sc_disconnected = 1; USB_ATTACH_ERROR_RETURN; } USB_ATTACH_SUCCESS_RETURN; } int ugen_set_config(sc, configno) struct ugen_softc *sc; int configno; { usbd_device_handle dev = sc->sc_udev; usbd_interface_handle iface; usb_endpoint_descriptor_t *ed; struct ugen_endpoint *sce; u_int8_t niface, nendpt; int ifaceno, endptno, endpt; usbd_status r; DPRINTFN(1,("ugen_set_config: %s to configno %d, sc=%p\n", USBDEVNAME(sc->sc_dev), configno, sc)); if (usbd_get_config_descriptor(dev)->bConfigurationValue != configno) { /* Avoid setting the current value. */ r = usbd_set_config_no(dev, configno, 0); if (r != USBD_NORMAL_COMPLETION) return (r); } r = usbd_interface_count(dev, &niface); if (r != USBD_NORMAL_COMPLETION) return (r); memset(sc->sc_endpoints, 0, sizeof sc->sc_endpoints); for (ifaceno = 0; ifaceno < niface; ifaceno++) { DPRINTFN(1,("ugen_set_config: ifaceno %d\n", ifaceno)); r = usbd_device2interface_handle(dev, ifaceno, &iface); if (r != USBD_NORMAL_COMPLETION) return (r); r = usbd_endpoint_count(iface, &nendpt); if (r != USBD_NORMAL_COMPLETION) return (r); for (endptno = 0; endptno < nendpt; endptno++) { ed = usbd_interface2endpoint_descriptor(iface,endptno); endpt = ed->bEndpointAddress; sce = &sc->sc_endpoints[UE_GET_ADDR(endpt)] [UE_GET_IN(endpt)]; DPRINTFN(1,("ugen_set_config: endptno %d, endpt=0x%02x" "(%d,%d), sce=%p\n", endptno, endpt, UE_GET_ADDR(endpt), UE_GET_IN(endpt), sce)); sce->sc = sc; sce->edesc = ed; sce->iface = iface; } } return (USBD_NORMAL_COMPLETION); } void ugen_disco(p) void *p; { struct ugen_softc *sc = p; sc->sc_disconnected = 1; } int ugenopen(dev, flag, mode, p) dev_t dev; int flag; int mode; struct proc *p; { int unit = UGENUNIT(dev); int endpt = UGENENDPOINT(dev); usb_endpoint_descriptor_t *edesc; struct ugen_endpoint *sce; int dir, isize; usbd_status r; USB_GET_SC_OPEN(ugen, unit, sc); DPRINTFN(5, ("ugenopen: flag=%d, mode=%d, unit=%d endpt=%d\n", flag, mode, unit, endpt)); if (sc->sc_disconnected) return (EIO); if (endpt == USB_CONTROL_ENDPOINT) { /*if ((flag & (FWRITE|FREAD)) != (FWRITE|FREAD)) return (EACCES);*/ sce = &sc->sc_endpoints[USB_CONTROL_ENDPOINT][OUT]; if (sce->state & UGEN_OPEN) return (EBUSY); } else { switch (flag & (FWRITE|FREAD)) { case FWRITE: dir = OUT; break; case FREAD: dir = IN; break; default: return (EACCES); } sce = &sc->sc_endpoints[endpt][dir]; DPRINTFN(5, ("ugenopen: sc=%p, endpt=%d, dir=%d, sce=%p\n", sc, endpt, dir, sce)); if (sce->state & UGEN_OPEN) return (EBUSY); edesc = sce->edesc; if (!edesc) return (ENXIO); switch (edesc->bmAttributes & UE_XFERTYPE) { case UE_INTERRUPT: isize = UGETW(edesc->wMaxPacketSize); if (isize == 0) /* shouldn't happen */ return (EINVAL); sce->ibuf = malloc(isize, M_USB, M_WAITOK); DPRINTFN(5, ("ugenopen: intr endpt=%d,isize=%d\n", endpt, isize)); #if defined(__NetBSD__) if (clalloc(&sce->q, UGEN_IBSIZE, 0) == -1) return (ENOMEM); #elif defined(__FreeBSD__) clist_alloc_cblocks(&sce->q, UGEN_IBSIZE, 0); #endif r = usbd_open_pipe_intr(sce->iface, edesc->bEndpointAddress, USBD_SHORT_XFER_OK, &sce->pipeh, sce, sce->ibuf, isize, ugenintr); if (r != USBD_NORMAL_COMPLETION) { free(sce->ibuf, M_USB); #if defined(__NetBSD__) clfree(&sce->q); #elif defined(__FreeBSD__) clist_free_cblocks(&sce->q); #endif return (EIO); } usbd_set_disco(sce->pipeh, ugen_disco, sc); DPRINTFN(5, ("ugenopen: interrupt open done\n")); break; case UE_BULK: r = usbd_open_pipe(sce->iface, edesc->bEndpointAddress, 0, &sce->pipeh); if (r != USBD_NORMAL_COMPLETION) return (EIO); break; case UE_CONTROL: case UE_ISOCHRONOUS: return (EINVAL); } } sce->state |= UGEN_OPEN; return (0); } int ugenclose(dev, flag, mode, p) dev_t dev; int flag; int mode; struct proc *p; { USB_GET_SC(ugen, UGENUNIT(dev), sc); int endpt = UGENENDPOINT(dev); struct ugen_endpoint *sce; int dir; DPRINTFN(5, ("ugenclose: flag=%d, mode=%d\n", flag, mode)); if (sc->sc_disconnected) return (EIO); if (endpt == USB_CONTROL_ENDPOINT) { DPRINTFN(5, ("ugenclose: close control\n")); sc->sc_endpoints[endpt][OUT].state = 0; return (0); } flag = FWRITE | FREAD; /* XXX bug if generic open/close */ /* The open modes have been joined, so check for both modes. */ for (dir = OUT; dir <= IN; dir++) { if (flag & (dir == OUT ? FWRITE : FREAD)) { sce = &sc->sc_endpoints[endpt][dir]; if (!sce || !sce->pipeh) /* XXX */ continue; /* XXX */ DPRINTFN(5, ("ugenclose: endpt=%d dir=%d sce=%p\n", endpt, dir, sce)); sce->state = 0; usbd_abort_pipe(sce->pipeh); usbd_close_pipe(sce->pipeh); sce->pipeh = 0; if (sce->ibuf) { free(sce->ibuf, M_USB); sce->ibuf = 0; } } } return (0); } int ugenread(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { USB_GET_SC(ugen, UGENUNIT(dev), sc); int endpt = UGENENDPOINT(dev); struct ugen_endpoint *sce = &sc->sc_endpoints[endpt][IN]; u_int32_t n, tn; char buf[UGEN_BBSIZE]; usbd_request_handle reqh; usbd_status r; int s; int error = 0; u_char buffer[UGEN_CHUNK]; DPRINTFN(5, ("ugenread: %d:%d\n", UGENUNIT(dev), UGENENDPOINT(dev))); if (sc->sc_disconnected) return (EIO); #ifdef DIAGNOSTIC if (!sce->edesc) { printf("ugenread: no edesc\n"); return (EIO); } if (!sce->pipeh) { printf("ugenread: no pipe\n"); return (EIO); } #endif switch (sce->edesc->bmAttributes & UE_XFERTYPE) { case UE_INTERRUPT: /* Block until activity occured. */ s = splusb(); while (sce->q.c_cc == 0) { if (flag & IO_NDELAY) { splx(s); return (EWOULDBLOCK); } sce->state |= UGEN_ASLP; DPRINTFN(5, ("ugenread: sleep on %p\n", sc)); error = tsleep((caddr_t)sce, PZERO | PCATCH, "ugenri", 0); DPRINTFN(5, ("ugenread: woke, error=%d\n", error)); if (error) { sce->state &= ~UGEN_ASLP; splx(s); return (error); } } splx(s); /* Transfer as many chunks as possible. */ while (sce->q.c_cc > 0 && uio->uio_resid > 0) { n = min(sce->q.c_cc, uio->uio_resid); if (n > sizeof(buffer)) n = sizeof(buffer); /* Remove a small chunk from the input queue. */ q_to_b(&sce->q, buffer, n); DPRINTFN(5, ("ugenread: got %d chars\n", n)); /* Copy the data to the user process. */ error = uiomove(buffer, n, uio); if (error) break; } break; case UE_BULK: reqh = usbd_alloc_request(); if (reqh == 0) return (ENOMEM); while ((n = min(UGEN_BBSIZE, uio->uio_resid)) != 0) { DPRINTFN(1, ("ugenread: start transfer %d bytes\n", n)); tn = n; r = usbd_bulk_transfer(reqh, sce->pipeh, 0, buf, &tn, "ugenrb"); if (r != USBD_NORMAL_COMPLETION) { if (r == USBD_INTERRUPTED) error = EINTR; else error = EIO; break; } DPRINTFN(1, ("ugenread: got %d bytes\n", tn)); error = uiomove(buf, tn, uio); if (error || tn < n) break; } usbd_free_request(reqh); break; default: return (ENXIO); } return (error); } int ugenwrite(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { USB_GET_SC(ugen, UGENUNIT(dev), sc); int endpt = UGENENDPOINT(dev); struct ugen_endpoint *sce = &sc->sc_endpoints[endpt][OUT]; size_t n; int error = 0; char buf[UGEN_BBSIZE]; usbd_request_handle reqh; usbd_status r; if (sc->sc_disconnected) return (EIO); #ifdef DIAGNOSTIC if (!sce->edesc) { printf("ugenwrite: no edesc\n"); return (EIO); } if (!sce->pipeh) { printf("ugenwrite: no pipe\n"); return (EIO); } #endif DPRINTF(("ugenwrite\n")); switch (sce->edesc->bmAttributes & UE_XFERTYPE) { case UE_BULK: reqh = usbd_alloc_request(); if (reqh == 0) return (EIO); while ((n = min(UGEN_BBSIZE, uio->uio_resid)) != 0) { error = uiomove(buf, n, uio); if (error) break; DPRINTFN(1, ("ugenwrite: transfer %d bytes\n", n)); r = usbd_bulk_transfer(reqh, sce->pipeh, 0, buf, &n, "ugenwb"); if (r != USBD_NORMAL_COMPLETION) { if (r == USBD_INTERRUPTED) error = EINTR; else error = EIO; break; } } usbd_free_request(reqh); break; default: return (ENXIO); } return (error); } void ugenintr(reqh, addr, status) usbd_request_handle reqh; usbd_private_handle addr; usbd_status status; { struct ugen_endpoint *sce = addr; /*struct ugen_softc *sc = sce->sc;*/ usbd_private_handle priv; void *buffer; u_int32_t count; usbd_status xstatus; u_char *ibuf; if (status == USBD_CANCELLED) return; if (status != USBD_NORMAL_COMPLETION) { DPRINTF(("ugenintr: status=%d\n", status)); usbd_clear_endpoint_stall_async(sce->pipeh); return; } (void)usbd_get_request_status(reqh, &priv, &buffer, &count, &xstatus); ibuf = sce->ibuf; DPRINTFN(5, ("ugenintr: reqh=%p status=%d count=%d\n", reqh, xstatus, count)); DPRINTFN(5, (" data = %02x %02x %02x\n", ibuf[0], ibuf[1], ibuf[2])); (void)b_to_q(ibuf, count, &sce->q); if (sce->state & UGEN_ASLP) { sce->state &= ~UGEN_ASLP; DPRINTFN(5, ("ugen_intr: waking %p\n", sce)); wakeup((caddr_t)sce); } selwakeup(&sce->rsel); } usbd_status ugen_set_interface(sc, ifaceidx, altno) struct ugen_softc *sc; int ifaceidx, altno; { usbd_interface_handle iface; usb_endpoint_descriptor_t *ed; usbd_status r; struct ugen_endpoint *sce; u_int8_t niface, nendpt, endptno, endpt; DPRINTFN(15, ("ugen_set_interface %d %d\n", ifaceidx, altno)); r = usbd_interface_count(sc->sc_udev, &niface); if (r != USBD_NORMAL_COMPLETION) return (r); if (ifaceidx < 0 || ifaceidx >= niface) return (USBD_INVAL); r = usbd_device2interface_handle(sc->sc_udev, ifaceidx, &iface); if (r != USBD_NORMAL_COMPLETION) return (r); r = usbd_endpoint_count(iface, &nendpt); if (r != USBD_NORMAL_COMPLETION) return (r); for (endptno = 0; endptno < nendpt; endptno++) { ed = usbd_interface2endpoint_descriptor(iface,endptno); endpt = ed->bEndpointAddress; sce = &sc->sc_endpoints[UE_GET_ADDR(endpt)][UE_GET_IN(endpt)]; sce->sc = 0; sce->edesc = 0; sce->iface = 0; } /* change setting */ r = usbd_set_interface(iface, altno); if (r != USBD_NORMAL_COMPLETION) return (r); r = usbd_endpoint_count(iface, &nendpt); if (r != USBD_NORMAL_COMPLETION) return (r); for (endptno = 0; endptno < nendpt; endptno++) { ed = usbd_interface2endpoint_descriptor(iface,endptno); endpt = ed->bEndpointAddress; sce = &sc->sc_endpoints[UE_GET_ADDR(endpt)][UE_GET_IN(endpt)]; sce->sc = sc; sce->edesc = ed; sce->iface = iface; } return (0); } /* Retrieve a complete descriptor for a certain device and index. */ usb_config_descriptor_t * ugen_get_cdesc(sc, index, lenp) struct ugen_softc *sc; int index; int *lenp; { usb_config_descriptor_t *cdesc, *tdesc, cdescr; int len; usbd_status r; if (index == USB_CURRENT_CONFIG_INDEX) { tdesc = usbd_get_config_descriptor(sc->sc_udev); len = UGETW(tdesc->wTotalLength); if (lenp) *lenp = len; cdesc = malloc(len, M_TEMP, M_WAITOK); memcpy(cdesc, tdesc, len); DPRINTFN(5,("ugen_get_cdesc: current, len=%d\n", len)); } else { r = usbd_get_config_desc(sc->sc_udev, index, &cdescr); if (r != USBD_NORMAL_COMPLETION) return (0); len = UGETW(cdescr.wTotalLength); DPRINTFN(5,("ugen_get_cdesc: index=%d, len=%d\n", index, len)); if (lenp) *lenp = len; cdesc = malloc(len, M_TEMP, M_WAITOK); r = usbd_get_config_desc_full(sc->sc_udev, index, cdesc, len); if (r != USBD_NORMAL_COMPLETION) { free(cdesc, M_TEMP); return (0); } } return (cdesc); } int ugen_get_alt_index(sc, ifaceidx) struct ugen_softc *sc; int ifaceidx; { usbd_interface_handle iface; usbd_status r; r = usbd_device2interface_handle(sc->sc_udev, ifaceidx, &iface); if (r != USBD_NORMAL_COMPLETION) return (-1); return (usbd_get_interface_altindex(iface)); } int ugenioctl(dev, cmd, addr, flag, p) dev_t dev; u_long cmd; caddr_t addr; int flag; struct proc *p; { USB_GET_SC(ugen, UGENUNIT(dev), sc); int endpt = UGENENDPOINT(dev); struct ugen_endpoint *sce; usbd_status r; usbd_interface_handle iface; struct usb_config_desc *cd; usb_config_descriptor_t *cdesc; struct usb_interface_desc *id; usb_interface_descriptor_t *idesc; struct usb_endpoint_desc *ed; usb_endpoint_descriptor_t *edesc; struct usb_alt_interface *ai; struct usb_string_desc *si; u_int8_t conf, alt; DPRINTFN(5, ("ugenioctl: cmd=%08lx\n", cmd)); if (sc->sc_disconnected) return (EIO); switch (cmd) { case FIONBIO: /* All handled in the upper FS layer. */ return (0); case USB_SET_SHORT_XFER: /* This flag only affects read */ sce = &sc->sc_endpoints[endpt][IN]; #ifdef DIAGNOSTIC if (!sce->pipeh) { printf("ugenioctl: no pipe\n"); return (EIO); } #endif if (*(int *)addr) sce->state |= UGEN_SHORT_OK; else sce->state &= ~UGEN_SHORT_OK; return (0); default: break; } if (endpt != USB_CONTROL_ENDPOINT) return (EINVAL); switch (cmd) { #ifdef UGEN_DEBUG case USB_SETDEBUG: ugendebug = *(int *)addr; break; #endif case USB_GET_CONFIG: r = usbd_get_config(sc->sc_udev, &conf); if (r != USBD_NORMAL_COMPLETION) return (EIO); *(int *)addr = conf; break; case USB_SET_CONFIG: if (!(flag & FWRITE)) return (EPERM); r = ugen_set_config(sc, *(int *)addr); if (r != USBD_NORMAL_COMPLETION) return (EIO); break; case USB_GET_ALTINTERFACE: ai = (struct usb_alt_interface *)addr; r = usbd_device2interface_handle(sc->sc_udev, ai->interface_index, &iface); if (r != USBD_NORMAL_COMPLETION) return (EINVAL); idesc = usbd_get_interface_descriptor(iface); if (!idesc) return (EIO); ai->alt_no = idesc->bAlternateSetting; break; case USB_SET_ALTINTERFACE: if (!(flag & FWRITE)) return (EPERM); ai = (struct usb_alt_interface *)addr; r = usbd_device2interface_handle(sc->sc_udev, ai->interface_index, &iface); if (r != USBD_NORMAL_COMPLETION) return (EINVAL); r = ugen_set_interface(sc, ai->interface_index, ai->alt_no); if (r != USBD_NORMAL_COMPLETION) return (EINVAL); break; case USB_GET_NO_ALT: ai = (struct usb_alt_interface *)addr; cdesc = ugen_get_cdesc(sc, ai->config_index, 0); if (!cdesc) return (EINVAL); idesc = usbd_find_idesc(cdesc, ai->interface_index, 0); if (!idesc) return (EINVAL); ai->alt_no = usbd_get_no_alts(cdesc, idesc->bInterfaceNumber); break; case USB_GET_DEVICE_DESC: *(usb_device_descriptor_t *)addr = *usbd_get_device_descriptor(sc->sc_udev); break; case USB_GET_CONFIG_DESC: cd = (struct usb_config_desc *)addr; cdesc = ugen_get_cdesc(sc, cd->config_index, 0); if (!cdesc) return (EINVAL); cd->desc = *cdesc; free(cdesc, M_TEMP); break; case USB_GET_INTERFACE_DESC: id = (struct usb_interface_desc *)addr; cdesc = ugen_get_cdesc(sc, id->config_index, 0); if (!cdesc) return (EINVAL); if (id->config_index == USB_CURRENT_CONFIG_INDEX && id->alt_index == USB_CURRENT_ALT_INDEX) alt = ugen_get_alt_index(sc, id->interface_index); else alt = id->alt_index; idesc = usbd_find_idesc(cdesc, id->interface_index, alt); if (!idesc) { free(cdesc, M_TEMP); return (EINVAL); } id->desc = *idesc; free(cdesc, M_TEMP); break; case USB_GET_ENDPOINT_DESC: ed = (struct usb_endpoint_desc *)addr; cdesc = ugen_get_cdesc(sc, ed->config_index, 0); if (!cdesc) return (EINVAL); if (ed->config_index == USB_CURRENT_CONFIG_INDEX && ed->alt_index == USB_CURRENT_ALT_INDEX) alt = ugen_get_alt_index(sc, ed->interface_index); else alt = ed->alt_index; edesc = usbd_find_edesc(cdesc, ed->interface_index, alt, ed->endpoint_index); if (!edesc) { free(cdesc, M_TEMP); return (EINVAL); } ed->desc = *edesc; free(cdesc, M_TEMP); break; case USB_GET_FULL_DESC: { int len; struct iovec iov; struct uio uio; struct usb_full_desc *fd = (struct usb_full_desc *)addr; int error; cdesc = ugen_get_cdesc(sc, fd->config_index, &len); if (len > fd->size) len = fd->size; iov.iov_base = (caddr_t)fd->data; iov.iov_len = len; uio.uio_iov = &iov; uio.uio_iovcnt = 1; uio.uio_resid = len; uio.uio_offset = 0; uio.uio_segflg = UIO_USERSPACE; uio.uio_rw = UIO_READ; uio.uio_procp = p; error = uiomove((caddr_t)cdesc, len, &uio); free(cdesc, M_TEMP); return (error); } case USB_GET_STRING_DESC: si = (struct usb_string_desc *)addr; r = usbd_get_string_desc(sc->sc_udev, si->string_index, si->language_id, &si->desc); if (r != USBD_NORMAL_COMPLETION) return (EINVAL); break; case USB_DO_REQUEST: { struct usb_ctl_request *ur = (void *)addr; int len = UGETW(ur->request.wLength); struct iovec iov; struct uio uio; void *ptr = 0; usbd_status r; int error = 0; if (!(flag & FWRITE)) return (EPERM); /* Avoid requests that would damage the bus integrity. */ if ((ur->request.bmRequestType == UT_WRITE_DEVICE && ur->request.bRequest == UR_SET_ADDRESS) || (ur->request.bmRequestType == UT_WRITE_DEVICE && ur->request.bRequest == UR_SET_CONFIG) || (ur->request.bmRequestType == UT_WRITE_INTERFACE && ur->request.bRequest == UR_SET_INTERFACE)) return (EINVAL); if (len < 0 || len > 32767) return (EINVAL); if (len != 0) { iov.iov_base = (caddr_t)ur->data; iov.iov_len = len; uio.uio_iov = &iov; uio.uio_iovcnt = 1; uio.uio_resid = len; uio.uio_offset = 0; uio.uio_segflg = UIO_USERSPACE; uio.uio_rw = ur->request.bmRequestType & UT_READ ? UIO_READ : UIO_WRITE; uio.uio_procp = p; ptr = malloc(len, M_TEMP, M_WAITOK); if (uio.uio_rw == UIO_WRITE) { error = uiomove(ptr, len, &uio); if (error) goto ret; } } r = usbd_do_request_flags(sc->sc_udev, &ur->request, ptr, ur->flags, &ur->actlen); if (r) { error = EIO; goto ret; } if (len != 0) { if (uio.uio_rw == UIO_READ) { error = uiomove(ptr, len, &uio); if (error) goto ret; } } ret: if (ptr) free(ptr, M_TEMP); return (error); } case USB_GET_DEVICEINFO: usbd_fill_deviceinfo(sc->sc_udev, (struct usb_device_info *)addr); break; default: return (EINVAL); } return (0); } int ugenpoll(dev, events, p) dev_t dev; int events; struct proc *p; { USB_GET_SC(ugen, UGENUNIT(dev), sc); /* XXX */ struct ugen_endpoint *sce; int revents = 0; int s; if (sc->sc_disconnected) return (EIO); sce = &sc->sc_endpoints[UGENENDPOINT(dev)][IN]; #ifdef DIAGNOSTIC if (!sce->edesc) { printf("ugenwrite: no edesc\n"); return (EIO); } if (!sce->pipeh) { printf("ugenpoll: no pipe\n"); return (EIO); } #endif s = splusb(); switch (sce->edesc->bmAttributes & UE_XFERTYPE) { case UE_INTERRUPT: if (events & (POLLIN | POLLRDNORM)) { if (sce->q.c_cc > 0) revents |= events & (POLLIN | POLLRDNORM); else selrecord(p, &sce->rsel); } break; case UE_BULK: /* * We have no easy way of determining if a read will * yield any data or a write will happen. * Pretend they will. */ revents |= events & (POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM); break; default: break; } splx(s); return (revents); } #if defined(__FreeBSD__) static int ugen_detach(device_t self) { - const char *devinfo = device_get_desc(self); - DPRINTF(("%s: disconnected\n", USBDEVNAME(self))); - if (devinfo) { - device_set_desc(self, NULL); - free((void *)devinfo, M_USB); - } + device_set_desc(self, NULL); return 0; } CDEV_DRIVER_MODULE(ugen, uhub, ugen_driver, ugen_devclass, UGEN_CDEV_MAJOR, ugen_cdevsw, usbd_driver_load, 0); #endif Index: head/sys/dev/usb/uhci.c =================================================================== --- head/sys/dev/usb/uhci.c (revision 45719) +++ head/sys/dev/usb/uhci.c (revision 45720) @@ -1,2634 +1,2637 @@ /* $NetBSD: uhci.c,v 1.24 1999/02/20 23:26:16 augustss Exp $ */ /* $FreeBSD$ */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (augustss@carlstedt.se) at * Carlstedt Research & Technology. * * 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 NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */ /* * USB Universal Host Controller driver. * Handles PIIX3 and PIIX4. * * Data sheets: ftp://download.intel.com/design/intarch/datashts/29055002.pdf * ftp://download.intel.com/design/intarch/datashts/29056201.pdf * UHCI spec: http://www.intel.com/design/usb/uhci11d.pdf * USB spec: http://www.usb.org/cgi-usb/mailmerge.cgi/home/usb/docs/developers/ cgiform.tpl */ #include #include #include #include #if defined(__NetBSD__) #include #elif defined(__FreeBSD__) #include #include #endif #include #include #include +#if defined(__FreeBSD__) +#include +#endif #include #include #include #include #include #include #include #include #if defined(__FreeBSD__) #include #define delay(d) DELAY(d) #endif #ifdef UHCI_DEBUG #define DPRINTF(x) if (uhcidebug) logprintf x #define DPRINTFN(n,x) if (uhcidebug>(n)) logprintf x int uhcidebug = 1; #else #define DPRINTF(x) #define DPRINTFN(n,x) #endif #define MS_TO_TICKS(ms) ((ms) * hz / 1000) struct uhci_pipe { struct usbd_pipe pipe; uhci_intr_info_t *iinfo; int newtoggle; /* Info needed for different pipe kinds. */ union { /* Control pipe */ struct { uhci_soft_qh_t *sqh; usb_dma_t reqdma; usb_dma_t datadma; uhci_soft_td_t *setup, *stat; u_int length; } ctl; /* Interrupt pipe */ struct { usb_dma_t datadma; int npoll; uhci_soft_qh_t **qhs; } intr; /* Bulk pipe */ struct { uhci_soft_qh_t *sqh; usb_dma_t datadma; u_int length; int isread; } bulk; /* Iso pipe */ struct iso { u_int bufsize; u_int nbuf; usb_dma_t *bufs; uhci_soft_td_t **stds; } iso; } u; }; /* * The uhci_intr_info free list can be global since they contain * no dma specific data. The other free lists do. */ LIST_HEAD(, uhci_intr_info) uhci_ii_free; void uhci_busreset __P((uhci_softc_t *)); usbd_status uhci_run __P((uhci_softc_t *, int run)); uhci_soft_td_t *uhci_alloc_std __P((uhci_softc_t *)); void uhci_free_std __P((uhci_softc_t *, uhci_soft_td_t *)); uhci_soft_qh_t *uhci_alloc_sqh __P((uhci_softc_t *)); void uhci_free_sqh __P((uhci_softc_t *, uhci_soft_qh_t *)); uhci_intr_info_t *uhci_alloc_intr_info __P((uhci_softc_t *)); void uhci_free_intr_info __P((uhci_intr_info_t *ii)); #if 0 void uhci_enter_ctl_q __P((uhci_softc_t *, uhci_soft_qh_t *, uhci_intr_info_t *)); void uhci_exit_ctl_q __P((uhci_softc_t *, uhci_soft_qh_t *)); #endif void uhci_free_std_chain __P((uhci_softc_t *, uhci_soft_td_t *, uhci_soft_td_t *)); usbd_status uhci_alloc_std_chain __P((struct uhci_pipe *, uhci_softc_t *, int, int, int, usb_dma_t *, uhci_soft_td_t **, uhci_soft_td_t **)); void uhci_timo __P((void *)); void uhci_waitintr __P((uhci_softc_t *, usbd_request_handle)); void uhci_check_intr __P((uhci_softc_t *, uhci_intr_info_t *)); void uhci_ii_done __P((uhci_intr_info_t *, int)); void uhci_timeout __P((void *)); void uhci_wakeup_ctrl __P((void *, int, int, void *, int)); void uhci_lock_frames __P((uhci_softc_t *)); void uhci_unlock_frames __P((uhci_softc_t *)); void uhci_add_ctrl __P((uhci_softc_t *, uhci_soft_qh_t *)); void uhci_add_bulk __P((uhci_softc_t *, uhci_soft_qh_t *)); void uhci_remove_ctrl __P((uhci_softc_t *, uhci_soft_qh_t *)); void uhci_remove_bulk __P((uhci_softc_t *, uhci_soft_qh_t *)); int uhci_str __P((usb_string_descriptor_t *, int, char *)); void uhci_wakeup_cb __P((usbd_request_handle reqh)); usbd_status uhci_device_ctrl_transfer __P((usbd_request_handle)); usbd_status uhci_device_ctrl_start __P((usbd_request_handle)); void uhci_device_ctrl_abort __P((usbd_request_handle)); void uhci_device_ctrl_close __P((usbd_pipe_handle)); usbd_status uhci_device_intr_transfer __P((usbd_request_handle)); usbd_status uhci_device_intr_start __P((usbd_request_handle)); void uhci_device_intr_abort __P((usbd_request_handle)); void uhci_device_intr_close __P((usbd_pipe_handle)); usbd_status uhci_device_bulk_transfer __P((usbd_request_handle)); usbd_status uhci_device_bulk_start __P((usbd_request_handle)); void uhci_device_bulk_abort __P((usbd_request_handle)); void uhci_device_bulk_close __P((usbd_pipe_handle)); usbd_status uhci_device_isoc_transfer __P((usbd_request_handle)); usbd_status uhci_device_isoc_start __P((usbd_request_handle)); void uhci_device_isoc_abort __P((usbd_request_handle)); void uhci_device_isoc_close __P((usbd_pipe_handle)); usbd_status uhci_device_isoc_setbuf __P((usbd_pipe_handle, u_int, u_int)); usbd_status uhci_root_ctrl_transfer __P((usbd_request_handle)); usbd_status uhci_root_ctrl_start __P((usbd_request_handle)); void uhci_root_ctrl_abort __P((usbd_request_handle)); void uhci_root_ctrl_close __P((usbd_pipe_handle)); usbd_status uhci_root_intr_transfer __P((usbd_request_handle)); usbd_status uhci_root_intr_start __P((usbd_request_handle)); void uhci_root_intr_abort __P((usbd_request_handle)); void uhci_root_intr_close __P((usbd_pipe_handle)); usbd_status uhci_open __P((usbd_pipe_handle)); void uhci_poll __P((struct usbd_bus *)); usbd_status uhci_device_request __P((usbd_request_handle reqh)); void uhci_ctrl_done __P((uhci_intr_info_t *ii)); void uhci_bulk_done __P((uhci_intr_info_t *ii)); void uhci_add_intr __P((uhci_softc_t *, int, uhci_soft_qh_t *)); void uhci_remove_intr __P((uhci_softc_t *, int, uhci_soft_qh_t *)); usbd_status uhci_device_setintr __P((uhci_softc_t *sc, struct uhci_pipe *pipe, int ival)); void uhci_intr_done __P((uhci_intr_info_t *ii)); void uhci_isoc_done __P((uhci_intr_info_t *ii)); #ifdef UHCI_DEBUG static void uhci_dumpregs __P((uhci_softc_t *)); void uhci_dump_tds __P((uhci_soft_td_t *)); void uhci_dump_qh __P((uhci_soft_qh_t *)); void uhci_dump __P((void)); void uhci_dump_td __P((uhci_soft_td_t *)); #endif #if defined(__NetBSD__) #define UWRITE2(sc, r, x) bus_space_write_2((sc)->iot, (sc)->ioh, (r), (x)) #define UWRITE4(sc, r, x) bus_space_write_4((sc)->iot, (sc)->ioh, (r), (x)) #define UREAD2(sc, r) bus_space_read_2((sc)->iot, (sc)->ioh, (r)) #define UREAD4(sc, r) bus_space_read_4((sc)->iot, (sc)->ioh, (r)) #elif defined(__FreeBSD__) -#define UWRITE2(sc,r,x) outw((sc)->sc_iobase + (r), (x)) -#define UWRITE4(sc,r,x) outl((sc)->sc_iobase + (r), (x)) -#define UREAD1(sc,r) inb((sc)->sc_iobase + (r)) -#define UREAD2(sc,r) inw((sc)->sc_iobase + (r)) -#define UREAD4(sc,r) inl((sc)->sc_iobase + (r)) +#define UWRITE2(sc, r, x) bus_space_write_2((sc)->iot, (sc)->ioh, (r), (x)) +#define UWRITE4(sc, r, x) bus_space_write_4((sc)->iot, (sc)->ioh, (r), (x)) +#define UREAD1(sc, r) bus_space_read_1((sc)->iot, (sc)->ioh, (r)) +#define UREAD2(sc, r) bus_space_read_2((sc)->iot, (sc)->ioh, (r)) +#define UREAD4(sc, r) bus_space_read_4((sc)->iot, (sc)->ioh, (r)) #endif #define UHCICMD(sc, cmd) UWRITE2(sc, UHCI_CMD, cmd) #define UHCISTS(sc) UREAD2(sc, UHCI_STS) #define UHCI_RESET_TIMEOUT 100 /* reset timeout */ #define UHCI_CURFRAME(sc) (UREAD2(sc, UHCI_FRNUM) & UHCI_FRNUM_MASK) #define UHCI_INTR_ENDPT 1 struct usbd_methods uhci_root_ctrl_methods = { uhci_root_ctrl_transfer, uhci_root_ctrl_start, uhci_root_ctrl_abort, uhci_root_ctrl_close, 0, }; struct usbd_methods uhci_root_intr_methods = { uhci_root_intr_transfer, uhci_root_intr_start, uhci_root_intr_abort, uhci_root_intr_close, 0, }; struct usbd_methods uhci_device_ctrl_methods = { uhci_device_ctrl_transfer, uhci_device_ctrl_start, uhci_device_ctrl_abort, uhci_device_ctrl_close, 0, }; struct usbd_methods uhci_device_intr_methods = { uhci_device_intr_transfer, uhci_device_intr_start, uhci_device_intr_abort, uhci_device_intr_close, 0, }; struct usbd_methods uhci_device_bulk_methods = { uhci_device_bulk_transfer, uhci_device_bulk_start, uhci_device_bulk_abort, uhci_device_bulk_close, 0, }; struct usbd_methods uhci_device_isoc_methods = { uhci_device_isoc_transfer, uhci_device_isoc_start, uhci_device_isoc_abort, uhci_device_isoc_close, uhci_device_isoc_setbuf, }; void uhci_busreset(sc) uhci_softc_t *sc; { UHCICMD(sc, UHCI_CMD_GRESET); /* global reset */ usb_delay_ms(&sc->sc_bus, USB_BUS_RESET_DELAY); /* wait a little */ UHCICMD(sc, 0); /* do nothing */ } usbd_status uhci_init(sc) uhci_softc_t *sc; { usbd_status r; int i, j; uhci_soft_qh_t *csqh, *bsqh, *sqh; uhci_soft_td_t *std; usb_dma_t dma; static int uhci_global_init_done = 0; DPRINTFN(1,("uhci_init: start\n")); if (!uhci_global_init_done) { uhci_global_init_done = 1; LIST_INIT(&uhci_ii_free); } uhci_run(sc, 0); /* stop the controller */ UWRITE2(sc, UHCI_INTR, 0); /* disable interrupts */ uhci_busreset(sc); /* Allocate and initialize real frame array. */ r = usb_allocmem(sc->sc_dmatag, UHCI_FRAMELIST_COUNT * sizeof(uhci_physaddr_t), UHCI_FRAMELIST_ALIGN, &dma); if (r != USBD_NORMAL_COMPLETION) return (r); sc->sc_pframes = KERNADDR(&dma); UWRITE2(sc, UHCI_FRNUM, 0); /* set frame number to 0 */ UWRITE4(sc, UHCI_FLBASEADDR, DMAADDR(&dma)); /* set frame list */ /* Allocate the dummy QH where bulk traffic will be queued. */ bsqh = uhci_alloc_sqh(sc); if (!bsqh) return (USBD_NOMEM); bsqh->qh->qh_hlink = UHCI_PTR_T; /* end of QH chain */ bsqh->qh->qh_elink = UHCI_PTR_T; sc->sc_bulk_start = sc->sc_bulk_end = bsqh; /* Allocate the dummy QH where control traffic will be queued. */ csqh = uhci_alloc_sqh(sc); if (!csqh) return (USBD_NOMEM); csqh->qh->hlink = bsqh; csqh->qh->qh_hlink = bsqh->physaddr | UHCI_PTR_Q; csqh->qh->qh_elink = UHCI_PTR_T; sc->sc_ctl_start = sc->sc_ctl_end = csqh; /* * Make all (virtual) frame list pointers point to the interrupt * queue heads and the interrupt queue heads at the control * queue head and point the physical frame list to the virtual. */ for(i = 0; i < UHCI_VFRAMELIST_COUNT; i++) { std = uhci_alloc_std(sc); sqh = uhci_alloc_sqh(sc); if (!std || !sqh) return (USBD_NOMEM); std->td->link.sqh = sqh; std->td->td_link = sqh->physaddr | UHCI_PTR_Q; std->td->td_status = UHCI_TD_IOS; /* iso, inactive */ std->td->td_token = 0; std->td->td_buffer = 0; sqh->qh->hlink = csqh; sqh->qh->qh_hlink = csqh->physaddr | UHCI_PTR_Q; sqh->qh->elink = 0; sqh->qh->qh_elink = UHCI_PTR_T; sc->sc_vframes[i].htd = std; sc->sc_vframes[i].etd = std; sc->sc_vframes[i].hqh = sqh; sc->sc_vframes[i].eqh = sqh; for (j = i; j < UHCI_FRAMELIST_COUNT; j += UHCI_VFRAMELIST_COUNT) sc->sc_pframes[j] = std->physaddr; } LIST_INIT(&sc->sc_intrhead); /* Set up the bus struct. */ sc->sc_bus.open_pipe = uhci_open; sc->sc_bus.pipe_size = sizeof(struct uhci_pipe); sc->sc_bus.do_poll = uhci_poll; DPRINTFN(1,("uhci_init: enabling\n")); UWRITE2(sc, UHCI_INTR, UHCI_INTR_TOCRCIE | UHCI_INTR_RIE | UHCI_INTR_IOCE | UHCI_INTR_SPIE); /* enable interrupts */ return (uhci_run(sc, 1)); /* and here we go... */ } #ifdef UHCI_DEBUG static void uhci_dumpregs(sc) uhci_softc_t *sc; { printf("%s: regs: cmd=%04x, sts=%04x, intr=%04x, frnum=%04x, " "flbase=%08x, sof=%02x, portsc1=%04x, portsc2=%04x, ", USBDEVNAME(sc->sc_bus.bdev), UREAD2(sc, UHCI_CMD), UREAD2(sc, UHCI_STS), UREAD2(sc, UHCI_INTR), UREAD2(sc, UHCI_FRNUM), UREAD4(sc, UHCI_FLBASEADDR), UREAD1(sc, UHCI_SOF), UREAD2(sc, UHCI_PORTSC1), UREAD2(sc, UHCI_PORTSC2)); } int uhci_longtd = 1; void uhci_dump_td(p) uhci_soft_td_t *p; { printf("TD(%p) at %08lx link=0x%08lx st=0x%08lx tok=0x%08lx buf=0x%08lx\n", p, (long)p->physaddr, (long)p->td->td_link, (long)p->td->td_status, (long)p->td->td_token, (long)p->td->td_buffer); if (uhci_longtd) printf(" %b %b,errcnt=%d,actlen=%d pid=%02x,addr=%d,endpt=%d," "D=%d,maxlen=%d\n", (int)p->td->td_link, "\20\1T\2Q\3VF", (int)p->td->td_status, "\20\22BITSTUFF\23CRCTO\24NAK\25BABBLE\26DBUFFER\27" "STALLED\30ACTIVE\31IOC\32ISO\33LS\36SPD", UHCI_TD_GET_ERRCNT(p->td->td_status), UHCI_TD_GET_ACTLEN(p->td->td_status), UHCI_TD_GET_PID(p->td->td_token), UHCI_TD_GET_DEVADDR(p->td->td_token), UHCI_TD_GET_ENDPT(p->td->td_token), UHCI_TD_GET_DT(p->td->td_token), UHCI_TD_GET_MAXLEN(p->td->td_token)); } void uhci_dump_qh(p) uhci_soft_qh_t *p; { printf("QH(%p) at %08x: hlink=%08x elink=%08x\n", p, (int)p->physaddr, p->qh->qh_hlink, p->qh->qh_elink); } #if 0 void uhci_dump() { uhci_softc_t *sc = uhci; uhci_dumpregs(sc); printf("intrs=%d\n", sc->sc_intrs); printf("framelist[i].link = %08x\n", sc->sc_framelist[0].link); uhci_dump_qh(sc->sc_ctl_start->qh->hlink); } #endif void uhci_dump_tds(std) uhci_soft_td_t *std; { uhci_soft_td_t *p; for(p = std; p; p = p->td->link.std) uhci_dump_td(p); } #endif /* * This routine is executed periodically and simulates interrupts * from the root controller interrupt pipe for port status change. */ void uhci_timo(addr) void *addr; { usbd_request_handle reqh = addr; usbd_pipe_handle pipe = reqh->pipe; uhci_softc_t *sc = (uhci_softc_t *)pipe->device->bus; struct uhci_pipe *upipe = (struct uhci_pipe *)pipe; int s; u_char *p; DPRINTFN(15, ("uhci_timo\n")); p = KERNADDR(&upipe->u.intr.datadma); p[0] = 0; if (UREAD2(sc, UHCI_PORTSC1) & (UHCI_PORTSC_CSC|UHCI_PORTSC_OCIC)) p[0] |= 1<<1; if (UREAD2(sc, UHCI_PORTSC2) & (UHCI_PORTSC_CSC|UHCI_PORTSC_OCIC)) p[0] |= 1<<2; s = splusb(); if (p[0] != 0) { reqh->actlen = 1; reqh->status = USBD_NORMAL_COMPLETION; reqh->xfercb(reqh); } if (reqh->pipe->intrreqh == reqh) { usb_timeout(uhci_timo, reqh, sc->sc_ival, reqh->timo_handle); } else { usb_freemem(sc->sc_dmatag, &upipe->u.intr.datadma); usb_start_next(reqh->pipe); } splx(s); } void uhci_lock_frames(sc) uhci_softc_t *sc; { int s = splusb(); while (sc->sc_vflock) { sc->sc_vflock |= UHCI_WANT_LOCK; tsleep(&sc->sc_vflock, PRIBIO, "uhcqhl", 0); } sc->sc_vflock = UHCI_HAS_LOCK; splx(s); } void uhci_unlock_frames(sc) uhci_softc_t *sc; { int s = splusb(); sc->sc_vflock &= ~UHCI_HAS_LOCK; if (sc->sc_vflock & UHCI_WANT_LOCK) wakeup(&sc->sc_vflock); splx(s); } /* * Allocate an interrupt information struct. A free list is kept * for fast allocation. */ uhci_intr_info_t * uhci_alloc_intr_info(sc) uhci_softc_t *sc; { uhci_intr_info_t *ii; ii = LIST_FIRST(&uhci_ii_free); if (ii) LIST_REMOVE(ii, list); else { ii = malloc(sizeof(uhci_intr_info_t), M_USBDEV, M_NOWAIT); } ii->sc = sc; #if defined(__FreeBSD__) callout_handle_init(&ii->timeout_handle); #endif return ii; } void uhci_free_intr_info(ii) uhci_intr_info_t *ii; { LIST_INSERT_HEAD(&uhci_ii_free, ii, list); /* and put on free list */ } /* Add control QH, called at splusb(). */ void uhci_add_ctrl(sc, sqh) uhci_softc_t *sc; uhci_soft_qh_t *sqh; { uhci_qh_t *eqh; DPRINTFN(10, ("uhci_add_ctrl: sqh=%p\n", sqh)); eqh = sc->sc_ctl_end->qh; sqh->qh->hlink = eqh->hlink; sqh->qh->qh_hlink = eqh->qh_hlink; eqh->hlink = sqh; eqh->qh_hlink = sqh->physaddr | UHCI_PTR_Q; sc->sc_ctl_end = sqh; } /* Remove control QH, called at splusb(). */ void uhci_remove_ctrl(sc, sqh) uhci_softc_t *sc; uhci_soft_qh_t *sqh; { uhci_soft_qh_t *pqh; DPRINTFN(10, ("uhci_remove_ctrl: sqh=%p\n", sqh)); for (pqh = sc->sc_ctl_start; pqh->qh->hlink != sqh; pqh=pqh->qh->hlink) #if defined(DIAGNOSTIC) || defined(UHCI_DEBUG) if (pqh->qh->qh_hlink & UHCI_PTR_T) { printf("uhci_remove_ctrl: QH not found\n"); return; } #else ; #endif pqh->qh->hlink = sqh->qh->hlink; pqh->qh->qh_hlink = sqh->qh->qh_hlink; if (sc->sc_ctl_end == sqh) sc->sc_ctl_end = pqh; } /* Add bulk QH, called at splusb(). */ void uhci_add_bulk(sc, sqh) uhci_softc_t *sc; uhci_soft_qh_t *sqh; { uhci_qh_t *eqh; DPRINTFN(10, ("uhci_add_bulk: sqh=%p\n", sqh)); eqh = sc->sc_bulk_end->qh; sqh->qh->hlink = eqh->hlink; sqh->qh->qh_hlink = eqh->qh_hlink; eqh->hlink = sqh; eqh->qh_hlink = sqh->physaddr | UHCI_PTR_Q; sc->sc_bulk_end = sqh; } /* Remove bulk QH, called at splusb(). */ void uhci_remove_bulk(sc, sqh) uhci_softc_t *sc; uhci_soft_qh_t *sqh; { uhci_soft_qh_t *pqh; DPRINTFN(10, ("uhci_remove_bulk: sqh=%p\n", sqh)); for (pqh = sc->sc_bulk_start; pqh->qh->hlink != sqh; pqh = pqh->qh->hlink) #if defined(DIAGNOSTIC) || defined(UHCI_DEBUG) if (pqh->qh->qh_hlink & UHCI_PTR_T) { printf("uhci_remove_bulk: QH not found\n"); return; } #else ; #endif pqh->qh->hlink = sqh->qh->hlink; pqh->qh->qh_hlink = sqh->qh->qh_hlink; if (sc->sc_bulk_end == sqh) sc->sc_bulk_end = pqh; } int uhci_intr(p) void *p; { uhci_softc_t *sc = p; int status, ret; uhci_intr_info_t *ii; sc->sc_intrs++; #if defined(UHCI_DEBUG) if (uhcidebug > 9) { printf("uhci_intr %p\n", sc); uhci_dumpregs(sc); } #endif status = UREAD2(sc, UHCI_STS); ret = 0; if (status & UHCI_STS_USBINT) { UWRITE2(sc, UHCI_STS, UHCI_STS_USBINT); /* acknowledge */ ret = 1; } if (status & UHCI_STS_USBEI) { UWRITE2(sc, UHCI_STS, UHCI_STS_USBEI); /* acknowledge */ ret = 1; } if (status & UHCI_STS_RD) { UWRITE2(sc, UHCI_STS, UHCI_STS_RD); /* acknowledge */ printf("%s: resume detect\n", USBDEVNAME(sc->sc_bus.bdev)); ret = 1; } if (status & UHCI_STS_HSE) { UWRITE2(sc, UHCI_STS, UHCI_STS_HSE); /* acknowledge */ printf("%s: Host System Error\n", USBDEVNAME(sc->sc_bus.bdev)); ret = 1; } if (status & UHCI_STS_HCPE) { UWRITE2(sc, UHCI_STS, UHCI_STS_HCPE); /* acknowledge */ printf("%s: Host System Error\n", USBDEVNAME(sc->sc_bus.bdev)); ret = 1; } if (status & UHCI_STS_HCH) printf("%s: controller halted\n", USBDEVNAME(sc->sc_bus.bdev)); if (!ret) return 0; /* * Interrupts on UHCI really suck. When the host controller * interrupts because a transfer is completed there is no * way of knowing which transfer it was. You can scan down * the TDs and QHs of the previous frame to limit the search, * but that assumes that the interrupt was not delayed by more * than 1 ms, which may not always be true (e.g. after debug * output on a slow console). * We scan all interrupt descriptors to see if any have * completed. */ for (ii = LIST_FIRST(&sc->sc_intrhead); ii; ii = LIST_NEXT(ii, list)) uhci_check_intr(sc, ii); DPRINTFN(10, ("uhci_intr: exit\n")); return 1; } /* Check for an interrupt. */ void uhci_check_intr(sc, ii) uhci_softc_t *sc; uhci_intr_info_t *ii; { struct uhci_pipe *upipe; uhci_soft_td_t *std, *lstd; u_int32_t status; DPRINTFN(15, ("uhci_check_intr: ii=%p\n", ii)); #ifdef DIAGNOSTIC if (!ii) { printf("uhci_check_intr: no ii? %p\n", ii); return; } #endif if (!ii->stdstart) return; lstd = ii->stdend; #ifdef DIAGNOSTIC if (!lstd) { printf("uhci_check_intr: std==0\n"); return; } #endif /* If the last TD is still active the whole transfer probably is. */ if (lstd->td->td_status & UHCI_TD_ACTIVE) { DPRINTFN(15, ("uhci_check_intr: active ii=%p\n", ii)); for (std = ii->stdstart; std != lstd; std = std->td->link.std){ status = std->td->td_status; if ((status & UHCI_TD_STALLED) || (status & (UHCI_TD_SPD | UHCI_TD_ACTIVE)) == UHCI_TD_SPD) goto done; } DPRINTFN(15, ("uhci_check_intr: ii=%p std=%p still active\n", ii, ii->stdstart)); return; } done: usb_untimeout(uhci_timeout, ii, ii->timeout_handle); upipe = (struct uhci_pipe *)ii->reqh->pipe; upipe->pipe.endpoint->toggle = upipe->newtoggle; uhci_ii_done(ii, 0); } void uhci_ii_done(ii, timo) uhci_intr_info_t *ii; int timo; { usbd_request_handle reqh = ii->reqh; uhci_soft_td_t *std; u_int32_t tst; int len, status, attr; DPRINTFN(10, ("uhci_ii_done: ii=%p ready %d\n", ii, timo)); #ifdef DIAGNOSTIC { int s = splhigh(); if (ii->isdone) { printf("uhci_ii_done: is done!\n"); splx(s); return; } ii->isdone = 1; splx(s); } #endif /* The transfer is done, compute length and status. */ /* XXX Should stop at first inactive to get toggle right. */ /* XXX Is this correct for control xfers? */ for (len = status = 0, std = ii->stdstart; std != 0; std = std->td->link.std) { tst = std->td->td_status; status |= tst; #ifdef UHCI_DEBUG if ((tst & UHCI_TD_ERROR) && uhcidebug) { printf("uhci_ii_done: intr error TD:\n"); uhci_dump_td(std); } #endif if (UHCI_TD_GET_PID(std->td->td_token) != UHCI_TD_PID_SETUP) len += UHCI_TD_GET_ACTLEN(tst); } status &= UHCI_TD_ERROR; DPRINTFN(10, ("uhci_ii_done: len=%d, status=0x%x\n", len, status)); if (status != 0) { DPRINTFN(-1+(status & UHCI_TD_STALLED), ("uhci_ii_done: error, addr=%d, endpt=0x%02x, " "status 0x%b\n", reqh->pipe->device->address, reqh->pipe->endpoint->edesc->bEndpointAddress, (int)status, "\20\22BITSTUFF\23CRCTO\24NAK\25BABBLE\26DBUFFER\27" "STALLED\30ACTIVE")); if (status & UHCI_TD_STALLED) reqh->status = USBD_STALLED; else reqh->status = USBD_IOERROR; /* more info XXX */ reqh->actlen = 0; } else { reqh->status = USBD_NORMAL_COMPLETION; reqh->actlen = len; } if (timo) { /* We got a timeout. Make sure transaction is not active. */ reqh->status = USBD_TIMEOUT; for (std = ii->stdstart; std != 0; std = std->td->link.std) std->td->td_status &= ~UHCI_TD_ACTIVE; /* XXX should we wait 1 ms */ } DPRINTFN(5, ("uhci_ii_done: calling handler ii=%p\n", ii)); attr = reqh->pipe->endpoint->edesc->bmAttributes; switch (attr & UE_XFERTYPE) { case UE_CONTROL: uhci_ctrl_done(ii); usb_start_next(reqh->pipe); break; case UE_ISOCHRONOUS: uhci_isoc_done(ii); usb_start_next(reqh->pipe); break; case UE_BULK: uhci_bulk_done(ii); usb_start_next(reqh->pipe); break; case UE_INTERRUPT: uhci_intr_done(ii); break; } /* And finally execute callback. */ reqh->xfercb(reqh); } /* * Called when a request does not complete. */ void uhci_timeout(addr) void *addr; { uhci_intr_info_t *ii = addr; int s; DPRINTF(("uhci_timeout: ii=%p\n", ii)); s = splusb(); uhci_ii_done(ii, 1); splx(s); } /* * Wait here until controller claims to have an interrupt. * Then call uhci_intr and return. Use timeout to avoid waiting * too long. * Only used during boot when interrupts are not enabled yet. */ void uhci_waitintr(sc, reqh) uhci_softc_t *sc; usbd_request_handle reqh; { int timo = reqh->timeout; int usecs; uhci_intr_info_t *ii; DPRINTFN(15,("uhci_waitintr: timeout = %ds\n", timo)); reqh->status = USBD_IN_PROGRESS; for (usecs = timo * 1000000 / hz; usecs > 0; usecs -= 1000) { usb_delay_ms(&sc->sc_bus, 1); DPRINTFN(10,("uhci_waitintr: 0x%04x\n", UREAD2(sc, UHCI_STS))); if (UREAD2(sc, UHCI_STS) & UHCI_STS_USBINT) { uhci_intr(sc); if (reqh->status != USBD_IN_PROGRESS) return; } } /* Timeout */ DPRINTF(("uhci_waitintr: timeout\n")); for (ii = LIST_FIRST(&sc->sc_intrhead); ii && ii->reqh != reqh; ii = LIST_NEXT(ii, list)) ; if (ii) uhci_ii_done(ii, 1); else panic("uhci_waitintr: lost intr_info\n"); } void uhci_poll(bus) struct usbd_bus *bus; { uhci_softc_t *sc = (uhci_softc_t *)bus; if (UREAD2(sc, UHCI_STS) & UHCI_STS_USBINT) uhci_intr(sc); } #if 0 void uhci_reset(p) void *p; { uhci_softc_t *sc = p; int n; UHCICMD(sc, UHCI_CMD_HCRESET); /* The reset bit goes low when the controller is done. */ for (n = 0; n < UHCI_RESET_TIMEOUT && (UREAD2(sc, UHCI_CMD) & UHCI_CMD_HCRESET); n++) delay(100); if (n >= UHCI_RESET_TIMEOUT) printf("%s: controller did not reset\n", USBDEVNAME(sc->sc_bus.bdev)); } #endif usbd_status uhci_run(sc, run) uhci_softc_t *sc; int run; { int s, n, running; s = splusb(); running = ((UREAD2(sc, UHCI_STS) & UHCI_STS_HCH) == 0); if (run == running) { splx(s); return (USBD_NORMAL_COMPLETION); } UWRITE2(sc, UHCI_CMD, run ? UHCI_CMD_RS : 0); for(n = 0; n < 10; n++) { running = ((UREAD2(sc, UHCI_STS) & UHCI_STS_HCH) == 0); /* return when we've entered the state we want */ if (run == running) { splx(s); return (USBD_NORMAL_COMPLETION); } usb_delay_ms(&sc->sc_bus, 1); } splx(s); printf("%s: cannot %s\n", USBDEVNAME(sc->sc_bus.bdev), run ? "start" : "stop"); return (USBD_IOERROR); } /* * Memory management routines. * uhci_alloc_std allocates TDs * uhci_alloc_sqh allocates QHs * These two routines do their own free list management, * partly for speed, partly because allocating DMAable memory * has page size granularaity so much memory would be wasted if * only one TD/QH (32 bytes) was placed in each allocated chunk. */ uhci_soft_td_t * uhci_alloc_std(sc) uhci_softc_t *sc; { uhci_soft_td_t *std; usbd_status r; int i; usb_dma_t dma; if (!sc->sc_freetds) { DPRINTFN(2,("uhci_alloc_std: allocating chunk\n")); std = malloc(sizeof(uhci_soft_td_t) * UHCI_TD_CHUNK, M_USBDEV, M_NOWAIT); if (!std) return (0); r = usb_allocmem(sc->sc_dmatag, UHCI_TD_SIZE * UHCI_TD_CHUNK, UHCI_TD_ALIGN, &dma); if (r != USBD_NORMAL_COMPLETION) { free(std, M_USBDEV); return (0); } for(i = 0; i < UHCI_TD_CHUNK; i++, std++) { std->physaddr = DMAADDR(&dma) + i * UHCI_TD_SIZE; std->td = (uhci_td_t *) ((char *)KERNADDR(&dma) + i * UHCI_TD_SIZE); std->td->link.std = sc->sc_freetds; sc->sc_freetds = std; } } std = sc->sc_freetds; sc->sc_freetds = std->td->link.std; memset(std->td, 0, UHCI_TD_SIZE); return std; } void uhci_free_std(sc, std) uhci_softc_t *sc; uhci_soft_td_t *std; { #ifdef DIAGNOSTIC #define TD_IS_FREE 0x12345678 if (std->td->td_token == TD_IS_FREE) { printf("uhci_free_std: freeing free TD %p\n", std); return; } std->td->td_token = TD_IS_FREE; #endif std->td->link.std = sc->sc_freetds; sc->sc_freetds = std; } uhci_soft_qh_t * uhci_alloc_sqh(sc) uhci_softc_t *sc; { uhci_soft_qh_t *sqh; usbd_status r; int i, offs; usb_dma_t dma; if (!sc->sc_freeqhs) { DPRINTFN(2, ("uhci_alloc_sqh: allocating chunk\n")); sqh = malloc(sizeof(uhci_soft_qh_t) * UHCI_QH_CHUNK, M_USBDEV, M_NOWAIT); if (!sqh) return 0; r = usb_allocmem(sc->sc_dmatag, UHCI_QH_SIZE * UHCI_QH_CHUNK, UHCI_QH_ALIGN, &dma); if (r != USBD_NORMAL_COMPLETION) { free(sqh, M_USBDEV); return 0; } for(i = 0; i < UHCI_QH_CHUNK; i++, sqh++) { offs = i * UHCI_QH_SIZE; sqh->physaddr = DMAADDR(&dma) + offs; sqh->qh = (uhci_qh_t *) ((char *)KERNADDR(&dma) + offs); sqh->qh->hlink = sc->sc_freeqhs; sc->sc_freeqhs = sqh; } } sqh = sc->sc_freeqhs; sc->sc_freeqhs = sqh->qh->hlink; memset(sqh->qh, 0, UHCI_QH_SIZE); return (sqh); } void uhci_free_sqh(sc, sqh) uhci_softc_t *sc; uhci_soft_qh_t *sqh; { sqh->qh->hlink = sc->sc_freeqhs; sc->sc_freeqhs = sqh; } #if 0 /* * Enter a list of transfers onto a control queue. * Called at splusb() */ void uhci_enter_ctl_q(sc, sqh, ii) uhci_softc_t *sc; uhci_soft_qh_t *sqh; uhci_intr_info_t *ii; { DPRINTFN(5, ("uhci_enter_ctl_q: sqh=%p\n", sqh)); } #endif void uhci_free_std_chain(sc, std, stdend) uhci_softc_t *sc; uhci_soft_td_t *std; uhci_soft_td_t *stdend; { uhci_soft_td_t *p; for (; std != stdend; std = p) { p = std->td->link.std; uhci_free_std(sc, std); } } usbd_status uhci_alloc_std_chain(upipe, sc, len, rd, spd, dma, sp, ep) struct uhci_pipe *upipe; uhci_softc_t *sc; int len, rd, spd; usb_dma_t *dma; uhci_soft_td_t **sp, **ep; { uhci_soft_td_t *p, *lastp; uhci_physaddr_t lastlink; int i, ntd, l, tog, maxp; u_int32_t status; int addr = upipe->pipe.device->address; int endpt = upipe->pipe.endpoint->edesc->bEndpointAddress; DPRINTFN(15, ("uhci_alloc_std_chain: addr=%d endpt=%d len=%d ls=%d " "spd=%d\n", addr, endpt, len, upipe->pipe.device->lowspeed, spd)); if (len == 0) { *sp = *ep = 0; DPRINTFN(-1,("uhci_alloc_std_chain: len=0\n")); return (USBD_NORMAL_COMPLETION); } maxp = UGETW(upipe->pipe.endpoint->edesc->wMaxPacketSize); if (maxp == 0) { printf("uhci_alloc_std_chain: maxp=0\n"); return (USBD_INVAL); } ntd = (len + maxp - 1) / maxp; tog = upipe->pipe.endpoint->toggle; if (ntd % 2 == 0) tog ^= 1; upipe->newtoggle = tog ^ 1; lastp = 0; lastlink = UHCI_PTR_T; ntd--; status = UHCI_TD_SET_ERRCNT(2) | UHCI_TD_ACTIVE; if (upipe->pipe.device->lowspeed) status |= UHCI_TD_LS; if (spd) status |= UHCI_TD_SPD; for (i = ntd; i >= 0; i--) { p = uhci_alloc_std(sc); if (!p) { uhci_free_std_chain(sc, lastp, 0); return (USBD_NOMEM); } p->td->link.std = lastp; p->td->td_link = lastlink; lastp = p; lastlink = p->physaddr; p->td->td_status = status; if (i == ntd) { /* last TD */ l = len % maxp; if (l == 0) l = maxp; *ep = p; } else l = maxp; p->td->td_token = rd ? UHCI_TD_IN (l, endpt, addr, tog) : UHCI_TD_OUT(l, endpt, addr, tog); p->td->td_buffer = DMAADDR(dma) + i * maxp; tog ^= 1; } *sp = lastp; /*upipe->pipe.endpoint->toggle = tog;*/ DPRINTFN(10, ("uhci_alloc_std_chain: oldtog=%d newtog=%d\n", upipe->pipe.endpoint->toggle, upipe->newtoggle)); return (USBD_NORMAL_COMPLETION); } usbd_status uhci_device_bulk_transfer(reqh) usbd_request_handle reqh; { int s; usbd_status r; s = splusb(); r = usb_insert_transfer(reqh); splx(s); if (r != USBD_NORMAL_COMPLETION) return (r); else return (uhci_device_bulk_start(reqh)); } usbd_status uhci_device_bulk_start(reqh) usbd_request_handle reqh; { struct uhci_pipe *upipe = (struct uhci_pipe *)reqh->pipe; usbd_device_handle dev = upipe->pipe.device; uhci_softc_t *sc = (uhci_softc_t *)dev->bus; uhci_intr_info_t *ii = upipe->iinfo; uhci_soft_td_t *xfer, *xferend; uhci_soft_qh_t *sqh; usb_dma_t *dmap; usbd_status r; int len, isread; int s; DPRINTFN(3, ("uhci_device_bulk_transfer: reqh=%p buf=%p len=%d " "flags=%d\n", reqh, reqh->buffer, reqh->length, reqh->flags)); if (reqh->isreq) panic("uhci_device_bulk_transfer: a request\n"); len = reqh->length; dmap = &upipe->u.bulk.datadma; isread = reqh->pipe->endpoint->edesc->bEndpointAddress & UE_IN; sqh = upipe->u.bulk.sqh; upipe->u.bulk.isread = isread; upipe->u.bulk.length = len; r = usb_allocmem(sc->sc_dmatag, len, 0, dmap); if (r != USBD_NORMAL_COMPLETION) goto ret1; r = uhci_alloc_std_chain(upipe, sc, len, isread, reqh->flags & USBD_SHORT_XFER_OK, dmap, &xfer, &xferend); if (r != USBD_NORMAL_COMPLETION) goto ret2; xferend->td->td_status |= UHCI_TD_IOC; if (!isread && len != 0) memcpy(KERNADDR(dmap), reqh->buffer, len); #ifdef UHCI_DEBUG if (uhcidebug > 10) { printf("uhci_device_bulk_transfer: xfer(1)\n"); uhci_dump_tds(xfer); } #endif /* Set up interrupt info. */ ii->reqh = reqh; ii->stdstart = xfer; ii->stdend = xferend; #if defined(__FreeBSD__) callout_handle_init(&ii->timeout_handle); #endif #ifdef DIAGNOSTIC ii->isdone = 0; #endif sqh->qh->elink = xfer; sqh->qh->qh_elink = xfer->physaddr; sqh->intr_info = ii; s = splusb(); uhci_add_bulk(sc, sqh); LIST_INSERT_HEAD(&sc->sc_intrhead, ii, list); if (reqh->timeout && !sc->sc_bus.use_polling) { usb_timeout(uhci_timeout, ii, MS_TO_TICKS(reqh->timeout), ii->timeout_handle); } splx(s); #ifdef UHCI_DEBUG if (uhcidebug > 10) { printf("uhci_device_bulk_transfer: xfer(2)\n"); uhci_dump_tds(xfer); } #endif return (USBD_IN_PROGRESS); ret2: if (len != 0) usb_freemem(sc->sc_dmatag, dmap); ret1: return (r); } /* Abort a device bulk request. */ void uhci_device_bulk_abort(reqh) usbd_request_handle reqh; { /* XXX inactivate */ usb_delay_ms(reqh->pipe->device->bus, 1);/* make sure it is done */ /* XXX call done */ } /* Close a device bulk pipe. */ void uhci_device_bulk_close(pipe) usbd_pipe_handle pipe; { struct uhci_pipe *upipe = (struct uhci_pipe *)pipe; usbd_device_handle dev = upipe->pipe.device; uhci_softc_t *sc = (uhci_softc_t *)dev->bus; uhci_free_sqh(sc, upipe->u.bulk.sqh); uhci_free_intr_info(upipe->iinfo); /* XXX free other resources */ } usbd_status uhci_device_ctrl_transfer(reqh) usbd_request_handle reqh; { int s; usbd_status r; s = splusb(); r = usb_insert_transfer(reqh); splx(s); if (r != USBD_NORMAL_COMPLETION) return (r); else return (uhci_device_ctrl_start(reqh)); } usbd_status uhci_device_ctrl_start(reqh) usbd_request_handle reqh; { uhci_softc_t *sc = (uhci_softc_t *)reqh->pipe->device->bus; usbd_status r; if (!reqh->isreq) panic("uhci_device_ctrl_transfer: not a request\n"); r = uhci_device_request(reqh); if (r != USBD_NORMAL_COMPLETION) return (r); if (sc->sc_bus.use_polling) uhci_waitintr(sc, reqh); return (USBD_IN_PROGRESS); } usbd_status uhci_device_intr_transfer(reqh) usbd_request_handle reqh; { int s; usbd_status r; s = splusb(); r = usb_insert_transfer(reqh); splx(s); if (r != USBD_NORMAL_COMPLETION) return (r); else return (uhci_device_intr_start(reqh)); } usbd_status uhci_device_intr_start(reqh) usbd_request_handle reqh; { struct uhci_pipe *upipe = (struct uhci_pipe *)reqh->pipe; usbd_device_handle dev = upipe->pipe.device; uhci_softc_t *sc = (uhci_softc_t *)dev->bus; uhci_intr_info_t *ii = upipe->iinfo; uhci_soft_td_t *xfer, *xferend; uhci_soft_qh_t *sqh; usb_dma_t *dmap; usbd_status r; int len, i; int s; DPRINTFN(3, ("uhci_device_intr_transfer: reqh=%p buf=%p len=%d " "flags=%d\n", reqh, reqh->buffer, reqh->length, reqh->flags)); if (reqh->isreq) panic("uhci_device_intr_transfer: a request\n"); len = reqh->length; dmap = &upipe->u.intr.datadma; if (len == 0) return (USBD_INVAL); /* XXX should it be? */ r = usb_allocmem(sc->sc_dmatag, len, 0, dmap); if (r != USBD_NORMAL_COMPLETION) goto ret1; r = uhci_alloc_std_chain(upipe, sc, len, 1, reqh->flags & USBD_SHORT_XFER_OK, dmap, &xfer, &xferend); if (r != USBD_NORMAL_COMPLETION) goto ret2; xferend->td->td_status |= UHCI_TD_IOC; #ifdef UHCI_DEBUG if (uhcidebug > 10) { printf("uhci_device_intr_transfer: xfer(1)\n"); uhci_dump_tds(xfer); uhci_dump_qh(upipe->u.intr.qhs[0]); } #endif s = splusb(); /* Set up interrupt info. */ ii->reqh = reqh; ii->stdstart = xfer; ii->stdend = xferend; #ifdef DIAGNOSTIC ii->isdone = 0; #endif DPRINTFN(10,("uhci_device_intr_transfer: qhs[0]=%p\n", upipe->u.intr.qhs[0])); for (i = 0; i < upipe->u.intr.npoll; i++) { sqh = upipe->u.intr.qhs[i]; sqh->qh->elink = xfer; sqh->qh->qh_elink = xfer->physaddr; } splx(s); #ifdef UHCI_DEBUG if (uhcidebug > 10) { printf("uhci_device_intr_transfer: xfer(2)\n"); uhci_dump_tds(xfer); uhci_dump_qh(upipe->u.intr.qhs[0]); } #endif return (USBD_IN_PROGRESS); ret2: if (len != 0) usb_freemem(sc->sc_dmatag, dmap); ret1: return (r); } /* Abort a device control request. */ void uhci_device_ctrl_abort(reqh) usbd_request_handle reqh; { /* XXX inactivate */ usb_delay_ms(reqh->pipe->device->bus, 1); /* make sure it is done */ /* XXX call done */ } /* Close a device control pipe. */ void uhci_device_ctrl_close(pipe) usbd_pipe_handle pipe; { struct uhci_pipe *upipe = (struct uhci_pipe *)pipe; uhci_free_intr_info(upipe->iinfo); /* XXX free other resources */ } /* Abort a device interrupt request. */ void uhci_device_intr_abort(reqh) usbd_request_handle reqh; { struct uhci_pipe *upipe; DPRINTFN(1, ("uhci_device_intr_abort: reqh=%p\n", reqh)); /* XXX inactivate */ usb_delay_ms(reqh->pipe->device->bus, 2); /* make sure it is done */ if (reqh->pipe->intrreqh == reqh) { DPRINTF(("uhci_device_intr_abort: remove\n")); reqh->pipe->intrreqh = 0; upipe = (struct uhci_pipe *)reqh->pipe; uhci_intr_done(upipe->u.intr.qhs[0]->intr_info); } } /* Close a device interrupt pipe. */ void uhci_device_intr_close(pipe) usbd_pipe_handle pipe; { struct uhci_pipe *upipe = (struct uhci_pipe *)pipe; uhci_softc_t *sc = (uhci_softc_t *)pipe->device->bus; int i, s, npoll; upipe->iinfo->stdstart = 0; /* inactive */ /* Unlink descriptors from controller data structures. */ npoll = upipe->u.intr.npoll; uhci_lock_frames(sc); for (i = 0; i < npoll; i++) uhci_remove_intr(sc, upipe->u.intr.qhs[i]->pos, upipe->u.intr.qhs[i]); uhci_unlock_frames(sc); /* * We now have to wait for any activity on the physical * descriptors to stop. */ usb_delay_ms(&sc->sc_bus, 2); for(i = 0; i < npoll; i++) uhci_free_sqh(sc, upipe->u.intr.qhs[i]); free(upipe->u.intr.qhs, M_USB); s = splusb(); LIST_REMOVE(upipe->iinfo, list); /* remove from active list */ splx(s); uhci_free_intr_info(upipe->iinfo); /* XXX free other resources */ } usbd_status uhci_device_request(reqh) usbd_request_handle reqh; { struct uhci_pipe *upipe = (struct uhci_pipe *)reqh->pipe; usb_device_request_t *req = &reqh->request; usbd_device_handle dev = upipe->pipe.device; uhci_softc_t *sc = (uhci_softc_t *)dev->bus; int addr = dev->address; int endpt = upipe->pipe.endpoint->edesc->bEndpointAddress; uhci_intr_info_t *ii = upipe->iinfo; uhci_soft_td_t *setup, *xfer, *stat, *next, *xferend; uhci_soft_qh_t *sqh; usb_dma_t *dmap; int len; u_int32_t ls; usbd_status r; int isread; int s; DPRINTFN(3,("uhci_device_control type=0x%02x, request=0x%02x, " "wValue=0x%04x, wIndex=0x%04x len=%d, addr=%d, endpt=%d\n", req->bmRequestType, req->bRequest, UGETW(req->wValue), UGETW(req->wIndex), UGETW(req->wLength), addr, endpt)); ls = dev->lowspeed ? UHCI_TD_LS : 0; isread = req->bmRequestType & UT_READ; len = UGETW(req->wLength); setup = upipe->u.ctl.setup; stat = upipe->u.ctl.stat; sqh = upipe->u.ctl.sqh; dmap = &upipe->u.ctl.datadma; /* Set up data transaction */ if (len != 0) { r = usb_allocmem(sc->sc_dmatag, len, 0, dmap); if (r != USBD_NORMAL_COMPLETION) goto ret1; upipe->pipe.endpoint->toggle = 1; r = uhci_alloc_std_chain(upipe, sc, len, isread, reqh->flags & USBD_SHORT_XFER_OK, dmap, &xfer, &xferend); if (r != USBD_NORMAL_COMPLETION) goto ret2; next = xfer; xferend->td->link.std = stat; xferend->td->td_link = stat->physaddr; } else { next = stat; } upipe->u.ctl.length = len; memcpy(KERNADDR(&upipe->u.ctl.reqdma), req, sizeof *req); if (!isread && len != 0) memcpy(KERNADDR(dmap), reqh->buffer, len); setup->td->link.std = next; setup->td->td_link = next->physaddr; setup->td->td_status = UHCI_TD_SET_ERRCNT(2) | ls | UHCI_TD_ACTIVE; setup->td->td_token = UHCI_TD_SETUP(sizeof *req, endpt, addr); setup->td->td_buffer = DMAADDR(&upipe->u.ctl.reqdma); stat->td->link.std = 0; stat->td->td_link = UHCI_PTR_T; stat->td->td_status = UHCI_TD_SET_ERRCNT(2) | ls | UHCI_TD_ACTIVE | UHCI_TD_IOC; stat->td->td_token = isread ? UHCI_TD_OUT(0, endpt, addr, 1) : UHCI_TD_IN (0, endpt, addr, 1); stat->td->td_buffer = 0; #ifdef UHCI_DEBUG if (uhcidebug > 20) { printf("uhci_device_request: setup\n"); uhci_dump_td(setup); printf("uhci_device_request: stat\n"); uhci_dump_td(stat); } #endif /* Set up interrupt info. */ ii->reqh = reqh; ii->stdstart = setup; ii->stdend = stat; #if defined(__FreeBSD__) callout_handle_init(&ii->timeout_handle); #endif #ifdef DIAGNOSTIC ii->isdone = 0; #endif sqh->qh->elink = setup; sqh->qh->qh_elink = setup->physaddr; sqh->intr_info = ii; s = splusb(); uhci_add_ctrl(sc, sqh); LIST_INSERT_HEAD(&sc->sc_intrhead, ii, list); #ifdef UHCI_DEBUG if (uhcidebug > 12) { uhci_soft_td_t *std; uhci_soft_qh_t *xqh; uhci_soft_qh_t *sxqh; int maxqh = 0; uhci_physaddr_t link; printf("uhci_enter_ctl_q: follow from [0]\n"); for (std = sc->sc_vframes[0].htd, link = 0; (link & UHCI_PTR_Q) == 0; std = std->td->link.std) { link = std->td->td_link; uhci_dump_td(std); } for (sxqh = xqh = (uhci_soft_qh_t *)std; xqh; xqh = (maxqh++ == 5 || xqh->qh->hlink==sxqh || xqh->qh->hlink==xqh ? NULL : xqh->qh->hlink)) { uhci_dump_qh(xqh); uhci_dump_qh(sxqh); } printf("Enqueued QH:\n"); uhci_dump_qh(sqh); uhci_dump_tds(sqh->qh->elink); } #endif if (reqh->timeout && !sc->sc_bus.use_polling) { usb_timeout(uhci_timeout, ii, MS_TO_TICKS(reqh->timeout), ii->timeout_handle); } splx(s); return (USBD_NORMAL_COMPLETION); ret2: if (len != 0) usb_freemem(sc->sc_dmatag, dmap); ret1: return (r); } usbd_status uhci_device_isoc_transfer(reqh) usbd_request_handle reqh; { struct uhci_pipe *upipe = (struct uhci_pipe *)reqh->pipe; #ifdef UHCI_DEBUG usbd_device_handle dev = upipe->pipe.device; uhci_softc_t *sc = (uhci_softc_t *)dev->bus; #endif DPRINTFN(1,("uhci_device_isoc_transfer: sc=%p\n", sc)); if (upipe->u.iso.bufsize == 0) return (USBD_INVAL); /* XXX copy data */ return (USBD_XXX); } usbd_status uhci_device_isoc_start(reqh) usbd_request_handle reqh; { return (USBD_XXX); } void uhci_device_isoc_abort(reqh) usbd_request_handle reqh; { /* XXX Can't abort a single request. */ } void uhci_device_isoc_close(pipe) usbd_pipe_handle pipe; { struct uhci_pipe *upipe = (struct uhci_pipe *)pipe; usbd_device_handle dev = upipe->pipe.device; uhci_softc_t *sc = (uhci_softc_t *)dev->bus; struct iso *iso; int i; /* * Make sure all TDs are marked as inactive. * Wait for completion. * Unschedule. * Deallocate. */ iso = &upipe->u.iso; for (i = 0; i < UHCI_VFRAMELIST_COUNT; i++) iso->stds[i]->td->td_status &= ~UHCI_TD_ACTIVE; usb_delay_ms(&sc->sc_bus, 2); /* wait for completion */ uhci_lock_frames(sc); for (i = 0; i < UHCI_VFRAMELIST_COUNT; i++) { uhci_soft_td_t *std, *vstd; std = iso->stds[i]; for (vstd = sc->sc_vframes[i % UHCI_VFRAMELIST_COUNT].htd; vstd && vstd->td->link.std != std; vstd = vstd->td->link.std) ; if (!vstd) { /*panic*/ printf("uhci_device_isoc_close: %p not found\n", std); uhci_unlock_frames(sc); return; } vstd->td->link = std->td->link; vstd->td->td_link = std->td->td_link; uhci_free_std(sc, std); } uhci_unlock_frames(sc); for (i = 0; i < iso->nbuf; i++) usb_freemem(sc->sc_dmatag, &iso->bufs[i]); free(iso->stds, M_USB); free(iso->bufs, M_USB); /* XXX what else? */ } usbd_status uhci_device_isoc_setbuf(pipe, bufsize, nbuf) usbd_pipe_handle pipe; u_int bufsize; u_int nbuf; { struct uhci_pipe *upipe = (struct uhci_pipe *)pipe; usbd_device_handle dev = upipe->pipe.device; uhci_softc_t *sc = (uhci_softc_t *)dev->bus; int addr = upipe->pipe.device->address; int endpt = upipe->pipe.endpoint->edesc->bEndpointAddress; int rd = upipe->pipe.endpoint->edesc->bEndpointAddress & UE_IN; struct iso *iso; int i; usbd_status r; /* * For simplicity the number of buffers must fit nicely in the frame * list. */ if (UHCI_VFRAMELIST_COUNT % nbuf != 0) return (USBD_INVAL); iso = &upipe->u.iso; iso->bufsize = bufsize; iso->nbuf = nbuf; /* Allocate memory for buffers. */ iso->bufs = malloc(nbuf * sizeof(usb_dma_t), M_USB, M_WAITOK); iso->stds = malloc(UHCI_VFRAMELIST_COUNT * sizeof (uhci_soft_td_t *), M_USB, M_WAITOK); for (i = 0; i < nbuf; i++) { r = usb_allocmem(sc->sc_dmatag, bufsize, 0, &iso->bufs[i]); if (r != USBD_NORMAL_COMPLETION) { nbuf = i; goto bad1; } } /* Allocate the TDs. */ for (i = 0; i < UHCI_VFRAMELIST_COUNT; i++) { iso->stds[i] = uhci_alloc_std(sc); if (iso->stds[i] == 0) goto bad2; } /* XXX check schedule */ /* XXX interrupts */ /* Insert TDs into schedule, all marked inactive. */ uhci_lock_frames(sc); for (i = 0; i < UHCI_VFRAMELIST_COUNT; i++) { uhci_soft_td_t *std, *vstd; std = iso->stds[i]; std->td->td_status = UHCI_TD_IOS; /* iso, inactive */ std->td->td_token = rd ? UHCI_TD_IN (0, endpt, addr, 0) : UHCI_TD_OUT(0, endpt, addr, 0); std->td->td_buffer = DMAADDR(&iso->bufs[i % nbuf]); vstd = sc->sc_vframes[i % UHCI_VFRAMELIST_COUNT].htd; std->td->link = vstd->td->link; std->td->td_link = vstd->td->td_link; vstd->td->link.std = std; vstd->td->td_link = std->physaddr; } uhci_unlock_frames(sc); return (USBD_NORMAL_COMPLETION); bad2: while (--i >= 0) uhci_free_std(sc, iso->stds[i]); bad1: for (i = 0; i < nbuf; i++) usb_freemem(sc->sc_dmatag, &iso->bufs[i]); free(iso->stds, M_USB); free(iso->bufs, M_USB); return (USBD_NOMEM); } void uhci_isoc_done(ii) uhci_intr_info_t *ii; { } void uhci_intr_done(ii) uhci_intr_info_t *ii; { uhci_softc_t *sc = ii->sc; usbd_request_handle reqh = ii->reqh; struct uhci_pipe *upipe = (struct uhci_pipe *)reqh->pipe; usb_dma_t *dma; uhci_soft_qh_t *sqh; int i, npoll; DPRINTFN(5, ("uhci_intr_done: length=%d\n", reqh->actlen)); dma = &upipe->u.intr.datadma; memcpy(reqh->buffer, KERNADDR(dma), reqh->actlen); npoll = upipe->u.intr.npoll; for(i = 0; i < npoll; i++) { sqh = upipe->u.intr.qhs[i]; sqh->qh->elink = 0; sqh->qh->qh_elink = UHCI_PTR_T; } uhci_free_std_chain(sc, ii->stdstart, 0); /* XXX Wasteful. */ if (reqh->pipe->intrreqh == reqh) { uhci_soft_td_t *xfer, *xferend; /* This alloc cannot fail since we freed the chain above. */ uhci_alloc_std_chain(upipe, sc, reqh->length, 1, reqh->flags & USBD_SHORT_XFER_OK, dma, &xfer, &xferend); xferend->td->td_status |= UHCI_TD_IOC; #ifdef UHCI_DEBUG if (uhcidebug > 10) { printf("uhci_device_intr_done: xfer(1)\n"); uhci_dump_tds(xfer); uhci_dump_qh(upipe->u.intr.qhs[0]); } #endif ii->stdstart = xfer; ii->stdend = xferend; #ifdef DIAGNOSTIC ii->isdone = 0; #endif for (i = 0; i < npoll; i++) { sqh = upipe->u.intr.qhs[i]; sqh->qh->elink = xfer; sqh->qh->qh_elink = xfer->physaddr; } } else { usb_freemem(sc->sc_dmatag, dma); ii->stdstart = 0; /* mark as inactive */ usb_start_next(reqh->pipe); } } /* Deallocate request data structures */ void uhci_ctrl_done(ii) uhci_intr_info_t *ii; { uhci_softc_t *sc = ii->sc; usbd_request_handle reqh = ii->reqh; struct uhci_pipe *upipe = (struct uhci_pipe *)reqh->pipe; u_int len = upipe->u.ctl.length; usb_dma_t *dma; uhci_td_t *htd = ii->stdstart->td; #ifdef DIAGNOSTIC if (!reqh->isreq) panic("uhci_ctrl_done: not a request\n"); #endif LIST_REMOVE(ii, list); /* remove from active list */ uhci_remove_ctrl(sc, upipe->u.ctl.sqh); if (len != 0) { dma = &upipe->u.ctl.datadma; if (reqh->request.bmRequestType & UT_READ) memcpy(reqh->buffer, KERNADDR(dma), len); uhci_free_std_chain(sc, htd->link.std, ii->stdend); usb_freemem(sc->sc_dmatag, dma); } DPRINTFN(5, ("uhci_ctrl_done: length=%d\n", reqh->actlen)); } /* Deallocate request data structures */ void uhci_bulk_done(ii) uhci_intr_info_t *ii; { uhci_softc_t *sc = ii->sc; usbd_request_handle reqh = ii->reqh; struct uhci_pipe *upipe = (struct uhci_pipe *)reqh->pipe; u_int len = upipe->u.bulk.length; usb_dma_t *dma; uhci_td_t *htd = ii->stdstart->td; LIST_REMOVE(ii, list); /* remove from active list */ uhci_remove_bulk(sc, upipe->u.bulk.sqh); if (len != 0) { dma = &upipe->u.bulk.datadma; if (upipe->u.bulk.isread && len != 0) memcpy(reqh->buffer, KERNADDR(dma), len); uhci_free_std_chain(sc, htd->link.std, 0); usb_freemem(sc->sc_dmatag, dma); } DPRINTFN(4, ("uhci_bulk_done: length=%d\n", reqh->actlen)); /* XXX compute new toggle */ } /* Add interrupt QH, called with vflock. */ void uhci_add_intr(sc, n, sqh) uhci_softc_t *sc; int n; uhci_soft_qh_t *sqh; { struct uhci_vframe *vf = &sc->sc_vframes[n]; uhci_qh_t *eqh; DPRINTFN(4, ("uhci_add_intr: n=%d sqh=%p\n", n, sqh)); eqh = vf->eqh->qh; sqh->qh->hlink = eqh->hlink; sqh->qh->qh_hlink = eqh->qh_hlink; eqh->hlink = sqh; eqh->qh_hlink = sqh->physaddr | UHCI_PTR_Q; vf->eqh = sqh; vf->bandwidth++; } /* Remove interrupt QH, called with vflock. */ void uhci_remove_intr(sc, n, sqh) uhci_softc_t *sc; int n; uhci_soft_qh_t *sqh; { struct uhci_vframe *vf = &sc->sc_vframes[n]; uhci_soft_qh_t *pqh; DPRINTFN(4, ("uhci_remove_intr: n=%d sqh=%p\n", n, sqh)); for (pqh = vf->hqh; pqh->qh->hlink != sqh; pqh = pqh->qh->hlink) #if defined(DIAGNOSTIC) || defined(UHCI_DEBUG) if (pqh->qh->qh_hlink & UHCI_PTR_T) { printf("uhci_remove_intr: QH not found\n"); return; } #else ; #endif pqh->qh->hlink = sqh->qh->hlink; pqh->qh->qh_hlink = sqh->qh->qh_hlink; if (vf->eqh == sqh) vf->eqh = pqh; vf->bandwidth--; } usbd_status uhci_device_setintr(sc, upipe, ival) uhci_softc_t *sc; struct uhci_pipe *upipe; int ival; { uhci_soft_qh_t *sqh; int i, npoll, s; u_int bestbw, bw, bestoffs, offs; DPRINTFN(2, ("uhci_setintr: pipe=%p\n", upipe)); if (ival == 0) { printf("uhci_setintr: 0 interval\n"); return (USBD_INVAL); } if (ival > UHCI_VFRAMELIST_COUNT) ival = UHCI_VFRAMELIST_COUNT; npoll = (UHCI_VFRAMELIST_COUNT + ival - 1) / ival; DPRINTFN(2, ("uhci_setintr: ival=%d npoll=%d\n", ival, npoll)); upipe->u.intr.npoll = npoll; upipe->u.intr.qhs = malloc(npoll * sizeof(uhci_soft_qh_t *), M_USB, M_WAITOK); /* * Figure out which offset in the schedule that has most * bandwidth left over. */ #define MOD(i) ((i) & (UHCI_VFRAMELIST_COUNT-1)) for (bestoffs = offs = 0, bestbw = ~0; offs < ival; offs++) { for (bw = i = 0; i < npoll; i++) bw += sc->sc_vframes[MOD(i * ival + offs)].bandwidth; if (bw < bestbw) { bestbw = bw; bestoffs = offs; } } DPRINTFN(1, ("uhci_setintr: bw=%d offs=%d\n", bestbw, bestoffs)); upipe->iinfo->stdstart = 0; for(i = 0; i < npoll; i++) { upipe->u.intr.qhs[i] = sqh = uhci_alloc_sqh(sc); sqh->qh->elink = 0; sqh->qh->qh_elink = UHCI_PTR_T; sqh->pos = MOD(i * ival + bestoffs); sqh->intr_info = upipe->iinfo; } #undef MOD s = splusb(); LIST_INSERT_HEAD(&sc->sc_intrhead, upipe->iinfo, list); splx(s); uhci_lock_frames(sc); /* Enter QHs into the controller data structures. */ for(i = 0; i < npoll; i++) uhci_add_intr(sc, upipe->u.intr.qhs[i]->pos, upipe->u.intr.qhs[i]); uhci_unlock_frames(sc); DPRINTFN(5, ("uhci_setintr: returns %p\n", upipe)); return (USBD_NORMAL_COMPLETION); } /* Open a new pipe. */ usbd_status uhci_open(pipe) usbd_pipe_handle pipe; { uhci_softc_t *sc = (uhci_softc_t *)pipe->device->bus; struct uhci_pipe *upipe = (struct uhci_pipe *)pipe; usb_endpoint_descriptor_t *ed = pipe->endpoint->edesc; usbd_status r; DPRINTFN(1, ("uhci_open: pipe=%p, addr=%d, endpt=%d (%d)\n", pipe, pipe->device->address, ed->bEndpointAddress, sc->sc_addr)); if (pipe->device->address == sc->sc_addr) { switch (ed->bEndpointAddress) { case USB_CONTROL_ENDPOINT: pipe->methods = &uhci_root_ctrl_methods; break; case UE_IN | UHCI_INTR_ENDPT: pipe->methods = &uhci_root_intr_methods; break; default: return (USBD_INVAL); } } else { upipe->iinfo = uhci_alloc_intr_info(sc); if (upipe->iinfo == 0) return (USBD_NOMEM); switch (ed->bmAttributes & UE_XFERTYPE) { case UE_CONTROL: pipe->methods = &uhci_device_ctrl_methods; upipe->u.ctl.sqh = uhci_alloc_sqh(sc); if (upipe->u.ctl.sqh == 0) goto bad; upipe->u.ctl.setup = uhci_alloc_std(sc); if (upipe->u.ctl.setup == 0) { uhci_free_sqh(sc, upipe->u.ctl.sqh); goto bad; } upipe->u.ctl.stat = uhci_alloc_std(sc); if (upipe->u.ctl.stat == 0) { uhci_free_sqh(sc, upipe->u.ctl.sqh); uhci_free_std(sc, upipe->u.ctl.setup); goto bad; } r = usb_allocmem(sc->sc_dmatag, sizeof(usb_device_request_t), 0, &upipe->u.ctl.reqdma); if (r != USBD_NORMAL_COMPLETION) { uhci_free_sqh(sc, upipe->u.ctl.sqh); uhci_free_std(sc, upipe->u.ctl.setup); uhci_free_std(sc, upipe->u.ctl.stat); goto bad; } break; case UE_INTERRUPT: pipe->methods = &uhci_device_intr_methods; return (uhci_device_setintr(sc, upipe, ed->bInterval)); case UE_ISOCHRONOUS: pipe->methods = &uhci_device_isoc_methods; upipe->u.iso.nbuf = 0; return (USBD_NORMAL_COMPLETION); case UE_BULK: pipe->methods = &uhci_device_bulk_methods; upipe->u.bulk.sqh = uhci_alloc_sqh(sc); if (upipe->u.bulk.sqh == 0) goto bad; break; } } return (USBD_NORMAL_COMPLETION); bad: uhci_free_intr_info(upipe->iinfo); return (USBD_NOMEM); } /* * Data structures and routines to emulate the root hub. */ usb_device_descriptor_t uhci_devd = { USB_DEVICE_DESCRIPTOR_SIZE, UDESC_DEVICE, /* type */ {0x00, 0x01}, /* USB version */ UCLASS_HUB, /* class */ USUBCLASS_HUB, /* subclass */ 0, /* protocol */ 64, /* max packet */ {0},{0},{0x00,0x01}, /* device id */ 1,2,0, /* string indicies */ 1 /* # of configurations */ }; usb_config_descriptor_t uhci_confd = { USB_CONFIG_DESCRIPTOR_SIZE, UDESC_CONFIG, {USB_CONFIG_DESCRIPTOR_SIZE + USB_INTERFACE_DESCRIPTOR_SIZE + USB_ENDPOINT_DESCRIPTOR_SIZE}, 1, 1, 0, UC_SELF_POWERED, 0 /* max power */ }; usb_interface_descriptor_t uhci_ifcd = { USB_INTERFACE_DESCRIPTOR_SIZE, UDESC_INTERFACE, 0, 0, 1, UCLASS_HUB, USUBCLASS_HUB, 0, 0 }; usb_endpoint_descriptor_t uhci_endpd = { USB_ENDPOINT_DESCRIPTOR_SIZE, UDESC_ENDPOINT, UE_IN | UHCI_INTR_ENDPT, UE_INTERRUPT, {8}, 255 }; usb_hub_descriptor_t uhci_hubd_piix = { USB_HUB_DESCRIPTOR_SIZE, UDESC_HUB, 2, { UHD_PWR_NO_SWITCH | UHD_OC_INDIVIDUAL, 0 }, 50, /* power on to power good */ 0, { 0x00 }, /* both ports are removable */ }; int uhci_str(p, l, s) usb_string_descriptor_t *p; int l; char *s; { int i; if (l == 0) return (0); p->bLength = 2 * strlen(s) + 2; if (l == 1) return (1); p->bDescriptorType = UDESC_STRING; l -= 2; for (i = 0; s[i] && l > 1; i++, l -= 2) USETW2(p->bString[i], 0, s[i]); return (2*i+2); } /* * Simulate a hardware hub by handling all the necessary requests. */ usbd_status uhci_root_ctrl_transfer(reqh) usbd_request_handle reqh; { int s; usbd_status r; s = splusb(); r = usb_insert_transfer(reqh); splx(s); if (r != USBD_NORMAL_COMPLETION) return (r); else return (uhci_root_ctrl_start(reqh)); } usbd_status uhci_root_ctrl_start(reqh) usbd_request_handle reqh; { uhci_softc_t *sc = (uhci_softc_t *)reqh->pipe->device->bus; usb_device_request_t *req; void *buf; int port, x; int len, value, index, status, change, l, totlen = 0; usb_port_status_t ps; usbd_status r; if (!reqh->isreq) panic("uhci_root_ctrl_transfer: not a request\n"); req = &reqh->request; buf = reqh->buffer; DPRINTFN(10,("uhci_root_ctrl_control type=0x%02x request=%02x\n", req->bmRequestType, req->bRequest)); len = UGETW(req->wLength); value = UGETW(req->wValue); index = UGETW(req->wIndex); #define C(x,y) ((x) | ((y) << 8)) switch(C(req->bRequest, req->bmRequestType)) { case C(UR_CLEAR_FEATURE, UT_WRITE_DEVICE): case C(UR_CLEAR_FEATURE, UT_WRITE_INTERFACE): case C(UR_CLEAR_FEATURE, UT_WRITE_ENDPOINT): /* * DEVICE_REMOTE_WAKEUP and ENDPOINT_HALT are no-ops * for the integrated root hub. */ break; case C(UR_GET_CONFIG, UT_READ_DEVICE): if (len > 0) { *(u_int8_t *)buf = sc->sc_conf; totlen = 1; } break; case C(UR_GET_DESCRIPTOR, UT_READ_DEVICE): DPRINTFN(2,("uhci_root_ctrl_control wValue=0x%04x\n", value)); switch(value >> 8) { case UDESC_DEVICE: if ((value & 0xff) != 0) { r = USBD_IOERROR; goto ret; } totlen = l = min(len, USB_DEVICE_DESCRIPTOR_SIZE); memcpy(buf, &uhci_devd, l); break; case UDESC_CONFIG: if ((value & 0xff) != 0) { r = USBD_IOERROR; goto ret; } totlen = l = min(len, USB_CONFIG_DESCRIPTOR_SIZE); memcpy(buf, &uhci_confd, l); buf = (char *)buf + l; len -= l; l = min(len, USB_INTERFACE_DESCRIPTOR_SIZE); totlen += l; memcpy(buf, &uhci_ifcd, l); buf = (char *)buf + l; len -= l; l = min(len, USB_ENDPOINT_DESCRIPTOR_SIZE); totlen += l; memcpy(buf, &uhci_endpd, l); break; case UDESC_STRING: if (len == 0) break; *(u_int8_t *)buf = 0; totlen = 1; switch (value & 0xff) { case 1: /* Vendor */ totlen = uhci_str(buf, len, sc->sc_vendor); break; case 2: /* Product */ totlen = uhci_str(buf, len, "UHCI root hub"); break; } break; default: r = USBD_IOERROR; goto ret; } break; case C(UR_GET_INTERFACE, UT_READ_INTERFACE): if (len > 0) { *(u_int8_t *)buf = 0; totlen = 1; } break; case C(UR_GET_STATUS, UT_READ_DEVICE): if (len > 1) { USETW(((usb_status_t *)buf)->wStatus,UDS_SELF_POWERED); totlen = 2; } break; case C(UR_GET_STATUS, UT_READ_INTERFACE): case C(UR_GET_STATUS, UT_READ_ENDPOINT): if (len > 1) { USETW(((usb_status_t *)buf)->wStatus, 0); totlen = 2; } break; case C(UR_SET_ADDRESS, UT_WRITE_DEVICE): if (value >= USB_MAX_DEVICES) { r = USBD_IOERROR; goto ret; } sc->sc_addr = value; break; case C(UR_SET_CONFIG, UT_WRITE_DEVICE): if (value != 0 && value != 1) { r = USBD_IOERROR; goto ret; } sc->sc_conf = value; break; case C(UR_SET_DESCRIPTOR, UT_WRITE_DEVICE): break; case C(UR_SET_FEATURE, UT_WRITE_DEVICE): case C(UR_SET_FEATURE, UT_WRITE_INTERFACE): case C(UR_SET_FEATURE, UT_WRITE_ENDPOINT): r = USBD_IOERROR; goto ret; case C(UR_SET_INTERFACE, UT_WRITE_INTERFACE): break; case C(UR_SYNCH_FRAME, UT_WRITE_ENDPOINT): break; /* Hub requests */ case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_DEVICE): break; case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_OTHER): DPRINTFN(3, ("uhci_root_ctrl_control: UR_CLEAR_PORT_FEATURE " "port=%d feature=%d\n", index, value)); if (index == 1) port = UHCI_PORTSC1; else if (index == 2) port = UHCI_PORTSC2; else { r = USBD_IOERROR; goto ret; } switch(value) { case UHF_PORT_ENABLE: x = UREAD2(sc, port); UWRITE2(sc, port, x & ~UHCI_PORTSC_PE); break; case UHF_PORT_SUSPEND: x = UREAD2(sc, port); UWRITE2(sc, port, x & ~UHCI_PORTSC_SUSP); break; case UHF_PORT_RESET: x = UREAD2(sc, port); UWRITE2(sc, port, x & ~UHCI_PORTSC_PR); break; case UHF_C_PORT_CONNECTION: x = UREAD2(sc, port); UWRITE2(sc, port, x | UHCI_PORTSC_CSC); break; case UHF_C_PORT_ENABLE: x = UREAD2(sc, port); UWRITE2(sc, port, x | UHCI_PORTSC_POEDC); break; case UHF_C_PORT_OVER_CURRENT: x = UREAD2(sc, port); UWRITE2(sc, port, x | UHCI_PORTSC_OCIC); break; case UHF_C_PORT_RESET: sc->sc_isreset = 0; r = USBD_NORMAL_COMPLETION; goto ret; case UHF_PORT_CONNECTION: case UHF_PORT_OVER_CURRENT: case UHF_PORT_POWER: case UHF_PORT_LOW_SPEED: case UHF_C_PORT_SUSPEND: default: r = USBD_IOERROR; goto ret; } break; case C(UR_GET_BUS_STATE, UT_READ_CLASS_OTHER): if (index == 1) port = UHCI_PORTSC1; else if (index == 2) port = UHCI_PORTSC2; else { r = USBD_IOERROR; goto ret; } if (len > 0) { *(u_int8_t *)buf = (UREAD2(sc, port) & UHCI_PORTSC_LS) >> UHCI_PORTSC_LS_SHIFT; totlen = 1; } break; case C(UR_GET_DESCRIPTOR, UT_READ_CLASS_DEVICE): if (value != 0) { r = USBD_IOERROR; goto ret; } l = min(len, USB_HUB_DESCRIPTOR_SIZE); totlen = l; memcpy(buf, &uhci_hubd_piix, l); break; case C(UR_GET_STATUS, UT_READ_CLASS_DEVICE): if (len != 4) { r = USBD_IOERROR; goto ret; } memset(buf, 0, len); totlen = len; break; case C(UR_GET_STATUS, UT_READ_CLASS_OTHER): if (index == 1) port = UHCI_PORTSC1; else if (index == 2) port = UHCI_PORTSC2; else { r = USBD_IOERROR; goto ret; } if (len != 4) { r = USBD_IOERROR; goto ret; } x = UREAD2(sc, port); status = change = 0; if (x & UHCI_PORTSC_CCS ) status |= UPS_CURRENT_CONNECT_STATUS; if (x & UHCI_PORTSC_CSC ) change |= UPS_C_CONNECT_STATUS; if (x & UHCI_PORTSC_PE ) status |= UPS_PORT_ENABLED; if (x & UHCI_PORTSC_POEDC) change |= UPS_C_PORT_ENABLED; if (x & UHCI_PORTSC_OCI ) status |= UPS_OVERCURRENT_INDICATOR; if (x & UHCI_PORTSC_OCIC ) change |= UPS_C_OVERCURRENT_INDICATOR; if (x & UHCI_PORTSC_SUSP ) status |= UPS_SUSPEND; if (x & UHCI_PORTSC_LSDA ) status |= UPS_LOW_SPEED; status |= UPS_PORT_POWER; if (sc->sc_isreset) change |= UPS_C_PORT_RESET; USETW(ps.wPortStatus, status); USETW(ps.wPortChange, change); l = min(len, sizeof ps); memcpy(buf, &ps, l); totlen = l; break; case C(UR_SET_DESCRIPTOR, UT_WRITE_CLASS_DEVICE): r = USBD_IOERROR; goto ret; case C(UR_SET_FEATURE, UT_WRITE_CLASS_DEVICE): break; case C(UR_SET_FEATURE, UT_WRITE_CLASS_OTHER): if (index == 1) port = UHCI_PORTSC1; else if (index == 2) port = UHCI_PORTSC2; else { r = USBD_IOERROR; goto ret; } switch(value) { case UHF_PORT_ENABLE: x = UREAD2(sc, port); UWRITE2(sc, port, x | UHCI_PORTSC_PE); break; case UHF_PORT_SUSPEND: x = UREAD2(sc, port); UWRITE2(sc, port, x | UHCI_PORTSC_SUSP); break; case UHF_PORT_RESET: x = UREAD2(sc, port); UWRITE2(sc, port, x | UHCI_PORTSC_PR); usb_delay_ms(&sc->sc_bus, 10); UWRITE2(sc, port, x & ~UHCI_PORTSC_PR); delay(100); x = UREAD2(sc, port); UWRITE2(sc, port, x | UHCI_PORTSC_PE); delay(100); DPRINTFN(3,("uhci port %d reset, status = 0x%04x\n", index, UREAD2(sc, port))); sc->sc_isreset = 1; break; case UHF_C_PORT_CONNECTION: case UHF_C_PORT_ENABLE: case UHF_C_PORT_OVER_CURRENT: case UHF_PORT_CONNECTION: case UHF_PORT_OVER_CURRENT: case UHF_PORT_POWER: case UHF_PORT_LOW_SPEED: case UHF_C_PORT_SUSPEND: case UHF_C_PORT_RESET: default: r = USBD_IOERROR; goto ret; } break; default: r = USBD_IOERROR; goto ret; } reqh->actlen = totlen; r = USBD_NORMAL_COMPLETION; ret: reqh->status = r; reqh->xfercb(reqh); usb_start_next(reqh->pipe); return (USBD_IN_PROGRESS); } /* Abort a root control request. */ void uhci_root_ctrl_abort(reqh) usbd_request_handle reqh; { /* Nothing to do, all transfers are syncronous. */ } /* Close the root pipe. */ void uhci_root_ctrl_close(pipe) usbd_pipe_handle pipe; { usb_untimeout(uhci_timo, pipe->intrreqh, pipe->intrreqh->timo_handle); DPRINTF(("uhci_root_ctrl_close\n")); } /* Abort a root interrupt request. */ void uhci_root_intr_abort(reqh) usbd_request_handle reqh; { usb_untimeout(uhci_timo, reqh, reqh->timo_handle); } usbd_status uhci_root_intr_transfer(reqh) usbd_request_handle reqh; { int s; usbd_status r; s = splusb(); r = usb_insert_transfer(reqh); splx(s); if (r != USBD_NORMAL_COMPLETION) return (r); else return (uhci_root_intr_start(reqh)); } /* Start a transfer on the root interrupt pipe */ usbd_status uhci_root_intr_start(reqh) usbd_request_handle reqh; { usbd_pipe_handle pipe = reqh->pipe; uhci_softc_t *sc = (uhci_softc_t *)pipe->device->bus; struct uhci_pipe *upipe = (struct uhci_pipe *)pipe; usb_dma_t *dmap; usbd_status r; int len; DPRINTFN(3, ("uhci_root_intr_transfer: reqh=%p buf=%p len=%d " "flags=%d\n", reqh, reqh->buffer, reqh->length, reqh->flags)); len = reqh->length; dmap = &upipe->u.intr.datadma; if (len == 0) return (USBD_INVAL); /* XXX should it be? */ r = usb_allocmem(sc->sc_dmatag, len, 0, dmap); if (r != USBD_NORMAL_COMPLETION) return (r); sc->sc_ival = MS_TO_TICKS(reqh->pipe->endpoint->edesc->bInterval); usb_timeout(uhci_timo, reqh, sc->sc_ival, reqh->timo_handle); return (USBD_IN_PROGRESS); } /* Close the root interrupt pipe. */ void uhci_root_intr_close(pipe) usbd_pipe_handle pipe; { usb_untimeout(uhci_timo, pipe->intrreqh, pipe->intrreqh->timo_handle); DPRINTF(("uhci_root_intr_close\n")); } Index: head/sys/dev/usb/uhci_pci.c =================================================================== --- head/sys/dev/usb/uhci_pci.c (revision 45719) +++ head/sys/dev/usb/uhci_pci.c (revision 45720) @@ -1,215 +1,239 @@ -/* FreeBSD $Id: uhci_pci.c,v 1.4 1999/04/06 23:09:58 n_hibma Exp $ */ +/* FreeBSD $Id: uhci_pci.c,v 1.5 1999/04/11 14:24:20 n_hibma Exp $ */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (augustss@carlstedt.se) at * Carlstedt Research & Technology. * * 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 NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */ +#include "opt_bus.h" + #include #include #include #include #include #include #include #include +#include +#include +#include #include #include -#define PCI_CLASS_SERIALBUS 0x0c000000 -#define PCI_SUBCLASS_COMMUNICATIONS_SERIAL 0x00000000 -#define PCI_SUBCLASS_SERIALBUS_FIREWIRE 0x00000000 -#define PCI_SUBCLASS_SERIALBUS_ACCESS 0x00010000 -#define PCI_SUBCLASS_SERIALBUS_SSA 0x00020000 -#define PCI_SUBCLASS_SERIALBUS_USB 0x00030000 -#define PCI_SUBCLASS_SERIALBUS_FIBER 0x00040000 - -#define PCI_INTERFACE(d) (((d)>>8)&0xff) -#define PCI_SUBCLASS(d) ((d)&PCI_SUBCLASS_MASK) -#define PCI_CLASS(d) ((d)&PCI_CLASS_MASK) - - #include #include #include #include #include #include - #define PCI_UHCI_VENDORID_INTEL 0x8086 #define PCI_UHCI_VENDORID_VIA 0x1106 #define PCI_UHCI_DEVICEID_PIIX3 0x70208086ul static const char *uhci_device_piix3 = "Intel 82371SB (PIIX3) USB Host Controller"; #define PCI_UHCI_DEVICEID_PIIX4 0x71128086ul #define PCI_UHCI_DEVICEID_PIIX4E 0x71128086ul /* no separate step */ static const char *uhci_device_piix4 = "Intel 82371AB/EB (PIIX4) USB Host Controller"; #define PCI_UHCI_DEVICEID_VT83C572 0x30381106ul static const char *uhci_device_vt83c572 = "VIA 83C572 USB Host Controller"; static const char *uhci_device_generic = "UHCI (generic) USB Controller"; #define PCI_UHCI_BASE_REG 0x20 -static const char *uhci_pci_probe __P((pcici_t, pcidi_t)); -static void uhci_pci_attach __P((pcici_t, int)); - -static u_long uhci_count = 0; - -static struct pci_device uhci_pci_device = { - "uhci", - uhci_pci_probe, - uhci_pci_attach, - &uhci_count, - NULL -}; - -DATA_SET(pcidevice_set, uhci_pci_device); - - static const char * -uhci_pci_probe(pcici_t config_id, pcidi_t device_id) +uhci_pci_match(device_t dev) { - u_int32_t class; + u_int32_t device_id = pci_get_devid(dev); if (device_id == PCI_UHCI_DEVICEID_PIIX3) { return (uhci_device_piix3); } else if (device_id == PCI_UHCI_DEVICEID_PIIX4) { return (uhci_device_piix4); } else if (device_id == PCI_UHCI_DEVICEID_VT83C572) { return (uhci_device_vt83c572); } else { - class = pci_conf_read(config_id, PCI_CLASS_REG); - if ( PCI_CLASS(class) == PCI_CLASS_SERIALBUS - && PCI_SUBCLASS(class) == PCI_SUBCLASS_SERIALBUS_USB - && PCI_INTERFACE(class) == PCI_INTERFACE_UHCI) { + if ( pci_get_class(dev) == PCIC_SERIALBUS + && pci_get_subclass(dev) == PCIS_SERIALBUS_USB + && pci_get_progif(dev) == PCI_INTERFACE_UHCI) { return (uhci_device_generic); } } return NULL; /* dunno... */ } -static void -uhci_pci_attach(pcici_t config_id, int unit) +static int +uhci_pci_probe(device_t dev) { - int id, legsup; + const char *desc = uhci_pci_match(dev); + if (desc) { + device_set_desc(dev, desc); + return 0; + } else { + return ENXIO; + } +} + +static int +uhci_pci_attach(device_t dev) +{ + int unit = device_get_unit(dev); + int legsup; char *typestr; usbd_status err; - uhci_softc_t *sc = NULL; device_t usbus; + uhci_softc_t *sc = device_get_softc(dev); + int rid; + struct resource *res; + void *ih; + int error; - sc = malloc(sizeof(uhci_softc_t), M_DEVBUF, M_NOWAIT); - /* Do not free it below, intr might use the sc */ - if ( sc == NULL ) { - printf("uhci%d: could not allocate memory", unit); - return; - } - memset(sc, 0, sizeof(uhci_softc_t)); + rid = PCI_UHCI_BASE_REG; + res = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, + 0, ~0, 1, RF_ACTIVE); + if (!res) { + device_printf(dev, "could not map ports\n"); + return ENXIO; + } - if ( !pci_map_port(config_id, PCI_UHCI_BASE_REG, &sc->sc_iobase) ) { - printf("uhci%d: could not map port\n", unit); - return; - } + sc->iot = rman_get_bustag(res); + sc->ioh = rman_get_bushandle(res); - if ( !pci_map_int(config_id, (pci_inthand_t *)uhci_intr, - (void *) sc, &bio_imask)) { - printf("uhci%d: could not map irq\n", unit); - return; + rid = 0; + res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, + RF_SHAREABLE | RF_ACTIVE); + if (res == NULL) { + device_printf(dev, "could not allocate irq\n"); + return ENOMEM; } + + error = bus_setup_intr(dev, res, (driver_intr_t *) uhci_intr, sc, &ih); + if (error) { + device_printf(dev, "could not setup irq\n"); + return error; + } - usbus = device_add_child(root_bus, "usb", -1, sc); + usbus = device_add_child(dev, "usb", -1, sc); if (!usbus) { - printf("usb%d: could not add USB device to root bus\n", unit); - return; + printf("usb%d: could not add USB device\n", unit); + return ENOMEM; } - id = pci_conf_read(config_id, PCI_ID_REG); - switch (id) { + switch (pci_get_devid(dev)) { case PCI_UHCI_DEVICEID_PIIX3: device_set_desc(usbus, uhci_device_piix3); sprintf(sc->sc_vendor, "Intel"); break; case PCI_UHCI_DEVICEID_PIIX4: device_set_desc(usbus, uhci_device_piix4); sprintf(sc->sc_vendor, "Intel"); break; case PCI_UHCI_DEVICEID_VT83C572: device_set_desc(usbus, uhci_device_vt83c572); sprintf(sc->sc_vendor, "VIA"); break; default: - printf("(New UHCI DeviceId=0x%08x)\n", id); + printf("(New UHCI DeviceId=0x%08x)\n", pci_get_devid(dev)); device_set_desc(usbus, uhci_device_generic); - sprintf(sc->sc_vendor, "(0x%08x)", id); + sprintf(sc->sc_vendor, "(0x%08x)", pci_get_devid(dev)); } if (bootverbose) { - switch(pci_conf_read(config_id, PCI_USBREV) & PCI_USBREV_MASK) { + switch(pci_read_config(dev, PCI_USBREV, 4) & PCI_USBREV_MASK) { case PCI_USBREV_PRE_1_0: typestr = "pre 1.0"; break; case PCI_USBREV_1_0: typestr = "1.0"; break; default: typestr = "unknown"; break; } printf("uhci%d: USB version %s, chip rev. %d\n", unit, typestr, - (int) pci_conf_read(config_id, PCIR_REVID) & 0xff); + pci_get_revid(dev)); } - legsup = pci_conf_read(config_id, PCI_LEGSUP); + legsup = pci_read_config(dev, PCI_LEGSUP, 4); if ( !(legsup & PCI_LEGSUP_USBPIRQDEN) ) { #if ! (defined(USBVERBOSE) || defined(USB_DEBUG)) if (bootverbose) #endif printf("uhci%d: PIRQD enable not set\n", unit); legsup |= PCI_LEGSUP_USBPIRQDEN; - pci_conf_write(config_id, PCI_LEGSUP, legsup); + pci_write_config(dev, PCI_LEGSUP, legsup, 4); } sc->sc_bus.bdev = usbus; err = uhci_init(sc); if (err != USBD_NORMAL_COMPLETION) { printf("uhci%d: init failed, error=%d\n", unit, err); - device_delete_child(root_bus, usbus); + device_delete_child(dev, usbus); } - return; + return device_probe_and_attach(sc->sc_bus.bdev); } + +static device_method_t uhci_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, uhci_pci_probe), + DEVMETHOD(device_attach, uhci_pci_attach), + + /* Bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), + DEVMETHOD(bus_release_resource, bus_generic_release_resource), + DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), + DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), + DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), + DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), + + { 0, 0 } +}; + +static driver_t uhci_driver = { + "uhci", + uhci_methods, + DRIVER_TYPE_BIO, + sizeof(uhci_softc_t), +}; + +static devclass_t uhci_devclass; + +DRIVER_MODULE(uhci, pci, uhci_driver, uhci_devclass, 0, 0); Index: head/sys/dev/usb/uhcivar.h =================================================================== --- head/sys/dev/usb/uhcivar.h (revision 45719) +++ head/sys/dev/usb/uhcivar.h (revision 45720) @@ -1,170 +1,171 @@ /* $NetBSD: uhcivar.h,v 1.5 1998/12/26 12:53:02 augustss Exp $ */ /* $FreeBSD$ */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (augustss@carlstedt.se) at * Carlstedt Research & Technology. * * 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 NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */ /* * To avoid having 1024 TDs for each isochronous transfer we introduce * a virtual frame list. Every UHCI_VFRAMELIST_COUNT entries in the real * frame list points to a non-active TD. These, in turn, which form the * starts of the virtual frame list. This also has the advantage that it * simplifies linking in/out TD/QH in the schedule. * Furthermore, initially each of the inactive TDs point to an inactive * QH that forms the start of the interrupt traffic for that slot. * Each of these QHs point to the same QH that is the start of control * traffic. * * UHCI_VFRAMELIST_COUNT should be a power of 2 and <= UHCI_FRAMELIST_COUNT. */ #define UHCI_VFRAMELIST_COUNT 128 typedef struct uhci_soft_qh uhci_soft_qh_t; typedef struct uhci_soft_td uhci_soft_td_t; /* * An interrupt info struct contains the information needed to * execute a requested routine when the controller generates an * interrupt. Since we cannot know which transfer generated * the interrupt all structs are linked together so they can be * searched at interrupt time. */ typedef struct uhci_intr_info { struct uhci_softc *sc; usbd_request_handle reqh; uhci_soft_td_t *stdstart; uhci_soft_td_t *stdend; LIST_ENTRY(uhci_intr_info) list; #if defined(__FreeBSD__) struct callout_handle timeout_handle; #endif /* defined(__FreeBSD__) */ #ifdef DIAGNOSTIC int isdone; #endif } uhci_intr_info_t; /* * Extra information that we need for a TD. */ struct uhci_soft_td { uhci_td_t *td; /* The real TD */ uhci_physaddr_t physaddr; /* and its physical address. */ }; #define UHCI_TD_CHUNK 128 /*(PAGE_SIZE / UHCI_TD_SIZE)*/ /* * Extra information that we need for a QH. */ struct uhci_soft_qh { uhci_qh_t *qh; /* The real QH */ uhci_physaddr_t physaddr; /* and its physical address. */ int pos; /* Timeslot position */ uhci_intr_info_t *intr_info; /* Who to call on completion. */ }; #define UHCI_QH_CHUNK 128 /*(PAGE_SIZE / UHCI_QH_SIZE)*/ /* Only used for buffer free list. */ struct uhci_buffer { struct uhci_buffer *next; }; #define UHCI_BUFFER_SIZE 64 #define UHCI_BUFFER_CHUNK 64 /*(PAGE_SIZE / UHCI_BUFFER_SIZE)*/ /* * Information about an entry in the virtial frame list. */ struct uhci_vframe { uhci_soft_td_t *htd; /* pointer to dummy TD */ uhci_soft_td_t *etd; /* pointer to last TD */ uhci_soft_qh_t *hqh; /* pointer to dummy QH */ uhci_soft_qh_t *eqh; /* pointer to last QH */ u_int bandwidth; /* max bandwidth used by this frame */ }; typedef struct uhci_softc { struct usbd_bus sc_bus; /* base device */ #if defined(__NetBSD__) void *sc_ih; /* interrupt vectoring */ bus_space_tag_t iot; bus_space_handle_t ioh; bus_dma_tag_t sc_dmatag; /* DMA tag */ /* XXX should keep track of all DMA memory */ #elif defined(__FreeBSD__) - int sc_iobase; + bus_space_tag_t iot; + bus_space_handle_t ioh; #endif /* defined(__FreeBSD__) */ uhci_physaddr_t *sc_pframes; struct uhci_vframe sc_vframes[UHCI_VFRAMELIST_COUNT]; uhci_soft_qh_t *sc_ctl_start; /* dummy QH for control */ uhci_soft_qh_t *sc_ctl_end; /* last control QH */ uhci_soft_qh_t *sc_bulk_start; /* dummy QH for bulk */ uhci_soft_qh_t *sc_bulk_end; /* last bulk transfer */ uhci_soft_td_t *sc_freetds; uhci_soft_qh_t *sc_freeqhs; struct uhci_buffer *sc_freebuffers; u_int8_t sc_addr; /* device address */ u_int8_t sc_conf; /* device configuration */ char sc_isreset; int sc_intrs; LIST_HEAD(, uhci_intr_info) sc_intrhead; /* Info for the root hub interrupt channel. */ int sc_ival; char sc_vflock; #define UHCI_HAS_LOCK 1 #define UHCI_WANT_LOCK 2 #if defined(__NetBSD__) usb_dma_t *sc_mallocs; #endif char sc_vendor[16]; } uhci_softc_t; usbd_status uhci_init __P((uhci_softc_t *)); int uhci_intr __P((void *)); #if 0 void uhci_reset __P((void *)); #endif Index: head/sys/dev/usb/uhid.c =================================================================== --- head/sys/dev/usb/uhid.c (revision 45719) +++ head/sys/dev/usb/uhid.c (revision 45720) @@ -1,547 +1,542 @@ /* $NetBSD: uhid.c,v 1.14 1999/01/08 11:58:25 augustss Exp $ */ /* $FreeBSD$ */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (augustss@carlstedt.se) at * Carlstedt Research & Technology. * * 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 NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */ #include #include #include #include #if defined(__NetBSD__) #include #include #elif defined(__FreeBSD__) #include #include #include #include #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #ifdef UHID_DEBUG #define DPRINTF(x) if (uhiddebug) logprintf x #define DPRINTFN(n,x) if (uhiddebug>(n)) logprintf x int uhiddebug = 1; #else #define DPRINTF(x) #define DPRINTFN(n,x) #endif struct uhid_softc { bdevice sc_dev; /* base device */ usbd_interface_handle sc_iface; /* interface */ usbd_pipe_handle sc_intrpipe; /* interrupt pipe */ int sc_ep_addr; int sc_isize; int sc_osize; int sc_fsize; u_int8_t sc_iid; u_int8_t sc_oid; u_int8_t sc_fid; char *sc_ibuf; char *sc_obuf; void *sc_repdesc; int sc_repdesc_size; struct clist sc_q; struct selinfo sc_rsel; u_char sc_state; /* driver state */ #define UHID_OPEN 0x01 /* device is open */ #define UHID_ASLP 0x02 /* waiting for device data */ #define UHID_NEEDCLEAR 0x04 /* needs clearing endpoint stall */ #define UHID_IMMED 0x08 /* return read data immediately */ int sc_disconnected; /* device is gone */ }; #define UHIDUNIT(dev) (minor(dev)) #define UHID_CHUNK 128 /* chunk size for read */ #define UHID_BSIZE 1020 /* buffer size */ int uhidopen __P((dev_t, int, int, struct proc *)); int uhidclose __P((dev_t, int, int, struct proc *p)); int uhidread __P((dev_t, struct uio *uio, int)); int uhidwrite __P((dev_t, struct uio *uio, int)); int uhidioctl __P((dev_t, u_long, caddr_t, int, struct proc *)); int uhidpoll __P((dev_t, int, struct proc *)); void uhid_intr __P((usbd_request_handle, usbd_private_handle, usbd_status)); void uhid_disco __P((void *)); USB_DECLARE_DRIVER(uhid); USB_MATCH(uhid) { USB_MATCH_START(uhid, uaa); usb_interface_descriptor_t *id; if (!uaa->iface) return (UMATCH_NONE); id = usbd_get_interface_descriptor(uaa->iface); if (!id || id->bInterfaceClass != UCLASS_HID) return (UMATCH_NONE); return (UMATCH_IFACECLASS_GENERIC); } USB_ATTACH(uhid) { USB_ATTACH_START(uhid, sc, uaa); usbd_interface_handle iface = uaa->iface; usb_interface_descriptor_t *id; usb_endpoint_descriptor_t *ed; int size; void *desc; usbd_status r; char devinfo[1024]; sc->sc_disconnected = 1; sc->sc_iface = iface; id = usbd_get_interface_descriptor(iface); usbd_devinfo(uaa->device, 0, devinfo); USB_ATTACH_SETUP; printf("%s: %s, iclass %d/%d\n", USBDEVNAME(sc->sc_dev), devinfo, id->bInterfaceClass, id->bInterfaceSubClass); ed = usbd_interface2endpoint_descriptor(iface, 0); if (!ed) { printf("%s: could not read endpoint descriptor\n", USBDEVNAME(sc->sc_dev)); USB_ATTACH_ERROR_RETURN; } DPRINTFN(10,("uhid_attach: bLength=%d bDescriptorType=%d " "bEndpointAddress=%d-%s bmAttributes=%d wMaxPacketSize=%d" " bInterval=%d\n", ed->bLength, ed->bDescriptorType, ed->bEndpointAddress & UE_ADDR, ed->bEndpointAddress & UE_IN ? "in" : "out", ed->bmAttributes & UE_XFERTYPE, UGETW(ed->wMaxPacketSize), ed->bInterval)); if ((ed->bEndpointAddress & UE_IN) != UE_IN || (ed->bmAttributes & UE_XFERTYPE) != UE_INTERRUPT) { printf("%s: unexpected endpoint\n", USBDEVNAME(sc->sc_dev)); USB_ATTACH_ERROR_RETURN; } sc->sc_ep_addr = ed->bEndpointAddress; sc->sc_disconnected = 0; r = usbd_alloc_report_desc(uaa->iface, &desc, &size, M_USB); if (r != USBD_NORMAL_COMPLETION) { printf("%s: no report descriptor\n", USBDEVNAME(sc->sc_dev)); USB_ATTACH_ERROR_RETURN; } (void)usbd_set_idle(iface, 0, 0); sc->sc_isize = hid_report_size(desc, size, hid_input, &sc->sc_iid); sc->sc_osize = hid_report_size(desc, size, hid_output, &sc->sc_oid); sc->sc_fsize = hid_report_size(desc, size, hid_feature, &sc->sc_fid); sc->sc_repdesc = desc; sc->sc_repdesc_size = size; USB_ATTACH_SUCCESS_RETURN; } #if defined(__FreeBSD__) static int uhid_detach(device_t self) { - const char *devinfo = device_get_desc(self); - DPRINTF(("%s: disconnected\n", USBDEVNAME(self))); - if (devinfo) { - device_set_desc(self, NULL); - free((void *)devinfo, M_USB); - } + device_set_desc(self, NULL); return 0; } #endif void uhid_disco(p) void *p; { struct uhid_softc *sc = p; DPRINTF(("ums_hid: sc=%p\n", sc)); usbd_abort_pipe(sc->sc_intrpipe); sc->sc_disconnected = 1; } void uhid_intr(reqh, addr, status) usbd_request_handle reqh; usbd_private_handle addr; usbd_status status; { struct uhid_softc *sc = addr; DPRINTFN(5, ("uhid_intr: status=%d\n", status)); DPRINTFN(5, ("uhid_intr: data = %02x %02x %02x\n", sc->sc_ibuf[0], sc->sc_ibuf[1], sc->sc_ibuf[2])); if (status == USBD_CANCELLED) return; if (status != USBD_NORMAL_COMPLETION) { DPRINTF(("uhid_intr: status=%d\n", status)); sc->sc_state |= UHID_NEEDCLEAR; return; } (void) b_to_q(sc->sc_ibuf, sc->sc_isize, &sc->sc_q); if (sc->sc_state & UHID_ASLP) { sc->sc_state &= ~UHID_ASLP; DPRINTFN(5, ("uhid_intr: waking %p\n", sc)); wakeup((caddr_t)sc); } selwakeup(&sc->sc_rsel); } int uhidopen(dev, flag, mode, p) dev_t dev; int flag; int mode; struct proc *p; { usbd_status r; USB_GET_SC_OPEN(uhid, UHIDUNIT(dev), sc); if (!sc) return (ENXIO); DPRINTF(("uhidopen: sc=%p, disco=%d\n", sc, sc->sc_disconnected)); if (sc->sc_disconnected) return (EIO); if (sc->sc_state & UHID_OPEN) return (EBUSY); #if defined(__NetBSD__) if (clalloc(&sc->sc_q, UHID_BSIZE, 0) == -1) return (ENOMEM); #elif defined(__FreeBSD__) clist_alloc_cblocks(&sc->sc_q, UHID_BSIZE, 0); #endif sc->sc_state |= UHID_OPEN; sc->sc_state &= ~UHID_IMMED; sc->sc_ibuf = malloc(sc->sc_isize, M_USB, M_WAITOK); sc->sc_obuf = malloc(sc->sc_osize, M_USB, M_WAITOK); /* Set up interrupt pipe. */ r = usbd_open_pipe_intr(sc->sc_iface, sc->sc_ep_addr, USBD_SHORT_XFER_OK, &sc->sc_intrpipe, sc, sc->sc_ibuf, sc->sc_isize, uhid_intr); if (r != USBD_NORMAL_COMPLETION) { DPRINTF(("uhidopen: usbd_open_pipe_intr failed, " "error=%d\n",r)); sc->sc_state &= ~UHID_OPEN; return (EIO); } usbd_set_disco(sc->sc_intrpipe, uhid_disco, sc); return (0); } int uhidclose(dev, flag, mode, p) dev_t dev; int flag; int mode; struct proc *p; { USB_GET_SC(uhid, UHIDUNIT(dev), sc); if (sc->sc_disconnected) return (EIO); DPRINTF(("uhidclose: sc=%p\n", sc)); /* Disable interrupts. */ usbd_abort_pipe(sc->sc_intrpipe); usbd_close_pipe(sc->sc_intrpipe); sc->sc_state &= ~UHID_OPEN; #if defined(__NetBSD__) clfree(&sc->sc_q); #elif defined(__FreeBSD__) clist_free_cblocks(&sc->sc_q); #endif free(sc->sc_ibuf, M_USB); free(sc->sc_obuf, M_USB); return (0); } int uhidread(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { int s; int error = 0; size_t length; u_char buffer[UHID_CHUNK]; usbd_status r; USB_GET_SC(uhid, UHIDUNIT(dev), sc); if (sc->sc_disconnected) return (EIO); DPRINTFN(1, ("uhidread\n")); if (sc->sc_state & UHID_IMMED) { DPRINTFN(1, ("uhidread immed\n")); r = usbd_get_report(sc->sc_iface, UHID_INPUT_REPORT, sc->sc_iid, sc->sc_ibuf, sc->sc_isize); if (r != USBD_NORMAL_COMPLETION) return (EIO); return (uiomove(buffer, sc->sc_isize, uio)); } s = splusb(); while (sc->sc_q.c_cc == 0) { if (flag & IO_NDELAY) { splx(s); return EWOULDBLOCK; } sc->sc_state |= UHID_ASLP; DPRINTFN(5, ("uhidread: sleep on %p\n", sc)); error = tsleep((caddr_t)sc, PZERO | PCATCH, "uhidrea", 0); DPRINTFN(5, ("uhidread: woke, error=%d\n", error)); if (error) { sc->sc_state &= ~UHID_ASLP; splx(s); return (error); } if (sc->sc_state & UHID_NEEDCLEAR) { DPRINTFN(-1,("uhidread: clearing stall\n")); sc->sc_state &= ~UHID_NEEDCLEAR; usbd_clear_endpoint_stall(sc->sc_intrpipe); } } splx(s); /* Transfer as many chunks as possible. */ while (sc->sc_q.c_cc > 0 && uio->uio_resid > 0) { length = min(sc->sc_q.c_cc, uio->uio_resid); if (length > sizeof(buffer)) length = sizeof(buffer); /* Remove a small chunk from the input queue. */ (void) q_to_b(&sc->sc_q, buffer, length); DPRINTFN(5, ("uhidread: got %d chars\n", length)); /* Copy the data to the user process. */ if ((error = uiomove(buffer, length, uio)) != 0) break; } return (error); } int uhidwrite(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { int error; int size; usbd_status r; USB_GET_SC(uhid, UHIDUNIT(dev), sc); if (sc->sc_disconnected) return (EIO); DPRINTFN(1, ("uhidwrite\n")); size = sc->sc_osize; error = 0; while (uio->uio_resid > 0) { if (uio->uio_resid != size) return (0); if ((error = uiomove(sc->sc_obuf, size, uio)) != 0) break; if (sc->sc_oid) r = usbd_set_report(sc->sc_iface, UHID_OUTPUT_REPORT, sc->sc_obuf[0], sc->sc_obuf+1, size-1); else r = usbd_set_report(sc->sc_iface, UHID_OUTPUT_REPORT, 0, sc->sc_obuf, size); if (r != USBD_NORMAL_COMPLETION) { error = EIO; break; } } return (error); } int uhidioctl(dev, cmd, addr, flag, p) dev_t dev; u_long cmd; caddr_t addr; int flag; struct proc *p; { struct usb_ctl_report_desc *rd; struct usb_ctl_report *re; int size, id; usbd_status r; USB_GET_SC(uhid, UHIDUNIT(dev), sc); if (sc->sc_disconnected) return (EIO); DPRINTFN(2, ("uhidioctl: cmd=%lx\n", cmd)); switch (cmd) { case FIONBIO: /* All handled in the upper FS layer. */ break; case USB_GET_REPORT_DESC: rd = (struct usb_ctl_report_desc *)addr; size = min(sc->sc_repdesc_size, sizeof rd->data); rd->size = size; memcpy(rd->data, sc->sc_repdesc, size); break; case USB_SET_IMMED: if (*(int *)addr) { /* XXX should read into ibuf, but does it matter */ r = usbd_get_report(sc->sc_iface, UHID_INPUT_REPORT, sc->sc_iid, sc->sc_ibuf, sc->sc_isize); if (r != USBD_NORMAL_COMPLETION) return (EOPNOTSUPP); sc->sc_state |= UHID_IMMED; } else sc->sc_state &= ~UHID_IMMED; break; case USB_GET_REPORT: re = (struct usb_ctl_report *)addr; switch (re->report) { case UHID_INPUT_REPORT: size = sc->sc_isize; id = sc->sc_iid; break; case UHID_OUTPUT_REPORT: size = sc->sc_osize; id = sc->sc_oid; break; case UHID_FEATURE_REPORT: size = sc->sc_fsize; id = sc->sc_fid; break; default: return (EINVAL); } r = usbd_get_report(sc->sc_iface, re->report, id, re->data, size); if (r != USBD_NORMAL_COMPLETION) return (EIO); break; default: return (EINVAL); } return (0); } int uhidpoll(dev, events, p) dev_t dev; int events; struct proc *p; { int revents = 0; int s; USB_GET_SC(uhid, UHIDUNIT(dev), sc); if (sc->sc_disconnected) return (EIO); s = splusb(); if (events & (POLLOUT | POLLWRNORM)) revents |= events & (POLLOUT | POLLWRNORM); if (events & (POLLIN | POLLRDNORM)) { if (sc->sc_q.c_cc > 0) revents |= events & (POLLIN | POLLRDNORM); else selrecord(p, &sc->sc_rsel); } splx(s); return (revents); } #if defined(__FreeBSD__) DRIVER_MODULE(uhid, uhub, uhid_driver, uhid_devclass, usbd_driver_load, 0); #endif Index: head/sys/dev/usb/uhub.c =================================================================== --- head/sys/dev/usb/uhub.c (revision 45719) +++ head/sys/dev/usb/uhub.c (revision 45720) @@ -1,540 +1,554 @@ /* $NetBSD: uhub.c,v 1.16 1999/01/10 19:13:15 augustss Exp $ */ /* $FreeBSD$ */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (augustss@carlstedt.se) at * Carlstedt Research & Technology. * * 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 NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */ /* * USB spec: http://www.usb.org/cgi-usb/mailmerge.cgi/home/usb/docs/developers/cgiform.tpl */ #include #include #include #include #if defined(__NetBSD__) #include #elif defined(__FreeBSD__) #include #include #endif #include #include #include #include #include #ifdef USB_DEBUG #define DPRINTF(x) if (usbdebug) logprintf x #define DPRINTFN(n,x) if (usbdebug>(n)) logprintf x extern int usbdebug; #else #define DPRINTF(x) #define DPRINTFN(n,x) #endif struct uhub_softc { bdevice sc_dev; /* base device */ usbd_device_handle sc_hub; /* USB device */ usbd_pipe_handle sc_ipipe; /* interrupt pipe */ u_int8_t sc_status[1]; /* XXX more ports */ u_char sc_running; }; usbd_status uhub_init_port __P((struct usbd_port *)); void uhub_disconnect_port __P((struct usbd_port *up)); usbd_status uhub_explore __P((usbd_device_handle hub)); void uhub_intr __P((usbd_request_handle, usbd_private_handle, usbd_status)); +#ifdef __FreeBSD__ +#include "usb_if.h" +static void uhub_disconnected __P((device_t)); +#endif /* void uhub_disco __P((void *)); */ -USB_DECLARE_DRIVER(uhub); +USB_DECLARE_DRIVER_INIT(uhub, + DEVMETHOD(usb_disconnected, uhub_disconnected)); #if defined(__FreeBSD__) devclass_t uhubroot_devclass; static device_method_t uhubroot_methods[] = { DEVMETHOD(device_probe, uhub_match), DEVMETHOD(device_attach, uhub_attach), /* detach is not allowed for a root hub */ {0,0} }; static driver_t uhubroot_driver = { "uhub", uhubroot_methods, DRIVER_TYPE_MISC, sizeof(struct uhub_softc) }; #endif #if defined(__NetBSD__) struct cfattach uhub_uhub_ca = { sizeof(struct uhub_softc), uhub_match, uhub_attach }; #endif USB_MATCH(uhub) { USB_MATCH_START(uhub, uaa); usb_device_descriptor_t *dd = usbd_get_device_descriptor(uaa->device); DPRINTFN(5,("uhub_match, dd=%p\n", dd)); /* * The subclass for hubs seems to be 0 for some and 1 for others, * so we just ignore the subclass. */ if (uaa->iface == 0 && dd->bDeviceClass == UCLASS_HUB) return (UMATCH_DEVCLASS_DEVSUBCLASS); return (UMATCH_NONE); } USB_ATTACH(uhub) { USB_ATTACH_START(uhub, sc, uaa); usbd_device_handle dev = uaa->device; char devinfo[1024]; usbd_status r; struct usbd_hub *hub; usb_device_request_t req; usb_hub_descriptor_t hubdesc; int p, port, nports, nremov; usbd_interface_handle iface; usb_endpoint_descriptor_t *ed; DPRINTFN(1,("uhub_attach\n")); sc->sc_hub = dev; usbd_devinfo(dev, 1, devinfo); USB_ATTACH_SETUP; printf("%s: %s\n", USBDEVNAME(sc->sc_dev), devinfo); r = usbd_set_config_index(dev, 0, 1); if (r != USBD_NORMAL_COMPLETION) { DPRINTF(("%s: configuration failed, %s\n", USBDEVNAME(sc->sc_dev), usbd_errstr(r))); USB_ATTACH_ERROR_RETURN; } if (dev->depth > USB_HUB_MAX_DEPTH) { printf("%s: hub depth (%d) exceeded, hub ignored\n", USBDEVNAME(sc->sc_dev), USB_HUB_MAX_DEPTH); USB_ATTACH_ERROR_RETURN; } /* Get hub descriptor. */ req.bmRequestType = UT_READ_CLASS_DEVICE; req.bRequest = UR_GET_DESCRIPTOR; USETW(req.wValue, 0); USETW(req.wIndex, 0); USETW(req.wLength, USB_HUB_DESCRIPTOR_SIZE); DPRINTFN(1,("usb_init_hub: getting hub descriptor\n")); r = usbd_do_request(dev, &req, &hubdesc); nports = hubdesc.bNbrPorts; if (r == USBD_NORMAL_COMPLETION && nports > 7) { USETW(req.wLength, USB_HUB_DESCRIPTOR_SIZE + (nports+1) / 8); r = usbd_do_request(dev, &req, &hubdesc); } if (r != USBD_NORMAL_COMPLETION) { DPRINTF(("%s: getting hub descriptor failed, %s\n", USBDEVNAME(sc->sc_dev), usbd_errstr(r))); USB_ATTACH_ERROR_RETURN; } for (nremov = 0, port = 1; port <= nports; port++) if (!UHD_NOT_REMOV(&hubdesc, port)) nremov++; printf("%s: %d port%s with %d removable, %s powered\n", USBDEVNAME(sc->sc_dev), nports, nports != 1 ? "s" : "", nremov, dev->self_powered ? "self" : "bus"); hub = malloc(sizeof(*hub) + (nports-1) * sizeof(struct usbd_port), M_USB, M_NOWAIT); if (hub == 0) USB_ATTACH_ERROR_RETURN; dev->hub = hub; dev->hub->hubsoftc = sc; hub->explore = uhub_explore; hub->hubdesc = hubdesc; DPRINTFN(1,("usbhub_init_hub: selfpowered=%d, parent=%p, " "parent->selfpowered=%d\n", dev->self_powered, dev->powersrc->parent, dev->powersrc->parent ? dev->powersrc->parent->self_powered : 0)); if (!dev->self_powered && dev->powersrc->parent && !dev->powersrc->parent->self_powered) { printf("%s: bus powered hub connected to bus powered hub, " "ignored\n", USBDEVNAME(sc->sc_dev)); USB_ATTACH_ERROR_RETURN; } /* Set up interrupt pipe. */ r = usbd_device2interface_handle(dev, 0, &iface); if (r != USBD_NORMAL_COMPLETION) { printf("%s: no interface handle\n", USBDEVNAME(sc->sc_dev)); USB_ATTACH_ERROR_RETURN; } ed = usbd_interface2endpoint_descriptor(iface, 0); if (ed == 0) { printf("%s: no endpoint descriptor\n", USBDEVNAME(sc->sc_dev)); USB_ATTACH_ERROR_RETURN; } if ((ed->bmAttributes & UE_XFERTYPE) != UE_INTERRUPT) { printf("%s: bad interrupt endpoint\n", USBDEVNAME(sc->sc_dev)); USB_ATTACH_ERROR_RETURN; } r = usbd_open_pipe_intr(iface, ed->bEndpointAddress,USBD_SHORT_XFER_OK, &sc->sc_ipipe, sc, sc->sc_status, sizeof(sc->sc_status), uhub_intr); if (r != USBD_NORMAL_COMPLETION) { printf("%s: cannot open interrupt pipe\n", USBDEVNAME(sc->sc_dev)); USB_ATTACH_ERROR_RETURN; } /* Wait with power off for a while. */ usbd_delay_ms(dev, USB_POWER_DOWN_TIME); for (p = 0; p < nports; p++) { struct usbd_port *up = &hub->ports[p]; up->device = 0; up->parent = dev; up->portno = p+1; r = uhub_init_port(up); if (r != USBD_NORMAL_COMPLETION) printf("%s: init of port %d failed\n", USBDEVNAME(sc->sc_dev), up->portno); } sc->sc_running = 1; USB_ATTACH_SUCCESS_RETURN; } #if defined(__FreeBSD__) -static int -uhub_detach(device_t self) +static void +uhub_disconnected(device_t self) { struct uhub_softc *sc = device_get_softc(self); struct usbd_port *up; usbd_device_handle dev = sc->sc_hub; int nports; int p; DPRINTF(("%s: disconnected\n", USBDEVNAME(self))); nports = dev->hub->hubdesc.bNbrPorts; for (p = 0; p < nports; p++) { up = &sc->sc_hub->hub->ports[p]; - if (up->device) + if (up->device) { uhub_disconnect_port(up); + } } - free(sc->sc_hub->hub, M_USB); + return; +} +static int +uhub_detach(device_t self) +{ + struct uhub_softc *sc = device_get_softc(self); + DPRINTF(("%s: disconnected\n", USBDEVNAME(self))); + free(sc->sc_hub->hub, M_USB); return 0; } #endif usbd_status uhub_init_port(up) struct usbd_port *up; { int port = up->portno; usbd_device_handle dev = up->parent; usbd_status r; u_int16_t pstatus; r = usbd_get_port_status(dev, port, &up->status); if (r != USBD_NORMAL_COMPLETION) return (r); pstatus = UGETW(up->status.wPortStatus); DPRINTF(("usbd_init_port: adding hub port=%d status=0x%04x " "change=0x%04x\n", port, pstatus, UGETW(up->status.wPortChange))); if ((pstatus & UPS_PORT_POWER) == 0) { /* Port lacks power, turn it on */ /* First let the device go through a good power cycle, */ usbd_delay_ms(dev, USB_PORT_POWER_DOWN_TIME); /* then turn the power on. */ r = usbd_set_port_feature(dev, port, UHF_PORT_POWER); if (r != USBD_NORMAL_COMPLETION) return (r); r = usbd_get_port_status(dev, port, &up->status); if (r != USBD_NORMAL_COMPLETION) return (r); DPRINTF(("usb_init_port: turn on port %d power status=0x%04x " "change=0x%04x\n", port, UGETW(up->status.wPortStatus), UGETW(up->status.wPortChange))); /* Wait for stable power. */ usbd_delay_ms(dev, dev->hub->hubdesc.bPwrOn2PwrGood * UHD_PWRON_FACTOR); } if (dev->self_powered) /* Self powered hub, give ports maximum current. */ up->power = USB_MAX_POWER; else up->power = USB_MIN_POWER; return (USBD_NORMAL_COMPLETION); } usbd_status uhub_explore(dev) usbd_device_handle dev; { usb_hub_descriptor_t *hd = &dev->hub->hubdesc; struct uhub_softc *sc = dev->hub->hubsoftc; struct usbd_port *up; usbd_status r; int port; int change, status; DPRINTFN(10, ("uhub_explore dev=%p addr=%d\n", dev, dev->address)); if (!sc->sc_running) return (USBD_NOT_STARTED); /* Ignore hubs that are too deep. */ if (dev->depth > USB_HUB_MAX_DEPTH) return (USBD_TOO_DEEP); for(port = 1; port <= hd->bNbrPorts; port++) { up = &dev->hub->ports[port-1]; r = usbd_get_port_status(dev, port, &up->status); if (r != USBD_NORMAL_COMPLETION) { DPRINTF(("uhub_explore: get port %d status failed, %s\n", port, usbd_errstr(r))); continue; } status = UGETW(up->status.wPortStatus); change = UGETW(up->status.wPortChange); DPRINTFN(5, ("uhub_explore: port %d status 0x%04x 0x%04x\n", port, status, change)); if (change & UPS_C_PORT_ENABLED) { usbd_clear_port_feature(dev, port, UHF_C_PORT_ENABLE); if (status & UPS_PORT_ENABLED) { printf("%s: port %d illegal enable change\n", USBDEVNAME(sc->sc_dev), port); } else { /* Port error condition. */ if (up->restartcnt++ < USBD_RESTART_MAX) { printf("%s: port %d error, restarting\n", USBDEVNAME(sc->sc_dev), port); goto disco; } else { printf("%s: port %d error, giving up\n", USBDEVNAME(sc->sc_dev), port); } } } if (!(change & UPS_C_CONNECT_STATUS)) { /* No status change, just do recursive explore. */ if (up->device && up->device->hub) up->device->hub->explore(up->device); continue; } DPRINTF(("uhub_explore: status change hub=%d port=%d\n", dev->address, port)); usbd_clear_port_feature(dev, port, UHF_C_PORT_CONNECTION); usbd_clear_port_feature(dev, port, UHF_C_PORT_ENABLE); /* * If there is already a device on the port the change status * must mean that is has disconnected. Looking at the * current connect status is not enough to figure this out * since a new unit may have been connected before we handle * the disconnect. */ disco: if (up->device) { /* Disconnected */ DPRINTF(("uhub_explore: device %d disappeared " "on port %d\n", up->device->address, port)); uhub_disconnect_port(up); usbd_clear_port_feature(dev, port, UHF_C_PORT_CONNECTION); } if (!(status & UPS_CURRENT_CONNECT_STATUS)) continue; /* Connected */ up->restartcnt = 0; /* Wait for maximum device power up time. */ usbd_delay_ms(dev, USB_PORT_POWERUP_DELAY); /* Reset port, which implies enabling it. */ if (usbd_reset_port(dev, port, &up->status) != USBD_NORMAL_COMPLETION) continue; /* Get device info and set its address. */ r = usbd_new_device(&sc->sc_dev, dev->bus, dev->depth + 1, status & UPS_LOW_SPEED, port, up); /* XXX retry a few times? */ if (r != USBD_NORMAL_COMPLETION) { DPRINTFN(-1,("uhub_explore: usb_new_device failed, %s\n", usbd_errstr(r))); /* Avoid addressing problems by disabling. */ /* usbd_reset_port(dev, port, &up->status); */ /* XXX * What should we do. The device may or may not be at its * assigned address. In any case we'd like to ignore it. * Maybe the port should be disabled until the device is * disconnected. */ if (r == USBD_SET_ADDR_FAILED || 1) {/* XXX */ /* The unit refused to accept a new * address, and since we cannot leave * it at 0 we have to disable the port * instead. */ printf("%s: device problem, disabling " "port %d\n", USBDEVNAME(sc->sc_dev), port); usbd_clear_port_feature(dev, port, UHF_PORT_ENABLE); /* Make sure we don't try to restart it. */ up->restartcnt = USBD_RESTART_MAX; } } else { if (up->device->hub) up->device->hub->explore(up->device); } } return (USBD_NORMAL_COMPLETION); } void uhub_disconnect_port(up) struct usbd_port *up; { usbd_device_handle dev = up->device; usbd_pipe_handle p, n; int i; struct softc { /* all softc begin like this */ bdevice sc_dev; }; struct softc *sc; struct softc *scp; if (!dev) /* no device driver attached at port */ return; sc = (struct softc *)dev->softc; scp = (struct softc *)up->parent->softc; DPRINTFN(3,("uhub_disconnect_port: up=%p dev=%p port=%d\n", up, dev, up->portno)); printf("%s: at %s port %d (addr %d) disconnected\n", USBDEVNAME(sc->sc_dev), USBDEVNAME(scp->sc_dev), up->portno, dev->address); if (!dev->cdesc) { /* Partially attached device, just drop it. */ dev->bus->devices[dev->address] = 0; up->device = 0; return; } /* Remove the device */ for (i = 0; i < dev->cdesc->bNumInterface; i++) { for (p = LIST_FIRST(&dev->ifaces[i].pipes); p; p = n) { n = LIST_NEXT(p, next); if (p->disco) p->disco(p->discoarg); } } /* XXX Free all data structures and disable further I/O. */ if (dev->hub) { struct usbd_port *rup; int p, nports; DPRINTFN(3,("usb_disconnect: hub, recursing\n")); nports = dev->hub->hubdesc.bNbrPorts; for(p = 0; p < nports; p++) { rup = &dev->hub->ports[p]; if (rup->device) uhub_disconnect_port(rup); } } #if defined(__FreeBSD__) + USB_DISCONNECTED(sc->sc_dev); device_delete_child(scp->sc_dev, sc->sc_dev); #endif /* clean up the kitchen */ for (i = 0; i < dev->cdesc->bNumInterface; i++) { for (p = LIST_FIRST(&dev->ifaces[i].pipes); p; p = n) { n = LIST_NEXT(p, next); usbd_abort_pipe(p); usbd_close_pipe(p); } } dev->bus->devices[dev->address] = 0; up->device = 0; /* XXX free */ } void uhub_intr(reqh, addr, status) usbd_request_handle reqh; usbd_private_handle addr; usbd_status status; { struct uhub_softc *sc = addr; DPRINTFN(5,("uhub_intr: sc=%p\n", sc)); if (status != USBD_NORMAL_COMPLETION) usbd_clear_endpoint_stall_async(sc->sc_ipipe); else usb_needs_explore(sc->sc_hub->bus); } #if defined(__FreeBSD__) DRIVER_MODULE(uhub, usb, uhubroot_driver, uhubroot_devclass, 0, 0); DRIVER_MODULE(uhub, uhub, uhub_driver, uhub_devclass, usbd_driver_load, 0); #endif Index: head/sys/dev/usb/ulpt.c =================================================================== --- head/sys/dev/usb/ulpt.c (revision 45719) +++ head/sys/dev/usb/ulpt.c (revision 45720) @@ -1,452 +1,447 @@ /* $NetBSD: ulpt.c,v 1.10 1999/01/08 11:58:25 augustss Exp $ */ /* $FreeBSD$ */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (augustss@carlstedt.se) at * Carlstedt Research & Technology. * * 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 NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */ #include #include #include #include #include #if defined(__NetBSD__) #include #include #elif defined(__FreeBSD__) #include #include #include #endif #include #include #include #include #include #include #include #include #define TIMEOUT hz*16 /* wait up to 16 seconds for a ready */ #define STEP hz/4 #define LPTPRI (PZERO+8) #define ULPT_BSIZE 1024 #ifdef ULPT_DEBUG #define DPRINTF(x) if (ulptdebug) logprintf x #define DPRINTFN(n,x) if (ulptdebug>(n)) logprintf x int ulptdebug = 1; #else #define DPRINTF(x) #define DPRINTFN(n,x) #endif #define UR_GET_DEVICE_ID 0 #define UR_GET_PORT_STATUS 1 #define UR_SOFT_RESET 2 #define LPS_NERR 0x08 /* printer no error */ #define LPS_SELECT 0x10 /* printer selected */ #define LPS_NOPAPER 0x20 /* printer out of paper */ #define LPS_INVERT (LPS_SELECT|LPS_NERR) #define LPS_MASK (LPS_SELECT|LPS_NERR|LPS_NOPAPER) struct ulpt_softc { bdevice sc_dev; usbd_device_handle sc_udev; /* device */ usbd_interface_handle sc_iface; /* interface */ int sc_ifaceno; usbd_pipe_handle sc_bulkpipe; /* bulk pipe */ int sc_bulk; u_char sc_state; #define ULPT_OPEN 0x01 /* device is open */ #define ULPT_OBUSY 0x02 /* printer is busy doing output */ #define ULPT_INIT 0x04 /* waiting to initialize for open */ u_char sc_flags; #if defined(__NetBSD__) #define ULPT_NOPRIME 0x40 /* don't prime on open */ #elif defined(__FreeBSD__) /* values taken from i386/isa/lpt.c */ #define ULPT_POS_INIT 0x04 /* if we are a postive init signal */ #define ULPT_POS_ACK 0x08 /* if we are a positive going ack */ #define ULPT_NOPRIME 0x10 /* don't prime the printer at all */ #define ULPT_PRIMEOPEN 0x20 /* prime on every open */ #define ULPT_AUTOLF 0x40 /* tell printer to do an automatic lf */ #define ULPT_BYPASS 0x80 /* bypass printer ready checks */ #endif u_char sc_laststatus; }; #if defined(__NetBSD__) int ulptopen __P((dev_t, int, int, struct proc *)); int ulptclose __P((dev_t, int, int, struct proc *p)); int ulptwrite __P((dev_t, struct uio *uio, int)); int ulptioctl __P((dev_t, u_long, caddr_t, int, struct proc *)); #elif defined(__FreeBSD__) static d_open_t ulptopen; static d_close_t ulptclose; static d_write_t ulptwrite; static d_ioctl_t ulptioctl; #define ULPT_CDEV_MAJOR 113 static struct cdevsw ulpt_cdevsw = { ulptopen, ulptclose, noread, ulptwrite, ulptioctl, nostop, nullreset, nodevtotty, seltrue, nommap, nostrat, "ulpt", NULL, -1 }; #endif int ulpt_status __P((struct ulpt_softc *)); void ulpt_reset __P((struct ulpt_softc *)); int ulpt_statusmsg __P((u_char, struct ulpt_softc *)); #if defined(__NetBSD__) #define ULPTUNIT(s) (minor(s) & 0x1f) #define ULPTFLAGS(s) (minor(s) & 0xe0) #elif defined(__FreeBSD__) /* defines taken from i386/isa/lpt.c */ #define ULPTUNIT(s) (minor(s) & 0x03) #define ULPTFLAGS(s) (minor(s) & 0xfc) #endif USB_DECLARE_DRIVER(ulpt); USB_MATCH(ulpt) { USB_MATCH_START(ulpt, uaa); usb_interface_descriptor_t *id; DPRINTFN(10,("ulpt_match\n")); if (!uaa->iface) return (UMATCH_NONE); id = usbd_get_interface_descriptor(uaa->iface); if (id && id->bInterfaceClass == UCLASS_PRINTER && id->bInterfaceSubClass == USUBCLASS_PRINTER && (id->bInterfaceProtocol == UPROTO_PRINTER_UNI || id->bInterfaceProtocol == UPROTO_PRINTER_BI)) return (UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO); return (UMATCH_NONE); } USB_ATTACH(ulpt) { USB_ATTACH_START(ulpt, sc, uaa); usbd_device_handle dev = uaa->device; usbd_interface_handle iface = uaa->iface; usb_interface_descriptor_t *id = usbd_get_interface_descriptor(iface); #if 0 usb_config_descriptor_t *cd = usbd_get_config_descriptor(dev); usb_device_request_t req; #endif char devinfo[1024]; usb_endpoint_descriptor_t *ed; usbd_status r; DPRINTFN(10,("ulpt_attach: sc=%p\n", sc)); usbd_devinfo(dev, 0, devinfo); USB_ATTACH_SETUP; printf("%s: %s, iclass %d/%d\n", USBDEVNAME(sc->sc_dev), devinfo, id->bInterfaceClass, id->bInterfaceSubClass); /* Figure out which endpoint is the bulk out endpoint. */ ed = usbd_interface2endpoint_descriptor(iface, 0); if (!ed) goto nobulk; if ((ed->bEndpointAddress & UE_IN) != UE_OUT || (ed->bmAttributes & UE_XFERTYPE) != UE_BULK) { /* In case we are using a bidir protocol... */ ed = usbd_interface2endpoint_descriptor(iface, 1); if (!ed) goto nobulk; if ((ed->bEndpointAddress & UE_IN) != UE_OUT || (ed->bmAttributes & UE_XFERTYPE) != UE_BULK) goto nobulk; } sc->sc_bulk = ed->bEndpointAddress; DPRINTFN(10, ("ulpt_attach: bulk=%d\n", sc->sc_bulk)); sc->sc_iface = iface; r = usbd_interface2device_handle(iface, &sc->sc_udev); if (r != USBD_NORMAL_COMPLETION) USB_ATTACH_ERROR_RETURN; sc->sc_ifaceno = id->bInterfaceNumber; #if 0 XXX needs a different way to read the id string since the length is unknown. usbd_do_request() returns error on a short transfer. req.bmRequestType = UT_READ_CLASS_INTERFACE; req.bRequest = UR_GET_DEVICE_ID; USETW(req.wValue, cd->bConfigurationValue); USETW2(req.wIndex, id->bInterfaceNumber, id->bAlternateSetting); USETW(req.wLength, sizeof devinfo - 1); r = usbd_do_request(dev, &req, devinfo); if (r == USBD_NORMAL_COMPLETION) { int len; char *idstr; len = (devinfo[0] << 8) | (devinfo[1] & 0xff); /* devinfo now contains an IEEE-1284 device ID */ idstr = devinfo+2; idstr[len] = 0; printf("%s: device id <%s>\n", USBDEVNAME(sc->sc_dev), idstr); } else { printf("%s: cannot get device id\n", USBDEVNAME(sc->sc_dev)); } #endif USB_ATTACH_SUCCESS_RETURN; nobulk: printf("%s: could not find bulk endpoint\n", USBDEVNAME(sc->sc_dev)); USB_ATTACH_ERROR_RETURN; } int ulpt_status(sc) struct ulpt_softc *sc; { usb_device_request_t req; usbd_status r; u_char status; req.bmRequestType = UT_READ_CLASS_INTERFACE; req.bRequest = UR_GET_PORT_STATUS; USETW(req.wValue, 0); USETW(req.wIndex, sc->sc_ifaceno); USETW(req.wLength, 1); r = usbd_do_request(sc->sc_udev, &req, &status); DPRINTFN(1, ("ulpt_status: status=0x%02x r=%d\n", status, r)); if (r == USBD_NORMAL_COMPLETION) return (status); else return (0); } void ulpt_reset(sc) struct ulpt_softc *sc; { usb_device_request_t req; DPRINTFN(1, ("ulpt_reset\n")); req.bmRequestType = UT_WRITE_CLASS_OTHER; req.bRequest = UR_SOFT_RESET; USETW(req.wValue, 0); USETW(req.wIndex, sc->sc_ifaceno); USETW(req.wLength, 0); (void)usbd_do_request(sc->sc_udev, &req, 0); } /* * Reset the printer, then wait until it's selected and not busy. */ int ulptopen(dev, flag, mode, p) dev_t dev; int flag; int mode; struct proc *p; { u_char flags = ULPTFLAGS(dev); usbd_status r; int spin, error; USB_GET_SC_OPEN(ulpt, ULPTUNIT(dev), sc); if (!sc || !sc->sc_iface) return ENXIO; if (sc->sc_state) return EBUSY; sc->sc_state = ULPT_INIT; sc->sc_flags = flags; DPRINTF(("ulptopen: flags=0x%x\n", (unsigned)flags)); #if defined(ULPT_DEBUG) && defined(__FreeBSD__) /* Ignoring these flags might not be a good idea */ if ((flags & ~ULPT_NOPRIME) != 0) printf("ulptopen: flags ignored: %b\n", flags, "\20\3POS_INIT\4POS_ACK\6PRIME_OPEN\7AUTOLF\10BYPASS"); #endif if ((flags & ULPT_NOPRIME) == 0) ulpt_reset(sc); for (spin = 0; (ulpt_status(sc) & LPS_SELECT) == 0; spin += STEP) { if (spin >= TIMEOUT) { sc->sc_state = 0; return EBUSY; } /* wait 1/4 second, give up if we get a signal */ error = tsleep((caddr_t)sc, LPTPRI | PCATCH, "ulptop", STEP); if (error != EWOULDBLOCK) { sc->sc_state = 0; return error; } } r = usbd_open_pipe(sc->sc_iface, sc->sc_bulk, 0, &sc->sc_bulkpipe); if (r != USBD_NORMAL_COMPLETION) { sc->sc_state = 0; return (EIO); } sc->sc_state = ULPT_OPEN; DPRINTF(("ulptopen: done\n")); return (0); } int ulpt_statusmsg(status, sc) u_char status; struct ulpt_softc *sc; { u_char new; status = (status ^ LPS_INVERT) & LPS_MASK; new = status & ~sc->sc_laststatus; sc->sc_laststatus = status; if (new & LPS_SELECT) log(LOG_NOTICE, "%s: offline\n", USBDEVNAME(sc->sc_dev)); else if (new & LPS_NOPAPER) log(LOG_NOTICE, "%s: out of paper\n", USBDEVNAME(sc->sc_dev)); else if (new & LPS_NERR) log(LOG_NOTICE, "%s: output error\n", USBDEVNAME(sc->sc_dev)); return status; } int ulptclose(dev, flag, mode, p) dev_t dev; int flag; int mode; struct proc *p; { USB_GET_SC(ulpt, ULPTUNIT(dev), sc); usbd_close_pipe(sc->sc_bulkpipe); sc->sc_state = 0; DPRINTF(("ulptclose: closed\n")); return (0); } int ulptwrite(dev, uio, flags) dev_t dev; struct uio *uio; int flags; { size_t n; int error = 0; char buf[ULPT_BSIZE]; usbd_request_handle reqh; usbd_status r; USB_GET_SC(ulpt, ULPTUNIT(dev), sc); DPRINTF(("ulptwrite\n")); reqh = usbd_alloc_request(); if (reqh == 0) return (ENOMEM); while ((n = min(ULPT_BSIZE, uio->uio_resid)) != 0) { ulpt_statusmsg(ulpt_status(sc), sc); error = uiomove(buf, n, uio); if (error) break; /* XXX use callback to enable interrupt? */ r = usbd_setup_request(reqh, sc->sc_bulkpipe, 0, buf, n, 0, USBD_NO_TIMEOUT, 0); if (r != USBD_NORMAL_COMPLETION) { error = EIO; break; } DPRINTFN(1, ("ulptwrite: transfer %d bytes\n", n)); r = usbd_sync_transfer(reqh); if (r != USBD_NORMAL_COMPLETION) { DPRINTF(("ulptwrite: error=%d\n", r)); usbd_clear_endpoint_stall(sc->sc_bulkpipe); error = EIO; break; } } usbd_free_request(reqh); return (error); } int ulptioctl(dev, cmd, data, flag, p) dev_t dev; u_long cmd; caddr_t data; int flag; struct proc *p; { int error = 0; switch (cmd) { default: error = ENODEV; } return error; } #if defined(__FreeBSD__) static int ulpt_detach(device_t self) { - const char *devinfo = device_get_desc(self); - DPRINTF(("%s: disconnected\n", USBDEVNAME(self))); - if (devinfo) { - device_set_desc(self, NULL); - free((void *)devinfo, M_USB); - } + device_set_desc(self, NULL); return 0; } CDEV_DRIVER_MODULE(ulpt, uhub, ulpt_driver, ulpt_devclass, ULPT_CDEV_MAJOR, ulpt_cdevsw, usbd_driver_load, 0); #endif Index: head/sys/dev/usb/umodem.c =================================================================== --- head/sys/dev/usb/umodem.c (revision 45719) +++ head/sys/dev/usb/umodem.c (revision 45720) @@ -1,143 +1,138 @@ /* $NetBSD: umodem.c,v 1.5 1999/01/08 11:58:25 augustss Exp $ */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (augustss@carlstedt.se) at * Carlstedt Research & Technology. * * 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 NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */ #include #include #include #include #if defined(__NetBSD__) #include #elif defined(__FreeBSD__) #include #include #include #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef UMODEM_DEBUG #define DPRINTF(x) if (umodemdebug) logprintf x #define DPRINTFN(n,x) if (umodemdebug>(n)) logprintf x int umodemdebug = 1; #else #define DPRINTF(x) #define DPRINTFN(n,x) #endif struct umodem_softc { bdevice sc_dev; /* base device */ usbd_interface_handle sc_ctl; /* control interface */ usbd_interface_handle sc_data; /* data interface */ uByte cmCaps; uByte acmCaps; }; void umodem_intr __P((usbd_request_handle, usbd_private_handle, usbd_status)); void umodem_disco __P((void *)); USB_DECLARE_DRIVER(umodem); USB_MATCH(umodem) { USB_MATCH_START(umodem, uaa); usb_interface_descriptor_t *id; if (!uaa->iface) return (UMATCH_NONE); id = usbd_get_interface_descriptor(uaa->iface); if (!id || id->bInterfaceClass != UCLASS_CDC || id->bInterfaceSubClass != USUBCLASS_ABSTRACT_CONTROL_MODEL || id->bInterfaceProtocol != UPROTO_CDC_AT) return (UMATCH_NONE); return (UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO); } USB_ATTACH(umodem) { USB_ATTACH_START(umodem, sc, uaa); usbd_interface_handle iface = uaa->iface; usb_interface_descriptor_t *id; char devinfo[1024]; sc->sc_ctl = iface; id = usbd_get_interface_descriptor(iface); usbd_devinfo(uaa->device, 0, devinfo); USB_ATTACH_SETUP; printf("%s: %s, iclass %d/%d\n", USBDEVNAME(sc->sc_dev), devinfo, id->bInterfaceClass, id->bInterfaceSubClass); USB_ATTACH_SUCCESS_RETURN; } #if defined(__FreeBSD__) static int umodem_detach(device_t self) { - const char *devinfo = device_get_desc(self); - DPRINTF(("%s: disconnected\n", USBDEVNAME(self))); - if (devinfo) { - device_set_desc(self, NULL); - free((void *)devinfo, M_USB); - } + device_set_desc(self, NULL); return 0; } #endif #if defined(__FreeBSD__) DRIVER_MODULE(umodem, uhub, umodem_driver, umodem_devclass, usbd_driver_load,0); #endif Index: head/sys/dev/usb/ums.c =================================================================== --- head/sys/dev/usb/ums.c (revision 45719) +++ head/sys/dev/usb/ums.c (revision 45720) @@ -1,800 +1,796 @@ /* $NetBSD: ums.c,v 1.19 1999/01/08 11:58:25 augustss Exp $ */ /* $FreeBSD$ */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (augustss@carlstedt.se) at * Carlstedt Research & Technology. * * 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 NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */ #include #include #include #include #if defined(__NetBSD__) #include #include #elif defined(__FreeBSD__) #include #include #include #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(__NetBSD__) #include #include #elif defined(__FreeBSD__) #include #endif #ifdef UMS_DEBUG #define DPRINTF(x) if (umsdebug) logprintf x #define DPRINTFN(n,x) if (umsdebug>(n)) logprintf x int umsdebug = 1; #else #define DPRINTF(x) #define DPRINTFN(n,x) #endif #define UMSUNIT(s) (minor(s)&0x1f) #define QUEUE_BUFSIZE 400 /* MUST be divisible by 5 _and_ 8 */ struct ums_softc { bdevice sc_dev; /* base device */ usbd_interface_handle sc_iface; /* interface */ usbd_pipe_handle sc_intrpipe; /* interrupt pipe */ int sc_ep_addr; u_char *sc_ibuf; u_int8_t sc_iid; int sc_isize; struct hid_location sc_loc_x, sc_loc_y, sc_loc_z; struct hid_location *sc_loc_btn; int sc_enabled; int sc_disconnected; /* device is gone */ int flags; /* device configuration */ #define UMS_Z 0x01 /* z direction available */ int nbuttons; #define MAX_BUTTONS 7 /* chosen because sc_buttons is u_char */ #if defined(__NetBSD__) u_char sc_buttons; /* mouse button status */ struct device *sc_wsmousedev; #elif defined(__FreeBSD__) u_char qbuf[QUEUE_BUFSIZE]; /* must be divisable by 3&4 */ u_char dummy[100]; /* XXX just for safety and for now */ int qcount, qhead, qtail; mousehw_t hw; mousemode_t mode; mousestatus_t status; int state; # define UMS_ASLEEP 0x01 /* readFromDevice is waiting */ # define UMS_SELECT 0x02 /* select is waiting */ struct selinfo rsel; /* process waiting in select */ #endif }; #define MOUSE_FLAGS_MASK (HIO_CONST|HIO_RELATIVE) #define MOUSE_FLAGS (HIO_RELATIVE) void ums_intr __P((usbd_request_handle, usbd_private_handle, usbd_status)); void ums_disco __P((void *)); static int ums_enable __P((void *)); static void ums_disable __P((void *)); #if defined(__NetBSD__) static int ums_ioctl __P((void *, u_long, caddr_t, int, struct proc *)); const struct wsmouse_accessops ums_accessops = { ums_enable, ums_ioctl, ums_disable, }; #elif defined(__FreeBSD__) static d_open_t ums_open; static d_close_t ums_close; static d_read_t ums_read; static d_ioctl_t ums_ioctl; static d_poll_t ums_poll; #define UMS_CDEV_MAJOR 111 static struct cdevsw ums_cdevsw = { ums_open, ums_close, ums_read, nowrite, ums_ioctl, nostop, nullreset, nodevtotty, ums_poll, nommap, nostrat, "ums", NULL, -1 }; #endif USB_DECLARE_DRIVER(ums); USB_MATCH(ums) { USB_MATCH_START(ums, uaa); usb_interface_descriptor_t *id; int size, ret; void *desc; usbd_status r; if (!uaa->iface) return (UMATCH_NONE); id = usbd_get_interface_descriptor(uaa->iface); if (!id || id->bInterfaceClass != UCLASS_HID) return (UMATCH_NONE); r = usbd_alloc_report_desc(uaa->iface, &desc, &size, M_TEMP); if (r != USBD_NORMAL_COMPLETION) return (UMATCH_NONE); if (hid_is_collection(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE))) ret = UMATCH_IFACECLASS; else ret = UMATCH_NONE; free(desc, M_TEMP); return (ret); } USB_ATTACH(ums) { USB_ATTACH_START(ums, sc, uaa); usbd_interface_handle iface = uaa->iface; usb_interface_descriptor_t *id; usb_endpoint_descriptor_t *ed; #if defined(__NetBSD__) struct wsmousedev_attach_args a; #endif int size; void *desc; usbd_status r; char devinfo[1024]; u_int32_t flags; int i; struct hid_location loc_btn; sc->sc_disconnected = 1; sc->sc_iface = iface; id = usbd_get_interface_descriptor(iface); usbd_devinfo(uaa->device, 0, devinfo); USB_ATTACH_SETUP; printf("%s: %s, iclass %d/%d\n", USBDEVNAME(sc->sc_dev), devinfo, id->bInterfaceClass, id->bInterfaceSubClass); ed = usbd_interface2endpoint_descriptor(iface, 0); if (!ed) { printf("%s: could not read endpoint descriptor\n", USBDEVNAME(sc->sc_dev)); USB_ATTACH_ERROR_RETURN; } DPRINTFN(10,("ums_attach: bLength=%d bDescriptorType=%d " "bEndpointAddress=%d-%s bmAttributes=%d wMaxPacketSize=%d" " bInterval=%d\n", ed->bLength, ed->bDescriptorType, ed->bEndpointAddress & UE_ADDR, ed->bEndpointAddress & UE_IN ? "in" : "out", ed->bmAttributes & UE_XFERTYPE, UGETW(ed->wMaxPacketSize), ed->bInterval)); if ((ed->bEndpointAddress & UE_IN) != UE_IN || (ed->bmAttributes & UE_XFERTYPE) != UE_INTERRUPT) { printf("%s: unexpected endpoint\n", USBDEVNAME(sc->sc_dev)); USB_ATTACH_ERROR_RETURN; } r = usbd_alloc_report_desc(uaa->iface, &desc, &size, M_TEMP); if (r != USBD_NORMAL_COMPLETION) USB_ATTACH_ERROR_RETURN; if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X), hid_input, &sc->sc_loc_x, &flags)) { printf("%s: mouse has no X report\n", USBDEVNAME(sc->sc_dev)); USB_ATTACH_ERROR_RETURN; } if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) { printf("%s: X report 0x%04x not supported\n", USBDEVNAME(sc->sc_dev), flags); USB_ATTACH_ERROR_RETURN; } if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y), hid_input, &sc->sc_loc_y, &flags)) { printf("%s: mouse has no Y report\n", USBDEVNAME(sc->sc_dev)); USB_ATTACH_ERROR_RETURN; } if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) { printf("%s: Y report 0x%04x not supported\n", USBDEVNAME(sc->sc_dev), flags); USB_ATTACH_ERROR_RETURN; } /* try to guess the Z activator: first check Z, then WHEEL */ if (hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Z), hid_input, &sc->sc_loc_z, &flags) || hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_WHEEL), hid_input, &sc->sc_loc_z, &flags)) { if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) { sc->sc_loc_z.size = 0; /* Bad Z coord, ignore it */ } else { sc->flags |= UMS_Z; } } /* figure out the number of buttons */ for (i = 1; i <= MAX_BUTTONS; i++) if (!hid_locate(desc, size, HID_USAGE2(HUP_BUTTON, i), hid_input, &loc_btn, 0)) break; sc->nbuttons = i - 1; sc->sc_loc_btn = malloc(sizeof(struct hid_location)*sc->nbuttons, M_USBDEV, M_NOWAIT); if (!sc->sc_loc_btn) { printf("%s: no memory\n", USBDEVNAME(sc->sc_dev)); USB_ATTACH_ERROR_RETURN; } printf("%s: %d buttons%s\n", USBDEVNAME(sc->sc_dev), sc->nbuttons, sc->flags & UMS_Z? " and Z dir." : ""); for (i = 1; i <= sc->nbuttons; i++) hid_locate(desc, size, HID_USAGE2(HUP_BUTTON, i), hid_input, &sc->sc_loc_btn[i-1], 0); sc->sc_isize = hid_report_size(desc, size, hid_input, &sc->sc_iid); sc->sc_ibuf = malloc(sc->sc_isize, M_USB, M_NOWAIT); if (!sc->sc_ibuf) { printf("%s: no memory\n", USBDEVNAME(sc->sc_dev)); free(sc->sc_loc_btn, M_USB); USB_ATTACH_ERROR_RETURN; } sc->sc_ep_addr = ed->bEndpointAddress; sc->sc_disconnected = 0; free(desc, M_TEMP); #ifdef UMS_DEBUG DPRINTF(("ums_attach: sc=%p\n", sc)); DPRINTF(("ums_attach: X\t%d/%d\n", sc->sc_loc_x.pos, sc->sc_loc_x.size)); DPRINTF(("ums_attach: Y\t%d/%d\n", sc->sc_loc_x.pos, sc->sc_loc_x.size)); if (sc->flags & UMS_Z) DPRINTF(("ums_attach: Z\t%d/%d\n", sc->sc_loc_z.pos, sc->sc_loc_z.size)); for (i = 1; i <= sc->nbuttons; i++) { DPRINTF(("ums_attach: B%d\t%d/%d\n", i, sc->sc_loc_btn[i-1].pos,sc->sc_loc_btn[i-1].size)); } DPRINTF(("ums_attach: size=%d, id=%d\n", sc->sc_isize, sc->sc_iid)); #endif #if defined(__NetBSD__) a.accessops = &ums_accessops; a.accesscookie = sc; sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint); #elif defined(__FreeBSD__) if (sc->nbuttons > MOUSE_MSC_MAXBUTTON) sc->hw.buttons = MOUSE_MSC_MAXBUTTON; else sc->hw.buttons = sc->nbuttons; sc->hw.iftype = MOUSE_IF_USB; sc->hw.type = MOUSE_MOUSE; sc->hw.model = MOUSE_MODEL_GENERIC; sc->hw.hwid = 0; sc->mode.protocol = MOUSE_PROTO_MSC; sc->mode.rate = -1; sc->mode.resolution = MOUSE_RES_UNKNOWN; sc->mode.accelfactor = 0; sc->mode.level = 0; sc->mode.packetsize = MOUSE_MSC_PACKETSIZE; sc->mode.syncmask[0] = MOUSE_MSC_SYNCMASK; sc->mode.syncmask[1] = MOUSE_MSC_SYNC; sc->status.flags = 0; sc->status.button = sc->status.obutton = 0; sc->status.dx = sc->status.dy = sc->status.dz = 0; sc->rsel.si_flags = 0; sc->rsel.si_pid = 0; #endif USB_ATTACH_SUCCESS_RETURN; } #if defined(__FreeBSD__) static int ums_detach(device_t self) { struct ums_softc *sc = device_get_softc(self); - const char *devinfo = device_get_desc(self); if (sc->sc_enabled) { usbd_abort_pipe(sc->sc_intrpipe); usbd_close_pipe(sc->sc_intrpipe); } sc->sc_disconnected = 1; DPRINTF(("%s: disconnected\n", USBDEVNAME(self))); - if (devinfo) { - device_set_desc(self, NULL); - free((void *)devinfo, M_USB); - } + device_set_desc(self, NULL); free(sc->sc_loc_btn, M_USB); free(sc->sc_ibuf, M_USB); /* someone waiting for data */ if (sc->state & UMS_ASLEEP) { sc->state &= ~UMS_ASLEEP; wakeup(sc); } if (sc->state & UMS_SELECT) { sc->state &= ~UMS_SELECT; selwakeup(&sc->rsel); } return 0; } #endif void ums_disco(p) void *p; { struct ums_softc *sc = p; DPRINTF(("ums_disco: sc=%p\n", sc)); usbd_abort_pipe(sc->sc_intrpipe); sc->sc_disconnected = 1; } void ums_intr(reqh, addr, status) usbd_request_handle reqh; usbd_private_handle addr; usbd_status status; { struct ums_softc *sc = addr; u_char *ibuf; int dx, dy, dz; u_char buttons = 0; int i; #if defined(__NetBSD__) #define UMS_BUT(i) ((i) == 1 || (i) == 2 ? 3 - (i) : i) #elif defined(__FreeBSD__) #define UMS_BUT(i) ((i) < 3 ? (((i) + 2) % 3) : (i)) #endif DPRINTFN(5, ("ums_intr: sc=%p status=%d\n", sc, status)); DPRINTFN(5, ("ums_intr: data = %02x %02x %02x\n", sc->sc_ibuf[0], sc->sc_ibuf[1], sc->sc_ibuf[2])); if (status == USBD_CANCELLED) return; if (status != USBD_NORMAL_COMPLETION) { DPRINTF(("ums_intr: status=%d\n", status)); usbd_clear_endpoint_stall_async(sc->sc_intrpipe); return; } ibuf = sc->sc_ibuf; if (sc->sc_iid) { if (*ibuf++ != sc->sc_iid) return; } dx = hid_get_data(ibuf, &sc->sc_loc_x); dy = -hid_get_data(ibuf, &sc->sc_loc_y); dz = hid_get_data(ibuf, &sc->sc_loc_z); for (i = 0; i < sc->nbuttons; i++) if (hid_get_data(ibuf, &sc->sc_loc_btn[i])) buttons |= (1 << UMS_BUT(i)); #if defined(__NetBSD__) if (dx || dy || buttons != sc->sc_buttons) { DPRINTFN(10, ("ums_intr: x:%d y:%d z:%d buttons:0x%x\n", dx, dy, dz, buttons)); sc->sc_buttons = buttons; if (sc->sc_wsmousedev) wsmouse_input(sc->sc_wsmousedev, buttons, dx, dy, dz); #elif defined(__FreeBSD__) if (dx || dy || dz || (sc->flags & UMS_Z) || buttons != sc->status.button) { DPRINTFN(5, ("ums_intr: x:%d y:%d z:%d buttons:0x%x\n", dx, dy, dz, buttons)); sc->status.button = buttons; sc->status.dx += dx; sc->status.dy += dy; sc->status.dz += dz; /* Discard data in case of full buffer */ if (sc->qcount == sizeof(sc->qbuf)) { DPRINTF(("Buffer full, discarded packet")); return; } if (dx > 254) dx = 254; if (dx < -256) dx = -256; if (dy > 254) dy = 254; if (dy < -256) dy = -256; if (dz > 126) dz = 126; if (dz < -128) dz = -128; sc->qbuf[sc->qhead] = sc->mode.syncmask[1]; sc->qbuf[sc->qhead] |= ~buttons & MOUSE_MSC_BUTTONS; sc->qbuf[sc->qhead+1] = dx >> 1; sc->qbuf[sc->qhead+2] = dy >> 1; sc->qbuf[sc->qhead+3] = dx - (dx >> 1); sc->qbuf[sc->qhead+4] = dy - (dy >> 1); if (sc->mode.level == 1) { sc->qbuf[sc->qhead+5] = dz >> 1; sc->qbuf[sc->qhead+6] = dz - (dz >> 1); sc->qbuf[sc->qhead+7] = ((~buttons >> 3) & MOUSE_SYS_EXTBUTTONS); } sc->qhead += sc->mode.packetsize; sc->qcount += sc->mode.packetsize; #ifdef UMS_DEBUG if (sc->qhead > sizeof(sc->qbuf)) DPRINTF(("Buffer overrun! %d %d\n", sc->qhead, sizeof(sc->qbuf))); #endif /* wrap round at end of buffer */ if (sc->qhead >= sizeof(sc->qbuf)) sc->qhead = 0; /* someone waiting for data */ if (sc->state & UMS_ASLEEP) { sc->state &= ~UMS_ASLEEP; wakeup(sc); } if (sc->state & UMS_SELECT) { sc->state &= ~UMS_SELECT; selwakeup(&sc->rsel); } #endif } } static int ums_enable(v) void *v; { struct ums_softc *sc = v; usbd_status r; if (sc->sc_enabled) return EBUSY; sc->sc_enabled = 1; #if defined(__NetBSD__) sc->sc_buttons = 0; #elif defined(__FreeBSD__) sc->qcount = 0; sc->qhead = sc->qtail = 0; sc->status.flags = 0; sc->status.button = sc->status.obutton = 0; sc->status.dx = sc->status.dy = sc->status.dz = 0; #endif /* Set up interrupt pipe. */ r = usbd_open_pipe_intr(sc->sc_iface, sc->sc_ep_addr, USBD_SHORT_XFER_OK, &sc->sc_intrpipe, sc, sc->sc_ibuf, sc->sc_isize, ums_intr); if (r != USBD_NORMAL_COMPLETION) { DPRINTF(("ums_enable: usbd_open_pipe_intr failed, error=%d\n", r)); sc->sc_enabled = 0; return (EIO); } usbd_set_disco(sc->sc_intrpipe, ums_disco, sc); return (0); } static void ums_disable(v) void *v; { struct ums_softc *sc = v; /* Disable interrupts. */ usbd_abort_pipe(sc->sc_intrpipe); usbd_close_pipe(sc->sc_intrpipe); sc->sc_enabled = 0; #if defined(__FreeBSD__) if (sc->qcount != 0) DPRINTF(("Discarded %d bytes in queue\n", sc->qcount)); #endif } #if defined(__NetBSD__) static int ums_ioctl(v, cmd, data, flag, p) void *v; u_long cmd; caddr_t data; int flag; struct proc *p; { switch (cmd) { case WSMOUSEIO_GTYPE: *(u_int *)data = WSMOUSE_TYPE_USB; return (0); } return (-1); } #elif defined(__FreeBSD__) static int ums_open(dev_t dev, int flag, int fmt, struct proc *p) { USB_GET_SC_OPEN(ums, UMSUNIT(dev), sc); return ums_enable(sc); } static int ums_close(dev_t dev, int flag, int fmt, struct proc *p) { USB_GET_SC(ums, UMSUNIT(dev), sc); if (sc->sc_enabled) ums_disable(sc); return 0; } static int ums_read(dev_t dev, struct uio *uio, int flag) { USB_GET_SC(ums, UMSUNIT(dev), sc); int s; char buf[sizeof(sc->qbuf)]; int l = 0; int error; s = splusb(); while (sc->qcount == 0 ) { /* NWH XXX non blocking I/O ?? if (non blocking I/O ) { splx(s); return EWOULDBLOCK; } else { */ sc->state |= UMS_ASLEEP; error = tsleep(sc, PZERO | PCATCH, "umsrea", 0); if (error) { splx(s); return error; } else if (!sc->sc_enabled) { splx(s); return EINTR; } } while ((sc->qcount > 0) && (uio->uio_resid > 0)) { l = (sc->qcount < uio->uio_resid? sc->qcount:uio->uio_resid); if (l > sizeof(buf)) l = sizeof(buf); if (l > sizeof(sc->qbuf) - sc->qtail) /* transfer till end of buf */ l = sizeof(sc->qbuf) - sc->qtail; splx(s); uiomove(&sc->qbuf[sc->qtail], l, uio); s = splusb(); if ( sc->qcount - l < 0 ) { DPRINTF(("qcount below 0, count=%d l=%d\n", sc->qcount, l)); sc->qcount = l; } sc->qcount -= l; /* remove the bytes from the buffer */ sc->qtail = (sc->qtail + l) % sizeof(sc->qbuf); } splx(s); return 0; } static int ums_poll(dev_t dev, int events, struct proc *p) { USB_GET_SC(ums, UMSUNIT(dev), sc); int revents = 0; int s; s = splusb(); if (events & (POLLIN | POLLRDNORM)) { if (sc->qcount) { revents = events & (POLLIN | POLLRDNORM); } else { sc->state |= UMS_SELECT; selrecord(p, &sc->rsel); } } splx(s); return revents; } int ums_ioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p) { USB_GET_SC(ums, UMSUNIT(dev), sc); int error = 0; int s; mousemode_t mode; switch(cmd) { case MOUSE_GETHWINFO: *(mousehw_t *)addr = sc->hw; break; case MOUSE_GETMODE: *(mousemode_t *)addr = sc->mode; break; case MOUSE_SETMODE: mode = *(mousemode_t *)addr; if (mode.level == -1) /* don't change the current setting */ ; else if ((mode.level < 0) || (mode.level > 1)) return (EINVAL); s = splusb(); sc->mode.level = mode.level; if (sc->mode.level == 0) { if (sc->nbuttons > MOUSE_MSC_MAXBUTTON) sc->hw.buttons = MOUSE_MSC_MAXBUTTON; else sc->hw.buttons = sc->nbuttons; sc->mode.protocol = MOUSE_PROTO_MSC; sc->mode.packetsize = MOUSE_MSC_PACKETSIZE; sc->mode.syncmask[0] = MOUSE_MSC_SYNCMASK; sc->mode.syncmask[1] = MOUSE_MSC_SYNC; } else if (sc->mode.level == 1) { if (sc->nbuttons > MOUSE_SYS_MAXBUTTON) sc->hw.buttons = MOUSE_SYS_MAXBUTTON; else sc->hw.buttons = sc->nbuttons; sc->mode.protocol = MOUSE_PROTO_SYSMOUSE; sc->mode.packetsize = MOUSE_SYS_PACKETSIZE; sc->mode.syncmask[0] = MOUSE_SYS_SYNCMASK; sc->mode.syncmask[1] = MOUSE_SYS_SYNC; } bzero(sc->qbuf, sizeof(sc->qbuf)); sc->qhead = sc->qtail = sc->qcount = 0; splx(s); break; case MOUSE_GETLEVEL: *(int *)addr = sc->mode.level; break; case MOUSE_SETLEVEL: if (*(int *)addr < 0 || *(int *)addr > 1) return (EINVAL); s = splusb(); sc->mode.level = *(int *)addr; if (sc->mode.level == 0) { if (sc->nbuttons > MOUSE_MSC_MAXBUTTON) sc->hw.buttons = MOUSE_MSC_MAXBUTTON; else sc->hw.buttons = sc->nbuttons; sc->mode.protocol = MOUSE_PROTO_MSC; sc->mode.packetsize = MOUSE_MSC_PACKETSIZE; sc->mode.syncmask[0] = MOUSE_MSC_SYNCMASK; sc->mode.syncmask[1] = MOUSE_MSC_SYNC; } else if (sc->mode.level == 1) { if (sc->nbuttons > MOUSE_SYS_MAXBUTTON) sc->hw.buttons = MOUSE_SYS_MAXBUTTON; else sc->hw.buttons = sc->nbuttons; sc->mode.protocol = MOUSE_PROTO_SYSMOUSE; sc->mode.packetsize = MOUSE_SYS_PACKETSIZE; sc->mode.syncmask[0] = MOUSE_SYS_SYNCMASK; sc->mode.syncmask[1] = MOUSE_SYS_SYNC; } bzero(sc->qbuf, sizeof(sc->qbuf)); sc->qhead = sc->qtail = sc->qcount = 0; splx(s); break; case MOUSE_GETSTATUS: { mousestatus_t *status = (mousestatus_t *) addr; s = splusb(); *status = sc->status; sc->status.obutton = sc->status.button; sc->status.button = 0; sc->status.dx = sc->status.dy = sc->status.dz = 0; splx(s); if (status->dx || status->dy || status->dz) status->flags |= MOUSE_POSCHANGED; if (status->button != status->obutton) status->flags |= MOUSE_BUTTONSCHANGED; break; } default: error = ENOTTY; } return error; } #endif #if defined(__FreeBSD__) CDEV_DRIVER_MODULE(ums, uhub, ums_driver, ums_devclass, UMS_CDEV_MAJOR, ums_cdevsw, usbd_driver_load, 0); #endif Index: head/sys/dev/usb/usb.c =================================================================== --- head/sys/dev/usb/usb.c (revision 45719) +++ head/sys/dev/usb/usb.c (revision 45720) @@ -1,412 +1,413 @@ /* $NetBSD: usb.c,v 1.11 1999/01/08 11:58:25 augustss Exp $ */ /* $FreeBSD$ */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (augustss@carlstedt.se) at * Carlstedt Research & Technology. * * 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 NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */ /* * USB specifications and other documentation can be found at * http://www.usb.org/developers/data/ and * http://www.usb.org/developers/index.html . */ #include #include #include #include #if defined(__NetBSD__) #include #elif defined(__FreeBSD__) #include #include #include #include #include #endif #include #include #include #include #if defined(__FreeBSD__) MALLOC_DEFINE(M_USB, "USB", "USB"); MALLOC_DEFINE(M_USBDEV, "USBdev", "USB device"); #include "usb_if.h" #endif /* defined(__FreeBSD__) */ #include #include #include #ifdef USB_DEBUG #define DPRINTF(x) if (usbdebug) logprintf x #define DPRINTFN(n,x) if (usbdebug>(n)) logprintf x int usbdebug = 1; extern int uhcidebug, ohcidebug; #else #define DPRINTF(x) #define DPRINTFN(n,x) #endif #define USBUNIT(dev) (minor(dev)) struct usb_softc { bdevice sc_dev; /* base device */ usbd_bus_handle sc_bus; /* USB controller */ struct usbd_port sc_port; /* dummy port for root hub */ char sc_running; char sc_exploring; struct selinfo sc_consel; /* waiting for connect change */ }; #if defined(__NetBSD__) int usbopen __P((dev_t, int, int, struct proc *)); int usbclose __P((dev_t, int, int, struct proc *)); int usbioctl __P((dev_t, u_long, caddr_t, int, struct proc *)); int usbpoll __P((dev_t, int, struct proc *)); #elif defined(__FreeBSD__) d_open_t usbopen; d_close_t usbclose; d_ioctl_t usbioctl; int usbpoll __P((dev_t, int, struct proc *)); struct cdevsw usb_cdevsw = { usbopen, usbclose, noread, nowrite, usbioctl, nullstop, nullreset, nodevtotty, usbpoll, nommap, nostrat, "usb", NULL, -1 }; #endif usbd_status usb_discover __P((struct usb_softc *)); USB_DECLARE_DRIVER_INIT(usb, DEVMETHOD(bus_print_child, usbd_print_child)); USB_MATCH(usb) { DPRINTF(("usbd_match\n")); return (UMATCH_GENERIC); } USB_ATTACH(usb) { #if defined(__NetBSD__) struct usb_softc *sc = (struct usb_softc *)self; #elif defined(__FreeBSD__) struct usb_softc *sc = device_get_softc(self); void *aux = device_get_ivars(self); #endif usbd_device_handle dev; usbd_status r; #if defined(__NetBSD__) printf("\n"); #elif defined(__FreeBSD__) sc->sc_dev = self; #endif DPRINTF(("usbd_attach\n")); usbd_init(); sc->sc_bus = aux; sc->sc_bus->usbctl = sc; sc->sc_running = 1; sc->sc_bus->use_polling = 1; sc->sc_port.power = USB_MAX_POWER; r = usbd_new_device(&sc->sc_dev, sc->sc_bus, 0, 0, 0, &sc->sc_port); if (r == USBD_NORMAL_COMPLETION) { dev = sc->sc_port.device; if (!dev->hub) { sc->sc_running = 0; printf("%s: root device is not a hub\n", USBDEVNAME(sc->sc_dev)); USB_ATTACH_ERROR_RETURN; } sc->sc_bus->root_hub = dev; dev->hub->explore(sc->sc_bus->root_hub); } else { printf("%s: root hub problem, error=%d\n", USBDEVNAME(sc->sc_dev), r); sc->sc_running = 0; } sc->sc_bus->use_polling = 0; USB_ATTACH_SUCCESS_RETURN; } #if defined(__NetBSD__) int usbctlprint(aux, pnp) void *aux; const char *pnp; { /* only "usb"es can attach to host controllers */ if (pnp) printf("usb at %s", pnp); return (UNCONF); } #endif int usbopen(dev, flag, mode, p) dev_t dev; int flag, mode; struct proc *p; { USB_GET_SC_OPEN(usb, USBUNIT(dev), sc); if (sc == 0 || !sc->sc_running) return (ENXIO); return (0); } int usbclose(dev, flag, mode, p) dev_t dev; int flag, mode; struct proc *p; { return (0); } int usbioctl(dev, cmd, data, flag, p) dev_t dev; u_long cmd; caddr_t data; int flag; struct proc *p; { USB_GET_SC(usb, USBUNIT(dev), sc); if (sc == 0 || !sc->sc_running) return (ENXIO); switch (cmd) { #ifdef USB_DEBUG case USB_SETDEBUG: usbdebug = uhcidebug = ohcidebug = *(int *)data; break; #endif case USB_DISCOVER: usb_discover(sc); break; case USB_REQUEST: { struct usb_ctl_request *ur = (void *)data; int len = UGETW(ur->request.wLength); struct iovec iov; struct uio uio; void *ptr = 0; int addr = ur->addr; usbd_status r; int error = 0; DPRINTF(("usbioctl: USB_REQUEST addr=%d len=%d\n", addr, len)); if (len < 0 || len > 32768) return (EINVAL); if (addr < 0 || addr >= USB_MAX_DEVICES || sc->sc_bus->devices[addr] == 0) return (EINVAL); if (len != 0) { iov.iov_base = (caddr_t)ur->data; iov.iov_len = len; uio.uio_iov = &iov; uio.uio_iovcnt = 1; uio.uio_resid = len; uio.uio_offset = 0; uio.uio_segflg = UIO_USERSPACE; uio.uio_rw = ur->request.bmRequestType & UT_READ ? UIO_READ : UIO_WRITE; uio.uio_procp = p; ptr = malloc(len, M_TEMP, M_WAITOK); if (uio.uio_rw == UIO_WRITE) { error = uiomove(ptr, len, &uio); if (error) goto ret; } } r = usbd_do_request_flags(sc->sc_bus->devices[addr], &ur->request, ptr, ur->flags, &ur->actlen); if (r) { error = EIO; goto ret; } if (len != 0) { if (uio.uio_rw == UIO_READ) { error = uiomove(ptr, len, &uio); if (error) goto ret; } } ret: if (ptr) free(ptr, M_TEMP); return (error); } case USB_DEVICEINFO: { struct usb_device_info *di = (void *)data; int addr = di->addr; usbd_device_handle dev; if (addr < 1 || addr >= USB_MAX_DEVICES) return (EINVAL); dev = sc->sc_bus->devices[addr]; if (dev == 0) return (ENXIO); usbd_fill_deviceinfo(dev, di); break; } case USB_DEVICESTATS: *(struct usb_device_stats *)data = sc->sc_bus->stats; break; default: return (ENXIO); } return (0); } int usbpoll(dev, events, p) dev_t dev; int events; struct proc *p; { int revents, s; USB_GET_SC(usb, USBUNIT(dev), sc); DPRINTFN(2, ("usbpoll: sc=%p events=0x%x\n", sc, events)); s = splusb(); revents = 0; if (events & (POLLOUT | POLLWRNORM)) if (sc->sc_bus->needs_explore) revents |= events & (POLLOUT | POLLWRNORM); DPRINTFN(2, ("usbpoll: revents=0x%x\n", revents)); if (revents == 0) { if (events & (POLLOUT | POLLWRNORM)) { DPRINTFN(2, ("usbpoll: selrecord\n")); selrecord(p, &sc->sc_consel); } } splx(s); return (revents); } #if 0 int usb_bus_count() { int i, n; for (i = n = 0; i < usb_cd.cd_ndevs; i++) if (usb_cd.cd_devs[i]) n++; return (n); } #endif #if defined(__NetBSD__) usbd_status usb_get_bus_handle(n, h) int n; usbd_bus_handle *h; { int i; for (i = 0; i < usb_cd.cd_ndevs; i++) if (usb_cd.cd_devs[i] && n-- == 0) { *h = usb_cd.cd_devs[i]; return (USBD_NORMAL_COMPLETION); } return (USBD_INVAL); } #endif usbd_status usb_discover(sc) struct usb_softc *sc; { int s; /* Explore device tree from the root */ /* We need mutual exclusion while traversing the device tree. */ s = splusb(); while (sc->sc_exploring) tsleep(&sc->sc_exploring, PRIBIO, "usbdis", 0); sc->sc_exploring = 1; sc->sc_bus->needs_explore = 0; splx(s); sc->sc_bus->root_hub->hub->explore(sc->sc_bus->root_hub); s = splusb(); sc->sc_exploring = 0; wakeup(&sc->sc_exploring); splx(s); /* XXX should we start over if sc_needsexplore is set again? */ return (0); } void usb_needs_explore(bus) usbd_bus_handle bus; { bus->needs_explore = 1; selwakeup(&bus->usbctl->sc_consel); } #if defined(__FreeBSD__) int usb_detach(device_t self) { DPRINTF(("%s: disconnected\n", USBDEVNAME(self))); return (1); } -DRIVER_MODULE(usb, root, usb_driver, usb_devclass, 0, 0); +DRIVER_MODULE(usb, uhci, usb_driver, usb_devclass, 0, 0); +DRIVER_MODULE(usb, ohci, usb_driver, usb_devclass, 0, 0); #endif Index: head/sys/dev/usb/usb_if.m =================================================================== --- head/sys/dev/usb/usb_if.m (revision 45719) +++ head/sys/dev/usb/usb_if.m (revision 45720) @@ -1,40 +1,47 @@ # # Copyright (c) 1992-1998 Nick Hibma # 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, # without modification, immediately at the beginning of 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. 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. # -# $Id: usb_if.m,v 1.3 1999/01/07 23:31:37 n_hibma Exp $ +# $Id: usb_if.m,v 1.4 1999/03/22 19:58:59 n_hibma Exp $ # # USB interface description # INTERFACE usb; # The device should start probing for new drivers again # METHOD int reconfigure { + device_t dev; +}; + +# The device is being disconnected and should clean up before +# being destroyed. +# +METHOD void disconnected { device_t dev; }; Index: head/sys/dev/usb/usb_port.h =================================================================== --- head/sys/dev/usb/usb_port.h (revision 45719) +++ head/sys/dev/usb/usb_port.h (revision 45720) @@ -1,222 +1,222 @@ /* $NetBSD: usb_port.h,v 1.5 1999/01/08 11:58:25 augustss Exp $ */ /* $FreeBSD$ */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (augustss@carlstedt.se) at * Carlstedt Research & Technology. * * 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 NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */ /* * Macro's to cope with the differences between operating systems. */ /* * NetBSD */ #if defined(__NetBSD__) #include "opt_usbverbose.h" #define USBDEVNAME(bdev) ((bdev).dv_xname) typedef struct device bdevice; /* base device */ #define usb_timeout(f, d, t, h) timeout((f), (d), (t)) #define usb_untimeout(f, d, h) untimeout((f), (d)) #define USB_DECLARE_DRIVER_INIT(dname, _2) \ int __CONCAT(dname,_match) __P((struct device *, struct cfdata *, void *)); \ void __CONCAT(dname,_attach) __P((struct device *, struct device *, void *)); \ \ extern struct cfdriver __CONCAT(dname,_cd); \ \ struct cfattach __CONCAT(dname,_ca) = { \ sizeof(struct __CONCAT(dname,_softc)), \ __CONCAT(dname,_match), \ __CONCAT(dname,_attach) \ } #define USB_MATCH(dname) \ int \ __CONCAT(dname,_match)(parent, match, aux) \ struct device *parent; \ struct cfdata *match; \ void *aux; #define USB_MATCH_START(dname, uaa) \ struct usb_attach_arg *uaa = aux #define USB_ATTACH(dname) \ void \ __CONCAT(dname,_attach)(parent, self, aux) \ struct device *parent; \ struct device *self; \ void *aux; #define USB_ATTACH_START(dname, sc, uaa) \ struct __CONCAT(dname,_softc) *sc = \ (struct __CONCAT(dname,_softc) *)self; \ struct usb_attach_arg *uaa = aux /* Returns from attach */ #define USB_ATTACH_ERROR_RETURN return #define USB_ATTACH_SUCCESS_RETURN return #define USB_ATTACH_SETUP printf("\n") #define USB_GET_SC_OPEN(dname, unit, sc) \ struct __CONCAT(dname,_softc) *sc; \ if (unit >= __CONCAT(dname,_cd).cd_ndevs) \ return (ENXIO); \ sc = __CONCAT(dname,_cd).cd_devs[unit]; \ if (!sc) \ return (ENXIO) #define USB_GET_SC(dname, unit, sc) \ struct __CONCAT(dname,_softc) *sc = __CONCAT(dname,_cd).cd_devs[unit] #define USB_DO_ATTACH(dev, bdev, parent, args, print, sub) \ ((dev)->softc = config_found_sm(parent, args, print, sub)) #define logprintf printf #elif defined(__FreeBSD__) /* * FreeBSD */ #include "opt_usb.h" /* * The following is not a type def to avoid error messages * because of includes in the wrong order. */ #define bdevice device_t -#define USBDEVNAME(bdev) usbd_devname(&bdev) +#define USBDEVNAME(bdev) device_get_nameunit(bdev) /* XXX Change this when FreeBSD has memset */ #define memset(d, v, s) \ do{ \ if ((v) == 0) \ bzero((d), (s)); \ else \ panic("Non zero filler for memset, cannot handle!"); \ } while (0) #define usb_timeout(f, d, t, h) ((h) = timeout((f), (d), (t))) #define usb_untimeout(f, d, h) untimeout((f), (d), (h)) #define USB_DECLARE_DRIVER_INIT(dname, init...) \ static device_probe_t __CONCAT(dname,_match); \ static device_attach_t __CONCAT(dname,_attach); \ static device_detach_t __CONCAT(dname,_detach); \ \ static devclass_t __CONCAT(dname,_devclass); \ \ static device_method_t __CONCAT(dname,_methods)[] = { \ DEVMETHOD(device_probe, __CONCAT(dname,_match)), \ DEVMETHOD(device_attach, __CONCAT(dname,_attach)), \ DEVMETHOD(device_detach, __CONCAT(dname,_detach)), \ init, \ {0,0} \ }; \ \ static driver_t __CONCAT(dname,_driver) = { \ #dname, \ __CONCAT(dname,_methods), \ DRIVER_TYPE_MISC, \ sizeof(struct __CONCAT(dname,_softc)) \ } #define USB_MATCH(dname) \ static int \ __CONCAT(dname,_match)(device_t device) #define USB_MATCH_START(dname, uaa) \ struct usb_attach_arg *uaa = device_get_ivars(device) #define USB_ATTACH(dname) \ static int \ __CONCAT(dname,_attach)(device_t self) #define USB_ATTACH_START(dname, sc, uaa) \ struct __CONCAT(dname,_softc) *sc = device_get_softc(self); \ struct usb_attach_arg *uaa = device_get_ivars(self) /* Returns from attach */ #define USB_ATTACH_ERROR_RETURN return ENXIO #define USB_ATTACH_SUCCESS_RETURN return 0 #define USB_ATTACH_SETUP \ sc->sc_dev = self; \ usbd_device_set_desc(self, devinfo) #define USB_GET_SC_OPEN(dname, unit, sc) \ struct __CONCAT(dname,_softc) *sc = \ devclass_get_softc(__CONCAT(dname,_devclass), unit); \ if (!sc) \ return (ENXIO) #define USB_GET_SC(dname, unit, sc) \ struct __CONCAT(dname,_softc) *sc = \ devclass_get_softc(__CONCAT(dname,_devclass), unit) #define USB_DO_ATTACH(dev, bdev, parent, args, print, sub) \ (device_probe_and_attach((bdev)) == 0 ? \ ((dev)->softc = device_get_softc(bdev)) : 0) /* conversion from one type of queue to the other */ #define SIMPLEQ_REMOVE_HEAD STAILQ_REMOVE_HEAD_UNTIL #define SIMPLEQ_INSERT_HEAD STAILQ_INSERT_HEAD #define SIMPLEQ_INSERT_TAIL STAILQ_INSERT_TAIL #define SIMPLEQ_NEXT STAILQ_NEXT #define SIMPLEQ_FIRST STAILQ_FIRST #define SIMPLEQ_HEAD STAILQ_HEAD #define SIMPLEQ_INIT STAILQ_INIT #define SIMPLEQ_ENTRY STAILQ_ENTRY #include #define logprintf(args...) log(LOG_DEBUG, args); #endif /* __FreeBSD__ */ #define USB_DECLARE_DRIVER(dname) \ USB_DECLARE_DRIVER_INIT(dname, {0,0} ) Index: head/sys/dev/usb/usbdi.c =================================================================== --- head/sys/dev/usb/usbdi.c (revision 45719) +++ head/sys/dev/usb/usbdi.c (revision 45720) @@ -1,1349 +1,1326 @@ /* $NetBSD: usbdi.c,v 1.20 1999/01/08 11:58:26 augustss Exp $ */ /* $FreeBSD$ */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (augustss@carlstedt.se) at * Carlstedt Research & Technology. * * 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 NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */ #include #include #include #if defined(__NetBSD__) #include #else #include #include #include #endif #include #include #include #include #include #include #if defined(__FreeBSD__) #include "usb_if.h" #endif #ifdef USB_DEBUG #define DPRINTF(x) if (usbdebug) logprintf x #define DPRINTFN(n,x) if (usbdebug>(n)) logprintf x extern int usbdebug; #else #define DPRINTF(x) #define DPRINTFN(n,x) #endif static usbd_status usbd_ar_pipe __P((usbd_pipe_handle pipe)); static usbd_status usbd_ar_iface __P((usbd_interface_handle iface)); static void usbd_transfer_cb __P((usbd_request_handle reqh)); static void usbd_sync_transfer_cb __P((usbd_request_handle reqh)); static usbd_status usbd_do_transfer __P((usbd_request_handle reqh)); void usbd_do_request_async_cb __P((usbd_request_handle, usbd_private_handle, usbd_status)); static SIMPLEQ_HEAD(, usbd_request) usbd_free_requests; #if defined(__FreeBSD__) #define USB_CDEV_MAJOR 108 extern struct cdevsw usb_cdevsw; #endif #ifdef USB_DEBUG char *usbd_error_strs[USBD_ERROR_MAX] = { "NORMAL_COMPLETION", "IN_PROGRESS", "PENDING_REQUESTS", "NOT_STARTED", "INVAL", "IS_IDLE", "NOMEM", "CANCELLED", "BAD_ADDRESS", "IN_USE", "INTERFACE_NOT_ACTIVE", "NO_ADDR", "SET_ADDR_FAILED", "NO_POWER", "TOO_DEEP", "IOERROR", "NOT_CONFIGURED", "TIMEOUT", "SHORT_XFER", "STALLED", "XXX", }; #endif usbd_status usbd_open_pipe(iface, address, flags, pipe) usbd_interface_handle iface; u_int8_t address; u_int8_t flags; usbd_pipe_handle *pipe; { usbd_pipe_handle p; struct usbd_endpoint *ep; usbd_status r; int i; if (iface->state != USBD_INTERFACE_ACTIVE) return (USBD_INTERFACE_NOT_ACTIVE); for (i = 0; i < iface->idesc->bNumEndpoints; i++) { ep = &iface->endpoints[i]; if (ep->edesc->bEndpointAddress == address) goto found; } return (USBD_BAD_ADDRESS); found: if ((flags & USBD_EXCLUSIVE_USE) && ep->refcnt != 0) return (USBD_IN_USE); r = usbd_setup_pipe(iface->device, iface, ep, &p); if (r != USBD_NORMAL_COMPLETION) return (r); LIST_INSERT_HEAD(&iface->pipes, p, next); *pipe = p; return (USBD_NORMAL_COMPLETION); } usbd_status usbd_open_pipe_intr(iface, address, flags, pipe, priv, buffer, length, cb) usbd_interface_handle iface; u_int8_t address; u_int8_t flags; usbd_pipe_handle *pipe; usbd_private_handle priv; void *buffer; u_int32_t length; usbd_callback cb; { usbd_status r; usbd_request_handle reqh; usbd_pipe_handle ipipe; reqh = usbd_alloc_request(); if (reqh == 0) return (USBD_NOMEM); r = usbd_open_pipe(iface, address, USBD_EXCLUSIVE_USE, &ipipe); if (r != USBD_NORMAL_COMPLETION) goto bad1; r = usbd_setup_request(reqh, ipipe, priv, buffer, length, USBD_XFER_IN | flags, USBD_NO_TIMEOUT, cb); if (r != USBD_NORMAL_COMPLETION) goto bad2; ipipe->intrreqh = reqh; r = usbd_transfer(reqh); *pipe = ipipe; if (r != USBD_IN_PROGRESS) goto bad3; return (USBD_NORMAL_COMPLETION); bad3: ipipe->intrreqh = 0; bad2: usbd_close_pipe(ipipe); bad1: usbd_free_request(reqh); return r; } usbd_status usbd_open_pipe_iso(iface, address, flags, pipe, priv, bufsize, nbuf, cb) usbd_interface_handle iface; u_int8_t address; u_int8_t flags; usbd_pipe_handle *pipe; usbd_private_handle priv; u_int32_t bufsize; u_int32_t nbuf; usbd_callback cb; { usbd_status r; usbd_pipe_handle p; r = usbd_open_pipe(iface, address, USBD_EXCLUSIVE_USE, &p); if (r != USBD_NORMAL_COMPLETION) return (r); if (!p->methods->isobuf) { usbd_close_pipe(p); return (USBD_INVAL); } r = p->methods->isobuf(p, bufsize, nbuf); if (r != USBD_NORMAL_COMPLETION) { usbd_close_pipe(p); return (r); } *pipe = p; return r; } usbd_status usbd_close_pipe(pipe) usbd_pipe_handle pipe; { if (pipe->iface->state != USBD_INTERFACE_ACTIVE) return (USBD_INTERFACE_NOT_ACTIVE); if (--pipe->refcnt != 0) return (USBD_NORMAL_COMPLETION); if (SIMPLEQ_FIRST(&pipe->queue) != 0) return (USBD_PENDING_REQUESTS); LIST_REMOVE(pipe, next); pipe->endpoint->refcnt--; pipe->methods->close(pipe); if (pipe->intrreqh) usbd_free_request(pipe->intrreqh); free(pipe, M_USB); return (USBD_NORMAL_COMPLETION); } usbd_status usbd_transfer(reqh) usbd_request_handle reqh; { reqh->xfercb = usbd_transfer_cb; return (usbd_do_transfer(reqh)); } static usbd_status usbd_do_transfer(reqh) usbd_request_handle reqh; { usbd_pipe_handle pipe = reqh->pipe; DPRINTFN(10,("usbd_do_transfer: reqh=%p\n", reqh)); reqh->done = 0; return (pipe->methods->transfer(reqh)); } usbd_request_handle usbd_alloc_request() { usbd_request_handle reqh; reqh = SIMPLEQ_FIRST(&usbd_free_requests); if (reqh) SIMPLEQ_REMOVE_HEAD(&usbd_free_requests, reqh, next); else reqh = malloc(sizeof(*reqh), M_USB, M_NOWAIT); if (!reqh) return (0); memset(reqh, 0, sizeof *reqh); return (reqh); } usbd_status usbd_free_request(reqh) usbd_request_handle reqh; { SIMPLEQ_INSERT_HEAD(&usbd_free_requests, reqh, next); return (USBD_NORMAL_COMPLETION); } usbd_status usbd_setup_request(reqh, pipe, priv, buffer, length, flags, timeout, callback) usbd_request_handle reqh; usbd_pipe_handle pipe; usbd_private_handle priv; void *buffer; u_int32_t length; u_int16_t flags; u_int32_t timeout; void (*callback) __P((usbd_request_handle, usbd_private_handle, usbd_status)); { reqh->pipe = pipe; reqh->priv = priv; reqh->buffer = buffer; reqh->length = length; reqh->actlen = 0; reqh->flags = flags; reqh->timeout = timeout; reqh->status = USBD_NOT_STARTED; reqh->callback = callback; reqh->retries = 1; reqh->isreq = 0; return (USBD_NORMAL_COMPLETION); } usbd_status usbd_setup_device_request(reqh, req) usbd_request_handle reqh; usb_device_request_t *req; { reqh->isreq = 1; reqh->request = *req; return (USBD_NORMAL_COMPLETION); } usbd_status usbd_setup_default_request(reqh, dev, priv, timeout, req, buffer, length, flags, callback) usbd_request_handle reqh; usbd_device_handle dev; usbd_private_handle priv; u_int32_t timeout; usb_device_request_t *req; void *buffer; u_int32_t length; u_int16_t flags; void (*callback) __P((usbd_request_handle, usbd_private_handle, usbd_status)); { reqh->pipe = dev->default_pipe; reqh->priv = priv; reqh->buffer = buffer; reqh->length = length; reqh->actlen = 0; reqh->flags = flags; reqh->timeout = timeout; reqh->status = USBD_NOT_STARTED; reqh->callback = callback; reqh->request = *req; reqh->retries = 1; reqh->isreq = 1; return (USBD_NORMAL_COMPLETION); } usbd_status usbd_set_request_timeout(reqh, timeout) usbd_request_handle reqh; u_int32_t timeout; { reqh->timeout = timeout; return (USBD_NORMAL_COMPLETION); } usbd_status usbd_get_request_status(reqh, priv, buffer, count, status) usbd_request_handle reqh; usbd_private_handle *priv; void **buffer; u_int32_t *count; usbd_status *status; { *priv = reqh->priv; *buffer = reqh->buffer; *count = reqh->actlen; *status = reqh->status; return (USBD_NORMAL_COMPLETION); } usbd_status usbd_request_device_data(reqh, req) usbd_request_handle reqh; usb_device_request_t *req; { if (!reqh->isreq) return (USBD_INVAL); *req = reqh->request; return (USBD_NORMAL_COMPLETION); } #if 0 usb_descriptor_t * usbd_get_descriptor(iface, desc_type) usbd_interface_handle *iface; u_int8_t desc_type; XX #endif usb_config_descriptor_t * usbd_get_config_descriptor(dev) usbd_device_handle dev; { return (dev->cdesc); } usb_interface_descriptor_t * usbd_get_interface_descriptor(iface) usbd_interface_handle iface; { return (iface->idesc); } usb_device_descriptor_t * usbd_get_device_descriptor(dev) usbd_device_handle dev; { return (&dev->ddesc); } usb_endpoint_descriptor_t * usbd_interface2endpoint_descriptor(iface, index) usbd_interface_handle iface; u_int8_t index; { if (index >= iface->idesc->bNumEndpoints) return (0); return (iface->endpoints[index].edesc); } usbd_status usbd_set_configuration(dev, conf) usbd_device_handle dev; u_int8_t conf; { return usbd_set_config_no(dev, conf, 0); } usbd_status usbd_retry_request(reqh, retry_count) usbd_request_handle reqh; u_int32_t retry_count; { usbd_status r; r = usbd_set_pipe_state(reqh->pipe, USBD_PIPE_ACTIVE); if (r != USBD_NORMAL_COMPLETION) return (r); reqh->retries = retry_count; return (usbd_transfer(reqh)); } usbd_status usbd_abort_pipe(pipe) usbd_pipe_handle pipe; { usbd_status r; int s, state; if (pipe->iface->state != USBD_INTERFACE_ACTIVE) return (USBD_INTERFACE_NOT_ACTIVE); s = splusb(); state = pipe->state; r = usbd_ar_pipe(pipe); pipe->state = state; splx(s); return (r); } usbd_status usbd_abort_interface(iface) usbd_interface_handle iface; { usbd_status r; int s, st; s = splusb(); st = iface->state; r = usbd_ar_iface(iface); iface->state = st; splx(s); return (r); } usbd_status usbd_reset_pipe(pipe) usbd_pipe_handle pipe; { usbd_status r; int s; if (pipe->iface->state != USBD_INTERFACE_ACTIVE) return (USBD_INTERFACE_NOT_ACTIVE); s = splusb(); r = usbd_ar_pipe(pipe); /* XXX anything else */ pipe->state = USBD_PIPE_ACTIVE; splx(s); return (r); } usbd_status usbd_reset_interface(iface) usbd_interface_handle iface; { usbd_status r; int s; s = splusb(); r = usbd_ar_iface(iface); /* XXX anything else */ iface->state = USBD_INTERFACE_ACTIVE; splx(s); return (r); } usbd_status usbd_clear_endpoint_stall(pipe) usbd_pipe_handle pipe; { usbd_device_handle dev = pipe->device; usb_device_request_t req; usbd_status r; req.bmRequestType = UT_WRITE_ENDPOINT; req.bRequest = UR_CLEAR_FEATURE; USETW(req.wValue, UF_ENDPOINT_HALT); USETW(req.wIndex, pipe->endpoint->edesc->bEndpointAddress); USETW(req.wLength, 0); r = usbd_do_request(dev, &req, 0); #if 0 XXX should we do this? if (r == USBD_NORMAL_COMPLETION) { pipe->state = USBD_PIPE_ACTIVE; /* XXX activate pipe */ } #endif return (r); } usbd_status usbd_clear_endpoint_stall_async(pipe) usbd_pipe_handle pipe; { usbd_device_handle dev = pipe->device; usb_device_request_t req; usbd_status r; req.bmRequestType = UT_WRITE_ENDPOINT; req.bRequest = UR_CLEAR_FEATURE; USETW(req.wValue, UF_ENDPOINT_HALT); USETW(req.wIndex, pipe->endpoint->edesc->bEndpointAddress); USETW(req.wLength, 0); r = usbd_do_request_async(dev, &req, 0); return (r); } usbd_status usbd_set_pipe_state(pipe, state) usbd_pipe_handle pipe; usbd_pipe_state state; { int s; usbd_status r; usbd_request_handle reqh; if (pipe->iface->state != USBD_INTERFACE_ACTIVE) return (USBD_INTERFACE_NOT_ACTIVE); if (state != USBD_PIPE_ACTIVE && state != USBD_PIPE_STALLED && state != USBD_PIPE_IDLE) return (USBD_INVAL); pipe->state = state; r = USBD_NORMAL_COMPLETION; if (state == USBD_PIPE_ACTIVE) { s = splusb(); if (!pipe->running) { reqh = SIMPLEQ_FIRST(&pipe->queue); if (reqh != 0) { pipe->running = 1; splx(s); r = pipe->methods->start(reqh); } else splx(s); } else splx(s); } return (r); } usbd_status usbd_get_pipe_state(pipe, state, endpoint_state, request_count) usbd_pipe_handle pipe; usbd_pipe_state *state; u_int32_t *endpoint_state; u_int32_t *request_count; { int n; usbd_request_handle r; *state = pipe->state; *endpoint_state = pipe->endpoint->state; for (r = SIMPLEQ_FIRST(&pipe->queue), n = 0; r != 0; r = SIMPLEQ_NEXT(r, next), n++) ; *request_count = n; return (USBD_NORMAL_COMPLETION); } usbd_status usbd_set_interface_state(iface, state) usbd_interface_handle iface; usbd_interface_state state; { int ps; usbd_pipe_handle p; if (state == USBD_INTERFACE_ACTIVE) ps = USBD_PIPE_ACTIVE; else if (state == USBD_INTERFACE_STALLED) ps = USBD_PIPE_STALLED; else if (state == USBD_INTERFACE_IDLE) ps = USBD_PIPE_IDLE; else return (USBD_INVAL); iface->state = USBD_INTERFACE_ACTIVE; /* to allow setting the pipe */ for (p = LIST_FIRST(&iface->pipes); p != 0; p = LIST_NEXT(p, next)) usbd_set_pipe_state(p, ps); iface->state = state; return (USBD_NORMAL_COMPLETION); } usbd_status usbd_get_interface_state(iface, state) usbd_interface_handle iface; usbd_interface_state *state; { *state = iface->state; return (USBD_NORMAL_COMPLETION); } usbd_status usbd_get_device_state(dev, state) usbd_device_handle dev; usbd_device_state *state; { *state = dev->state; return (USBD_NORMAL_COMPLETION); } #if 0 usbd_status usbd_set_device_state(dev, state) usbd_device_handle dev; usbd_device_state state; X #endif usbd_status usbd_device_address(dev, address) usbd_device_handle dev; u_int8_t *address; { *address = dev->address; return (USBD_NORMAL_COMPLETION); } usbd_status usbd_endpoint_address(pipe, address) usbd_pipe_handle pipe; u_int8_t *address; { *address = pipe->endpoint->edesc->bEndpointAddress; return (USBD_NORMAL_COMPLETION); } usbd_status usbd_endpoint_count(iface, count) usbd_interface_handle iface; u_int8_t *count; { *count = iface->idesc->bNumEndpoints; return (USBD_NORMAL_COMPLETION); } usbd_status usbd_interface_count(dev, count) usbd_device_handle dev; u_int8_t *count; { if (!dev->cdesc) return (USBD_NOT_CONFIGURED); *count = dev->cdesc->bNumInterface; return (USBD_NORMAL_COMPLETION); } #if 0 u_int8_t usbd_bus_count() { return (usb_bus_count()); } usbd_status usbd_get_bus_handle(index, bus) u_int8_t index; usbd_bus_handle *bus; { return (usb_get_bus_handle(index, bus)); } usbd_status usbd_get_root_hub(bus, dev) usbd_bus_handle bus; usbd_device_handle *dev; { *dev = bus->root_hub; return (USBD_NORMAL_COMPLETION); } usbd_status usbd_port_count(dev, nports) usbd_device_handle dev; u_int8_t *nports; { if (dev->hub == 0) return (USBD_INVAL); *nports = dev->hub->hubdesc.bNbrPorts; return (USBD_NORMAL_COMPLETION); } usbd_status usbd_hub2device_handle(dev, port, devp) usbd_device_handle dev; u_int8_t port; usbd_device_handle *devp; { if (dev->hub == 0 || port >= dev->hub->hubdesc.bNbrPorts || dev->hub->ports[port].device == 0) return (USBD_INVAL); *devp = dev->hub->ports[port].device; return (USBD_NORMAL_COMPLETION); } #endif usbd_status usbd_request2pipe_handle(reqh, pipe) usbd_request_handle reqh; usbd_pipe_handle *pipe; { *pipe = reqh->pipe; return (USBD_NORMAL_COMPLETION); } usbd_status usbd_pipe2interface_handle(pipe, iface) usbd_pipe_handle pipe; usbd_interface_handle *iface; { *iface = pipe->iface; return (USBD_NORMAL_COMPLETION); } usbd_status usbd_interface2device_handle(iface, dev) usbd_interface_handle iface; usbd_device_handle *dev; { *dev = iface->device; return (USBD_NORMAL_COMPLETION); } usbd_status usbd_device2bus_handle(dev, bus) usbd_device_handle dev; usbd_bus_handle *bus; { *bus = dev->bus; return (USBD_NORMAL_COMPLETION); } usbd_status usbd_device2interface_handle(dev, ifaceno, iface) usbd_device_handle dev; u_int8_t ifaceno; usbd_interface_handle *iface; { if (!dev->cdesc) return (USBD_NOT_CONFIGURED); if (ifaceno >= dev->cdesc->bNumInterface) return (USBD_INVAL); *iface = &dev->ifaces[ifaceno]; return (USBD_NORMAL_COMPLETION); } usbd_status usbd_set_interface_private_handle(iface, priv) usbd_interface_handle iface; usbd_private_handle priv; { iface->priv = priv; return (USBD_NORMAL_COMPLETION); } usbd_status usbd_get_interface_private_handle(iface, priv) usbd_interface_handle iface; usbd_private_handle *priv; { *priv = iface->priv; return (USBD_NORMAL_COMPLETION); } usbd_status usbd_reference_pipe(pipe) usbd_pipe_handle pipe; { pipe->refcnt++; return (USBD_NORMAL_COMPLETION); } usbd_status usbd_dereference_pipe(pipe) usbd_pipe_handle pipe; { pipe->refcnt--; return (USBD_NORMAL_COMPLETION); } usbd_lock_token usbd_lock() { return (splusb()); } void usbd_unlock(tok) usbd_lock_token tok; { splx(tok); } /* XXXX use altno */ usbd_status usbd_set_interface(iface, altidx) usbd_interface_handle iface; int altidx; { usb_device_request_t req; usbd_status r; if (LIST_FIRST(&iface->pipes) != 0) return (USBD_IN_USE); if (iface->endpoints) free(iface->endpoints, M_USB); iface->endpoints = 0; iface->idesc = 0; iface->state = USBD_INTERFACE_IDLE; r = usbd_fill_iface_data(iface->device, iface->index, altidx); if (r != USBD_NORMAL_COMPLETION) return (r); req.bmRequestType = UT_WRITE_INTERFACE; req.bRequest = UR_SET_INTERFACE; USETW(req.wValue, iface->idesc->bAlternateSetting); USETW(req.wIndex, iface->idesc->bInterfaceNumber); USETW(req.wLength, 0); return usbd_do_request(iface->device, &req, 0); } int usbd_get_no_alts(cdesc, ifaceno) usb_config_descriptor_t *cdesc; int ifaceno; { char *p = (char *)cdesc; char *end = p + UGETW(cdesc->wTotalLength); usb_interface_descriptor_t *d; int n; for (n = 0; p < end; p += d->bLength) { d = (usb_interface_descriptor_t *)p; if (p + d->bLength <= end && d->bDescriptorType == UDESC_INTERFACE && d->bInterfaceNumber == ifaceno) n++; } return (n); } int usbd_get_interface_altindex(iface) usbd_interface_handle iface; { return (iface->altindex); } usbd_status usbd_get_interface(iface, aiface) usbd_interface_handle iface; u_int8_t *aiface; { usb_device_request_t req; req.bmRequestType = UT_READ_INTERFACE; req.bRequest = UR_GET_INTERFACE; USETW(req.wValue, 0); USETW(req.wIndex, iface->idesc->bInterfaceNumber); USETW(req.wLength, 1); return usbd_do_request(iface->device, &req, aiface); } /*** Internal routines ***/ /* Dequeue all pipe operations, called at splusb(). */ static usbd_status usbd_ar_pipe(pipe) usbd_pipe_handle pipe; { usbd_request_handle reqh; #if 0 for (;;) { reqh = SIMPLEQ_FIRST(&pipe->queue); if (reqh == 0) break; SIMPLEQ_REMOVE_HEAD(&pipe->queue, reqh, next); reqh->status = USBD_CANCELLED; if (reqh->callback) reqh->callback(reqh, reqh->priv, reqh->status); } #else while ((reqh = SIMPLEQ_FIRST(&pipe->queue))) { pipe->methods->abort(reqh); SIMPLEQ_REMOVE_HEAD(&pipe->queue, reqh, next); } #endif return (USBD_NORMAL_COMPLETION); } /* Dequeue all interface operations, called at splusb(). */ static usbd_status usbd_ar_iface(iface) usbd_interface_handle iface; { usbd_pipe_handle p; usbd_status r, ret = USBD_NORMAL_COMPLETION; for (p = LIST_FIRST(&iface->pipes); p != 0; p = LIST_NEXT(p, next)) { r = usbd_ar_pipe(p); if (r != USBD_NORMAL_COMPLETION) ret = r; } return (ret); } static int usbd_global_init_done = 0; void usbd_init() { #if defined(__FreeBSD__) dev_t dev; #endif if (!usbd_global_init_done) { usbd_global_init_done = 1; SIMPLEQ_INIT(&usbd_free_requests); #if defined(__FreeBSD__) dev = makedev(USB_CDEV_MAJOR, 0); cdevsw_add(&dev, &usb_cdevsw, NULL); #endif } } static void usbd_transfer_cb(reqh) usbd_request_handle reqh; { usbd_pipe_handle pipe = reqh->pipe; /* Count completed transfers. */ ++pipe->device->bus->stats.requests [pipe->endpoint->edesc->bmAttributes & UE_XFERTYPE]; /* XXX check retry count */ reqh->done = 1; if (reqh->status == USBD_NORMAL_COMPLETION && reqh->actlen < reqh->length && !(reqh->flags & USBD_SHORT_XFER_OK)) { DPRINTFN(-1, ("usbd_transfer_cb: short xfer %d<%d (bytes)\n", reqh->actlen, reqh->length)); reqh->status = USBD_SHORT_XFER; } if (reqh->callback) reqh->callback(reqh, reqh->priv, reqh->status); } static void usbd_sync_transfer_cb(reqh) usbd_request_handle reqh; { usbd_transfer_cb(reqh); if (!reqh->pipe->device->bus->use_polling) wakeup(reqh); } /* Like usbd_transfer(), but waits for completion. */ usbd_status usbd_sync_transfer(reqh) usbd_request_handle reqh; { usbd_status r; int s; reqh->xfercb = usbd_sync_transfer_cb; r = usbd_do_transfer(reqh); if (r != USBD_IN_PROGRESS) return (r); s = splusb(); if (!reqh->done) { if (reqh->pipe->device->bus->use_polling) panic("usbd_sync_transfer: not done\n"); tsleep(reqh, PRIBIO, "usbsyn", 0); } splx(s); return (reqh->status); } usbd_status usbd_do_request(dev, req, data) usbd_device_handle dev; usb_device_request_t *req; void *data; { return (usbd_do_request_flags(dev, req, data, 0, 0)); } usbd_status usbd_do_request_flags(dev, req, data, flags, actlen) usbd_device_handle dev; usb_device_request_t *req; void *data; u_int16_t flags; int *actlen; { usbd_request_handle reqh; usbd_status r; #ifdef DIAGNOSTIC if (!curproc) { printf("usbd_do_request: not in process context\n"); return (USBD_XXX); } #endif reqh = usbd_alloc_request(); if (reqh == 0) return (USBD_NOMEM); r = usbd_setup_default_request( reqh, dev, 0, USBD_DEFAULT_TIMEOUT, req, data, UGETW(req->wLength), flags, 0); if (r != USBD_NORMAL_COMPLETION) goto bad; r = usbd_sync_transfer(reqh); #if defined(USB_DEBUG) || defined(DIAGNOSTIC) if (reqh->actlen > reqh->length) printf("usbd_do_request: overrun addr=%d type=0x%02x req=0x" "%02x val=%d index=%d rlen=%d length=%d actlen=%d\n", dev->address, reqh->request.bmRequestType, reqh->request.bRequest, UGETW(reqh->request.wValue), UGETW(reqh->request.wIndex), UGETW(reqh->request.wLength), reqh->length, reqh->actlen); #endif if (actlen) *actlen = reqh->actlen; if (r == USBD_STALLED) { /* * The control endpoint has stalled. Control endpoints * should not halt, but some may do so anyway so clear * any halt condition. */ usb_device_request_t treq; usb_status_t status; u_int16_t s; usbd_status nr; treq.bmRequestType = UT_READ_ENDPOINT; treq.bRequest = UR_GET_STATUS; USETW(treq.wValue, 0); USETW(treq.wIndex, 0); USETW(treq.wLength, sizeof(usb_status_t)); nr = usbd_setup_default_request( reqh, dev, 0, USBD_DEFAULT_TIMEOUT, &treq, &status, sizeof(usb_status_t), 0, 0); if (nr != USBD_NORMAL_COMPLETION) goto bad; nr = usbd_sync_transfer(reqh); if (nr != USBD_NORMAL_COMPLETION) goto bad; s = UGETW(status.wStatus); DPRINTF(("usbd_do_request: status = 0x%04x\n", s)); if (!(s & UES_HALT)) goto bad; treq.bmRequestType = UT_WRITE_ENDPOINT; treq.bRequest = UR_CLEAR_FEATURE; USETW(treq.wValue, UF_ENDPOINT_HALT); USETW(treq.wIndex, 0); USETW(treq.wLength, 0); nr = usbd_setup_default_request( reqh, dev, 0, USBD_DEFAULT_TIMEOUT, &treq, &status, 0, 0, 0); if (nr != USBD_NORMAL_COMPLETION) goto bad; nr = usbd_sync_transfer(reqh); if (nr != USBD_NORMAL_COMPLETION) goto bad; } bad: usbd_free_request(reqh); return (r); } void usbd_do_request_async_cb(reqh, priv, status) usbd_request_handle reqh; usbd_private_handle priv; usbd_status status; { #if defined(USB_DEBUG) || defined(DIAGNOSTIC) if (reqh->actlen > reqh->length) printf("usbd_do_request: overrun addr=%d type=0x%02x req=0x" "%02x val=%d index=%d rlen=%d length=%d actlen=%d\n", reqh->pipe->device->address, reqh->request.bmRequestType, reqh->request.bRequest, UGETW(reqh->request.wValue), UGETW(reqh->request.wIndex), UGETW(reqh->request.wLength), reqh->length, reqh->actlen); #endif usbd_free_request(reqh); } /* * Execute a request without waiting for completion. * Can be used from interrupt context. */ usbd_status usbd_do_request_async(dev, req, data) usbd_device_handle dev; usb_device_request_t *req; void *data; { usbd_request_handle reqh; usbd_status r; reqh = usbd_alloc_request(); if (reqh == 0) return (USBD_NOMEM); r = usbd_setup_default_request( reqh, dev, 0, USBD_DEFAULT_TIMEOUT, req, data, UGETW(req->wLength), 0, usbd_do_request_async_cb); if (r != USBD_NORMAL_COMPLETION) { usbd_free_request(reqh); return (r); } r = usbd_transfer(reqh); if (r != USBD_IN_PROGRESS) return (r); return (USBD_NORMAL_COMPLETION); } struct usbd_quirks * usbd_get_quirks(dev) usbd_device_handle dev; { return (dev->quirks); } void usbd_set_disco(p, hdl, data) usbd_pipe_handle p; void (*hdl) __P((void *)); void *data; { p->disco = hdl; p->discoarg = data; } /* XXX do periodic free() of free list */ /* * Called from keyboard driver when in polling mode. */ void usbd_dopoll(iface) usbd_interface_handle iface; { iface->device->bus->do_poll(iface->device->bus); } void usbd_set_polling(iface, on) usbd_interface_handle iface; int on; { iface->device->bus->use_polling = on; } usb_endpoint_descriptor_t * usbd_get_endpoint_descriptor(iface, address) usbd_interface_handle iface; u_int8_t address; { struct usbd_endpoint *ep; int i; for (i = 0; i < iface->idesc->bNumEndpoints; i++) { ep = &iface->endpoints[i]; if (ep->edesc->bEndpointAddress == address) return (iface->endpoints[i].edesc); } return (0); } #if defined(__FreeBSD__) void usbd_print_child(device_t parent, device_t child) { /* struct usb_softc *sc = device_get_softc(child); */ printf(" at %s%d", device_get_name(parent), device_get_unit(parent)); /* XXX How do we get to the usbd_device_handle??? usbd_device_handle dev = invalidadosch; printf(" addr %d", dev->addr); if (bootverbose) { if (dev->lowspeed) printf(", lowspeed"); if (dev->self_powered) printf(", self powered"); else printf(", %dmA", dev->power); printf(", config %d", dev->config); } */ } /* Reconfigure all the USB busses in the system. */ int usbd_driver_load(module_t mod, int what, void *arg) { #if 1 return 0; #else devclass_t usb_devclass = devclass_find("usb"); devclass_t ugen_devclass = devclass_find("ugen"); device_t *devlist; int devcount; int error; if ( what == MOD_LOAD || what == MOD_UNLOAD ) { if (!usb_devclass) return 0; /* just ignore call */ /* Detach all the generic devices and do a reconfigure * of the bus. This should attach the new driver to anything * that is already connected and it can handle. * XXX For the moment disabled. The detach does not remove * the device from the list of devices attached to the hub. * Legacy of converting from NetBSD to FreeBSD. */ if (ugen_devclass) { /* detach devices from generic driver if possible */ error = devclass_get_devices(ugen_devclass, &devlist, &devcount); if (!error) for (devcount--; devcount >= 0; devcount--) (void)DEVICE_DETACH(devlist[devcount]); free(devlist, M_TEMP); } /* Reconfigure the busses, possibly attaching something to the * new driver */ error = devclass_get_devices(usb_devclass, &devlist, &devcount); if (error) return 0; /* XXX maybe transient, or error? */ for (devcount--; devcount >= 0; devcount--) USB_RECONFIGURE(devlist[devcount]); free(devlist, M_TEMP); } return 0; /* nothing to do */ #endif } /* Set the description of the device including a malloc and copy. */ void usbd_device_set_desc(device_t device, char *devinfo) { - size_t l; - char *desc; - - if ( devinfo ) { - l = strlen(devinfo); - desc = malloc(l+1, M_USB, M_NOWAIT); - if (desc) - memcpy(desc, devinfo, l+1); - } else - desc = NULL; - - device_set_desc(device, desc); -} - -char * -usbd_devname(bdevice *bdev) -{ - static char buf[20]; - /* XXX a static buffer is not exactly a good idea, but the only - * thing that goes wrong is the string that is being printed - */ - - sprintf(buf, "%s%d", device_get_name(*bdev), device_get_unit(*bdev)); - return (buf); + device_set_desc_copy(device, devinfo); } #endif char * usbd_errstr(usbd_status err) { static char buffer[5]; /* XXX static buffer */ #ifdef USB_DEBUG if ( err < USBD_ERROR_MAX ) { return usbd_error_strs[err]; } else { snprintf(buffer, 4, "%d", err); return buffer; } #else snprintf(buffer, 4, "%d", err); return buffer; #endif } Index: head/sys/dev/usb/usbdi.h =================================================================== --- head/sys/dev/usb/usbdi.h (revision 45719) +++ head/sys/dev/usb/usbdi.h (revision 45720) @@ -1,347 +1,346 @@ /* $NetBSD: usbdi.h,v 1.16 1999/01/08 11:58:26 augustss Exp $ */ /* $FreeBSD$ */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (augustss@carlstedt.se) at * Carlstedt Research & Technology. * * 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 NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */ typedef struct usbd_bus *usbd_bus_handle; typedef struct usbd_device *usbd_device_handle; typedef struct usbd_interface *usbd_interface_handle; typedef struct usbd_pipe *usbd_pipe_handle; typedef struct usbd_request *usbd_request_handle; typedef void *usbd_private_handle; typedef enum { USBD_ENDPOINT_ACTIVE, USBD_ENDPOINT_STALLED, } usbd_endpoint_state; typedef enum { USBD_PIPE_ACTIVE, USBD_PIPE_STALLED, USBD_PIPE_IDLE, } usbd_pipe_state; typedef enum { USBD_INTERFACE_ACTIVE, USBD_INTERFACE_STALLED, USBD_INTERFACE_IDLE, } usbd_interface_state; typedef enum { USBD_DEVICE_ATTACHED, USBD_DEVICE_POWERED, USBD_DEVICE_DEFAULT, USBD_DEVICE_ADDRESSED, USBD_DEVICE_CONFIGURED, USBD_DEVICE_SUSPENDED, } usbd_device_state; typedef enum { USBD_NORMAL_COMPLETION = 0, USBD_IN_PROGRESS, /* errors */ USBD_PENDING_REQUESTS, USBD_NOT_STARTED, USBD_INVAL, USBD_IS_IDLE, USBD_NOMEM, USBD_CANCELLED, USBD_BAD_ADDRESS, USBD_IN_USE, USBD_INTERFACE_NOT_ACTIVE, USBD_NO_ADDR, USBD_SET_ADDR_FAILED, USBD_NO_POWER, USBD_TOO_DEEP, USBD_IOERROR, USBD_NOT_CONFIGURED, USBD_TIMEOUT, USBD_SHORT_XFER, USBD_STALLED, USBD_INTERRUPTED, USBD_XXX, #define USBD_ERROR_MAX 21 /* used for usbd_error_strs */ } usbd_status; typedef int usbd_lock_token; typedef void (*usbd_callback) __P((usbd_request_handle, usbd_private_handle, usbd_status)); /* Open flags */ #define USBD_EXCLUSIVE_USE 0x01 /* Request flags */ #define USBD_XFER_OUT 0x01 #define USBD_XFER_IN 0x02 #define USBD_SHORT_XFER_OK 0x04 #define USBD_NO_TIMEOUT 0 #define USBD_DEFAULT_TIMEOUT 5000 /* ms = 5 s */ usbd_status usbd_open_pipe __P((usbd_interface_handle iface, u_int8_t address, u_int8_t flags, usbd_pipe_handle *pipe)); usbd_status usbd_close_pipe __P((usbd_pipe_handle pipe)); usbd_status usbd_transfer __P((usbd_request_handle req)); usbd_request_handle usbd_alloc_request __P((void)); usbd_status usbd_free_request __P((usbd_request_handle reqh)); usbd_status usbd_setup_request __P((usbd_request_handle reqh, usbd_pipe_handle pipe, usbd_private_handle priv, void *buffer, u_int32_t length, u_int16_t flags, u_int32_t timeout, usbd_callback)); usbd_status usbd_setup_device_request __P((usbd_request_handle reqh, usb_device_request_t *req)); usbd_status usbd_setup_default_request __P((usbd_request_handle reqh, usbd_device_handle dev, usbd_private_handle priv, u_int32_t timeout, usb_device_request_t *req, void *buffer, u_int32_t length, u_int16_t flags, usbd_callback)); usbd_status usbd_set_request_timeout __P((usbd_request_handle reqh, u_int32_t timeout)); usbd_status usbd_get_request_status __P((usbd_request_handle reqh, usbd_private_handle *priv, void **buffer, u_int32_t *count, usbd_status *status)); usbd_status usbd_request_device_data __P((usbd_request_handle reqh, usb_device_request_t *req)); usb_descriptor_t *usbd_get_descriptor __P((usbd_interface_handle *iface, u_int8_t desc_type)); usb_endpoint_descriptor_t *usbd_interface2endpoint_descriptor __P((usbd_interface_handle iface, u_int8_t address)); usbd_status usbd_set_configuration __P((usbd_device_handle dev, u_int8_t conf)); usbd_status usbd_retry_request __P((usbd_request_handle reqh, u_int32_t retry_count)); usbd_status usbd_abort_pipe __P((usbd_pipe_handle pipe)); usbd_status usbd_abort_interface __P((usbd_interface_handle iface)); usbd_status usbd_reset_pipe __P((usbd_pipe_handle pipe)); usbd_status usbd_reset_interface __P((usbd_interface_handle iface)); usbd_status usbd_clear_endpoint_stall __P((usbd_pipe_handle pipe)); usbd_status usbd_clear_endpoint_stall_async __P((usbd_pipe_handle pipe)); usbd_status usbd_set_pipe_state __P((usbd_pipe_handle pipe, usbd_pipe_state state)); usbd_status usbd_get_pipe_state __P((usbd_pipe_handle pipe, usbd_pipe_state *state, u_int32_t *endpoint_state, u_int32_t *request_count)); usbd_status usbd_set_interface_state __P((usbd_interface_handle iface, usbd_interface_state state)); usbd_status usbd_get_interface_state __P((usbd_interface_handle iface, usbd_interface_state *state)); usbd_status usbd_get_device_state __P((usbd_device_handle dev, usbd_device_state *state)); usbd_status usbd_set_device_state __P((usbd_device_handle dev, usbd_device_state state)); usbd_status usbd_device_address __P((usbd_device_handle dev, u_int8_t *address)); usbd_status usbd_endpoint_address __P((usbd_pipe_handle dev, u_int8_t *address)); usbd_status usbd_endpoint_count __P((usbd_interface_handle dev, u_int8_t *count)); usbd_status usbd_interface_count __P((usbd_device_handle dev, u_int8_t *count)); #if 0 u_int8_t usbd_bus_count __P((void)); usbd_status usbd_get_bus_handle __P((u_int8_t index, usbd_bus_handle *bus)); usbd_status usbd_get_root_hub __P((usbd_bus_handle bus, usbd_device_handle *dev)); usbd_status usbd_port_count __P((usbd_device_handle hub, u_int8_t *nports)); usbd_status usbd_hub2device_handle __P((usbd_device_handle hub, u_int8_t port, usbd_device_handle *dev)); #endif usbd_status usbd_request2pipe_handle __P((usbd_request_handle reqh, usbd_pipe_handle *pipe)); usbd_status usbd_pipe2interface_handle __P((usbd_pipe_handle pipe, usbd_interface_handle *iface)); usbd_status usbd_interface2device_handle __P((usbd_interface_handle iface, usbd_device_handle *dev)); usbd_status usbd_device2bus_handle __P((usbd_device_handle dev, usbd_bus_handle *bus)); usbd_status usbd_device2interface_handle __P((usbd_device_handle dev, u_int8_t ifaceno, usbd_interface_handle *iface)); usbd_status usbd_set_interface_private_handle __P((usbd_interface_handle iface, usbd_private_handle priv)); usbd_status usbd_get_interface_private_handle __P((usbd_interface_handle iface, usbd_private_handle *priv)); usbd_status usbd_reference_pipe __P((usbd_pipe_handle pipe)); usbd_status usbd_dereference_pipe __P((usbd_pipe_handle pipe)); usbd_lock_token usbd_lock __P((void)); void usbd_unlock __P((usbd_lock_token tok)); /* Non-standard */ usbd_status usbd_sync_transfer __P((usbd_request_handle req)); usbd_status usbd_open_pipe_intr __P((usbd_interface_handle iface, u_int8_t address, u_int8_t flags, usbd_pipe_handle *pipe, usbd_private_handle priv, void *buffer, u_int32_t length, usbd_callback)); usbd_status usbd_open_pipe_iso __P((usbd_interface_handle iface, u_int8_t address, u_int8_t flags, usbd_pipe_handle *pipe, usbd_private_handle priv, u_int32_t bufsize, u_int32_t nbuf, usbd_callback)); usbd_status usbd_do_request __P((usbd_device_handle dev, usb_device_request_t *req, void *data)); usbd_status usbd_do_request_async __P((usbd_device_handle dev, usb_device_request_t *req, void *data)); usbd_status usbd_do_request_flags __P((usbd_device_handle dev, usb_device_request_t *req, void *data, u_int16_t flags, int *)); usb_interface_descriptor_t *usbd_get_interface_descriptor __P((usbd_interface_handle iface)); usb_config_descriptor_t *usbd_get_config_descriptor __P((usbd_device_handle dev)); usb_device_descriptor_t *usbd_get_device_descriptor __P((usbd_device_handle dev)); usbd_status usbd_set_interface __P((usbd_interface_handle, int)); int usbd_get_no_alts __P((usb_config_descriptor_t *, int)); usbd_status usbd_get_interface __P((usbd_interface_handle iface, u_int8_t *aiface)); void usbd_fill_deviceinfo __P((usbd_device_handle dev, struct usb_device_info *di)); int usbd_get_interface_altindex __P((usbd_interface_handle iface)); usb_interface_descriptor_t *usbd_find_idesc __P((usb_config_descriptor_t *cd, int iindex, int ano)); usb_endpoint_descriptor_t *usbd_find_edesc __P((usb_config_descriptor_t *cd, int ifaceidx, int altidx, int endptidx)); char * usbd_errstr(usbd_status err); void usbd_dopoll __P((usbd_interface_handle)); void usbd_set_polling __P((usbd_interface_handle iface, int on)); /* NetBSD attachment information */ /* Attach data */ struct usb_attach_arg { int port; int configno; int ifaceno; usbd_device_handle device; /* current device */ usbd_interface_handle iface; /* current interface */ int usegeneric; usbd_interface_handle *ifaces; /* all interfaces */ int nifaces; /* number of interfaces */ }; #if defined(__NetBSD__) /* Match codes. */ /* First five codes is for a whole device. */ #define UMATCH_VENDOR_PRODUCT_REV 14 #define UMATCH_VENDOR_PRODUCT 13 #define UMATCH_VENDOR_DEVCLASS_DEVPROTO 12 #define UMATCH_DEVCLASS_DEVSUBCLASS_DEVPROTO 11 #define UMATCH_DEVCLASS_DEVSUBCLASS 10 /* Next six codes are for interfaces. */ #define UMATCH_VENDOR_PRODUCT_REV_CONF_IFACE 9 #define UMATCH_VENDOR_PRODUCT_CONF_IFACE 8 #define UMATCH_VENDOR_IFACESUBCLASS_IFACEPROTO 7 #define UMATCH_VENDOR_IFACESUBCLASS 6 #define UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO 5 #define UMATCH_IFACECLASS_IFACESUBCLASS 4 #define UMATCH_IFACECLASS 3 #define UMATCH_IFACECLASS_GENERIC 2 /* Generic driver */ #define UMATCH_GENERIC 1 /* No match */ #define UMATCH_NONE 0 #elif defined(__FreeBSD__) /* FreeBSD needs values less than zero */ /* for the moment disabled #define UMATCH_VENDOR_PRODUCT_REV -14 #define UMATCH_VENDOR_PRODUCT -13 #define UMATCH_VENDOR_DEVCLASS_DEVPROTO -12 #define UMATCH_DEVCLASS_DEVSUBCLASS_DEVPROTO -11 #define UMATCH_DEVCLASS_DEVSUBCLASS -10 #define UMATCH_VENDOR_PRODUCT_REV_CONF_IFACE -9 #define UMATCH_VENDOR_PRODUCT_CONF_IFACE -8 #define UMATCH_VENDOR_IFACESUBCLASS_IFACEPROTO -7 #define UMATCH_VENDOR_IFACESUBCLASS -6 #define UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO -5 #define UMATCH_IFACECLASS_IFACESUBCLASS -4 #define UMATCH_IFACECLASS -3 #define UMATCH_IFACECLASS_GENERIC -2 #define UMATCH_GENERIC -1 #define UMATCH_NONE ENXIO * For the moment we use Yes/No answers with appropriate * sorting in the config file */ #define UMATCH_VENDOR_PRODUCT_REV 0 #define UMATCH_VENDOR_PRODUCT 0 #define UMATCH_VENDOR_DEVCLASS_DEVPROTO 0 #define UMATCH_DEVCLASS_DEVSUBCLASS_DEVPROTO 0 #define UMATCH_DEVCLASS_DEVSUBCLASS 0 #define UMATCH_VENDOR_PRODUCT_REV_CONF_IFACE 0 #define UMATCH_VENDOR_PRODUCT_CONF_IFACE 0 #define UMATCH_VENDOR_IFACESUBCLASS_IFACEPROTO 0 #define UMATCH_VENDOR_IFACESUBCLASS 0 #define UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO 0 #define UMATCH_IFACECLASS_IFACESUBCLASS 0 #define UMATCH_IFACECLASS 0 #define UMATCH_IFACECLASS_GENERIC 0 #define UMATCH_GENERIC 0 #define UMATCH_NONE ENXIO #endif void usbd_devinfo __P((usbd_device_handle, int, char *)); struct usbd_quirks *usbd_get_quirks __P((usbd_device_handle)); void usbd_set_disco __P((usbd_pipe_handle, void (*)(void *), void *)); usb_endpoint_descriptor_t *usbd_get_endpoint_descriptor __P((usbd_interface_handle iface, u_int8_t address)); #if defined(__FreeBSD__) int usbd_driver_load __P((module_t mod, int what, void *arg)); void usbd_device_set_desc __P((device_t device, char *devinfo)); -char *usbd_devname(bdevice *bdev); bus_print_child_t usbd_print_child; #endif /* XXX */ #define splusb splbio #define IPL_USB IPL_BIO /* XXX */ Index: head/sys/i386/apm/apm.c =================================================================== --- head/sys/i386/apm/apm.c (revision 45719) +++ head/sys/i386/apm/apm.c (revision 45720) @@ -1,1027 +1,1043 @@ /* * APM (Advanced Power Management) BIOS Device Driver * * Copyright (c) 1994 UKAI, Fumitoshi. * Copyright (c) 1994-1995 by HOSOKAWA, Tatsumi * Copyright (c) 1996 Nate Williams * Copyright (c) 1997 Poul-Henning Kamp * * This software may be used, modified, copied, and distributed, 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. * * Sep, 1994 Implemented on FreeBSD 1.1.5.1R (Toshiba AVS001WD) * - * $Id: apm.c,v 1.76 1998/12/04 21:28:39 archie Exp $ + * $Id: apm.c,v 1.77 1998/12/10 23:36:14 msmith Exp $ */ #include "opt_devfs.h" #include "opt_vm86.h" #include #include #include #ifdef DEVFS #include #endif /*DEVFS*/ #include #include #include -#include +#include #include #include #include #include #include #include #include #include #ifdef VM86 #include #include #endif static int apm_display __P((int newstate)); static int apm_int __P((u_long *eax, u_long *ebx, u_long *ecx, u_long *edx)); static void apm_resume __P((void)); /* static data */ struct apm_softc { int initialized, active; int always_halt_cpu, slow_idle_cpu; int disabled, disengaged; u_int minorversion, majorversion; u_int cs32_base, cs16_base, ds_base; u_int cs16_limit, cs32_limit, ds_limit; u_int cs_entry; u_int intversion; struct apmhook sc_suspend; struct apmhook sc_resume; #ifdef DEVFS void *sc_devfs_token; #endif }; static struct apm_softc apm_softc; static struct apmhook *hook[NAPM_HOOK]; /* XXX */ #define is_enabled(foo) ((foo) ? "enabled" : "disabled") /* Map version number to integer (keeps ordering of version numbers) */ #define INTVERSION(major, minor) ((major)*100 + (minor)) static struct callout_handle apm_timeout_ch = CALLOUT_HANDLE_INITIALIZER(&apm_timeout_ch); static timeout_t apm_timeout; static d_open_t apmopen; static d_close_t apmclose; static d_ioctl_t apmioctl; #define CDEV_MAJOR 39 static struct cdevsw apm_cdevsw = { apmopen, apmclose, noread, nowrite, /*39*/ apmioctl, nostop, nullreset, nodevtotty,/* APM */ seltrue, nommap, NULL , "apm" ,NULL, -1}; /* setup APM GDT discriptors */ static void setup_apm_gdt(u_int code32_base, u_int code16_base, u_int data_base, u_int code32_limit, u_int code16_limit, u_int data_limit) { /* setup 32bit code segment */ gdt_segs[GAPMCODE32_SEL].ssd_base = code32_base; gdt_segs[GAPMCODE32_SEL].ssd_limit = code32_limit; /* setup 16bit code segment */ gdt_segs[GAPMCODE16_SEL].ssd_base = code16_base; gdt_segs[GAPMCODE16_SEL].ssd_limit = code16_limit; /* setup data segment */ gdt_segs[GAPMDATA_SEL ].ssd_base = data_base; gdt_segs[GAPMDATA_SEL ].ssd_limit = data_limit; /* reflect these changes on physical GDT */ ssdtosd(gdt_segs + GAPMCODE32_SEL, &gdt[GAPMCODE32_SEL].sd); ssdtosd(gdt_segs + GAPMCODE16_SEL, &gdt[GAPMCODE16_SEL].sd); ssdtosd(gdt_segs + GAPMDATA_SEL , &gdt[GAPMDATA_SEL ].sd); } /* 48bit far pointer. Do not staticize - used from apm_setup.s */ struct addr48 { u_long offset; u_short segment; } apm_addr; static int apm_errno; static int apm_int(u_long *eax, u_long *ebx, u_long *ecx, u_long *edx) { struct apm_bios_arg apa; int cf; apa.eax = *eax; apa.ebx = *ebx; apa.ecx = *ecx; apa.edx = *edx; cf = apm_bios_call(&apa); *eax = apa.eax; *ebx = apa.ebx; *ecx = apa.ecx; *edx = apa.edx; apm_errno = ((*eax) >> 8) & 0xff; return cf; } /* enable/disable power management */ static int apm_enable_disable_pm(int enable) { struct apm_softc *sc = &apm_softc; u_long eax, ebx, ecx, edx; eax = (APM_BIOS << 8) | APM_ENABLEDISABLEPM; if (sc->intversion >= INTVERSION(1, 1)) ebx = PMDV_ALLDEV; else ebx = 0xffff; /* APM version 1.0 only */ ecx = enable; edx = 0; return apm_int(&eax, &ebx, &ecx, &edx); } static void apm_driver_version(int version) { u_long eax, ebx, ecx, edx; /* First try APM 1.2 */ eax = (APM_BIOS << 8) | APM_DRVVERSION; ebx = 0x0; ecx = version; edx = 0; if(!apm_int(&eax, &ebx, &ecx, &edx)) apm_version = eax & 0xffff; } /* engage/disengage power management (APM 1.1 or later) */ static int apm_engage_disengage_pm(int engage) { u_long eax, ebx, ecx, edx; eax = (APM_BIOS << 8) | APM_ENGAGEDISENGAGEPM; ebx = PMDV_ALLDEV; ecx = engage; edx = 0; return(apm_int(&eax, &ebx, &ecx, &edx)); } /* get PM event */ static u_int apm_getevent(void) { u_long eax, ebx, ecx, edx; eax = (APM_BIOS << 8) | APM_GETPMEVENT; ebx = 0; ecx = 0; edx = 0; if (apm_int(&eax, &ebx, &ecx, &edx)) return PMEV_NOEVENT; return ebx & 0xffff; } /* suspend entire system */ static int apm_suspend_system(int state) { u_long eax, ebx, ecx, edx; eax = (APM_BIOS << 8) | APM_SETPWSTATE; ebx = PMDV_ALLDEV; ecx = state; edx = 0; if (apm_int(&eax, &ebx, &ecx, &edx)) { printf("Entire system suspend failure: errcode = %ld\n", 0xff & (eax >> 8)); return 1; } return 0; } /* Display control */ /* * Experimental implementation: My laptop machine can't handle this function * If your laptop can control the display via APM, please inform me. * HOSOKAWA, Tatsumi */ static int apm_display(int newstate) { u_long eax, ebx, ecx, edx; eax = (APM_BIOS << 8) | APM_SETPWSTATE; ebx = PMDV_DISP0; ecx = newstate ? PMST_APMENABLED:PMST_SUSPEND; edx = 0; if (apm_int(&eax, &ebx, &ecx, &edx)) { printf("Display off failure: errcode = %ld\n", 0xff & (eax >> 8)); return 1; } return 0; } /* * Turn off the entire system. */ static void apm_power_off(int howto, void *junk) { u_long eax, ebx, ecx, edx; /* Not halting powering off, or not active */ if (!(howto & RB_POWEROFF) || !apm_softc.active) return; eax = (APM_BIOS << 8) | APM_SETPWSTATE; ebx = PMDV_ALLDEV; ecx = PMST_OFF; edx = 0; apm_int(&eax, &ebx, &ecx, &edx); } /* APM Battery low handler */ static void apm_battery_low(void) { printf("\007\007 * * * BATTERY IS LOW * * * \007\007"); } /* APM hook manager */ static struct apmhook * apm_add_hook(struct apmhook **list, struct apmhook *ah) { int s; struct apmhook *p, *prev; #ifdef APM_DEBUG printf("Add hook \"%s\"\n", ah->ah_name); #endif s = splhigh(); if (ah == NULL) panic("illegal apm_hook!"); prev = NULL; for (p = *list; p != NULL; prev = p, p = p->ah_next) if (p->ah_order > ah->ah_order) break; if (prev == NULL) { ah->ah_next = *list; *list = ah; } else { ah->ah_next = prev->ah_next; prev->ah_next = ah; } splx(s); return ah; } static void apm_del_hook(struct apmhook **list, struct apmhook *ah) { int s; struct apmhook *p, *prev; s = splhigh(); prev = NULL; for (p = *list; p != NULL; prev = p, p = p->ah_next) if (p == ah) goto deleteit; panic("Tried to delete unregistered apm_hook."); goto nosuchnode; deleteit: if (prev != NULL) prev->ah_next = p->ah_next; else *list = p->ah_next; nosuchnode: splx(s); } /* APM driver calls some functions automatically */ static void apm_execute_hook(struct apmhook *list) { struct apmhook *p; for (p = list; p != NULL; p = p->ah_next) { #ifdef APM_DEBUG printf("Execute APM hook \"%s.\"\n", p->ah_name); #endif if ((*(p->ah_fun))(p->ah_arg)) printf("Warning: APM hook \"%s\" failed", p->ah_name); } } /* establish an apm hook */ struct apmhook * apm_hook_establish(int apmh, struct apmhook *ah) { if (apmh < 0 || apmh >= NAPM_HOOK) return NULL; return apm_add_hook(&hook[apmh], ah); } /* disestablish an apm hook */ void apm_hook_disestablish(int apmh, struct apmhook *ah) { if (apmh < 0 || apmh >= NAPM_HOOK) return; apm_del_hook(&hook[apmh], ah); } static struct timeval suspend_time; static struct timeval diff_time; static int apm_default_resume(void *arg) { int pl; u_int second, minute, hour; struct timeval resume_time, tmp_time; /* modified for adjkerntz */ pl = splsoftclock(); inittodr(0); /* adjust time to RTC */ microtime(&resume_time); getmicrotime(&tmp_time); timevaladd(&tmp_time, &diff_time); #ifdef FIXME /* XXX THIS DOESN'T WORK!!! */ time = tmp_time; #endif #ifdef APM_FIXUP_CALLTODO /* Calculate the delta time suspended */ timevalsub(&resume_time, &suspend_time); /* Fixup the calltodo list with the delta time. */ adjust_timeout_calltodo(&resume_time); #endif /* APM_FIXUP_CALLTODOK */ splx(pl); #ifndef APM_FIXUP_CALLTODO second = resume_time.tv_sec - suspend_time.tv_sec; #else /* APM_FIXUP_CALLTODO */ /* * We've already calculated resume_time to be the delta between * the suspend and the resume. */ second = resume_time.tv_sec; #endif /* APM_FIXUP_CALLTODO */ hour = second / 3600; second %= 3600; minute = second / 60; second %= 60; log(LOG_NOTICE, "resumed from suspended mode (slept %02d:%02d:%02d)\n", hour, minute, second); return 0; } static int apm_default_suspend(void *arg) { int pl; pl = splsoftclock(); microtime(&diff_time); inittodr(0); microtime(&suspend_time); timevalsub(&diff_time, &suspend_time); splx(pl); return 0; } static void apm_processevent(void); /* * Public interface to the suspend/resume: * * Execute suspend and resume hook before and after sleep, respectively. * */ void apm_suspend(int state) { struct apm_softc *sc = &apm_softc; + int error; if (!sc) return; if (sc->initialized) { + error = DEVICE_SUSPEND(root_bus); + if (error) + return; /* XXX no error reporting */ apm_execute_hook(hook[APM_HOOK_SUSPEND]); if (apm_suspend_system(state) == 0) apm_processevent(); else /* Failure, 'resume' the system again */ apm_execute_hook(hook[APM_HOOK_RESUME]); } } void apm_resume(void) { struct apm_softc *sc = &apm_softc; if (!sc) return; - if (sc->initialized) + if (sc->initialized) { + DEVICE_RESUME(root_bus); apm_execute_hook(hook[APM_HOOK_RESUME]); + } } /* get APM information */ static int apm_get_info(apm_info_t aip) { struct apm_softc *sc = &apm_softc; u_long eax, ebx, ecx, edx; eax = (APM_BIOS << 8) | APM_GETPWSTATUS; ebx = PMDV_ALLDEV; ecx = 0; edx = 0xffff; /* default to unknown battery time */ if (apm_int(&eax, &ebx, &ecx, &edx)) return 1; aip->ai_infoversion = 1; aip->ai_acline = (ebx >> 8) & 0xff; aip->ai_batt_stat = ebx & 0xff; aip->ai_batt_life = ecx & 0xff; aip->ai_major = (u_int)sc->majorversion; aip->ai_minor = (u_int)sc->minorversion; aip->ai_status = (u_int)sc->active; edx &= 0xffff; if (edx == 0xffff) /* Time is unknown */ aip->ai_batt_time = -1; else if (edx & 0x8000) /* Time is in minutes */ aip->ai_batt_time = (edx & 0x7fff) * 60; else /* Time is in seconds */ aip->ai_batt_time = edx; eax = (APM_BIOS << 8) | APM_GETCAPABILITIES; ebx = 0; ecx = 0; edx = 0; if (apm_int(&eax, &ebx, &ecx, &edx)) { aip->ai_batteries = -1; /* Unknown */ aip->ai_capabilities = 0xff00; /* Unknown, with no bits set */ } else { aip->ai_batteries = ebx & 0xff; aip->ai_capabilities = ecx & 0xf; } bzero(aip->ai_spare, sizeof aip->ai_spare); return 0; } /* inform APM BIOS that CPU is idle */ void apm_cpu_idle(void) { struct apm_softc *sc = &apm_softc; if (sc->active) { u_long eax, ebx, ecx, edx; eax = (APM_BIOS <<8) | APM_CPUIDLE; edx = ecx = ebx = 0; apm_int(&eax, &ebx, &ecx, &edx); } /* * Some APM implementation halts CPU in BIOS, whenever * "CPU-idle" function are invoked, but swtch() of * FreeBSD halts CPU, therefore, CPU is halted twice * in the sched loop. It makes the interrupt latency * terribly long and be able to cause a serious problem * in interrupt processing. We prevent it by removing * "hlt" operation from swtch() and managed it under * APM driver. */ if (!sc->active || sc->always_halt_cpu) __asm("hlt"); /* wait for interrupt */ } /* inform APM BIOS that CPU is busy */ void apm_cpu_busy(void) { struct apm_softc *sc = &apm_softc; /* * The APM specification says this is only necessary if your BIOS * slows down the processor in the idle task, otherwise it's not * necessary. */ if (sc->slow_idle_cpu && sc->active) { u_long eax, ebx, ecx, edx; eax = (APM_BIOS <<8) | APM_CPUBUSY; edx = ecx = ebx = 0; apm_int(&eax, &ebx, &ecx, &edx); } } /* * APM timeout routine: * * This routine is automatically called by timer once per second. */ static void apm_timeout(void *dummy) { struct apm_softc *sc = &apm_softc; apm_processevent(); if (sc->active == 1) /* Run slightly more oftan than 1 Hz */ apm_timeout_ch = timeout(apm_timeout, NULL, hz - 1 ); } /* enable APM BIOS */ static void apm_event_enable(void) { struct apm_softc *sc = &apm_softc; #ifdef APM_DEBUG printf("called apm_event_enable()\n"); #endif if (sc->initialized) { sc->active = 1; apm_timeout(sc); } } /* disable APM BIOS */ static void apm_event_disable(void) { struct apm_softc *sc = &apm_softc; #ifdef APM_DEBUG printf("called apm_event_disable()\n"); #endif if (sc->initialized) { untimeout(apm_timeout, NULL, apm_timeout_ch); sc->active = 0; } } /* halt CPU in scheduling loop */ static void apm_halt_cpu(void) { struct apm_softc *sc = &apm_softc; if (sc->initialized) sc->always_halt_cpu = 1; } /* don't halt CPU in scheduling loop */ static void apm_not_halt_cpu(void) { struct apm_softc *sc = &apm_softc; if (sc->initialized) sc->always_halt_cpu = 0; } /* device driver definitions */ -static int apmprobe (struct isa_device *); -static int apmattach(struct isa_device *); -struct isa_driver apmdriver = { apmprobe, apmattach, "apm" }; /* * probe APM (dummy): * * APM probing routine is placed on locore.s and apm_init.S because * this process forces the CPU to turn to real mode or V86 mode. * Current version uses real mode, but in a future version, we want * to use V86 mode in APM initialization. * * XXX If VM86 is defined, we do. */ static int -apmprobe(struct isa_device *dvp) +apm_probe(device_t dev) { #ifdef VM86 struct vm86frame vmf; int i; #endif + int flags; - if ( dvp->id_unit > 0 ) { + device_set_desc(dev, "APM BIOS"); + + if ( device_get_unit(dev) > 0 ) { printf("apm: Only one APM driver supported.\n"); - return 0; + return ENXIO; } + if (resource_int_value("apm", 0, "flags", &flags) != 0) + flags = 0; + #ifdef VM86 bzero(&vmf, sizeof(struct vm86frame)); /* safety */ vmf.vmf_ax = (APM_BIOS << 8) | APM_INSTCHECK; vmf.vmf_bx = 0; if (((i = vm86_intcall(SYSTEM_BIOS, &vmf)) == 0) && !(vmf.vmf_eflags & PSL_C) && (vmf.vmf_bx == 0x504d)) { apm_version = vmf.vmf_ax; apm_flags = vmf.vmf_cx; vmf.vmf_ax = (APM_BIOS << 8) | APM_PROT32CONNECT; vmf.vmf_bx = 0; if (((i = vm86_intcall(SYSTEM_BIOS, &vmf)) == 0) && !(vmf.vmf_eflags & PSL_C)) { apm_cs32_base = vmf.vmf_ax; apm_cs_entry = vmf.vmf_ebx; apm_cs16_base = vmf.vmf_cx; apm_ds_base = vmf.vmf_dx; apm_cs32_limit = vmf.vmf_si; if (apm_version >= 0x0102) apm_cs16_limit = (vmf.esi.r_ex >> 16); apm_ds_limit = vmf.vmf_di; #ifdef APM_DEBUG printf("apm: BIOS probe/32-bit connect successful\n"); #endif } else { /* XXX constant typo! */ if (vmf.vmf_ah == APME_PROT32NOTDUPPORTED) { apm_version = APMINI_NOT32BIT; } else { apm_version = APMINI_CONNECTERR; } #ifdef APM_DEBUG printf("apm: BIOS 32-bit connect failed: error 0x%x carry %d ah 0x%x\n", i, (vmf.vmf_eflags & PSL_C) ? 1 : 0, vmf.vmf_ah); #endif } } else { apm_version = APMINI_CANTFIND; #ifdef APM_DEBUG printf("apm: BIOS probe failed: error 0x%x carry %d bx 0x%x\n", i, (vmf.vmf_eflags & PSL_C) ? 1 : 0, vmf.vmf_bx); #endif } #endif bzero(&apm_softc, sizeof(apm_softc)); switch (apm_version) { case APMINI_CANTFIND: /* silent */ return 0; case APMINI_NOT32BIT: printf("apm: 32bit connection is not supported.\n"); return 0; case APMINI_CONNECTERR: printf("apm: 32-bit connection error.\n"); return 0; } - if (dvp->id_flags & 0x20) + if (flags & 0x20) statclock_disable = 1; - return -1; + return 0; } /* Process APM event */ static void apm_processevent(void) { int apm_event; #ifdef APM_DEBUG # define OPMEV_DEBUGMESSAGE(symbol) case symbol: \ printf("Received APM Event: " #symbol "\n"); #else # define OPMEV_DEBUGMESSAGE(symbol) case symbol: #endif do { apm_event = apm_getevent(); switch (apm_event) { OPMEV_DEBUGMESSAGE(PMEV_STANDBYREQ); apm_suspend(PMST_STANDBY); break; OPMEV_DEBUGMESSAGE(PMEV_SUSPENDREQ); apm_suspend(PMST_SUSPEND); break; OPMEV_DEBUGMESSAGE(PMEV_USERSUSPENDREQ); apm_suspend(PMST_SUSPEND); break; OPMEV_DEBUGMESSAGE(PMEV_CRITSUSPEND); apm_suspend(PMST_SUSPEND); break; OPMEV_DEBUGMESSAGE(PMEV_NORMRESUME); apm_resume(); break; OPMEV_DEBUGMESSAGE(PMEV_CRITRESUME); apm_resume(); break; OPMEV_DEBUGMESSAGE(PMEV_STANDBYRESUME); apm_resume(); break; OPMEV_DEBUGMESSAGE(PMEV_BATTERYLOW); apm_battery_low(); apm_suspend(PMST_SUSPEND); break; OPMEV_DEBUGMESSAGE(PMEV_POWERSTATECHANGE); break; OPMEV_DEBUGMESSAGE(PMEV_UPDATETIME); inittodr(0); /* adjust time to RTC */ break; case PMEV_NOEVENT: break; default: printf("Unknown Original APM Event 0x%x\n", apm_event); break; } } while (apm_event != PMEV_NOEVENT); } /* * Attach APM: * * Initialize APM driver (APM BIOS itself has been initialized in locore.s) */ static int -apmattach(struct isa_device *dvp) +apm_attach(device_t dev) { #define APM_KERNBASE KERNBASE struct apm_softc *sc = &apm_softc; + int flags; + if (resource_int_value("apm", 0, "flags", &flags) != 0) + flags = 0; + sc->initialized = 0; /* Must be externally enabled */ sc->active = 0; /* setup APM parameters */ sc->cs16_base = (apm_cs16_base << 4) + APM_KERNBASE; sc->cs32_base = (apm_cs32_base << 4) + APM_KERNBASE; sc->ds_base = (apm_ds_base << 4) + APM_KERNBASE; sc->cs32_limit = apm_cs32_limit - 1; if (apm_cs16_limit == 0) apm_cs16_limit = apm_cs32_limit; sc->cs16_limit = apm_cs16_limit - 1; sc->ds_limit = apm_ds_limit - 1; sc->cs_entry = apm_cs_entry; /* Always call HLT in idle loop */ sc->always_halt_cpu = 1; sc->slow_idle_cpu = ((apm_flags & APM_CPUIDLE_SLOW) != 0); sc->disabled = ((apm_flags & APM_DISABLED) != 0); sc->disengaged = ((apm_flags & APM_DISENGAGED) != 0); /* print bootstrap messages */ #ifdef APM_DEBUG printf("apm: APM BIOS version %04x\n", apm_version); printf("apm: Code32 0x%08x, Code16 0x%08x, Data 0x%08x\n", sc->cs32_base, sc->cs16_base, sc->ds_base); printf("apm: Code entry 0x%08x, Idling CPU %s, Management %s\n", sc->cs_entry, is_enabled(sc->slow_idle_cpu), is_enabled(!sc->disabled)); printf("apm: CS32_limit=0x%x, CS16_limit=0x%x, DS_limit=0x%x\n", (u_short)sc->cs32_limit, (u_short)sc->cs16_limit, (u_short)sc->ds_limit); #endif /* APM_DEBUG */ #if 0 /* Workaround for some buggy APM BIOS implementations */ sc->cs_limit = 0xffff; sc->ds_limit = 0xffff; #endif /* setup GDT */ setup_apm_gdt(sc->cs32_base, sc->cs16_base, sc->ds_base, sc->cs32_limit, sc->cs16_limit, sc->ds_limit); /* setup entry point 48bit pointer */ apm_addr.segment = GSEL(GAPMCODE32_SEL, SEL_KPL); apm_addr.offset = sc->cs_entry; - if ((dvp->id_flags & 0x10)) { - if ((dvp->id_flags & 0xf) >= 0x2) { + if ((flags & 0x10)) { + if ((flags & 0xf) >= 0x2) { apm_driver_version(0x102); } - if (!apm_version && (dvp->id_flags & 0xf) >= 0x1) { + if (!apm_version && (flags & 0xf) >= 0x1) { apm_driver_version(0x101); } } else { apm_driver_version(0x102); if (!apm_version) apm_driver_version(0x101); } if (!apm_version) apm_version = 0x100; sc->minorversion = ((apm_version & 0x00f0) >> 4) * 10 + ((apm_version & 0x000f) >> 0); sc->majorversion = ((apm_version & 0xf000) >> 12) * 10 + ((apm_version & 0x0f00) >> 8); sc->intversion = INTVERSION(sc->majorversion, sc->minorversion); #ifdef APM_DEBUG if (sc->intversion >= INTVERSION(1, 1)) printf("apm: Engaged control %s\n", is_enabled(!sc->disengaged)); #endif printf("apm: found APM BIOS version %d.%d\n", sc->majorversion, sc->minorversion); #ifdef APM_DEBUG printf("apm: Slow Idling CPU %s\n", is_enabled(sc->slow_idle_cpu)); #endif /* enable power management */ if (sc->disabled) { if (apm_enable_disable_pm(1)) { #ifdef APM_DEBUG printf("apm: *Warning* enable function failed! [%x]\n", apm_errno); #endif } } /* engage power managment (APM 1.1 or later) */ if (sc->intversion >= INTVERSION(1, 1) && sc->disengaged) { if (apm_engage_disengage_pm(1)) { #ifdef APM_DEBUG printf("apm: *Warning* engage function failed err=[%x]", apm_errno); printf(" (Docked or using external power?).\n"); #endif } } /* default suspend hook */ sc->sc_suspend.ah_fun = apm_default_suspend; sc->sc_suspend.ah_arg = sc; sc->sc_suspend.ah_name = "default suspend"; sc->sc_suspend.ah_order = APM_MAX_ORDER; /* default resume hook */ sc->sc_resume.ah_fun = apm_default_resume; sc->sc_resume.ah_arg = sc; sc->sc_resume.ah_name = "default resume"; sc->sc_resume.ah_order = APM_MIN_ORDER; apm_hook_establish(APM_HOOK_SUSPEND, &sc->sc_suspend); apm_hook_establish(APM_HOOK_RESUME , &sc->sc_resume); apm_event_enable(); /* Power the system off using APM */ at_shutdown_pri(apm_power_off, NULL, SHUTDOWN_FINAL, SHUTDOWN_PRI_LAST); sc->initialized = 1; #ifdef DEVFS sc->sc_devfs_token = devfs_add_devswf(&apm_cdevsw, 0, DV_CHR, 0, 0, 0600, "apm"); #endif return 0; } static int apmopen(dev_t dev, int flag, int fmt, struct proc *p) { struct apm_softc *sc = &apm_softc; if (minor(dev) != 0 || !sc->initialized) return (ENXIO); return 0; } static int apmclose(dev_t dev, int flag, int fmt, struct proc *p) { return 0; } static int apmioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p) { struct apm_softc *sc = &apm_softc; int error = 0; int newstate; if (minor(dev) != 0 || !sc->initialized) return (ENXIO); #ifdef APM_DEBUG printf("APM ioctl: cmd = 0x%x\n", cmd); #endif switch (cmd) { case APMIO_SUSPEND: if (sc->active) apm_suspend(PMST_SUSPEND); else error = EINVAL; break; case APMIO_STANDBY: if (sc->active) apm_suspend(PMST_STANDBY); else error = EINVAL; break; case APMIO_GETINFO_OLD: { struct apm_info info; apm_info_old_t aiop; if (apm_get_info(&info)) error = ENXIO; aiop = (apm_info_old_t)addr; aiop->ai_major = info.ai_major; aiop->ai_minor = info.ai_minor; aiop->ai_acline = info.ai_acline; aiop->ai_batt_stat = info.ai_batt_stat; aiop->ai_batt_life = info.ai_batt_life; aiop->ai_status = info.ai_status; } break; case APMIO_GETINFO: if (apm_get_info((apm_info_t)addr)) error = ENXIO; break; case APMIO_ENABLE: apm_event_enable(); break; case APMIO_DISABLE: apm_event_disable(); break; case APMIO_HALTCPU: apm_halt_cpu(); break; case APMIO_NOTHALTCPU: apm_not_halt_cpu(); break; case APMIO_DISPLAY: newstate = *(int *)addr; if (apm_display(newstate)) error = ENXIO; break; case APMIO_BIOS: if (apm_bios_call((struct apm_bios_arg*)addr) == 0) ((struct apm_bios_arg*)addr)->eax &= 0xff; break; default: error = EINVAL; break; } return error; } +static device_method_t apm_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, apm_probe), + DEVMETHOD(device_attach, apm_attach), -static apm_devsw_installed = 0; + { 0, 0 } +}; -static void -apm_drvinit(void *unused) -{ - dev_t dev; +static driver_t apm_driver = { + "apm", + apm_methods, + DRIVER_TYPE_MISC, + 1, /* no softc (XXX) */ +}; - if( ! apm_devsw_installed ) { - dev = makedev(CDEV_MAJOR,0); - cdevsw_add(&dev,&apm_cdevsw,NULL); - apm_devsw_installed = 1; - } -} +static devclass_t apm_devclass; -SYSINIT(apmdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,apm_drvinit,NULL) +CDEV_DRIVER_MODULE(apm, nexus, apm_driver, apm_devclass, + CDEV_MAJOR, apm_cdevsw, 0, 0); Index: head/sys/i386/bios/apm.c =================================================================== --- head/sys/i386/bios/apm.c (revision 45719) +++ head/sys/i386/bios/apm.c (revision 45720) @@ -1,1027 +1,1043 @@ /* * APM (Advanced Power Management) BIOS Device Driver * * Copyright (c) 1994 UKAI, Fumitoshi. * Copyright (c) 1994-1995 by HOSOKAWA, Tatsumi * Copyright (c) 1996 Nate Williams * Copyright (c) 1997 Poul-Henning Kamp * * This software may be used, modified, copied, and distributed, 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. * * Sep, 1994 Implemented on FreeBSD 1.1.5.1R (Toshiba AVS001WD) * - * $Id: apm.c,v 1.76 1998/12/04 21:28:39 archie Exp $ + * $Id: apm.c,v 1.77 1998/12/10 23:36:14 msmith Exp $ */ #include "opt_devfs.h" #include "opt_vm86.h" #include #include #include #ifdef DEVFS #include #endif /*DEVFS*/ #include #include #include -#include +#include #include #include #include #include #include #include #include #include #ifdef VM86 #include #include #endif static int apm_display __P((int newstate)); static int apm_int __P((u_long *eax, u_long *ebx, u_long *ecx, u_long *edx)); static void apm_resume __P((void)); /* static data */ struct apm_softc { int initialized, active; int always_halt_cpu, slow_idle_cpu; int disabled, disengaged; u_int minorversion, majorversion; u_int cs32_base, cs16_base, ds_base; u_int cs16_limit, cs32_limit, ds_limit; u_int cs_entry; u_int intversion; struct apmhook sc_suspend; struct apmhook sc_resume; #ifdef DEVFS void *sc_devfs_token; #endif }; static struct apm_softc apm_softc; static struct apmhook *hook[NAPM_HOOK]; /* XXX */ #define is_enabled(foo) ((foo) ? "enabled" : "disabled") /* Map version number to integer (keeps ordering of version numbers) */ #define INTVERSION(major, minor) ((major)*100 + (minor)) static struct callout_handle apm_timeout_ch = CALLOUT_HANDLE_INITIALIZER(&apm_timeout_ch); static timeout_t apm_timeout; static d_open_t apmopen; static d_close_t apmclose; static d_ioctl_t apmioctl; #define CDEV_MAJOR 39 static struct cdevsw apm_cdevsw = { apmopen, apmclose, noread, nowrite, /*39*/ apmioctl, nostop, nullreset, nodevtotty,/* APM */ seltrue, nommap, NULL , "apm" ,NULL, -1}; /* setup APM GDT discriptors */ static void setup_apm_gdt(u_int code32_base, u_int code16_base, u_int data_base, u_int code32_limit, u_int code16_limit, u_int data_limit) { /* setup 32bit code segment */ gdt_segs[GAPMCODE32_SEL].ssd_base = code32_base; gdt_segs[GAPMCODE32_SEL].ssd_limit = code32_limit; /* setup 16bit code segment */ gdt_segs[GAPMCODE16_SEL].ssd_base = code16_base; gdt_segs[GAPMCODE16_SEL].ssd_limit = code16_limit; /* setup data segment */ gdt_segs[GAPMDATA_SEL ].ssd_base = data_base; gdt_segs[GAPMDATA_SEL ].ssd_limit = data_limit; /* reflect these changes on physical GDT */ ssdtosd(gdt_segs + GAPMCODE32_SEL, &gdt[GAPMCODE32_SEL].sd); ssdtosd(gdt_segs + GAPMCODE16_SEL, &gdt[GAPMCODE16_SEL].sd); ssdtosd(gdt_segs + GAPMDATA_SEL , &gdt[GAPMDATA_SEL ].sd); } /* 48bit far pointer. Do not staticize - used from apm_setup.s */ struct addr48 { u_long offset; u_short segment; } apm_addr; static int apm_errno; static int apm_int(u_long *eax, u_long *ebx, u_long *ecx, u_long *edx) { struct apm_bios_arg apa; int cf; apa.eax = *eax; apa.ebx = *ebx; apa.ecx = *ecx; apa.edx = *edx; cf = apm_bios_call(&apa); *eax = apa.eax; *ebx = apa.ebx; *ecx = apa.ecx; *edx = apa.edx; apm_errno = ((*eax) >> 8) & 0xff; return cf; } /* enable/disable power management */ static int apm_enable_disable_pm(int enable) { struct apm_softc *sc = &apm_softc; u_long eax, ebx, ecx, edx; eax = (APM_BIOS << 8) | APM_ENABLEDISABLEPM; if (sc->intversion >= INTVERSION(1, 1)) ebx = PMDV_ALLDEV; else ebx = 0xffff; /* APM version 1.0 only */ ecx = enable; edx = 0; return apm_int(&eax, &ebx, &ecx, &edx); } static void apm_driver_version(int version) { u_long eax, ebx, ecx, edx; /* First try APM 1.2 */ eax = (APM_BIOS << 8) | APM_DRVVERSION; ebx = 0x0; ecx = version; edx = 0; if(!apm_int(&eax, &ebx, &ecx, &edx)) apm_version = eax & 0xffff; } /* engage/disengage power management (APM 1.1 or later) */ static int apm_engage_disengage_pm(int engage) { u_long eax, ebx, ecx, edx; eax = (APM_BIOS << 8) | APM_ENGAGEDISENGAGEPM; ebx = PMDV_ALLDEV; ecx = engage; edx = 0; return(apm_int(&eax, &ebx, &ecx, &edx)); } /* get PM event */ static u_int apm_getevent(void) { u_long eax, ebx, ecx, edx; eax = (APM_BIOS << 8) | APM_GETPMEVENT; ebx = 0; ecx = 0; edx = 0; if (apm_int(&eax, &ebx, &ecx, &edx)) return PMEV_NOEVENT; return ebx & 0xffff; } /* suspend entire system */ static int apm_suspend_system(int state) { u_long eax, ebx, ecx, edx; eax = (APM_BIOS << 8) | APM_SETPWSTATE; ebx = PMDV_ALLDEV; ecx = state; edx = 0; if (apm_int(&eax, &ebx, &ecx, &edx)) { printf("Entire system suspend failure: errcode = %ld\n", 0xff & (eax >> 8)); return 1; } return 0; } /* Display control */ /* * Experimental implementation: My laptop machine can't handle this function * If your laptop can control the display via APM, please inform me. * HOSOKAWA, Tatsumi */ static int apm_display(int newstate) { u_long eax, ebx, ecx, edx; eax = (APM_BIOS << 8) | APM_SETPWSTATE; ebx = PMDV_DISP0; ecx = newstate ? PMST_APMENABLED:PMST_SUSPEND; edx = 0; if (apm_int(&eax, &ebx, &ecx, &edx)) { printf("Display off failure: errcode = %ld\n", 0xff & (eax >> 8)); return 1; } return 0; } /* * Turn off the entire system. */ static void apm_power_off(int howto, void *junk) { u_long eax, ebx, ecx, edx; /* Not halting powering off, or not active */ if (!(howto & RB_POWEROFF) || !apm_softc.active) return; eax = (APM_BIOS << 8) | APM_SETPWSTATE; ebx = PMDV_ALLDEV; ecx = PMST_OFF; edx = 0; apm_int(&eax, &ebx, &ecx, &edx); } /* APM Battery low handler */ static void apm_battery_low(void) { printf("\007\007 * * * BATTERY IS LOW * * * \007\007"); } /* APM hook manager */ static struct apmhook * apm_add_hook(struct apmhook **list, struct apmhook *ah) { int s; struct apmhook *p, *prev; #ifdef APM_DEBUG printf("Add hook \"%s\"\n", ah->ah_name); #endif s = splhigh(); if (ah == NULL) panic("illegal apm_hook!"); prev = NULL; for (p = *list; p != NULL; prev = p, p = p->ah_next) if (p->ah_order > ah->ah_order) break; if (prev == NULL) { ah->ah_next = *list; *list = ah; } else { ah->ah_next = prev->ah_next; prev->ah_next = ah; } splx(s); return ah; } static void apm_del_hook(struct apmhook **list, struct apmhook *ah) { int s; struct apmhook *p, *prev; s = splhigh(); prev = NULL; for (p = *list; p != NULL; prev = p, p = p->ah_next) if (p == ah) goto deleteit; panic("Tried to delete unregistered apm_hook."); goto nosuchnode; deleteit: if (prev != NULL) prev->ah_next = p->ah_next; else *list = p->ah_next; nosuchnode: splx(s); } /* APM driver calls some functions automatically */ static void apm_execute_hook(struct apmhook *list) { struct apmhook *p; for (p = list; p != NULL; p = p->ah_next) { #ifdef APM_DEBUG printf("Execute APM hook \"%s.\"\n", p->ah_name); #endif if ((*(p->ah_fun))(p->ah_arg)) printf("Warning: APM hook \"%s\" failed", p->ah_name); } } /* establish an apm hook */ struct apmhook * apm_hook_establish(int apmh, struct apmhook *ah) { if (apmh < 0 || apmh >= NAPM_HOOK) return NULL; return apm_add_hook(&hook[apmh], ah); } /* disestablish an apm hook */ void apm_hook_disestablish(int apmh, struct apmhook *ah) { if (apmh < 0 || apmh >= NAPM_HOOK) return; apm_del_hook(&hook[apmh], ah); } static struct timeval suspend_time; static struct timeval diff_time; static int apm_default_resume(void *arg) { int pl; u_int second, minute, hour; struct timeval resume_time, tmp_time; /* modified for adjkerntz */ pl = splsoftclock(); inittodr(0); /* adjust time to RTC */ microtime(&resume_time); getmicrotime(&tmp_time); timevaladd(&tmp_time, &diff_time); #ifdef FIXME /* XXX THIS DOESN'T WORK!!! */ time = tmp_time; #endif #ifdef APM_FIXUP_CALLTODO /* Calculate the delta time suspended */ timevalsub(&resume_time, &suspend_time); /* Fixup the calltodo list with the delta time. */ adjust_timeout_calltodo(&resume_time); #endif /* APM_FIXUP_CALLTODOK */ splx(pl); #ifndef APM_FIXUP_CALLTODO second = resume_time.tv_sec - suspend_time.tv_sec; #else /* APM_FIXUP_CALLTODO */ /* * We've already calculated resume_time to be the delta between * the suspend and the resume. */ second = resume_time.tv_sec; #endif /* APM_FIXUP_CALLTODO */ hour = second / 3600; second %= 3600; minute = second / 60; second %= 60; log(LOG_NOTICE, "resumed from suspended mode (slept %02d:%02d:%02d)\n", hour, minute, second); return 0; } static int apm_default_suspend(void *arg) { int pl; pl = splsoftclock(); microtime(&diff_time); inittodr(0); microtime(&suspend_time); timevalsub(&diff_time, &suspend_time); splx(pl); return 0; } static void apm_processevent(void); /* * Public interface to the suspend/resume: * * Execute suspend and resume hook before and after sleep, respectively. * */ void apm_suspend(int state) { struct apm_softc *sc = &apm_softc; + int error; if (!sc) return; if (sc->initialized) { + error = DEVICE_SUSPEND(root_bus); + if (error) + return; /* XXX no error reporting */ apm_execute_hook(hook[APM_HOOK_SUSPEND]); if (apm_suspend_system(state) == 0) apm_processevent(); else /* Failure, 'resume' the system again */ apm_execute_hook(hook[APM_HOOK_RESUME]); } } void apm_resume(void) { struct apm_softc *sc = &apm_softc; if (!sc) return; - if (sc->initialized) + if (sc->initialized) { + DEVICE_RESUME(root_bus); apm_execute_hook(hook[APM_HOOK_RESUME]); + } } /* get APM information */ static int apm_get_info(apm_info_t aip) { struct apm_softc *sc = &apm_softc; u_long eax, ebx, ecx, edx; eax = (APM_BIOS << 8) | APM_GETPWSTATUS; ebx = PMDV_ALLDEV; ecx = 0; edx = 0xffff; /* default to unknown battery time */ if (apm_int(&eax, &ebx, &ecx, &edx)) return 1; aip->ai_infoversion = 1; aip->ai_acline = (ebx >> 8) & 0xff; aip->ai_batt_stat = ebx & 0xff; aip->ai_batt_life = ecx & 0xff; aip->ai_major = (u_int)sc->majorversion; aip->ai_minor = (u_int)sc->minorversion; aip->ai_status = (u_int)sc->active; edx &= 0xffff; if (edx == 0xffff) /* Time is unknown */ aip->ai_batt_time = -1; else if (edx & 0x8000) /* Time is in minutes */ aip->ai_batt_time = (edx & 0x7fff) * 60; else /* Time is in seconds */ aip->ai_batt_time = edx; eax = (APM_BIOS << 8) | APM_GETCAPABILITIES; ebx = 0; ecx = 0; edx = 0; if (apm_int(&eax, &ebx, &ecx, &edx)) { aip->ai_batteries = -1; /* Unknown */ aip->ai_capabilities = 0xff00; /* Unknown, with no bits set */ } else { aip->ai_batteries = ebx & 0xff; aip->ai_capabilities = ecx & 0xf; } bzero(aip->ai_spare, sizeof aip->ai_spare); return 0; } /* inform APM BIOS that CPU is idle */ void apm_cpu_idle(void) { struct apm_softc *sc = &apm_softc; if (sc->active) { u_long eax, ebx, ecx, edx; eax = (APM_BIOS <<8) | APM_CPUIDLE; edx = ecx = ebx = 0; apm_int(&eax, &ebx, &ecx, &edx); } /* * Some APM implementation halts CPU in BIOS, whenever * "CPU-idle" function are invoked, but swtch() of * FreeBSD halts CPU, therefore, CPU is halted twice * in the sched loop. It makes the interrupt latency * terribly long and be able to cause a serious problem * in interrupt processing. We prevent it by removing * "hlt" operation from swtch() and managed it under * APM driver. */ if (!sc->active || sc->always_halt_cpu) __asm("hlt"); /* wait for interrupt */ } /* inform APM BIOS that CPU is busy */ void apm_cpu_busy(void) { struct apm_softc *sc = &apm_softc; /* * The APM specification says this is only necessary if your BIOS * slows down the processor in the idle task, otherwise it's not * necessary. */ if (sc->slow_idle_cpu && sc->active) { u_long eax, ebx, ecx, edx; eax = (APM_BIOS <<8) | APM_CPUBUSY; edx = ecx = ebx = 0; apm_int(&eax, &ebx, &ecx, &edx); } } /* * APM timeout routine: * * This routine is automatically called by timer once per second. */ static void apm_timeout(void *dummy) { struct apm_softc *sc = &apm_softc; apm_processevent(); if (sc->active == 1) /* Run slightly more oftan than 1 Hz */ apm_timeout_ch = timeout(apm_timeout, NULL, hz - 1 ); } /* enable APM BIOS */ static void apm_event_enable(void) { struct apm_softc *sc = &apm_softc; #ifdef APM_DEBUG printf("called apm_event_enable()\n"); #endif if (sc->initialized) { sc->active = 1; apm_timeout(sc); } } /* disable APM BIOS */ static void apm_event_disable(void) { struct apm_softc *sc = &apm_softc; #ifdef APM_DEBUG printf("called apm_event_disable()\n"); #endif if (sc->initialized) { untimeout(apm_timeout, NULL, apm_timeout_ch); sc->active = 0; } } /* halt CPU in scheduling loop */ static void apm_halt_cpu(void) { struct apm_softc *sc = &apm_softc; if (sc->initialized) sc->always_halt_cpu = 1; } /* don't halt CPU in scheduling loop */ static void apm_not_halt_cpu(void) { struct apm_softc *sc = &apm_softc; if (sc->initialized) sc->always_halt_cpu = 0; } /* device driver definitions */ -static int apmprobe (struct isa_device *); -static int apmattach(struct isa_device *); -struct isa_driver apmdriver = { apmprobe, apmattach, "apm" }; /* * probe APM (dummy): * * APM probing routine is placed on locore.s and apm_init.S because * this process forces the CPU to turn to real mode or V86 mode. * Current version uses real mode, but in a future version, we want * to use V86 mode in APM initialization. * * XXX If VM86 is defined, we do. */ static int -apmprobe(struct isa_device *dvp) +apm_probe(device_t dev) { #ifdef VM86 struct vm86frame vmf; int i; #endif + int flags; - if ( dvp->id_unit > 0 ) { + device_set_desc(dev, "APM BIOS"); + + if ( device_get_unit(dev) > 0 ) { printf("apm: Only one APM driver supported.\n"); - return 0; + return ENXIO; } + if (resource_int_value("apm", 0, "flags", &flags) != 0) + flags = 0; + #ifdef VM86 bzero(&vmf, sizeof(struct vm86frame)); /* safety */ vmf.vmf_ax = (APM_BIOS << 8) | APM_INSTCHECK; vmf.vmf_bx = 0; if (((i = vm86_intcall(SYSTEM_BIOS, &vmf)) == 0) && !(vmf.vmf_eflags & PSL_C) && (vmf.vmf_bx == 0x504d)) { apm_version = vmf.vmf_ax; apm_flags = vmf.vmf_cx; vmf.vmf_ax = (APM_BIOS << 8) | APM_PROT32CONNECT; vmf.vmf_bx = 0; if (((i = vm86_intcall(SYSTEM_BIOS, &vmf)) == 0) && !(vmf.vmf_eflags & PSL_C)) { apm_cs32_base = vmf.vmf_ax; apm_cs_entry = vmf.vmf_ebx; apm_cs16_base = vmf.vmf_cx; apm_ds_base = vmf.vmf_dx; apm_cs32_limit = vmf.vmf_si; if (apm_version >= 0x0102) apm_cs16_limit = (vmf.esi.r_ex >> 16); apm_ds_limit = vmf.vmf_di; #ifdef APM_DEBUG printf("apm: BIOS probe/32-bit connect successful\n"); #endif } else { /* XXX constant typo! */ if (vmf.vmf_ah == APME_PROT32NOTDUPPORTED) { apm_version = APMINI_NOT32BIT; } else { apm_version = APMINI_CONNECTERR; } #ifdef APM_DEBUG printf("apm: BIOS 32-bit connect failed: error 0x%x carry %d ah 0x%x\n", i, (vmf.vmf_eflags & PSL_C) ? 1 : 0, vmf.vmf_ah); #endif } } else { apm_version = APMINI_CANTFIND; #ifdef APM_DEBUG printf("apm: BIOS probe failed: error 0x%x carry %d bx 0x%x\n", i, (vmf.vmf_eflags & PSL_C) ? 1 : 0, vmf.vmf_bx); #endif } #endif bzero(&apm_softc, sizeof(apm_softc)); switch (apm_version) { case APMINI_CANTFIND: /* silent */ return 0; case APMINI_NOT32BIT: printf("apm: 32bit connection is not supported.\n"); return 0; case APMINI_CONNECTERR: printf("apm: 32-bit connection error.\n"); return 0; } - if (dvp->id_flags & 0x20) + if (flags & 0x20) statclock_disable = 1; - return -1; + return 0; } /* Process APM event */ static void apm_processevent(void) { int apm_event; #ifdef APM_DEBUG # define OPMEV_DEBUGMESSAGE(symbol) case symbol: \ printf("Received APM Event: " #symbol "\n"); #else # define OPMEV_DEBUGMESSAGE(symbol) case symbol: #endif do { apm_event = apm_getevent(); switch (apm_event) { OPMEV_DEBUGMESSAGE(PMEV_STANDBYREQ); apm_suspend(PMST_STANDBY); break; OPMEV_DEBUGMESSAGE(PMEV_SUSPENDREQ); apm_suspend(PMST_SUSPEND); break; OPMEV_DEBUGMESSAGE(PMEV_USERSUSPENDREQ); apm_suspend(PMST_SUSPEND); break; OPMEV_DEBUGMESSAGE(PMEV_CRITSUSPEND); apm_suspend(PMST_SUSPEND); break; OPMEV_DEBUGMESSAGE(PMEV_NORMRESUME); apm_resume(); break; OPMEV_DEBUGMESSAGE(PMEV_CRITRESUME); apm_resume(); break; OPMEV_DEBUGMESSAGE(PMEV_STANDBYRESUME); apm_resume(); break; OPMEV_DEBUGMESSAGE(PMEV_BATTERYLOW); apm_battery_low(); apm_suspend(PMST_SUSPEND); break; OPMEV_DEBUGMESSAGE(PMEV_POWERSTATECHANGE); break; OPMEV_DEBUGMESSAGE(PMEV_UPDATETIME); inittodr(0); /* adjust time to RTC */ break; case PMEV_NOEVENT: break; default: printf("Unknown Original APM Event 0x%x\n", apm_event); break; } } while (apm_event != PMEV_NOEVENT); } /* * Attach APM: * * Initialize APM driver (APM BIOS itself has been initialized in locore.s) */ static int -apmattach(struct isa_device *dvp) +apm_attach(device_t dev) { #define APM_KERNBASE KERNBASE struct apm_softc *sc = &apm_softc; + int flags; + if (resource_int_value("apm", 0, "flags", &flags) != 0) + flags = 0; + sc->initialized = 0; /* Must be externally enabled */ sc->active = 0; /* setup APM parameters */ sc->cs16_base = (apm_cs16_base << 4) + APM_KERNBASE; sc->cs32_base = (apm_cs32_base << 4) + APM_KERNBASE; sc->ds_base = (apm_ds_base << 4) + APM_KERNBASE; sc->cs32_limit = apm_cs32_limit - 1; if (apm_cs16_limit == 0) apm_cs16_limit = apm_cs32_limit; sc->cs16_limit = apm_cs16_limit - 1; sc->ds_limit = apm_ds_limit - 1; sc->cs_entry = apm_cs_entry; /* Always call HLT in idle loop */ sc->always_halt_cpu = 1; sc->slow_idle_cpu = ((apm_flags & APM_CPUIDLE_SLOW) != 0); sc->disabled = ((apm_flags & APM_DISABLED) != 0); sc->disengaged = ((apm_flags & APM_DISENGAGED) != 0); /* print bootstrap messages */ #ifdef APM_DEBUG printf("apm: APM BIOS version %04x\n", apm_version); printf("apm: Code32 0x%08x, Code16 0x%08x, Data 0x%08x\n", sc->cs32_base, sc->cs16_base, sc->ds_base); printf("apm: Code entry 0x%08x, Idling CPU %s, Management %s\n", sc->cs_entry, is_enabled(sc->slow_idle_cpu), is_enabled(!sc->disabled)); printf("apm: CS32_limit=0x%x, CS16_limit=0x%x, DS_limit=0x%x\n", (u_short)sc->cs32_limit, (u_short)sc->cs16_limit, (u_short)sc->ds_limit); #endif /* APM_DEBUG */ #if 0 /* Workaround for some buggy APM BIOS implementations */ sc->cs_limit = 0xffff; sc->ds_limit = 0xffff; #endif /* setup GDT */ setup_apm_gdt(sc->cs32_base, sc->cs16_base, sc->ds_base, sc->cs32_limit, sc->cs16_limit, sc->ds_limit); /* setup entry point 48bit pointer */ apm_addr.segment = GSEL(GAPMCODE32_SEL, SEL_KPL); apm_addr.offset = sc->cs_entry; - if ((dvp->id_flags & 0x10)) { - if ((dvp->id_flags & 0xf) >= 0x2) { + if ((flags & 0x10)) { + if ((flags & 0xf) >= 0x2) { apm_driver_version(0x102); } - if (!apm_version && (dvp->id_flags & 0xf) >= 0x1) { + if (!apm_version && (flags & 0xf) >= 0x1) { apm_driver_version(0x101); } } else { apm_driver_version(0x102); if (!apm_version) apm_driver_version(0x101); } if (!apm_version) apm_version = 0x100; sc->minorversion = ((apm_version & 0x00f0) >> 4) * 10 + ((apm_version & 0x000f) >> 0); sc->majorversion = ((apm_version & 0xf000) >> 12) * 10 + ((apm_version & 0x0f00) >> 8); sc->intversion = INTVERSION(sc->majorversion, sc->minorversion); #ifdef APM_DEBUG if (sc->intversion >= INTVERSION(1, 1)) printf("apm: Engaged control %s\n", is_enabled(!sc->disengaged)); #endif printf("apm: found APM BIOS version %d.%d\n", sc->majorversion, sc->minorversion); #ifdef APM_DEBUG printf("apm: Slow Idling CPU %s\n", is_enabled(sc->slow_idle_cpu)); #endif /* enable power management */ if (sc->disabled) { if (apm_enable_disable_pm(1)) { #ifdef APM_DEBUG printf("apm: *Warning* enable function failed! [%x]\n", apm_errno); #endif } } /* engage power managment (APM 1.1 or later) */ if (sc->intversion >= INTVERSION(1, 1) && sc->disengaged) { if (apm_engage_disengage_pm(1)) { #ifdef APM_DEBUG printf("apm: *Warning* engage function failed err=[%x]", apm_errno); printf(" (Docked or using external power?).\n"); #endif } } /* default suspend hook */ sc->sc_suspend.ah_fun = apm_default_suspend; sc->sc_suspend.ah_arg = sc; sc->sc_suspend.ah_name = "default suspend"; sc->sc_suspend.ah_order = APM_MAX_ORDER; /* default resume hook */ sc->sc_resume.ah_fun = apm_default_resume; sc->sc_resume.ah_arg = sc; sc->sc_resume.ah_name = "default resume"; sc->sc_resume.ah_order = APM_MIN_ORDER; apm_hook_establish(APM_HOOK_SUSPEND, &sc->sc_suspend); apm_hook_establish(APM_HOOK_RESUME , &sc->sc_resume); apm_event_enable(); /* Power the system off using APM */ at_shutdown_pri(apm_power_off, NULL, SHUTDOWN_FINAL, SHUTDOWN_PRI_LAST); sc->initialized = 1; #ifdef DEVFS sc->sc_devfs_token = devfs_add_devswf(&apm_cdevsw, 0, DV_CHR, 0, 0, 0600, "apm"); #endif return 0; } static int apmopen(dev_t dev, int flag, int fmt, struct proc *p) { struct apm_softc *sc = &apm_softc; if (minor(dev) != 0 || !sc->initialized) return (ENXIO); return 0; } static int apmclose(dev_t dev, int flag, int fmt, struct proc *p) { return 0; } static int apmioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p) { struct apm_softc *sc = &apm_softc; int error = 0; int newstate; if (minor(dev) != 0 || !sc->initialized) return (ENXIO); #ifdef APM_DEBUG printf("APM ioctl: cmd = 0x%x\n", cmd); #endif switch (cmd) { case APMIO_SUSPEND: if (sc->active) apm_suspend(PMST_SUSPEND); else error = EINVAL; break; case APMIO_STANDBY: if (sc->active) apm_suspend(PMST_STANDBY); else error = EINVAL; break; case APMIO_GETINFO_OLD: { struct apm_info info; apm_info_old_t aiop; if (apm_get_info(&info)) error = ENXIO; aiop = (apm_info_old_t)addr; aiop->ai_major = info.ai_major; aiop->ai_minor = info.ai_minor; aiop->ai_acline = info.ai_acline; aiop->ai_batt_stat = info.ai_batt_stat; aiop->ai_batt_life = info.ai_batt_life; aiop->ai_status = info.ai_status; } break; case APMIO_GETINFO: if (apm_get_info((apm_info_t)addr)) error = ENXIO; break; case APMIO_ENABLE: apm_event_enable(); break; case APMIO_DISABLE: apm_event_disable(); break; case APMIO_HALTCPU: apm_halt_cpu(); break; case APMIO_NOTHALTCPU: apm_not_halt_cpu(); break; case APMIO_DISPLAY: newstate = *(int *)addr; if (apm_display(newstate)) error = ENXIO; break; case APMIO_BIOS: if (apm_bios_call((struct apm_bios_arg*)addr) == 0) ((struct apm_bios_arg*)addr)->eax &= 0xff; break; default: error = EINVAL; break; } return error; } +static device_method_t apm_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, apm_probe), + DEVMETHOD(device_attach, apm_attach), -static apm_devsw_installed = 0; + { 0, 0 } +}; -static void -apm_drvinit(void *unused) -{ - dev_t dev; +static driver_t apm_driver = { + "apm", + apm_methods, + DRIVER_TYPE_MISC, + 1, /* no softc (XXX) */ +}; - if( ! apm_devsw_installed ) { - dev = makedev(CDEV_MAJOR,0); - cdevsw_add(&dev,&apm_cdevsw,NULL); - apm_devsw_installed = 1; - } -} +static devclass_t apm_devclass; -SYSINIT(apmdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,apm_drvinit,NULL) +CDEV_DRIVER_MODULE(apm, nexus, apm_driver, apm_devclass, + CDEV_MAJOR, apm_cdevsw, 0, 0); Index: head/sys/i386/conf/GENERIC =================================================================== --- head/sys/i386/conf/GENERIC (revision 45719) +++ head/sys/i386/conf/GENERIC (revision 45720) @@ -1,225 +1,225 @@ # # GENERIC -- Generic machine with WD/AHx/NCR/BTx family disks # # For more information read the handbook part System Administration -> # Configuring the FreeBSD Kernel -> The Configuration File. # The handbook is available in /usr/share/doc/handbook or online as # latest version from the FreeBSD World Wide Web server # # # An exhaustive list of options and more detailed explanations of the # device lines is present in the ./LINT configuration file. If you are # in doubt as to the purpose or necessity of a line, check first in LINT. # -# $Id: GENERIC,v 1.160 1999/04/16 16:17:05 n_hibma Exp $ +# $Id: GENERIC,v 1.161 1999/04/16 18:27:18 jkh Exp $ machine "i386" cpu "I386_CPU" cpu "I486_CPU" cpu "I586_CPU" cpu "I686_CPU" ident GENERIC maxusers 32 #makeoptions DEBUG="-g" #Build kernel with gdb(1) debug symbols options MATH_EMULATE #Support for x87 emulation options INET #InterNETworking options FFS #Berkeley Fast Filesystem options FFS_ROOT #FFS usable as root device [keep this!] options MFS #Memory Filesystem options MFS_ROOT #MFS usable as root device, "MFS" req'ed options NFS #Network Filesystem options NFS_ROOT #NFS usable as root device, "NFS" req'ed options MSDOSFS #MSDOS Filesystem options "CD9660" #ISO 9660 Filesystem options "CD9660_ROOT" #CD-ROM usable as root. "CD9660" req'ed options PROCFS #Process filesystem options "COMPAT_43" #Compatible with BSD 4.3 [KEEP THIS!] options SCSI_DELAY=15000 #Be pessimistic about Joe SCSI device options UCONSOLE #Allow users to grab the console options FAILSAFE #Be conservative options USERCONFIG #boot -c editor options VISUAL_USERCONFIG #visual boot -c editor config kernel root on wd0 # To make an SMP kernel, the next two are needed #options SMP # Symmetric MultiProcessor Kernel #options APIC_IO # Symmetric (APIC) I/O # Optionally these may need tweaked, (defaults shown): #options NCPU=2 # number of CPUs #options NBUS=4 # number of busses #options NAPIC=1 # number of IO APICs #options NINTR=24 # number of INTs -controller isa0 -controller pnp0 # PnP support for ISA -controller eisa0 -controller pci0 +controller isa0 at nexus? +#controller pnp0 # PnP support for ISA +#controller eisa0 +controller pci0 at nexus? controller fdc0 at isa? port "IO_FD1" bio irq 6 drq 2 disk fd0 at fdc0 drive 0 disk fd1 at fdc0 drive 1 controller wdc0 at isa? port "IO_WD1" bio irq 14 disk wd0 at wdc0 drive 0 disk wd1 at wdc0 drive 1 controller wdc1 at isa? port "IO_WD2" bio irq 15 disk wd2 at wdc1 drive 0 disk wd3 at wdc1 drive 1 device wcd0 #IDE CD-ROM device wfd0 #IDE Floppy (e.g. LS-120) # A single entry for any of these controllers (ncr, ahb, ahc) is # sufficient for any number of installed devices. controller ncr0 controller ahb0 controller ahc0 controller isp0 # This controller offers a number of configuration options, too many to # document here - see the LINT file in this directory and look up the # dpt0 entry there for much fuller documentation on this. controller dpt0 controller adv0 at isa? port ? cam irq ? controller adw0 controller bt0 at isa? port ? cam irq ? controller aha0 at isa? port ? cam irq ? controller scbus0 device da0 device sa0 device pass0 device cd0 #Only need one of these, the code dynamically grows device wt0 at isa? port 0x300 bio irq 5 drq 1 device mcd0 at isa? port 0x300 bio irq 10 controller matcd0 at isa? port 0x230 bio device scd0 at isa? port 0x230 bio # atkbdc0 controlls both the keyboard and the PS/2 mouse -controller atkbdc0 at isa? port IO_KBD tty -device atkbd0 at isa? tty irq 1 -device psm0 at isa? tty irq 12 +controller atkbdc0 at isa? port IO_KBD +device atkbd0 at atkbdc? tty irq 1 +device psm0 at atkbdc? tty irq 12 device vga0 at isa? port ? conflicts # splash screen/screen saver pseudo-device splash # syscons is the default console driver, resembling an SCO console device sc0 at isa? tty # Enable this and PCVT_FREEBSD for pcvt vt220 compatible console driver #device vt0 at isa? tty #options XSERVER # support for X server #options FAT_CURSOR # start with block cursor # If you have a ThinkPAD, uncomment this along with the rest of the PCVT lines #options PCVT_SCANSET=2 # IBM keyboards are non-std -device npx0 at isa? port IO_NPX irq 13 +device npx0 at nexus? port IO_NPX irq 13 # # Laptop support (see LINT for more options) # -device apm0 at isa? disable flags 0x31 # Advanced Power Management +device apm0 at nexus? disable flags 0x31 # Advanced Power Management # PCCARD (PCMCIA) support #controller card0 #device pcic0 at card? #device pcic1 at card? device sio0 at isa? port "IO_COM1" flags 0x10 tty irq 4 device sio1 at isa? port "IO_COM2" tty irq 3 device sio2 at isa? disable port "IO_COM3" tty irq 5 device sio3 at isa? disable port "IO_COM4" tty irq 9 # Parallel port device ppc0 at isa? port? tty irq 7 controller ppbus0 device lpt0 at ppbus? device plip0 at ppbus? device ppi0 at ppbus? #controller vpo0 at ppbus? # # The following Ethernet NICs are all PCI devices. # device ax0 # ASIX AX88140A device de0 # DEC/Intel DC21x4x (``Tulip'') device fxp0 # Intel EtherExpress PRO/100B (82557, 82558) device mx0 # Macronix 98713/98715/98725 (``PMAC'') device pn0 # Lite-On 82c168/82c169 (``PNIC'') device rl0 # RealTek 8129/8139 device tl0 # Texas Instruments ThunderLAN device tx0 # SMC 9432TX (83c170 ``EPIC'') device vr0 # VIA Rhine, Rhine II device vx0 # 3Com 3c590, 3c595 (``Vortex'') device wb0 # Winbond W89C840F device xl0 # 3Com 3c90x (``Boomerang'', ``Cyclone'') # Order is important here due to intrusive probes, do *not* alphabetize # this list of network interfaces until the probes have been fixed. # Right now it appears that the ie0 must be probed before ep0. See # revision 1.20 of this file. device ed0 at isa? port 0x280 net irq 10 iomem 0xd8000 device ie0 at isa? port 0x300 net irq 10 iomem 0xd0000 device ep0 at isa? port 0x300 net irq 10 device ex0 at isa? port? net irq? device fe0 at isa? port 0x300 net irq ? device le0 at isa? port 0x300 net irq 5 iomem 0xd0000 device lnc0 at isa? port 0x280 net irq 10 drq 0 -device ze0 at isa? port 0x300 net irq 10 iomem 0xd8000 -device zp0 at isa? port 0x300 net irq 10 iomem 0xd8000 +#device ze0 at isa? port 0x300 net irq 10 iomem 0xd8000 +#device zp0 at isa? port 0x300 net irq 10 iomem 0xd8000 device cs0 at isa? port 0x300 net irq ? pseudo-device loop pseudo-device ether pseudo-device sl 1 pseudo-device ppp 1 pseudo-device tun 1 pseudo-device pty 16 pseudo-device gzip # Exec gzipped a.out's # KTRACE enables the system-call tracing facility ktrace(2). # This adds 4 KB bloat to your kernel, and slightly increases # the costs of each syscall. options KTRACE #kernel tracing # This provides support for System V shared memory and message queues. # options SYSVSHM options SYSVMSG options SYSVSEM # The `bpfilter' pseudo-device enables the Berkeley Packet Filter. Be # aware of the legal and administrative consequences of enabling this # option. The number of devices determines the maximum number of # simultaneous BPF clients programs runnable. #pseudo-device bpfilter 4 #Berkeley packet filter # USB support #controller uhci0 #controller ohci0 #controller usb0 # # for the moment we have to specify the priorities of the device # drivers explicitly by the ordering in the list below. This will # be changed in the future. # #device ums0 #device ukbd0 #device ulpt0 #device uhid0 #device ugen0 Index: head/sys/i386/conf/LINT =================================================================== --- head/sys/i386/conf/LINT (revision 45719) +++ head/sys/i386/conf/LINT (revision 45720) @@ -1,2130 +1,2130 @@ # # LINT -- config file for checking all the sources, tries to pull in # as much of the source tree as it can. # -# $Id: LINT,v 1.581 1999/04/14 16:54:00 peter Exp $ +# $Id: LINT,v 1.582 1999/04/16 16:17:04 n_hibma Exp $ # # NB: You probably don't want to try running a kernel built from this # file. Instead, you should start from GENERIC, and add options from # this file as required. # # # This directive is mandatory; it defines the architecture to be # configured for; in this case, the 386 family based IBM-PC and # compatibles. # machine "i386" # # This is the ``identification'' of the kernel. Usually this should # be the same as the name of your kernel. # ident LINT # # The `maxusers' parameter controls the static sizing of a number of # internal system tables by a complicated formula defined in param.c. # maxusers 10 # # The `makeoptions' parameter allows variables to be passed to the # generated Makefile in the build area. DEBUG happens to be magic. # The following is equivalent to 'config -g KERNELNAME' and creates # 'kernel.debug' compiled with -g debugging as well as a normal # 'kernel'. Use 'make install.debug' to install the debug kernel # but that isn't normally necessary as the debug symbols are not loaded # by the kernel and are not useful there anyway. # #makeoptions DEBUG="-g" #Build kernel with gdb(1) debug symbols # # Certain applications can grow to be larger than the 128M limit # that FreeBSD initially imposes. Below are some options to # allow that limit to grow to 256MB, and can be increased further # with changing the parameters. MAXDSIZ is the maximum that the # limit can be set to, and the DFLDSIZ is the default value for # the limit. You might want to set the default lower than the # max, and explicitly set the maximum with a shell command for processes # that regularly exceed the limit like INND. # options "MAXDSIZ=(256*1024*1024)" options "DFLDSIZ=(256*1024*1024)" # When this is set, be extra conservative in various parts of the kernel # and choose functionality over speed (on the widest variety of systems). options FAILSAFE # Options for the VM subsystem #options PQ_NOOPT # No coloring options PQ_LARGECACHE # color for 512k/16k cache #options PQ_HUGECACHE # color for 1024k/16k cache # This allows you to actually store this configuration file into # the kernel binary itself, where it may be later read by saying: # strings -aout -n 3 /kernel | grep ^___ | sed -e 's/^___//' > MYKERNEL # options INCLUDE_CONFIG_FILE # Include this file in kernel # # This directive defines a number of things: # - The compiled kernel is to be called `kernel' # - The root filesystem might be on partition wd0a # - Crash dumps will be written to wd0b, if possible. Specifying the # dump device here is not recommended. Use dumpon(8). # config kernel root on wd0 dumps on wd0 ##################################################################### # SMP OPTIONS: # # SMP enables building of a Symmetric MultiProcessor Kernel. # APIC_IO enables the use of the IO APIC for Symmetric I/O. # NCPU sets the number of CPUs, defaults to 2. # NBUS sets the number of busses, defaults to 4. # NAPIC sets the number of IO APICs on the motherboard, defaults to 1. # NINTR sets the total number of INTs provided by the motherboard. # # Notes: # # An SMP kernel will ONLY run on an Intel MP spec. qualified motherboard. # # Be sure to disable 'cpu "I386_CPU"' && 'cpu "I486_CPU"' for SMP kernels. # # Check the 'Rogue SMP hardware' section to see if additional options # are required by your hardware. # # Mandatory: options SMP # Symmetric MultiProcessor Kernel options APIC_IO # Symmetric (APIC) I/O # Optional, these are the defaults plus 1: options NCPU=5 # number of CPUs options NBUS=5 # number of busses options NAPIC=2 # number of IO APICs options NINTR=25 # number of INTs # # Rogue SMP hardware: # # Bridged PCI cards: # # The MP tables of most of the current generation MP motherboards # do NOT properly support bridged PCI cards. To use one of these # cards you should refer to ??? ##################################################################### # CPU OPTIONS # # You must specify at least one CPU (the one you intend to run on); # deleting the specification for CPUs you don't need to use may make # parts of the system run faster. This is especially true removing # I386_CPU. # cpu "I386_CPU" cpu "I486_CPU" cpu "I586_CPU" # aka Pentium(tm) cpu "I686_CPU" # aka Pentium Pro(tm) # # Options for CPU features. # # CPU_BLUELIGHTNING_FPU_OP_CACHE enables FPU operand cache on IBM # BlueLightning CPU. It works only with Cyrix FPU, and this option # should not be used with Intel FPU. # # CPU_BLUELIGHTNING_3X enables triple-clock mode on IBM Blue Lightning # CPU if CPU supports it. The default is double-clock mode on # BlueLightning CPU box. # # CPU_BTB_EN enables branch target buffer on Cyrix 5x86 (NOTE 1). # # CPU_DIRECT_MAPPED_CACHE sets L1 cache of Cyrix 486DLC CPU in direct # mapped mode. Default is 2-way set associative mode. # # CPU_CYRIX_NO_LOCK enables weak locking for the entire address space # of Cyrix 6x86 and 6x86MX CPUs. If this option is not set and # FAILESAFE is defined, NO_LOCK bit of CCR1 is cleared. (NOTE 3) # # CPU_DISABLE_5X86_LSSER disables load store serialize (i.e. enables # reorder). This option should not be used if you use memory mapped # I/O device(s). # # CPU_FASTER_5X86_FPU enables faster FPU exception handler. # # CPU_I486_ON_386 enables CPU cache on i486 based CPU upgrade products # for i386 machines. # # CPU_IORT defines I/O clock delay time (NOTE 1). Default vaules of # I/O clock delay time on Cyrix 5x86 and 6x86 are 0 and 7,respectively # (no clock delay). # # CPU_LOOP_EN prevents flushing the prefetch buffer if the destination # of a jump is already present in the prefetch buffer on Cyrix 5x86(NOTE # 1). # # CPU_RSTK_EN enables return stack on Cyrix 5x86 (NOTE 1). # # CPU_SUSP_HLT enables suspend on HALT. If this option is set, CPU # enters suspend mode following execution of HALT instruction. # # CPU_WT_ALLOC enables write allocation on Cyrix 6x86/6x86MX and AMD # K5/K6/K6-2 cpus. # # CYRIX_CACHE_WORKS enables CPU cache on Cyrix 486 CPUs with cache # flush at hold state. # # CYRIX_CACHE_REALLY_WORKS enables (1) CPU cache on Cyrix 486 CPUs # without cache flush at hold state, and (2) write-back CPU cache on # Cyrix 6x86 whose revision < 2.7 (NOTE 2). # # NO_F00F_HACK disables the hack that prevents Pentiums (and ONLY # Pentiums) from locking up when a LOCK CMPXCHG8B instruction is # executed. This should be included for ALL kernels that won't run # on a Pentium. # # NO_MEMORY_HOLE is an optimisation for systems with AMD K6 processors # which indicates that the 15-16MB range is *definitely* not being # occupied by an ISA memory hole. # # NOTE 1: The options, CPU_BTB_EN, CPU_LOOP_EN, CPU_IORT, # CPU_LOOP_ENand CPU_RSTK_EN should not be used becasue of CPU bugs. # These options may crash your system. # # NOTE 2: If CYRIX_CACHE_REALLY_WORKS is not set, CPU cache is enabled # in write-through mode when revision < 2.7. If revision of Cyrix # 6x86 >= 2.7, CPU cache is always enabled in write-back mode. # # NOTE 3: This option may cause failures for software that requires # locked cycles in order to operate correctly. # options "CPU_BLUELIGHTNING_FPU_OP_CACHE" options "CPU_BLUELIGHTNING_3X" options "CPU_BTB_EN" options "CPU_DIRECT_MAPPED_CACHE" options "CPU_DISABLE_5X86_LSSER" options "CPU_FASTER_5X86_FPU" options "CPU_I486_ON_386" options "CPU_IORT" options "CPU_LOOP_EN" options "CPU_RSTK_EN" options "CPU_SUSP_HLT" options "CPU_WT_ALLOC" options "CYRIX_CACHE_WORKS" options "CYRIX_CACHE_REALLY_WORKS" #options "NO_F00F_HACK" # # A math emulator is mandatory if you wish to run on hardware which # does not have a floating-point processor. Pick either the original, # bogus (but freely-distributable) math emulator, or a much more # fully-featured but GPL-licensed emulator taken from Linux. # options MATH_EMULATE #Support for x87 emulation # Don't enable both of these in a real config. options GPL_MATH_EMULATE #Support for x87 emulation via #new math emulator ##################################################################### # COMPATIBILITY OPTIONS # # Implement system calls compatible with 4.3BSD and older versions of # FreeBSD. You probably do NOT want to remove this as much current code # still relies on the 4.3 emulation. # options "COMPAT_43" # # Statically compile in the i386 a.out LKM compatability support. # Also available as an KLD module. # options LKM # # Allow user-mode programs to manipulate their local descriptor tables. # This option is required for the WINE Windows(tm) emulator, and is # not used by anything else (that we know of). # options USER_LDT #allow user-level control of i386 ldt # # These three options provide support for System V Interface # Definition-style interprocess communication, in the form of shared # memory, semaphores, and message queues, respectively. # options SYSVSHM options SYSVSEM options SYSVMSG # # This option includes a MD5 routine in the kernel, this is used for # various authentication and privacy uses. # options "MD5" # # Allow processes to switch to vm86 mode, as well as enabling direct # user-mode access to the I/O port space. This option is necessary for # the doscmd emulator to run. # options "VM86" ##################################################################### # DEBUGGING OPTIONS # # Enable the kernel debugger. # options DDB # # Don't drop into DDB for a panic. Intended for unattended operation # where you may want to drop to DDB from the console, but still want # the machine to recover from a panic # options DDB_UNATTENDED # # If using GDB remote mode to debug the kernel, there's a non-standard # extension to the remote protocol that can be used to use the serial # port as both the debugging port and the system console. It's non- # standard and you're on your own if you enable it. See also the # "remotechat" variables in the FreeBSD specific version of gdb. # options GDB_REMOTE_CHAT # # KTRACE enables the system-call tracing facility ktrace(2). # options KTRACE #kernel tracing # # The INVARIANTS option is used in a number of source files to enable # extra sanity checking of internal structures. This support is not # enabled by default because of the extra time it would take to check # for these conditions, which can only occur as a result of # programming errors. # options INVARIANTS # # The INVARIANT_SUPPORT option makes us compile in support for # verifying some of the internal structures. It is a prerequisite for # 'INVARIANTS', as enabling 'INVARIANTS' will make these functions be # called. The intent is that you can set 'INVARIANTS' for single # source files (by changing the source file or specifying it on the # command line) if you have 'INVARIANT_SUPPORT' enabled. # options INVARIANT_SUPPORT # # The DIAGNOSTIC option is used to enable extra debugging information # from some parts of the kernel. As this makes everything more noisy, # it is disabled by default. # options DIAGNOSTIC # # PERFMON causes the driver for Pentium/Pentium Pro performance counters # to be compiled. See perfmon(4) for more information. # options PERFMON # # This option let some drivers co-exist that can't co-exist in a running # system. This is used to be able to compile all kernel code in one go for # quality assurance purposes (like this file, which the option takes it name # from.) # options COMPILING_LINT # XXX - this doesn't belong here. # Allow ordinary users to take the console - this is useful for X. options UCONSOLE # XXX - this doesn't belong here either options USERCONFIG #boot -c editor options INTRO_USERCONFIG #imply -c and show intro screen options VISUAL_USERCONFIG #visual boot -c editor ##################################################################### # NETWORKING OPTIONS # # Protocol families: # Only the INET (Internet) family is officially supported in FreeBSD. # Source code for the NS (Xerox Network Service) is provided for amusement # value. # options INET #Internet communications protocols options IPX #IPX/SPX communications protocols options IPXIP #IPX in IP encapsulation (not available) options IPTUNNEL #IP in IPX encapsulation (not available) options NETATALK #Appletalk communications protocols # These are currently broken but are shipped due to interest. #options NS #Xerox NS protocols # These are currently broken and are no longer shipped due to lack # of interest. #options CCITT #X.25 network layer #options ISO #options TPIP #ISO TP class 4 over IP #options TPCONS #ISO TP class 0 over X.25 #options LLC #X.25 link layer for Ethernets #options HDLC #X.25 link layer for serial lines #options EON #ISO CLNP over IP #options NSIP #XNS over IP # # Network interfaces: # The `loop' pseudo-device is MANDATORY when networking is enabled. # The `ether' pseudo-device provides generic code to handle # Ethernets; it is MANDATORY when a Ethernet device driver is # configured or token-ring is enabled. # The 'fddi' pseudo-device provides generic code to support FDDI. # The `sppp' pseudo-device serves a similar role for certain types # of synchronous PPP links (like `cx', `ar'). # The `sl' pseudo-device implements the Serial Line IP (SLIP) service. # The `ppp' pseudo-device implements the Point-to-Point Protocol. # The `bpfilter' pseudo-device enables the Berkeley Packet Filter. Be # aware of the legal and administrative consequences of enabling this # option. The number of devices determines the maximum number of # simultaneous BPF clients programs runnable. # The `disc' pseudo-device implements a minimal network interface, # which throws away all packets sent and never receives any. It is # included for testing purposes. # The `tun' pseudo-device implements (user-)ppp and nos-tun # The `streams' pseudo-device implements SysVR4 STREAMS emulation. # # The PPP_BSDCOMP option enables support for compress(1) style entire # packet compression, the PPP_DEFLATE is for zlib/gzip style compression. # PPP_FILTER enables code for filtering the ppp data stream and selecting # events for resetting the demand dial activity timer - requires bpfilter. # See pppd(8) for more details. # pseudo-device ether #Generic Ethernet pseudo-device token #Generic TokenRing pseudo-device fddi #Generic FDDI pseudo-device sppp #Generic Synchronous PPP pseudo-device loop #Network loopback device pseudo-device bpfilter 4 #Berkeley packet filter pseudo-device disc #Discard device pseudo-device tun 1 #Tunnel driver (ppp(8), nos-tun(8)) pseudo-device sl 2 #Serial Line IP pseudo-device ppp 2 #Point-to-point protocol pseudo-device streams options PPP_BSDCOMP #PPP BSD-compress support options PPP_DEFLATE #PPP zlib/deflate/gzip support options PPP_FILTER #enable bpf filtering (needs bpfilter) # # Internet family options: # # TCP_COMPAT_42 causes the TCP code to emulate certain bugs present in # 4.2BSD. This option should not be used unless you have a 4.2BSD # machine and TCP connections fail. # # MROUTING enables the kernel multicast packet forwarder, which works # with mrouted(8). # # IPFIREWALL enables support for IP firewall construction, in # conjunction with the `ipfw' program. IPFIREWALL_VERBOSE sends # logged packets to the system logger. IPFIREWALL_VERBOSE_LIMIT # limits the number of times a matching entry can be logged. # # WARNING: IPFIREWALL defaults to a policy of "deny ip from any to any" # and if you do not add other rules during startup to allow access, # YOU WILL LOCK YOURSELF OUT. It is suggested that you set firewall=open # in /etc/rc.conf when first enabling this feature, then refining the # firewall rules in /etc/rc.firewall after you've tested that the new kernel # feature works properly. # # IPFIREWALL_DEFAULT_TO_ACCEPT causes the default rule (at boot) to # allow everything. Use with care, if a cracker can crash your # firewall machine, they can get to your protected machines. However, # if you are using it as an as-needed filter for specific problems as # they arise, then this may be for you. Changing the default to 'allow' # means that you won't get stuck if the kernel and /sbin/ipfw binary get # out of sync. # # IPDIVERT enables the divert IP sockets, used by ``ipfw divert'' # # IPFILTER enables Darren Reed's ipfilter package. # IPFILTER_LOG enables ipfilter's logging. # IPFILTER_LKM enables LKM support for an ipfilter module (untested). # # IPSTEALTH enables code to support stealth forwarding (i.e., forwarding # packets without touching the ttl). This can be useful to hide firewalls # from traceroute and similar tools. # # TCPDEBUG is undocumented. # options "TCP_COMPAT_42" #emulate 4.2BSD TCP bugs options MROUTING # Multicast routing options IPFIREWALL #firewall options IPFIREWALL_VERBOSE #print information about # dropped packets options IPFIREWALL_FORWARD #enable transparent proxy support options "IPFIREWALL_VERBOSE_LIMIT=100" #limit verbosity options IPFIREWALL_DEFAULT_TO_ACCEPT #allow everything by default options IPDIVERT #divert sockets options IPFILTER #kernel ipfilter support options IPFILTER_LOG #ipfilter logging #options IPFILTER_LKM #kernel support for ip_fil.o LKM options IPSTEALTH #support for stealth forwarding options TCPDEBUG # ICMP_BANDLIM enables icmp error response bandwidth limiting. You # typically want this option as it will help protect the machine from # D.O.S. packet attacks. # options "ICMP_BANDLIM" # DUMMYNET enables the "dummynet" bandwidth limiter. You need # IPFIREWALL as well. See the dummynet(4) manpage for more info. # BRIDGE enables bridging between ethernet cards -- see bridge(4). # You can use IPFIREWALL and dummynet together with bridging. options DUMMYNET options BRIDGE # # ATM (HARP version) options # # ATM_CORE includes the base ATM functionality code. This must be included # for ATM support. # # ATM_IP includes support for running IP over ATM. # # At least one (and usually only one) of the following signalling managers # must be included (note that all signalling managers include PVC support): # ATM_SIGPVC includes support for the PVC-only signalling manager `sigpvc'. # ATM_SPANS includes support for the `spans' signalling manager, which runs # the FORE Systems's proprietary SPANS signalling protocol. # ATM_UNI includes support for the `uni30' and `uni31' signalling managers, # which run the ATM Forum UNI 3.x signalling protocols. # # The `hea' driver provides support for the Efficient Networks, Inc. # ENI-155p ATM PCI Adapter. # # The `hfa' driver provides support for the FORE Systems, Inc. # PCA-200E ATM PCI Adapter. # options ATM_CORE #core ATM protocol family options ATM_IP #IP over ATM support options ATM_SIGPVC #SIGPVC signalling manager options ATM_SPANS #SPANS signalling manager options ATM_UNI #UNI signalling manager device hea0 #Efficient ENI-155p ATM PCI device hfa0 #FORE PCA-200E ATM PCI ##################################################################### # FILESYSTEM OPTIONS # # Only the root, /usr, and /tmp filesystems need be statically # compiled; everything else will be automatically loaded at mount # time. (Exception: the UFS family---FFS, and MFS --- cannot # currently be demand-loaded.) Some people still prefer to statically # compile other filesystems as well. # # NB: The NULL, PORTAL, UMAP and UNION filesystems are known to be # buggy, and WILL panic your system if you attempt to do anything with # them. They are included here as an incentive for some enterprising # soul to sit down and fix them. # # One of these is mandatory: options FFS #Fast filesystem options MFS #Memory File System options NFS #Network File System # The rest are optional: # options NFS_NOSERVER #Disable the NFS-server code. options "CD9660" #ISO 9660 filesystem options FDESC #File descriptor filesystem options KERNFS #Kernel filesystem options MSDOSFS #MS DOS File System options NTFS #NT File System options NULLFS #NULL filesystem options PORTAL #Portal filesystem options PROCFS #Process filesystem options UMAPFS #UID map filesystem options UNION #Union filesystem # The xFS_ROOT options REQUIRE the associated ``options xFS'' options "CD9660_ROOT" #CD-ROM usable as root device options FFS_ROOT #FFS usable as root device options MFS_ROOT #MFS usable as root device options NFS_ROOT #NFS usable as root device # This code is still experimental (e.g. doesn't handle disk slices well). # Also, 'options MFS' is currently incompatible with DEVFS. options DEVFS #devices filesystem # Soft updates is technique for improving file system speed and # making abrupt shutdown less risky. It is not enabled by default due # to copyright restraints on the code that implement it. # # Read ../../ufs/ffs/README.softupdates to learn what you need to # do to enable this. ../../../contrib/sys/softupdates/README gives # more details on how they actually work. # #options SOFTUPDATES # Make space in the kernel for a MFS root filesystem. Define to the number # of kilobytes to reserve for the filesystem. options MFS_ROOT_SIZE=10 # Allows MFS filesystems to be exported via nfs options EXPORTMFS # Allow this many swap-devices. options NSWAPDEV=20 # Disk quotas are supported when this option is enabled. options QUOTA #enable disk quotas # Add more checking code to various filesystems #options NULLFS_DIAGNOSTIC #options KERNFS_DIAGNOSTIC #options UMAPFS_DIAGNOSTIC #options UNION_DIAGNOSTIC # In particular multi-session CD-Rs might require a huge amount of # time in order to "settle". If we are about mounting them as the # root f/s, we gotta wait a little. # # The number is supposed to be in seconds. options "CD9660_ROOTDELAY=20" # If you are running a machine just as a fileserver for PC and MAC # users, using SAMBA or Netatalk, you may consider setting this option # and keeping all those users' directories on a filesystem that is # mounted with the suiddir option. This gives new files the same # ownership as the directory (similiar to group). It's a security hole # if you let these users run programs, so confine it to file-servers # (but it'll save you lots of headaches in those cases). Root owned # directories are exempt and X bits are cleared. The suid bit must be # set on the directory as well; see chmod(1) PC owners can't see/set # ownerships so they keep getting their toes trodden on. This saves # you all the support calls as the filesystem it's used on will act as # they expect: "It's my dir so it must be my file". # options SUIDDIR # Add some error checking code to the null_bypass routine # in the NULL filesystem #options SAFETY # NFS options: options "NFS_MINATTRTIMO=3" # VREG attrib cache timeout in sec options "NFS_MAXATTRTIMO=60" options "NFS_MINDIRATTRTIMO=30" # VDIR attrib cache timeout in sec options "NFS_MAXDIRATTRTIMO=60" options "NFS_GATHERDELAY=10" # Default write gather delay (msec) options "NFS_UIDHASHSIZ=29" # Tune the size of nfssvc_sock with this options "NFS_WDELAYHASHSIZ=16" # and with this options "NFS_MUIDHASHSIZ=63" # Tune the size of nfsmount with this options NFS_DEBUG # Enable NFS Debugging # Coda stuff: options CODA #CODA filesystem. pseudo-device vcoda 4 #coda minicache <-> venus comm. # # Add support for the EXT2FS filesystem of Linux fame. Be a bit # careful with this - the ext2fs code has a tendency to lag behind # changes and not be exercised very much, so mounting read/write could # be dangerous (and even mounting read only could result in panics.) # options "EXT2FS" ##################################################################### # POSIX P1003.1B # Real time extensions added int the 1993 Posix # P1003_1B: Infrastructure # _KPOSIX_PRIORITY_SCHEDULING: Build in _POSIX_PRIORITY_SCHEDULING # _KPOSIX_VERSION: Version kernel is built for options "P1003_1B" options "_KPOSIX_PRIORITY_SCHEDULING" options "_KPOSIX_VERSION=199309L" ##################################################################### # SCSI DEVICES # SCSI DEVICE CONFIGURATION # The SCSI subsystem consists of the `base' SCSI code, a number of # high-level SCSI device `type' drivers, and the low-level host-adapter # device drivers. The host adapters are listed in the ISA and PCI # device configuration sections below. # # Beginning with FreeBSD 2.0.5 you can wire down your SCSI devices so # that a given bus, target, and LUN always come on line as the same # device unit. In earlier versions the unit numbers were assigned # in the order that the devices were probed on the SCSI bus. This # means that if you removed a disk drive, you may have had to rewrite # your /etc/fstab file, and also that you had to be careful when adding # a new disk as it may have been probed earlier and moved your device # configuration around. # This old behavior is maintained as the default behavior. The unit # assignment begins with the first non-wired down unit for a device # type. For example, if you wire a disk as "da3" then the first # non-wired disk will be assigned da4. # The syntax for wiring down devices is: # controller scbus0 at ahc0 # Single bus device # controller scbus1 at ahc1 bus 0 # Single bus device # controller scbus3 at ahc2 bus 0 # Twin bus device # controller scbus2 at ahc2 bus 1 # Twin bus device # disk da0 at scbus0 target 0 unit 0 # disk da1 at scbus3 target 1 # disk da2 at scbus2 target 3 # tape st1 at scbus1 target 6 # device cd0 at scbus? # "units" (SCSI logical unit number) that are not specified are # treated as if specified as LUN 0. # All SCSI devices allocate as many units as are required. # The "unknown" device (uk? in pre-2.0.5) is now part of the base SCSI # configuration and doesn't have to be explicitly configured. controller scbus0 #base SCSI code device ch0 #SCSI media changers device da0 #SCSI direct access devices (aka disks) device sa0 #SCSI tapes device cd0 #SCSI CD-ROMs #device od0 #SCSI optical disk device pass0 #CAM passthrough driver # The previous devices (ch, da, st, cd) are recognized by config. # config doesn't (and shouldn't) know about these newer ones, # so we have to specify that they are on a SCSI bus with the "at scbus?" # clause. device pt0 at scbus? # SCSI processor type device sctarg0 at scbus? # SCSI target # CAM OPTIONS: # debugging options: # -- NOTE -- If you specify one of the bus/target/lun options, you must # specify them all! # CAMDEBUG: When defined enables debugging macros # CAM_DEBUG_BUS: Debug the given bus. Use -1 to debug all busses. # CAM_DEBUG_TARGET: Debug the given target. Use -1 to debug all targets. # CAM_DEBUG_LUN: Debug the given lun. Use -1 to debug all luns. # CAM_DEBUG_FLAGS: OR together CAM_DEBUG_INFO, CAM_DEBUG_TRACE, # CAM_DEBUG_SUBTRACE, and CAM_DEBUG_CDB # # CAM_MAX_HIGHPOWER: Maximum number of concurrent high power (start unit) cmds # SCSI_NO_SENSE_STRINGS: When defined disables sense descriptions # SCSI_NO_OP_STRINGS: When defined disables opcode descriptions # SCSI_REPORT_GEOMETRY: Always report disk geometry at boot up instead # of only when booting verbosely. # SCSI_DELAY: The number of MILLISECONDS to freeze the SIM (scsi adapter) # queue after a bus reset, and the number of milliseconds to # freeze the device queue after a bus device reset. options CAMDEBUG options "CAM_DEBUG_BUS=-1" options "CAM_DEBUG_TARGET=-1" options "CAM_DEBUG_LUN=-1" options "CAM_DEBUG_FLAGS=CAM_DEBUG_INFO|CAM_DEBUG_TRACE|CAM_DEBUG_CDB" options "CAM_MAX_HIGHPOWER=4" options SCSI_NO_SENSE_STRINGS options SCSI_NO_OP_STRINGS options SCSI_REPORT_GEOMETRY options SCSI_DELAY=8000 # Be pessimistic about Joe SCSI device # Options for the CAM CDROM driver: # CHANGER_MIN_BUSY_SECONDS: Guaranteed minimum time quantum for a changer LUN # CHANGER_MAX_BUSY_SECONDS: Maximum time quantum per changer LUN, only # enforced if there is I/O waiting for another LUN # The compiled in defaults for these variables are 2 and 10 seconds, # respectively. # # These can also be changed on the fly with the following sysctl variables: # kern.cam.cd.changer.min_busy_seconds # kern.cam.cd.changer.max_busy_seconds # options "CHANGER_MIN_BUSY_SECONDS=2" options "CHANGER_MAX_BUSY_SECONDS=10" # Options for the CAM sequential access driver: # SA_SPACE_TIMEOUT: Timeout for space operations, in minutes # SA_REWIND_TIMEOUT: Timeout for rewind operations, in minutes # SA_ERASE_TIMEOUT: Timeout for erase operations, in minutes options "SA_SPACE_TIMEOUT=(60)" options "SA_REWIND_TIMEOUT=(2*60)" options "SA_ERASE_TIMEOUT=(4*60)" ##################################################################### # MISCELLANEOUS DEVICES AND OPTIONS # The `pty' device usually turns out to be ``effectively mandatory'', # as it is required for `telnetd', `rlogind', `screen', `emacs', and # `xterm', among others. pseudo-device pty 16 #Pseudo ttys - can go as high as 256 pseudo-device speaker #Play IBM BASIC-style noises out your speaker pseudo-device gzip #Exec gzipped a.out's pseudo-device vn #Vnode driver (turns a file into a device) pseudo-device snp 3 #Snoop device - to look at pty/vty/etc.. pseudo-device ccd 4 #Concatenated disk driver # Configuring Vinum into the kernel is not necessary, since the kld # module gets started automatically when vinum(8) starts. This # device is also untested. Use at your own risk. # # The option VINUMDEBUG must match the value set in CFLAGS # in /usr/src/sbin/vinum/Makefile. Failure to do so will result in # the following message from vinum(8): # # Can't get vinum config: Invalid argument # # see vinum(4) for more reasons not to use these options. pseudo-device vinum #Vinum concat/mirror/raid driver options VINUMDEBUG #enable Vinum debugging hooks # These are only for watching for bitrot in old tty code. # broken #pseudo-device tb # Size of the kernel message buffer. Should be N * pagesize. options "MSGBUF_SIZE=40960" ##################################################################### # HARDWARE DEVICE CONFIGURATION # ISA and EISA devices: # EISA support is available for some device, so they can be auto-probed. # Micro Channel is not supported at all. # # Mandatory ISA devices: isa, npx # -controller isa0 +controller isa0 at nexus? # # Options for `isa': # # AUTO_EOI_1 enables the `automatic EOI' feature for the master 8259A # interrupt controller. This saves about 0.7-1.25 usec for each interrupt. # This option breaks suspend/resume on some portables. # # AUTO_EOI_2 enables the `automatic EOI' feature for the slave 8259A # interrupt controller. This saves about 0.7-1.25 usec for each interrupt. # Automatic EOI is documented not to work for for the slave with the # original i8259A, but it works for some clones and some integrated # versions. # # MAXMEM specifies the amount of RAM on the machine; if this is not # specified, FreeBSD will first read the amount of memory from the CMOS # RAM, so the amount of memory will initially be limited to 64MB or 16MB # depending on the BIOS. If the BIOS reports 64MB, a memory probe will # then attempt to detect the installed amount of RAM. If this probe # fails to detect >64MB RAM you will have to use the MAXMEM option. # The amount is in kilobytes, so for a machine with 128MB of RAM, it would # be 131072 (128 * 1024). # # TUNE_1542 enables the automatic ISA bus speed selection for the # Adaptec 1542 boards. Does not work for all boards, use it with caution. # # BROKEN_KEYBOARD_RESET disables the use of the keyboard controller to # reset the CPU for reboot. This is needed on some systems with broken # keyboard controllers. # # PAS_JOYSTICK_ENABLE enables the gameport on the ProAudio Spectrum options "AUTO_EOI_1" #options "AUTO_EOI_2" options "MAXMEM=(128*1024)" options "TUNE_1542" #options BROKEN_KEYBOARD_RESET #options PAS_JOYSTICK_ENABLE # Enable support for the kernel PLL to use an external PPS signal, # under supervision of [x]ntpd(8) # More info in ntpd documentation: http://www.eecis.udel.edu/~ntp options PPS_SYNC # If you see the "calcru: negative time of %ld usec for pid %d (%s)\n" # message you probably have some broken sw/hw which disables interrupts # for too long. You can make the system more resistant to this by # choosing a high value for NTIMECOUNTER. The default is 5, there # is no upper limit but more than a couple of hundred are not productive. # A better strategy may be to sysctl -w kern.timecounter.method=1 options "NTIMECOUNTER=20" # Enable PnP support in the kernel. This allows you to automaticly # attach to PnP cards for drivers that support it and allows you to # configure cards from USERCONFIG. See pnp(4) for more info. controller pnp0 # The keyboard controller; it controlls the keyboard and the PS/2 mouse. -controller atkbdc0 at isa? port IO_KBD tty +controller atkbdc0 at isa? port IO_KBD # The AT keyboard -device atkbd0 at isa? tty irq 1 +device atkbd0 at atkbdc? tty irq 1 # Options for atkbd: options ATKBD_DFLT_KEYMAP # specify the built-in keymap makeoptions ATKBD_DFLT_KEYMAP="jp.106" # These options are valid for other keyboard drivers as well. options KBD_DISABLE_KEYMAP_LOAD # refuse to load a keymap options KBD_INSTALL_CDEV # install a CDEV entry in /dev # `flags' for atkbd: # 0x01 Force detection of keyboard, else we always assume a keyboard # 0x02 Don't reset keyboard, useful for some newer ThinkPads # 0x04 Old-style (XT) keyboard support, useful for older ThinkPads # PS/2 mouse -device psm0 at isa? tty irq 12 +device psm0 at atkbdc? tty irq 12 # Options for psm: options PSM_HOOKAPM #hook the APM resume event, useful #for some laptops options PSM_RESETAFTERSUSPEND #reset the device at the resume event # The video card driver. device vga0 at isa? port ? conflicts # Options for vga: # Try the following option if the mouse pointer is not drawn correctly # or font does not seem to be loaded properly. May cause flicker on # some systems. options VGA_ALT_SEQACCESS # If you can dispense with some vga driver features, you may want to # use the following options to save some memory. options VGA_NO_FONT_LOADING # don't save/load font options VGA_NO_MODE_CHANGE # don't change video modes # Older video cards may require this option for proper operation. options VGA_SLOW_IOACCESS # do byte-wide i/o's to TS and GDC regs # To include support for VESA video modes options VESA # needs VM86 defined too!! # Splash screen at start up! Screen savers require this too. pseudo-device splash # The pcvt console driver (vt220 compatible). device vt0 at isa? tty options XSERVER # support for running an X server. options FAT_CURSOR # start with block cursor # This PCVT option is for keyboards such as those used on IBM ThinkPad laptops options PCVT_SCANSET=2 # IBM keyboards are non-std # Other PCVT options are documented in pcvt(4). options "PCVT_24LINESDEF" options PCVT_CTRL_ALT_DEL options PCVT_EMU_MOUSE options PCVT_FREEBSD=211 options PCVT_META_ESC options PCVT_NSCREENS=9 options PCVT_PRETTYSCRNS options PCVT_SCREENSAVER options PCVT_USEKBDSEC options "PCVT_VT220KEYB" # The syscons console driver (sco color console compatible). device sc0 at isa? tty options MAXCONS=16 # number of virtual consoles options "STD8X16FONT" # Compile font in makeoptions "STD8X16FONT"="cp850" options SC_HISTORY_SIZE=200 # number of history buffer lines options SC_DISABLE_REBOOT # disable reboot key sequence # # `flags' for sc0: # 0x01 Use a 'visual' bell # 0x02 Use a 'blink' cursor # 0x04 Use a 'underline' cursor # 0x06 Use a 'blinking underline' (destructive) cursor # 0x40 Make the bell quiet if it is rung in the backgroud vty. # # The Numeric Processing eXtension driver. This should be configured if # your machine has a math co-processor, unless the coprocessor is very # buggy. If it is not configured then you *must* configure math emulation # (see above). If both npx0 and emulation are configured, then only npx0 # is used (provided it works). -device npx0 at isa? port IO_NPX iosiz 0x0 flags 0x0 irq 13 +device npx0 at nexus? port IO_NPX iosiz 0x0 flags 0x0 irq 13 # # `flags' for npx0: # 0x01 don't use the npx registers to optimize bcopy # 0x02 don't use the npx registers to optimize bzero # 0x04 don't use the npx registers to optimize copyin or copyout. # The npx registers are normally used to optimize copying and zeroing when # all of the following conditions are satisfied: # "I586_CPU" is an option # the cpu is an i586 (perhaps not a Pentium) # the probe for npx0 succeeds # INT 16 exception handling works. # Then copying and zeroing using the npx registers is normally 30-100% faster. # The flags can be used to control cases where it doesn't work or is slower. # Setting them at boot time using userconfig works right (the optimizations # are not used until later in the bootstrap when npx0 is attached). # # # `iosiz' for npx0: # This can be used instead of the MAXMEM option to set the memory size. If # it is nonzero, then it overrides both the MAXMEM option and the memory # size reported by the BIOS. Setting it at boot time using userconfig takes # effect on the next reboot after the change has been recorded in the kernel # binary (the size is used early in the boot before userconfig has a chance # to change it). # # # Optional ISA and EISA devices: # # # SCSI host adapters: `aha', `bt' # # adv: All Narrow SCSI bus AdvanSys controllers. # adw: Second Generation AdvanSys controllers including the ADV940UW. # aha: Adaptec 154x # ahc: Adaptec 274x/284x/294x # bt: Most Buslogic controllers # # Note that the order is important in order for Buslogic cards to be # probed correctly. # controller bt0 at isa? port "IO_BT0" cam irq ? controller adv0 at isa? port ? cam irq ? controller adw0 controller aha0 at isa? port ? cam irq ? # # ATA and ATAPI devices # This is work in progress, use at your own risk. # It currently reuses the majors of wd.c and friends. # It cannot co-exist with the old system in one kernel. # You only need one "controller ata0" for it to find all # PCI devices on modern machines. #controller ata0 #device atadisk0 # ATA disk drives #device atapicd0 # ATAPI CDROM drives #device atapifd0 # ATAPI floppy drives #device atapist0 # ATAPI tape drives # # If you need ISA only devices, this is the lines to add: #controller ata1 at isa? port "IO_WD1" bio irq 14 #controller ata2 at isa? port "IO_WD2" bio irq 15 # # All the controller lines can coexist, the driver will # find out which ones are there. # # ST-506, ESDI, and IDE hard disks: `wdc' and `wd' # # The flags fields are used to enable the multi-sector I/O and # the 32BIT I/O modes. The flags may be used in either the controller # definition or in the individual disk definitions. The controller # definition is supported for the boot configuration stuff. # # Each drive has a 16 bit flags value defined: # The low 8 bits are the maximum value for the multi-sector I/O, # where 0xff defaults to the maximum that the drive can handle. # The high bit of the 16 bit flags (0x8000) allows probing for # 32 bit transfers. Bit 14 (0x4000) enables a hack to wake # up powered-down laptop drives. Bit 13 (0x2000) allows # probing for PCI IDE DMA controllers, such as Intel's PIIX # south bridges. Bit 12 (0x1000) sets LBA mode instead of the # default CHS mode for accessing the drive. See the wd.4 man page. # # The flags field for the drives can be specified in the controller # specification with the low 16 bits for drive 0, and the high 16 bits # for drive 1. # e.g.: #controller wdc0 at isa? port "IO_WD1" bio irq 14 flags 0x00ff8004 # # specifies that drive 0 will be allowed to probe for 32 bit transfers and # a maximum multi-sector transfer of 4 sectors, and drive 1 will not be # allowed to probe for 32 bit transfers, but will allow multi-sector # transfers up to the maximum that the drive supports. # # If you are using a PCI controller that is not running in compatibility # mode (for example, it is a 2nd IDE PCI interface), then use config line(s) # such as: # #controller wdc2 at isa? port "0" bio irq ? flags 0xa0ffa0ff #disk wd4 at wdc2 drive 0 #disk wd5 at wdc2 drive 1 # #controller wdc3 at isa? port "0" bio irq ? flags 0xa0ffa0ff #disk wd6 at wdc3 drive 0 #disk wd7 at wdc3 drive 1 # # Note that the above config would be useful for a Promise card, when used # on a MB that already has a PIIX controller. Note the bogus irq and port # entries. These are automatically filled in by the IDE/PCI support. # controller wdc0 at isa? port "IO_WD1" bio irq 14 disk wd0 at wdc0 drive 0 disk wd1 at wdc0 drive 1 controller wdc1 at isa? port "IO_WD2" bio irq 15 disk wd2 at wdc1 drive 0 disk wd3 at wdc1 drive 1 # # This option allow you to override the default probe time for IDE # devices, to get a faster probe. Setting this below 10000 violate # the IDE specs, but may still work for you (it will work for most # people). # options IDE_DELAY=8000 # Be optimistic about Joe IDE device # IDE CD-ROM & CD-R/RW driver - requires wdc controller and ATAPI option device wcd0 # IDE floppy driver - requires wdc controller and ATAPI option device wfd0 # IDE tape driver - requires wdc controller and ATAPI option device wst0 # # Standard floppy disk controllers and floppy tapes: `fdc', `fd', and `ft' # controller fdc0 at isa? port "IO_FD1" bio irq 6 drq 2 # # FDC_DEBUG enables floppy debugging. Since the debug output is huge, you # gotta turn it actually on by setting the variable fd_debug with DDB, # however. options FDC_DEBUG # FDC_YE enables support for the floppies used on the Libretto. This is a # pcmcia floppy. You will also need to add #card "Y-E DATA" "External FDD" # config 0x4 "fdc0" 10 # to your pccard.conf file. options FDC_YE # This option is undocumented on purpose. options FDC_PRINT_BOGUS_CHIPTYPE # # Activate this line instead of the fdc0 line above if you happen to # have an Insight floppy tape. Probing them proved to be dangerous # for people with floppy disks only, so it's "hidden" behind a flag: #controller fdc0 at isa? port "IO_FD1" bio flags 1 irq 6 drq 2 disk fd0 at fdc0 drive 0 disk fd1 at fdc0 drive 1 # # Other standard PC hardware: `mse', `sio', etc. # # mse: Logitech and ATI InPort bus mouse ports # sio: serial ports (see sio(4)) device mse0 at isa? port 0x23c tty irq 5 device sio0 at isa? port "IO_COM1" tty flags 0x10 irq 4 # # `flags' for serial drivers that support consoles (only for sio now): # 0x10 enable console support for this unit. The other console flags # are ignored unless this is set. Enabling console support does # not make the unit the preferred console - boot with -h or set # the 0x20 flag for that. Currently, at most one unit can have # console support; the first one (in config file order) with # this flag set is preferred. Setting this flag for sio0 gives # the old behaviour. # 0x20 force this unit to be the console (unless there is another # higher priority console). This replaces the COMCONSOLE option. # 0x40 reserve this unit for low level console operations. Do not # access the device in any normal way. # # PnP `flags' (set via userconfig using pnp x flags y) # 0x1 disable probing of this device. Used to prevent your modem # from being attached as a PnP modem. # # Options for serial drivers that support consoles (only for sio now): options BREAK_TO_DEBUGGER #a BREAK on a comconsole goes to #DDB, if available. options CONSPEED=9600 #default speed for serial console (default 9600) # Options for sio: options COM_ESP #code for Hayes ESP options COM_MULTIPORT #code for some cards with shared IRQs options "EXTRA_SIO=2" #number of extra sio ports to allocate # Other flags for sio that aren't documented in the man page. # 0x20000 enable hardware RTS/CTS and larger FIFOs. Only works for # ST16650A-compatible UARTs. # # Network interfaces: `cx', `ed', `el', `ep', `ie', `is', `le', `lnc' # # ar: Arnet SYNC/570i hdlc sync 2/4 port V.35/X.21 serial driver (requires sppp) # cs: IBM Etherjet and other Crystal Semi CS89x0-based adapters # cx: Cronyx/Sigma multiport sync/async (with Cisco or PPP framing) # ed: Western Digital and SMC 80xx; Novell NE1000 and NE2000; 3Com 3C503 # el: 3Com 3C501 (slow!) # ep: 3Com 3C509 (buggy) # ex: Intel EtherExpress Pro/10 and other i82595-based adapters # fe: Fujitsu MB86960A/MB86965A Ethernet # ie: AT&T StarLAN 10 and EN100; 3Com 3C507; unknown NI5210; Intel EtherExpress # le: Digital Equipment EtherWorks 2 and EtherWorks 3 (DEPCA, DE100, # DE101, DE200, DE201, DE202, DE203, DE204, DE205, DE422) # lnc: Lance/PCnet cards (Isolan, Novell NE2100, NE32-VL, AMD Am7990 & Am79C960) # rdp: RealTek RTL 8002-based pocket ethernet adapters # sr: RISCom/N2 hdlc sync 1/2 port V.35/X.21 serial driver (requires sppp) # wl: Lucent Wavelan (ISA card only). # ze: IBM/National Semiconductor PCMCIA ethernet controller. # zp: 3Com PCMCIA Etherlink III (It does not require shared memory for # send/receive operation, but it needs 'iomem' to read/write the # attribute memory) # oltr: Olicom ISA token-ring adapters OC-3115, OC-3117, OC-3118 and OC-3133 # (no options needed) # device ar0 at isa? port 0x300 net irq 10 iomem 0xd0000 device cs0 at isa? port 0x300 net irq ? device cx0 at isa? port 0x240 net irq 15 drq 7 device ed0 at isa? port 0x280 net irq 5 iomem 0xd8000 device el0 at isa? port 0x300 net irq 9 device ep0 at isa? port 0x300 net irq 10 device ex0 at isa? port? net irq? device fe0 at isa? port 0x300 net irq ? device ie0 at isa? port 0x300 net irq 5 iomem 0xd0000 device ie1 at isa? port 0x360 net irq 7 iomem 0xd0000 device le0 at isa? port 0x300 net irq 5 iomem 0xd0000 device lnc0 at isa? port 0x280 net irq 10 drq 0 device rdp0 at isa? port 0x378 net irq 7 flags 2 device sr0 at isa? port 0x300 net irq 5 iomem 0xd0000 options WLCACHE # enables the signal-strength cache options WLDEBUG # enables verbose debugging output device wl0 at isa? port 0x300 net irq ? # We can (bogusly) include both the dedicated PCCARD drivers and the generic # support when COMPILING_LINT. device ze0 at isa? port 0x300 net irq 5 iomem 0xd8000 device zp0 at isa? port 0x300 net irq 10 iomem 0xd8000 device oltr0 at isa? # # ATM related options # # The `en' device provides support for Efficient Networks (ENI) # ENI-155 PCI midway cards, and the Adaptec 155Mbps PCI ATM cards (ANA-59x0). # # atm pseudo-device provides generic atm functions and is required for # atm devices. # NATM enables the netnatm protocol family that can be used to # bypass TCP/IP. # # the current driver supports only PVC operations (no atm-arp, no multicast). # for more details, please read the original documents at # http://www.ccrc.wustl.edu/pub/chuck/bsdatm/wucs.html # pseudo-device atm device en0 device en1 options NATM #native ATM # # Audio drivers: `snd', `sb', `pas', `gus', `pca' # # snd: Voxware sound support code # sb: SoundBlaster PCM - SoundBlaster, SB Pro, SB16, ProAudioSpectrum # sbxvi: SoundBlaster 16 # sbmidi: SoundBlaster 16 MIDI interface # pas: ProAudioSpectrum PCM and MIDI # gus: Gravis Ultrasound - Ultrasound, Ultrasound 16, Ultrasound MAX # gusxvi: Gravis Ultrasound 16-bit PCM (do not use) # mss: Microsoft Sound System # css: Crystal Sound System (CSS 423x PnP) # sscape: Ensoniq Soundscape MIDI interface # sscape_mss: Ensoniq Soundscape PCM (requires sscape) # opl: Yamaha OPL-2 and OPL-3 FM - SB, SB Pro, SB 16, ProAudioSpectrum # uart: stand-alone 6850 UART for MIDI # mpu: Roland MPU-401 stand-alone card # # Note: It has been reprted that ISA DMA with the SoundBlaster will # lock up the machine (PR docs/5358). If this happens to you, # turning off USWC write posting in your machine's BIOS may fix # the problem. # # Beware! The addresses specified below are also hard-coded in # i386/isa/sound/sound_config.h. If you change the values here, you # must also change the values in the include file. # # pcm: PCM audio through various sound cards. # # This has support for a large number of new audio cards, based on # CS423x, OPTi931, Yamaha OPL-SAx, and also for SB16, GusPnP. # For more information about this driver and supported cards, # see the pcm.4 man page and /sys/i386/isa/snd/CARDS. # # The flags of the device tells the device a bit more info about the # device that normally is obtained through the PnP interface. # bit 2..0 secondary DMA channel; # bit 4 set if the board uses two dma channels; # bit 15..8 board type, overrides autodetection; leave it # zero if don't know what to put in (and you don't, # since this is unsupported at the moment...). # # This driver will use the new PnP code if it's available. # # pca: PCM audio through your PC speaker # # If you have a GUS-MAX card and want to use the CS4231 codec on the # card the drqs for the gus max must be 8 bit (1, 2, or 3). # # If you would like to use the full duplex option on the gus, then define # flags to be the ``read dma channel''. # # options BROKEN_BUS_CLOCK #PAS-16 isn't working and OPTI chipset # options SYMPHONY_PAS #PAS-16 isn't working and SYMPHONY chipset # options EXCLUDE_SBPRO #PAS-16 # options SBC_IRQ=5 #PAS-16. Must match irq on sb0 line. # PAS16: The order of the pas0/sb0/opl0 is important since the # sb emulation is enabled in the pas-16 attach. # # To overide the GUS defaults use: # options GUS_DMA2 # options GUS_DMA # options GUS_IRQ # # The i386/isa/sound/sound.doc has more information. # Controls all "VOXWARE" driver sound devices. See Luigi's driver # below for an alternate which may work better for some cards. # controller snd0 device pas0 at isa? port 0x388 irq 10 drq 6 device sb0 at isa? port 0x220 irq 5 drq 1 device sbxvi0 at isa? drq 5 device sbmidi0 at isa? port 0x330 device awe0 at isa? port 0x620 device gus0 at isa? port 0x220 irq 12 drq 1 #device gus0 at isa? port 0x220 irq 12 drq 1 flags 0x3 device mss0 at isa? port 0x530 irq 10 drq 1 device css0 at isa? port 0x534 irq 5 drq 1 flags 0x08 device sscape0 at isa? port 0x330 irq 9 drq 0 device trix0 at isa? port 0x330 irq 6 drq 0 device sscape_mss0 at isa? port 0x534 irq 5 drq 1 device opl0 at isa? port 0x388 device mpu0 at isa? port 0x330 irq 6 drq 0 device uart0 at isa? port 0x330 irq 5 # Luigi's snd code (use INSTEAD of snd0 and all VOXWARE drivers!). # You may also wish to enable the pnp controller with this, for pnp # sound cards. # #device pcm0 at isa? port ? tty irq 10 drq 1 flags 0x0 # Not controlled by `snd' device pca0 at isa? port "IO_TIMER1" tty # # Miscellaneous hardware: # # mcd: Mitsumi CD-ROM # scd: Sony CD-ROM # matcd: Matsushita/Panasonic CD-ROM # wt: Wangtek and Archive QIC-02/QIC-36 tape drives # ctx: Cortex-I frame grabber # apm: Laptop Advanced Power Management (experimental) # spigot: The Creative Labs Video Spigot video-acquisition board # meteor: Matrox Meteor video capture board # bktr: Brooktree bt848/848a/849/878/879 family video capture and TV Tuner board # cy: Cyclades serial driver # dgb: Digiboard PC/Xi and PC/Xe series driver (ALPHA QUALITY!) # dgm: Digiboard PC/Xem driver # gp: National Instruments AT-GPIB and AT-GPIB/TNT board # asc: GI1904-based hand scanners, e.g. the Trust Amiscan Grey # gsc: Genius GS-4500 hand scanner. # joy: joystick # labpc: National Instrument's Lab-PC and Lab-PC+ # rc: RISCom/8 multiport card # rp: Comtrol Rocketport(ISA) - single card # tw: TW-523 power line interface for use with X-10 home control products # si: Specialix SI/XIO 4-32 port terminal multiplexor # stl: Stallion EasyIO and EasyConnection 8/32 (cd1400 based) # stli: Stallion EasyConnection 8/64, ONboard, Brumby (intelligent) # Notes on APM # The flags takes the following meaning for apm0: # 0x0020 Statclock is broken. # 0x0011 Limit APM protocol to 1.1 or 1.0 # 0x0010 Limit APM protocol to 1.0 # If apm is omitted, some systems require sysctl -w kern.timcounter.method=1 # for correct timekeeping. # Notes on the spigot: # The video spigot is at 0xad6. This port address can not be changed. # The irq values may only be 10, 11, or 15 # I/O memory is an 8kb region. Possible values are: # 0a0000, 0a2000, ..., 0fffff, f00000, f02000, ..., ffffff # The start address must be on an even boundary. # Add the following option if you want to allow non-root users to be able # to access the spigot. This option is not secure because it allows users # direct access to the I/O page. # options SPIGOT_UNSECURE # Notes on the Comtrol Rocketport driver: # # The exact values used for rp0 depend on how many boards you have # in the system. The manufacturer's sample configs are listed as: # # Comtrol Rocketport ISA single card # device rp0 at isa? port 0x280 tty # # If instead you have two ISA cards, one installed at 0x100 and the # second installed at 0x180, then you should add the following to # your kernel configuration file: # # device rp0 at isa? port 0x100 tty # device rp1 at isa? port 0x180 tty # # For 4 ISA cards, it might be something like this: # # device rp0 at isa? port 0x180 tty # device rp1 at isa? port 0x100 tty # device rp2 at isa? port 0x340 tty # device rp3 at isa? port 0x240 tty # # And for PCI cards, you only need say: # # device rp0 # device rp1 # ... # Note: Make sure that any Rocketport PCI devices are specified BEFORE the # ISA Rocketport devices. # Notes on the Digiboard driver: # # The following flag values have special meanings: # 0x01 - alternate layout of pins (dgb & dgm) # 0x02 - use the windowed PC/Xe in 64K mode (dgb only) # Notes on the Specialix SI/XIO driver: # **This is NOT a Specialix supported Driver!** # The host card is memory, not IO mapped. # The Rev 1 host cards use a 64K chunk, on a 32K boundary. # The Rev 2 host cards use a 32K chunk, on a 32K boundary. # The cards can use an IRQ of 11, 12 or 15. # Notes on the Stallion stl and stli drivers: # See src/i386/isa/README.stl for complete instructions. # This is version 0.0.5alpha, unsupported by Stallion. # The stl driver has a secondary IO port hard coded at 0x280. You need # to change src/i386/isa/stallion.c if you reconfigure this on the boards. # The "flags" and "iosiz" settings on the stli driver depend on the board: # EasyConnection 8/64 ISA: flags 23 iosiz 0x1000 # EasyConnection 8/64 EISA: flags 24 iosiz 0x10000 # EasyConnection 8/64 MCA: flags 25 iosiz 0x1000 # ONboard ISA: flags 4 iosiz 0x10000 # ONboard EISA: flags 7 iosiz 0x10000 # ONboard MCA: flags 3 iosiz 0x10000 # Brumby: flags 2 iosiz 0x4000 # Stallion: flags 1 iosiz 0x10000 device mcd0 at isa? port 0x300 bio irq 10 # for the Sony CDU31/33A CDROM device scd0 at isa? port 0x230 bio # for the SoundBlaster 16 multicd - up to 4 devices controller matcd0 at isa? port 0x230 bio device wt0 at isa? port 0x300 bio irq 5 drq 1 device ctx0 at isa? port 0x230 iomem 0xd0000 device spigot0 at isa? port 0xad6 irq 15 iomem 0xee000 -device apm0 at isa? +device apm0 at nexus? device gp0 at isa? port 0x2c0 tty device gsc0 at isa? port "IO_GSC1" tty drq 3 device joy0 at isa? port IO_GAME device cy0 at isa? tty irq 10 iomem 0xd4000 iosiz 0x2000 options CY_PCI_FASTINTR # Use with cy_pci unless irq is shared device dgb0 at isa? port 0x220 iomem 0xfc000 iosiz ? tty options "NDGBPORTS=16" # Defaults to 16*NDGB device dgm0 at isa? port 0x104 iomem 0xd0000 iosiz ? tty device labpc0 at isa? port 0x260 tty irq 5 device rc0 at isa? port 0x220 tty irq 12 device rp0 at isa? port 0x280 tty # the port and irq for tw0 are fictitious device tw0 at isa? port 0x380 tty irq 11 device si0 at isa? iomem 0xd0000 tty irq 12 device asc0 at isa? port "IO_ASC1" tty drq 3 irq 10 device stl0 at isa? port 0x2a0 tty irq 10 device stli0 at isa? port 0x2a0 tty iomem 0xcc000 flags 23 iosiz 0x1000 # You are unlikely to have the hardware for loran0 device loran0 at isa? port ? tty irq 5 # HOT1 Xilinx 6200 card (www.vcc.com) device xrpu0 # # EISA devices: # # The EISA bus device is eisa0. It provides auto-detection and # configuration support for all devices on the EISA bus. # # The `ahb' device provides support for the Adaptec 174X adapter. # # The `ahc' device provides support for the Adaptec 274X and 284X # adapters. The 284X, although a VLB card responds to EISA probes. # # fea: DEC DEFEA EISA FDDI adapter # controller eisa0 controller ahb0 controller ahc0 device fea0 # The aic7xxx driver will attempt to use memory mapped I/O for all PCI # controllers that have it configured only if this option is set. Unfortunately, # this doesn't work on some motherboards, which prevents it from being the # default. options AHC_ALLOW_MEMIO # By default, only 10 EISA slots are probed, since the slot numbers # above clash with the configuration address space of the PCI subsystem, # and the EISA probe is not very smart about this. This is sufficient # for most machines, but in particular the HP NetServer LC series comes # with an onboard AIC7770 dual-channel SCSI controller on EISA slot #11, # thus you need to bump this figure to 12 for them. options "EISA_SLOTS=12" # # PCI devices & PCI options: # # The main PCI bus device is `pci'. It provides auto-detection and # configuration support for all devices on the PCI bus, using either # configuration mode defined in the PCI specification. # # The `ahc' device provides support for the Adaptec 29/3940(U)(W) # and motherboard based AIC7870/AIC7880 adapters. # # The `ncr' device provides support for the NCR 53C810 and 53C825 # self-contained SCSI host adapters. # # The `isp' device provides support for the Qlogic ISP 1020, 1040 # nd 1040B PCI SCSI host adapters, as well as the Qlogic ISP 2100 # FC/AL Host Adapter. # # The `ax' device provides support for PCI fast ethernet adapters # based on the ASIX Electronics AX88140A chip, including the Alfa # Inc. GFC2204. # # The `de' device provides support for the Digital Equipment DC21040 # self-contained Ethernet adapter. # # The `fxp' device provides support for the Intel EtherExpress Pro/100B # PCI Fast Ethernet adapters. # # The `mx' device provides support for various fast ethernet adapters # based on the Macronix 98713, 987615 ans 98725 series chips. # # The `pn' device provides support for various fast ethernet adapters # based on the Lite-On 82c168 and 82c169 PNIC chips, including the # LinkSys LNE100TX, the NetGear FA310TX rev. D1 and the Matrox # FastNIC 10/100. # # The 'rl' device provides support for PCI fast ethernet adapters based # on the RealTek 8129/8139 chipset. Note that the RealTek driver defaults # to useing programmed I/O to do register accesses because memory mapped # mode seems to cause severe lockups on SMP hardware. This driver also # supports the Accton EN1207D `Cheetah' adapter, which uses a chip called # the MPX 5030/5038, which is either a RealTek in disguise or a RealTek # workalike. # # The 'ti' device provides support for PCI gigabit ethernet NICs based # on the Alteon Networks Tigon 1 and Tigon 2 chipsets. This includes the # Alteon AceNIC, the 3Com 3c985, the Netgear GA620 and various others. # Note that you will probably want to bump up NBMCLUSTERS a lot to use # this driver. # # The 'tl' device provides support for the Texas Instruments TNETE100 # series 'ThunderLAN' cards and integrated ethernet controllers. This # includes several Compaq Netelligent 10/100 cards and the built-in # ethernet controllers in several Compaq Prosignia, Proliant and # Deskpro systems. It also supports several Olicom 10Mbps and 10/100 # boards. # # The `tx' device provides support for the SMC 9432TX cards. # # The `vr' device provides support for various fast ethernet adapters # based on the VIA Technologies VT3043 `Rhine I' and VT86C100A `Rhine II' # chips, including the D-Link DFE530TX. # # The `vx' device provides support for the 3Com 3C590 and 3C595 # early support # # The `wb' device provides support for various fast ethernet adapters # based on the Winbond W89C840F chip. Note: this is not the same as # the Winbond W89C940F, which is an NE2000 clone. # # The `xl' device provides support for the 3Com 3c900, 3c905 and # 3c905B (Fast) Etherlink XL cards and integrated controllers. This # includes the integrated 3c905B-TX chips in certain Dell Optiplex and # Dell Precision desktop machines and the integrated 3c905-TX chips # in Dell Latitude laptop docking stations. # # The `fpa' device provides support for the Digital DEFPA PCI FDDI # adapter. pseudo-device fddi is also needed. # # The `meteor' device is a PCI video capture board. It can also have the # following options: # options METEOR_ALLOC_PAGES=xxx preallocate kernel pages for data entry # figure (ROWS*COLUMN*BYTES_PER_PIXEL*FRAME+PAGE_SIZE-1)/PAGE_SIZE # options METEOR_DEALLOC_PAGES remove all allocated pages on close(2) # options METEOR_DEALLOC_ABOVE=xxx remove all allocated pages above the # specified amount. If this value is below the allocated amount no action # taken # options METEOR_SYSTEM_DEFAULT={METEOR_PAL|METEOR_NTSC|METEOR_SECAM}, used # for initialization of fps routine when a signal is not present. # # The 'bktr' device is a PCI video capture device using the Brooktree # bt848/bt848a/bt849/bt878/bt879 chipset. When used with a TV Tuner it forms a # TV card, eg Miro PC/TV,Hauppauge WinCast/TV WinTV, VideoLogic Captivator, # Intel Smart Video III, AverMedia, IMS Turbo. # The following options can be used to override the auto detection # options OVERRIDE_CARD=xxx # options OVERRIDE_TUNER=xxx # options OVERRIDE_MSP=1 # options OVERRIDE_DBX=1 # The current values are found in /usr/src/sys/pci/brooktree848.c # # options BROOKTREE_SYSTEM_DEFAULT=BROOKTREE_PAL # This is required for Dual Crystal (28&35Mhz) boards where PAL is used # to prevent hangs during initialisation. eg VideoLogic Captivator PCI. # # PAL or SECAM users who have a 28Mhz crystal (and no 35Mhz crystal) # must enable PLL mode with this option. eg some new Hauppauge cards. # options BKTR_USE_PLL # # Using sysctl(8) run-time overrides on a per-card basis can be made # # The "oltr" driver supports the following Olicom PCI token-ring adapters # OC-3136, OC-3137, OC-3139, OC-3140, OC-3141, OC-3540, OC-3250 # -controller pci0 +controller pci0 at nexus? controller ahc1 controller ncr0 controller isp0 # # Options for ISP # # SCSI_ISP_NO_FWLOAD_MASK - mask of isp unit numbers (obviously # a max of 32) that you wish to disable # to disable the loading of firmware on. # SCSI_ISP_NO_NVRAM_MASK - mask of isp unit numbers (obviously # a max of 32) that you wish to disable # them picking up information from NVRAM # (for broken cards you can't fix the NVRAM # on- very rare, or for systems you can't # change NVRAM on (e.g. alpha) and you don't # like what's in there) # SCSI_ISP_PREFER_MEM_MAP - control preference for using memory mappings # instead of I/O space mappings. It defaults # to 1 for i386, 0 for alpha. Set to 1 to # unconditionally prefer mapping memory, # else it will use I/O space mappings. Of # course, this can fail if the PCI implement- # ation doesn't support what you want. # # SCSI_ISP_FABRIC enable loading of Fabric f/w flavor (2100). # SCSI_ISP_SCCLUN enable loading of expanded lun f/w (2100). # # ISP_DISABLE_1020_SUPPORT Disable support for 1020/1040 cards # ISP_DISABLE_1080_SUPPORT Disable support for 1080/1240 cards # ISP_DISABLE_2100_SUPPORT Disable support for 2100 cards # (these really just to save code space) # (use of all three will cause the driver to not compile) options SCSI_ISP_NO_FWLOAD_MASK="0x12" # disable FW load for isp1 and isp4 options SCSI_ISP_NO_NVRAM_MASK="0x1" # disable NVRAM for isp0 options SCSI_ISP_PREFER_MEM_MAP="0" # prefer I/O mapping #options "ISP_DISABLE_1020_SUPPORT" #options "ISP_DISABLE_1080_SUPPORT" #options "ISP_DISABLE_2100_SUPPORT" device ax0 device de0 device fxp0 device mx0 device pn0 device rl0 device ti0 device tl0 device tx0 device vr0 device vx0 device wb0 device xl0 device fpa0 device meteor0 device oltr0 # Brooktree driver has been ported to the new I2C framework. Thus, # you'll need at least iicbus, iicbb and smbus. iic/smb are only needed if you # want to control other I2C slaves connected to the external connector of # some cards. # device bktr0 # # PCI options # #options PCI_QUIET #quiets PCI code on chipset settings # # PCCARD/PCMCIA # # card: slot controller # pcic: slots controller card0 device pcic0 at card? device pcic1 at card? # You may need to reset all pccards after resuming options PCIC_RESUME_RESET # reset after resume # # Laptop/Notebook options: # # See also: # apm under `Miscellaneous hardware' # above. # For older notebooks that signal a powerfail condition (external # power supply dropped, or battery state low) by issuing an NMI: options POWERFAIL_NMI # make it beep instead of panicing # # SMB bus # # System Management Bus support provided by the 'smbus' device. # # Supported devices: # smb standard io # # Supported interfaces: # iicsmb I2C to SMB bridge with any iicbus interface # bktr brooktree848 I2C hardware interface # intpm Intel PIIX4 Power Management Unit # alpm Acer Aladdin-IV/V/Pro2 Power Management Unit # controller smbus0 controller intpm0 controller alpm0 device smb0 at smbus? # # I2C Bus # # Philips i2c bus support is provided by the `iicbus' device. # # Supported devices: # ic i2c network interface # iic i2c standard io # iicsmb i2c to smb bridge. Allow i2c i/o with smb commands. # # Supported interfaces: # pcf Philips PCF8584 ISA-bus controller # bktr brooktree848 I2C software interface # # Other: # iicbb generic I2C bit-banging code (needed by lpbb, bktr) # controller iicbus0 controller iicbb0 device ic0 at iicbus? device iic0 at iicbus? device iicsmb0 at iicbus? controller pcf0 at isa? port 0x320 net irq 5 # ISDN4BSD section # i4b passive ISDN cards support (isic - I4b Siemens Isdn Chipset driver) # note that the ``options'' and ``device'' lines must BOTH be defined ! # # Non-PnP Cards: # -------------- # # Teles S0/8 or Niccy 1008 options "TEL_S0_8" #device isic0 at isa? iomem 0xd0000 net irq 5 flags 1 # # Teles S0/16 or Creatix ISDN-S0 or Niccy 1016 options "TEL_S0_16" #device isic0 at isa? port 0xd80 iomem 0xd0000 net irq 5 flags 2 # # Teles S0/16.3 options "TEL_S0_16_3" #device isic0 at isa? port 0xd80 net irq 5 flags 3 # # AVM A1 or AVM Fritz!Card options "AVM_A1" #device isic0 at isa? port 0x340 net irq 5 flags 4 # # USRobotics Sportster ISDN TA intern options "USR_STI" #device isic0 at isa? port 0x268 net irq 5 flags 7 # # ITK ix1 Micro options "ITKIX1" #device isic0 at isa? port 0x398 net irq 10 flags 18 # # PnP-Cards: # ---------- # # Teles S0/16.3 PnP options "TEL_S0_16_3_P" #device isic0 at isa? port ? net irq ? # # Creatix ISDN-S0 P&P options "CRTX_S0_P" #device isic0 at isa? port ? net irq ? # # Dr. Neuhaus Niccy Go@ options "DRN_NGO" #device isic0 at isa? port ? net irq ? # # Sedlbauer Win Speed options "SEDLBAUER" #device isic0 at isa? port ? net irq ? # # Dynalink IS64PH options "DYNALINK" #device isic0 at isa? port ? net irq ? # # ELSA QuickStep 1000pro ISA options "ELSA_QS1ISA" #device isic0 at isa? port ? net irq ? # # PCI-Cards: # ---------- # # ELSA QuickStep 1000pro PCI options "ELSA_QS1PCI" #device isic0 # # PCMCIA-Cards: # ------------- # # AVM PCMCIA Fritz!Card options "AVM_A1_PCMCIA" device isic0 at isa? port 0x340 net irq 5 flags 10 # # Active Cards: # ------------- # # Stollmann Tina-dd control device device tina0 at isa? port 0x260 net irq 10 # # ISDN Protocol Stack # ------------------- # # Q.921 / layer 2 - i4b passive cards D channel handling pseudo-device "i4bq921" # # Q.931 / layer 3 - i4b passive cards D channel handling pseudo-device "i4bq931" # # layer 4 - i4b common passive and active card handling pseudo-device "i4b" # # ISDN devices # ------------ # # userland driver to do ISDN tracing (for passive cards only) pseudo-device "i4btrc" 4 # # userland driver to control the whole thing pseudo-device "i4bctl" # # userland driver for access to raw B channel pseudo-device "i4brbch" 4 # # userland driver for telephony pseudo-device "i4btel" 2 # # network driver for IP over raw HDLC ISDN pseudo-device "i4bipr" 4 # enable VJ header compression detection for ipr i/f options IPR_VJ # # network driver for sync PPP over ISDN pseudo-device "i4bisppp" 4 # Parallel-Port Bus # # Parallel port bus support is provided by the `ppbus' device. # Multiple devices may be attached to the parallel port, devices # are automatically probed and attached when found. # # Supported devices: # vpo Iomega Zip Drive # Requires SCSI disk support ('scbus' and 'da'), best # performance is achieved with ports in EPP 1.9 mode. # lpt Parallel Printer # plip Parallel network interface # ppi General-purpose I/O ("Geek Port") + IEEE1284 I/O # pps Pulse per second Timing Interface # lpbb Philips official parallel port I2C bit-banging interface # # Supported interfaces: # ppc ISA-bus parallel port interfaces. # options "DEBUG_1284" # IEEE1284 signaling protocol debug options "PERIPH_1284" # Makes your computer act as a IEEE1284 # compliant peripheral options "DONTPROBE_1284"# Avoid boot detection of PnP parallel devices options "VP0_DEBUG" # ZIP/ZIP+ debug options "LPT_DEBUG" # Printer driver debug options "PPC_DEBUG" # Parallel chipset level debug options "PLIP_DEBUG" # Parallel network IP interface debug controller ppbus0 controller vpo0 at ppbus? device lpt0 at ppbus? device plip0 at ppbus? device ppi0 at ppbus? device pps0 at ppbus? device lpbb0 at ppbus? device ppc0 at isa? port? tty irq 7 # Kernel BOOTP support options BOOTP # Use BOOTP to obtain IP address/hostname options BOOTP_NFSROOT # NFS mount root filesystem using BOOTP info options "BOOTP_NFSV3" # Use NFS v3 to NFS mount root options BOOTP_COMPAT # Workaround for broken bootp daemons. options "BOOTP_WIRED_TO=fxp0" # Use interface fxp0 for BOOTP # # Add tie-ins for a hardware watchdog. This only enable the hooks; # the user must still supply the actual driver. # options HW_WDOG # # Set the number of PV entries per process. Increasing this can # stop panics related to heavy use of shared memory. However, that can # (combined with large amounts of physical memory) cause panics at # boot time due the kernel running out of VM space. # # If you're tweaking this, you might also want to increase the sysctls # "vm.v_free_min", "vm.v_free_reserved", and "vm.v_free_target". # # The value below is the one more than the default. # options "PMAP_SHPGPERPROC=201" # # Disable swapping. This option removes all code which actually performs # swapping, so it's not possible to turn it back on at run-time. # # This is sometimes usable for systems which don't have any swap space # (see also sysctls "vm.defer_swapspace_pageouts" and # "vm.disable_swapspace_pageouts") # #options NO_SWAPPING # Set the number of sf_bufs to allocate. sf_bufs are virtual buffers # for sendfile(2) that are used to map file VM pages, and normally # default to a quantity that is roughly 16*MAXUSERS+512. You would # typically want about 4 of these for each simultaneous file send. # options "NSFBUFS=1024" # # Enable extra debugging code for locks. This stores the filename and # line of whatever aquired the lock in the lock itself, and change a # number of function calls to pass around the relevant data. This is # not at all useful unless you are debugging lock code. Also note # that it is likely to break e.g. fstat(1) unless you recompile your # userland with -DDEBUG_LOCKS as well. # options DEBUG_LOCKS # More undocumented options for linting. options CLK_CALIBRATION_LOOP options "CLK_USE_I8254_CALIBRATION" options CLK_USE_TSC_CALIBRATION options "TIMER_FREQ=((14318182+6)/12)" options CLUSTERDEBUG options COMPAT_LINUX options CPU_UPGRADE_HW_CACHE options DEBUG options DEBUG_VFS_LOCKS #options DISABLE_PSE options "I586_PMC_GUPROF=0x70000" options "IBCS2" options KEY options KEY_DEBUG options LOCKF_DEBUG options LOUTB options KBD_MAXRETRY=4 options KBD_MAXWAIT=6 options KBD_RESETDELAY=201 options KBDIO_DEBUG=2 options MSGMNB=2049 options MSGMNI=41 options MSGSEG=2049 options MSGSSZ=16 options MSGTQL=41 options NBUF=512 options NETATALKDEBUG options NMBCLUSTERS=1024 options NPX_DEBUG options PANIC_REBOOT_WAIT_TIME=16 options PSM_DEBUG=1 options SCSI_NCR_DEBUG options SCSI_NCR_DFLT_TAGS=4 options SCSI_NCR_MAX_SYNC=10000 options SCSI_NCR_MAX_WIDE=1 options SCSI_NCR_MYADDR=7 options SEMMAP=31 options SEMMNI=11 options SEMMNS=61 options SEMMNU=31 options SEMMSL=61 options SEMOPM=101 options SEMUME=11 options SHOW_BUSYBUFS # List buffers that prevent root unmount options SHMALL=1025 options "SHMMAX=(SHMMAXPGS*PAGE_SIZE+1)" options SHMMAXPGS=1025 options SHMMIN=2 options SHMMNI=33 options SHMSEG=9 options SI_DEBUG options SIMPLELOCK_DEBUG options SPX_HACK options VFS_BIO_DEBUG options ENABLE_ALART # The 'dpt' driver provides support for DPT controllers (http://www.dpt.com/). # These have hardware RAID-{0,1,5} support, and do multi-initiator I/O. # The DPT controllers are commonly re-licensed under other brand-names - # some controllers by Olivetti, Dec, HP, AT&T, SNI, AST, Alphatronic, NEC and # Compaq are actually DPT controllers. # # See sys/dev/dpt for debugging and other subtle options. # DPT_VERIFY_HINTR Performs some strict hardware interrupts testing. # Only use if you suspect PCI bus corruption problems # DPT_RESTRICTED_FREELIST Normally, the freelisat used by the DPT for queue # will grow to accomodate increased use. This growth # will NOT shrink. To restrict the number of queue # slots to exactly what the DPT can hold at one time, # enable this option. # DPT_MEASURE_PERFORMANCE Enables a set of (semi)invasive metrics. Various # instruments are enabled. The tools in # /usr/sbin/dpt_* assume these to be enabled. # DPT_FREELIST_IS_STACK For optimal L{1,2} CPU cache utilization, enable # this option. Otherwise, the transaction queue is # a LIFO. I cannot measure the performance gain. # DPT_HANDLE_TIMEOUTS Normally device timeouts are handled by the DPT. # If you ant the driver to handle timeouts, enable # this option. If your system is very busy, this # option will create more trouble than solve. # DPT_TIMEOUT_FACTOR Used to compute the excessive amount of time to # wait when timing out with the above option. # DPT_DEBUG_xxxx These are controllable from sys/dev/dpt/dpt.h # DPT_LOST_IRQ When enabled, will try, once per second, to catch # any interrupt that got lost. Seems to help in some # DPT-firmware/Motherboard combinations. Minimal # cost, great benefit. # DPT_RESET_HBA Make "reset" actually reset the controller # instead of fudging it. Only enable this if you # are 100% certain you need it. # DPT_SHUTDOWN_SLEEP Reset controller if a request take more than # this number of seconds. Do NOT enable this # unless you are really, really, really certain # you need it. You are advised to call Simon (the # driver author) before setting it, and NEVER, # EVER set it to less than 300s (5 minutes). controller dpt0 # DPT options options DPT_VERIFY_HINTR options DPT_RESTRICTED_FREELIST #!CAM# options DPT_MEASURE_PERFORMANCE options DPT_FREELIST_IS_STACK #!CAM# options DPT_HANDLE_TIMEOUTS options DPT_TIMEOUT_FACTOR=4 options DPT_INTR_DELAY=200 # Some motherboards need that options DPT_LOST_IRQ options DPT_RESET_HBA # Don't EVER set this without having talked to Simon Shapiro on the phone # first. options DPT_SHUTDOWN_SLEEP=500 # USB support # UHCI controller controller uhci0 # OHCI controller controller ohci0 # General USB code (mandatory for USB) controller usb0 # # for the moment we have to specify the priorities of the device # drivers explicitly by the ordering in the list below. This will # be changed in the future. # # USB mouse device ums0 # USB keyboard device ukbd0 # USB printer device ulpt0 # Human Interface Device (anything with buttons and dials) device uhid0 # Generic USB device driver device ugen0 # options UHCI_DEBUG options OHCI_DEBUG options USB_DEBUG options UHUB_DEBUG options UMS_DEBUG options UKBD_DEBUG options UMASS_DEBUG options UHID_DEBUG options UGEN_DEBUG options ULPT_DEBUG Index: head/sys/i386/conf/Makefile.i386 =================================================================== --- head/sys/i386/conf/Makefile.i386 (revision 45719) +++ head/sys/i386/conf/Makefile.i386 (revision 45720) @@ -1,280 +1,280 @@ # Makefile.i386 -- with config changes. # Copyright 1990 W. Jolitz # from: @(#)Makefile.i386 7.1 5/10/91 -# $Id: Makefile.i386,v 1.144 1999/04/13 18:25:08 peter Exp $ +# $Id: Makefile.i386,v 1.145 1999/04/15 14:52:23 bde Exp $ # # Makefile for FreeBSD # # This makefile is constructed from a machine description: # config machineid # Most changes should be made in the machine description # /sys/i386/conf/``machineid'' # after which you should do # config machineid # Generic makefile changes should be made in # /sys/i386/conf/Makefile.i386 # after which config should be rerun for all machines. # # Which version of config(8) is required. -%VERSREQ= 300012 +%VERSREQ= 400013 KERNFORMAT?= elf STD8X16FONT?= iso .if exists(./@/.) S= ./@ .else S= ../.. .endif I386= ${S}/i386 COPTFLAGS?=-O INCLUDES= -nostdinc -I- -I. -I$S # This hack is to allow kernel compiles to succeed on machines w/out srcdist .if exists($S/../include) INCLUDES+= -I$S/../include .else INCLUDES+= -I/usr/include .endif COPTS= ${INCLUDES} ${IDENT} -DKERNEL -DVM_STACK -include opt_global.h CFLAGS= ${COPTFLAGS} ${CWARNFLAGS} ${DEBUG} ${COPTS} # XXX LOCORE means "don't declare C stuff" not "for locore.s". ASM_CFLAGS= -x assembler-with-cpp -DLOCORE ${CFLAGS} # Use the default object format for genassym, etc. GEN_CFLAGS= ${COPTFLAGS} ${CWARNFLAGS} ${DEBUG} ${COPTS} # Select the correct set of tools. Can't set OBJFORMAT here because it # doesn't get exported into the environment, and if it were exported # then it might break building of genassym, etc. .if ${KERNFORMAT} == "elf" CFLAGS+= -elf .else CFLAGS+= -aout .endif LOAD_ADDRESS?= C0100000 DEFINED_PROF= ${PROF} .if defined(PROF) CFLAGS+= -malign-functions=4 .if ${PROFLEVEL} >= 2 IDENT+= -DGPROF4 -DGUPROF PROF+= -mprofiler-epilogue .endif .endif NORMAL_C= ${CC} -c ${CFLAGS} ${PROF} $< NORMAL_C_C= ${CC} -c ${CFLAGS} ${PROF} $< NORMAL_S= ${CC} -c ${ASM_CFLAGS} $< DRIVER_C= ${CC} -c ${CFLAGS} ${PROF} $< DRIVER_C_C= ${CC} -c ${CFLAGS} ${PROF} $< DRIVER_S= ${CC} -c -x ${ASM_CFLAGS} $< PROFILE_C= ${CC} -c ${CFLAGS} $< GEN_CFILES= ${I386}/i386/genassym.c # setdef0.c and setdef1.c are intentionally # omitted from SYSTEM_CFILES. They include setdefs.h, a header which # is generated from all of ${OBJS}. We don't want to have to compile # everything just to do a make depend. SYSTEM_CFILES= ioconf.c param.c vnode_if.c config.c SYSTEM_SFILES= ${I386}/i386/locore.s SYSTEM_DEP= Makefile symbols.exclude symbols.sort ${SYSTEM_OBJS} .if ${CFLAGS:M-g} == "" SYMORDER_EXCLUDE=-x symbols.exclude .endif SYSTEM_LD_HEAD= @echo loading ${.TARGET}; rm -f ${.TARGET} .if ${KERNFORMAT} == aout || ${KERNFORMAT} == aoutkld SYSTEM_OBJS= locore.o vnode_if.o ${OBJS} ioconf.o param.o config.o SYSTEM_LD= @${LD} -aout -Bforcedynamic -Z -T ${LOAD_ADDRESS} -o ${.TARGET} -X ${SYSTEM_OBJS} vers.o SYSTEM_LD_TAIL= @echo rearranging symbols; \ symorder -m ${SYMORDER_EXCLUDE} symbols.sort ${.TARGET}; \ size -aout ${.TARGET} ; chmod 755 ${.TARGET} .endif .if ${KERNFORMAT} == elf SYSTEM_OBJS= locore.o setdef0.o vnode_if.o ${OBJS} ioconf.o param.o config.o \ setdef1.o hack.So SYSTEM_LD= @${LD} -elf -Bdynamic -T $S/i386/conf/kernel.script \ -export-dynamic -dynamic-linker /red/herring \ -o ${.TARGET} -X ${SYSTEM_OBJS} vers.o SYSTEM_LD_TAIL= @size -elf ${.TARGET} ; chmod 755 ${.TARGET} SYSTEM_DEP+= $S/i386/conf/kernel.script .endif .if defined(DEBUG) FULLKERNEL= ${KERNEL}.debug .else FULLKERNEL= ${KERNEL} .endif %BEFORE_DEPEND %OBJS %CFILES %SFILES %MFILES %LOAD %CLEAN .if !exists(.depend) ${SYSTEM_OBJS}: vnode_if.h ${BEFORE_DEPEND:M*.h} .endif clean: rm -f *.o *.so *.So *.ko *.s eddep errs genassym gensetdefs \ ${KERNEL} ${FULLKERNEL} linterrs makelinks param.c \ setdef[01].c setdefs.h symbols.exclude symbols.sort tags \ vers.c vnode_if.c vnode_if.h ${CLEAN} #lint: /tmp param.c # @lint -hbxn -DGENERIC -Dvolatile= ${COPTS} \ # ${I386}/i386/Locore.c ${CFILES} ioconf.c param.c | \ # grep -v 'struct/union .* never defined' | \ # grep -v 'possible pointer alignment problem' symbols.exclude: echo "gcc2_compiled." >symbols.exclude echo "___gnu_compiled_c" >>symbols.exclude symbols.sort: ${I386}/i386/symbols.raw grep -v '^#' ${I386}/i386/symbols.raw \ | sed 's/^ //' | sort -u > symbols.sort locore.o: ${I386}/i386/locore.s assym.s ${NORMAL_S} .if ${KERNFORMAT} == elf # This is a hack. BFD "optimizes" away dynamic mode if there are no # dynamic references. We could probably do a '-Bforcedynamic' mode like # in the a.out ld. For now, this works. hack.So: Makefile touch hack.c ${CC} -elf -shared -nostdlib hack.c -o hack.So rm -f hack.c .endif .ORDER: setdefs.h setdef0.c setdef1.c setdef0.o: setdef0.c setdefs.h ${NORMAL_C} setdef1.o: setdef1.c setdefs.h ${NORMAL_C} setdef0.c setdef1.c setdefs.h: ${OBJS} @echo generating linker set emulation glue for ELF @gensetdefs ${OBJS} # this rule stops ./assym.s in .depend from causing problems ./assym.s: assym.s assym.s: genassym ./genassym >assym.s genassym.o: ${I386}/i386/genassym.c ${CC} -c ${GEN_CFLAGS} ${I386}/i386/genassym.c genassym: genassym.o ${CC} ${GEN_CFLAGS} genassym.o -o ${.TARGET} ${SYSTEM_OBJS} genassym.o vers.o: opt_global.h # XXX this assumes that the options for NORMAL_C* and DRIVER_C* are identical. depend: assym.s param.c vnode_if.h ${BEFORE_DEPEND} rm -f .newdep mkdep -a -f .newdep ${CFLAGS} ${CFILES} ${SYSTEM_CFILES} mkdep -a -f .newdep ${GEN_CFLAGS} ${GEN_CFILES} env MKDEP_CPP="${CC} -E" \ mkdep -a -f .newdep ${ASM_CFLAGS} ${SFILES} ${SYSTEM_SFILES} rm -f .depend mv -f .newdep .depend cleandepend: rm -f .depend links: egrep '#if' ${CFILES:Nswapkernel.c} | sed -f $S/conf/defines | \ sed -e 's/:.*//' -e 's/\.c/.o/' | sort -u > dontlink echo ${CFILES:Nswapkernel.c} | tr -s ' ' '\12' | sed 's/\.c/.o/' | \ sort -u | comm -23 - dontlink | \ sed 's,../.*/\(.*.o\),rm -f \1;ln -s ../GENERIC/\1 \1,' > makelinks sh makelinks && rm -f dontlink tags: @echo "see $S/kern/Makefile for tags" .if defined(DEBUG) ${KERNEL}: ${FULLKERNEL} .if ${KERNFORMAT} == "elf" objcopy --strip-debug ${FULLKERNEL} ${KERNEL} .else cp ${FULLKERNEL} ${KERNEL} strip -d kernel .endif .endif install install.debug: @if [ ! -f ${KERNEL}${.TARGET:S/install//} ] ; then \ echo "You must first build a kernel first." ; \ exit 1 ; \ fi .if exists(${DESTDIR}/${KERNEL}) -chflags noschg ${DESTDIR}/${KERNEL} mv ${DESTDIR}/${KERNEL} ${DESTDIR}/${KERNEL}.old .endif PATH=$${PATH}:/sbin:/usr/sbin; \ if [ `sysctl -n kern.bootfile` = ${DESTDIR}/${KERNEL} ] ; then \ sysctl -w kern.bootfile=${DESTDIR}/${KERNEL}.old ; \ if [ -f /var/db/kvm_kernel.db ] ; then \ mv -f /var/db/kvm_kernel.db /var/db/kvm_kernel.old.db ; \ fi \ fi install -c -m 555 -o root -g wheel -fschg \ ${KERNEL}${.TARGET:S/install//} ${DESTDIR}/${KERNEL} config.o: ${NORMAL_C} ioconf.o: ${NORMAL_C} param.c: $S/conf/param.c -rm -f param.c cp $S/conf/param.c . param.o: ${NORMAL_C} vers.c: $S/conf/newvers.sh $S/sys/param.h ${SYSTEM_DEP} sh $S/conf/newvers.sh ${KERN_IDENT} ${IDENT} # XXX strictly, everything depends on Makefile because changes to ${PROF} # only appear there, but we don't handle that. vers.o: ${NORMAL_C} .ORDER: vnode_if.c vnode_if.h vnode_if.c vnode_if.h: $S/kern/vnode_if.sh $S/kern/vnode_if.src sh $S/kern/vnode_if.sh $S/kern/vnode_if.src vnode_if.o: ${NORMAL_C} .if exists($S/../share/mk) .include "$S/../share/mk/bsd.kern.mk" .else .include .endif %RULES # DO NOT DELETE THIS LINE -- make depend uses it Index: head/sys/i386/conf/NOTES =================================================================== --- head/sys/i386/conf/NOTES (revision 45719) +++ head/sys/i386/conf/NOTES (revision 45720) @@ -1,2130 +1,2130 @@ # # LINT -- config file for checking all the sources, tries to pull in # as much of the source tree as it can. # -# $Id: LINT,v 1.581 1999/04/14 16:54:00 peter Exp $ +# $Id: LINT,v 1.582 1999/04/16 16:17:04 n_hibma Exp $ # # NB: You probably don't want to try running a kernel built from this # file. Instead, you should start from GENERIC, and add options from # this file as required. # # # This directive is mandatory; it defines the architecture to be # configured for; in this case, the 386 family based IBM-PC and # compatibles. # machine "i386" # # This is the ``identification'' of the kernel. Usually this should # be the same as the name of your kernel. # ident LINT # # The `maxusers' parameter controls the static sizing of a number of # internal system tables by a complicated formula defined in param.c. # maxusers 10 # # The `makeoptions' parameter allows variables to be passed to the # generated Makefile in the build area. DEBUG happens to be magic. # The following is equivalent to 'config -g KERNELNAME' and creates # 'kernel.debug' compiled with -g debugging as well as a normal # 'kernel'. Use 'make install.debug' to install the debug kernel # but that isn't normally necessary as the debug symbols are not loaded # by the kernel and are not useful there anyway. # #makeoptions DEBUG="-g" #Build kernel with gdb(1) debug symbols # # Certain applications can grow to be larger than the 128M limit # that FreeBSD initially imposes. Below are some options to # allow that limit to grow to 256MB, and can be increased further # with changing the parameters. MAXDSIZ is the maximum that the # limit can be set to, and the DFLDSIZ is the default value for # the limit. You might want to set the default lower than the # max, and explicitly set the maximum with a shell command for processes # that regularly exceed the limit like INND. # options "MAXDSIZ=(256*1024*1024)" options "DFLDSIZ=(256*1024*1024)" # When this is set, be extra conservative in various parts of the kernel # and choose functionality over speed (on the widest variety of systems). options FAILSAFE # Options for the VM subsystem #options PQ_NOOPT # No coloring options PQ_LARGECACHE # color for 512k/16k cache #options PQ_HUGECACHE # color for 1024k/16k cache # This allows you to actually store this configuration file into # the kernel binary itself, where it may be later read by saying: # strings -aout -n 3 /kernel | grep ^___ | sed -e 's/^___//' > MYKERNEL # options INCLUDE_CONFIG_FILE # Include this file in kernel # # This directive defines a number of things: # - The compiled kernel is to be called `kernel' # - The root filesystem might be on partition wd0a # - Crash dumps will be written to wd0b, if possible. Specifying the # dump device here is not recommended. Use dumpon(8). # config kernel root on wd0 dumps on wd0 ##################################################################### # SMP OPTIONS: # # SMP enables building of a Symmetric MultiProcessor Kernel. # APIC_IO enables the use of the IO APIC for Symmetric I/O. # NCPU sets the number of CPUs, defaults to 2. # NBUS sets the number of busses, defaults to 4. # NAPIC sets the number of IO APICs on the motherboard, defaults to 1. # NINTR sets the total number of INTs provided by the motherboard. # # Notes: # # An SMP kernel will ONLY run on an Intel MP spec. qualified motherboard. # # Be sure to disable 'cpu "I386_CPU"' && 'cpu "I486_CPU"' for SMP kernels. # # Check the 'Rogue SMP hardware' section to see if additional options # are required by your hardware. # # Mandatory: options SMP # Symmetric MultiProcessor Kernel options APIC_IO # Symmetric (APIC) I/O # Optional, these are the defaults plus 1: options NCPU=5 # number of CPUs options NBUS=5 # number of busses options NAPIC=2 # number of IO APICs options NINTR=25 # number of INTs # # Rogue SMP hardware: # # Bridged PCI cards: # # The MP tables of most of the current generation MP motherboards # do NOT properly support bridged PCI cards. To use one of these # cards you should refer to ??? ##################################################################### # CPU OPTIONS # # You must specify at least one CPU (the one you intend to run on); # deleting the specification for CPUs you don't need to use may make # parts of the system run faster. This is especially true removing # I386_CPU. # cpu "I386_CPU" cpu "I486_CPU" cpu "I586_CPU" # aka Pentium(tm) cpu "I686_CPU" # aka Pentium Pro(tm) # # Options for CPU features. # # CPU_BLUELIGHTNING_FPU_OP_CACHE enables FPU operand cache on IBM # BlueLightning CPU. It works only with Cyrix FPU, and this option # should not be used with Intel FPU. # # CPU_BLUELIGHTNING_3X enables triple-clock mode on IBM Blue Lightning # CPU if CPU supports it. The default is double-clock mode on # BlueLightning CPU box. # # CPU_BTB_EN enables branch target buffer on Cyrix 5x86 (NOTE 1). # # CPU_DIRECT_MAPPED_CACHE sets L1 cache of Cyrix 486DLC CPU in direct # mapped mode. Default is 2-way set associative mode. # # CPU_CYRIX_NO_LOCK enables weak locking for the entire address space # of Cyrix 6x86 and 6x86MX CPUs. If this option is not set and # FAILESAFE is defined, NO_LOCK bit of CCR1 is cleared. (NOTE 3) # # CPU_DISABLE_5X86_LSSER disables load store serialize (i.e. enables # reorder). This option should not be used if you use memory mapped # I/O device(s). # # CPU_FASTER_5X86_FPU enables faster FPU exception handler. # # CPU_I486_ON_386 enables CPU cache on i486 based CPU upgrade products # for i386 machines. # # CPU_IORT defines I/O clock delay time (NOTE 1). Default vaules of # I/O clock delay time on Cyrix 5x86 and 6x86 are 0 and 7,respectively # (no clock delay). # # CPU_LOOP_EN prevents flushing the prefetch buffer if the destination # of a jump is already present in the prefetch buffer on Cyrix 5x86(NOTE # 1). # # CPU_RSTK_EN enables return stack on Cyrix 5x86 (NOTE 1). # # CPU_SUSP_HLT enables suspend on HALT. If this option is set, CPU # enters suspend mode following execution of HALT instruction. # # CPU_WT_ALLOC enables write allocation on Cyrix 6x86/6x86MX and AMD # K5/K6/K6-2 cpus. # # CYRIX_CACHE_WORKS enables CPU cache on Cyrix 486 CPUs with cache # flush at hold state. # # CYRIX_CACHE_REALLY_WORKS enables (1) CPU cache on Cyrix 486 CPUs # without cache flush at hold state, and (2) write-back CPU cache on # Cyrix 6x86 whose revision < 2.7 (NOTE 2). # # NO_F00F_HACK disables the hack that prevents Pentiums (and ONLY # Pentiums) from locking up when a LOCK CMPXCHG8B instruction is # executed. This should be included for ALL kernels that won't run # on a Pentium. # # NO_MEMORY_HOLE is an optimisation for systems with AMD K6 processors # which indicates that the 15-16MB range is *definitely* not being # occupied by an ISA memory hole. # # NOTE 1: The options, CPU_BTB_EN, CPU_LOOP_EN, CPU_IORT, # CPU_LOOP_ENand CPU_RSTK_EN should not be used becasue of CPU bugs. # These options may crash your system. # # NOTE 2: If CYRIX_CACHE_REALLY_WORKS is not set, CPU cache is enabled # in write-through mode when revision < 2.7. If revision of Cyrix # 6x86 >= 2.7, CPU cache is always enabled in write-back mode. # # NOTE 3: This option may cause failures for software that requires # locked cycles in order to operate correctly. # options "CPU_BLUELIGHTNING_FPU_OP_CACHE" options "CPU_BLUELIGHTNING_3X" options "CPU_BTB_EN" options "CPU_DIRECT_MAPPED_CACHE" options "CPU_DISABLE_5X86_LSSER" options "CPU_FASTER_5X86_FPU" options "CPU_I486_ON_386" options "CPU_IORT" options "CPU_LOOP_EN" options "CPU_RSTK_EN" options "CPU_SUSP_HLT" options "CPU_WT_ALLOC" options "CYRIX_CACHE_WORKS" options "CYRIX_CACHE_REALLY_WORKS" #options "NO_F00F_HACK" # # A math emulator is mandatory if you wish to run on hardware which # does not have a floating-point processor. Pick either the original, # bogus (but freely-distributable) math emulator, or a much more # fully-featured but GPL-licensed emulator taken from Linux. # options MATH_EMULATE #Support for x87 emulation # Don't enable both of these in a real config. options GPL_MATH_EMULATE #Support for x87 emulation via #new math emulator ##################################################################### # COMPATIBILITY OPTIONS # # Implement system calls compatible with 4.3BSD and older versions of # FreeBSD. You probably do NOT want to remove this as much current code # still relies on the 4.3 emulation. # options "COMPAT_43" # # Statically compile in the i386 a.out LKM compatability support. # Also available as an KLD module. # options LKM # # Allow user-mode programs to manipulate their local descriptor tables. # This option is required for the WINE Windows(tm) emulator, and is # not used by anything else (that we know of). # options USER_LDT #allow user-level control of i386 ldt # # These three options provide support for System V Interface # Definition-style interprocess communication, in the form of shared # memory, semaphores, and message queues, respectively. # options SYSVSHM options SYSVSEM options SYSVMSG # # This option includes a MD5 routine in the kernel, this is used for # various authentication and privacy uses. # options "MD5" # # Allow processes to switch to vm86 mode, as well as enabling direct # user-mode access to the I/O port space. This option is necessary for # the doscmd emulator to run. # options "VM86" ##################################################################### # DEBUGGING OPTIONS # # Enable the kernel debugger. # options DDB # # Don't drop into DDB for a panic. Intended for unattended operation # where you may want to drop to DDB from the console, but still want # the machine to recover from a panic # options DDB_UNATTENDED # # If using GDB remote mode to debug the kernel, there's a non-standard # extension to the remote protocol that can be used to use the serial # port as both the debugging port and the system console. It's non- # standard and you're on your own if you enable it. See also the # "remotechat" variables in the FreeBSD specific version of gdb. # options GDB_REMOTE_CHAT # # KTRACE enables the system-call tracing facility ktrace(2). # options KTRACE #kernel tracing # # The INVARIANTS option is used in a number of source files to enable # extra sanity checking of internal structures. This support is not # enabled by default because of the extra time it would take to check # for these conditions, which can only occur as a result of # programming errors. # options INVARIANTS # # The INVARIANT_SUPPORT option makes us compile in support for # verifying some of the internal structures. It is a prerequisite for # 'INVARIANTS', as enabling 'INVARIANTS' will make these functions be # called. The intent is that you can set 'INVARIANTS' for single # source files (by changing the source file or specifying it on the # command line) if you have 'INVARIANT_SUPPORT' enabled. # options INVARIANT_SUPPORT # # The DIAGNOSTIC option is used to enable extra debugging information # from some parts of the kernel. As this makes everything more noisy, # it is disabled by default. # options DIAGNOSTIC # # PERFMON causes the driver for Pentium/Pentium Pro performance counters # to be compiled. See perfmon(4) for more information. # options PERFMON # # This option let some drivers co-exist that can't co-exist in a running # system. This is used to be able to compile all kernel code in one go for # quality assurance purposes (like this file, which the option takes it name # from.) # options COMPILING_LINT # XXX - this doesn't belong here. # Allow ordinary users to take the console - this is useful for X. options UCONSOLE # XXX - this doesn't belong here either options USERCONFIG #boot -c editor options INTRO_USERCONFIG #imply -c and show intro screen options VISUAL_USERCONFIG #visual boot -c editor ##################################################################### # NETWORKING OPTIONS # # Protocol families: # Only the INET (Internet) family is officially supported in FreeBSD. # Source code for the NS (Xerox Network Service) is provided for amusement # value. # options INET #Internet communications protocols options IPX #IPX/SPX communications protocols options IPXIP #IPX in IP encapsulation (not available) options IPTUNNEL #IP in IPX encapsulation (not available) options NETATALK #Appletalk communications protocols # These are currently broken but are shipped due to interest. #options NS #Xerox NS protocols # These are currently broken and are no longer shipped due to lack # of interest. #options CCITT #X.25 network layer #options ISO #options TPIP #ISO TP class 4 over IP #options TPCONS #ISO TP class 0 over X.25 #options LLC #X.25 link layer for Ethernets #options HDLC #X.25 link layer for serial lines #options EON #ISO CLNP over IP #options NSIP #XNS over IP # # Network interfaces: # The `loop' pseudo-device is MANDATORY when networking is enabled. # The `ether' pseudo-device provides generic code to handle # Ethernets; it is MANDATORY when a Ethernet device driver is # configured or token-ring is enabled. # The 'fddi' pseudo-device provides generic code to support FDDI. # The `sppp' pseudo-device serves a similar role for certain types # of synchronous PPP links (like `cx', `ar'). # The `sl' pseudo-device implements the Serial Line IP (SLIP) service. # The `ppp' pseudo-device implements the Point-to-Point Protocol. # The `bpfilter' pseudo-device enables the Berkeley Packet Filter. Be # aware of the legal and administrative consequences of enabling this # option. The number of devices determines the maximum number of # simultaneous BPF clients programs runnable. # The `disc' pseudo-device implements a minimal network interface, # which throws away all packets sent and never receives any. It is # included for testing purposes. # The `tun' pseudo-device implements (user-)ppp and nos-tun # The `streams' pseudo-device implements SysVR4 STREAMS emulation. # # The PPP_BSDCOMP option enables support for compress(1) style entire # packet compression, the PPP_DEFLATE is for zlib/gzip style compression. # PPP_FILTER enables code for filtering the ppp data stream and selecting # events for resetting the demand dial activity timer - requires bpfilter. # See pppd(8) for more details. # pseudo-device ether #Generic Ethernet pseudo-device token #Generic TokenRing pseudo-device fddi #Generic FDDI pseudo-device sppp #Generic Synchronous PPP pseudo-device loop #Network loopback device pseudo-device bpfilter 4 #Berkeley packet filter pseudo-device disc #Discard device pseudo-device tun 1 #Tunnel driver (ppp(8), nos-tun(8)) pseudo-device sl 2 #Serial Line IP pseudo-device ppp 2 #Point-to-point protocol pseudo-device streams options PPP_BSDCOMP #PPP BSD-compress support options PPP_DEFLATE #PPP zlib/deflate/gzip support options PPP_FILTER #enable bpf filtering (needs bpfilter) # # Internet family options: # # TCP_COMPAT_42 causes the TCP code to emulate certain bugs present in # 4.2BSD. This option should not be used unless you have a 4.2BSD # machine and TCP connections fail. # # MROUTING enables the kernel multicast packet forwarder, which works # with mrouted(8). # # IPFIREWALL enables support for IP firewall construction, in # conjunction with the `ipfw' program. IPFIREWALL_VERBOSE sends # logged packets to the system logger. IPFIREWALL_VERBOSE_LIMIT # limits the number of times a matching entry can be logged. # # WARNING: IPFIREWALL defaults to a policy of "deny ip from any to any" # and if you do not add other rules during startup to allow access, # YOU WILL LOCK YOURSELF OUT. It is suggested that you set firewall=open # in /etc/rc.conf when first enabling this feature, then refining the # firewall rules in /etc/rc.firewall after you've tested that the new kernel # feature works properly. # # IPFIREWALL_DEFAULT_TO_ACCEPT causes the default rule (at boot) to # allow everything. Use with care, if a cracker can crash your # firewall machine, they can get to your protected machines. However, # if you are using it as an as-needed filter for specific problems as # they arise, then this may be for you. Changing the default to 'allow' # means that you won't get stuck if the kernel and /sbin/ipfw binary get # out of sync. # # IPDIVERT enables the divert IP sockets, used by ``ipfw divert'' # # IPFILTER enables Darren Reed's ipfilter package. # IPFILTER_LOG enables ipfilter's logging. # IPFILTER_LKM enables LKM support for an ipfilter module (untested). # # IPSTEALTH enables code to support stealth forwarding (i.e., forwarding # packets without touching the ttl). This can be useful to hide firewalls # from traceroute and similar tools. # # TCPDEBUG is undocumented. # options "TCP_COMPAT_42" #emulate 4.2BSD TCP bugs options MROUTING # Multicast routing options IPFIREWALL #firewall options IPFIREWALL_VERBOSE #print information about # dropped packets options IPFIREWALL_FORWARD #enable transparent proxy support options "IPFIREWALL_VERBOSE_LIMIT=100" #limit verbosity options IPFIREWALL_DEFAULT_TO_ACCEPT #allow everything by default options IPDIVERT #divert sockets options IPFILTER #kernel ipfilter support options IPFILTER_LOG #ipfilter logging #options IPFILTER_LKM #kernel support for ip_fil.o LKM options IPSTEALTH #support for stealth forwarding options TCPDEBUG # ICMP_BANDLIM enables icmp error response bandwidth limiting. You # typically want this option as it will help protect the machine from # D.O.S. packet attacks. # options "ICMP_BANDLIM" # DUMMYNET enables the "dummynet" bandwidth limiter. You need # IPFIREWALL as well. See the dummynet(4) manpage for more info. # BRIDGE enables bridging between ethernet cards -- see bridge(4). # You can use IPFIREWALL and dummynet together with bridging. options DUMMYNET options BRIDGE # # ATM (HARP version) options # # ATM_CORE includes the base ATM functionality code. This must be included # for ATM support. # # ATM_IP includes support for running IP over ATM. # # At least one (and usually only one) of the following signalling managers # must be included (note that all signalling managers include PVC support): # ATM_SIGPVC includes support for the PVC-only signalling manager `sigpvc'. # ATM_SPANS includes support for the `spans' signalling manager, which runs # the FORE Systems's proprietary SPANS signalling protocol. # ATM_UNI includes support for the `uni30' and `uni31' signalling managers, # which run the ATM Forum UNI 3.x signalling protocols. # # The `hea' driver provides support for the Efficient Networks, Inc. # ENI-155p ATM PCI Adapter. # # The `hfa' driver provides support for the FORE Systems, Inc. # PCA-200E ATM PCI Adapter. # options ATM_CORE #core ATM protocol family options ATM_IP #IP over ATM support options ATM_SIGPVC #SIGPVC signalling manager options ATM_SPANS #SPANS signalling manager options ATM_UNI #UNI signalling manager device hea0 #Efficient ENI-155p ATM PCI device hfa0 #FORE PCA-200E ATM PCI ##################################################################### # FILESYSTEM OPTIONS # # Only the root, /usr, and /tmp filesystems need be statically # compiled; everything else will be automatically loaded at mount # time. (Exception: the UFS family---FFS, and MFS --- cannot # currently be demand-loaded.) Some people still prefer to statically # compile other filesystems as well. # # NB: The NULL, PORTAL, UMAP and UNION filesystems are known to be # buggy, and WILL panic your system if you attempt to do anything with # them. They are included here as an incentive for some enterprising # soul to sit down and fix them. # # One of these is mandatory: options FFS #Fast filesystem options MFS #Memory File System options NFS #Network File System # The rest are optional: # options NFS_NOSERVER #Disable the NFS-server code. options "CD9660" #ISO 9660 filesystem options FDESC #File descriptor filesystem options KERNFS #Kernel filesystem options MSDOSFS #MS DOS File System options NTFS #NT File System options NULLFS #NULL filesystem options PORTAL #Portal filesystem options PROCFS #Process filesystem options UMAPFS #UID map filesystem options UNION #Union filesystem # The xFS_ROOT options REQUIRE the associated ``options xFS'' options "CD9660_ROOT" #CD-ROM usable as root device options FFS_ROOT #FFS usable as root device options MFS_ROOT #MFS usable as root device options NFS_ROOT #NFS usable as root device # This code is still experimental (e.g. doesn't handle disk slices well). # Also, 'options MFS' is currently incompatible with DEVFS. options DEVFS #devices filesystem # Soft updates is technique for improving file system speed and # making abrupt shutdown less risky. It is not enabled by default due # to copyright restraints on the code that implement it. # # Read ../../ufs/ffs/README.softupdates to learn what you need to # do to enable this. ../../../contrib/sys/softupdates/README gives # more details on how they actually work. # #options SOFTUPDATES # Make space in the kernel for a MFS root filesystem. Define to the number # of kilobytes to reserve for the filesystem. options MFS_ROOT_SIZE=10 # Allows MFS filesystems to be exported via nfs options EXPORTMFS # Allow this many swap-devices. options NSWAPDEV=20 # Disk quotas are supported when this option is enabled. options QUOTA #enable disk quotas # Add more checking code to various filesystems #options NULLFS_DIAGNOSTIC #options KERNFS_DIAGNOSTIC #options UMAPFS_DIAGNOSTIC #options UNION_DIAGNOSTIC # In particular multi-session CD-Rs might require a huge amount of # time in order to "settle". If we are about mounting them as the # root f/s, we gotta wait a little. # # The number is supposed to be in seconds. options "CD9660_ROOTDELAY=20" # If you are running a machine just as a fileserver for PC and MAC # users, using SAMBA or Netatalk, you may consider setting this option # and keeping all those users' directories on a filesystem that is # mounted with the suiddir option. This gives new files the same # ownership as the directory (similiar to group). It's a security hole # if you let these users run programs, so confine it to file-servers # (but it'll save you lots of headaches in those cases). Root owned # directories are exempt and X bits are cleared. The suid bit must be # set on the directory as well; see chmod(1) PC owners can't see/set # ownerships so they keep getting their toes trodden on. This saves # you all the support calls as the filesystem it's used on will act as # they expect: "It's my dir so it must be my file". # options SUIDDIR # Add some error checking code to the null_bypass routine # in the NULL filesystem #options SAFETY # NFS options: options "NFS_MINATTRTIMO=3" # VREG attrib cache timeout in sec options "NFS_MAXATTRTIMO=60" options "NFS_MINDIRATTRTIMO=30" # VDIR attrib cache timeout in sec options "NFS_MAXDIRATTRTIMO=60" options "NFS_GATHERDELAY=10" # Default write gather delay (msec) options "NFS_UIDHASHSIZ=29" # Tune the size of nfssvc_sock with this options "NFS_WDELAYHASHSIZ=16" # and with this options "NFS_MUIDHASHSIZ=63" # Tune the size of nfsmount with this options NFS_DEBUG # Enable NFS Debugging # Coda stuff: options CODA #CODA filesystem. pseudo-device vcoda 4 #coda minicache <-> venus comm. # # Add support for the EXT2FS filesystem of Linux fame. Be a bit # careful with this - the ext2fs code has a tendency to lag behind # changes and not be exercised very much, so mounting read/write could # be dangerous (and even mounting read only could result in panics.) # options "EXT2FS" ##################################################################### # POSIX P1003.1B # Real time extensions added int the 1993 Posix # P1003_1B: Infrastructure # _KPOSIX_PRIORITY_SCHEDULING: Build in _POSIX_PRIORITY_SCHEDULING # _KPOSIX_VERSION: Version kernel is built for options "P1003_1B" options "_KPOSIX_PRIORITY_SCHEDULING" options "_KPOSIX_VERSION=199309L" ##################################################################### # SCSI DEVICES # SCSI DEVICE CONFIGURATION # The SCSI subsystem consists of the `base' SCSI code, a number of # high-level SCSI device `type' drivers, and the low-level host-adapter # device drivers. The host adapters are listed in the ISA and PCI # device configuration sections below. # # Beginning with FreeBSD 2.0.5 you can wire down your SCSI devices so # that a given bus, target, and LUN always come on line as the same # device unit. In earlier versions the unit numbers were assigned # in the order that the devices were probed on the SCSI bus. This # means that if you removed a disk drive, you may have had to rewrite # your /etc/fstab file, and also that you had to be careful when adding # a new disk as it may have been probed earlier and moved your device # configuration around. # This old behavior is maintained as the default behavior. The unit # assignment begins with the first non-wired down unit for a device # type. For example, if you wire a disk as "da3" then the first # non-wired disk will be assigned da4. # The syntax for wiring down devices is: # controller scbus0 at ahc0 # Single bus device # controller scbus1 at ahc1 bus 0 # Single bus device # controller scbus3 at ahc2 bus 0 # Twin bus device # controller scbus2 at ahc2 bus 1 # Twin bus device # disk da0 at scbus0 target 0 unit 0 # disk da1 at scbus3 target 1 # disk da2 at scbus2 target 3 # tape st1 at scbus1 target 6 # device cd0 at scbus? # "units" (SCSI logical unit number) that are not specified are # treated as if specified as LUN 0. # All SCSI devices allocate as many units as are required. # The "unknown" device (uk? in pre-2.0.5) is now part of the base SCSI # configuration and doesn't have to be explicitly configured. controller scbus0 #base SCSI code device ch0 #SCSI media changers device da0 #SCSI direct access devices (aka disks) device sa0 #SCSI tapes device cd0 #SCSI CD-ROMs #device od0 #SCSI optical disk device pass0 #CAM passthrough driver # The previous devices (ch, da, st, cd) are recognized by config. # config doesn't (and shouldn't) know about these newer ones, # so we have to specify that they are on a SCSI bus with the "at scbus?" # clause. device pt0 at scbus? # SCSI processor type device sctarg0 at scbus? # SCSI target # CAM OPTIONS: # debugging options: # -- NOTE -- If you specify one of the bus/target/lun options, you must # specify them all! # CAMDEBUG: When defined enables debugging macros # CAM_DEBUG_BUS: Debug the given bus. Use -1 to debug all busses. # CAM_DEBUG_TARGET: Debug the given target. Use -1 to debug all targets. # CAM_DEBUG_LUN: Debug the given lun. Use -1 to debug all luns. # CAM_DEBUG_FLAGS: OR together CAM_DEBUG_INFO, CAM_DEBUG_TRACE, # CAM_DEBUG_SUBTRACE, and CAM_DEBUG_CDB # # CAM_MAX_HIGHPOWER: Maximum number of concurrent high power (start unit) cmds # SCSI_NO_SENSE_STRINGS: When defined disables sense descriptions # SCSI_NO_OP_STRINGS: When defined disables opcode descriptions # SCSI_REPORT_GEOMETRY: Always report disk geometry at boot up instead # of only when booting verbosely. # SCSI_DELAY: The number of MILLISECONDS to freeze the SIM (scsi adapter) # queue after a bus reset, and the number of milliseconds to # freeze the device queue after a bus device reset. options CAMDEBUG options "CAM_DEBUG_BUS=-1" options "CAM_DEBUG_TARGET=-1" options "CAM_DEBUG_LUN=-1" options "CAM_DEBUG_FLAGS=CAM_DEBUG_INFO|CAM_DEBUG_TRACE|CAM_DEBUG_CDB" options "CAM_MAX_HIGHPOWER=4" options SCSI_NO_SENSE_STRINGS options SCSI_NO_OP_STRINGS options SCSI_REPORT_GEOMETRY options SCSI_DELAY=8000 # Be pessimistic about Joe SCSI device # Options for the CAM CDROM driver: # CHANGER_MIN_BUSY_SECONDS: Guaranteed minimum time quantum for a changer LUN # CHANGER_MAX_BUSY_SECONDS: Maximum time quantum per changer LUN, only # enforced if there is I/O waiting for another LUN # The compiled in defaults for these variables are 2 and 10 seconds, # respectively. # # These can also be changed on the fly with the following sysctl variables: # kern.cam.cd.changer.min_busy_seconds # kern.cam.cd.changer.max_busy_seconds # options "CHANGER_MIN_BUSY_SECONDS=2" options "CHANGER_MAX_BUSY_SECONDS=10" # Options for the CAM sequential access driver: # SA_SPACE_TIMEOUT: Timeout for space operations, in minutes # SA_REWIND_TIMEOUT: Timeout for rewind operations, in minutes # SA_ERASE_TIMEOUT: Timeout for erase operations, in minutes options "SA_SPACE_TIMEOUT=(60)" options "SA_REWIND_TIMEOUT=(2*60)" options "SA_ERASE_TIMEOUT=(4*60)" ##################################################################### # MISCELLANEOUS DEVICES AND OPTIONS # The `pty' device usually turns out to be ``effectively mandatory'', # as it is required for `telnetd', `rlogind', `screen', `emacs', and # `xterm', among others. pseudo-device pty 16 #Pseudo ttys - can go as high as 256 pseudo-device speaker #Play IBM BASIC-style noises out your speaker pseudo-device gzip #Exec gzipped a.out's pseudo-device vn #Vnode driver (turns a file into a device) pseudo-device snp 3 #Snoop device - to look at pty/vty/etc.. pseudo-device ccd 4 #Concatenated disk driver # Configuring Vinum into the kernel is not necessary, since the kld # module gets started automatically when vinum(8) starts. This # device is also untested. Use at your own risk. # # The option VINUMDEBUG must match the value set in CFLAGS # in /usr/src/sbin/vinum/Makefile. Failure to do so will result in # the following message from vinum(8): # # Can't get vinum config: Invalid argument # # see vinum(4) for more reasons not to use these options. pseudo-device vinum #Vinum concat/mirror/raid driver options VINUMDEBUG #enable Vinum debugging hooks # These are only for watching for bitrot in old tty code. # broken #pseudo-device tb # Size of the kernel message buffer. Should be N * pagesize. options "MSGBUF_SIZE=40960" ##################################################################### # HARDWARE DEVICE CONFIGURATION # ISA and EISA devices: # EISA support is available for some device, so they can be auto-probed. # Micro Channel is not supported at all. # # Mandatory ISA devices: isa, npx # -controller isa0 +controller isa0 at nexus? # # Options for `isa': # # AUTO_EOI_1 enables the `automatic EOI' feature for the master 8259A # interrupt controller. This saves about 0.7-1.25 usec for each interrupt. # This option breaks suspend/resume on some portables. # # AUTO_EOI_2 enables the `automatic EOI' feature for the slave 8259A # interrupt controller. This saves about 0.7-1.25 usec for each interrupt. # Automatic EOI is documented not to work for for the slave with the # original i8259A, but it works for some clones and some integrated # versions. # # MAXMEM specifies the amount of RAM on the machine; if this is not # specified, FreeBSD will first read the amount of memory from the CMOS # RAM, so the amount of memory will initially be limited to 64MB or 16MB # depending on the BIOS. If the BIOS reports 64MB, a memory probe will # then attempt to detect the installed amount of RAM. If this probe # fails to detect >64MB RAM you will have to use the MAXMEM option. # The amount is in kilobytes, so for a machine with 128MB of RAM, it would # be 131072 (128 * 1024). # # TUNE_1542 enables the automatic ISA bus speed selection for the # Adaptec 1542 boards. Does not work for all boards, use it with caution. # # BROKEN_KEYBOARD_RESET disables the use of the keyboard controller to # reset the CPU for reboot. This is needed on some systems with broken # keyboard controllers. # # PAS_JOYSTICK_ENABLE enables the gameport on the ProAudio Spectrum options "AUTO_EOI_1" #options "AUTO_EOI_2" options "MAXMEM=(128*1024)" options "TUNE_1542" #options BROKEN_KEYBOARD_RESET #options PAS_JOYSTICK_ENABLE # Enable support for the kernel PLL to use an external PPS signal, # under supervision of [x]ntpd(8) # More info in ntpd documentation: http://www.eecis.udel.edu/~ntp options PPS_SYNC # If you see the "calcru: negative time of %ld usec for pid %d (%s)\n" # message you probably have some broken sw/hw which disables interrupts # for too long. You can make the system more resistant to this by # choosing a high value for NTIMECOUNTER. The default is 5, there # is no upper limit but more than a couple of hundred are not productive. # A better strategy may be to sysctl -w kern.timecounter.method=1 options "NTIMECOUNTER=20" # Enable PnP support in the kernel. This allows you to automaticly # attach to PnP cards for drivers that support it and allows you to # configure cards from USERCONFIG. See pnp(4) for more info. controller pnp0 # The keyboard controller; it controlls the keyboard and the PS/2 mouse. -controller atkbdc0 at isa? port IO_KBD tty +controller atkbdc0 at isa? port IO_KBD # The AT keyboard -device atkbd0 at isa? tty irq 1 +device atkbd0 at atkbdc? tty irq 1 # Options for atkbd: options ATKBD_DFLT_KEYMAP # specify the built-in keymap makeoptions ATKBD_DFLT_KEYMAP="jp.106" # These options are valid for other keyboard drivers as well. options KBD_DISABLE_KEYMAP_LOAD # refuse to load a keymap options KBD_INSTALL_CDEV # install a CDEV entry in /dev # `flags' for atkbd: # 0x01 Force detection of keyboard, else we always assume a keyboard # 0x02 Don't reset keyboard, useful for some newer ThinkPads # 0x04 Old-style (XT) keyboard support, useful for older ThinkPads # PS/2 mouse -device psm0 at isa? tty irq 12 +device psm0 at atkbdc? tty irq 12 # Options for psm: options PSM_HOOKAPM #hook the APM resume event, useful #for some laptops options PSM_RESETAFTERSUSPEND #reset the device at the resume event # The video card driver. device vga0 at isa? port ? conflicts # Options for vga: # Try the following option if the mouse pointer is not drawn correctly # or font does not seem to be loaded properly. May cause flicker on # some systems. options VGA_ALT_SEQACCESS # If you can dispense with some vga driver features, you may want to # use the following options to save some memory. options VGA_NO_FONT_LOADING # don't save/load font options VGA_NO_MODE_CHANGE # don't change video modes # Older video cards may require this option for proper operation. options VGA_SLOW_IOACCESS # do byte-wide i/o's to TS and GDC regs # To include support for VESA video modes options VESA # needs VM86 defined too!! # Splash screen at start up! Screen savers require this too. pseudo-device splash # The pcvt console driver (vt220 compatible). device vt0 at isa? tty options XSERVER # support for running an X server. options FAT_CURSOR # start with block cursor # This PCVT option is for keyboards such as those used on IBM ThinkPad laptops options PCVT_SCANSET=2 # IBM keyboards are non-std # Other PCVT options are documented in pcvt(4). options "PCVT_24LINESDEF" options PCVT_CTRL_ALT_DEL options PCVT_EMU_MOUSE options PCVT_FREEBSD=211 options PCVT_META_ESC options PCVT_NSCREENS=9 options PCVT_PRETTYSCRNS options PCVT_SCREENSAVER options PCVT_USEKBDSEC options "PCVT_VT220KEYB" # The syscons console driver (sco color console compatible). device sc0 at isa? tty options MAXCONS=16 # number of virtual consoles options "STD8X16FONT" # Compile font in makeoptions "STD8X16FONT"="cp850" options SC_HISTORY_SIZE=200 # number of history buffer lines options SC_DISABLE_REBOOT # disable reboot key sequence # # `flags' for sc0: # 0x01 Use a 'visual' bell # 0x02 Use a 'blink' cursor # 0x04 Use a 'underline' cursor # 0x06 Use a 'blinking underline' (destructive) cursor # 0x40 Make the bell quiet if it is rung in the backgroud vty. # # The Numeric Processing eXtension driver. This should be configured if # your machine has a math co-processor, unless the coprocessor is very # buggy. If it is not configured then you *must* configure math emulation # (see above). If both npx0 and emulation are configured, then only npx0 # is used (provided it works). -device npx0 at isa? port IO_NPX iosiz 0x0 flags 0x0 irq 13 +device npx0 at nexus? port IO_NPX iosiz 0x0 flags 0x0 irq 13 # # `flags' for npx0: # 0x01 don't use the npx registers to optimize bcopy # 0x02 don't use the npx registers to optimize bzero # 0x04 don't use the npx registers to optimize copyin or copyout. # The npx registers are normally used to optimize copying and zeroing when # all of the following conditions are satisfied: # "I586_CPU" is an option # the cpu is an i586 (perhaps not a Pentium) # the probe for npx0 succeeds # INT 16 exception handling works. # Then copying and zeroing using the npx registers is normally 30-100% faster. # The flags can be used to control cases where it doesn't work or is slower. # Setting them at boot time using userconfig works right (the optimizations # are not used until later in the bootstrap when npx0 is attached). # # # `iosiz' for npx0: # This can be used instead of the MAXMEM option to set the memory size. If # it is nonzero, then it overrides both the MAXMEM option and the memory # size reported by the BIOS. Setting it at boot time using userconfig takes # effect on the next reboot after the change has been recorded in the kernel # binary (the size is used early in the boot before userconfig has a chance # to change it). # # # Optional ISA and EISA devices: # # # SCSI host adapters: `aha', `bt' # # adv: All Narrow SCSI bus AdvanSys controllers. # adw: Second Generation AdvanSys controllers including the ADV940UW. # aha: Adaptec 154x # ahc: Adaptec 274x/284x/294x # bt: Most Buslogic controllers # # Note that the order is important in order for Buslogic cards to be # probed correctly. # controller bt0 at isa? port "IO_BT0" cam irq ? controller adv0 at isa? port ? cam irq ? controller adw0 controller aha0 at isa? port ? cam irq ? # # ATA and ATAPI devices # This is work in progress, use at your own risk. # It currently reuses the majors of wd.c and friends. # It cannot co-exist with the old system in one kernel. # You only need one "controller ata0" for it to find all # PCI devices on modern machines. #controller ata0 #device atadisk0 # ATA disk drives #device atapicd0 # ATAPI CDROM drives #device atapifd0 # ATAPI floppy drives #device atapist0 # ATAPI tape drives # # If you need ISA only devices, this is the lines to add: #controller ata1 at isa? port "IO_WD1" bio irq 14 #controller ata2 at isa? port "IO_WD2" bio irq 15 # # All the controller lines can coexist, the driver will # find out which ones are there. # # ST-506, ESDI, and IDE hard disks: `wdc' and `wd' # # The flags fields are used to enable the multi-sector I/O and # the 32BIT I/O modes. The flags may be used in either the controller # definition or in the individual disk definitions. The controller # definition is supported for the boot configuration stuff. # # Each drive has a 16 bit flags value defined: # The low 8 bits are the maximum value for the multi-sector I/O, # where 0xff defaults to the maximum that the drive can handle. # The high bit of the 16 bit flags (0x8000) allows probing for # 32 bit transfers. Bit 14 (0x4000) enables a hack to wake # up powered-down laptop drives. Bit 13 (0x2000) allows # probing for PCI IDE DMA controllers, such as Intel's PIIX # south bridges. Bit 12 (0x1000) sets LBA mode instead of the # default CHS mode for accessing the drive. See the wd.4 man page. # # The flags field for the drives can be specified in the controller # specification with the low 16 bits for drive 0, and the high 16 bits # for drive 1. # e.g.: #controller wdc0 at isa? port "IO_WD1" bio irq 14 flags 0x00ff8004 # # specifies that drive 0 will be allowed to probe for 32 bit transfers and # a maximum multi-sector transfer of 4 sectors, and drive 1 will not be # allowed to probe for 32 bit transfers, but will allow multi-sector # transfers up to the maximum that the drive supports. # # If you are using a PCI controller that is not running in compatibility # mode (for example, it is a 2nd IDE PCI interface), then use config line(s) # such as: # #controller wdc2 at isa? port "0" bio irq ? flags 0xa0ffa0ff #disk wd4 at wdc2 drive 0 #disk wd5 at wdc2 drive 1 # #controller wdc3 at isa? port "0" bio irq ? flags 0xa0ffa0ff #disk wd6 at wdc3 drive 0 #disk wd7 at wdc3 drive 1 # # Note that the above config would be useful for a Promise card, when used # on a MB that already has a PIIX controller. Note the bogus irq and port # entries. These are automatically filled in by the IDE/PCI support. # controller wdc0 at isa? port "IO_WD1" bio irq 14 disk wd0 at wdc0 drive 0 disk wd1 at wdc0 drive 1 controller wdc1 at isa? port "IO_WD2" bio irq 15 disk wd2 at wdc1 drive 0 disk wd3 at wdc1 drive 1 # # This option allow you to override the default probe time for IDE # devices, to get a faster probe. Setting this below 10000 violate # the IDE specs, but may still work for you (it will work for most # people). # options IDE_DELAY=8000 # Be optimistic about Joe IDE device # IDE CD-ROM & CD-R/RW driver - requires wdc controller and ATAPI option device wcd0 # IDE floppy driver - requires wdc controller and ATAPI option device wfd0 # IDE tape driver - requires wdc controller and ATAPI option device wst0 # # Standard floppy disk controllers and floppy tapes: `fdc', `fd', and `ft' # controller fdc0 at isa? port "IO_FD1" bio irq 6 drq 2 # # FDC_DEBUG enables floppy debugging. Since the debug output is huge, you # gotta turn it actually on by setting the variable fd_debug with DDB, # however. options FDC_DEBUG # FDC_YE enables support for the floppies used on the Libretto. This is a # pcmcia floppy. You will also need to add #card "Y-E DATA" "External FDD" # config 0x4 "fdc0" 10 # to your pccard.conf file. options FDC_YE # This option is undocumented on purpose. options FDC_PRINT_BOGUS_CHIPTYPE # # Activate this line instead of the fdc0 line above if you happen to # have an Insight floppy tape. Probing them proved to be dangerous # for people with floppy disks only, so it's "hidden" behind a flag: #controller fdc0 at isa? port "IO_FD1" bio flags 1 irq 6 drq 2 disk fd0 at fdc0 drive 0 disk fd1 at fdc0 drive 1 # # Other standard PC hardware: `mse', `sio', etc. # # mse: Logitech and ATI InPort bus mouse ports # sio: serial ports (see sio(4)) device mse0 at isa? port 0x23c tty irq 5 device sio0 at isa? port "IO_COM1" tty flags 0x10 irq 4 # # `flags' for serial drivers that support consoles (only for sio now): # 0x10 enable console support for this unit. The other console flags # are ignored unless this is set. Enabling console support does # not make the unit the preferred console - boot with -h or set # the 0x20 flag for that. Currently, at most one unit can have # console support; the first one (in config file order) with # this flag set is preferred. Setting this flag for sio0 gives # the old behaviour. # 0x20 force this unit to be the console (unless there is another # higher priority console). This replaces the COMCONSOLE option. # 0x40 reserve this unit for low level console operations. Do not # access the device in any normal way. # # PnP `flags' (set via userconfig using pnp x flags y) # 0x1 disable probing of this device. Used to prevent your modem # from being attached as a PnP modem. # # Options for serial drivers that support consoles (only for sio now): options BREAK_TO_DEBUGGER #a BREAK on a comconsole goes to #DDB, if available. options CONSPEED=9600 #default speed for serial console (default 9600) # Options for sio: options COM_ESP #code for Hayes ESP options COM_MULTIPORT #code for some cards with shared IRQs options "EXTRA_SIO=2" #number of extra sio ports to allocate # Other flags for sio that aren't documented in the man page. # 0x20000 enable hardware RTS/CTS and larger FIFOs. Only works for # ST16650A-compatible UARTs. # # Network interfaces: `cx', `ed', `el', `ep', `ie', `is', `le', `lnc' # # ar: Arnet SYNC/570i hdlc sync 2/4 port V.35/X.21 serial driver (requires sppp) # cs: IBM Etherjet and other Crystal Semi CS89x0-based adapters # cx: Cronyx/Sigma multiport sync/async (with Cisco or PPP framing) # ed: Western Digital and SMC 80xx; Novell NE1000 and NE2000; 3Com 3C503 # el: 3Com 3C501 (slow!) # ep: 3Com 3C509 (buggy) # ex: Intel EtherExpress Pro/10 and other i82595-based adapters # fe: Fujitsu MB86960A/MB86965A Ethernet # ie: AT&T StarLAN 10 and EN100; 3Com 3C507; unknown NI5210; Intel EtherExpress # le: Digital Equipment EtherWorks 2 and EtherWorks 3 (DEPCA, DE100, # DE101, DE200, DE201, DE202, DE203, DE204, DE205, DE422) # lnc: Lance/PCnet cards (Isolan, Novell NE2100, NE32-VL, AMD Am7990 & Am79C960) # rdp: RealTek RTL 8002-based pocket ethernet adapters # sr: RISCom/N2 hdlc sync 1/2 port V.35/X.21 serial driver (requires sppp) # wl: Lucent Wavelan (ISA card only). # ze: IBM/National Semiconductor PCMCIA ethernet controller. # zp: 3Com PCMCIA Etherlink III (It does not require shared memory for # send/receive operation, but it needs 'iomem' to read/write the # attribute memory) # oltr: Olicom ISA token-ring adapters OC-3115, OC-3117, OC-3118 and OC-3133 # (no options needed) # device ar0 at isa? port 0x300 net irq 10 iomem 0xd0000 device cs0 at isa? port 0x300 net irq ? device cx0 at isa? port 0x240 net irq 15 drq 7 device ed0 at isa? port 0x280 net irq 5 iomem 0xd8000 device el0 at isa? port 0x300 net irq 9 device ep0 at isa? port 0x300 net irq 10 device ex0 at isa? port? net irq? device fe0 at isa? port 0x300 net irq ? device ie0 at isa? port 0x300 net irq 5 iomem 0xd0000 device ie1 at isa? port 0x360 net irq 7 iomem 0xd0000 device le0 at isa? port 0x300 net irq 5 iomem 0xd0000 device lnc0 at isa? port 0x280 net irq 10 drq 0 device rdp0 at isa? port 0x378 net irq 7 flags 2 device sr0 at isa? port 0x300 net irq 5 iomem 0xd0000 options WLCACHE # enables the signal-strength cache options WLDEBUG # enables verbose debugging output device wl0 at isa? port 0x300 net irq ? # We can (bogusly) include both the dedicated PCCARD drivers and the generic # support when COMPILING_LINT. device ze0 at isa? port 0x300 net irq 5 iomem 0xd8000 device zp0 at isa? port 0x300 net irq 10 iomem 0xd8000 device oltr0 at isa? # # ATM related options # # The `en' device provides support for Efficient Networks (ENI) # ENI-155 PCI midway cards, and the Adaptec 155Mbps PCI ATM cards (ANA-59x0). # # atm pseudo-device provides generic atm functions and is required for # atm devices. # NATM enables the netnatm protocol family that can be used to # bypass TCP/IP. # # the current driver supports only PVC operations (no atm-arp, no multicast). # for more details, please read the original documents at # http://www.ccrc.wustl.edu/pub/chuck/bsdatm/wucs.html # pseudo-device atm device en0 device en1 options NATM #native ATM # # Audio drivers: `snd', `sb', `pas', `gus', `pca' # # snd: Voxware sound support code # sb: SoundBlaster PCM - SoundBlaster, SB Pro, SB16, ProAudioSpectrum # sbxvi: SoundBlaster 16 # sbmidi: SoundBlaster 16 MIDI interface # pas: ProAudioSpectrum PCM and MIDI # gus: Gravis Ultrasound - Ultrasound, Ultrasound 16, Ultrasound MAX # gusxvi: Gravis Ultrasound 16-bit PCM (do not use) # mss: Microsoft Sound System # css: Crystal Sound System (CSS 423x PnP) # sscape: Ensoniq Soundscape MIDI interface # sscape_mss: Ensoniq Soundscape PCM (requires sscape) # opl: Yamaha OPL-2 and OPL-3 FM - SB, SB Pro, SB 16, ProAudioSpectrum # uart: stand-alone 6850 UART for MIDI # mpu: Roland MPU-401 stand-alone card # # Note: It has been reprted that ISA DMA with the SoundBlaster will # lock up the machine (PR docs/5358). If this happens to you, # turning off USWC write posting in your machine's BIOS may fix # the problem. # # Beware! The addresses specified below are also hard-coded in # i386/isa/sound/sound_config.h. If you change the values here, you # must also change the values in the include file. # # pcm: PCM audio through various sound cards. # # This has support for a large number of new audio cards, based on # CS423x, OPTi931, Yamaha OPL-SAx, and also for SB16, GusPnP. # For more information about this driver and supported cards, # see the pcm.4 man page and /sys/i386/isa/snd/CARDS. # # The flags of the device tells the device a bit more info about the # device that normally is obtained through the PnP interface. # bit 2..0 secondary DMA channel; # bit 4 set if the board uses two dma channels; # bit 15..8 board type, overrides autodetection; leave it # zero if don't know what to put in (and you don't, # since this is unsupported at the moment...). # # This driver will use the new PnP code if it's available. # # pca: PCM audio through your PC speaker # # If you have a GUS-MAX card and want to use the CS4231 codec on the # card the drqs for the gus max must be 8 bit (1, 2, or 3). # # If you would like to use the full duplex option on the gus, then define # flags to be the ``read dma channel''. # # options BROKEN_BUS_CLOCK #PAS-16 isn't working and OPTI chipset # options SYMPHONY_PAS #PAS-16 isn't working and SYMPHONY chipset # options EXCLUDE_SBPRO #PAS-16 # options SBC_IRQ=5 #PAS-16. Must match irq on sb0 line. # PAS16: The order of the pas0/sb0/opl0 is important since the # sb emulation is enabled in the pas-16 attach. # # To overide the GUS defaults use: # options GUS_DMA2 # options GUS_DMA # options GUS_IRQ # # The i386/isa/sound/sound.doc has more information. # Controls all "VOXWARE" driver sound devices. See Luigi's driver # below for an alternate which may work better for some cards. # controller snd0 device pas0 at isa? port 0x388 irq 10 drq 6 device sb0 at isa? port 0x220 irq 5 drq 1 device sbxvi0 at isa? drq 5 device sbmidi0 at isa? port 0x330 device awe0 at isa? port 0x620 device gus0 at isa? port 0x220 irq 12 drq 1 #device gus0 at isa? port 0x220 irq 12 drq 1 flags 0x3 device mss0 at isa? port 0x530 irq 10 drq 1 device css0 at isa? port 0x534 irq 5 drq 1 flags 0x08 device sscape0 at isa? port 0x330 irq 9 drq 0 device trix0 at isa? port 0x330 irq 6 drq 0 device sscape_mss0 at isa? port 0x534 irq 5 drq 1 device opl0 at isa? port 0x388 device mpu0 at isa? port 0x330 irq 6 drq 0 device uart0 at isa? port 0x330 irq 5 # Luigi's snd code (use INSTEAD of snd0 and all VOXWARE drivers!). # You may also wish to enable the pnp controller with this, for pnp # sound cards. # #device pcm0 at isa? port ? tty irq 10 drq 1 flags 0x0 # Not controlled by `snd' device pca0 at isa? port "IO_TIMER1" tty # # Miscellaneous hardware: # # mcd: Mitsumi CD-ROM # scd: Sony CD-ROM # matcd: Matsushita/Panasonic CD-ROM # wt: Wangtek and Archive QIC-02/QIC-36 tape drives # ctx: Cortex-I frame grabber # apm: Laptop Advanced Power Management (experimental) # spigot: The Creative Labs Video Spigot video-acquisition board # meteor: Matrox Meteor video capture board # bktr: Brooktree bt848/848a/849/878/879 family video capture and TV Tuner board # cy: Cyclades serial driver # dgb: Digiboard PC/Xi and PC/Xe series driver (ALPHA QUALITY!) # dgm: Digiboard PC/Xem driver # gp: National Instruments AT-GPIB and AT-GPIB/TNT board # asc: GI1904-based hand scanners, e.g. the Trust Amiscan Grey # gsc: Genius GS-4500 hand scanner. # joy: joystick # labpc: National Instrument's Lab-PC and Lab-PC+ # rc: RISCom/8 multiport card # rp: Comtrol Rocketport(ISA) - single card # tw: TW-523 power line interface for use with X-10 home control products # si: Specialix SI/XIO 4-32 port terminal multiplexor # stl: Stallion EasyIO and EasyConnection 8/32 (cd1400 based) # stli: Stallion EasyConnection 8/64, ONboard, Brumby (intelligent) # Notes on APM # The flags takes the following meaning for apm0: # 0x0020 Statclock is broken. # 0x0011 Limit APM protocol to 1.1 or 1.0 # 0x0010 Limit APM protocol to 1.0 # If apm is omitted, some systems require sysctl -w kern.timcounter.method=1 # for correct timekeeping. # Notes on the spigot: # The video spigot is at 0xad6. This port address can not be changed. # The irq values may only be 10, 11, or 15 # I/O memory is an 8kb region. Possible values are: # 0a0000, 0a2000, ..., 0fffff, f00000, f02000, ..., ffffff # The start address must be on an even boundary. # Add the following option if you want to allow non-root users to be able # to access the spigot. This option is not secure because it allows users # direct access to the I/O page. # options SPIGOT_UNSECURE # Notes on the Comtrol Rocketport driver: # # The exact values used for rp0 depend on how many boards you have # in the system. The manufacturer's sample configs are listed as: # # Comtrol Rocketport ISA single card # device rp0 at isa? port 0x280 tty # # If instead you have two ISA cards, one installed at 0x100 and the # second installed at 0x180, then you should add the following to # your kernel configuration file: # # device rp0 at isa? port 0x100 tty # device rp1 at isa? port 0x180 tty # # For 4 ISA cards, it might be something like this: # # device rp0 at isa? port 0x180 tty # device rp1 at isa? port 0x100 tty # device rp2 at isa? port 0x340 tty # device rp3 at isa? port 0x240 tty # # And for PCI cards, you only need say: # # device rp0 # device rp1 # ... # Note: Make sure that any Rocketport PCI devices are specified BEFORE the # ISA Rocketport devices. # Notes on the Digiboard driver: # # The following flag values have special meanings: # 0x01 - alternate layout of pins (dgb & dgm) # 0x02 - use the windowed PC/Xe in 64K mode (dgb only) # Notes on the Specialix SI/XIO driver: # **This is NOT a Specialix supported Driver!** # The host card is memory, not IO mapped. # The Rev 1 host cards use a 64K chunk, on a 32K boundary. # The Rev 2 host cards use a 32K chunk, on a 32K boundary. # The cards can use an IRQ of 11, 12 or 15. # Notes on the Stallion stl and stli drivers: # See src/i386/isa/README.stl for complete instructions. # This is version 0.0.5alpha, unsupported by Stallion. # The stl driver has a secondary IO port hard coded at 0x280. You need # to change src/i386/isa/stallion.c if you reconfigure this on the boards. # The "flags" and "iosiz" settings on the stli driver depend on the board: # EasyConnection 8/64 ISA: flags 23 iosiz 0x1000 # EasyConnection 8/64 EISA: flags 24 iosiz 0x10000 # EasyConnection 8/64 MCA: flags 25 iosiz 0x1000 # ONboard ISA: flags 4 iosiz 0x10000 # ONboard EISA: flags 7 iosiz 0x10000 # ONboard MCA: flags 3 iosiz 0x10000 # Brumby: flags 2 iosiz 0x4000 # Stallion: flags 1 iosiz 0x10000 device mcd0 at isa? port 0x300 bio irq 10 # for the Sony CDU31/33A CDROM device scd0 at isa? port 0x230 bio # for the SoundBlaster 16 multicd - up to 4 devices controller matcd0 at isa? port 0x230 bio device wt0 at isa? port 0x300 bio irq 5 drq 1 device ctx0 at isa? port 0x230 iomem 0xd0000 device spigot0 at isa? port 0xad6 irq 15 iomem 0xee000 -device apm0 at isa? +device apm0 at nexus? device gp0 at isa? port 0x2c0 tty device gsc0 at isa? port "IO_GSC1" tty drq 3 device joy0 at isa? port IO_GAME device cy0 at isa? tty irq 10 iomem 0xd4000 iosiz 0x2000 options CY_PCI_FASTINTR # Use with cy_pci unless irq is shared device dgb0 at isa? port 0x220 iomem 0xfc000 iosiz ? tty options "NDGBPORTS=16" # Defaults to 16*NDGB device dgm0 at isa? port 0x104 iomem 0xd0000 iosiz ? tty device labpc0 at isa? port 0x260 tty irq 5 device rc0 at isa? port 0x220 tty irq 12 device rp0 at isa? port 0x280 tty # the port and irq for tw0 are fictitious device tw0 at isa? port 0x380 tty irq 11 device si0 at isa? iomem 0xd0000 tty irq 12 device asc0 at isa? port "IO_ASC1" tty drq 3 irq 10 device stl0 at isa? port 0x2a0 tty irq 10 device stli0 at isa? port 0x2a0 tty iomem 0xcc000 flags 23 iosiz 0x1000 # You are unlikely to have the hardware for loran0 device loran0 at isa? port ? tty irq 5 # HOT1 Xilinx 6200 card (www.vcc.com) device xrpu0 # # EISA devices: # # The EISA bus device is eisa0. It provides auto-detection and # configuration support for all devices on the EISA bus. # # The `ahb' device provides support for the Adaptec 174X adapter. # # The `ahc' device provides support for the Adaptec 274X and 284X # adapters. The 284X, although a VLB card responds to EISA probes. # # fea: DEC DEFEA EISA FDDI adapter # controller eisa0 controller ahb0 controller ahc0 device fea0 # The aic7xxx driver will attempt to use memory mapped I/O for all PCI # controllers that have it configured only if this option is set. Unfortunately, # this doesn't work on some motherboards, which prevents it from being the # default. options AHC_ALLOW_MEMIO # By default, only 10 EISA slots are probed, since the slot numbers # above clash with the configuration address space of the PCI subsystem, # and the EISA probe is not very smart about this. This is sufficient # for most machines, but in particular the HP NetServer LC series comes # with an onboard AIC7770 dual-channel SCSI controller on EISA slot #11, # thus you need to bump this figure to 12 for them. options "EISA_SLOTS=12" # # PCI devices & PCI options: # # The main PCI bus device is `pci'. It provides auto-detection and # configuration support for all devices on the PCI bus, using either # configuration mode defined in the PCI specification. # # The `ahc' device provides support for the Adaptec 29/3940(U)(W) # and motherboard based AIC7870/AIC7880 adapters. # # The `ncr' device provides support for the NCR 53C810 and 53C825 # self-contained SCSI host adapters. # # The `isp' device provides support for the Qlogic ISP 1020, 1040 # nd 1040B PCI SCSI host adapters, as well as the Qlogic ISP 2100 # FC/AL Host Adapter. # # The `ax' device provides support for PCI fast ethernet adapters # based on the ASIX Electronics AX88140A chip, including the Alfa # Inc. GFC2204. # # The `de' device provides support for the Digital Equipment DC21040 # self-contained Ethernet adapter. # # The `fxp' device provides support for the Intel EtherExpress Pro/100B # PCI Fast Ethernet adapters. # # The `mx' device provides support for various fast ethernet adapters # based on the Macronix 98713, 987615 ans 98725 series chips. # # The `pn' device provides support for various fast ethernet adapters # based on the Lite-On 82c168 and 82c169 PNIC chips, including the # LinkSys LNE100TX, the NetGear FA310TX rev. D1 and the Matrox # FastNIC 10/100. # # The 'rl' device provides support for PCI fast ethernet adapters based # on the RealTek 8129/8139 chipset. Note that the RealTek driver defaults # to useing programmed I/O to do register accesses because memory mapped # mode seems to cause severe lockups on SMP hardware. This driver also # supports the Accton EN1207D `Cheetah' adapter, which uses a chip called # the MPX 5030/5038, which is either a RealTek in disguise or a RealTek # workalike. # # The 'ti' device provides support for PCI gigabit ethernet NICs based # on the Alteon Networks Tigon 1 and Tigon 2 chipsets. This includes the # Alteon AceNIC, the 3Com 3c985, the Netgear GA620 and various others. # Note that you will probably want to bump up NBMCLUSTERS a lot to use # this driver. # # The 'tl' device provides support for the Texas Instruments TNETE100 # series 'ThunderLAN' cards and integrated ethernet controllers. This # includes several Compaq Netelligent 10/100 cards and the built-in # ethernet controllers in several Compaq Prosignia, Proliant and # Deskpro systems. It also supports several Olicom 10Mbps and 10/100 # boards. # # The `tx' device provides support for the SMC 9432TX cards. # # The `vr' device provides support for various fast ethernet adapters # based on the VIA Technologies VT3043 `Rhine I' and VT86C100A `Rhine II' # chips, including the D-Link DFE530TX. # # The `vx' device provides support for the 3Com 3C590 and 3C595 # early support # # The `wb' device provides support for various fast ethernet adapters # based on the Winbond W89C840F chip. Note: this is not the same as # the Winbond W89C940F, which is an NE2000 clone. # # The `xl' device provides support for the 3Com 3c900, 3c905 and # 3c905B (Fast) Etherlink XL cards and integrated controllers. This # includes the integrated 3c905B-TX chips in certain Dell Optiplex and # Dell Precision desktop machines and the integrated 3c905-TX chips # in Dell Latitude laptop docking stations. # # The `fpa' device provides support for the Digital DEFPA PCI FDDI # adapter. pseudo-device fddi is also needed. # # The `meteor' device is a PCI video capture board. It can also have the # following options: # options METEOR_ALLOC_PAGES=xxx preallocate kernel pages for data entry # figure (ROWS*COLUMN*BYTES_PER_PIXEL*FRAME+PAGE_SIZE-1)/PAGE_SIZE # options METEOR_DEALLOC_PAGES remove all allocated pages on close(2) # options METEOR_DEALLOC_ABOVE=xxx remove all allocated pages above the # specified amount. If this value is below the allocated amount no action # taken # options METEOR_SYSTEM_DEFAULT={METEOR_PAL|METEOR_NTSC|METEOR_SECAM}, used # for initialization of fps routine when a signal is not present. # # The 'bktr' device is a PCI video capture device using the Brooktree # bt848/bt848a/bt849/bt878/bt879 chipset. When used with a TV Tuner it forms a # TV card, eg Miro PC/TV,Hauppauge WinCast/TV WinTV, VideoLogic Captivator, # Intel Smart Video III, AverMedia, IMS Turbo. # The following options can be used to override the auto detection # options OVERRIDE_CARD=xxx # options OVERRIDE_TUNER=xxx # options OVERRIDE_MSP=1 # options OVERRIDE_DBX=1 # The current values are found in /usr/src/sys/pci/brooktree848.c # # options BROOKTREE_SYSTEM_DEFAULT=BROOKTREE_PAL # This is required for Dual Crystal (28&35Mhz) boards where PAL is used # to prevent hangs during initialisation. eg VideoLogic Captivator PCI. # # PAL or SECAM users who have a 28Mhz crystal (and no 35Mhz crystal) # must enable PLL mode with this option. eg some new Hauppauge cards. # options BKTR_USE_PLL # # Using sysctl(8) run-time overrides on a per-card basis can be made # # The "oltr" driver supports the following Olicom PCI token-ring adapters # OC-3136, OC-3137, OC-3139, OC-3140, OC-3141, OC-3540, OC-3250 # -controller pci0 +controller pci0 at nexus? controller ahc1 controller ncr0 controller isp0 # # Options for ISP # # SCSI_ISP_NO_FWLOAD_MASK - mask of isp unit numbers (obviously # a max of 32) that you wish to disable # to disable the loading of firmware on. # SCSI_ISP_NO_NVRAM_MASK - mask of isp unit numbers (obviously # a max of 32) that you wish to disable # them picking up information from NVRAM # (for broken cards you can't fix the NVRAM # on- very rare, or for systems you can't # change NVRAM on (e.g. alpha) and you don't # like what's in there) # SCSI_ISP_PREFER_MEM_MAP - control preference for using memory mappings # instead of I/O space mappings. It defaults # to 1 for i386, 0 for alpha. Set to 1 to # unconditionally prefer mapping memory, # else it will use I/O space mappings. Of # course, this can fail if the PCI implement- # ation doesn't support what you want. # # SCSI_ISP_FABRIC enable loading of Fabric f/w flavor (2100). # SCSI_ISP_SCCLUN enable loading of expanded lun f/w (2100). # # ISP_DISABLE_1020_SUPPORT Disable support for 1020/1040 cards # ISP_DISABLE_1080_SUPPORT Disable support for 1080/1240 cards # ISP_DISABLE_2100_SUPPORT Disable support for 2100 cards # (these really just to save code space) # (use of all three will cause the driver to not compile) options SCSI_ISP_NO_FWLOAD_MASK="0x12" # disable FW load for isp1 and isp4 options SCSI_ISP_NO_NVRAM_MASK="0x1" # disable NVRAM for isp0 options SCSI_ISP_PREFER_MEM_MAP="0" # prefer I/O mapping #options "ISP_DISABLE_1020_SUPPORT" #options "ISP_DISABLE_1080_SUPPORT" #options "ISP_DISABLE_2100_SUPPORT" device ax0 device de0 device fxp0 device mx0 device pn0 device rl0 device ti0 device tl0 device tx0 device vr0 device vx0 device wb0 device xl0 device fpa0 device meteor0 device oltr0 # Brooktree driver has been ported to the new I2C framework. Thus, # you'll need at least iicbus, iicbb and smbus. iic/smb are only needed if you # want to control other I2C slaves connected to the external connector of # some cards. # device bktr0 # # PCI options # #options PCI_QUIET #quiets PCI code on chipset settings # # PCCARD/PCMCIA # # card: slot controller # pcic: slots controller card0 device pcic0 at card? device pcic1 at card? # You may need to reset all pccards after resuming options PCIC_RESUME_RESET # reset after resume # # Laptop/Notebook options: # # See also: # apm under `Miscellaneous hardware' # above. # For older notebooks that signal a powerfail condition (external # power supply dropped, or battery state low) by issuing an NMI: options POWERFAIL_NMI # make it beep instead of panicing # # SMB bus # # System Management Bus support provided by the 'smbus' device. # # Supported devices: # smb standard io # # Supported interfaces: # iicsmb I2C to SMB bridge with any iicbus interface # bktr brooktree848 I2C hardware interface # intpm Intel PIIX4 Power Management Unit # alpm Acer Aladdin-IV/V/Pro2 Power Management Unit # controller smbus0 controller intpm0 controller alpm0 device smb0 at smbus? # # I2C Bus # # Philips i2c bus support is provided by the `iicbus' device. # # Supported devices: # ic i2c network interface # iic i2c standard io # iicsmb i2c to smb bridge. Allow i2c i/o with smb commands. # # Supported interfaces: # pcf Philips PCF8584 ISA-bus controller # bktr brooktree848 I2C software interface # # Other: # iicbb generic I2C bit-banging code (needed by lpbb, bktr) # controller iicbus0 controller iicbb0 device ic0 at iicbus? device iic0 at iicbus? device iicsmb0 at iicbus? controller pcf0 at isa? port 0x320 net irq 5 # ISDN4BSD section # i4b passive ISDN cards support (isic - I4b Siemens Isdn Chipset driver) # note that the ``options'' and ``device'' lines must BOTH be defined ! # # Non-PnP Cards: # -------------- # # Teles S0/8 or Niccy 1008 options "TEL_S0_8" #device isic0 at isa? iomem 0xd0000 net irq 5 flags 1 # # Teles S0/16 or Creatix ISDN-S0 or Niccy 1016 options "TEL_S0_16" #device isic0 at isa? port 0xd80 iomem 0xd0000 net irq 5 flags 2 # # Teles S0/16.3 options "TEL_S0_16_3" #device isic0 at isa? port 0xd80 net irq 5 flags 3 # # AVM A1 or AVM Fritz!Card options "AVM_A1" #device isic0 at isa? port 0x340 net irq 5 flags 4 # # USRobotics Sportster ISDN TA intern options "USR_STI" #device isic0 at isa? port 0x268 net irq 5 flags 7 # # ITK ix1 Micro options "ITKIX1" #device isic0 at isa? port 0x398 net irq 10 flags 18 # # PnP-Cards: # ---------- # # Teles S0/16.3 PnP options "TEL_S0_16_3_P" #device isic0 at isa? port ? net irq ? # # Creatix ISDN-S0 P&P options "CRTX_S0_P" #device isic0 at isa? port ? net irq ? # # Dr. Neuhaus Niccy Go@ options "DRN_NGO" #device isic0 at isa? port ? net irq ? # # Sedlbauer Win Speed options "SEDLBAUER" #device isic0 at isa? port ? net irq ? # # Dynalink IS64PH options "DYNALINK" #device isic0 at isa? port ? net irq ? # # ELSA QuickStep 1000pro ISA options "ELSA_QS1ISA" #device isic0 at isa? port ? net irq ? # # PCI-Cards: # ---------- # # ELSA QuickStep 1000pro PCI options "ELSA_QS1PCI" #device isic0 # # PCMCIA-Cards: # ------------- # # AVM PCMCIA Fritz!Card options "AVM_A1_PCMCIA" device isic0 at isa? port 0x340 net irq 5 flags 10 # # Active Cards: # ------------- # # Stollmann Tina-dd control device device tina0 at isa? port 0x260 net irq 10 # # ISDN Protocol Stack # ------------------- # # Q.921 / layer 2 - i4b passive cards D channel handling pseudo-device "i4bq921" # # Q.931 / layer 3 - i4b passive cards D channel handling pseudo-device "i4bq931" # # layer 4 - i4b common passive and active card handling pseudo-device "i4b" # # ISDN devices # ------------ # # userland driver to do ISDN tracing (for passive cards only) pseudo-device "i4btrc" 4 # # userland driver to control the whole thing pseudo-device "i4bctl" # # userland driver for access to raw B channel pseudo-device "i4brbch" 4 # # userland driver for telephony pseudo-device "i4btel" 2 # # network driver for IP over raw HDLC ISDN pseudo-device "i4bipr" 4 # enable VJ header compression detection for ipr i/f options IPR_VJ # # network driver for sync PPP over ISDN pseudo-device "i4bisppp" 4 # Parallel-Port Bus # # Parallel port bus support is provided by the `ppbus' device. # Multiple devices may be attached to the parallel port, devices # are automatically probed and attached when found. # # Supported devices: # vpo Iomega Zip Drive # Requires SCSI disk support ('scbus' and 'da'), best # performance is achieved with ports in EPP 1.9 mode. # lpt Parallel Printer # plip Parallel network interface # ppi General-purpose I/O ("Geek Port") + IEEE1284 I/O # pps Pulse per second Timing Interface # lpbb Philips official parallel port I2C bit-banging interface # # Supported interfaces: # ppc ISA-bus parallel port interfaces. # options "DEBUG_1284" # IEEE1284 signaling protocol debug options "PERIPH_1284" # Makes your computer act as a IEEE1284 # compliant peripheral options "DONTPROBE_1284"# Avoid boot detection of PnP parallel devices options "VP0_DEBUG" # ZIP/ZIP+ debug options "LPT_DEBUG" # Printer driver debug options "PPC_DEBUG" # Parallel chipset level debug options "PLIP_DEBUG" # Parallel network IP interface debug controller ppbus0 controller vpo0 at ppbus? device lpt0 at ppbus? device plip0 at ppbus? device ppi0 at ppbus? device pps0 at ppbus? device lpbb0 at ppbus? device ppc0 at isa? port? tty irq 7 # Kernel BOOTP support options BOOTP # Use BOOTP to obtain IP address/hostname options BOOTP_NFSROOT # NFS mount root filesystem using BOOTP info options "BOOTP_NFSV3" # Use NFS v3 to NFS mount root options BOOTP_COMPAT # Workaround for broken bootp daemons. options "BOOTP_WIRED_TO=fxp0" # Use interface fxp0 for BOOTP # # Add tie-ins for a hardware watchdog. This only enable the hooks; # the user must still supply the actual driver. # options HW_WDOG # # Set the number of PV entries per process. Increasing this can # stop panics related to heavy use of shared memory. However, that can # (combined with large amounts of physical memory) cause panics at # boot time due the kernel running out of VM space. # # If you're tweaking this, you might also want to increase the sysctls # "vm.v_free_min", "vm.v_free_reserved", and "vm.v_free_target". # # The value below is the one more than the default. # options "PMAP_SHPGPERPROC=201" # # Disable swapping. This option removes all code which actually performs # swapping, so it's not possible to turn it back on at run-time. # # This is sometimes usable for systems which don't have any swap space # (see also sysctls "vm.defer_swapspace_pageouts" and # "vm.disable_swapspace_pageouts") # #options NO_SWAPPING # Set the number of sf_bufs to allocate. sf_bufs are virtual buffers # for sendfile(2) that are used to map file VM pages, and normally # default to a quantity that is roughly 16*MAXUSERS+512. You would # typically want about 4 of these for each simultaneous file send. # options "NSFBUFS=1024" # # Enable extra debugging code for locks. This stores the filename and # line of whatever aquired the lock in the lock itself, and change a # number of function calls to pass around the relevant data. This is # not at all useful unless you are debugging lock code. Also note # that it is likely to break e.g. fstat(1) unless you recompile your # userland with -DDEBUG_LOCKS as well. # options DEBUG_LOCKS # More undocumented options for linting. options CLK_CALIBRATION_LOOP options "CLK_USE_I8254_CALIBRATION" options CLK_USE_TSC_CALIBRATION options "TIMER_FREQ=((14318182+6)/12)" options CLUSTERDEBUG options COMPAT_LINUX options CPU_UPGRADE_HW_CACHE options DEBUG options DEBUG_VFS_LOCKS #options DISABLE_PSE options "I586_PMC_GUPROF=0x70000" options "IBCS2" options KEY options KEY_DEBUG options LOCKF_DEBUG options LOUTB options KBD_MAXRETRY=4 options KBD_MAXWAIT=6 options KBD_RESETDELAY=201 options KBDIO_DEBUG=2 options MSGMNB=2049 options MSGMNI=41 options MSGSEG=2049 options MSGSSZ=16 options MSGTQL=41 options NBUF=512 options NETATALKDEBUG options NMBCLUSTERS=1024 options NPX_DEBUG options PANIC_REBOOT_WAIT_TIME=16 options PSM_DEBUG=1 options SCSI_NCR_DEBUG options SCSI_NCR_DFLT_TAGS=4 options SCSI_NCR_MAX_SYNC=10000 options SCSI_NCR_MAX_WIDE=1 options SCSI_NCR_MYADDR=7 options SEMMAP=31 options SEMMNI=11 options SEMMNS=61 options SEMMNU=31 options SEMMSL=61 options SEMOPM=101 options SEMUME=11 options SHOW_BUSYBUFS # List buffers that prevent root unmount options SHMALL=1025 options "SHMMAX=(SHMMAXPGS*PAGE_SIZE+1)" options SHMMAXPGS=1025 options SHMMIN=2 options SHMMNI=33 options SHMSEG=9 options SI_DEBUG options SIMPLELOCK_DEBUG options SPX_HACK options VFS_BIO_DEBUG options ENABLE_ALART # The 'dpt' driver provides support for DPT controllers (http://www.dpt.com/). # These have hardware RAID-{0,1,5} support, and do multi-initiator I/O. # The DPT controllers are commonly re-licensed under other brand-names - # some controllers by Olivetti, Dec, HP, AT&T, SNI, AST, Alphatronic, NEC and # Compaq are actually DPT controllers. # # See sys/dev/dpt for debugging and other subtle options. # DPT_VERIFY_HINTR Performs some strict hardware interrupts testing. # Only use if you suspect PCI bus corruption problems # DPT_RESTRICTED_FREELIST Normally, the freelisat used by the DPT for queue # will grow to accomodate increased use. This growth # will NOT shrink. To restrict the number of queue # slots to exactly what the DPT can hold at one time, # enable this option. # DPT_MEASURE_PERFORMANCE Enables a set of (semi)invasive metrics. Various # instruments are enabled. The tools in # /usr/sbin/dpt_* assume these to be enabled. # DPT_FREELIST_IS_STACK For optimal L{1,2} CPU cache utilization, enable # this option. Otherwise, the transaction queue is # a LIFO. I cannot measure the performance gain. # DPT_HANDLE_TIMEOUTS Normally device timeouts are handled by the DPT. # If you ant the driver to handle timeouts, enable # this option. If your system is very busy, this # option will create more trouble than solve. # DPT_TIMEOUT_FACTOR Used to compute the excessive amount of time to # wait when timing out with the above option. # DPT_DEBUG_xxxx These are controllable from sys/dev/dpt/dpt.h # DPT_LOST_IRQ When enabled, will try, once per second, to catch # any interrupt that got lost. Seems to help in some # DPT-firmware/Motherboard combinations. Minimal # cost, great benefit. # DPT_RESET_HBA Make "reset" actually reset the controller # instead of fudging it. Only enable this if you # are 100% certain you need it. # DPT_SHUTDOWN_SLEEP Reset controller if a request take more than # this number of seconds. Do NOT enable this # unless you are really, really, really certain # you need it. You are advised to call Simon (the # driver author) before setting it, and NEVER, # EVER set it to less than 300s (5 minutes). controller dpt0 # DPT options options DPT_VERIFY_HINTR options DPT_RESTRICTED_FREELIST #!CAM# options DPT_MEASURE_PERFORMANCE options DPT_FREELIST_IS_STACK #!CAM# options DPT_HANDLE_TIMEOUTS options DPT_TIMEOUT_FACTOR=4 options DPT_INTR_DELAY=200 # Some motherboards need that options DPT_LOST_IRQ options DPT_RESET_HBA # Don't EVER set this without having talked to Simon Shapiro on the phone # first. options DPT_SHUTDOWN_SLEEP=500 # USB support # UHCI controller controller uhci0 # OHCI controller controller ohci0 # General USB code (mandatory for USB) controller usb0 # # for the moment we have to specify the priorities of the device # drivers explicitly by the ordering in the list below. This will # be changed in the future. # # USB mouse device ums0 # USB keyboard device ukbd0 # USB printer device ulpt0 # Human Interface Device (anything with buttons and dials) device uhid0 # Generic USB device driver device ugen0 # options UHCI_DEBUG options OHCI_DEBUG options USB_DEBUG options UHUB_DEBUG options UMS_DEBUG options UKBD_DEBUG options UMASS_DEBUG options UHID_DEBUG options UGEN_DEBUG options ULPT_DEBUG Index: head/sys/i386/conf/files.i386 =================================================================== --- head/sys/i386/conf/files.i386 (revision 45719) +++ head/sys/i386/conf/files.i386 (revision 45720) @@ -1,376 +1,379 @@ # This file tells config what files go into building a kernel, # files marked standard are always included. # -# $Id: files.i386,v 1.234 1999/04/13 19:38:10 peter Exp $ +# $Id: files.i386,v 1.235 1999/04/15 14:52:23 bde Exp $ # # The long compile-with and dependency lines are required because of # limitations in config: backslash-newline doesn't work in strings, and # dependency lines other than the first are silently ignored. # linux_genassym optional compat_linux \ dependency "$S/i386/linux/linux_genassym.c $S/i386/linux/linux.h" \ compile-with "${CC} ${CFLAGS} ${PARAM} -UKERNEL -o $@ $<" \ no-obj no-implicit-rule \ clean "linux_genassym" # linux_assym.h optional compat_linux \ dependency "linux_genassym" \ compile-with "./linux_genassym > $@" \ no-obj no-implicit-rule before-depend \ clean "linux_assym.h" # font8x16.o optional std8x16font \ compile-with "uudecode < /usr/share/syscons/fonts/${STD8X16FONT}-8x16.fnt && file2c 'unsigned char font_16[16*256] = {' '};' < ${STD8X16FONT}-8x16 > font8x16.c && ${CC} -c ${CFLAGS} font8x16.c" \ no-implicit-rule before-depend \ clean "${STD8X16FONT}-8x16 font8x16.c" # atkbdmap.h optional atkbd_dflt_keymap \ compile-with "kbdcontrol -L ${ATKBD_DFLT_KEYMAP} | sed -e 's/^static keymap_t.* = /static keymap_t key_map = /' -e 's/^static accentmap_t.* = /static accentmap_t accent_map = /' > atkbdmap.h" \ no-obj no-implicit-rule before-depend \ clean "atkbdmap.h" # ukbdmap.h optional ukbd_dflt_keymap \ compile-with "kbdcontrol -L ${UKBD_DFLT_KEYMAP} | sed -e 's/^static keymap_t.* = /static keymap_t key_map = /' -e 's/^static accentmap_t.* = /static accentmap_t accent_map = /' > ukbdmap.h" \ no-obj no-implicit-rule before-depend \ clean "ukbdmap.h" # dev/ata/ata-all.c optional ata device-driver dev/ata/ata-dma.c optional ata device-driver dev/ata/atapi-all.c optional ata device-driver dev/ata/ata-disk.c optional atadisk device-driver dev/ata/atapi-cd.c optional atapicd device-driver dev/ata/atapi-fd.c optional atapifd device-driver dev/ata/atapi-tape.c optional atapist device-driver dev/fb/fb.c optional fb device-driver dev/fb/fb.c optional vga device-driver dev/fb/splash.c optional splash dev/kbd/atkbd.c optional atkbd device-driver dev/kbd/atkbdc.c optional atkbdc device-driver dev/kbd/kbd.c optional atkbd device-driver dev/kbd/kbd.c optional kbd device-driver dev/kbd/kbd.c optional ukbd device-driver dev/syscons/syscons.c optional sc device-driver dev/syscons/scvidctl.c optional sc device-driver dev/syscons/scvesactl.c optional sc device-driver i386/apm/apm.c optional apm device-driver i386/apm/apm_setup.s optional apm i386/eisa/dpt_eisa.c optional eisa dpt device-driver i386/eisa/3c5x9.c optional ep device-driver i386/eisa/adv_eisa.c optional adv device-driver i386/eisa/ahc_eisa.c optional eisa ahc device-driver \ dependency "aic7xxx_reg.h $S/i386/eisa/ahc_eisa.c" i386/eisa/ahb.c optional ahb device-driver i386/eisa/bt_eisa.c optional bt device-driver i386/eisa/eisaconf.c optional eisa i386/eisa/if_vx_eisa.c optional vx device-driver i386/eisa/if_fea.c optional fea device-driver i386/i386/autoconf.c standard device-driver i386/i386/bios.c standard i386/i386/bioscall.s standard i386/i386/busdma_machdep.c standard i386/i386/cons.c standard i386/i386/db_disasm.c optional ddb i386/i386/db_interface.c optional ddb i386/i386/db_trace.c optional ddb i386/i386/elf_machdep.c standard i386/i386/exception.s standard i386/i386/globals.s standard i386/i386/i386-gdbstub.c optional ddb i386/i386/identcpu.c standard i386/i386/in_cksum.c optional inet i386/i386/initcpu.c standard # locore.s needs to be handled in Makefile to put it first. Otherwise it's # now normal. # i386/i386/locore.s standard i386/i386/machdep.c standard i386/i386/math_emulate.c optional math_emulate i386/i386/mem.c standard i386/i386/i686_mem.c standard i386/i386/mp_machdep.c optional smp i386/i386/mpapic.c optional smp i386/i386/mpboot.s optional smp i386/i386/mplock.s optional smp +i386/i386/nexus.c standard i386/i386/perfmon.c optional perfmon profiling-routine i386/i386/perfmon.c optional perfmon i386/i386/pmap.c standard i386/i386/procfs_machdep.c standard i386/i386/simplelock.s optional smp i386/i386/support.s standard i386/i386/swapgeneric.c standard i386/i386/swtch.s standard i386/i386/sys_machdep.c standard i386/i386/trap.c standard i386/i386/userconfig.c optional userconfig i386/i386/vm_machdep.c standard i386/i386/vm86.c optional vm86 i386/ibcs2/ibcs2_fcntl.c optional ibcs2 i386/ibcs2/ibcs2_stat.c optional ibcs2 i386/ibcs2/ibcs2_ipc.c optional ibcs2 i386/ibcs2/ibcs2_msg.c optional ibcs2 i386/ibcs2/ibcs2_misc.c optional ibcs2 i386/ibcs2/ibcs2_other.c optional ibcs2 i386/ibcs2/ibcs2_signal.c optional ibcs2 i386/ibcs2/ibcs2_ioctl.c optional ibcs2 i386/ibcs2/ibcs2_socksys.c optional ibcs2 i386/ibcs2/ibcs2_sysi86.c optional ibcs2 i386/ibcs2/ibcs2_util.c optional ibcs2 i386/ibcs2/ibcs2_isc.c optional ibcs2 i386/ibcs2/ibcs2_isc_sysent.c optional ibcs2 i386/ibcs2/ibcs2_xenix.c optional ibcs2 i386/ibcs2/ibcs2_xenix_sysent.c optional ibcs2 i386/ibcs2/ibcs2_errno.c optional ibcs2 i386/ibcs2/ibcs2_sysent.c optional ibcs2 i386/ibcs2/ibcs2_sysvec.c optional ibcs2 i386/ibcs2/imgact_coff.c optional ibcs2 i386/isa/adv_isa.c optional adv device-driver #i386/isa/aha1542.c optional aha device-driver i386/isa/aha_isa.c optional aha device-driver -i386/isa/atkbd_isa.c optional atkbd device-driver -i386/isa/atkbdc_isa.c optional atkbdc device-driver +isa/atkbd_isa.c optional atkbd device-driver +isa/atkbdc_isa.c optional atkbdc device-driver i386/isa/bt_isa.c optional bt device-driver i386/isa/clock.c standard i386/isa/cronyx.c optional cx device-driver i386/isa/ctx.c optional ctx device-driver i386/isa/cx.c optional cx device-driver i386/isa/cy.c optional cy device-driver i386/isa/diskslice_machdep.c standard i386/isa/elink.c optional ep device-driver i386/isa/elink.c optional ie device-driver i386/isa/fd.c optional fd device-driver i386/isa/gpib.c optional gp device-driver i386/isa/asc.c optional asc device-driver i386/isa/gsc.c optional gsc device-driver i386/isa/if_ar.c optional ar device-driver i386/isa/if_cs.c optional cs device-driver i386/isa/if_cx.c optional cx device-driver i386/isa/if_ed.c optional ed device-driver i386/isa/if_el.c optional el device-driver i386/isa/if_ep.c optional ep device-driver i386/isa/if_ex.c optional ex device-driver i386/isa/if_fe.c optional fe device-driver i386/isa/if_ie.c optional ie device-driver i386/isa/if_le.c optional le device-driver i386/isa/if_lnc.c optional lnc device-driver i386/isa/if_rdp.c optional rdp device-driver i386/isa/if_sr.c optional sr device-driver i386/isa/if_wl.c optional wl device-driver i386/isa/if_ze.c optional ze device-driver i386/isa/if_zp.c optional zp device-driver contrib/dev/oltr/if_oltr.c optional oltr device-driver trlld.o optional oltr device-driver \ dependency "$S/contrib/dev/oltr/i386-${KERNFORMAT}.trlld.o.uu" \ compile-with "uudecode < $S/contrib/dev/oltr/i386-${KERNFORMAT}.trlld.o.uu" \ no-implicit-rule contrib/dev/oltr/trlldmac.c optional oltr device-driver contrib/dev/oltr/trlldhm.c optional oltr device-driver contrib/dev/oltr/trlldbm.c optional oltr device-driver i386/isa/ipl_funcs.c standard \ compile-with "${CC} -c ${CFLAGS} ${DEFINED_PROF:S/^$/-fomit-frame-pointer/} $<" i386/isa/intr_machdep.c standard i386/isa/isa.c optional isa device-driver i386/isa/istallion.c optional stli device-driver i386/isa/joy.c optional joy device-driver i386/isa/loran.c optional loran device-driver i386/isa/labpc.c optional labpc device-driver i386/isa/mcd.c optional mcd device-driver i386/isa/mse.c optional mse device-driver i386/isa/npx.c mandatory npx device-driver i386/isa/pcaudio.c optional pca device-driver i386/isa/matcd/matcd.c optional matcd device-driver +i386/isa/isa_compat.c optional isa device-driver +i386/isa/isa_dma.c optional isa device-driver i386/isa/pcibus.c optional pci device-driver i386/isa/pcicx.c optional ze device-driver i386/isa/pcicx.c optional zp device-driver i386/isa/pcvt/pcvt_drv.c optional vt device-driver i386/isa/pcvt/pcvt_ext.c optional vt device-driver i386/isa/pcvt/pcvt_kbd.c optional vt device-driver i386/isa/pcvt/pcvt_out.c optional vt device-driver i386/isa/pcvt/pcvt_sup.c optional vt device-driver i386/isa/pcvt/pcvt_vtf.c optional vt device-driver i386/isa/pnp.c optional pnp device-driver i386/isa/prof_machdep.c optional profiling-routine i386/isa/ppc.c optional ppc device-driver i386/isa/pcf.c optional pcf device-driver -i386/isa/psm.c optional psm device-driver +isa/psm.c optional psm device-driver i386/isa/random_machdep.c standard i386/isa/rc.c optional rc device-driver i386/isa/rp.c optional rp device-driver i386/isa/scd.c optional scd device-driver i386/isa/si.c optional si device-driver i386/isa/si2_z280.c optional si device-driver i386/isa/si3_t225.c optional si device-driver -i386/isa/sio.c optional sio device-driver +isa/sio.c optional sio device-driver i386/isa/snd/sound.c optional pcm device-driver i386/isa/snd/dmabuf.c optional pcm device-driver i386/isa/snd/ad1848.c optional pcm device-driver i386/isa/snd/sb_dsp.c optional pcm device-driver i386/isa/snd/clones.c optional pcm device-driver i386/isa/sound/dev_table.c optional snd device-driver i386/isa/sound/soundcard.c optional snd device-driver i386/isa/sound/sound_switch.c optional snd device-driver i386/isa/sound/audio.c optional snd device-driver i386/isa/sound/dmabuf.c optional snd device-driver i386/isa/sound/sys_timer.c optional snd device-driver i386/isa/sound/sequencer.c optional snd device-driver i386/isa/sound/patmgr.c optional snd device-driver i386/isa/sound/adlib_card.c optional opl device-driver i386/isa/sound/opl3.c optional opl device-driver i386/isa/sound/gus_card.c optional gus device-driver i386/isa/sound/gus_midi.c optional gus device-driver i386/isa/sound/gus_vol.c optional gus device-driver i386/isa/sound/gus_wave.c optional gus device-driver i386/isa/sound/ics2101.c optional gus device-driver i386/isa/sound/sound_timer.c optional gus device-driver i386/isa/sound/sound_timer.c optional css device-driver i386/isa/sound/sound_timer.c optional mss device-driver i386/isa/sound/midi_synth.c optional gus device-driver i386/isa/sound/midibuf.c optional gus device-driver i386/isa/sound/ad1848.c optional gusxvi device-driver i386/isa/sound/ad1848.c optional gus device-driver i386/isa/sound/ad1848.c optional mss device-driver i386/isa/sound/ad1848.c optional css device-driver i386/isa/sound/sound_timer.c optional mss device-driver i386/isa/sound/midi_synth.c optional mss device-driver i386/isa/sound/midibuf.c optional mss device-driver i386/isa/sound/mpu401.c optional mpu device-driver i386/isa/sound/midi_synth.c optional mpu device-driver i386/isa/sound/midibuf.c optional mpu device-driver i386/isa/sound/pas2_card.c optional pas device-driver i386/isa/sound/pas2_midi.c optional pas device-driver i386/isa/sound/pas2_mixer.c optional pas device-driver i386/isa/sound/pas2_pcm.c optional pas device-driver i386/isa/sound/midi_synth.c optional pas device-driver i386/isa/sound/midibuf.c optional pas device-driver i386/isa/sound/sb_card.c optional sb device-driver i386/isa/sound/sb_dsp.c optional sb device-driver i386/isa/sound/sb_midi.c optional sb device-driver i386/isa/sound/sb_mixer.c optional sb device-driver i386/isa/sound/midi_synth.c optional sb device-driver i386/isa/sound/midibuf.c optional sb device-driver i386/isa/sound/sb16_dsp.c optional sbxvi device-driver i386/isa/sound/sb16_midi.c optional sbmidi device-driver i386/isa/sound/uart6850.c optional uart device-driver i386/isa/sound/midi_synth.c optional uart device-driver i386/isa/sound/midi_synth.c optional css device-driver i386/isa/sound/midibuf.c optional uart device-driver i386/isa/sound/midibuf.c optional css device-driver i386/isa/sound/trix.c optional trix device-driver i386/isa/sound/adlib_card.c optional trix device-driver i386/isa/sound/opl3.c optional trix device-driver i386/isa/sound/ad1848.c optional trix device-driver i386/isa/sound/sound_timer.c optional trix device-driver i386/isa/sound/sscape.c optional sscape device-driver i386/isa/sound/ad1848.c optional sscape device-driver i386/isa/sound/sound_timer.c optional sscape device-driver i386/isa/sound/mpu401.c optional sscape device-driver i386/isa/sound/midi_synth.c optional sscape device-driver i386/isa/sound/midibuf.c optional sscape device-driver i386/isa/sound/cs4232.c optional css device-driver i386/isa/spigot.c optional spigot device-driver i386/isa/spkr.c optional speaker device-driver i386/isa/stallion.c optional stl device-driver -i386/isa/syscons_isa.c optional sc device-driver +isa/syscons_isa.c optional sc device-driver i386/isa/vesa.c optional vga device-driver -i386/isa/vga_isa.c optional vga device-driver +isa/vga_isa.c optional vga device-driver i386/isa/tw.c optional tw device-driver i386/isa/wd.c optional wdc device-driver i386/isa/wd.c optional wd device-driver i386/isa/atapi.c optional wdc device-driver i386/isa/atapi-cd.c optional wcd device-driver i386/isa/wfd.c optional wfd device-driver i386/isa/wst.c optional wst device-driver i386/isa/wt.c optional wt device-driver i386/linux/imgact_linux.c optional compat_linux i386/linux/linux_dummy.c optional compat_linux i386/linux/linux_file.c optional compat_linux i386/linux/linux_ioctl.c optional compat_linux i386/linux/linux_ipc.c optional compat_linux i386/linux/linux_locore.s optional compat_linux \ dependency "linux_assym.h" i386/linux/linux_misc.c optional compat_linux i386/linux/linux_signal.c optional compat_linux i386/linux/linux_socket.c optional compat_linux i386/linux/linux_stats.c optional compat_linux i386/linux/linux_sysent.c optional compat_linux i386/linux/linux_sysvec.c optional compat_linux i386/linux/linux_util.c optional compat_linux i4b/layer1/i4b_isic.c optional isic device-driver i4b/layer1/i4b_isic_isa.c optional isic device-driver i4b/layer1/i4b_isic_pnp.c optional isic device-driver i4b/layer1/i4b_isic_pci.c optional isic device-driver i4b/layer1/i4b_isic_pcmcia.c optional isic device-driver i4b/layer1/i4b_isac.c optional isic device-driver i4b/layer1/i4b_hscx.c optional isic device-driver i4b/layer1/i4b_l1.c optional isic device-driver i4b/layer1/i4b_l1fsm.c optional isic device-driver i4b/layer1/i4b_bchan.c optional isic device-driver i4b/layer1/i4b_tel_s08.c optional isic device-driver i4b/layer1/i4b_tel_s016.c optional isic device-driver i4b/layer1/i4b_tel_s0163.c optional isic device-driver i4b/layer1/i4b_tel_s0P.c optional isic device-driver i4b/layer1/i4b_ctx_s0P.c optional isic device-driver i4b/layer1/i4b_avm_a1.c optional isic device-driver i4b/layer1/i4b_avm_fritz_pci.c optional isic device-driver i4b/layer1/i4b_avm_fritz_pcmcia.c optional isic device-driver i4b/layer1/i4b_usr_sti.c optional isic device-driver i4b/layer1/i4b_itk_ix1.c optional isic device-driver i4b/layer1/i4b_drn_ngo.c optional isic device-driver i4b/layer1/i4b_sws.c optional isic device-driver i4b/layer1/i4b_dynalink.c optional isic device-driver i4b/layer1/i4b_elsa_qs1i.c optional isic device-driver i4b/layer1/i4b_elsa_qs1p.c optional isic device-driver libkern/bcd.c standard libkern/divdi3.c standard libkern/inet_ntoa.c standard libkern/index.c standard libkern/mcount.c optional profiling-routine libkern/moddi3.c standard libkern/qdivrem.c standard libkern/qsort.c standard libkern/random.c standard libkern/rindex.c standard libkern/scanc.c standard libkern/skpc.c standard libkern/strcat.c standard libkern/strcmp.c standard libkern/strcpy.c standard libkern/strlen.c standard libkern/strncmp.c standard libkern/strncpy.c standard libkern/udivdi3.c standard libkern/umoddi3.c standard gnu/i386/fpemul/div_small.s optional gpl_math_emulate gnu/i386/fpemul/errors.c optional gpl_math_emulate gnu/i386/fpemul/fpu_arith.c optional gpl_math_emulate gnu/i386/fpemul/fpu_aux.c optional gpl_math_emulate gnu/i386/fpemul/fpu_entry.c optional gpl_math_emulate gnu/i386/fpemul/fpu_etc.c optional gpl_math_emulate gnu/i386/fpemul/fpu_trig.c optional gpl_math_emulate gnu/i386/fpemul/get_address.c optional gpl_math_emulate gnu/i386/fpemul/load_store.c optional gpl_math_emulate gnu/i386/fpemul/poly_2xm1.c optional gpl_math_emulate gnu/i386/fpemul/poly_atan.c optional gpl_math_emulate gnu/i386/fpemul/poly_div.s optional gpl_math_emulate gnu/i386/fpemul/poly_l2.c optional gpl_math_emulate gnu/i386/fpemul/poly_mul64.s optional gpl_math_emulate gnu/i386/fpemul/poly_sin.c optional gpl_math_emulate gnu/i386/fpemul/poly_tan.c optional gpl_math_emulate gnu/i386/fpemul/polynomial.s optional gpl_math_emulate gnu/i386/fpemul/reg_add_sub.c optional gpl_math_emulate gnu/i386/fpemul/reg_compare.c optional gpl_math_emulate gnu/i386/fpemul/reg_constant.c optional gpl_math_emulate gnu/i386/fpemul/reg_div.s optional gpl_math_emulate gnu/i386/fpemul/reg_ld_str.c optional gpl_math_emulate gnu/i386/fpemul/reg_mul.c optional gpl_math_emulate gnu/i386/fpemul/reg_norm.s optional gpl_math_emulate gnu/i386/fpemul/reg_round.s optional gpl_math_emulate gnu/i386/fpemul/reg_u_add.s optional gpl_math_emulate gnu/i386/fpemul/reg_u_div.s optional gpl_math_emulate gnu/i386/fpemul/reg_u_mul.s optional gpl_math_emulate gnu/i386/fpemul/reg_u_sub.s optional gpl_math_emulate gnu/i386/fpemul/wm_shrx.s optional gpl_math_emulate gnu/i386/fpemul/wm_sqrt.s optional gpl_math_emulate gnu/i386/isa/dgb.c optional dgb device-driver gnu/i386/isa/dgm.c optional dgm device-driver gnu/i386/isa/sound/awe_wave.c optional awe device-driver pci/es1370.c optional pcm device-driver pci/ide_pci.c optional wd device-driver Index: head/sys/i386/i386/autoconf.c =================================================================== --- head/sys/i386/i386/autoconf.c (revision 45719) +++ head/sys/i386/i386/autoconf.c (revision 45720) @@ -1,499 +1,507 @@ /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * William Jolitz. * * 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: @(#)autoconf.c 7.1 (Berkeley) 5/9/91 - * $Id: autoconf.c,v 1.111 1999/01/19 00:10:59 peter Exp $ + * $Id: autoconf.c,v 1.112 1999/04/15 14:52:24 bde Exp $ */ /* * Setup the system to run on the current machine. * * Configure() is called at boot time and initializes the vba * device tables and the memory controller monitoring. Available * devices are determined (from possibilities mentioned in ioconf.c), * and the drivers are initialized. */ #include "opt_bootp.h" #include "opt_ffs.h" #include "opt_cd9660.h" #include "opt_mfs.h" #include "opt_nfsroot.h" +#include "opt_bus.h" #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef APIC_IO #include #endif /* APIC_IO */ #include #include "isa.h" #if NISA > 0 +#ifdef OLD_BUS_ARCH #include +#else +device_t isa_bus_device = 0; +#endif /* OLD_BUS_ARCH */ #endif #include "pnp.h" #if NPNP > 0 +#ifdef OLD_BUS_ARCH #include #endif +#endif #include "eisa.h" #if NEISA > 0 #include #endif #include "pci.h" #if NPCI > 0 #include #endif -#include - static void configure_first __P((void *)); static void configure __P((void *)); static void configure_final __P((void *)); static void configure_finish __P((void)); static void configure_start __P((void)); static int setdumpdev __P((dev_t dev)); static void setroot __P((void)); SYSINIT(configure1, SI_SUB_CONFIGURE, SI_ORDER_FIRST, configure_first, NULL); /* SI_ORDER_SECOND is hookable */ SYSINIT(configure2, SI_SUB_CONFIGURE, SI_ORDER_THIRD, configure, NULL); /* SI_ORDER_MIDDLE is hookable */ SYSINIT(configure3, SI_SUB_CONFIGURE, SI_ORDER_ANY, configure_final, NULL); #if defined(CD9660) || defined(CD9660_ROOT) #include #include #include #include /* * XXX All this CD-ROM root stuff is fairly messy. Ick. * * We need to try out all our potential CDROM drives, so we need a table. */ static struct { char *name; int major; } try_cdrom[] = { { "cd", 6 }, { "mcd", 7 }, { "scd", 16 }, { "matcd", 17 }, { "wcd", 19 }, { 0, 0} }; static int find_cdrom_root __P((void)); static int find_cdrom_root() { int i, j, error; struct cdevsw *bd; dev_t orootdev; #if CD9660_ROOTDELAY > 0 DELAY(CD9660_ROOTDELAY * 1000000); #endif orootdev = rootdev; for (i = 0 ; i < 2; i++) for (j = 0 ; try_cdrom[j].name ; j++) { if (try_cdrom[j].major >= nblkdev) continue; rootdev = makedev(try_cdrom[j].major, i * 8); bd = bdevsw[major(rootdev)]; if (bd == NULL || bd->d_open == NULL) continue; if (bootverbose) printf("trying %s%d as rootdev (0x%x)\n", try_cdrom[j].name, i, rootdev); error = (bd->d_open)(rootdev, FREAD, S_IFBLK, curproc); if (error == 0) { if (bd->d_close != NULL) (bd->d_close)(rootdev, FREAD, S_IFBLK, curproc); return 0; } } rootdev = orootdev; return EINVAL; } #endif /* CD9660 || CD9660_ROOT */ #ifdef MFS_ROOT extern u_char *mfs_getimage __P((void)); #endif static void configure_start() { } static void configure_finish() { } +device_t nexus_dev; + /* * Determine i/o configuration for a machine. */ static void configure_first(dummy) void *dummy; { configure_start(); /* DDB hook? */ } static void configure(dummy) void *dummy; { /* Allow all routines to decide for themselves if they want intrs */ /* * XXX Since this cannot be achieved on all architectures, we should * XXX go back to disabling all interrupts until configuration is * XXX completed and switch any devices that rely on the current * XXX behavior to no longer rely on interrupts or to register an * XXX interrupt_driven_config_hook for the task. */ /* * XXX The above is wrong, because we're implicitly at splhigh(), * XXX and should stay there, so enabling interrupts in the CPU * XXX and the ICU at most gives pending interrupts which just get * XXX in the way. */ #ifdef APIC_IO bsp_apic_configure(); enable_intr(); #else enable_intr(); INTREN(IRQ_SLAVE); #endif /* APIC_IO */ #if NEISA > 0 eisa_configure(); #endif -#if NPCI > 0 - pci_configure(); -#endif - #if NPNP > 0 pnp_configure(); #endif -#if NISA > 0 - isa_configure(); -#endif + /* nexus0 is the top of the i386 device tree */ + device_add_child(root_bus, "nexus", 0, 0); /* initialize new bus architecture */ root_bus_configure(); + +#if NISA > 0 + if (isa_bus_device) + bus_generic_attach(isa_bus_device); +#endif /* * Now we're ready to handle (pending) interrupts. * XXX this is slightly misplaced. */ spl0(); /* * Allow lowering of the ipl to the lowest kernel level if we * panic (or call tsleep() before clearing `cold'). No level is * completely safe (since a panic may occur in a critical region * at splhigh()), but we want at least bio interrupts to work. */ safepri = cpl; } static void configure_final(dummy) void *dummy; { int i; configure_finish(); /* DDB hook? */ cninit_finish(); if (bootverbose) { #ifdef APIC_IO imen_dump(); #endif /* APIC_IO */ /* * Print out the BIOS's idea of the disk geometries. */ printf("BIOS Geometries:\n"); for (i = 0; i < N_BIOS_GEOM; i++) { unsigned long bios_geom; int max_cylinder, max_head, max_sector; bios_geom = bootinfo.bi_bios_geom[i]; /* * XXX the bootstrap punts a 1200K floppy geometry * when the get-disk-geometry interrupt fails. Skip * drives that have this geometry. */ if (bios_geom == 0x4f010f) continue; printf(" %x:%08lx ", i, bios_geom); max_cylinder = bios_geom >> 16; max_head = (bios_geom >> 8) & 0xff; max_sector = bios_geom & 0xff; printf( "0..%d=%d cylinders, 0..%d=%d heads, 1..%d=%d sectors\n", max_cylinder, max_cylinder + 1, max_head, max_head + 1, max_sector, max_sector); } printf(" %d accounted for\n", bootinfo.bi_n_bios_used); printf("Device configuration finished.\n"); } cold = 0; } void cpu_rootconf() { /* * XXX NetBSD has a much cleaner approach to finding root. * XXX We should adopt their code. */ #if defined(CD9660) || defined(CD9660_ROOT) if ((boothowto & RB_CDROM)) { if (bootverbose) printf("Considering CD-ROM root f/s.\n"); /* NB: find_cdrom_root() sets rootdev if successful. */ if (find_cdrom_root() == 0) mountrootfsname = "cd9660"; else if (bootverbose) printf("No CD-ROM available as root f/s.\n"); } #endif #ifdef MFS_ROOT if (!mountrootfsname) { if (bootverbose) printf("Considering MFS root f/s.\n"); if (mfs_getimage()) mountrootfsname = "mfs"; else if (bootverbose) printf("No MFS image available as root f/s.\n"); } #endif #ifdef BOOTP_NFSROOT if (!mountrootfsname && !nfs_diskless_valid) { if (bootverbose) printf("Considering BOOTP NFS root f/s.\n"); mountrootfsname = "nfs"; } #endif /* BOOTP_NFSROOT */ #if defined(NFS) || defined(NFS_ROOT) if (!mountrootfsname && nfs_diskless_valid) { if (bootverbose) printf("Considering NFS root f/s.\n"); mountrootfsname = "nfs"; } #endif /* NFS */ #if defined(FFS) || defined(FFS_ROOT) if (!mountrootfsname) { mountrootfsname = "ufs"; if (bootverbose) printf("Considering FFS root f/s.\n"); if (boothowto & RB_ASKNAME) setconf(); else setroot(); } #endif if (!mountrootfsname) { panic("Nobody wants to mount my root for me"); } } void cpu_dumpconf() { if (setdumpdev(dumpdev) != 0) dumpdev = NODEV; } static int setdumpdev(dev) dev_t dev; { int maj, psize; long newdumplo; if (dev == NODEV) { dumpdev = dev; return (0); } maj = major(dev); if (maj >= nblkdev || bdevsw[maj] == NULL) return (ENXIO); /* XXX is this right? */ if (bdevsw[maj]->d_psize == NULL) return (ENXIO); /* XXX should be ENODEV ? */ psize = bdevsw[maj]->d_psize(dev); if (psize == -1) return (ENXIO); /* XXX should be ENODEV ? */ /* * XXX should clean up checking in dumpsys() to be more like this, * and nuke dodump sysctl (too many knobs), and move this to * kern_shutdown.c... */ newdumplo = psize - Maxmem * PAGE_SIZE / DEV_BSIZE; if (newdumplo < 0) return (ENOSPC); dumpdev = dev; dumplo = newdumplo; return (0); } u_long bootdev = 0; /* not a dev_t - encoding is different */ #define FDMAJOR 2 #define FDUNITSHIFT 6 /* * Attempt to find the device from which we were booted. * If we can do so, and not instructed not to do so, * set rootdevs[] and rootdevnames[] to correspond to the * boot device(s). */ static void setroot() { int majdev, mindev, unit, slice, part; dev_t newrootdev; char partname[2]; char *sname; if (boothowto & RB_DFLTROOT || (bootdev & B_MAGICMASK) != B_DEVMAGIC) return; majdev = B_TYPE(bootdev); if (majdev >= nblkdev || bdevsw[majdev] == NULL) return; unit = B_UNIT(bootdev); slice = B_SLICE(bootdev); if (slice == WHOLE_DISK_SLICE) slice = COMPATIBILITY_SLICE; if (slice < 0 || slice >= MAX_SLICES) return; /* * XXX kludge for inconsistent unit numbering and lack of slice * support for floppies. */ if (majdev == FDMAJOR) { slice = COMPATIBILITY_SLICE; part = RAW_PART; mindev = unit << FDUNITSHIFT; } else { part = B_PARTITION(bootdev); mindev = dkmakeminor(unit, slice, part); } newrootdev = makedev(majdev, mindev); rootdevs[0] = newrootdev; sname = dsname(bdevsw[majdev]->d_name, unit, slice, part, partname); rootdevnames[0] = malloc(strlen(sname) + 2, M_DEVBUF, M_NOWAIT); sprintf(rootdevnames[0], "%s%s", sname, partname); /* * For properly dangerously dedicated disks (ones with a historical * bogus partition table), the boot blocks will give slice = 4, but * the kernel will only provide the compatibility slice since it * knows that slice 4 is not a real slice. Arrange to try mounting * the compatibility slice as root if mounting the slice passed by * the boot blocks fails. This handles the dangerously dedicated * case and perhaps others. */ if (slice == COMPATIBILITY_SLICE) return; slice = COMPATIBILITY_SLICE; rootdevs[1] = dkmodslice(newrootdev, slice); sname = dsname(bdevsw[majdev]->d_name, unit, slice, part, partname); rootdevnames[1] = malloc(strlen(sname) + 2, M_DEVBUF, M_NOWAIT); sprintf(rootdevnames[1], "%s%s", sname, partname); } static int sysctl_kern_dumpdev SYSCTL_HANDLER_ARGS { int error; dev_t ndumpdev; ndumpdev = dumpdev; error = sysctl_handle_opaque(oidp, &ndumpdev, sizeof ndumpdev, req); if (error == 0 && req->newptr != NULL) error = setdumpdev(ndumpdev); return (error); } SYSCTL_PROC(_kern, KERN_DUMPDEV, dumpdev, CTLTYPE_OPAQUE|CTLFLAG_RW, 0, sizeof dumpdev, sysctl_kern_dumpdev, "T,dev_t", ""); Index: head/sys/i386/i386/exception.s =================================================================== --- head/sys/i386/i386/exception.s (revision 45719) +++ head/sys/i386/i386/exception.s (revision 45720) @@ -1,372 +1,372 @@ /*- * Copyright (c) 1990 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. * - * $Id: exception.s,v 1.56 1999/02/25 11:03:08 bde Exp $ + * $Id: exception.s,v 1.57 1999/02/28 10:53:28 bde Exp $ */ #include "npx.h" #include "opt_vm86.h" #include #include #include #include #include #ifdef SMP #include /** CPL_AND_CML, REAL_ */ #endif #include "assym.s" #ifndef SMP #define ECPL_LOCK /* make these nops */ #define ECPL_UNLOCK #define ICPL_LOCK #define ICPL_UNLOCK #define FAST_ICPL_UNLOCK #define AICPL_LOCK #define AICPL_UNLOCK #define AVCPL_LOCK #define AVCPL_UNLOCK #endif /* SMP */ #define KCSEL 0x08 /* kernel code selector */ #define KDSEL 0x10 /* kernel data selector */ #define SEL_RPL_MASK 0x0003 #define TRAPF_CS_OFF (13 * 4) .text /*****************************************************************************/ /* Trap handling */ /*****************************************************************************/ /* * Trap and fault vector routines */ #define IDTVEC(name) ALIGN_TEXT; .globl __CONCAT(_X,name); __CONCAT(_X,name): #define TRAP(a) pushl $(a) ; jmp _alltraps /* * XXX - debugger traps are now interrupt gates so at least bdb doesn't lose * control. The sti's give the standard losing behaviour for ddb and kgdb. */ #ifdef BDE_DEBUGGER #define BDBTRAP(name) \ ss ; \ cmpb $0,_bdb_exists ; \ je 1f ; \ testb $SEL_RPL_MASK,4(%esp) ; \ jne 1f ; \ ss ; \ .globl __CONCAT(__CONCAT(bdb_,name),_ljmp); \ __CONCAT(__CONCAT(bdb_,name),_ljmp): \ ljmp $0,$0 ; \ 1: #else #define BDBTRAP(name) #endif #define BPTTRAP(a) testl $PSL_I,4+8(%esp) ; je 1f ; sti ; 1: ; TRAP(a) MCOUNT_LABEL(user) MCOUNT_LABEL(btrap) IDTVEC(div) pushl $0; TRAP(T_DIVIDE) IDTVEC(dbg) BDBTRAP(dbg) pushl $0; BPTTRAP(T_TRCTRAP) IDTVEC(nmi) pushl $0; TRAP(T_NMI) IDTVEC(bpt) BDBTRAP(bpt) pushl $0; BPTTRAP(T_BPTFLT) IDTVEC(ofl) pushl $0; TRAP(T_OFLOW) IDTVEC(bnd) pushl $0; TRAP(T_BOUND) IDTVEC(ill) pushl $0; TRAP(T_PRIVINFLT) IDTVEC(dna) pushl $0; TRAP(T_DNA) IDTVEC(fpusegm) pushl $0; TRAP(T_FPOPFLT) IDTVEC(tss) TRAP(T_TSSFLT) IDTVEC(missing) TRAP(T_SEGNPFLT) IDTVEC(stk) TRAP(T_STKFLT) IDTVEC(prot) TRAP(T_PROTFLT) IDTVEC(page) TRAP(T_PAGEFLT) IDTVEC(mchk) pushl $0; TRAP(T_MCHK) IDTVEC(rsvd) pushl $0; TRAP(T_RESERVED) IDTVEC(fpu) #if NNPX > 0 /* * Handle like an interrupt (except for accounting) so that we can - * call npxintr to clear the error. It would be better to handle + * call npx_intr to clear the error. It would be better to handle * npx interrupts as traps. This used to be difficult for nested * interrupts, but now it is fairly easy - mask nested ones the * same as SWI_AST's. */ pushl $0 /* dummy error code */ pushl $0 /* dummy trap type */ pushal pushl %ds pushl %es /* now stack frame is a trap frame */ movl $KDSEL,%eax movl %ax,%ds movl %ax,%es FAKE_MCOUNT(12*4(%esp)) #ifdef SMP MPLOCKED incl _cnt+V_TRAP FPU_LOCK ECPL_LOCK #ifdef CPL_AND_CML movl _cml,%eax pushl %eax /* save original cml */ orl $SWI_AST_MASK,%eax movl %eax,_cml #else movl _cpl,%eax pushl %eax /* save original cpl */ orl $SWI_AST_MASK,%eax movl %eax,_cpl #endif /* CPL_AND_CML */ ECPL_UNLOCK pushl $0 /* dummy unit to finish intr frame */ #else /* SMP */ movl _cpl,%eax pushl %eax pushl $0 /* dummy unit to finish intr frame */ incl _cnt+V_TRAP orl $SWI_AST_MASK,%eax movl %eax,_cpl #endif /* SMP */ - call _npxintr + call _npx_intr incb _intr_nesting_level MEXITCOUNT jmp _doreti #else /* NNPX > 0 */ pushl $0; TRAP(T_ARITHTRAP) #endif /* NNPX > 0 */ IDTVEC(align) TRAP(T_ALIGNFLT) SUPERALIGN_TEXT .globl _alltraps _alltraps: pushal pushl %ds pushl %es alltraps_with_regs_pushed: movl $KDSEL,%eax movl %ax,%ds movl %ax,%es FAKE_MCOUNT(12*4(%esp)) calltrap: FAKE_MCOUNT(_btrap) /* init "from" _btrap -> calltrap */ MPLOCKED incl _cnt+V_TRAP ALIGN_LOCK ECPL_LOCK #ifdef CPL_AND_CML movl _cml,%eax movl %eax,%ebx /* keep orig. cml here during trap() */ orl $SWI_AST_MASK,%eax movl %eax,_cml #else movl _cpl,%eax movl %eax,%ebx /* keep orig. cpl here during trap() */ orl $SWI_AST_MASK,%eax movl %eax,_cpl #endif ECPL_UNLOCK call _trap /* * Return via _doreti to handle ASTs. Have to change trap frame * to interrupt frame. */ pushl %ebx /* cpl to restore */ subl $4,%esp /* dummy unit to finish intr frame */ MPLOCKED incb _intr_nesting_level MEXITCOUNT jmp _doreti /* * Call gate entry for syscall. * The intersegment call has been set up to specify one dummy parameter. * This leaves a place to put eflags so that the call frame can be * converted to a trap frame. Note that the eflags is (semi-)bogusly * pushed into (what will be) tf_err and then copied later into the * final spot. It has to be done this way because esp can't be just * temporarily altered for the pushfl - an interrupt might come in * and clobber the saved cs/eip. */ SUPERALIGN_TEXT IDTVEC(syscall) pushfl /* save eflags in tf_err for now */ subl $4,%esp /* skip over tf_trapno */ pushal pushl %ds pushl %es movl $KDSEL,%eax /* switch to kernel segments */ movl %ax,%ds movl %ax,%es movl TF_ERR(%esp),%eax /* copy saved eflags to final spot */ movl %eax,TF_EFLAGS(%esp) movl $7,TF_ERR(%esp) /* sizeof "lcall 7,0" */ FAKE_MCOUNT(12*4(%esp)) MPLOCKED incl _cnt+V_SYSCALL SYSCALL_LOCK ECPL_LOCK #ifdef CPL_AND_CML movl $SWI_AST_MASK,_cml #else movl $SWI_AST_MASK,_cpl #endif ECPL_UNLOCK call _syscall /* * Return via _doreti to handle ASTs. */ pushl $0 /* cpl to restore */ subl $4,%esp /* dummy unit to finish intr frame */ movb $1,_intr_nesting_level MEXITCOUNT jmp _doreti /* * Call gate entry for Linux/NetBSD syscall (int 0x80) */ SUPERALIGN_TEXT IDTVEC(int0x80_syscall) subl $8,%esp /* skip over tf_trapno and tf_err */ pushal pushl %ds pushl %es movl $KDSEL,%eax /* switch to kernel segments */ movl %ax,%ds movl %ax,%es movl $2,TF_ERR(%esp) /* sizeof "int 0x80" */ FAKE_MCOUNT(12*4(%esp)) MPLOCKED incl _cnt+V_SYSCALL ALTSYSCALL_LOCK ECPL_LOCK #ifdef CPL_AND_CML movl $SWI_AST_MASK,_cml #else movl $SWI_AST_MASK,_cpl #endif ECPL_UNLOCK call _syscall /* * Return via _doreti to handle ASTs. */ pushl $0 /* cpl to restore */ subl $4,%esp /* dummy unit to finish intr frame */ movb $1,_intr_nesting_level MEXITCOUNT jmp _doreti ENTRY(fork_trampoline) call _spl0 #ifdef SMP cmpl $0,_switchtime jne 1f pushl $_switchtime call _microuptime popl %edx movl _ticks,%eax movl %eax,_switchticks 1: #endif /* * cpu_set_fork_handler intercepts this function call to * have this call a non-return function to stay in kernel mode. * initproc has its own fork handler, but it does return. */ pushl %ebx /* arg1 */ call %esi /* function */ addl $4,%esp /* cut from syscall */ /* * Return via _doreti to handle ASTs. */ pushl $0 /* cpl to restore */ subl $4,%esp /* dummy unit to finish intr frame */ movb $1,_intr_nesting_level MEXITCOUNT jmp _doreti #ifdef VM86 /* * Include vm86 call routines, which want to call _doreti. */ #include "i386/i386/vm86bios.s" #endif /* VM86 */ /* * Include what was once config+isa-dependent code. * XXX it should be in a stand-alone file. It's still icu-dependent and * belongs in i386/isa. */ #include "i386/isa/vector.s" /* * Include what was once icu-dependent code. * XXX it should be merged into this file (also move the definition of * imen to vector.s or isa.c). * Before including it, set up a normal asm environment so that vector.s * doesn't have to know that stuff is included after it. */ .data ALIGN_DATA .text SUPERALIGN_TEXT #include "i386/isa/ipl.s" Index: head/sys/i386/i386/legacy.c =================================================================== --- head/sys/i386/i386/legacy.c (nonexistent) +++ head/sys/i386/i386/legacy.c (revision 45720) @@ -0,0 +1,409 @@ +/* + * Copyright 1998 Massachusetts Institute of Technology + * + * Permission to use, copy, modify, and distribute this software and + * its documentation for any purpose and without fee is hereby + * granted, provided that both the above copyright notice and this + * permission notice appear in all copies, that both the above + * copyright notice and this permission notice appear in all + * supporting documentation, and that the name of M.I.T. not be used + * in advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. M.I.T. makes + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS + * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT + * SHALL M.I.T. 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. + * + * $Id$ + */ + +/* + * This code implements a `root nexus' for Intel Architecture + * machines. The function of the root nexus is to serve as an + * attachment point for both processors and buses, and to manage + * resources which are common to all of them. In particular, + * this code implements the core resource managers for interrupt + * requests, DMA requests (which rightfully should be a part of the + * ISA code but it's easier to do it here for now), I/O port addresses, + * and I/O memory address space. + */ + +#include "opt_smp.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#ifdef APIC_IO +#include +#include +#endif + +#include +#include +#include + +#include + +#include "eisa.h" +#include "isa.h" +#include "pci.h" +#include "npx.h" +#include "apm.h" + +static struct rman irq_rman, drq_rman, port_rman, mem_rman; + +static int nexus_probe(device_t); +static void nexus_print_child(device_t, device_t); +static struct resource *nexus_alloc_resource(device_t, device_t, int, int *, + u_long, u_long, u_long, u_int); +static int nexus_activate_resource(device_t, device_t, int, int, + struct resource *); +static int nexus_deactivate_resource(device_t, device_t, int, int, + struct resource *); +static int nexus_release_resource(device_t, device_t, int, int, + struct resource *); +static int nexus_setup_intr(device_t, device_t, struct resource *, + void (*)(void *), void *, void **); +static int nexus_teardown_intr(device_t, device_t, struct resource *, + void *); + +static device_method_t nexus_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, nexus_probe), + DEVMETHOD(device_attach, bus_generic_attach), + DEVMETHOD(device_detach, bus_generic_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + + /* Bus interface */ + DEVMETHOD(bus_print_child, nexus_print_child), + DEVMETHOD(bus_read_ivar, bus_generic_read_ivar), + DEVMETHOD(bus_write_ivar, bus_generic_write_ivar), + DEVMETHOD(bus_alloc_resource, nexus_alloc_resource), + DEVMETHOD(bus_release_resource, nexus_release_resource), + DEVMETHOD(bus_activate_resource, nexus_activate_resource), + DEVMETHOD(bus_deactivate_resource, nexus_deactivate_resource), + DEVMETHOD(bus_setup_intr, nexus_setup_intr), + DEVMETHOD(bus_teardown_intr, nexus_teardown_intr), + + { 0, 0 } +}; + +static driver_t nexus_driver = { + "nexus", + nexus_methods, + DRIVER_TYPE_MISC, + 1, /* no softc */ +}; +static devclass_t nexus_devclass; + +DRIVER_MODULE(nexus, root, nexus_driver, nexus_devclass, 0, 0); + +#ifdef APIC_IO +#define LASTIRQ (NINTR - 1) +#else +#define LASTIRQ 15 +#endif + +static int +nexus_probe(device_t dev) +{ + device_t child; + + device_quiet(dev); /* suppress attach message for neatness */ + + irq_rman.rm_start = 0; + irq_rman.rm_end = LASTIRQ; + irq_rman.rm_type = RMAN_ARRAY; + irq_rman.rm_descr = "Interrupt request lines"; + if (rman_init(&irq_rman) + || rman_manage_region(&irq_rman, 0, 1) + || rman_manage_region(&irq_rman, 3, LASTIRQ)) + panic("nexus_probe irq_rman"); + + drq_rman.rm_start = 0; + drq_rman.rm_end = 7; + drq_rman.rm_type = RMAN_ARRAY; + drq_rman.rm_descr = "DMA request lines"; + /* XXX drq 0 not available on some machines */ + if (rman_init(&drq_rman) + || rman_manage_region(&drq_rman, 0, 7)) + panic("nexus_probe drq_rman"); + + port_rman.rm_start = 0; + port_rman.rm_end = 0xffff; + port_rman.rm_type = RMAN_ARRAY; + port_rman.rm_descr = "I/O ports"; + if (rman_init(&port_rman) + || rman_manage_region(&port_rman, 0, 0xffff)) + panic("nexus_probe port_rman"); + + mem_rman.rm_start = 0; + mem_rman.rm_end = ~0u; + mem_rman.rm_type = RMAN_ARRAY; + mem_rman.rm_descr = "I/O memory addresses"; + if (rman_init(&mem_rman) + || rman_manage_region(&mem_rman, 0, ~0)) + panic("nexus_probe mem_rman"); + +#if NNPX > 0 + child = device_add_child(dev, "npx", 0, 0); + if (child == 0) + panic("nexus_probe npx"); +#endif /* NNPX > 0 */ +#if NAPM > 0 + child = device_add_child(dev, "apm", 0, 0); + if (child == 0) + panic("nexus_probe apm"); +#endif /* NAPM > 0 */ +#if NPCI > 0 + /* Add a PCI bridge if pci bus is present */ + if (pci_cfgopen() != 0) { + child = device_add_child(dev, "pcib", 0, 0); + if (child == 0) + panic("nexus_probe pcib"); + } +#endif +#if 0 && NEISA > 0 + child = device_add_child(dev, "eisa", 0, 0); + if (child == 0) + panic("nexus_probe eisa"); +#endif +#if NISA > 0 + /* Add an ISA bus directly if pci bus is not present */ + if (pci_cfgopen() == 0) { + child = device_add_child(dev, "isa", 0, 0); + if (child == 0) + panic("nexus_probe isa"); + } +#endif + return 0; +} + +static void +nexus_print_child(device_t bus, device_t child) +{ + printf(" on motherboard"); +} + +/* + * Allocate a resource on behalf of child. NB: child is usually going to be a + * child of one of our descendants, not a direct child of nexus0. + * (Exceptions include npx.) + */ +static struct resource * +nexus_alloc_resource(device_t bus, device_t child, int type, int *rid, + u_long start, u_long end, u_long count, u_int flags) +{ + struct resource *rv; + struct rman *rm; + + switch (type) { + case SYS_RES_IRQ: + rm = &irq_rman; + break; + + case SYS_RES_DRQ: + rm = &drq_rman; + break; + + case SYS_RES_IOPORT: + rm = &port_rman; + break; + + case SYS_RES_MEMORY: + rm = &mem_rman; + break; + + default: + return 0; + } + + rv = rman_reserve_resource(rm, start, end, count, flags, child); + if (rv == 0) + return 0; + + if (type == SYS_RES_MEMORY) { + caddr_t vaddr = 0; + + if (rv->r_end < 1024 * 1024 * 1024) { + /* + * The first 1Mb is mapped at KERNBASE. + */ + vaddr = (caddr_t)((uintptr_t)KERNBASE + rv->r_start); + } else { + u_int32_t paddr; + u_int32_t psize; + u_int32_t poffs; + + paddr = rv->r_start; + psize = rv->r_end - rv->r_start; + + poffs = paddr - trunc_page(paddr); + vaddr = (caddr_t) pmap_mapdev(paddr-poffs, psize+poffs) + poffs; + } + rman_set_virtual(rv, vaddr); + rman_set_bustag(rv, I386_BUS_SPACE_MEM); + rman_set_bushandle(rv, (bus_space_handle_t) vaddr); + } else if (type == SYS_RES_IOPORT) { + rman_set_bustag(rv, I386_BUS_SPACE_IO); + rman_set_bushandle(rv, rv->r_start); + } + return rv; +} + +static int +nexus_activate_resource(device_t bus, device_t child, int type, int rid, + struct resource *r) +{ + return (rman_activate_resource(r)); +} + +static int +nexus_deactivate_resource(device_t bus, device_t child, int type, int rid, + struct resource *r) +{ + return (rman_deactivate_resource(r)); +} + +static int +nexus_release_resource(device_t bus, device_t child, int type, int rid, + struct resource *r) +{ + return (rman_release_resource(r)); +} + +/* + * Currently this uses the really grody interface from kern/kern_intr.c + * (which really doesn't belong in kern/anything.c). Eventually, all of + * the code in kern_intr.c and machdep_intr.c should get moved here, since + * this is going to be the official interface. + */ +static int +nexus_setup_intr(device_t bus, device_t child, struct resource *irq, + void (*ihand)(void *), void *arg, void **cookiep) +{ + intrmask_t *mask; + driver_t *driver; + int error, icflags; + + if (child) + device_printf(child, "interrupting at irq %d\n", + (int)irq->r_start); + + *cookiep = 0; + if (irq->r_flags & RF_SHAREABLE) + icflags = 0; + else + icflags = INTR_EXCL; + + driver = device_get_driver(child); + switch (driver->type) { + case DRIVER_TYPE_TTY: + mask = &tty_imask; + break; + case (DRIVER_TYPE_TTY | DRIVER_TYPE_FAST): + mask = &tty_imask; + icflags |= INTR_FAST; + break; + case DRIVER_TYPE_BIO: + mask = &bio_imask; + break; + case DRIVER_TYPE_NET: + mask = &net_imask; + break; + case DRIVER_TYPE_CAM: + mask = &cam_imask; + break; + case DRIVER_TYPE_MISC: + mask = 0; + break; + default: + panic("still using grody create_intr interface"); + } + + /* + * We depend here on rman_activate_resource() being idempotent. + */ + error = rman_activate_resource(irq); + if (error) + return (error); + + *cookiep = intr_create((void *)(intptr_t)-1, irq->r_start, ihand, arg, + mask, icflags); + if (*cookiep) + error = intr_connect(*cookiep); + else + error = EINVAL; /* XXX ??? */ + + return (error); +} + +static int +nexus_teardown_intr(device_t dev, device_t child, struct resource *r, void *ih) +{ + return (intr_destroy(ih)); +} + +static devclass_t pcib_devclass; + +static int +nexus_pcib_probe(device_t dev) +{ + device_set_desc(dev, "PCI host bus adapter"); + + device_add_child(dev, "pci", 0, 0); + + return 0; +} + +static device_method_t nexus_pcib_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, nexus_pcib_probe), + DEVMETHOD(device_attach, bus_generic_attach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + + /* Bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), + DEVMETHOD(bus_release_resource, bus_generic_release_resource), + DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), + DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), + DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), + DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), + + { 0, 0 } +}; + +static driver_t nexus_pcib_driver = { + "pcib", + nexus_pcib_methods, + DRIVER_TYPE_MISC, + 1, +}; + +DRIVER_MODULE(pcib, nexus, nexus_pcib_driver, pcib_devclass, 0, 0); Property changes on: head/sys/i386/i386/legacy.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: head/sys/i386/i386/machdep.c =================================================================== --- head/sys/i386/i386/machdep.c (revision 45719) +++ head/sys/i386/i386/machdep.c (revision 45720) @@ -1,1940 +1,1946 @@ /*- * Copyright (c) 1992 Terrence R. Lambert. * Copyright (c) 1982, 1987, 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * William Jolitz. * * 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: @(#)machdep.c 7.4 (Berkeley) 6/3/91 - * $Id: machdep.c,v 1.327 1999/03/06 04:46:18 wollman Exp $ + * $Id: machdep.c,v 1.328 1999/04/03 22:19:58 jdp Exp $ */ #include "apm.h" #include "ether.h" #include "npx.h" #include "opt_atalk.h" #include "opt_cpu.h" #include "opt_ddb.h" #include "opt_inet.h" #include "opt_ipx.h" #include "opt_maxmem.h" #include "opt_msgbuf.h" #include "opt_perfmon.h" #include "opt_smp.h" #include "opt_sysvipc.h" #include "opt_user_ldt.h" #include "opt_userconfig.h" #include "opt_vm86.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include #ifdef SYSVSHM #include #endif #ifdef SYSVMSG #include #endif #ifdef SYSVSEM #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(INET) || defined(IPX) || defined(NATM) || defined(NETATALK) \ || NETHER > 0 || defined(NS) #define NETISR #endif #ifdef NETISR #include #endif #include #include #include #include #include #include #include #include #include /* pcb.h included via sys/user.h */ #ifdef SMP #include #endif #ifdef PERFMON #include #endif +#ifdef OLD_BUS_ARCH #include +#endif #include #ifndef VM86 #include #endif #include #include extern void init386 __P((int first)); extern void dblfault_handler __P((void)); extern void printcpuinfo(void); /* XXX header file */ extern void earlysetcpuclass(void); /* same header file */ extern void finishidentcpu(void); extern void panicifcpuunsupported(void); extern void initializecpu(void); static void cpu_startup __P((void *)); SYSINIT(cpu, SI_SUB_CPU, SI_ORDER_FIRST, cpu_startup, NULL) static MALLOC_DEFINE(M_MBUF, "mbuf", "mbuf"); int _udatasel, _ucodesel; u_int atdevbase; #if defined(SWTCH_OPTIM_STATS) extern int swtch_optim_stats; SYSCTL_INT(_debug, OID_AUTO, swtch_optim_stats, CTLFLAG_RD, &swtch_optim_stats, 0, ""); SYSCTL_INT(_debug, OID_AUTO, tlb_flush_count, CTLFLAG_RD, &tlb_flush_count, 0, ""); #endif #ifdef PC98 static int ispc98 = 1; #else static int ispc98 = 0; #endif SYSCTL_INT(_machdep, OID_AUTO, ispc98, CTLFLAG_RD, &ispc98, 0, ""); int physmem = 0; int cold = 1; static int sysctl_hw_physmem SYSCTL_HANDLER_ARGS { int error = sysctl_handle_int(oidp, 0, ctob(physmem), req); return (error); } SYSCTL_PROC(_hw, HW_PHYSMEM, physmem, CTLTYPE_INT|CTLFLAG_RD, 0, 0, sysctl_hw_physmem, "I", ""); static int sysctl_hw_usermem SYSCTL_HANDLER_ARGS { int error = sysctl_handle_int(oidp, 0, ctob(physmem - cnt.v_wire_count), req); return (error); } SYSCTL_PROC(_hw, HW_USERMEM, usermem, CTLTYPE_INT|CTLFLAG_RD, 0, 0, sysctl_hw_usermem, "I", ""); static int sysctl_hw_availpages SYSCTL_HANDLER_ARGS { int error = sysctl_handle_int(oidp, 0, i386_btop(avail_end - avail_start), req); return (error); } SYSCTL_PROC(_hw, OID_AUTO, availpages, CTLTYPE_INT|CTLFLAG_RD, 0, 0, sysctl_hw_availpages, "I", ""); static int sysctl_machdep_msgbuf SYSCTL_HANDLER_ARGS { int error; /* Unwind the buffer, so that it's linear (possibly starting with * some initial nulls). */ error=sysctl_handle_opaque(oidp,msgbufp->msg_ptr+msgbufp->msg_bufr, msgbufp->msg_size-msgbufp->msg_bufr,req); if(error) return(error); if(msgbufp->msg_bufr>0) { error=sysctl_handle_opaque(oidp,msgbufp->msg_ptr, msgbufp->msg_bufr,req); } return(error); } SYSCTL_PROC(_machdep, OID_AUTO, msgbuf, CTLTYPE_STRING|CTLFLAG_RD, 0, 0, sysctl_machdep_msgbuf, "A","Contents of kernel message buffer"); static int msgbuf_clear; static int sysctl_machdep_msgbuf_clear SYSCTL_HANDLER_ARGS { int error; error = sysctl_handle_int(oidp, oidp->oid_arg1, oidp->oid_arg2, req); if (!error && req->newptr) { /* Clear the buffer and reset write pointer */ bzero(msgbufp->msg_ptr,msgbufp->msg_size); msgbufp->msg_bufr=msgbufp->msg_bufx=0; msgbuf_clear=0; } return (error); } SYSCTL_PROC(_machdep, OID_AUTO, msgbuf_clear, CTLTYPE_INT|CTLFLAG_RW, &msgbuf_clear, 0, sysctl_machdep_msgbuf_clear, "I", "Clear kernel message buffer"); int bootverbose = 0, Maxmem = 0; long dumplo; vm_offset_t phys_avail[10]; /* must be 2 less so 0 0 can signal end of chunks */ #define PHYS_AVAIL_ARRAY_END ((sizeof(phys_avail) / sizeof(vm_offset_t)) - 2) #ifdef NETISR static void setup_netisrs __P((struct linker_set *)); #endif static vm_offset_t buffer_sva, buffer_eva; vm_offset_t clean_sva, clean_eva; static vm_offset_t pager_sva, pager_eva; #ifdef NETISR extern struct linker_set netisr_set; #endif #if NNPX > 0 extern struct isa_driver npxdriver; #endif #define offsetof(type, member) ((size_t)(&((type *)0)->member)) static void cpu_startup(dummy) void *dummy; { register unsigned i; register caddr_t v; vm_offset_t maxaddr; vm_size_t size = 0; int firstaddr; vm_offset_t minaddr; if (boothowto & RB_VERBOSE) bootverbose++; /* * Good {morning,afternoon,evening,night}. */ printf(version); earlysetcpuclass(); startrtclock(); printcpuinfo(); panicifcpuunsupported(); #ifdef PERFMON perfmon_init(); #endif printf("real memory = %u (%uK bytes)\n", ptoa(Maxmem), ptoa(Maxmem) / 1024); /* * Display any holes after the first chunk of extended memory. */ if (bootverbose) { int indx; printf("Physical memory chunk(s):\n"); for (indx = 0; phys_avail[indx + 1] != 0; indx += 2) { int size1 = phys_avail[indx + 1] - phys_avail[indx]; printf("0x%08x - 0x%08x, %u bytes (%u pages)\n", phys_avail[indx], phys_avail[indx + 1] - 1, size1, size1 / PAGE_SIZE); } } #ifdef NETISR /* * Quickly wire in netisrs. */ setup_netisrs(&netisr_set); #endif /* * Calculate callout wheel size */ for (callwheelsize = 1, callwheelbits = 0; callwheelsize < ncallout; callwheelsize <<= 1, ++callwheelbits) ; callwheelmask = callwheelsize - 1; /* * Allocate space for system data structures. * The first available kernel virtual address is in "v". * As pages of kernel virtual memory are allocated, "v" is incremented. * As pages of memory are allocated and cleared, * "firstaddr" is incremented. * An index into the kernel page table corresponding to the * virtual memory address maintained in "v" is kept in "mapaddr". */ /* * Make two passes. The first pass calculates how much memory is * needed and allocates it. The second pass assigns virtual * addresses to the various data structures. */ firstaddr = 0; again: v = (caddr_t)firstaddr; #define valloc(name, type, num) \ (name) = (type *)v; v = (caddr_t)((name)+(num)) #define valloclim(name, type, num, lim) \ (name) = (type *)v; v = (caddr_t)((lim) = ((name)+(num))) valloc(callout, struct callout, ncallout); valloc(callwheel, struct callout_tailq, callwheelsize); #ifdef SYSVSHM valloc(shmsegs, struct shmid_ds, shminfo.shmmni); #endif #ifdef SYSVSEM valloc(sema, struct semid_ds, seminfo.semmni); valloc(sem, struct sem, seminfo.semmns); /* This is pretty disgusting! */ valloc(semu, int, (seminfo.semmnu * seminfo.semusz) / sizeof(int)); #endif #ifdef SYSVMSG valloc(msgpool, char, msginfo.msgmax); valloc(msgmaps, struct msgmap, msginfo.msgseg); valloc(msghdrs, struct msg, msginfo.msgtql); valloc(msqids, struct msqid_ds, msginfo.msgmni); #endif if (nbuf == 0) { nbuf = 30; if( physmem > 1024) nbuf += min((physmem - 1024) / 8, 2048); } nswbuf = max(min(nbuf/4, 64), 16); valloc(swbuf, struct buf, nswbuf); valloc(buf, struct buf, nbuf); /* * End of first pass, size has been calculated so allocate memory */ if (firstaddr == 0) { size = (vm_size_t)(v - firstaddr); firstaddr = (int)kmem_alloc(kernel_map, round_page(size)); if (firstaddr == 0) panic("startup: no room for tables"); goto again; } /* * End of second pass, addresses have been assigned */ if ((vm_size_t)(v - firstaddr) != size) panic("startup: table size inconsistency"); clean_map = kmem_suballoc(kernel_map, &clean_sva, &clean_eva, (nbuf*BKVASIZE) + (nswbuf*MAXPHYS) + pager_map_size); buffer_map = kmem_suballoc(clean_map, &buffer_sva, &buffer_eva, (nbuf*BKVASIZE)); pager_map = kmem_suballoc(clean_map, &pager_sva, &pager_eva, (nswbuf*MAXPHYS) + pager_map_size); pager_map->system_map = 1; exec_map = kmem_suballoc(kernel_map, &minaddr, &maxaddr, (16*(ARG_MAX+(PAGE_SIZE*3)))); /* * Finally, allocate mbuf pool. Since mclrefcnt is an off-size * we use the more space efficient malloc in place of kmem_alloc. */ { vm_offset_t mb_map_size; int xclusters; /* Allow override of NMBCLUSTERS from the kernel environment */ if (getenv_int("kern.ipc.nmbclusters", &xclusters) && xclusters > nmbclusters) nmbclusters = xclusters; mb_map_size = nmbufs * MSIZE + nmbclusters * MCLBYTES; mb_map_size = roundup2(mb_map_size, max(MCLBYTES, PAGE_SIZE)); mclrefcnt = malloc(mb_map_size / MCLBYTES, M_MBUF, M_NOWAIT); bzero(mclrefcnt, mb_map_size / MCLBYTES); mb_map = kmem_suballoc(kmem_map, (vm_offset_t *)&mbutl, &maxaddr, mb_map_size); mb_map->system_map = 1; } /* * Initialize callouts */ SLIST_INIT(&callfree); for (i = 0; i < ncallout; i++) { callout_init(&callout[i]); callout[i].c_flags = CALLOUT_LOCAL_ALLOC; SLIST_INSERT_HEAD(&callfree, &callout[i], c_links.sle); } for (i = 0; i < callwheelsize; i++) { TAILQ_INIT(&callwheel[i]); } #if defined(USERCONFIG) userconfig(); cninit(); /* the preferred console may have changed */ #endif printf("avail memory = %u (%uK bytes)\n", ptoa(cnt.v_free_count), ptoa(cnt.v_free_count) / 1024); /* * Set up buffers, so they can be used to read disk labels. */ bufinit(); vm_pager_bufferinit(); #ifdef SMP /* * OK, enough kmem_alloc/malloc state should be up, lets get on with it! */ mp_start(); /* fire up the APs and APICs */ mp_announce(); #endif /* SMP */ } #ifdef NETISR int register_netisr(num, handler) int num; netisr_t *handler; { if (num < 0 || num >= (sizeof(netisrs)/sizeof(*netisrs)) ) { printf("register_netisr: bad isr number: %d\n", num); return (EINVAL); } netisrs[num] = handler; return (0); } static void setup_netisrs(ls) struct linker_set *ls; { int i; const struct netisrtab *nit; for(i = 0; ls->ls_items[i]; i++) { nit = (const struct netisrtab *)ls->ls_items[i]; register_netisr(nit->nit_num, nit->nit_isr); } } #endif /* NETISR */ /* * Send an interrupt to process. * * Stack is set up to allow sigcode stored * at top to call routine, followed by kcall * to sigreturn routine below. After sigreturn * resets the signal mask, the stack, and the * frame pointer, it returns to the user * specified pc, psl. */ void sendsig(catcher, sig, mask, code) sig_t catcher; int sig, mask; u_long code; { register struct proc *p = curproc; register struct trapframe *regs; register struct sigframe *fp; struct sigframe sf; struct sigacts *psp = p->p_sigacts; int oonstack; regs = p->p_md.md_regs; oonstack = psp->ps_sigstk.ss_flags & SS_ONSTACK; /* * Allocate and validate space for the signal handler context. */ if ((psp->ps_flags & SAS_ALTSTACK) && !oonstack && (psp->ps_sigonstack & sigmask(sig))) { fp = (struct sigframe *)(psp->ps_sigstk.ss_sp + psp->ps_sigstk.ss_size - sizeof(struct sigframe)); psp->ps_sigstk.ss_flags |= SS_ONSTACK; } else { fp = (struct sigframe *)regs->tf_esp - 1; } /* * grow() will return FALSE if the fp will not fit inside the stack * and the stack can not be grown. useracc will return FALSE * if access is denied. */ #ifdef VM_STACK if ((grow_stack (p, (int)fp) == FALSE) || #else if ((grow(p, (int)fp) == FALSE) || #endif (useracc((caddr_t)fp, sizeof(struct sigframe), B_WRITE) == FALSE)) { /* * Process has trashed its stack; give it an illegal * instruction to halt it in its tracks. */ SIGACTION(p, SIGILL) = SIG_DFL; sig = sigmask(SIGILL); p->p_sigignore &= ~sig; p->p_sigcatch &= ~sig; p->p_sigmask &= ~sig; psignal(p, SIGILL); return; } /* * Build the argument list for the signal handler. */ if (p->p_sysent->sv_sigtbl) { if (sig < p->p_sysent->sv_sigsize) sig = p->p_sysent->sv_sigtbl[sig]; else sig = p->p_sysent->sv_sigsize + 1; } sf.sf_signum = sig; sf.sf_code = code; sf.sf_scp = &fp->sf_sc; sf.sf_addr = (char *) regs->tf_err; sf.sf_handler = catcher; /* save scratch registers */ sf.sf_sc.sc_eax = regs->tf_eax; sf.sf_sc.sc_ebx = regs->tf_ebx; sf.sf_sc.sc_ecx = regs->tf_ecx; sf.sf_sc.sc_edx = regs->tf_edx; sf.sf_sc.sc_esi = regs->tf_esi; sf.sf_sc.sc_edi = regs->tf_edi; sf.sf_sc.sc_cs = regs->tf_cs; sf.sf_sc.sc_ds = regs->tf_ds; sf.sf_sc.sc_ss = regs->tf_ss; sf.sf_sc.sc_es = regs->tf_es; sf.sf_sc.sc_isp = regs->tf_isp; /* * Build the signal context to be used by sigreturn. */ sf.sf_sc.sc_onstack = oonstack; sf.sf_sc.sc_mask = mask; sf.sf_sc.sc_sp = regs->tf_esp; sf.sf_sc.sc_fp = regs->tf_ebp; sf.sf_sc.sc_pc = regs->tf_eip; sf.sf_sc.sc_ps = regs->tf_eflags; sf.sf_sc.sc_trapno = regs->tf_trapno; sf.sf_sc.sc_err = regs->tf_err; #ifdef VM86 /* * If we're a vm86 process, we want to save the segment registers. * We also change eflags to be our emulated eflags, not the actual * eflags. */ if (regs->tf_eflags & PSL_VM) { struct trapframe_vm86 *tf = (struct trapframe_vm86 *)regs; struct vm86_kernel *vm86 = &p->p_addr->u_pcb.pcb_ext->ext_vm86; sf.sf_sc.sc_gs = tf->tf_vm86_gs; sf.sf_sc.sc_fs = tf->tf_vm86_fs; sf.sf_sc.sc_es = tf->tf_vm86_es; sf.sf_sc.sc_ds = tf->tf_vm86_ds; if (vm86->vm86_has_vme == 0) sf.sf_sc.sc_ps = (tf->tf_eflags & ~(PSL_VIF | PSL_VIP)) | (vm86->vm86_eflags & (PSL_VIF | PSL_VIP)); /* * We should never have PSL_T set when returning from vm86 * mode. It may be set here if we deliver a signal before * getting to vm86 mode, so turn it off. * * Clear PSL_NT to inhibit T_TSSFLT faults on return from * syscalls made by the signal handler. This just avoids * wasting time for our lazy fixup of such faults. PSL_NT * does nothing in vm86 mode, but vm86 programs can set it * almost legitimately in probes for old cpu types. */ tf->tf_eflags &= ~(PSL_VM | PSL_NT | PSL_T | PSL_VIF | PSL_VIP); } #endif /* VM86 */ /* * Copy the sigframe out to the user's stack. */ if (copyout(&sf, fp, sizeof(struct sigframe)) != 0) { /* * Something is wrong with the stack pointer. * ...Kill the process. */ sigexit(p, SIGILL); } regs->tf_esp = (int)fp; regs->tf_eip = PS_STRINGS - *(p->p_sysent->sv_szsigcode); regs->tf_cs = _ucodesel; regs->tf_ds = _udatasel; regs->tf_es = _udatasel; regs->tf_ss = _udatasel; } /* * System call to cleanup state after a signal * has been taken. Reset signal mask and * stack state from context left by sendsig (above). * Return to previous pc and psl as specified by * context left by sendsig. Check carefully to * make sure that the user has not modified the * state to gain improper privileges. */ int sigreturn(p, uap) struct proc *p; struct sigreturn_args /* { struct sigcontext *sigcntxp; } */ *uap; { register struct sigcontext *scp; register struct sigframe *fp; register struct trapframe *regs = p->p_md.md_regs; int eflags; /* * (XXX old comment) regs->tf_esp points to the return address. * The user scp pointer is above that. * The return address is faked in the signal trampoline code * for consistency. */ scp = uap->sigcntxp; fp = (struct sigframe *) ((caddr_t)scp - offsetof(struct sigframe, sf_sc)); if (useracc((caddr_t)fp, sizeof (*fp), B_WRITE) == 0) return(EFAULT); eflags = scp->sc_ps; #ifdef VM86 if (eflags & PSL_VM) { struct trapframe_vm86 *tf = (struct trapframe_vm86 *)regs; struct vm86_kernel *vm86; /* * if pcb_ext == 0 or vm86_inited == 0, the user hasn't * set up the vm86 area, and we can't enter vm86 mode. */ if (p->p_addr->u_pcb.pcb_ext == 0) return (EINVAL); vm86 = &p->p_addr->u_pcb.pcb_ext->ext_vm86; if (vm86->vm86_inited == 0) return (EINVAL); /* go back to user mode if both flags are set */ if ((eflags & PSL_VIP) && (eflags & PSL_VIF)) trapsignal(p, SIGBUS, 0); if (vm86->vm86_has_vme) { eflags = (tf->tf_eflags & ~VME_USERCHANGE) | (eflags & VME_USERCHANGE) | PSL_VM; } else { vm86->vm86_eflags = eflags; /* save VIF, VIP */ eflags = (tf->tf_eflags & ~VM_USERCHANGE) | (eflags & VM_USERCHANGE) | PSL_VM; } tf->tf_vm86_ds = scp->sc_ds; tf->tf_vm86_es = scp->sc_es; tf->tf_vm86_fs = scp->sc_fs; tf->tf_vm86_gs = scp->sc_gs; tf->tf_ds = _udatasel; tf->tf_es = _udatasel; } else { #endif /* VM86 */ /* * Don't allow users to change privileged or reserved flags. */ #define EFLAGS_SECURE(ef, oef) ((((ef) ^ (oef)) & ~PSL_USERCHANGE) == 0) /* * XXX do allow users to change the privileged flag PSL_RF. * The cpu sets PSL_RF in tf_eflags for faults. Debuggers * should sometimes set it there too. tf_eflags is kept in * the signal context during signal handling and there is no * other place to remember it, so the PSL_RF bit may be * corrupted by the signal handler without us knowing. * Corruption of the PSL_RF bit at worst causes one more or * one less debugger trap, so allowing it is fairly harmless. */ if (!EFLAGS_SECURE(eflags & ~PSL_RF, regs->tf_eflags & ~PSL_RF)) { #ifdef DEBUG printf("sigreturn: eflags = 0x%x\n", eflags); #endif return(EINVAL); } /* * Don't allow users to load a valid privileged %cs. Let the * hardware check for invalid selectors, excess privilege in * other selectors, invalid %eip's and invalid %esp's. */ #define CS_SECURE(cs) (ISPL(cs) == SEL_UPL) if (!CS_SECURE(scp->sc_cs)) { #ifdef DEBUG printf("sigreturn: cs = 0x%x\n", scp->sc_cs); #endif trapsignal(p, SIGBUS, T_PROTFLT); return(EINVAL); } regs->tf_ds = scp->sc_ds; regs->tf_es = scp->sc_es; #ifdef VM86 } #endif /* restore scratch registers */ regs->tf_eax = scp->sc_eax; regs->tf_ebx = scp->sc_ebx; regs->tf_ecx = scp->sc_ecx; regs->tf_edx = scp->sc_edx; regs->tf_esi = scp->sc_esi; regs->tf_edi = scp->sc_edi; regs->tf_cs = scp->sc_cs; regs->tf_ss = scp->sc_ss; regs->tf_isp = scp->sc_isp; if (useracc((caddr_t)scp, sizeof (*scp), B_WRITE) == 0) return(EINVAL); if (scp->sc_onstack & 01) p->p_sigacts->ps_sigstk.ss_flags |= SS_ONSTACK; else p->p_sigacts->ps_sigstk.ss_flags &= ~SS_ONSTACK; p->p_sigmask = scp->sc_mask & ~sigcantmask; regs->tf_ebp = scp->sc_fp; regs->tf_esp = scp->sc_sp; regs->tf_eip = scp->sc_pc; regs->tf_eflags = eflags; return(EJUSTRETURN); } /* * Machine dependent boot() routine * * I haven't seen anything to put here yet * Possibly some stuff might be grafted back here from boot() */ void cpu_boot(int howto) { } /* * Shutdown the CPU as much as possible */ void cpu_halt(void) { for (;;) __asm__ ("hlt"); } /* * Clear registers on exec */ void setregs(p, entry, stack, ps_strings) struct proc *p; u_long entry; u_long stack; u_long ps_strings; { struct trapframe *regs = p->p_md.md_regs; struct pcb *pcb = &p->p_addr->u_pcb; #ifdef USER_LDT /* was i386_user_cleanup() in NetBSD */ if (pcb->pcb_ldt) { if (pcb == curpcb) { lldt(_default_ldt); currentldt = _default_ldt; } kmem_free(kernel_map, (vm_offset_t)pcb->pcb_ldt, pcb->pcb_ldt_len * sizeof(union descriptor)); pcb->pcb_ldt_len = (int)pcb->pcb_ldt = 0; } #endif bzero((char *)regs, sizeof(struct trapframe)); regs->tf_eip = entry; regs->tf_esp = stack; regs->tf_eflags = PSL_USER | (regs->tf_eflags & PSL_T); regs->tf_ss = _udatasel; regs->tf_ds = _udatasel; regs->tf_es = _udatasel; regs->tf_cs = _ucodesel; /* PS_STRINGS value for BSD/OS binaries. It is 0 for non-BSD/OS. */ regs->tf_ebx = ps_strings; /* reset %fs and %gs as well */ pcb->pcb_fs = _udatasel; pcb->pcb_gs = _udatasel; if (pcb == curpcb) { __asm("movw %w0,%%fs" : : "r" (_udatasel)); __asm("movw %w0,%%gs" : : "r" (_udatasel)); } /* * Initialize the math emulator (if any) for the current process. * Actually, just clear the bit that says that the emulator has * been initialized. Initialization is delayed until the process * traps to the emulator (if it is done at all) mainly because * emulators don't provide an entry point for initialization. */ p->p_addr->u_pcb.pcb_flags &= ~FP_SOFTFP; /* * Arrange to trap the next npx or `fwait' instruction (see npx.c * for why fwait must be trapped at least if there is an npx or an * emulator). This is mainly to handle the case where npx0 is not * configured, since the npx routines normally set up the trap * otherwise. It should be done only at boot time, but doing it * here allows modifying `npx_exists' for testing the emulator on * systems with an npx. */ load_cr0(rcr0() | CR0_MP | CR0_TS); #if NNPX > 0 /* Initialize the npx (if any) for the current process. */ npxinit(__INITIAL_NPXCW__); #endif /* * XXX - Linux emulator * Make sure sure edx is 0x0 on entry. Linux binaries depend * on it. */ p->p_retval[1] = 0; } static int sysctl_machdep_adjkerntz SYSCTL_HANDLER_ARGS { int error; error = sysctl_handle_int(oidp, oidp->oid_arg1, oidp->oid_arg2, req); if (!error && req->newptr) resettodr(); return (error); } SYSCTL_PROC(_machdep, CPU_ADJKERNTZ, adjkerntz, CTLTYPE_INT|CTLFLAG_RW, &adjkerntz, 0, sysctl_machdep_adjkerntz, "I", ""); SYSCTL_INT(_machdep, CPU_DISRTCSET, disable_rtc_set, CTLFLAG_RW, &disable_rtc_set, 0, ""); SYSCTL_STRUCT(_machdep, CPU_BOOTINFO, bootinfo, CTLFLAG_RD, &bootinfo, bootinfo, ""); SYSCTL_INT(_machdep, CPU_WALLCLOCK, wall_cmos_clock, CTLFLAG_RW, &wall_cmos_clock, 0, ""); /* * Initialize 386 and configure to run kernel */ /* * Initialize segments & interrupt table */ int _default_ldt; #ifdef SMP union descriptor gdt[NGDT + NCPU]; /* global descriptor table */ #else union descriptor gdt[NGDT]; /* global descriptor table */ #endif struct gate_descriptor idt[NIDT]; /* interrupt descriptor table */ union descriptor ldt[NLDT]; /* local descriptor table */ #ifdef SMP /* table descriptors - used to load tables by microp */ struct region_descriptor r_gdt, r_idt; #endif extern struct i386tss common_tss; /* One tss per cpu */ #ifdef VM86 extern struct segment_descriptor common_tssd; extern int private_tss; /* flag indicating private tss */ extern u_int my_tr; /* which task register setting */ #endif /* VM86 */ #if defined(I586_CPU) && !defined(NO_F00F_HACK) struct gate_descriptor *t_idt; extern int has_f00f_bug; #endif static struct i386tss dblfault_tss; static char dblfault_stack[PAGE_SIZE]; extern struct user *proc0paddr; /* software prototypes -- in more palatable form */ struct soft_segment_descriptor gdt_segs[ #ifdef SMP NGDT + NCPU #endif ] = { /* GNULL_SEL 0 Null Descriptor */ { 0x0, /* segment base address */ 0x0, /* length */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GCODE_SEL 1 Code Descriptor for kernel */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMERA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GDATA_SEL 2 Data Descriptor for kernel */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMRWA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GLDT_SEL 3 LDT Descriptor */ { (int) ldt, /* segment base address */ sizeof(ldt)-1, /* length - all address space */ SDT_SYSLDT, /* segment type */ SEL_UPL, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* unused - default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GTGATE_SEL 4 Null Descriptor - Placeholder */ { 0x0, /* segment base address */ 0x0, /* length - all address space */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GPANIC_SEL 5 Panic Tss Descriptor */ { (int) &dblfault_tss, /* segment base address */ sizeof(struct i386tss)-1,/* length - all address space */ SDT_SYS386TSS, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* unused - default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GPROC0_SEL 6 Proc 0 Tss Descriptor */ { (int) &common_tss, /* segment base address */ sizeof(struct i386tss)-1,/* length - all address space */ SDT_SYS386TSS, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* unused - default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GUSERLDT_SEL 7 User LDT Descriptor per process */ { (int) ldt, /* segment base address */ (512 * sizeof(union descriptor)-1), /* length */ SDT_SYSLDT, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* unused - default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GAPMCODE32_SEL 8 APM BIOS 32-bit interface (32bit Code) */ { 0, /* segment base address (overwritten by APM) */ 0xfffff, /* length */ SDT_MEMERA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GAPMCODE16_SEL 9 APM BIOS 32-bit interface (16bit Code) */ { 0, /* segment base address (overwritten by APM) */ 0xfffff, /* length */ SDT_MEMERA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GAPMDATA_SEL 10 APM BIOS 32-bit interface (Data) */ { 0, /* segment base address (overwritten by APM) */ 0xfffff, /* length */ SDT_MEMRWA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, }; static struct soft_segment_descriptor ldt_segs[] = { /* Null Descriptor - overwritten by call gate */ { 0x0, /* segment base address */ 0x0, /* length - all address space */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* Null Descriptor - overwritten by call gate */ { 0x0, /* segment base address */ 0x0, /* length - all address space */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* Null Descriptor - overwritten by call gate */ { 0x0, /* segment base address */ 0x0, /* length - all address space */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* Code Descriptor for user */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMERA, /* segment type */ SEL_UPL, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* Null Descriptor - overwritten by call gate */ { 0x0, /* segment base address */ 0x0, /* length - all address space */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* Data Descriptor for user */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMRWA, /* segment type */ SEL_UPL, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, }; void setidt(idx, func, typ, dpl, selec) int idx; inthand_t *func; int typ; int dpl; int selec; { struct gate_descriptor *ip; #if defined(I586_CPU) && !defined(NO_F00F_HACK) ip = (t_idt != NULL ? t_idt : idt) + idx; #else ip = idt + idx; #endif ip->gd_looffset = (int)func; ip->gd_selector = selec; ip->gd_stkcpy = 0; ip->gd_xx = 0; ip->gd_type = typ; ip->gd_dpl = dpl; ip->gd_p = 1; ip->gd_hioffset = ((int)func)>>16 ; } #define IDTVEC(name) __CONCAT(X,name) extern inthand_t IDTVEC(div), IDTVEC(dbg), IDTVEC(nmi), IDTVEC(bpt), IDTVEC(ofl), IDTVEC(bnd), IDTVEC(ill), IDTVEC(dna), IDTVEC(fpusegm), IDTVEC(tss), IDTVEC(missing), IDTVEC(stk), IDTVEC(prot), IDTVEC(page), IDTVEC(mchk), IDTVEC(rsvd), IDTVEC(fpu), IDTVEC(align), IDTVEC(syscall), IDTVEC(int0x80_syscall); void sdtossd(sd, ssd) struct segment_descriptor *sd; struct soft_segment_descriptor *ssd; { ssd->ssd_base = (sd->sd_hibase << 24) | sd->sd_lobase; ssd->ssd_limit = (sd->sd_hilimit << 16) | sd->sd_lolimit; ssd->ssd_type = sd->sd_type; ssd->ssd_dpl = sd->sd_dpl; ssd->ssd_p = sd->sd_p; ssd->ssd_def32 = sd->sd_def32; ssd->ssd_gran = sd->sd_gran; } void init386(first) int first; { int x; unsigned biosbasemem, biosextmem; struct gate_descriptor *gdp; int gsel_tss; +#if NNPX > 0 + int msize; +#endif - struct isa_device *idp; #ifndef SMP /* table descriptors - used to load tables by microp */ struct region_descriptor r_gdt, r_idt; #endif int pagesinbase, pagesinext; vm_offset_t target_page; int pa_indx, off; int speculative_mprobe; /* * Prevent lowering of the ipl if we call tsleep() early. */ safepri = cpl; proc0.p_addr = proc0paddr; atdevbase = ISA_HOLE_START + KERNBASE; /* * Initialize the console before we print anything out. */ cninit(); /* * make gdt memory segments, the code segment goes up to end of the * page with etext in it, the data segment goes to the end of * the address space */ /* * XXX text protection is temporarily (?) disabled. The limit was * i386_btop(round_page(etext)) - 1. */ gdt_segs[GCODE_SEL].ssd_limit = i386_btop(0) - 1; gdt_segs[GDATA_SEL].ssd_limit = i386_btop(0) - 1; #ifdef BDE_DEBUGGER #define NGDT1 8 /* avoid overwriting db entries with APM ones */ #else #define NGDT1 (sizeof gdt_segs / sizeof gdt_segs[0]) #endif for (x = 0; x < NGDT1; x++) ssdtosd(&gdt_segs[x], &gdt[x].sd); #ifdef VM86 common_tssd = gdt[GPROC0_SEL].sd; #endif /* VM86 */ #ifdef SMP /* * Spin these up now. init_secondary() grabs them. We could use * #for(x,y,z) / #endfor cpp directives if they existed. */ for (x = 0; x < NCPU; x++) { gdt_segs[NGDT + x] = gdt_segs[GPROC0_SEL]; ssdtosd(&gdt_segs[NGDT + x], &gdt[NGDT + x].sd); } #endif /* make ldt memory segments */ /* * The data segment limit must not cover the user area because we * don't want the user area to be writable in copyout() etc. (page * level protection is lost in kernel mode on 386's). Also, we * don't want the user area to be writable directly (page level * protection of the user area is not available on 486's with * CR0_WP set, because there is no user-read/kernel-write mode). * * XXX - VM_MAXUSER_ADDRESS is an end address, not a max. And it * should be spelled ...MAX_USER... */ #define VM_END_USER_RW_ADDRESS VM_MAXUSER_ADDRESS /* * The code segment limit has to cover the user area until we move * the signal trampoline out of the user area. This is safe because * the code segment cannot be written to directly. */ #define VM_END_USER_R_ADDRESS (VM_END_USER_RW_ADDRESS + UPAGES * PAGE_SIZE) ldt_segs[LUCODE_SEL].ssd_limit = i386_btop(VM_END_USER_R_ADDRESS) - 1; ldt_segs[LUDATA_SEL].ssd_limit = i386_btop(VM_END_USER_RW_ADDRESS) - 1; for (x = 0; x < sizeof ldt_segs / sizeof ldt_segs[0]; x++) ssdtosd(&ldt_segs[x], &ldt[x].sd); /* exceptions */ for (x = 0; x < NIDT; x++) setidt(x, &IDTVEC(rsvd), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(0, &IDTVEC(div), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(1, &IDTVEC(dbg), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(2, &IDTVEC(nmi), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(3, &IDTVEC(bpt), SDT_SYS386TGT, SEL_UPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(4, &IDTVEC(ofl), SDT_SYS386TGT, SEL_UPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(5, &IDTVEC(bnd), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(6, &IDTVEC(ill), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(7, &IDTVEC(dna), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(8, 0, SDT_SYSTASKGT, SEL_KPL, GSEL(GPANIC_SEL, SEL_KPL)); setidt(9, &IDTVEC(fpusegm), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(10, &IDTVEC(tss), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(11, &IDTVEC(missing), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(12, &IDTVEC(stk), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(13, &IDTVEC(prot), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(14, &IDTVEC(page), SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(15, &IDTVEC(rsvd), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(16, &IDTVEC(fpu), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(17, &IDTVEC(align), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(18, &IDTVEC(mchk), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(0x80, &IDTVEC(int0x80_syscall), SDT_SYS386TGT, SEL_UPL, GSEL(GCODE_SEL, SEL_KPL)); #include "isa.h" #if NISA >0 isa_defaultirq(); #endif rand_initialize(); r_gdt.rd_limit = sizeof(gdt) - 1; r_gdt.rd_base = (int) gdt; lgdt(&r_gdt); r_idt.rd_limit = sizeof(idt) - 1; r_idt.rd_base = (int) idt; lidt(&r_idt); _default_ldt = GSEL(GLDT_SEL, SEL_KPL); lldt(_default_ldt); #ifdef USER_LDT currentldt = _default_ldt; #endif #ifdef DDB kdb_init(); if (boothowto & RB_KDB) Debugger("Boot flags requested debugger"); #endif finishidentcpu(); /* Final stage of CPU initialization */ setidt(6, &IDTVEC(ill), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(13, &IDTVEC(prot), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); initializecpu(); /* Initialize CPU registers */ /* make an initial tss so cpu can get interrupt stack on syscall! */ #ifdef VM86 common_tss.tss_esp0 = (int) proc0.p_addr + UPAGES*PAGE_SIZE - 16; #else common_tss.tss_esp0 = (int) proc0.p_addr + UPAGES*PAGE_SIZE; #endif /* VM86 */ common_tss.tss_ss0 = GSEL(GDATA_SEL, SEL_KPL) ; common_tss.tss_ioopt = (sizeof common_tss) << 16; gsel_tss = GSEL(GPROC0_SEL, SEL_KPL); ltr(gsel_tss); #ifdef VM86 private_tss = 0; my_tr = GPROC0_SEL; #endif dblfault_tss.tss_esp = dblfault_tss.tss_esp0 = dblfault_tss.tss_esp1 = dblfault_tss.tss_esp2 = (int) &dblfault_stack[sizeof(dblfault_stack)]; dblfault_tss.tss_ss = dblfault_tss.tss_ss0 = dblfault_tss.tss_ss1 = dblfault_tss.tss_ss2 = GSEL(GDATA_SEL, SEL_KPL); dblfault_tss.tss_cr3 = (int)IdlePTD; dblfault_tss.tss_eip = (int) dblfault_handler; dblfault_tss.tss_eflags = PSL_KERNEL; dblfault_tss.tss_ds = dblfault_tss.tss_es = dblfault_tss.tss_fs = dblfault_tss.tss_gs = GSEL(GDATA_SEL, SEL_KPL); dblfault_tss.tss_cs = GSEL(GCODE_SEL, SEL_KPL); dblfault_tss.tss_ldt = GSEL(GLDT_SEL, SEL_KPL); #ifdef VM86 initial_bioscalls(&biosbasemem, &biosextmem); #else /* Use BIOS values stored in RTC CMOS RAM, since probing * breaks certain 386 AT relics. */ biosbasemem = rtcin(RTC_BASELO)+ (rtcin(RTC_BASEHI)<<8); biosextmem = rtcin(RTC_EXTLO)+ (rtcin(RTC_EXTHI)<<8); #endif /* * If BIOS tells us that it has more than 640k in the basemem, * don't believe it - set it to 640k. */ if (biosbasemem > 640) { printf("Preposterous RTC basemem of %uK, truncating to 640K\n", biosbasemem); biosbasemem = 640; } if (bootinfo.bi_memsizes_valid && bootinfo.bi_basemem > 640) { printf("Preposterous BIOS basemem of %uK, truncating to 640K\n", bootinfo.bi_basemem); bootinfo.bi_basemem = 640; } /* * Warn if the official BIOS interface disagrees with the RTC * interface used above about the amount of base memory or the * amount of extended memory. Prefer the BIOS value for the base * memory. This is necessary for machines that `steal' base * memory for use as BIOS memory, at least if we are going to use * the BIOS for apm. Prefer the RTC value for extended memory. * Eventually the hackish interface shouldn't even be looked at. */ if (bootinfo.bi_memsizes_valid) { if (bootinfo.bi_basemem != biosbasemem) { vm_offset_t pa; printf( "BIOS basemem (%uK) != RTC basemem (%uK), setting to BIOS value\n", bootinfo.bi_basemem, biosbasemem); biosbasemem = bootinfo.bi_basemem; /* * XXX if biosbasemem is now < 640, there is `hole' * between the end of base memory and the start of * ISA memory. The hole may be empty or it may * contain BIOS code or data. Map it read/write so * that the BIOS can write to it. (Memory from 0 to * the physical end of the kernel is mapped read-only * to begin with and then parts of it are remapped. * The parts that aren't remapped form holes that * remain read-only and are unused by the kernel. * The base memory area is below the physical end of * the kernel and right now forms a read-only hole. * The part of it from PAGE_SIZE to * (trunc_page(biosbasemem * 1024) - 1) will be * remapped and used by the kernel later.) * * This code is similar to the code used in * pmap_mapdev, but since no memory needs to be * allocated we simply change the mapping. */ for (pa = trunc_page(biosbasemem * 1024); pa < ISA_HOLE_START; pa += PAGE_SIZE) { unsigned *pte; pte = (unsigned *)vtopte(pa + KERNBASE); *pte = pa | PG_RW | PG_V; } } if (bootinfo.bi_extmem != biosextmem) printf("BIOS extmem (%uK) != RTC extmem (%uK)\n", bootinfo.bi_extmem, biosextmem); } #ifdef SMP /* make hole for AP bootstrap code */ pagesinbase = mp_bootaddress(biosbasemem) / PAGE_SIZE; #else pagesinbase = biosbasemem * 1024 / PAGE_SIZE; #endif pagesinext = biosextmem * 1024 / PAGE_SIZE; /* * Special hack for chipsets that still remap the 384k hole when * there's 16MB of memory - this really confuses people that * are trying to use bus mastering ISA controllers with the * "16MB limit"; they only have 16MB, but the remapping puts * them beyond the limit. */ /* * If extended memory is between 15-16MB (16-17MB phys address range), * chop it to 15MB. */ if ((pagesinext > 3840) && (pagesinext < 4096)) pagesinext = 3840; /* * Maxmem isn't the "maximum memory", it's one larger than the * highest page of the physical address space. It should be * called something like "Maxphyspage". */ Maxmem = pagesinext + 0x100000/PAGE_SIZE; /* * Indicate that we wish to do a speculative search for memory beyond * the end of the reported size if the indicated amount is 64MB (0x4000 * pages) - which is the largest amount that the BIOS/bootblocks can * currently report. If a specific amount of memory is indicated via * the MAXMEM option or the npx0 "msize", then don't do the speculative * memory probe. */ if (Maxmem >= 0x4000) speculative_mprobe = TRUE; else speculative_mprobe = FALSE; #ifdef MAXMEM Maxmem = MAXMEM/4; speculative_mprobe = FALSE; #endif #if NNPX > 0 - idp = find_isadev(isa_devtab_null, &npxdriver, 0); - if (idp != NULL && idp->id_msize != 0) { - Maxmem = idp->id_msize / 4; - speculative_mprobe = FALSE; + if (resource_int_value("npx", 0, "msize", &msize) == 0) { + if (msize != 0) { + Maxmem = msize / 4; + speculative_mprobe = FALSE; + } } #endif #ifdef SMP /* look for the MP hardware - needed for apic addresses */ mp_probe(); #endif /* call pmap initialization to make new kernel address space */ pmap_bootstrap (first, 0); /* * Size up each available chunk of physical memory. */ /* * We currently don't bother testing base memory. * XXX ...but we probably should. */ pa_indx = 0; if (pagesinbase > 1) { phys_avail[pa_indx++] = PAGE_SIZE; /* skip first page of memory */ phys_avail[pa_indx] = ptoa(pagesinbase);/* memory up to the ISA hole */ physmem = pagesinbase - 1; } else { /* point at first chunk end */ pa_indx++; } for (target_page = avail_start; target_page < ptoa(Maxmem); target_page += PAGE_SIZE) { int tmp, page_bad; page_bad = FALSE; /* * map page into kernel: valid, read/write, non-cacheable */ *(int *)CMAP1 = PG_V | PG_RW | PG_N | target_page; invltlb(); tmp = *(int *)CADDR1; /* * Test for alternating 1's and 0's */ *(volatile int *)CADDR1 = 0xaaaaaaaa; if (*(volatile int *)CADDR1 != 0xaaaaaaaa) { page_bad = TRUE; } /* * Test for alternating 0's and 1's */ *(volatile int *)CADDR1 = 0x55555555; if (*(volatile int *)CADDR1 != 0x55555555) { page_bad = TRUE; } /* * Test for all 1's */ *(volatile int *)CADDR1 = 0xffffffff; if (*(volatile int *)CADDR1 != 0xffffffff) { page_bad = TRUE; } /* * Test for all 0's */ *(volatile int *)CADDR1 = 0x0; if (*(volatile int *)CADDR1 != 0x0) { /* * test of page failed */ page_bad = TRUE; } /* * Restore original value. */ *(int *)CADDR1 = tmp; /* * Adjust array of valid/good pages. */ if (page_bad == FALSE) { /* * If this good page is a continuation of the * previous set of good pages, then just increase * the end pointer. Otherwise start a new chunk. * Note that "end" points one higher than end, * making the range >= start and < end. * If we're also doing a speculative memory * test and we at or past the end, bump up Maxmem * so that we keep going. The first bad page * will terminate the loop. */ if (phys_avail[pa_indx] == target_page) { phys_avail[pa_indx] += PAGE_SIZE; if (speculative_mprobe == TRUE && phys_avail[pa_indx] >= (64*1024*1024)) Maxmem++; } else { pa_indx++; if (pa_indx == PHYS_AVAIL_ARRAY_END) { printf("Too many holes in the physical address space, giving up\n"); pa_indx--; break; } phys_avail[pa_indx++] = target_page; /* start */ phys_avail[pa_indx] = target_page + PAGE_SIZE; /* end */ } physmem++; } } *(int *)CMAP1 = 0; invltlb(); /* * XXX * The last chunk must contain at least one page plus the message * buffer to avoid complicating other code (message buffer address * calculation, etc.). */ while (phys_avail[pa_indx - 1] + PAGE_SIZE + round_page(MSGBUF_SIZE) >= phys_avail[pa_indx]) { physmem -= atop(phys_avail[pa_indx] - phys_avail[pa_indx - 1]); phys_avail[pa_indx--] = 0; phys_avail[pa_indx--] = 0; } Maxmem = atop(phys_avail[pa_indx]); /* Trim off space for the message buffer. */ phys_avail[pa_indx] -= round_page(MSGBUF_SIZE); avail_end = phys_avail[pa_indx]; /* now running on new page tables, configured,and u/iom is accessible */ /* Map the message buffer. */ for (off = 0; off < round_page(MSGBUF_SIZE); off += PAGE_SIZE) pmap_enter(kernel_pmap, (vm_offset_t)msgbufp + off, avail_end + off, VM_PROT_ALL, TRUE); msgbufinit(msgbufp, MSGBUF_SIZE); /* make a call gate to reenter kernel with */ gdp = &ldt[LSYS5CALLS_SEL].gd; x = (int) &IDTVEC(syscall); gdp->gd_looffset = x++; gdp->gd_selector = GSEL(GCODE_SEL,SEL_KPL); gdp->gd_stkcpy = 1; gdp->gd_type = SDT_SYS386CGT; gdp->gd_dpl = SEL_UPL; gdp->gd_p = 1; gdp->gd_hioffset = ((int) &IDTVEC(syscall)) >>16; /* XXX does this work? */ ldt[LBSDICALLS_SEL] = ldt[LSYS5CALLS_SEL]; ldt[LSOL26CALLS_SEL] = ldt[LSYS5CALLS_SEL]; /* transfer to user mode */ _ucodesel = LSEL(LUCODE_SEL, SEL_UPL); _udatasel = LSEL(LUDATA_SEL, SEL_UPL); /* setup proc 0's pcb */ proc0.p_addr->u_pcb.pcb_flags = 0; proc0.p_addr->u_pcb.pcb_cr3 = (int)IdlePTD; #ifdef SMP proc0.p_addr->u_pcb.pcb_mpnest = 1; #endif #ifdef VM86 proc0.p_addr->u_pcb.pcb_ext = 0; #endif /* Sigh, relocate physical addresses left from bootstrap */ if (bootinfo.bi_modulep) { preload_metadata = (caddr_t)bootinfo.bi_modulep + KERNBASE; preload_bootstrap_relocate(KERNBASE); } if (bootinfo.bi_envp) kern_envp = (caddr_t)bootinfo.bi_envp + KERNBASE; } #if defined(I586_CPU) && !defined(NO_F00F_HACK) static void f00f_hack(void *unused); SYSINIT(f00f_hack, SI_SUB_INTRINSIC, SI_ORDER_FIRST, f00f_hack, NULL); static void f00f_hack(void *unused) { #ifndef SMP struct region_descriptor r_idt; #endif vm_offset_t tmp; if (!has_f00f_bug) return; printf("Intel Pentium detected, installing workaround for F00F bug\n"); r_idt.rd_limit = sizeof(idt) - 1; tmp = kmem_alloc(kernel_map, PAGE_SIZE * 2); if (tmp == 0) panic("kmem_alloc returned 0"); if (((unsigned int)tmp & (PAGE_SIZE-1)) != 0) panic("kmem_alloc returned non-page-aligned memory"); /* Put the first seven entries in the lower page */ t_idt = (struct gate_descriptor*)(tmp + PAGE_SIZE - (7*8)); bcopy(idt, t_idt, sizeof(idt)); r_idt.rd_base = (int)t_idt; lidt(&r_idt); if (vm_map_protect(kernel_map, tmp, tmp + PAGE_SIZE, VM_PROT_READ, FALSE) != KERN_SUCCESS) panic("vm_map_protect failed"); return; } #endif /* defined(I586_CPU) && !NO_F00F_HACK */ int ptrace_set_pc(p, addr) struct proc *p; unsigned long addr; { p->p_md.md_regs->tf_eip = addr; return (0); } int ptrace_single_step(p) struct proc *p; { p->p_md.md_regs->tf_eflags |= PSL_T; return (0); } int ptrace_read_u_check(p, addr, len) struct proc *p; vm_offset_t addr; size_t len; { vm_offset_t gap; if ((vm_offset_t) (addr + len) < addr) return EPERM; if ((vm_offset_t) (addr + len) <= sizeof(struct user)) return 0; gap = (char *) p->p_md.md_regs - (char *) p->p_addr; if ((vm_offset_t) addr < gap) return EPERM; if ((vm_offset_t) (addr + len) <= (vm_offset_t) (gap + sizeof(struct trapframe))) return 0; return EPERM; } int ptrace_write_u(p, off, data) struct proc *p; vm_offset_t off; long data; { struct trapframe frame_copy; vm_offset_t min; struct trapframe *tp; /* * Privileged kernel state is scattered all over the user area. * Only allow write access to parts of regs and to fpregs. */ min = (char *)p->p_md.md_regs - (char *)p->p_addr; if (off >= min && off <= min + sizeof(struct trapframe) - sizeof(int)) { tp = p->p_md.md_regs; frame_copy = *tp; *(int *)((char *)&frame_copy + (off - min)) = data; if (!EFLAGS_SECURE(frame_copy.tf_eflags, tp->tf_eflags) || !CS_SECURE(frame_copy.tf_cs)) return (EINVAL); *(int*)((char *)p->p_addr + off) = data; return (0); } min = offsetof(struct user, u_pcb) + offsetof(struct pcb, pcb_savefpu); if (off >= min && off <= min + sizeof(struct save87) - sizeof(int)) { *(int*)((char *)p->p_addr + off) = data; return (0); } return (EFAULT); } int fill_regs(p, regs) struct proc *p; struct reg *regs; { struct pcb *pcb; struct trapframe *tp; tp = p->p_md.md_regs; regs->r_es = tp->tf_es; regs->r_ds = tp->tf_ds; regs->r_edi = tp->tf_edi; regs->r_esi = tp->tf_esi; regs->r_ebp = tp->tf_ebp; regs->r_ebx = tp->tf_ebx; regs->r_edx = tp->tf_edx; regs->r_ecx = tp->tf_ecx; regs->r_eax = tp->tf_eax; regs->r_eip = tp->tf_eip; regs->r_cs = tp->tf_cs; regs->r_eflags = tp->tf_eflags; regs->r_esp = tp->tf_esp; regs->r_ss = tp->tf_ss; pcb = &p->p_addr->u_pcb; regs->r_fs = pcb->pcb_fs; regs->r_gs = pcb->pcb_gs; return (0); } int set_regs(p, regs) struct proc *p; struct reg *regs; { struct pcb *pcb; struct trapframe *tp; tp = p->p_md.md_regs; if (!EFLAGS_SECURE(regs->r_eflags, tp->tf_eflags) || !CS_SECURE(regs->r_cs)) return (EINVAL); tp->tf_es = regs->r_es; tp->tf_ds = regs->r_ds; tp->tf_edi = regs->r_edi; tp->tf_esi = regs->r_esi; tp->tf_ebp = regs->r_ebp; tp->tf_ebx = regs->r_ebx; tp->tf_edx = regs->r_edx; tp->tf_ecx = regs->r_ecx; tp->tf_eax = regs->r_eax; tp->tf_eip = regs->r_eip; tp->tf_cs = regs->r_cs; tp->tf_eflags = regs->r_eflags; tp->tf_esp = regs->r_esp; tp->tf_ss = regs->r_ss; pcb = &p->p_addr->u_pcb; pcb->pcb_fs = regs->r_fs; pcb->pcb_gs = regs->r_gs; return (0); } int fill_fpregs(p, fpregs) struct proc *p; struct fpreg *fpregs; { bcopy(&p->p_addr->u_pcb.pcb_savefpu, fpregs, sizeof *fpregs); return (0); } int set_fpregs(p, fpregs) struct proc *p; struct fpreg *fpregs; { bcopy(fpregs, &p->p_addr->u_pcb.pcb_savefpu, sizeof *fpregs); return (0); } #ifndef DDB void Debugger(const char *msg) { printf("Debugger(\"%s\") called.\n", msg); } #endif /* no DDB */ #include /* * Determine the size of the transfer, and make sure it is * within the boundaries of the partition. Adjust transfer * if needed, and signal errors or early completion. */ int bounds_check_with_label(struct buf *bp, struct disklabel *lp, int wlabel) { struct partition *p = lp->d_partitions + dkpart(bp->b_dev); int labelsect = lp->d_partitions[0].p_offset; int maxsz = p->p_size, sz = (bp->b_bcount + DEV_BSIZE - 1) >> DEV_BSHIFT; /* overwriting disk label ? */ /* XXX should also protect bootstrap in first 8K */ if (bp->b_blkno + p->p_offset <= LABELSECTOR + labelsect && #if LABELSECTOR != 0 bp->b_blkno + p->p_offset + sz > LABELSECTOR + labelsect && #endif (bp->b_flags & B_READ) == 0 && wlabel == 0) { bp->b_error = EROFS; goto bad; } #if defined(DOSBBSECTOR) && defined(notyet) /* overwriting master boot record? */ if (bp->b_blkno + p->p_offset <= DOSBBSECTOR && (bp->b_flags & B_READ) == 0 && wlabel == 0) { bp->b_error = EROFS; goto bad; } #endif /* beyond partition? */ if (bp->b_blkno < 0 || bp->b_blkno + sz > maxsz) { /* if exactly at end of disk, return an EOF */ if (bp->b_blkno == maxsz) { bp->b_resid = bp->b_bcount; return(0); } /* or truncate if part of it fits */ sz = maxsz - bp->b_blkno; if (sz <= 0) { bp->b_error = EINVAL; goto bad; } bp->b_bcount = sz << DEV_BSHIFT; } bp->b_pblkno = bp->b_blkno + p->p_offset; return(1); bad: bp->b_flags |= B_ERROR; return(-1); } #ifdef DDB /* * Provide inb() and outb() as functions. They are normally only * available as macros calling inlined functions, thus cannot be * called inside DDB. * * The actual code is stolen from , and de-inlined. */ #undef inb #undef outb /* silence compiler warnings */ u_char inb(u_int); void outb(u_int, u_char); u_char inb(u_int port) { u_char data; /* * We use %%dx and not %1 here because i/o is done at %dx and not at * %edx, while gcc generates inferior code (movw instead of movl) * if we tell it to load (u_short) port. */ __asm __volatile("inb %%dx,%0" : "=a" (data) : "d" (port)); return (data); } void outb(u_int port, u_char data) { u_char al; /* * Use an unnecessary assignment to help gcc's register allocator. * This make a large difference for gcc-1.40 and a tiny difference * for gcc-2.6.0. For gcc-1.40, al had to be ``asm("ax")'' for * best results. gcc-2.6.0 can't handle this. */ al = data; __asm __volatile("outb %0,%%dx" : : "a" (al), "d" (port)); } #endif /* DDB */ Index: head/sys/i386/i386/nexus.c =================================================================== --- head/sys/i386/i386/nexus.c (nonexistent) +++ head/sys/i386/i386/nexus.c (revision 45720) @@ -0,0 +1,409 @@ +/* + * Copyright 1998 Massachusetts Institute of Technology + * + * Permission to use, copy, modify, and distribute this software and + * its documentation for any purpose and without fee is hereby + * granted, provided that both the above copyright notice and this + * permission notice appear in all copies, that both the above + * copyright notice and this permission notice appear in all + * supporting documentation, and that the name of M.I.T. not be used + * in advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. M.I.T. makes + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS + * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT + * SHALL M.I.T. 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. + * + * $Id$ + */ + +/* + * This code implements a `root nexus' for Intel Architecture + * machines. The function of the root nexus is to serve as an + * attachment point for both processors and buses, and to manage + * resources which are common to all of them. In particular, + * this code implements the core resource managers for interrupt + * requests, DMA requests (which rightfully should be a part of the + * ISA code but it's easier to do it here for now), I/O port addresses, + * and I/O memory address space. + */ + +#include "opt_smp.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#ifdef APIC_IO +#include +#include +#endif + +#include +#include +#include + +#include + +#include "eisa.h" +#include "isa.h" +#include "pci.h" +#include "npx.h" +#include "apm.h" + +static struct rman irq_rman, drq_rman, port_rman, mem_rman; + +static int nexus_probe(device_t); +static void nexus_print_child(device_t, device_t); +static struct resource *nexus_alloc_resource(device_t, device_t, int, int *, + u_long, u_long, u_long, u_int); +static int nexus_activate_resource(device_t, device_t, int, int, + struct resource *); +static int nexus_deactivate_resource(device_t, device_t, int, int, + struct resource *); +static int nexus_release_resource(device_t, device_t, int, int, + struct resource *); +static int nexus_setup_intr(device_t, device_t, struct resource *, + void (*)(void *), void *, void **); +static int nexus_teardown_intr(device_t, device_t, struct resource *, + void *); + +static device_method_t nexus_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, nexus_probe), + DEVMETHOD(device_attach, bus_generic_attach), + DEVMETHOD(device_detach, bus_generic_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + + /* Bus interface */ + DEVMETHOD(bus_print_child, nexus_print_child), + DEVMETHOD(bus_read_ivar, bus_generic_read_ivar), + DEVMETHOD(bus_write_ivar, bus_generic_write_ivar), + DEVMETHOD(bus_alloc_resource, nexus_alloc_resource), + DEVMETHOD(bus_release_resource, nexus_release_resource), + DEVMETHOD(bus_activate_resource, nexus_activate_resource), + DEVMETHOD(bus_deactivate_resource, nexus_deactivate_resource), + DEVMETHOD(bus_setup_intr, nexus_setup_intr), + DEVMETHOD(bus_teardown_intr, nexus_teardown_intr), + + { 0, 0 } +}; + +static driver_t nexus_driver = { + "nexus", + nexus_methods, + DRIVER_TYPE_MISC, + 1, /* no softc */ +}; +static devclass_t nexus_devclass; + +DRIVER_MODULE(nexus, root, nexus_driver, nexus_devclass, 0, 0); + +#ifdef APIC_IO +#define LASTIRQ (NINTR - 1) +#else +#define LASTIRQ 15 +#endif + +static int +nexus_probe(device_t dev) +{ + device_t child; + + device_quiet(dev); /* suppress attach message for neatness */ + + irq_rman.rm_start = 0; + irq_rman.rm_end = LASTIRQ; + irq_rman.rm_type = RMAN_ARRAY; + irq_rman.rm_descr = "Interrupt request lines"; + if (rman_init(&irq_rman) + || rman_manage_region(&irq_rman, 0, 1) + || rman_manage_region(&irq_rman, 3, LASTIRQ)) + panic("nexus_probe irq_rman"); + + drq_rman.rm_start = 0; + drq_rman.rm_end = 7; + drq_rman.rm_type = RMAN_ARRAY; + drq_rman.rm_descr = "DMA request lines"; + /* XXX drq 0 not available on some machines */ + if (rman_init(&drq_rman) + || rman_manage_region(&drq_rman, 0, 7)) + panic("nexus_probe drq_rman"); + + port_rman.rm_start = 0; + port_rman.rm_end = 0xffff; + port_rman.rm_type = RMAN_ARRAY; + port_rman.rm_descr = "I/O ports"; + if (rman_init(&port_rman) + || rman_manage_region(&port_rman, 0, 0xffff)) + panic("nexus_probe port_rman"); + + mem_rman.rm_start = 0; + mem_rman.rm_end = ~0u; + mem_rman.rm_type = RMAN_ARRAY; + mem_rman.rm_descr = "I/O memory addresses"; + if (rman_init(&mem_rman) + || rman_manage_region(&mem_rman, 0, ~0)) + panic("nexus_probe mem_rman"); + +#if NNPX > 0 + child = device_add_child(dev, "npx", 0, 0); + if (child == 0) + panic("nexus_probe npx"); +#endif /* NNPX > 0 */ +#if NAPM > 0 + child = device_add_child(dev, "apm", 0, 0); + if (child == 0) + panic("nexus_probe apm"); +#endif /* NAPM > 0 */ +#if NPCI > 0 + /* Add a PCI bridge if pci bus is present */ + if (pci_cfgopen() != 0) { + child = device_add_child(dev, "pcib", 0, 0); + if (child == 0) + panic("nexus_probe pcib"); + } +#endif +#if 0 && NEISA > 0 + child = device_add_child(dev, "eisa", 0, 0); + if (child == 0) + panic("nexus_probe eisa"); +#endif +#if NISA > 0 + /* Add an ISA bus directly if pci bus is not present */ + if (pci_cfgopen() == 0) { + child = device_add_child(dev, "isa", 0, 0); + if (child == 0) + panic("nexus_probe isa"); + } +#endif + return 0; +} + +static void +nexus_print_child(device_t bus, device_t child) +{ + printf(" on motherboard"); +} + +/* + * Allocate a resource on behalf of child. NB: child is usually going to be a + * child of one of our descendants, not a direct child of nexus0. + * (Exceptions include npx.) + */ +static struct resource * +nexus_alloc_resource(device_t bus, device_t child, int type, int *rid, + u_long start, u_long end, u_long count, u_int flags) +{ + struct resource *rv; + struct rman *rm; + + switch (type) { + case SYS_RES_IRQ: + rm = &irq_rman; + break; + + case SYS_RES_DRQ: + rm = &drq_rman; + break; + + case SYS_RES_IOPORT: + rm = &port_rman; + break; + + case SYS_RES_MEMORY: + rm = &mem_rman; + break; + + default: + return 0; + } + + rv = rman_reserve_resource(rm, start, end, count, flags, child); + if (rv == 0) + return 0; + + if (type == SYS_RES_MEMORY) { + caddr_t vaddr = 0; + + if (rv->r_end < 1024 * 1024 * 1024) { + /* + * The first 1Mb is mapped at KERNBASE. + */ + vaddr = (caddr_t)((uintptr_t)KERNBASE + rv->r_start); + } else { + u_int32_t paddr; + u_int32_t psize; + u_int32_t poffs; + + paddr = rv->r_start; + psize = rv->r_end - rv->r_start; + + poffs = paddr - trunc_page(paddr); + vaddr = (caddr_t) pmap_mapdev(paddr-poffs, psize+poffs) + poffs; + } + rman_set_virtual(rv, vaddr); + rman_set_bustag(rv, I386_BUS_SPACE_MEM); + rman_set_bushandle(rv, (bus_space_handle_t) vaddr); + } else if (type == SYS_RES_IOPORT) { + rman_set_bustag(rv, I386_BUS_SPACE_IO); + rman_set_bushandle(rv, rv->r_start); + } + return rv; +} + +static int +nexus_activate_resource(device_t bus, device_t child, int type, int rid, + struct resource *r) +{ + return (rman_activate_resource(r)); +} + +static int +nexus_deactivate_resource(device_t bus, device_t child, int type, int rid, + struct resource *r) +{ + return (rman_deactivate_resource(r)); +} + +static int +nexus_release_resource(device_t bus, device_t child, int type, int rid, + struct resource *r) +{ + return (rman_release_resource(r)); +} + +/* + * Currently this uses the really grody interface from kern/kern_intr.c + * (which really doesn't belong in kern/anything.c). Eventually, all of + * the code in kern_intr.c and machdep_intr.c should get moved here, since + * this is going to be the official interface. + */ +static int +nexus_setup_intr(device_t bus, device_t child, struct resource *irq, + void (*ihand)(void *), void *arg, void **cookiep) +{ + intrmask_t *mask; + driver_t *driver; + int error, icflags; + + if (child) + device_printf(child, "interrupting at irq %d\n", + (int)irq->r_start); + + *cookiep = 0; + if (irq->r_flags & RF_SHAREABLE) + icflags = 0; + else + icflags = INTR_EXCL; + + driver = device_get_driver(child); + switch (driver->type) { + case DRIVER_TYPE_TTY: + mask = &tty_imask; + break; + case (DRIVER_TYPE_TTY | DRIVER_TYPE_FAST): + mask = &tty_imask; + icflags |= INTR_FAST; + break; + case DRIVER_TYPE_BIO: + mask = &bio_imask; + break; + case DRIVER_TYPE_NET: + mask = &net_imask; + break; + case DRIVER_TYPE_CAM: + mask = &cam_imask; + break; + case DRIVER_TYPE_MISC: + mask = 0; + break; + default: + panic("still using grody create_intr interface"); + } + + /* + * We depend here on rman_activate_resource() being idempotent. + */ + error = rman_activate_resource(irq); + if (error) + return (error); + + *cookiep = intr_create((void *)(intptr_t)-1, irq->r_start, ihand, arg, + mask, icflags); + if (*cookiep) + error = intr_connect(*cookiep); + else + error = EINVAL; /* XXX ??? */ + + return (error); +} + +static int +nexus_teardown_intr(device_t dev, device_t child, struct resource *r, void *ih) +{ + return (intr_destroy(ih)); +} + +static devclass_t pcib_devclass; + +static int +nexus_pcib_probe(device_t dev) +{ + device_set_desc(dev, "PCI host bus adapter"); + + device_add_child(dev, "pci", 0, 0); + + return 0; +} + +static device_method_t nexus_pcib_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, nexus_pcib_probe), + DEVMETHOD(device_attach, bus_generic_attach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + + /* Bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), + DEVMETHOD(bus_release_resource, bus_generic_release_resource), + DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), + DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), + DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), + DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), + + { 0, 0 } +}; + +static driver_t nexus_pcib_driver = { + "pcib", + nexus_pcib_methods, + DRIVER_TYPE_MISC, + 1, +}; + +DRIVER_MODULE(pcib, nexus, nexus_pcib_driver, pcib_devclass, 0, 0); Property changes on: head/sys/i386/i386/nexus.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: head/sys/i386/i386/userconfig.c =================================================================== --- head/sys/i386/i386/userconfig.c (revision 45719) +++ head/sys/i386/i386/userconfig.c (revision 45720) @@ -1,3558 +1,3631 @@ /** ** Copyright (c) 1995 ** Michael Smith, msmith@freebsd.org. All rights reserved. ** ** This code contains a module marked : * Copyright (c) 1991 Regents of the University of California. * All rights reserved. * Copyright (c) 1994 Jordan K. Hubbard * All rights reserved. * Copyright (c) 1994 David Greenman * All rights reserved. * * Many additional changes by Bruce Evans * * This code is derived from software contributed by the * University of California Berkeley, Jordan K. Hubbard, * David Greenman and Bruce Evans. ** As such, it contains code subject to the above copyrights. ** The module and its copyright can be found below. ** ** 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 as ** the first lines of this file unmodified. ** 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 acknowledgment: ** This product includes software developed by Michael Smith. ** 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 MICHAEL SMITH ``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 MICHAEL SMITH 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. ** - ** $Id: userconfig.c,v 1.132 1999/02/21 16:33:51 n_hibma Exp $ + ** $Id: userconfig.c,v 1.133 1999/04/06 17:08:30 wpaul Exp $ **/ /** ** USERCONFIG ** ** Kernel boot-time configuration manipulation tool for FreeBSD. ** ** Two modes of operation are supported : the default is the line-editor mode, ** the command "visual" invokes the fullscreen mode. ** ** The line-editor mode is the old favorite from FreeBSD 2.0/20.05 &c., the ** fullscreen mode requires syscons or a minimal-ansi serial console. **/ /** ** USERCONFIG, visual mode. ** ** msmith@freebsd.org ** ** Look for "EDIT THIS LIST" to add to the list of known devices ** ** ** There are a number of assumptions made in this code. ** ** - That the console supports a minimal set of ANSI escape sequences ** (See the screen manipulation section for a summary) ** and has at least 24 rows. ** - That values less than or equal to zero for any of the device ** parameters indicate that the driver does not use the parameter. ** - That the only tunable parameter for PCI devices are their flags. ** - That flags are _always_ editable. ** ** Devices marked as disabled are imported as such. PCI devices are ** listed under a seperate heading for informational purposes only. ** To date, there is no means for changing the behaviour of PCI drivers ** from UserConfig. ** ** Note that some EISA devices probably fall into this category as well, ** and in fact the actual bus supported by some drivers is less than clear. ** A longer-term goal might be to list drivers by instance rather than ** per bus-presence. ** ** For this tool to be useful, the list of devices below _MUST_ be updated ** when a new driver is brought into the kernel. It is not possible to ** extract this information from the drivers in the kernel. ** ** XXX - TODO: ** ** - Display _what_ a device conflicts with. ** - Implement page up/down (as what?) ** - Wizard mode (no restrictions) ** - Find out how to put syscons back into low-intensity mode so that the ** !b escape is useful on the console. (It seems to be that it actually ** gets low/high intensity backwards. That looks OK.) ** ** - Only display headings with devices under them. (difficult) **/ #include "opt_userconfig.h" #include "pci.h" #include #include #include #include #include #include #include +#include #include #include #include + #include #include "pnp.h" #if NPNP > 0 #include #endif #include static MALLOC_DEFINE(M_DEVL, "isa_devlist", "isa_device lists in userconfig()"); static struct isa_device *isa_devlist; /* list read by kget to extract changes */ +static struct isa_device *isa_devtab; /* fake isa_device table */ +static struct isa_driver *isa_drvtab; /* fake driver list */ static int userconfig_boot_parsing; /* set if we are reading from the boot instructions */ #define putchar(x) cnputc(x) +static void load_devtab(void); +static void free_devtab(void); +static void save_resource(struct isa_device *); + static int sysctl_machdep_uc_devlist SYSCTL_HANDLER_ARGS { struct isa_device *id; int error=0; char name[8]; if(!req->oldptr) { /* Only sizing */ id=isa_devlist; while(id) { error+=sizeof(struct isa_device)+8; id=id->id_next; } return(SYSCTL_OUT(req,0,error)); } else { /* Output the data. The buffer is filled with consecutive * struct isa_device and char buf[8], containing the name * (not guaranteed to end with '\0'). */ id=isa_devlist; while(id) { error=sysctl_handle_opaque(oidp,id, sizeof(struct isa_device),req); if(error) return(error); strncpy(name,id->id_driver->name,8); error=sysctl_handle_opaque(oidp,name, 8,req); if(error) return(error); id=id->id_next; } return(0); } } SYSCTL_PROC( _machdep, OID_AUTO, uc_devlist, CTLFLAG_RD, 0, 0, sysctl_machdep_uc_devlist, "A", "List of ISA devices changed in UserConfig"); /* ** Obtain command input. ** ** Initially, input is read from a possibly-loaded script. ** At the end of the script, or if no script is supplied, ** behaviour is determined by the RB_CONFIG (-c) flag. If ** the flag is set, user input is read from the console; if ** unset, the 'quit' command is invoked and userconfig ** will exit. ** ** Note that quit commands encountered in the script will be ** ignored if the RB_CONFIG flag is supplied. */ static const char *config_script; static int config_script_size; /* use of int for -ve magic value */ #define has_config_script() (config_script_size > 0) static int init_config_script(void) { caddr_t autoentry, autoattr; /* Look for loaded userconfig script */ autoentry = preload_search_by_type("userconfig_script"); if (autoentry != NULL) { /* We have one, get size and data */ config_script_size = 0; if ((autoattr = preload_search_info(autoentry, MODINFO_SIZE)) != NULL) config_script_size = (size_t)*(u_int32_t *)autoattr; config_script = NULL; if ((autoattr = preload_search_info(autoentry, MODINFO_ADDR)) != NULL) config_script = *(const char **)autoattr; /* sanity check */ if ((config_script_size == 0) || (config_script == NULL)) { config_script_size = 0; config_script = NULL; } } return has_config_script(); } static int getchar(void) { int c = -1; #ifdef INTRO_USERCONFIG static int intro = 0; #endif if (has_config_script()) { /* Consume character from loaded userconfig script, display */ userconfig_boot_parsing = 1; c = *config_script; config_script++; config_script_size--; } else { #ifdef INTRO_USERCONFIG if (userconfig_boot_parsing) { if (!(boothowto & RB_CONFIG)) { /* userconfig_script, !RB_CONFIG -> quit */ if (intro == 0) { c = 'q'; config_script = "uit\n"; config_script_size = strlen(config_script); /* userconfig_script will be 1 on the next pass */ } } else { /* userconfig_script, RB_CONFIG -> cngetc() */ } } else { if (!(boothowto & RB_CONFIG)) { /* no userconfig_script, !RB_CONFIG -> show intro */ if (intro == 0) { intro = 1; c = 'i'; config_script = "ntro\n"; config_script_size = strlen(config_script); /* userconfig_script will be 1 on the next pass */ } } else { /* no userconfig_script, RB_CONFIG -> cngetc() */ } } #else /* !INTRO_USERCONFIG */ /* assert(boothowto & RB_CONFIG) */ #endif /* INTRO_USERCONFIG */ userconfig_boot_parsing = 0; if (c <= 0) c = cngetc(); } return(c); } #ifndef FALSE #define FALSE (0) #define TRUE (!FALSE) #endif #ifdef VISUAL_USERCONFIG -static struct isa_device *devtabs[] = { isa_devtab_bio, isa_devtab_tty, isa_devtab_net, - isa_devtab_cam, isa_devtab_null, NULL }; typedef struct { char dev[16]; /* device basename */ char name[60]; /* long name */ int attrib; /* things to do with the device */ int class; /* device classification */ } DEV_INFO; #define FLG_INVISIBLE (1<<0) /* device should not be shown */ #define FLG_MANDATORY (1<<1) /* device can be edited but not disabled */ #define FLG_FIXIRQ (1<<2) /* device IRQ cannot be changed */ #define FLG_FIXIOBASE (1<<3) /* device iobase cannot be changed */ #define FLG_FIXMADDR (1<<4) /* device maddr cannot be changed */ #define FLG_FIXMSIZE (1<<5) /* device msize cannot be changed */ #define FLG_FIXDRQ (1<<6) /* device DRQ cannot be changed */ #define FLG_FIXED (FLG_FIXIRQ|FLG_FIXIOBASE|FLG_FIXMADDR|FLG_FIXMSIZE|FLG_FIXDRQ) #define FLG_IMMUTABLE (FLG_FIXED|FLG_MANDATORY) #define CLS_STORAGE 1 /* storage devices */ #define CLS_NETWORK 2 /* network interfaces */ #define CLS_COMMS 3 /* serial, parallel ports */ #define CLS_INPUT 4 /* user input : mice, keyboards, joysticks etc */ #define CLS_MMEDIA 5 /* "multimedia" devices (sound, video, etc) */ #define CLS_PCI 254 /* PCI devices */ #define CLS_MISC 255 /* none of the above */ typedef struct { char name[60]; int number; } DEVCLASS_INFO; static DEVCLASS_INFO devclass_names[] = { { "Storage : ", CLS_STORAGE}, { "Network : ", CLS_NETWORK}, { "Communications : ", CLS_COMMS}, { "Input : ", CLS_INPUT}, { "Multimedia : ", CLS_MMEDIA}, { "PCI : ", CLS_PCI}, { "Miscellaneous : ", CLS_MISC}, { "",0}}; /********************* EDIT THIS LIST **********************/ /** Notes : ** ** - PCI devices should be marked FLG_IMMUTABLE. They should not be movable ** or editable, and have no attributes. This is handled in getdevs() and ** devinfo(), so drivers that have a presence on busses other than PCI ** should have appropriate flags set below. ** - Devices that shouldn't be seen or removed should be marked FLG_INVISIBLE. ** - XXX The list below should be reviewed by the driver authors to verify ** that the correct flags have been set for each driver, and that the ** descriptions are accurate. **/ static DEV_INFO device_info[] = { /*---Name----- ---Description---------------------------------------------- */ {"isp", "QLogic ISP SCSI Controller", FLG_IMMUTABLE, CLS_STORAGE}, {"dpt", "DPT SCSI RAID Controller", FLG_IMMUTABLE, CLS_STORAGE}, {"adv", "AdvanSys SCSI narrow controller", 0, CLS_STORAGE}, {"adw", "AdvanSys SCSI WIDE controller", 0, CLS_STORAGE}, {"bt", "Buslogic SCSI controller", 0, CLS_STORAGE}, {"ahc", "Adaptec 274x/284x/294x SCSI controller", 0, CLS_STORAGE}, {"ahb", "Adaptec 174x SCSI controller", 0, CLS_STORAGE}, {"aha", "Adaptec 154x SCSI controller", 0, CLS_STORAGE}, {"uha", "Ultrastor 14F/24F/34F SCSI controller",0, CLS_STORAGE}, {"aic", "Adaptec 152x SCSI and compatible sound cards", 0, CLS_STORAGE}, {"nca", "ProAudio Spectrum SCSI and compatibles", 0, CLS_STORAGE}, {"sea", "Seagate ST01/ST02 SCSI and compatibles", 0, CLS_STORAGE}, {"wds", "Western Digitial WD7000 SCSI controller", 0, CLS_STORAGE}, {"ncr", "NCR/Symbios 53C810/15/25/60/75 SCSI controller",FLG_FIXED,CLS_STORAGE}, {"wdc", "IDE/ESDI/MFM disk controller", 0, CLS_STORAGE}, {"fdc", "Floppy disk controller", FLG_FIXED, CLS_STORAGE}, {"mcd", "Mitsumi CD-ROM", 0, CLS_STORAGE}, {"scd", "Sony CD-ROM", 0, CLS_STORAGE}, {"matcdc", "Matsushita/Panasonic/Creative CDROM", 0, CLS_STORAGE}, {"wt", "Wangtek/Archive QIC-02 Tape drive", 0, CLS_STORAGE}, {"amd", "Tekram DC-390(T) / AMD 53c974 based PCI SCSI", FLG_FIXED, CLS_STORAGE}, {"cs", "IBM EtherJet, CS89x0-based Ethernet adapters",0, CLS_NETWORK}, {"ed", "NE1000,NE2000,3C503,WD/SMC80xx Ethernet adapters",0, CLS_NETWORK}, {"el", "3C501 Ethernet adapter", 0, CLS_NETWORK}, {"ep", "3C509 Ethernet adapter", 0, CLS_NETWORK}, {"ex", "Intel EtherExpress Pro/10 Ethernet adapter", 0, CLS_NETWORK}, {"fe", "Fujitsu MD86960A/MB869685A Ethernet adapters", 0, CLS_NETWORK}, {"fea", "DEC DEFEA EISA FDDI adapter", 0, CLS_NETWORK}, {"fxp", "Intel EtherExpress Pro/100B Ethernet adapter", 0, CLS_NETWORK}, {"ie", "AT&T Starlan 10 and EN100, 3C507, NI5210 Ethernet adapters",0,CLS_NETWORK}, {"ix", "Intel EtherExpress Ethernet adapter", 0, CLS_NETWORK}, {"le", "DEC Etherworks 2 and 3 Ethernet adapters", 0, CLS_NETWORK}, {"lnc", "Isolan, Novell NE2100/NE32-VL Ethernet adapters", 0,CLS_NETWORK}, {"ti", "Alteon Networks Tigon gigabit Ethernet adapters", 0,CLS_NETWORK}, {"tl", "Texas Instruments ThunderLAN Ethernet adapters", 0,CLS_NETWORK}, {"tx", "SMC 9432TX Ethernet adapters", 0, CLS_NETWORK}, {"vx", "3COM 3C590/3C595 Ethernet adapters", 0, CLS_NETWORK}, {"ze", "IBM/National Semiconductor PCMCIA Ethernet adapter",0, CLS_NETWORK}, {"zp", "3COM PCMCIA Etherlink III Ethernet adapter", 0, CLS_NETWORK}, {"ax", "ASIC AX88140A ethernet adapter", FLG_FIXED, CLS_NETWORK}, {"de", "DEC DC21040 Ethernet adapter", FLG_FIXED, CLS_NETWORK}, {"fpa", "DEC DEFPA PCI FDDI adapter", FLG_FIXED, CLS_NETWORK}, {"rl", "RealTek 8129/8139 ethernet adapter", FLG_FIXED, CLS_NETWORK}, {"mx", "Macronix PMAC ethernet adapter", FLG_FIXED, CLS_NETWORK}, {"pn", "Lite-On 82c168/82c169 PNIC adapter", FLG_FIXED, CLS_NETWORK}, {"tl", "Texas Instruments ThunderLAN ethernet adapter", FLG_FIXED, CLS_NETWORK}, {"vr", "VIA Rhine/Rhine II ethernet adapter", FLG_FIXED, CLS_NETWORK}, {"wb", "Winbond W89C840F ethernet adapter", FLG_FIXED, CLS_NETWORK}, {"xl", "3COM 3C90x PCI ethernet adapter", FLG_FIXED, CLS_NETWORK}, {"rdp", "RealTek RTL8002 Pocket Ethernet", 0, CLS_NETWORK}, {"sio", "8250/16450/16550 Serial port", 0, CLS_COMMS}, {"cx", "Cronyx/Sigma multiport sync/async adapter",0, CLS_COMMS}, {"rc", "RISCom/8 multiport async adapter", 0, CLS_COMMS}, {"cy", "Cyclades multiport async adapter", 0, CLS_COMMS}, {"cyy", "Cyclades Ye/PCI multiport async adapter",FLG_INVISIBLE,CLS_COMMS}, {"dgb", "Digiboard PC/Xe, PC/Xi async adapter", 0, CLS_COMMS}, {"si", "Specialix SI/XIO async adapter", 0, CLS_COMMS}, {"stl", "Stallion EasyIO/Easy Connection 8/32 async adapter",0, CLS_COMMS}, {"stli", "Stallion intelligent async adapter" ,0, CLS_COMMS}, {"ppc", "Parallel Port chipset", 0, CLS_COMMS}, {"gp", "National Instruments AT-GPIB/TNT driver", 0, CLS_COMMS}, {"uhci", "UHCI USB host controller driver", FLG_IMMUTABLE, CLS_COMMS}, {"ohci", "OHCI USB host controller driver", FLG_IMMUTABLE, CLS_COMMS}, {"atkbdc", "Keyboard controller", FLG_INVISIBLE, CLS_INPUT}, {"atkbd", "Keyboard", FLG_FIXED, CLS_INPUT}, {"mse", "Microsoft Bus Mouse", 0, CLS_INPUT}, {"psm", "PS/2 Mouse", FLG_FIXED, CLS_INPUT}, {"joy", "Joystick", FLG_FIXED, CLS_INPUT}, {"vt", "PCVT console driver", FLG_IMMUTABLE, CLS_INPUT}, {"sc", "Syscons console driver", FLG_IMMUTABLE, CLS_INPUT}, {"bktr", "Brooktree BT848 based frame grabber/tuner card", 0,CLS_MMEDIA}, {"pcm", "New Luigi audio driver for all supported sound cards", 0,CLS_MMEDIA}, {"sb", "Soundblaster PCM (SB, SBPro, SB16, ProAudio Spectrum)",0,CLS_MMEDIA}, {"sbxvi", "Soundblaster 16", 0, CLS_MMEDIA}, {"sbmidi", "Soundblaster MIDI interface", 0, CLS_MMEDIA}, {"awe", "AWE32 MIDI", 0, CLS_MMEDIA}, {"pas", "ProAudio Spectrum PCM and MIDI", 0, CLS_MMEDIA}, {"gus", "Gravis Ultrasound, Ultrasound 16 and Ultrasound MAX",0,CLS_MMEDIA}, {"gusxvi", "Gravis Ultrasound 16-bit PCM", 0, CLS_MMEDIA}, {"gusmax", "Gravis Ultrasound MAX", 0, CLS_MMEDIA}, {"mss", "Microsoft Sound System", 0, CLS_MMEDIA}, {"opl", "OPL-2/3 FM, Soundblaster, SBPro, SB16, ProAudio Spectrum",0,CLS_MMEDIA}, {"mpu", "Roland MPU401 MIDI", 0, CLS_MMEDIA}, {"sscape", "Ensoniq Soundscape MIDI interface", 0, CLS_MMEDIA}, {"sscape_mss", "Ensoniq Soundscape PCM", 0, CLS_MMEDIA}, {"uart", "6850 MIDI UART", 0, CLS_MMEDIA}, {"pca", "PC speaker PCM audio driver", FLG_FIXED, CLS_MMEDIA}, {"ctx", "Coretex-I frame grabber", 0, CLS_MMEDIA}, {"spigot", "Creative Labs Video Spigot video capture", 0, CLS_MMEDIA}, {"scc", "IBM Smart Capture Card", 0, CLS_MMEDIA}, {"gsc", "Genius GS-4500 hand scanner", 0, CLS_MMEDIA}, {"asc", "AmiScan scanner", 0, CLS_MMEDIA}, {"qcam", "QuickCam parallel port camera", 0, CLS_MMEDIA}, {"apm", "Advanced Power Management", FLG_FIXED, CLS_MISC}, {"labpc", "National Instruments Lab-PC/Lab-PC+", 0, CLS_MISC}, {"npx", "Math coprocessor", FLG_INVISIBLE, CLS_MISC}, {"lkm", "Loadable PCI driver support", FLG_INVISIBLE, CLS_MISC}, {"vga", "Catchall PCI VGA driver", FLG_INVISIBLE, CLS_MISC}, {"chip", "PCI chipset support", FLG_INVISIBLE, CLS_MISC}, {"piix", "Intel 82371 Bus-master IDE controller", FLG_INVISIBLE, CLS_MISC}, {"ide_pci", "PCI IDE controller", FLG_INVISIBLE, CLS_MISC}, {"","",0,0}}; typedef struct _devlist_struct { char name[80]; int attrib; /* flag values as per the FLG_* defines above */ int class; /* disk, etc as per the CLS_* defines above */ char dev[16]; int iobase,irq,drq,maddr,msize,unit,flags,conflict_ok,id; int comment; /* 0 = device, 1 = comment, 2 = collapsed comment */ int conflicts; /* set/reset by findconflict, count of conflicts */ int changed; /* nonzero if the device has been edited */ struct isa_device *device; struct _devlist_struct *prev,*next; } DEV_LIST; #define DEV_DEVICE 0 #define DEV_COMMENT 1 #define DEV_ZOOMED 2 #define LIST_CURRENT (1<<0) #define LIST_SELECTED (1<<1) #define KEY_EXIT 0 /* return codes from dolist() and friends */ #define KEY_DO 1 #define KEY_DEL 2 #define KEY_TAB 3 #define KEY_REDRAW 4 #define KEY_UP 5 /* these only returned from editval() */ #define KEY_DOWN 6 #define KEY_LEFT 7 #define KEY_RIGHT 8 #define KEY_NULL 9 /* this allows us to spin & redraw */ #define KEY_ZOOM 10 /* these for zoom all/collapse all */ #define KEY_UNZOOM 11 #define KEY_HELP 12 /* duh? */ static void redraw(void); static void insdev(DEV_LIST *dev, DEV_LIST *list); static int devinfo(DEV_LIST *dev); static int visuserconfig(void); static DEV_LIST *active = NULL,*inactive = NULL; /* driver lists */ static DEV_LIST *alist,*ilist; /* visible heads of the driver lists */ static DEV_LIST scratch; /* scratch record */ static int conflicts; /* total conflict count */ static char lines[] = "--------------------------------------------------------------------------------"; static char spaces[] = " "; /** ** Device manipulation stuff : find, describe, configure. **/ /** ** setdev ** ** Sets the device referenced by (*dev) to the parameters in the struct, ** and the enable flag according to (enabled) **/ static void setdev(DEV_LIST *dev, int enabled) { if (dev->iobase == -2) /* PCI device */ return; dev->device->id_iobase = dev->iobase; /* copy happy */ dev->device->id_irq = (u_short)(dev->irq < 16 ? 1<irq : 0); /* IRQ is bitfield */ dev->device->id_drq = (short)dev->drq; dev->device->id_maddr = (caddr_t)dev->maddr; dev->device->id_msize = dev->msize; dev->device->id_flags = dev->flags; dev->device->id_enabled = enabled; } /** ** getdevs ** ** Walk the kernel device tables and build the active and inactive lists **/ static void getdevs(void) { - int i,j; + int i; struct isa_device *ap; - for (j = 0; devtabs[j]; j++) /* ISA devices */ - { - ap = devtabs[j]; /* pointer to array of devices */ + ap = isa_devtab; /* pointer to array of devices */ for (i = 0; ap[i].id_id; i++) /* for each device in this table */ { scratch.unit = ap[i].id_unit; /* device parameters */ strcpy(scratch.dev,ap[i].id_driver->name); scratch.iobase = ap[i].id_iobase; scratch.irq = ffs(ap[i].id_irq)-1; scratch.drq = ap[i].id_drq; scratch.maddr = (int)ap[i].id_maddr; scratch.msize = ap[i].id_msize; scratch.flags = ap[i].id_flags; scratch.conflict_ok = ap[i].id_conflicts; scratch.comment = DEV_DEVICE; /* admin stuff */ scratch.conflicts = 0; scratch.device = &ap[i]; /* save pointer for later reference */ scratch.changed = 0; if (!devinfo(&scratch)) /* get more info on the device */ insdev(&scratch,ap[i].id_enabled?active:inactive); } - } #if NPCI > 0 for (i = 0; i < pcidevice_set.ls_length; i++) { if (pcidevice_set.ls_items[i]) { if (((const struct pci_device *)pcidevice_set.ls_items[i])->pd_name) { strcpy(scratch.dev,((const struct pci_device *)pcidevice_set.ls_items[i])->pd_name); scratch.iobase = -2; /* mark as PCI for future reference */ scratch.irq = -2; scratch.drq = -2; scratch.maddr = -2; scratch.msize = -2; scratch.flags = 0; scratch.conflict_ok = 0; /* shouldn't conflict */ scratch.comment = DEV_DEVICE; /* is a device */ scratch.unit = 0; /* arbitrary number of them */ scratch.conflicts = 0; scratch.device = NULL; scratch.changed = 0; if (!devinfo(&scratch)) /* look up name, set class and flags */ insdev(&scratch,active); /* always active */ } } } #endif /* NPCI > 0 */ } /** ** Devinfo ** ** Fill in (dev->name), (dev->attrib) and (dev->type) from the device_info array. ** If the device is unknown, put it in the CLS_MISC class, with no flags. ** ** If the device is marked "invisible", return nonzero; the caller should ** not insert any such device into either list. ** ** PCI devices are always inserted into CLS_PCI, regardless of the class associated ** with the driver type. **/ static int devinfo(DEV_LIST *dev) { int i; for (i = 0; device_info[i].class; i++) { if (!strcmp(dev->dev,device_info[i].dev)) { if (device_info[i].attrib & FLG_INVISIBLE) /* forget we ever saw this one */ return(1); strcpy(dev->name,device_info[i].name); /* get the name */ if (dev->iobase == -2) { /* is this a PCI device? */ dev->attrib = FLG_IMMUTABLE; /* dark green ones up the back... */ dev->class = CLS_PCI; } else { dev->attrib = device_info[i].attrib; /* light green ones up the front */ dev->class = device_info[i].class; } return(0); } } strcpy(dev->name,"Unknown device"); dev->attrib = 0; dev->class = CLS_MISC; return(0); } /** ** List manipulation stuff : add, move, initialise, free, traverse ** ** Note that there are assumptions throughout this code that ** the first entry in a list will never move. (assumed to be ** a comment). **/ /** ** Adddev ** ** appends a copy of (dev) to the end of (*list) **/ static void addev(DEV_LIST *dev, DEV_LIST **list) { DEV_LIST *lp,*ap; lp = (DEV_LIST *)malloc(sizeof(DEV_LIST),M_DEVL,M_WAITOK); bcopy(dev,lp,sizeof(DEV_LIST)); /* create copied record */ if (*list) /* list exists */ { ap = *list; while(ap->next) ap = ap->next; /* scoot to end of list */ lp->prev = ap; lp->next = NULL; ap->next = lp; }else{ /* list does not yet exist */ *list = lp; lp->prev = lp->next = NULL; /* list now exists */ } } /** ** Findspot ** ** Finds the 'appropriate' place for (dev) in (list) ** ** 'Appropriate' means in numeric order with other devices of the same type, ** or in alphabetic order following a comment of the appropriate type. ** or at the end of the list if an appropriate comment is not found. (this should ** never happen) ** (Note that the appropriate point is never the top, but may be the bottom) **/ static DEV_LIST * findspot(DEV_LIST *dev, DEV_LIST *list) { DEV_LIST *ap = NULL; /* search for a previous instance of the same device */ if (dev->iobase != -2) /* avoid PCI devices grouping with non-PCI devices */ { for (ap = list; ap; ap = ap->next) { if (ap->comment != DEV_DEVICE) /* ignore comments */ continue; if (ap->iobase == -2) /* don't group with a PCI device */ continue; if (!strcmp(dev->dev,ap->dev)) /* same base device */ { if ((dev->unit <= ap->unit) /* belongs before (equal is bad) */ || !ap->next) /* or end of list */ { ap = ap->prev; /* back up one */ break; /* done here */ } if (ap->next) /* if the next item exists */ { if (ap->next->comment != DEV_DEVICE) /* next is a comment */ break; if (strcmp(dev->dev,ap->next->dev)) /* next is a different device */ break; } } } } if (!ap) /* not sure yet */ { /* search for a class that the device might belong to */ for (ap = list; ap; ap = ap->next) { if (ap->comment != DEV_DEVICE) /* look for simlar devices */ continue; if (dev->class != ap->class) /* of same class too 8) */ continue; if (strcmp(dev->dev,ap->dev) < 0) /* belongs before the current entry */ { ap = ap->prev; /* back up one */ break; /* done here */ } if (ap->next) /* if the next item exists */ if (ap->next->comment != DEV_DEVICE) /* next is a comment, go here */ break; } } if (!ap) /* didn't find a match */ { for (ap = list; ap->next; ap = ap->next) /* try for a matching comment */ if ((ap->comment != DEV_DEVICE) && (ap->class == dev->class)) /* appropriate place? */ break; } /* or just put up with last */ return(ap); } /** ** Insdev ** ** Inserts a copy of (dev) at the appropriate point in (list) **/ static void insdev(DEV_LIST *dev, DEV_LIST *list) { DEV_LIST *lp,*ap; lp = (DEV_LIST *)malloc(sizeof(DEV_LIST),M_DEVL,M_WAITOK); bcopy(dev,lp,sizeof(DEV_LIST)); /* create copied record */ ap = findspot(lp,list); /* find appropriate spot */ lp->next = ap->next; /* point to next */ if (ap->next) ap->next->prev = lp; /* point next to new */ lp->prev = ap; /* point new to current */ ap->next = lp; /* and current to new */ } /** ** Movedev ** ** Moves (dev) from its current list to an appropriate place in (list) ** (dev) may not come from the top of a list, but it may from the bottom. **/ static void movedev(DEV_LIST *dev, DEV_LIST *list) { DEV_LIST *ap; ap = findspot(dev,list); dev->prev->next = dev->next; /* remove from old list */ if (dev->next) dev->next->prev = dev->prev; dev->next = ap->next; /* insert in new list */ if (ap->next) ap->next->prev = dev; /* point next to new */ dev->prev = ap; /* point new to current */ ap->next = dev; /* and current to new */ } /** ** Initlist ** ** Initialises (*list) with the basic headings **/ static void initlist(DEV_LIST **list) { int i; for(i = 0; devclass_names[i].name[0]; i++) /* for each devtype name */ { strcpy(scratch.name,devclass_names[i].name); scratch.comment = DEV_ZOOMED; scratch.class = devclass_names[i].number; scratch.attrib = FLG_MANDATORY; /* can't be moved */ addev(&scratch,list); /* add to the list */ } } /** ** savelist ** ** Walks (list) and saves the settings of any entry marked as changed. ** ** The device's active field is set according to (active). ** ** Builds the isa_devlist used by dset to extract the changed device information. ** The code for this was taken almost verbatim from the original module. **/ static void savelist(DEV_LIST *list, int active) { struct isa_device *id_p,*id_pn; while (list) { if ((list->comment == DEV_DEVICE) && /* is a device */ (list->changed) && /* has been changed */ (list->iobase != -2) && /* is not a PCI device */ (list->device != NULL)) { /* has an isa_device structure */ setdev(list,active); /* set the device itself */ id_pn = NULL; for (id_p=isa_devlist; id_p; id_p=id_p->id_next) { /* look on the list for it */ if (id_p->id_id == list->device->id_id) { id_pn = id_p->id_next; bcopy(list->device,id_p,sizeof(struct isa_device)); + save_resource(list->device); id_p->id_next = id_pn; break; } } if (!id_pn) /* not already on the list */ { id_pn = malloc(sizeof(struct isa_device),M_DEVL,M_WAITOK); bcopy(list->device,id_pn,sizeof(struct isa_device)); + save_resource(list->device); id_pn->id_next = isa_devlist; isa_devlist = id_pn; /* park at top of list */ } } list = list->next; } } /** ** nukelist ** ** Frees all storage in use by a (list). **/ static void nukelist(DEV_LIST *list) { DEV_LIST *dp; if (!list) return; while(list->prev) /* walk to head of list */ list = list->prev; while(list) { dp = list; list = list->next; free(dp,M_DEVL); } } /** ** prevent ** ** Returns the previous entry in (list), skipping zoomed regions. Returns NULL ** if there is no previous entry. (Only possible if list->prev == NULL given the ** premise that there is always a comment at the head of the list) **/ static DEV_LIST * prevent(DEV_LIST *list) { DEV_LIST *dp; if (!list) return(NULL); dp = list->prev; /* start back one */ while(dp) { if (dp->comment == DEV_ZOOMED) /* previous section is zoomed */ return(dp); /* so skip to comment */ if (dp->comment == DEV_COMMENT) /* not zoomed */ return(list->prev); /* one back as normal */ dp = dp->prev; /* backpedal */ } return(dp); /* NULL, we can assume */ } /** ** nextent ** ** Returns the next entry in (list), skipping zoomed regions. Returns NULL ** if there is no next entry. (Possible if the current entry is last, or ** if the current entry is the last heading and it's collapsed) **/ static DEV_LIST * nextent(DEV_LIST *list) { DEV_LIST *dp; if (!list) return(NULL); if (list->comment != DEV_ZOOMED) /* no reason to skip */ return(list->next); dp = list->next; while(dp) { if (dp->comment != DEV_DEVICE) /* found another heading */ break; dp = dp->next; } return(dp); /* back we go */ } /** ** ofsent ** ** Returns the (ofs)th entry down from (list), or NULL if it doesn't exist **/ static DEV_LIST * ofsent(int ofs, DEV_LIST *list) { while (ofs-- && list) list = nextent(list); return(list); } /** ** findconflict ** ** Scans every element of (list) and sets the conflict tags appropriately ** Returns the number of conflicts found. **/ static int findconflict(DEV_LIST *list) { int count = 0; /* number of conflicts found */ DEV_LIST *dp,*sp; for (dp = list; dp; dp = dp->next) /* over the whole list */ { if (dp->comment != DEV_DEVICE) /* comments don't usually conflict */ continue; if (dp->iobase == -2) /* it's a PCI device, not interested */ continue; dp->conflicts = 0; /* assume the best */ for (sp = list; sp; sp = sp->next) /* scan the entire list for conflicts */ { if (sp->comment != DEV_DEVICE) /* likewise */ continue; if (dp->iobase == -2) /* it's a PCI device, not interested */ continue; if (sp == dp) /* always conflict with itself */ continue; if (sp->conflict_ok && dp->conflict_ok) continue; /* both allowed to conflict */ if ((dp->iobase > 0) && /* iobase conflict? */ (dp->iobase == sp->iobase)) dp->conflicts = 1; if ((dp->irq > 0) && /* irq conflict? */ (dp->irq == sp->irq)) dp->conflicts = 1; if ((dp->drq > 0) && /* drq conflict? */ (dp->drq == sp->drq)) dp->conflicts = 1; if ((dp->maddr > 0) && /* maddr conflict? */ (dp->maddr == sp->maddr)) dp->conflicts = 1; if ((dp->msize > 0) && /* msize conflict? */ (dp->msize == sp->msize)) dp->conflicts = 1; } count += dp->conflicts; /* count conflicts */ } return(count); } /** ** expandlist ** ** Unzooms all headings in (list) **/ static void expandlist(DEV_LIST *list) { while(list) { if (list->comment == DEV_COMMENT) list->comment = DEV_ZOOMED; list = list->next; } } /** ** collapselist ** ** Zooms all headings in (list) **/ static void collapselist(DEV_LIST *list) { while(list) { if (list->comment == DEV_ZOOMED) list->comment = DEV_COMMENT; list = list->next; } } /** ** Screen-manipulation stuff ** ** This is the basic screen layout : ** ** 0 5 10 15 20 25 30 35 40 45 50 55 60 67 70 75 ** |....|....|....|....|....|....|....|....|....|....|....|....|....|....|....|.... ** +--------------------------------------------------------------------------------+ ** 0 -|---Active Drivers----------------------------xx Conflicts------Dev---IRQ--Port--| ** 1 -| ........................ ....... .. 0x....| ** 2 -| ........................ ....... .. 0x....| ** 3 -| ........................ ....... .. 0x....| ** 4 -| ........................ ....... .. 0x....| ** 5 -| ........................ ....... .. 0x....| ** 6 -| ........................ ....... .. 0x....| ** 7 -| ........................ ....... .. 0x....| ** 8 -| ........................ ....... .. 0x....| ** 9 -|---Inactive Drivers--------------------------------------------Dev--------------| ** 10-| ........................ ....... | ** 11-| ........................ ....... | ** 12-| ........................ ....... | ** 13-| ........................ ....... | ** 14-| ........................ ....... | ** 15-| ........................ ....... | ** 16-| ........................ ....... | ** 17-|------------------------------------------------------UP-DOWN-------------------| ** 18-| Relevant parameters for the current device | ** 19-| | ** 20-| | ** 21-|--------------------------------------------------------------------------------| ** 22-| Help texts go here | ** 23-| | ** +--------------------------------------------------------------------------------+ ** ** Help texts ** ** On a collapsed comment : ** ** [Enter] Expand device list [z] Expand all lists ** [TAB] Change fields [Q] Save and Exit ** ** On an expanded comment : ** ** [Enter] Collapse device list [Z] Collapse all lists ** [TAB] Change fields [Q] Save and Exit ** ** On a comment with no followers ** ** ** [TAB] Change fields [Q] Save and Exit ** ** On a device in the active list ** ** [Enter] Edit device parameters [DEL] Disable device ** [TAB] Change fields [Q] Save and Exit [?] Help ** ** On a device in the inactive list ** ** [Enter] Enable device ** [TAB] Change fields [Q] Save and Exit [?] Help ** ** While editing parameters ** ** ** [TAB] Change fields [Q] Save device parameters **/ /** ** ** The base-level screen primitives : ** ** bold() - enter bold mode \E[1m (md) ** inverse() - enter inverse mode \E[7m (so) ** normal() - clear bold/inverse mode \E[m (se) ** clear() - clear the screen \E[H\E[J (ce) ** move(x,y) - move the cursor to x,y \E[y;xH: (cm) **/ static void bold(void) { printf("\033[1m"); } static void inverse(void) { printf("\033[7m"); } static void normal(void) { printf("\033[m"); } static void clear(void) { normal(); printf("\033[H\033[J"); } static void move(int x, int y) { printf("\033[%d;%dH",y+1,x+1); } /** ** ** High-level screen primitives : ** ** putxyl(x,y,str,len) - put (len) bytes of (str) at (x,y), supports embedded formatting ** putxy(x,y,str) - put (str) at (x,y), supports embedded formatting ** erase(x,y,w,h) - clear the box (x,y,w,h) ** txtbox(x,y,w,y,str) - put (str) in a region at (x,y,w,h) ** putmsg(str) - put (str) in the message area ** puthelp(str) - put (str) in the upper helpline ** pad(str,len) - pad (str) to (len) with spaces ** drawline(row,detail,list,inverse,*dhelp) ** - draws a line for (*list) at (row) onscreen. If (detail) is ** nonzero, include port, IRQ and maddr, if (inverse) is nonzero, ** draw the line in inverse video, and display (*dhelp) on the ** helpline. ** drawlist(row,num,detail,list) ** - draw (num) entries from (list) at (row) onscreen, passile (detail) ** through to drawline(). ** showparams(dev) - displays the relevant parameters for (dev) below the lists onscreen. ** yesno(str) - displays (str) in the message area, and returns nonzero on 'y' or 'Y' ** redraw(); - Redraws the entire screen layout, including the ** - two list panels. **/ /** ** putxy ** writes (str) at x,y onscreen ** putxyl ** writes up to (len) of (str) at x,y onscreen. ** ** Supports embedded formatting : ** !i - inverse mode. ** !b - bold mode. ** !n - normal mode. **/ static void putxyl(int x, int y, char *str, int len) { move(x,y); normal(); while((*str) && (len--)) { if (*str == '!') /* format escape? */ { switch(*(str+1)) /* depending on the next character */ { case 'i': inverse(); str +=2; /* skip formatting */ len++; /* doesn't count for length */ break; case 'b': bold(); str +=2; /* skip formatting */ len++; /* doesn't count for length */ break; case 'n': normal(); str +=2; /* skip formatting */ len++; /* doesn't count for length */ break; default: putchar(*str++); /* not an escape */ } }else{ putchar(*str++); /* emit the character */ } } } #define putxy(x,y,str) putxyl(x,y,str,-1) /** ** erase ** ** Erases the region (x,y,w,h) **/ static void erase(int x, int y, int w, int h) { int i; normal(); for (i = 0; i < h; i++) putxyl(x,y++,spaces,w); } /** ** txtbox ** ** Writes (str) into the region (x,y,w,h), supports embedded formatting using ** putxy. Lines are not wrapped, newlines must be forced with \n. **/ static void txtbox(int x, int y, int w, int h, char *str) { int i = 0; h--; while((str[i]) && h) { if (str[i] == '\n') /* newline */ { putxyl(x,y,str,(i= len) /* no padding needed */ return; while(i < len) /* pad */ str[i++] = ' '; str[i] = '\0'; } /** ** drawline ** ** Displays entry (ofs) of (list) in region at (row) onscreen, optionally displaying ** the port and IRQ fields if (detail) is nonzero. If (inverse), in inverse video. ** ** The text (dhelp) is displayed if the item is a normal device, otherwise ** help is shown for normal or zoomed comments **/ static void drawline(int row, int detail, DEV_LIST *list, int inverse, char *dhelp) { char lbuf[90],nb[70],db[20],ib[16],pb[16]; if (list->comment == DEV_DEVICE) { nb[0] = ' '; strncpy(nb+1,list->name,57); }else{ strncpy(nb,list->name,58); if ((list->comment == DEV_ZOOMED) && (list->next)) if (list->next->comment == DEV_DEVICE) /* only mention if there's something hidden */ strcat(nb," (Collapsed)"); } nb[58] = '\0'; pad(nb,60); if (list->conflicts) /* device in conflict? */ if (inverse) { strcpy(nb+54," !nCONF!i "); /* tag conflict, careful of length */ }else{ strcpy(nb+54," !iCONF!n "); /* tag conflict, careful of length */ } if (list->comment == DEV_DEVICE) { sprintf(db,"%s%d",list->dev,list->unit); pad(db,8); }else{ strcpy(db," "); } if ((list->irq > 0) && detail && (list->comment == DEV_DEVICE)) { sprintf(ib," %d",list->irq); pad(ib,4); }else{ strcpy(ib," "); } if ((list->iobase > 0) && detail && (list->comment == DEV_DEVICE)) { sprintf(pb,"0x%x",list->iobase); pad(pb,7); }else{ strcpy(pb," "); } sprintf(lbuf," %s%s%s%s%s",inverse?"!i":"",nb,db,ib,pb); putxyl(0,row,lbuf,80); if (dhelp) { switch(list->comment) { case DEV_DEVICE: /* ordinary device */ puthelp(dhelp); break; case DEV_COMMENT: puthelp(""); if (list->next) if (list->next->comment == DEV_DEVICE) puthelp(" [!bEnter!n] Collapse device list [!bC!n] Collapse all lists"); break; case DEV_ZOOMED: puthelp(""); if (list->next) if (list->next->comment == DEV_DEVICE) puthelp(" [!bEnter!n] Expand device list [!bX!n] Expand all lists"); break; default: puthelp(" WARNING: This list entry corrupted!"); break; } } move(0,row); /* put the cursor somewhere relevant */ } /** ** drawlist ** ** Displays (num) lines of the contents of (list) at (row), optionally displaying the ** port and IRQ fields as well if (detail) is nonzero ** ** printf in the kernel is essentially useless, so we do most of the hard work ourselves here. **/ static void drawlist(int row, int num, int detail, DEV_LIST *list) { int ofs; for(ofs = 0; ofs < num; ofs++) { if (list) { drawline(row+ofs,detail,list,0,NULL); /* NULL -> don't draw empty help string */ list = nextent(list); /* move down visible list */ }else{ erase(0,row+ofs,80,1); } } } /** ** redrawactive ** ** Redraws the active list **/ static void redrawactive(void) { char cbuf[16]; if (conflicts) { sprintf(cbuf,"!i%d conflict%s-",conflicts,(conflicts>1)?"s":""); putxy(45,0,cbuf); }else{ putxyl(45,0,lines,16); } drawlist(1,8,1,alist); /* draw device lists */ } /** ** redrawinactive ** ** Redraws the inactive list **/ static void redrawinactive(void) { drawlist(10,7,0,ilist); /* draw device lists */ } /** ** redraw ** ** Clear the screen and redraw the entire layout **/ static void redraw(void) { clear(); putxy(0,0,lines); putxy(3,0,"!bActive!n-!bDrivers"); putxy(63,0,"!bDev!n---!bIRQ!n--!bPort"); putxy(0,9,lines); putxy(3,9,"!bInactive!n-!bDrivers"); putxy(63,9,"!bDev"); putxy(0,17,lines); putxy(0,21,lines); masterhelp(" [!bTAB!n] Change fields [!bQ!n] Save and Exit [!b?!n] Help"); redrawactive(); redrawinactive(); } /** ** yesnocancel ** ** Put (str) in the message area, and return 1 if the user hits 'y' or 'Y', ** 2 if they hit 'c' or 'C', or 0 for 'n' or 'N'. **/ static int yesnocancel(char *str) { putmsg(str); for(;;) switch(getchar()) { case -1: case 'n': case 'N': return(0); case 'y': case 'Y': return(1); case 'c': case 'C': return(2); } } /** ** showparams ** ** Show device parameters in the region below the lists ** ** 0 5 10 15 20 25 30 35 40 45 50 55 60 67 70 75 ** |....|....|....|....|....|....|....|....|....|....|....|....|....|....|....|.... ** +--------------------------------------------------------------------------------+ ** 17-|--------------------------------------------------------------------------------| ** 18-| Port address : 0x0000 Memory address : 0x00000 Conflict allowed | ** 19-| IRQ number : 00 Memory size : 0x0000 | ** 20-| Flags : 0x0000 DRQ number : 00 | ** 21-|--------------------------------------------------------------------------------| **/ static void showparams(DEV_LIST *dev) { char buf[80]; erase(0,18,80,3); /* clear area */ if (!dev) return; if (dev->comment != DEV_DEVICE) return; if (dev->iobase > 0) { sprintf(buf,"Port address : 0x%x",dev->iobase); putxy(1,18,buf); } else { if (dev->iobase == -2) /* a PCI device */ putmsg(" PCI devices are displayed for informational purposes only, and\n" " cannot be disabled or configured here."); } if (dev->irq > 0) { sprintf(buf,"IRQ number : %d",dev->irq); putxy(1,19,buf); } sprintf(buf,"Flags : 0x%x",dev->flags); putxy(1,20,buf); if (dev->maddr > 0) { sprintf(buf,"Memory address : 0x%x",dev->maddr); putxy(26,18,buf); } if (dev->msize > 0) { sprintf(buf,"Memory size : 0x%x",dev->msize); putxy(26,19,buf); } if (dev->drq > 0) { sprintf(buf,"DRQ number : %d",dev->drq); putxy(26,20,buf); } if (dev->conflict_ok) putxy(54,18,"Conflict allowed"); } /** ** Editing functions for device parameters ** ** editval(x,y,width,hex,min,max,val) - Edit (*val) in a field (width) wide at (x,y) ** onscreen. Refuse values outsise (min) and (max). ** editparams(dev) - Edit the parameters for (dev) **/ #define VetRet(code) \ { \ if ((i >= min) && (i <= max)) /* legit? */ \ { \ *val = i; \ sprintf(buf,hex?"0x%x":"%d",i); \ putxy(hex?x-2:x,y,buf); \ return(code); /* all done and exit */ \ } \ i = *val; /* restore original value */ \ delta = 1; /* restore other stuff */ \ } /** ** editval ** ** Edit (*val) at (x,y) in (hex)?hex:decimal mode, allowing values between (min) and (max) ** in a field (width) wide. (Allow one space) ** If (ro) is set, we're in "readonly" mode, so disallow edits. ** ** Return KEY_TAB on \t, KEY_EXIT on 'q' **/ static int editval(int x, int y, int width, int hex, int min, int max, int *val, int ro) { int i = *val; /* work with copy of the value */ char buf[2+11+1],tc[11+1]; /* display buffer, text copy */ int xp = 0; /* cursor offset into text copy */ int delta = 1; /* force redraw first time in */ int c; int extended = 0; /* stage counter for extended key sequences */ if (hex) /* we presume there's a leading 0x onscreen */ putxy(x-2,y,"!i0x"); /* coz there sure is now */ for (;;) { if (delta) /* only update if necessary */ { sprintf(tc,hex?"%x":"%d",i); /* make a text copy of the value */ sprintf(buf,"!i%s",tc); /* format for printing */ erase(x,y,width,1); /* clear the area */ putxy(x,y,buf); /* write */ xp = strlen(tc); /* cursor always at end */ move(x+xp,y); /* position the cursor */ } c = getchar(); switch(extended) /* escape handling */ { case 0: if (c == 0x1b) /* esc? */ { extended = 1; /* flag and spin */ continue; } extended = 0; break; /* nope, drop through */ case 1: /* there was an escape prefix */ if (c == '[' || c == 'O') /* second character in sequence */ { extended = 2; continue; } if (c == 0x1b) return(KEY_EXIT); /* double esc exits */ extended = 0; break; /* nup, not a sequence. */ case 2: extended = 0; switch(c) /* looks like the real McCoy */ { case 'A': VetRet(KEY_UP); /* leave if OK */ continue; case 'B': VetRet(KEY_DOWN); /* leave if OK */ continue; case 'C': VetRet(KEY_RIGHT); /* leave if OK */ continue; case 'D': VetRet(KEY_LEFT); /* leave if OK */ continue; default: continue; } } switch(c) { case '\t': /* trying to tab off */ VetRet(KEY_TAB); /* verify and maybe return */ break; case -1: case 'q': case 'Q': VetRet(KEY_EXIT); break; case '\b': case '\177': /* BS or DEL */ if (ro) /* readonly? */ { puthelp(" !iThis value cannot be edited (Press ESC)"); while(getchar() != 0x1b); /* wait for key */ return(KEY_NULL); /* spin */ } if (xp) /* still something left to delete */ { i = (hex ? i/0x10u : i/10); /* strip last digit */ delta = 1; /* force update */ } break; case 588: VetRet(KEY_UP); break; case '\r': case '\n': case 596: VetRet(KEY_DOWN); break; case 591: VetRet(KEY_LEFT); break; case 593: VetRet(KEY_RIGHT); break; default: if (ro) /* readonly? */ { puthelp(" !iThis value cannot be edited (Press ESC)"); while(getchar() != 0x1b); /* wait for key */ return(KEY_NULL); /* spin */ } if (xp >= width) /* no room for more characters anyway */ break; if (hex) { if ((c >= '0') && (c <= '9')) { i = i*0x10 + (c-'0'); /* update value */ delta = 1; break; } if ((c >= 'a') && (c <= 'f')) { i = i*0x10 + (c-'a'+0xa); delta = 1; break; } if ((c >= 'A') && (c <= 'F')) { i = i*0x10 + (c-'A'+0xa); delta = 1; break; } }else{ if ((c >= '0') && (c <= '9')) { i = i*10 + (c-'0'); /* update value */ delta = 1; /* force redraw */ break; } } break; } } } /** ** editparams ** ** Edit the parameters for (dev) ** ** Note that it's _always_ possible to edit the flags, otherwise it might be ** possible for this to spin in an endless loop... ** 0 5 10 15 20 25 30 35 40 45 50 55 60 67 70 75 ** |....|....|....|....|....|....|....|....|....|....|....|....|....|....|....|.... ** +--------------------------------------------------------------------------------+ ** 17-|--------------------------------------------------------------------------------| ** 18-| Port address : 0x0000 Memory address : 0x00000 Conflict allowed | ** 19-| IRQ number : 00 Memory size : 0x0000 | ** 20-| Flags : 0x0000 DRQ number : 00 | ** 21-|--------------------------------------------------------------------------------| ** ** The "intelligence" in this function that hops around based on the directional ** returns from editval isn't very smart, and depends on the layout above. **/ static void editparams(DEV_LIST *dev) { int ret; char buf[16]; /* needs to fit the device name */ putxy(2,17,"!bParameters!n-!bfor!n-!bdevice!n-"); sprintf(buf,"!b%s",dev->dev); putxy(24,17,buf); erase(1,22,80,1); for (;;) { ep_iobase: if (dev->iobase > 0) { puthelp(" IO Port address (Hexadecimal, 0x1-0xffff)"); ret = editval(18,18,5,1,0x1,0xffff,&(dev->iobase),(dev->attrib & FLG_FIXIOBASE)); switch(ret) { case KEY_EXIT: goto ep_exit; case KEY_RIGHT: if (dev->maddr > 0) goto ep_maddr; break; case KEY_TAB: case KEY_DOWN: goto ep_irq; } goto ep_iobase; } ep_irq: if (dev->irq > 0) { puthelp(" Interrupt number (Decimal, 1-15)"); ret = editval(16,19,3,0,1,15,&(dev->irq),(dev->attrib & FLG_FIXIRQ)); switch(ret) { case KEY_EXIT: goto ep_exit; case KEY_RIGHT: if (dev->msize > 0) goto ep_msize; break; case KEY_UP: if (dev->iobase > 0) goto ep_iobase; break; case KEY_TAB: case KEY_DOWN: goto ep_flags; } goto ep_irq; } ep_flags: puthelp(" Device-specific flag values."); ret = editval(18,20,8,1,INT_MIN,INT_MAX,&(dev->flags),0); switch(ret) { case KEY_EXIT: goto ep_exit; case KEY_RIGHT: if (dev->drq > 0) goto ep_drq; break; case KEY_UP: if (dev->irq > 0) goto ep_irq; if (dev->iobase > 0) goto ep_iobase; break; case KEY_DOWN: if (dev->maddr > 0) goto ep_maddr; if (dev->msize > 0) goto ep_msize; if (dev->drq > 0) goto ep_drq; break; case KEY_TAB: goto ep_maddr; } goto ep_flags; ep_maddr: if (dev->maddr > 0) { puthelp(" Device memory start address (Hexadecimal, 0x1-0xfffff)"); ret = editval(45,18,6,1,0x1,0xfffff,&(dev->maddr),(dev->attrib & FLG_FIXMADDR)); switch(ret) { case KEY_EXIT: goto ep_exit; case KEY_LEFT: if (dev->iobase > 0) goto ep_iobase; break; case KEY_UP: goto ep_flags; case KEY_DOWN: if (dev->msize > 0) goto ep_msize; if (dev->drq > 0) goto ep_drq; break; case KEY_TAB: goto ep_msize; } goto ep_maddr; } ep_msize: if (dev->msize > 0) { puthelp(" Device memory size (Hexadecimal, 0x1-0x10000)"); ret = editval(45,19,5,1,0x1,0x10000,&(dev->msize),(dev->attrib & FLG_FIXMSIZE)); switch(ret) { case KEY_EXIT: goto ep_exit; case KEY_LEFT: if (dev->irq > 0) goto ep_irq; break; case KEY_UP: if (dev->maddr > 0) goto ep_maddr; goto ep_flags; case KEY_DOWN: if (dev->drq > 0) goto ep_drq; break; case KEY_TAB: goto ep_drq; } goto ep_msize; } ep_drq: if (dev->drq > 0) { puthelp(" Device DMA request number (Decimal, 1-7)"); ret = editval(43,20,2,0,1,7,&(dev->drq),(dev->attrib & FLG_FIXDRQ)); switch(ret) { case KEY_EXIT: goto ep_exit; case KEY_LEFT: goto ep_flags; case KEY_UP: if (dev->msize > 0) goto ep_msize; if (dev->maddr > 0) goto ep_maddr; goto ep_flags; case KEY_TAB: goto ep_iobase; } goto ep_drq; } } ep_exit: dev->changed = 1; /* mark as changed */ } static char *helptext[] = { " Using the UserConfig kernel settings editor", " -------------------------------------------", "", "VISUAL MODE:", "", "- - Layout -", "", "The screen displays a list of available drivers, divided into two", "scrolling lists: Active Drivers, and Inactive Drivers. Each list is", "by default collapsed and can be expanded to show all the drivers", "available in each category. The parameters for the currently selected", "driver are shown at the bottom of the screen.", "", "- - Moving around -", "", "To move in the current list, use the UP and DOWN cursor keys to select", "an item (the selected item will be highlighted). If the item is a", "category name, you may alternatively expand or collapse the list of", "drivers for that category by pressing [!bENTER!n]. Once the category is", "expanded, you can select each driver in the same manner and either:", "", " - change its parameters using [!bENTER!n]", " - move it to the Inactive list using [!bDEL!n]", "", "Use the [!bTAB!n] key to toggle between the Active and Inactive list; if", "you need to move a driver from the Inactive list back to the Active", "one, select it in the Inactive list, using [!bTAB!n] to change lists if", "necessary, and press [!bENTER!n] -- the device will be moved back to", "its place in the Active list.", "", "- - Altering the list/parameters -", "", "Any drivers for devices not installed in your system should be moved", "to the Inactive list, until there are no remaining parameter conflicts", "between the drivers, as indicated at the top.", "", "Once the list of Active drivers only contains entries for the devices", "present in your system, you can set their parameters (Interrupt, DMA", "channel, I/O addresses). To do this, select the driver and press", "[!bENTER!n]: it is now possible to edit the settings the settings at the", "bottom of the screen. Use [!bTAB!n] to change fields, and when you are", "finished, use [!bQ!n] to return to the list.", "", "- - Saving changes -", "", "When all settings seem correct, and you wish to proceed with the", "kernel device probing and boot, press [!bQ!n] -- you will be asked to", "confirm your choice.", "", NULL }; /** ** helpscreen ** ** Displays help text onscreen for people that are confused, using a simple ** pager. **/ static void helpscreen(void) { int topline = 0; /* where we are in the text */ int c, delta = 1; char prompt[80]; for (;;) /* loop until user quits */ { int line = 0; /* display help text */ if (delta) { clear(); /* remove everything else */ for (line = topline; (line < (topline + 24)) && (helptext[line]); line++) putxy(0,line-topline,helptext[line]); delta = 0; } /* prompt */ sprintf(prompt,"!i --%s-- [U]p [D]own [Q]uit !n",helptext[line] ? "MORE" : "END"); putxy(0,24,prompt); c = getchar(); /* so what do they say? */ switch (c) { case 'u': case 'U': case 'b': case 'B': /* wired into 'more' users' fingers */ if (topline > 0) /* room to go up? */ { topline -= 24; if (topline < 0) /* don't go too far */ topline = 0; delta = 1; } break; case 'd': case 'D': case ' ': /* expected by most people */ if (helptext[line]) /* maybe more below? */ { topline += 24; delta = 1; } break; case 'q': case 'Q': redraw(); /* restore the screen */ return; } } } /** ** High-level control functions **/ /** ** dolist ** ** Handle user movement within (*list) in the region starting at (row) onscreen with ** (num) lines, starting at (*ofs) offset from row onscreen. ** Pass (detail) on to drawing routines. ** ** If the user hits a key other than a cursor key, maybe return a code. ** ** (*list) points to the device at the top line in the region, (*ofs) is the ** position of the highlight within the region. All routines below ** this take only a device and an absolute row : use ofsent() to find the ** device, and add (*ofs) to (row) to find the absolute row. **/ static int dolist(int row, int num, int detail, int *ofs, DEV_LIST **list, char *dhelp) { int extended = 0; int c; DEV_LIST *lp; int delta = 1; for(;;) { if (delta) { showparams(ofsent(*ofs,*list)); /* show device parameters */ drawline(row+*ofs,detail,ofsent(*ofs,*list),1,dhelp); /* highlight current line */ delta = 0; } c = getchar(); /* get a character */ if ((extended == 2) || (c==588) || (c==596)) /* console gives "alternative" codes */ { extended = 0; /* no longer */ switch(c) { case 588: /* syscons' idea of 'up' */ case 'A': /* up */ if (*ofs) /* just a move onscreen */ { drawline(row+*ofs,detail,ofsent(*ofs,*list),0,dhelp);/* unhighlight current line */ (*ofs)--; /* move up */ }else{ lp = prevent(*list); /* can we go up? */ if (!lp) /* no */ break; *list = lp; /* yes, move up list */ drawlist(row,num,detail,*list); } delta = 1; break; case 596: /* dooby-do */ case 'B': /* down */ lp = ofsent(*ofs,*list); /* get current item */ if (!nextent(lp)) break; /* nothing more to move to */ drawline(row+*ofs,detail,ofsent(*ofs,*list),0,dhelp); /* unhighlight current line */ if (*ofs < (num-1)) /* room to move onscreen? */ { (*ofs)++; }else{ *list = nextent(*list); /* scroll region down */ drawlist(row,num,detail,*list); } delta = 1; break; } }else{ switch(c) { case '\033': extended=1; break; case '[': /* cheat : always preceeds cursor move */ case 'O': /* ANSI application key mode */ if (extended==1) extended=2; else extended=0; break; case 'Q': case 'q': return(KEY_EXIT); /* user requests exit */ case '\r': case '\n': return(KEY_DO); /* "do" something */ case '\b': case '\177': case 599: return(KEY_DEL); /* "delete" response */ case 'X': case 'x': return(KEY_UNZOOM); /* expand everything */ case 'C': case 'c': return(KEY_ZOOM); /* collapse everything */ case '\t': drawline(row+*ofs,detail,ofsent(*ofs,*list),0,dhelp); /* unhighlight current line */ return(KEY_TAB); /* "move" response */ case '\014': /* ^L, redraw */ return(KEY_REDRAW); case '?': /* helptext */ return(KEY_HELP); } } } } /** ** visuserconfig ** ** Do the fullscreen config thang **/ static int visuserconfig(void) { int actofs = 0, inactofs = 0, mode = 0, ret = -1, i; DEV_LIST *dp; initlist(&active); initlist(&inactive); alist = active; ilist = inactive; getdevs(); conflicts = findconflict(active); /* find conflicts in the active list only */ redraw(); for(;;) { switch(mode) { case 0: /* active devices */ ret = dolist(1,8,1,&actofs,&alist, " [!bEnter!n] Edit device parameters [!bDEL!n] Disable device"); switch(ret) { case KEY_TAB: mode = 1; /* swap lists */ break; case KEY_REDRAW: redraw(); break; case KEY_ZOOM: alist = active; actofs = 0; expandlist(active); redrawactive(); break; case KEY_UNZOOM: alist = active; actofs = 0; collapselist(active); redrawactive(); break; case KEY_DEL: dp = ofsent(actofs,alist); /* get current device */ if (dp) /* paranoia... */ { if (dp->attrib & FLG_MANDATORY) /* can't be deleted */ break; if (dp == alist) /* moving top item on list? */ { if (dp->next) { alist = dp->next; /* point list to non-moving item */ }else{ alist = dp->prev; /* end of list, go back instead */ } }else{ if (!dp->next) /* moving last item on list? */ actofs--; } dp->conflicts = 0; /* no conflicts on the inactive list */ movedev(dp,inactive); /* shift to inactive list */ conflicts = findconflict(active); /* update conflict tags */ dp->changed = 1; redrawactive(); /* redraw */ redrawinactive(); } break; case KEY_DO: /* edit device parameters */ dp = ofsent(actofs,alist); /* get current device */ if (dp) /* paranoia... */ { if (dp->comment == DEV_DEVICE) /* can't edit comments, zoom? */ { if (dp->iobase != -2) /* can't edit PCI devices */ { masterhelp(" [!bTAB!n] Change fields [!bQ!n] Save device parameters"); editparams(dp); masterhelp(" [!bTAB!n] Change fields [!bQ!n] Save and Exit [!b?!n] Help"); putxy(0,17,lines); conflicts = findconflict(active); /* update conflict tags */ } }else{ /* DO on comment = zoom */ switch(dp->comment) /* Depends on current state */ { case DEV_COMMENT: /* not currently zoomed */ dp->comment = DEV_ZOOMED; break; case DEV_ZOOMED: dp->comment = DEV_COMMENT; break; } } redrawactive(); } break; } break; case 1: /* inactive devices */ ret = dolist(10,7,0,&inactofs,&ilist, " [!bEnter!n] Enable device "); switch(ret) { case KEY_TAB: mode = 0; break; case KEY_REDRAW: redraw(); break; case KEY_ZOOM: ilist = inactive; inactofs = 0; expandlist(inactive); redrawinactive(); break; case KEY_UNZOOM: ilist = inactive; inactofs = 0; collapselist(inactive); redrawinactive(); break; case KEY_DO: dp = ofsent(inactofs,ilist); /* get current device */ if (dp) /* paranoia... */ { if (dp->comment == DEV_DEVICE) /* can't move comments, zoom? */ { if (dp == ilist) /* moving top of list? */ { if (dp->next) { ilist = dp->next; /* point list to non-moving item */ }else{ ilist = dp->prev; /* can't go down, go up instead */ } }else{ if (!dp->next) /* last entry on list? */ inactofs--; /* shift cursor up one */ } movedev(dp,active); /* shift to active list */ conflicts = findconflict(active); /* update conflict tags */ dp->changed = 1; alist = dp; /* put at top and current */ actofs = 0; while(dp->comment == DEV_DEVICE) dp = dp->prev; /* forcibly unzoom section */ dp ->comment = DEV_COMMENT; mode = 0; /* and swap modes to follow it */ }else{ /* DO on comment = zoom */ switch(dp->comment) /* Depends on current state */ { case DEV_COMMENT: /* not currently zoomed */ dp->comment = DEV_ZOOMED; break; case DEV_ZOOMED: dp->comment = DEV_COMMENT; break; } } redrawactive(); /* redraw */ redrawinactive(); } break; default: /* nothing else relevant here */ break; } break; default: mode = 0; /* shouldn't happen... */ } /* handle returns that are the same for both modes */ switch (ret) { case KEY_HELP: helpscreen(); break; case KEY_EXIT: i = yesnocancel(" Save these parameters before exiting? ([!bY!n]es/[!bN!n]o/[!bC!n]ancel) "); switch(i) { case 2: /* cancel */ redraw(); break; case 1: /* save and exit */ savelist(active,1); savelist(inactive,0); case 0: /* exit */ nukelist(active); /* clean up after ourselves */ nukelist(inactive); normal(); clear(); return(1); } break; } } } #endif /* VISUAL_USERCONFIG */ /* * Copyright (c) 1991 Regents of the University of California. * All rights reserved. * Copyright (c) 1994 Jordan K. Hubbard * All rights reserved. * Copyright (c) 1994 David Greenman * All rights reserved. * * Many additional changes by Bruce Evans * * This code is derived from software contributed by the * University of California Berkeley, Jordan K. Hubbard, * David Greenman and Bruce Evans. * * 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. * - * $Id: userconfig.c,v 1.132 1999/02/21 16:33:51 n_hibma Exp $ + * $Id: userconfig.c,v 1.133 1999/04/06 17:08:30 wpaul Exp $ */ #include "scbus.h" #define PARM_DEVSPEC 0x1 #define PARM_INT 0x2 #define PARM_ADDR 0x3 #define PARM_STRING 0x4 typedef struct _cmdparm { int type; union { struct isa_device *dparm; int iparm; union { void *aparm; const char *sparm; } u; } parm; } CmdParm; typedef int (*CmdFunc)(CmdParm *); typedef struct _cmd { char *name; CmdFunc handler; CmdParm *parms; } Cmd; #if 0 static void lsscsi(void); static int list_scsi(CmdParm *); #endif static int lsdevtab(struct isa_device *); static struct isa_device *find_device(char *, int); static struct isa_device *search_devtable(struct isa_device *, char *, int); static void cngets(char *, int); static Cmd *parse_cmd(char *); static int parse_args(const char *, CmdParm *); static unsigned long strtoul(const char *, const char **, int); static int save_dev(struct isa_device *); static int list_devices(CmdParm *); static int set_device_ioaddr(CmdParm *); static int set_device_irq(CmdParm *); static int set_device_drq(CmdParm *); static int set_device_iosize(CmdParm *); static int set_device_mem(CmdParm *); static int set_device_flags(CmdParm *); static int set_device_enable(CmdParm *); static int set_device_disable(CmdParm *); static int quitfunc(CmdParm *); static int helpfunc(CmdParm *); #if defined(INTRO_USERCONFIG) static int introfunc(CmdParm *); #endif #if NPNP > 0 static int lspnp(void); static int set_pnp_parms(CmdParm *); #endif static int lineno; #include "eisa.h" #if NEISA > 0 #include static int set_num_eisa_slots(CmdParm *); #endif /* NEISA > 0 */ static CmdParm addr_parms[] = { { PARM_DEVSPEC, {} }, { PARM_ADDR, {} }, { -1, {} }, }; static CmdParm int_parms[] = { { PARM_DEVSPEC, {} }, { PARM_INT, {} }, { -1, {} }, }; static CmdParm dev_parms[] = { { PARM_DEVSPEC, {} }, { -1, {} }, }; #if NPNP > 0 static CmdParm string_arg[] = { { PARM_STRING, {} }, { -1, {} }, }; #endif #if NEISA > 0 static CmdParm int_arg[] = { { PARM_INT, {} }, { -1, {} }, }; #endif /* NEISA > 0 */ static Cmd CmdList[] = { { "?", helpfunc, NULL }, /* ? (help) */ { "di", set_device_disable, dev_parms }, /* disable dev */ { "dr", set_device_drq, int_parms }, /* drq dev # */ #if NEISA > 0 { "ei", set_num_eisa_slots, int_arg }, /* # EISA slots */ #endif /* NEISA > 0 */ { "en", set_device_enable, dev_parms }, /* enable dev */ { "ex", quitfunc, NULL }, /* exit (quit) */ { "f", set_device_flags, int_parms }, /* flags dev mask */ { "h", helpfunc, NULL }, /* help */ #if defined(INTRO_USERCONFIG) { "intro", introfunc, NULL }, /* intro screen */ #endif { "iom", set_device_mem, addr_parms }, /* iomem dev addr */ { "ios", set_device_iosize, int_parms }, /* iosize dev size */ { "ir", set_device_irq, int_parms }, /* irq dev # */ { "l", list_devices, NULL }, /* ls, list */ #if NPNP > 0 { "pn", set_pnp_parms, string_arg }, /* pnp ... */ #endif { "po", set_device_ioaddr, int_parms }, /* port dev addr */ { "res", (CmdFunc)cpu_reset, NULL }, /* reset CPU */ { "q", quitfunc, NULL }, /* quit */ #if 0 { "s", list_scsi, NULL }, /* scsi */ #endif #ifdef VISUAL_USERCONFIG { "v", (CmdFunc)visuserconfig, NULL }, /* visual mode */ #endif { NULL, NULL, NULL }, }; void userconfig(void) { static char banner = 1; char input[80]; int rval; Cmd *cmd; + load_devtab(); init_config_script(); while (1) { /* Only display signon banner if we are about to go interactive */ if (!has_config_script()) { if (!(boothowto & RB_CONFIG)) #ifdef INTRO_USERCONFIG banner = 0; #else return; #endif if (banner) { banner = 0; printf("FreeBSD Kernel Configuration Utility - Version 1.2\n" " Type \"help\" for help" #ifdef VISUAL_USERCONFIG " or \"visual\" to go to the visual\n" " configuration interface (requires MGA/VGA display or\n" " serial terminal capable of displaying ANSI graphics)" #endif ".\n"); } } printf("config> "); cngets(input, 80); if (input[0] == '\0') continue; cmd = parse_cmd(input); if (!cmd) { printf("Invalid command or syntax. Type `?' for help.\n"); continue; } rval = (*cmd->handler)(cmd->parms); - if (rval) + if (rval) { + free_devtab(); return; + } } } static Cmd * parse_cmd(char *cmd) { Cmd *cp; for (cp = CmdList; cp->name; cp++) { int len = strlen(cp->name); if (!strncmp(cp->name, cmd, len)) { while (*cmd && *cmd != ' ' && *cmd != '\t') ++cmd; if (parse_args(cmd, cp->parms)) return NULL; else return cp; } } return NULL; } static int parse_args(const char *cmd, CmdParm *parms) { while (1) { const char *ptr; if (*cmd == ' ' || *cmd == '\t') { ++cmd; continue; } if (parms == NULL || parms->type == -1) { if (*cmd == '\0') return 0; printf("Extra arg(s): %s\n", cmd); return 1; } if (parms->type == PARM_DEVSPEC) { int i = 0; char devname[64]; int unit = 0; while (*cmd && !(*cmd == ' ' || *cmd == '\t' || (*cmd >= '0' && *cmd <= '9'))) devname[i++] = *(cmd++); devname[i] = '\0'; if (*cmd >= '0' && *cmd <= '9') { unit = strtoul(cmd, &ptr, 10); if (cmd == ptr) { printf("Invalid device number\n"); /* XXX should print invalid token here and elsewhere. */ return 1; } /* XXX else should require end of token. */ cmd = ptr; } if ((parms->parm.dparm = find_device(devname, unit)) == NULL) { printf("No such device: %s%d\n", devname, unit); return 1; } ++parms; continue; } if (parms->type == PARM_INT) { parms->parm.iparm = strtoul(cmd, &ptr, 0); if (cmd == ptr) { printf("Invalid numeric argument\n"); return 1; } cmd = ptr; ++parms; continue; } if (parms->type == PARM_ADDR) { parms->parm.u.aparm = (void *)(uintptr_t)strtoul(cmd, &ptr, 0); if (cmd == ptr) { printf("Invalid address argument\n"); return 1; } cmd = ptr; ++parms; continue; } if (parms->type == PARM_STRING) { parms->parm.u.sparm = cmd; return 0; } } return 0; } static int list_devices(CmdParm *parms) { lineno = 0; - if (lsdevtab(&isa_devtab_bio[0])) return 0; - if (lsdevtab(&isa_devtab_tty[0])) return 0; - if (lsdevtab(&isa_devtab_net[0])) return 0; - if (lsdevtab(&isa_devtab_cam[0])) return 0; - if (lsdevtab(&isa_devtab_null[0])) return 0; + if (lsdevtab(isa_devtab)) return 0; #if NPNP > 0 if (lspnp()) return 0; #endif #if NEISA > 0 printf("\nNumber of EISA slots to probe: %d\n", num_eisa_slots); #endif /* NEISA > 0 */ return 0; } static int set_device_ioaddr(CmdParm *parms) { parms[0].parm.dparm->id_iobase = parms[1].parm.iparm; save_dev(parms[0].parm.dparm); return 0; } static int set_device_irq(CmdParm *parms) { unsigned irq; irq = parms[1].parm.iparm; if (irq == 2) { printf("Warning: Remapping IRQ 2 to IRQ 9 - see config(8)\n"); irq = 9; } else if (irq != -1 && irq > 15) { printf("An IRQ > 15 would be invalid.\n"); return 0; } parms[0].parm.dparm->id_irq = (irq < 16 ? 1 << irq : 0); save_dev(parms[0].parm.dparm); return 0; } static int set_device_drq(CmdParm *parms) { unsigned drq; /* * The bounds checking is just to ensure that the value can be printed * in 5 characters. 32768 gets converted to -32768 and doesn't fit. */ drq = parms[1].parm.iparm; parms[0].parm.dparm->id_drq = (drq < 32768 ? drq : -1); save_dev(parms[0].parm.dparm); return 0; } static int set_device_iosize(CmdParm *parms) { parms[0].parm.dparm->id_msize = parms[1].parm.iparm; save_dev(parms[0].parm.dparm); return 0; } static int set_device_mem(CmdParm *parms) { parms[0].parm.dparm->id_maddr = parms[1].parm.u.aparm; save_dev(parms[0].parm.dparm); return 0; } static int set_device_flags(CmdParm *parms) { parms[0].parm.dparm->id_flags = parms[1].parm.iparm; save_dev(parms[0].parm.dparm); return 0; } static int set_device_enable(CmdParm *parms) { parms[0].parm.dparm->id_enabled = TRUE; save_dev(parms[0].parm.dparm); return 0; } static int set_device_disable(CmdParm *parms) { parms[0].parm.dparm->id_enabled = FALSE; save_dev(parms[0].parm.dparm); return 0; } #if NPNP > 0 static int sysctl_machdep_uc_pnplist SYSCTL_HANDLER_ARGS { int error=0; if(!req->oldptr) { /* Only sizing */ return(SYSCTL_OUT(req,0,sizeof(struct pnp_cinfo)*MAX_PNP_LDN)); } else { /* * Output the pnp_ldn_overrides[] table. */ error=sysctl_handle_opaque(oidp,&pnp_ldn_overrides, sizeof(struct pnp_cinfo)*MAX_PNP_LDN,req); if(error) return(error); return(0); } } SYSCTL_PROC( _machdep, OID_AUTO, uc_pnplist, CTLFLAG_RD, 0, 0, sysctl_machdep_uc_pnplist, "A", "List of PnP overrides changed in UserConfig"); /* * this function sets the kernel table to override bios PnP * configuration. */ static int set_pnp_parms(CmdParm *parms) { u_long idx, val, ldn, csn; int i; const char *q; const char *p = parms[0].parm.u.sparm; struct pnp_cinfo d; csn=strtoul(p,&q, 0); ldn=strtoul(q,&q, 0); for (p=q; *p && (*p==' ' || *p=='\t'); p++) ; if (csn < 1 || csn > MAX_PNP_CARDS || ldn >= MAX_PNP_LDN) { printf("bad csn/ldn %ld:%ld\n", csn, ldn); return 0; } for (i=0; i < MAX_PNP_LDN; i++) { if (pnp_ldn_overrides[i].csn == csn && pnp_ldn_overrides[i].ldn == ldn) break; } if (i==MAX_PNP_LDN) { for (i=0; i < MAX_PNP_LDN; i++) { if (pnp_ldn_overrides[i].csn <1 || pnp_ldn_overrides[i].csn > MAX_PNP_CARDS) break; } } if (i==MAX_PNP_LDN) { printf("sorry, no PnP entries available, try delete one\n"); return 0 ; } d = pnp_ldn_overrides[i] ; d.csn = csn; d.ldn = ldn ; while (*p) { idx = 0; val = 0; if (!strncmp(p,"irq",3)) { idx=strtoul(p+3,&q, 0); val=strtoul(q,&q, 0); if (idx >=0 && idx < 2) d.irq[idx] = val; } else if (!strncmp(p,"flags",5)) { idx=strtoul(p+5,&q, 0); d.flags = idx; } else if (!strncmp(p,"drq",3)) { idx=strtoul(p+3,&q, 0); val=strtoul(q,&q, 0); if (idx >=0 && idx < 2) d.drq[idx] = val; } else if (!strncmp(p,"port",4)) { idx=strtoul(p+4,&q, 0); val=strtoul(q,&q, 0); if (idx >=0 && idx < 8) d.port[idx] = val; } else if (!strncmp(p,"mem",3)) { idx=strtoul(p+3,&q, 0); val=strtoul(q,&q, 0); if (idx >=0 && idx < 4) d.mem[idx].base = val; } else if (!strncmp(p,"bios",4)) { q = p+ 4; d.override = 0 ; } else if (!strncmp(p,"os",2)) { q = p+2 ; d.override = 1 ; } else if (!strncmp(p,"disable",7)) { q = p+7 ; d.enable = 0 ; } else if (!strncmp(p,"enable",6)) { q = p+6; d.enable = 1 ; } else if (!strncmp(p,"delete",6)) { bzero(&pnp_ldn_overrides[i], sizeof (pnp_ldn_overrides[i])); if (i==0) pnp_ldn_overrides[i].csn = 255;/* not reinit */ return 0; } else { printf("unknown command <%s>\n", p); break; } for (p=q; *p && (*p==' ' || *p=='\t'); p++) ; } pnp_ldn_overrides[i] = d ; return 0; } #endif /* NPNP */ #if NEISA > 0 static int set_num_eisa_slots(CmdParm *parms) { int num_slots; num_slots = parms[0].parm.iparm; num_eisa_slots = (num_slots <= 16 ? num_slots : 10); return 0; } #endif /* NEISA > 0 */ static int quitfunc(CmdParm *parms) { /* * If kernel config supplied, and we are parsing it, and -c also supplied, * ignore a quit command, This provides a safety mechanism to allow * recovery from a damaged/buggy kernel config. */ if ((boothowto & RB_CONFIG) && userconfig_boot_parsing) return 0; return 1; } static int helpfunc(CmdParm *parms) { printf( "Command\t\t\tDescription\n" "-------\t\t\t-----------\n" "ls\t\t\tList currently configured devices\n" "port \tSet device port (i/o address)\n" "irq \tSet device irq\n" "drq \tSet device drq\n" "iomem \tSet device maddr (memory address)\n" "iosize \tSet device memory size\n" "flags \tSet device flags\n" "enable \tEnable device\n" "disable \tDisable device (will not be probed)\n"); #if NPNP > 0 printf( "pnp [enable|disable]\tenable/disable device\n" "pnp [os|bios]\tset parameters using FreeBSD or BIOS\n" "pnp [portX ]\tset addr for port X (0..7)\n" "pnp [memX ]\tset addr for memory range X (0..3)\n" "pnp [irq ]\tset irq X (0..1) to number, 0=unused\n" "pnp [drq ]\tset drq X (0..1) to number, 4=unused\n"); #endif #if NEISA > 0 printf("eisa \t\tSet the number of EISA slots to probe\n"); #endif /* NEISA > 0 */ printf( "quit\t\t\tExit this configuration utility\n" "reset\t\t\tReset CPU\n"); #ifdef VISUAL_USERCONFIG printf("visual\t\t\tGo to fullscreen mode.\n"); #endif printf( "help\t\t\tThis message\n\n" "Commands may be abbreviated to a unique prefix\n"); return 0; } #if defined(INTRO_USERCONFIG) #if defined (VISUAL_USERCONFIG) static void center(int y, char *str) { putxy((80 - strlen(str)) / 2, y, str); } #endif static int introfunc(CmdParm *parms) { #if defined (VISUAL_USERCONFIG) int curr_item, first_time, extended = 0; static char *choices[] = { " Skip kernel configuration and continue with installation ", " Start kernel configuration in full-screen visual mode ", " Start kernel configuration in CLI mode ", }; clear(); center(2, "!bKernel Configuration Menu!n"); curr_item = 0; first_time = 1; while (1) { char tmp[80]; int c, i; if (!extended) { for (i = 0; i < 3; i++) { tmp[0] = '\0'; if (curr_item == i) strcpy(tmp, "!i"); strcat(tmp, choices[i]); if (curr_item == i) strcat(tmp, "!n"); putxy(10, 5 + i, tmp); } if (first_time) { putxy(2, 10, "Here you have the chance to go into kernel configuration mode, making"); putxy(2, 11, "any changes which may be necessary to properly adjust the kernel to"); putxy(2, 12, "match your hardware configuration."); putxy(2, 14, "If you are installing FreeBSD for the first time, select Visual Mode"); putxy(2, 15, "(press Down-Arrow then ENTER)."); putxy(2, 17, "If you need to do more specialized kernel configuration and are an"); putxy(2, 18, "experienced FreeBSD user, select CLI mode."); putxy(2, 20, "If you are !icertain!n that you do not need to configure your kernel"); putxy(2, 21, "then simply press ENTER or Q now."); first_time = 0; } move(0, 0); /* move the cursor out of the way */ } c = getchar(); if ((extended == 2) || (c == 588) || (c == 596)) { /* console gives "alternative" codes */ extended = 0; /* no longer */ switch (c) { case 588: case 'A': /* up */ if (curr_item > 0) --curr_item; break; case 596: case 'B': /* down */ if (curr_item < 2) ++curr_item; break; } } else { switch(c) { case '\033': extended = 1; break; case '[': /* cheat : always preceeds cursor move */ case 'O': /* ANSI application key mode */ if (extended == 1) extended = 2; else extended = 0; break; case -1: case 'Q': case 'q': clear(); return 1; /* user requests exit */ case '1': /* select an item */ case 'S': case 's': curr_item = 0; break; case '2': case 'V': case 'v': curr_item = 1; break; case '3': case 'C': case 'c': curr_item = 2; break; case 'U': /* up */ case 'u': case 'P': case 'p': if (curr_item > 0) --curr_item; break; case 'D': /* down */ case 'd': case 'N': case 'n': if (curr_item < 2) ++curr_item; break; case '\r': case '\n': clear(); if (!curr_item) return 1; else if (curr_item == 1) return visuserconfig(); else { putxy(0, 1, "Type \"help\" for help or \"quit\" to exit."); /* enable quitfunc */ userconfig_boot_parsing=0; move (0, 3); boothowto |= RB_CONFIG; /* force -c */ return 0; } break; } } } #endif } #endif #if NPNP > 0 static int lspnp () { struct pnp_cinfo *c; int i, first = 1; for (i=0; i< MAX_PNP_LDN; i++) { c = &pnp_ldn_overrides[i]; if (c->csn >0 && c->csn != 255) { int pmax, mmax; static char pfmt[] = "port 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x "; static char mfmt[] = "mem 0x%x 0x%x 0x%x 0x%x"; char buf[256]; if (lineno >= 23) { if (!userconfig_boot_parsing) { printf(" "); if (getchar() == 'q') { printf("quit\n"); return (1); } printf("\n"); } lineno = 0; } if (lineno == 0 || first) printf("CSN LDN conf en irqs drqs others (PnP devices)\n"); first = 0 ; printf("%3d %3d %4s %2s %2d %-2d %2d %-2d ", c->csn, c->ldn, c->override ? "OS ":"BIOS", c->enable ? "Y":"N", c->irq[0], c->irq[1], c->drq[0], c->drq[1]); if (c->flags) printf("flags 0x%08lx ",c->flags); for (pmax = 7; pmax >=0 ; pmax--) if (c->port[pmax]!=0) break; for (mmax = 3; mmax >=0 ; mmax--) if (c->mem[mmax].base!=0) break; if (pmax>=0) { strcpy(buf, pfmt); buf[10 + 5*pmax]='\0'; printf(buf, c->port[0], c->port[1], c->port[2], c->port[3], c->port[4], c->port[5], c->port[6], c->port[7]); } if (mmax>=0) { strcpy(buf, mfmt); buf[8 + 5*mmax]='\0'; printf(buf, c->mem[0].base, c->mem[1].base, c->mem[2].base, c->mem[3].base); } printf("\n"); } } return 0 ; } #endif /* NPNP */ static int lsdevtab(struct isa_device *dt) { for (; dt->id_id != 0; dt++) { char dname[80]; if (lineno >= 23) { printf(" "); if (!userconfig_boot_parsing) { if (getchar() == 'q') { printf("quit\n"); return (1); } printf("\n"); } lineno = 0; } if (lineno == 0) { printf( "Device port irq drq iomem iosize unit flags enab confl\n" ); ++lineno; } sprintf(dname, "%s%d", dt->id_driver->name, dt->id_unit); printf("%-9.9s%-#11x%-6d%-6d%-8p%-9d%-6d%-#11x%-5s%-3s\n", dname, /* dt->id_id, dt->id_driver(by name), */ dt->id_iobase, ffs(dt->id_irq) - 1, dt->id_drq, dt->id_maddr, dt->id_msize, /* dt->id_intr(by name), */ dt->id_unit, dt->id_flags, /* dt->id_scsiid, dt->id_alive, dt->id_ri_flags, */ /* dt->id_reconfig, */ dt->id_enabled ? "Yes" : "No", dt->id_conflicts ? "Yes" : "No"); ++lineno; } return(0); } +static void +load_devtab(void) +{ + int i, val; + int count = resource_count(); + int id = 1; + int dt; + char *name; + int unit; + + isa_devtab = malloc(sizeof(struct isa_device)*(count + 1),M_DEVL,M_WAITOK); + isa_drvtab = malloc(sizeof(struct isa_driver)*(count + 1),M_DEVL,M_WAITOK); + bzero(isa_devtab, sizeof(struct isa_device) * (count + 1)); + bzero(isa_drvtab, sizeof(struct isa_driver) * (count + 1)); + dt = 0; + for (i = 0; i < count; i++) { + name = resource_query_name(i); + unit = resource_query_unit(i); + if (unit < 0) + continue; /* skip wildcards */ + isa_devtab[dt].id_id = id++; + isa_devtab[dt].id_driver = &isa_drvtab[dt]; + resource_int_value(name, unit, "port", &isa_devtab[dt].id_iobase); + val = 0; + resource_int_value(name, unit, "irq", &val); + isa_devtab[dt].id_irq = (1 << val); + resource_int_value(name, unit, "drq", &isa_devtab[dt].id_drq); + resource_int_value(name, unit, "maddr",(int *)&isa_devtab[dt].id_maddr); + resource_int_value(name, unit, "msize", &isa_devtab[dt].id_msize); + isa_devtab[dt].id_unit = unit; + resource_int_value(name, unit, "flags", &isa_devtab[dt].id_flags); + val = 0; + resource_int_value(name, unit, "disabled", &val); + isa_devtab[dt].id_enabled = !val; + isa_drvtab[dt].name = malloc(strlen(name) + 1, M_DEVL,M_WAITOK); + strcpy(isa_drvtab[dt].name, name); + dt++; + } +} + +static void +free_devtab(void) +{ + int i; + int count = resource_count(); + + for (i = 0; i < count; i++) + if (isa_drvtab[i].name) + free(isa_drvtab[i].name, M_DEVL); + free(isa_drvtab, M_DEVL); + free(isa_devtab, M_DEVL); +} + static struct isa_device * find_device(char *devname, int unit) { struct isa_device *ret; - if ((ret = search_devtable(&isa_devtab_bio[0], devname, unit)) != NULL) + if ((ret = search_devtable(isa_devtab, devname, unit)) != NULL) return ret; - if ((ret = search_devtable(&isa_devtab_tty[0], devname, unit)) != NULL) - return ret; - if ((ret = search_devtable(&isa_devtab_net[0], devname, unit)) != NULL) - return ret; - if ((ret = search_devtable(&isa_devtab_cam[0], devname, unit)) != NULL) - return ret; - if ((ret = search_devtable(&isa_devtab_null[0], devname, unit)) != NULL) - return ret; return NULL; } static struct isa_device * search_devtable(struct isa_device *dt, char *devname, int unit) { int i; for (i = 0; dt->id_id != 0; dt++) if (!strcmp(dt->id_driver->name, devname) && dt->id_unit == unit) return dt; return NULL; } static void cngets(char *input, int maxin) { int c, nchars = 0; while (1) { c = getchar(); /* Treat ^H or ^? as backspace */ if ((c == '\010' || c == '\177')) { if (nchars) { printf("\010 \010"); *--input = '\0', --nchars; } continue; } /* Treat ^U or ^X as kill line */ else if ((c == '\025' || c == '\030')) { while (nchars) { printf("\010 \010"); *--input = '\0', --nchars; } continue; } printf("%c", c); if ((++nchars == maxin) || (c == '\n') || (c == '\r') || ( c == -1)) { *input = '\0'; break; } *input++ = (u_char)c; } } /* * Kludges to get the library sources of strtoul.c to work in our * environment. isdigit() and isspace() could be used above too. */ #define isalpha(c) (((c) >= 'A' && (c) <= 'Z') \ || ((c) >= 'a' && (c) <= 'z')) /* unsafe */ #define isdigit(c) ((unsigned)((c) - '0') <= '9' - '0') #define isspace(c) ((c) == ' ' || (c) == '\t') /* unsafe */ #define isupper(c) ((unsigned)((c) - 'A') <= 'Z' - 'A') static int errno; /* * The following should be identical with the library sources for strtoul.c. */ /* * Convert a string to an unsigned long integer. * * Ignores `locale' stuff. Assumes that the upper and lower case * alphabets and digits are each contiguous. */ static unsigned long strtoul(nptr, endptr, base) const char *nptr; const char **endptr; register int base; { register const char *s = nptr; register unsigned long acc; register int c; register unsigned long cutoff; register int neg = 0, any, cutlim; /* * See strtol for comments as to the logic used. */ do { c = *s++; } while (isspace(c)); if (c == '-') { neg = 1; c = *s++; } else if (c == '+') c = *s++; if ((base == 0 || base == 16) && c == '0' && (*s == 'x' || *s == 'X')) { c = s[1]; s += 2; base = 16; } if (base == 0) base = c == '0' ? 8 : 10; cutoff = (unsigned long)ULONG_MAX / (unsigned long)base; cutlim = (unsigned long)ULONG_MAX % (unsigned long)base; for (acc = 0, any = 0;; c = *s++) { if (isdigit(c)) c -= '0'; else if (isalpha(c)) c -= isupper(c) ? 'A' - 10 : 'a' - 10; else break; if (c >= base) break; if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim)) any = -1; else { any = 1; acc *= base; acc += c; } } if (any < 0) { acc = ULONG_MAX; errno = ERANGE; } else if (neg) acc = -acc; if (endptr != 0) *endptr = (const char *)(any ? s - 1 : nptr); return (acc); } #if 0 /* scsi: Support for displaying configured SCSI devices. * There is no way to edit them, and this is inconsistent * with the ISA method. This is here as a basis for further work. */ static char * type_text(char *name) /* XXX: This is bogus */ { if (strcmp(name, "sd") == 0) return "disk"; if (strcmp(name, "st") == 0) return "tape"; return "device"; } id_put(char *desc, int id) { if (id != SCCONF_UNSPEC) { if (desc) printf("%s", desc); if (id == SCCONF_ANY) printf("?"); else printf("%d", id); } } static void lsscsi(void) { int i; printf("scsi: (can't be edited):\n"); for (i = 0; scsi_cinit[i].driver; i++) { id_put("controller scbus", scsi_cinit[i].scbus); if (scsi_cinit[i].unit != -1) { printf(" at "); id_put(scsi_cinit[i].driver, scsi_cinit[i].unit); } printf("\n"); } for (i = 0; scsi_dinit[i].name; i++) { printf("%s ", type_text(scsi_dinit[i].name)); id_put(scsi_dinit[i].name, scsi_dinit[i].unit); id_put(" at scbus", scsi_dinit[i].cunit); id_put(" target ", scsi_dinit[i].target); id_put(" lun ", scsi_dinit[i].lun); if (scsi_dinit[i].flags) printf(" flags 0x%x\n", scsi_dinit[i].flags); printf("\n"); } } static int list_scsi(CmdParm *parms) { lineno = 0; lsscsi(); return 0; } #endif +static void +save_resource(struct isa_device *idev) +{ + int i; + char *name; + int unit; + int count = resource_count(); + + for (i = 0; i < count; i++) { + name = resource_query_name(i); + unit = resource_query_unit(i); + if (strcmp(name, idev->id_driver->name) || unit != idev->id_unit) + continue; + resource_set_int(i, "port", isa_devtab[i].id_iobase); + resource_set_int(i, "irq", (1 << isa_devtab[i].id_irq)); + resource_set_int(i, "drq", isa_devtab[i].id_drq); + resource_set_int(i, "maddr", (int)isa_devtab[i].id_maddr); + resource_set_int(i, "msize", isa_devtab[i].id_msize); + resource_set_int(i, "flags", isa_devtab[i].id_flags); + resource_set_int(i, "disabled", !isa_devtab[i].id_enabled); + } +} + static int save_dev(idev) struct isa_device *idev; { struct isa_device *id_p,*id_pn; for (id_p=isa_devlist; id_p; id_p=id_p->id_next) { if (id_p->id_id == idev->id_id) { id_pn = id_p->id_next; bcopy(idev,id_p,sizeof(struct isa_device)); + save_resource(idev); id_p->id_next = id_pn; return 1; } } id_pn = malloc(sizeof(struct isa_device),M_DEVL,M_WAITOK); bcopy(idev,id_pn,sizeof(struct isa_device)); id_pn->id_next = isa_devlist; isa_devlist = id_pn; return 0; } Index: head/sys/i386/include/asnames.h =================================================================== --- head/sys/i386/include/asnames.h (revision 45719) +++ head/sys/i386/include/asnames.h (revision 45720) @@ -1,393 +1,393 @@ /*- * Copyright (c) 1997 John D. Polstra * 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. * - * $Id: asnames.h,v 1.29 1999/02/22 15:13:34 bde Exp $ + * $Id: asnames.h,v 1.30 1999/02/25 12:53:34 bde Exp $ */ #ifndef _MACHINE_ASNAMES_H_ #define _MACHINE_ASNAMES_H_ /* * This file is used by the kernel assembly language sources to provide * the proper mapping between the global names used in assembly language * code and the corresponding C symbols. By convention, all C symbols * that are referenced from assembly language are prefixed with `_'. * That happens to be the same prefix that the a.out compiler attaches * to each C symbol. * * When using the ELF compiler, C symbols are identical to the corresponding * assembly language symbols. Thus the extra underscores cause problems. * The defines in this file map the underscore names back to the proper * unadorned names. * * Every global symbol that is referenced from both C source and assembly * language source must have an entry in this file, or the kernel will * not build properly using the ELF compiler. * * This file is included by , and it is OK to rely * on that. */ #ifdef __ELF__ #define _APTD APTD #define _APTDpde APTDpde #define _APTmap APTmap #define _CONST_QNaN CONST_QNaN #define _IdlePTD IdlePTD #define _KPTphys KPTphys #define _MP_GDT MP_GDT #define _MPgetlock MPgetlock #define _MPrellock MPrellock #define _MPtrylock MPtrylock #define _PTD PTD #define _PTDpde PTDpde #define _PTmap PTmap #define _SMP_ioapic SMP_ioapic #define _SMP_prvpt SMP_prvpt #define _Xalign Xalign #define _Xbnd Xbnd #define _Xbpt Xbpt #define _Xcpuast Xcpuast #define _Xcpucheckstate Xcpucheckstate #define _Xcpustop Xcpustop #define _Xdbg Xdbg #define _Xdiv Xdiv #define _Xdna Xdna #define _Xfastintr0 Xfastintr0 #define _Xfastintr1 Xfastintr1 #define _Xfastintr10 Xfastintr10 #define _Xfastintr11 Xfastintr11 #define _Xfastintr12 Xfastintr12 #define _Xfastintr13 Xfastintr13 #define _Xfastintr14 Xfastintr14 #define _Xfastintr15 Xfastintr15 #define _Xfastintr16 Xfastintr16 #define _Xfastintr17 Xfastintr17 #define _Xfastintr18 Xfastintr18 #define _Xfastintr19 Xfastintr19 #define _Xfastintr2 Xfastintr2 #define _Xfastintr20 Xfastintr20 #define _Xfastintr21 Xfastintr21 #define _Xfastintr22 Xfastintr22 #define _Xfastintr23 Xfastintr23 #define _Xfastintr3 Xfastintr3 #define _Xfastintr4 Xfastintr4 #define _Xfastintr5 Xfastintr5 #define _Xfastintr6 Xfastintr6 #define _Xfastintr7 Xfastintr7 #define _Xfastintr8 Xfastintr8 #define _Xfastintr9 Xfastintr9 #define _Xforward_irq Xforward_irq #define _Xfpu Xfpu #define _Xfpusegm Xfpusegm #define _Xill Xill #define _Xint0x80_syscall Xint0x80_syscall #define _Xintr0 Xintr0 #define _Xintr1 Xintr1 #define _Xintr10 Xintr10 #define _Xintr11 Xintr11 #define _Xintr12 Xintr12 #define _Xintr13 Xintr13 #define _Xintr14 Xintr14 #define _Xintr15 Xintr15 #define _Xintr16 Xintr16 #define _Xintr17 Xintr17 #define _Xintr18 Xintr18 #define _Xintr19 Xintr19 #define _Xintr2 Xintr2 #define _Xintr20 Xintr20 #define _Xintr21 Xintr21 #define _Xintr22 Xintr22 #define _Xintr23 Xintr23 #define _Xintr3 Xintr3 #define _Xintr4 Xintr4 #define _Xintr5 Xintr5 #define _Xintr6 Xintr6 #define _Xintr7 Xintr7 #define _Xintr8 Xintr8 #define _Xintr8254 Xintr8254 #define _Xintr9 Xintr9 #define _XintrRTC XintrRTC #define _Xinvltlb Xinvltlb #define _Xmchk Xmchk #define _Xmissing Xmissing #define _Xnmi Xnmi #define _Xofl Xofl #define _Xpage Xpage #define _Xprot Xprot #define _Xrsvd Xrsvd #define _Xspuriousint Xspuriousint #define _Xstk Xstk #define _Xsyscall Xsyscall #define _Xtss Xtss #define __default_ldt _default_ldt #define __ucodesel _ucodesel #define __udatasel _udatasel #define _alltraps alltraps #define _ap_init ap_init #define _apic_base apic_base #define _apic_id_to_logical apic_id_to_logical #define _apic_imen apic_imen #define _apic_isrbit_location apic_isrbit_location #define _apic_pin_trigger apic_pin_trigger #define _apm_addr apm_addr #define _apm_bios_call apm_bios_call #define _apm_cs16_base apm_cs16_base #define _apm_cs16_limit apm_cs16_limit #define _apm_cs32_base apm_cs32_base #define _apm_cs32_limit apm_cs32_limit #define _apm_cs_entry apm_cs_entry #define _apm_cs_limit apm_cs_limit #define _apm_current_gdt_pdesc apm_current_gdt_pdesc #define _apm_ds_base apm_ds_base #define _apm_ds_limit apm_ds_limit #define _apm_flags apm_flags #define _apm_init_image apm_init_image #define _apm_init_image_size apm_init_image_size #define _apm_setup apm_setup #define _apm_version apm_version #define _arith_invalid arith_invalid #define _arith_overflow arith_overflow #define _arith_underflow arith_underflow #define _bcopy bcopy #define _bcopy_vector bcopy_vector #define _bigJump bigJump #define _bio_imask bio_imask #define _bluetrap bluetrap #define _bootCodeSeg bootCodeSeg #define _bootDataSeg bootDataSeg #define _bootMP bootMP #define _bootMP_size bootMP_size #define _bootPTD bootPTD #define _boot_get_mplock boot_get_mplock #define _bootdev bootdev #define _boothowto boothowto #define _bootinfo bootinfo #define _bootstrap_gdt bootstrap_gdt #define _bzero bzero #define _cam_imask cam_imask #define _checkstate_cpus checkstate_cpus #define _checkstate_cpustate checkstate_cpustate #define _checkstate_curproc checkstate_curproc #define _checkstate_need_ast checkstate_need_ast #define _checkstate_pc checkstate_pc #define _checkstate_pending_ast checkstate_pending_ast #define _checkstate_probed_cpus checkstate_probed_cpus #define _clock_lock clock_lock #define _cnt cnt #define _common_tss common_tss #define _common_tssd common_tssd #define _copyin_vector copyin_vector #define _copyout_vector copyout_vector #define _cpl cpl #define _cpl_lock cpl_lock #define _cpu cpu #define _cpu0prvpage cpu0prvpage #define _cpu0prvpt cpu0prvpt #define _cpu_apic_versions cpu_apic_versions #define _cpu_class cpu_class #define _cpu_feature cpu_feature #define _cpu_high cpu_high #define _cpu_id cpu_id #define _cpu_num_to_apic_id cpu_num_to_apic_id #define _cpu_switch cpu_switch #define _cpu_vendor cpu_vendor #define _cpuid cpuid #define _curpcb curpcb #define _curproc curproc #define _currentldt currentldt #define _cypoll cypoll #define _default_halt default_halt #define _denormal_operand denormal_operand #define _div_small div_small #define _divide_by_zero divide_by_zero #define _divide_kernel divide_kernel #define _do_page_zero_idle do_page_zero_idle #define _edata edata #define _eintrcnt eintrcnt #define _eintrnames eintrnames #define _end end #define _etext etext #define _exception exception #define _fast_intr_lock fast_intr_lock #define _fastmove fastmove #define _gdt gdt #define _generic_bcopy generic_bcopy #define _generic_bzero generic_bzero #define _generic_copyin generic_copyin #define _generic_copyout generic_copyout #define _get_align_lock get_align_lock #define _get_altsyscall_lock get_altsyscall_lock #define _get_fpu_lock get_fpu_lock #define _get_isrlock get_isrlock #define _get_mplock get_mplock #define _get_syscall_lock get_syscall_lock #define _getmicrouptime getmicrouptime #define _idqs idqs #define _ihandlers ihandlers #define _imen imen #define _imen_lock imen_lock #define _in_vm86call in_vm86call #define _init386 init386 #define _init_secondary init_secondary #define _initial_bioscalls initial_bioscalls #define _inside_intr inside_intr #define _intr_countp intr_countp #define _intr_handler intr_handler #define _intr_mask intr_mask #define _intr_nesting_level intr_nesting_level #define _intr_unit intr_unit #define _intrcnt intrcnt #define _intrnames intrnames #define _invltlb_ok invltlb_ok #define _ioapic ioapic #define _ipending ipending #define _isr_lock isr_lock #define _ivectors ivectors #define _kernelname kernelname #define _kstack kstack #define _lapic lapic #define _linux_sigcode linux_sigcode #define _linux_szsigcode linux_szsigcode #define _main main #define _mask8254 mask8254 #define _maskRTC maskRTC #define _microuptime microuptime #define _mp_gdtbase mp_gdtbase #define _mp_lock mp_lock #define _mp_ncpus mp_ncpus #define _mul64 mul64 #define _my_idlePTD my_idlePTD #define _my_tr my_tr #define _net_imask net_imask #define _netisr netisr #define _netisrs netisrs #define _nfs_diskless nfs_diskless #define _nfs_diskless_valid nfs_diskless_valid #define _normalize normalize #define _normalize_nuo normalize_nuo #define _npx_intrs_while_probing npx_intrs_while_probing #define _npx_traps_while_probing npx_traps_while_probing -#define _npxintr npxintr +#define _npx_intr npx_intr #define _npxproc npxproc #define _npxsave npxsave #define _other_cpus other_cpus #define _ovbcopy_vector ovbcopy_vector #define _panic panic #define _pc98_system_parameter pc98_system_parameter #define _poly_div16 poly_div16 #define _poly_div2 poly_div2 #define _poly_div4 poly_div4 #define _polynomial polynomial #define _private_tss private_tss #define _probeintr probeintr #define _probetrap probetrap #define _proc0 proc0 #define _proc0paddr proc0paddr #define _prv_CMAP1 prv_CMAP1 #define _prv_CMAP2 prv_CMAP2 #define _prv_CMAP3 prv_CMAP3 #define _prv_CPAGE1 prv_CPAGE1 #define _prv_CPAGE2 prv_CPAGE2 #define _prv_CPAGE3 prv_CPAGE3 #define _prv_PMAP1 prv_PMAP1 #define _prv_PPAGE1 prv_PPAGE1 #define _qs qs #define _rcpoll rcpoll #define _real_2op_NaN real_2op_NaN #define _reg_div reg_div #define _reg_u_add reg_u_add #define _reg_u_div reg_u_div #define _reg_u_mul reg_u_mul #define _reg_u_sub reg_u_sub #define _rel_mplock rel_mplock #define _round_reg round_reg #define _rtqs rtqs #define _s_lock s_lock #define _s_unlock s_unlock #define _secondary_main secondary_main #define _set_precision_flag_down set_precision_flag_down #define _set_precision_flag_up set_precision_flag_up #define _set_user_ldt set_user_ldt #define _shrx shrx #define _shrxs shrxs #define _sigcode sigcode #define _siopoll siopoll #define _smp_active smp_active #define _soft_imask soft_imask #define _softclock softclock #define _softnet_imask softnet_imask #define _softtty_imask softtty_imask #define _spl0 spl0 #define _splz splz #define _ss_lock ss_lock #define _ss_unlock ss_unlock #define _started_cpus started_cpus #define _stopped_cpus stopped_cpus #define _svr4_sigcode svr4_sigcode #define _svr4_sys_context svr4_sys_context #define _svr4_szsigcode svr4_szsigcode #define _swi_dispatcher swi_dispatcher #define _swi_generic swi_generic #define _swi_null swi_null #define _swi_vm swi_vm #define _switchticks switchticks #define _switchtime switchtime #define _syscall syscall #define _szsigcode szsigcode #define _ticks ticks #define _time time #define _timer0_max_count timer0_max_count #define _timer0_overflow_threshold timer0_overflow_threshold #define _timer0_prescaler_count timer0_prescaler_count #define _trap trap #define _trap_by_wrmsr trap_by_wrmsr #define _trapwrite trapwrite #define _tsc_bias tsc_bias #define _tsc_freq tsc_freq #define _tsc_multiplier tsc_multiplier #define _tty_imask tty_imask #define _userconfig_from_boot userconfig_from_boot #define _vec vec #define _vec8254 vec8254 #define _vecRTC vecRTC #define _vm86_emulate vm86_emulate #define _vm86_prepcall vm86_prepcall #define _vm86_sysarch vm86_sysarch #define _vm86_trap vm86_trap #define _vm86pa vm86pa #define _vm86paddr vm86paddr #define _vm86pcb vm86pcb #define _vm_page_zero_idle vm_page_zero_idle #define _want_resched want_resched #define _whichidqs whichidqs #define _whichqs whichqs #define _whichrtqs whichrtqs #define _wm_sqrt wm_sqrt #endif /* __ELF__ */ #endif /* !_MACHINE_ASNAMES_H_ */ Index: head/sys/i386/isa/aha_isa.c =================================================================== --- head/sys/i386/isa/aha_isa.c (revision 45719) +++ head/sys/i386/isa/aha_isa.c (revision 45720) @@ -1,291 +1,288 @@ /* * Product specific probe and attach routines for: * Adaptec 154x. * * Derived from code written by: * * Copyright (c) 1998 Justin T. Gibbs * 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, * without modification, immediately at the beginning of the file. * 2. 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 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. * - * $Id: aha_isa.c,v 1.5 1998/11/10 06:44:54 gibbs Exp $ + * $Id: aha_isa.c,v 1.6 1999/01/20 06:21:23 imp Exp $ */ #include "pnp.h" #include #include #include #include #include #include #include #include #if NPNP > 0 #include #endif static int aha_isa_probe(struct isa_device *dev); static int aha_isa_attach(struct isa_device *dev); static void aha_isa_intr(void *unit); struct isa_driver ahadriver = { aha_isa_probe, aha_isa_attach, "aha" }; /* * Check if the device can be found at the port given * and if so, set it up ready for further work * as an argument, takes the isa_device structure from * autoconf.c */ static int aha_isa_probe(dev) struct isa_device *dev; { /* * find unit and check we have that many defined */ struct aha_softc *aha; int port_index; int max_port_index; aha = NULL; /* * Bound our board search if the user has * specified an exact port. */ aha_find_probe_range(dev->id_iobase, &port_index, &max_port_index); if (port_index < 0) return 0; /* Attempt to find an adapter */ for (;port_index <= max_port_index; port_index++) { config_data_t config_data; u_int ioport; int error; ioport = aha_iop_from_bio(port_index); /* * Ensure this port has not already been claimed already * by a PCI, EISA or ISA adapter. */ if (aha_check_probed_iop(ioport) != 0) continue; dev->id_iobase = ioport; if (haveseen_isadev(dev, CC_IOADDR | CC_QUIET)) continue; /* Allocate a softc for use during probing */ aha = aha_alloc(dev->id_unit, I386_BUS_SPACE_IO, ioport); if (aha == NULL) break; /* We're going to attempt to probe it now, so mark it probed */ aha_mark_probed_bio(port_index); /* See if there is really a card present */ if (aha_probe(aha) || aha_fetch_adapter_info(aha)) { aha_free(aha); continue; } /* * Determine our IRQ, and DMA settings and * export them to the configuration system. */ error = aha_cmd(aha, AOP_INQUIRE_CONFIG, NULL, /*parmlen*/0, (u_int8_t*)&config_data, sizeof(config_data), DEFAULT_CMD_TIMEOUT); if (error != 0) { printf("aha_isa_probe: Could not determine IRQ or DMA " "settings for adapter at 0x%x. Failing probe\n", ioport); aha_free(aha); continue; } switch (config_data.dma_chan) { case DMA_CHAN_5: dev->id_drq = 5; break; case DMA_CHAN_6: dev->id_drq = 6; break; case DMA_CHAN_7: dev->id_drq = 7; break; default: printf("aha_isa_probe: Invalid DMA setting " "detected for adapter at 0x%x. " "Failing probe\n", ioport); return (0); } dev->id_irq = (config_data.irq << 9); dev->id_intr = aha_isa_intr; aha_unit++; return (AHA_NREGS); } return (0); } /* * Attach all the sub-devices we can find */ static int aha_isa_attach(dev) struct isa_device *dev; { struct aha_softc *aha; bus_dma_filter_t *filter; void *filter_arg; bus_addr_t lowaddr; aha = aha_softcs[dev->id_unit]; if (dev->id_drq != -1) isa_dmacascade(dev->id_drq); /* Allocate our parent dmatag */ filter = NULL; filter_arg = NULL; lowaddr = BUS_SPACE_MAXADDR_24BIT; if (bus_dma_tag_create(/*parent*/NULL, /*alignemnt*/0, /*boundary*/0, lowaddr, /*highaddr*/BUS_SPACE_MAXADDR, filter, filter_arg, /*maxsize*/BUS_SPACE_MAXSIZE_24BIT, /*nsegments*/BUS_SPACE_UNRESTRICTED, /*maxsegsz*/BUS_SPACE_MAXSIZE_24BIT, /*flags*/0, &aha->parent_dmat) != 0) { aha_free(aha); return (-1); } if (aha_init(aha)) { printf("aha init failed\n"); aha_free(aha); return (-1); } return (aha_attach(aha)); } /* * Handle an ISA interrupt. * XXX should go away as soon as ISA interrupt handlers * take a (void *) arg. */ static void aha_isa_intr(void *unit) { struct aha_softc* arg = aha_softcs[(int)unit]; aha_intr((void *)arg); } /* * support PnP cards if we are using 'em */ #if NPNP > 0 static char *ahapnp_probe(u_long csn, u_long vend_id); static void ahapnp_attach(u_long csn, u_long vend_id, char *name, struct isa_device *dev); static u_long nahapnp = NAHA; static struct pnp_device ahapnp = { "ahapnp", ahapnp_probe, ahapnp_attach, &nahapnp, &bio_imask }; DATA_SET (pnpdevice_set, ahapnp); static char * ahapnp_probe(u_long csn, u_long vend_id) { struct pnp_cinfo d; char *s = NULL; if (vend_id != AHA1542_PNP && vend_id != AHA1542_PNPCOMPAT) return (NULL); read_pnp_parms(&d, 0); if (d.enable == 0 || d.flags & 1) { printf("CSN %lu is disabled.\n", csn); return (NULL); } s = "Adaptec 1542CP"; return (s); } static void ahapnp_attach(u_long csn, u_long vend_id, char *name, struct isa_device *dev) { struct pnp_cinfo d; - struct isa_device *dvp; if (dev->id_unit >= NAHATOT) return; if (read_pnp_parms(&d, 0) == 0) { printf("failed to read pnp parms\n"); return; } write_pnp_parms(&d, 0); enable_pnp_card(); dev->id_iobase = d.port[0]; dev->id_irq = (1 << d.irq[0]); dev->id_intr = aha_intr; dev->id_drq = d.drq[0]; if (dev->id_driver == NULL) { dev->id_driver = &ahadriver; - dvp = find_isadev(isa_devtab_tty, &ahadriver, 0); - if (dvp != NULL) - dev->id_id = dvp->id_id; + dev->id_id = isa_compat_nextid(); } if ((dev->id_alive = aha_isa_probe(dev)) != 0) aha_isa_attach(dev); else printf("aha%d: probe failed\n", dev->id_unit); } #endif Index: head/sys/i386/isa/fd.c =================================================================== --- head/sys/i386/isa/fd.c (revision 45719) +++ head/sys/i386/isa/fd.c (revision 45720) @@ -1,2297 +1,2394 @@ /* * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Don Ahn. * * Libretto PCMCIA floppy support by David Horwitt (dhorwitt@ucsd.edu) * aided by the Linux floppy driver modifications from David Bateman * (dbateman@eng.uts.edu.au). * * Copyright (c) 1993, 1994 by * jc@irbs.UUCP (John Capo) * vak@zebub.msk.su (Serge Vakulenko) * ache@astral.msk.su (Andrew A. Chernov) * * Copyright (c) 1993, 1994, 1995 by * joerg_wunsch@uriah.sax.de (Joerg Wunsch) * dufault@hda.com (Peter Dufault) * * 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: @(#)fd.c 7.4 (Berkeley) 5/25/91 - * $Id: fd.c,v 1.133 1999/02/10 00:03:32 ken Exp $ + * $Id: fd.c,v 1.134 1999/04/06 03:06:51 peter Exp $ * */ #include "fd.h" #include "opt_devfs.h" #include "opt_fdc.h" #if NFDC > 0 #include #include #include +#include +#include #include -#include -#include -#include #include -#include #include +#include #include +#include #include #include -#include -#include -#include -#include -#include + +#include +#include +#include + +#include +#include +#include #include + #ifdef DEVFS #include #endif /* DEVFS */ +#include +#include +#include +#include +#include +#include + /* misuse a flag to identify format operation */ #define B_FORMAT B_XXX /* configuration flags */ #define FDC_PRETEND_D0 (1 << 0) /* pretend drive 0 to be there */ #ifdef FDC_YE #define FDC_IS_PCMCIA (1 << 1) /* if successful probe, then it's a PCMCIA device */ #endif /* internally used only, not really from CMOS: */ #define RTCFDT_144M_PRETENDED 0x1000 /* * this biotab field doubles as a field for the physical unit number * on the controller */ #define id_physid id_scsiid /* error returns for fd_cmd() */ #define FD_FAILED -1 #define FD_NOT_VALID -2 #define FDC_ERRMAX 100 /* do not log more */ #define NUMTYPES 14 #define NUMDENS (NUMTYPES - 6) /* These defines (-1) must match index for fd_types */ #define F_TAPE_TYPE 0x020 /* bit for fd_types to indicate tape */ #define NO_TYPE 0 /* must match NO_TYPE in ft.c */ #define FD_1720 1 #define FD_1480 2 #define FD_1440 3 #define FD_1200 4 #define FD_820 5 #define FD_800 6 #define FD_720 7 #define FD_360 8 #define FD_1480in5_25 9 #define FD_1440in5_25 10 #define FD_820in5_25 11 #define FD_800in5_25 12 #define FD_720in5_25 13 #define FD_360in5_25 14 static struct fd_type fd_types[NUMTYPES] = { { 21,2,0xFF,0x04,82,3444,1,FDC_500KBPS,2,0x0C,2 }, /* 1.72M in HD 3.5in */ { 18,2,0xFF,0x1B,82,2952,1,FDC_500KBPS,2,0x6C,1 }, /* 1.48M in HD 3.5in */ { 18,2,0xFF,0x1B,80,2880,1,FDC_500KBPS,2,0x6C,1 }, /* 1.44M in HD 3.5in */ { 15,2,0xFF,0x1B,80,2400,1,FDC_500KBPS,2,0x54,1 }, /* 1.2M in HD 5.25/3.5 */ { 10,2,0xFF,0x10,82,1640,1,FDC_250KBPS,2,0x2E,1 }, /* 820K in HD 3.5in */ { 10,2,0xFF,0x10,80,1600,1,FDC_250KBPS,2,0x2E,1 }, /* 800K in HD 3.5in */ { 9,2,0xFF,0x20,80,1440,1,FDC_250KBPS,2,0x50,1 }, /* 720K in HD 3.5in */ { 9,2,0xFF,0x2A,40, 720,1,FDC_250KBPS,2,0x50,1 }, /* 360K in DD 5.25in */ { 18,2,0xFF,0x02,82,2952,1,FDC_500KBPS,2,0x02,2 }, /* 1.48M in HD 5.25in */ { 18,2,0xFF,0x02,80,2880,1,FDC_500KBPS,2,0x02,2 }, /* 1.44M in HD 5.25in */ { 10,2,0xFF,0x10,82,1640,1,FDC_300KBPS,2,0x2E,1 }, /* 820K in HD 5.25in */ { 10,2,0xFF,0x10,80,1600,1,FDC_300KBPS,2,0x2E,1 }, /* 800K in HD 5.25in */ { 9,2,0xFF,0x20,80,1440,1,FDC_300KBPS,2,0x50,1 }, /* 720K in HD 5.25in */ { 9,2,0xFF,0x23,40, 720,2,FDC_300KBPS,2,0x50,1 }, /* 360K in HD 5.25in */ }; #define DRVS_PER_CTLR 2 /* 2 floppies */ /***********************************************************************\ * Per controller structure. * \***********************************************************************/ -struct fdc_data fdc_data[NFDC]; +static devclass_t fdc_devclass; /***********************************************************************\ * Per drive structure. * * N per controller (DRVS_PER_CTLR) * \***********************************************************************/ -static struct fd_data { +struct fd_data { struct fdc_data *fdc; /* pointer to controller structure */ int fdsu; /* this units number on this controller */ int type; /* Drive type (FD_1440...) */ struct fd_type *ft; /* pointer to the type descriptor */ int flags; #define FD_OPEN 0x01 /* it's open */ #define FD_ACTIVE 0x02 /* it's active */ #define FD_MOTOR 0x04 /* motor should be on */ #define FD_MOTOR_WAIT 0x08 /* motor coming up */ int skip; int hddrv; #define FD_NO_TRACK -2 int track; /* where we think the head is */ int options; /* user configurable options, see ioctl_fd.h */ struct callout_handle toffhandle; struct callout_handle tohandle; struct devstat device_stats; #ifdef DEVFS void *bdevs[1 + NUMDENS + MAXPARTITIONS]; void *cdevs[1 + NUMDENS + MAXPARTITIONS]; #endif -} fd_data[NFD]; + device_t dev; + fdu_t fdu; +}; +static devclass_t fd_devclass; /***********************************************************************\ * Throughout this file the following conventions will be used: * * fd is a pointer to the fd_data struct for the drive in question * * fdc is a pointer to the fdc_data struct for the controller * * fdu is the floppy drive unit number * * fdcu is the floppy controller unit number * * fdsu is the floppy drive unit number on that controller. (sub-unit) * \***********************************************************************/ #ifdef FDC_YE #include "card.h" static int yeattach(struct isa_device *); #endif -/* autoconfig functions */ -static int fdprobe(struct isa_device *); -static int fdattach(struct isa_device *); - /* needed for ft driver, thus exported */ -int in_fdc(fdcu_t); -int out_fdc(fdcu_t, int); +int in_fdc(struct fdc_data *); +int out_fdc(struct fdc_data *, int); /* internal functions */ -static void set_motor(fdcu_t, int, int); +static void fdc_add_device(device_t, const char *, int); +static void fdc_intr(void *); +static void set_motor(struct fdc_data *, int, int); # define TURNON 1 # define TURNOFF 0 static timeout_t fd_turnoff; static timeout_t fd_motor_on; -static void fd_turnon(fdu_t); +static void fd_turnon(struct fd_data *); static void fdc_reset(fdc_p); -static int fd_in(fdcu_t, int *); -static void fdstart(fdcu_t); +static int fd_in(struct fdc_data *, int *); +static void fdstart(struct fdc_data *); static timeout_t fd_iotimeout; static timeout_t fd_pseudointr; -static ointhand2_t fdintr; -static int fdstate(fdcu_t, fdc_p); -static int retrier(fdcu_t); +static int fdstate(struct fdc_data *); +static int retrier(struct fdc_data *); static int fdformat(dev_t, struct fd_formb *, struct proc *); static int enable_fifo(fdc_p fdc); static int fifo_threshold = 8; /* XXX: should be accessible via sysctl */ #define DEVIDLE 0 #define FINDWORK 1 #define DOSEEK 2 #define SEEKCOMPLETE 3 #define IOCOMPLETE 4 #define RECALCOMPLETE 5 #define STARTRECAL 6 #define RESETCTLR 7 #define SEEKWAIT 8 #define RECALWAIT 9 #define MOTORWAIT 10 #define IOTIMEDOUT 11 #define RESETCOMPLETE 12 #ifdef FDC_YE #define PIOREAD 13 #endif #ifdef FDC_DEBUG static char const * const fdstates[] = { "DEVIDLE", "FINDWORK", "DOSEEK", "SEEKCOMPLETE", "IOCOMPLETE", "RECALCOMPLETE", "STARTRECAL", "RESETCTLR", "SEEKWAIT", "RECALWAIT", "MOTORWAIT", "IOTIMEDOUT", "RESETCOMPLETE", #ifdef FDC_YE "PIOREAD", #endif }; /* CAUTION: fd_debug causes huge amounts of logging output */ static int volatile fd_debug = 0; #define TRACE0(arg) if(fd_debug) printf(arg) #define TRACE1(arg1, arg2) if(fd_debug) printf(arg1, arg2) #else /* FDC_DEBUG */ #define TRACE0(arg) #define TRACE1(arg1, arg2) #endif /* FDC_DEBUG */ #ifdef FDC_YE #if NCARD > 0 #include #include #include #include #include /* * PC-Card (PCMCIA) specific code. */ static int yeinit(struct pccard_devinfo *); /* init device */ static void yeunload(struct pccard_devinfo *); /* Disable driver */ static int yeintr(struct pccard_devinfo *); /* Interrupt handler */ PCCARD_MODULE(fdc, yeinit, yeunload, yeintr, 0, bio_imask); /* * this is the secret PIO data port (offset from base) */ #define FDC_YE_DATAPORT 6 /* * Initialize the device - called from Slot manager. */ static int yeinit(struct pccard_devinfo *devi) { fdc_p fdc = &fdc_data[devi->isahd.id_unit]; /* validate unit number. */ if (devi->isahd.id_unit >= NFDC) return(ENODEV); fdc->baseport = devi->isahd.id_iobase; /* * reset controller */ outb(fdc->baseport+FDOUT, 0); DELAY(100); outb(fdc->baseport+FDOUT, FDO_FRST); /* * wire into system */ if (yeattach(&devi->isahd) == 0) return(ENXIO); return(0); } /* * yeunload - unload the driver and clear the table. * XXX TODO: * This is usually called when the card is ejected, but * can be caused by a modunload of a controller driver. * The idea is to reset the driver's view of the device * and ensure that any driver entry points such as * read and write do not hang. */ static void yeunload(struct pccard_devinfo *devi) { if (fd_data[devi->isahd.id_unit].type == NO_TYPE) return; /* * this prevents Fdopen() and fdstrategy() from attempting * to access unloaded controller */ fd_data[devi->isahd.id_unit].type = NO_TYPE; printf("fdc%d: unload\n", devi->isahd.id_unit); } /* * yeintr - Shared interrupt called from * front end of PC-Card handler. */ static int yeintr(struct pccard_devinfo *devi) { fdintr((fdcu_t)devi->isahd.id_unit); return(1); } #endif /* NCARD > 0 */ #endif /* FDC_YE */ - -/* autoconfig structure */ - -struct isa_driver fdcdriver = { - fdprobe, fdattach, "fdc", -}; - static d_open_t Fdopen; /* NOTE, not fdopen */ static d_read_t fdread; static d_write_t fdwrite; static d_close_t fdclose; static d_ioctl_t fdioctl; static d_strategy_t fdstrategy; /* even if SLICE defined, these are needed for the ft support. */ #define CDEV_MAJOR 9 #define BDEV_MAJOR 2 - -static struct cdevsw fd_cdevsw = { - Fdopen, fdclose, fdread, fdwrite, - fdioctl, nostop, nullreset, nodevtotty, - seltrue, nommap, fdstrategy, "fd", - NULL, -1, nodump, nopsize, - D_DISK, 0, -1 }; - - -static struct isa_device *fdcdevs[NFDC]; - - static int -fdc_err(fdcu_t fdcu, const char *s) +fdc_err(struct fdc_data *fdc, const char *s) { - fdc_data[fdcu].fdc_errs++; - if(s) { - if(fdc_data[fdcu].fdc_errs < FDC_ERRMAX) - printf("fdc%d: %s", fdcu, s); - else if(fdc_data[fdcu].fdc_errs == FDC_ERRMAX) - printf("fdc%d: too many errors, not logging any more\n", - fdcu); + fdc->fdc_errs++; + if (s) { + if (fdc->fdc_errs < FDC_ERRMAX) { + device_print_prettyname(fdc->fdc_dev); + printf("%s", s); + } else if (fdc->fdc_errs == FDC_ERRMAX) { + device_print_prettyname(fdc->fdc_dev); + printf("too many errors, not logging any more\n"); + } } return FD_FAILED; } /* * fd_cmd: Send a command to the chip. Takes a varargs with this structure: * Unit number, * # of output bytes, output bytes as ints ..., * # of input bytes, input bytes as ints ... */ - static int -fd_cmd(fdcu_t fdcu, int n_out, ...) +fd_cmd(struct fdc_data *fdc, int n_out, ...) { u_char cmd; int n_in; int n; va_list ap; va_start(ap, n_out); cmd = (u_char)(va_arg(ap, int)); va_end(ap); va_start(ap, n_out); for (n = 0; n < n_out; n++) { - if (out_fdc(fdcu, va_arg(ap, int)) < 0) + if (out_fdc(fdc, va_arg(ap, int)) < 0) { char msg[50]; snprintf(msg, sizeof(msg), "cmd %x failed at out byte %d of %d\n", cmd, n + 1, n_out); - return fdc_err(fdcu, msg); + return fdc_err(fdc, msg); } } n_in = va_arg(ap, int); for (n = 0; n < n_in; n++) { int *ptr = va_arg(ap, int *); - if (fd_in(fdcu, ptr) < 0) + if (fd_in(fdc, ptr) < 0) { char msg[50]; snprintf(msg, sizeof(msg), "cmd %02x failed at in byte %d of %d\n", cmd, n + 1, n_in); - return fdc_err(fdcu, msg); + return fdc_err(fdc, msg); } } return 0; } static int enable_fifo(fdc_p fdc) { int i, j; if ((fdc->flags & FDC_HAS_FIFO) == 0) { /* * XXX: * Cannot use fd_cmd the normal way here, since * this might be an invalid command. Thus we send the * first byte, and check for an early turn of data directon. */ - if (out_fdc(fdc->fdcu, I8207X_CONFIGURE) < 0) - return fdc_err(fdc->fdcu, "Enable FIFO failed\n"); + if (out_fdc(fdc, I8207X_CONFIGURE) < 0) + return fdc_err(fdc, "Enable FIFO failed\n"); /* If command is invalid, return */ j = 100000; while ((i = inb(fdc->baseport + FDSTS) & (NE7_DIO | NE7_RQM)) != NE7_RQM && j-- > 0) if (i == (NE7_DIO | NE7_RQM)) { fdc_reset(fdc); return FD_FAILED; } if (j<0 || - fd_cmd(fdc->fdcu, 3, + fd_cmd(fdc, 3, 0, (fifo_threshold - 1) & 0xf, 0, 0) < 0) { fdc_reset(fdc); - return fdc_err(fdc->fdcu, "Enable FIFO failed\n"); + return fdc_err(fdc, "Enable FIFO failed\n"); } fdc->flags |= FDC_HAS_FIFO; return 0; } - if (fd_cmd(fdc->fdcu, 4, + if (fd_cmd(fdc, 4, I8207X_CONFIGURE, 0, (fifo_threshold - 1) & 0xf, 0, 0) < 0) - return fdc_err(fdc->fdcu, "Re-enable FIFO failed\n"); + return fdc_err(fdc, "Re-enable FIFO failed\n"); return 0; } static int fd_sense_drive_status(fdc_p fdc, int *st3p) { int st3; - if (fd_cmd(fdc->fdcu, 2, NE7CMD_SENSED, fdc->fdu, 1, &st3)) + if (fd_cmd(fdc, 2, NE7CMD_SENSED, fdc->fdu, 1, &st3)) { - return fdc_err(fdc->fdcu, "Sense Drive Status failed\n"); + return fdc_err(fdc, "Sense Drive Status failed\n"); } if (st3p) *st3p = st3; return 0; } static int fd_sense_int(fdc_p fdc, int *st0p, int *cylp) { - int st0, cyl; + int cyl, st0, ret; - int ret = fd_cmd(fdc->fdcu, 1, NE7CMD_SENSEI, 1, &st0); - - if (ret) - { - (void)fdc_err(fdc->fdcu, + ret = fd_cmd(fdc, 1, NE7CMD_SENSEI, 1, &st0); + if (ret) { + (void)fdc_err(fdc, "sense intr err reading stat reg 0\n"); return ret; } if (st0p) *st0p = st0; - if ((st0 & NE7_ST0_IC) == NE7_ST0_IC_IV) - { + if ((st0 & NE7_ST0_IC) == NE7_ST0_IC_IV) { /* * There doesn't seem to have been an interrupt. */ return FD_NOT_VALID; } - if (fd_in(fdc->fdcu, &cyl) < 0) - { - return fdc_err(fdc->fdcu, "can't get cyl num\n"); + if (fd_in(fdc, &cyl) < 0) { + return fdc_err(fdc, "can't get cyl num\n"); } if (cylp) *cylp = cyl; return 0; } static int fd_read_status(fdc_p fdc, int fdsu) { int i, ret; - for (i = 0; i < 7; i++) - { + for (i = 0; i < 7; i++) { /* * XXX types are poorly chosen. Only bytes can by read * from the hardware, but fdc->status[] wants u_ints and * fd_in() gives ints. */ int status; - ret = fd_in(fdc->fdcu, &status); + ret = fd_in(fdc, &status); fdc->status[i] = status; if (ret != 0) break; } if (ret == 0) fdc->flags |= FDC_STAT_VALID; else fdc->flags &= ~FDC_STAT_VALID; return ret; } /****************************************************************************/ /* autoconfiguration stuff */ /****************************************************************************/ -/* - * probe for existance of controller - */ static int -fdprobe(struct isa_device *dev) +fdc_probe(device_t dev) { - fdcu_t fdcu = dev->id_unit; - if(fdc_data[fdcu].flags & FDC_ATTACHED) - { - printf("fdc%d: unit used multiple times\n", fdcu); - return 0; + int error, i, ic_type; + struct fdc_data *fdc; + char myname[8]; /* better be long enough */ + + fdc = device_get_softc(dev); + bzero(fdc, sizeof *fdc); + fdc->fdc_dev = dev; + fdc->rid_ioport = fdc->rid_irq = fdc->rid_drq = 0; + fdc->res_ioport = fdc->res_irq = fdc->res_drq = 0; + + fdc->res_ioport = bus_alloc_resource(dev, SYS_RES_IOPORT, + &fdc->rid_ioport, 0ul, ~0ul, + IO_FDCSIZE, RF_ACTIVE); + if (fdc->res_ioport == 0) { + device_print_prettyname(dev); + printf("cannot reserve I/O port range\n"); + error = ENXIO; + goto out; } + fdc->baseport = fdc->res_ioport->r_start; - fdcdevs[fdcu] = dev; - fdc_data[fdcu].baseport = dev->id_iobase; + fdc->res_irq = bus_alloc_resource(dev, SYS_RES_IRQ, + &fdc->rid_irq, 0ul, ~0ul, 1, + RF_ACTIVE); + if (fdc->res_irq == 0) { + device_print_prettyname(dev); + printf("cannot reserve interrupt line\n"); + error = ENXIO; + goto out; + } + fdc->res_drq = bus_alloc_resource(dev, SYS_RES_DRQ, + &fdc->rid_drq, 0ul, ~0ul, 1, + RF_ACTIVE); + if (fdc->res_drq == 0) { + device_print_prettyname(dev); + printf("cannot reserve DMA request line\n"); + error = ENXIO; + goto out; + } + fdc->dmachan = fdc->res_drq->r_start; + error = BUS_SETUP_INTR(device_get_parent(dev), dev, + fdc->res_irq, fdc_intr, fdc, &fdc->fdc_intr); /* First - lets reset the floppy controller */ - outb(dev->id_iobase+FDOUT, 0); + outb(fdc->baseport + FDOUT, 0); DELAY(100); - outb(dev->id_iobase+FDOUT, FDO_FRST); + outb(fdc->baseport + FDOUT, FDO_FRST); /* see if it can handle a command */ - if (fd_cmd(fdcu, - 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), - 0)) - { - return(0); + if (fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), + NE7_SPEC_2(2, 0), 0)) { + error = ENXIO; + goto out; } + + if (fd_cmd(fdc, 1, NE7CMD_VERSION, 1, &ic_type) == 0) { + ic_type = (u_char)ic_type; + switch (ic_type) { + case 0x80: + device_set_desc(dev, "NEC 765 or clone"); + fdc->fdct = FDC_NE765; + break; + case 0x81: + device_set_desc(dev, "Intel 82077 or clone"); + fdc->fdct = FDC_I82077; + break; + case 0x90: + device_set_desc(dev, "NEC 72065B or clone"); + fdc->fdct = FDC_NE72065; + break; + default: + device_set_desc(dev, "generic floppy controller"); + fdc->fdct = FDC_UNKNOWN; + break; + } + } + + snprintf(myname, sizeof(myname), "%s%d", device_get_name(dev), + device_get_unit(dev)); + for (i = resource_query_string(-1, "at", myname); i != -1; + i = resource_query_string(i, "at", myname)) + fdc_add_device(dev, resource_query_name(i), + resource_query_unit(i)); #ifdef FDC_YE /* * don't succeed on probe; wait * for PCCARD subsystem to do it */ if (dev->id_flags & FDC_IS_PCMCIA) return(0); #endif - return (IO_FDCSIZE); + return (0); + +out: + if (fdc->fdc_intr) + BUS_TEARDOWN_INTR(device_get_parent(dev), dev, fdc->res_irq, + fdc->fdc_intr); + if (fdc->res_irq != 0) { + bus_deactivate_resource(dev, SYS_RES_IRQ, fdc->rid_irq, + fdc->res_irq); + bus_release_resource(dev, SYS_RES_IRQ, fdc->rid_irq, + fdc->res_irq); + } + if (fdc->res_ioport != 0) { + bus_deactivate_resource(dev, SYS_RES_IOPORT, fdc->rid_ioport, + fdc->res_ioport); + bus_release_resource(dev, SYS_RES_IOPORT, fdc->rid_ioport, + fdc->res_ioport); + } + if (fdc->res_drq != 0) { + bus_deactivate_resource(dev, SYS_RES_DRQ, fdc->rid_drq, + fdc->res_drq); + bus_release_resource(dev, SYS_RES_DRQ, fdc->rid_drq, + fdc->res_drq); + } + return (error); } /* - * wire controller into system, look for floppy units + * Aped dfr@freebsd.org's isa_add_device(). */ +static void +fdc_add_device(device_t dev, const char *name, int unit) +{ + int disabled, *ivar; + device_t child; + + ivar = malloc(sizeof *ivar, M_DEVBUF /* XXX */, M_NOWAIT); + if (ivar == 0) + return; + if (resource_int_value(name, unit, "drive", ivar) == 0) + *ivar = 0; + child = device_add_child(dev, name, unit, ivar); + if (child == 0) + return; + if (resource_int_value(name, unit, "disabled", &disabled) == 0) + device_disable(child); +} + static int -fdattach(struct isa_device *dev) +fdc_attach(device_t dev) { - unsigned fdt; - fdu_t fdu; - fdcu_t fdcu = dev->id_unit; - fdc_p fdc = fdc_data + fdcu; - fd_p fd; - int fdsu, st0, st3, i; - struct isa_device *fdup; - int ic_type = 0; -#ifdef DEVFS - int mynor; - int typemynor; - int typesize; -#endif + struct fdc_data *fdc = device_get_softc(dev); + fdcu_t fdcu = device_get_unit(dev); - dev->id_ointr = fdintr; fdc->fdcu = fdcu; fdc->flags |= FDC_ATTACHED; - fdc->dmachan = dev->id_drq; + /* Acquire the DMA channel forever, The driver will do the rest */ + /* XXX should integrate with rman */ isa_dma_acquire(fdc->dmachan); isa_dmainit(fdc->dmachan, 128 << 3 /* XXX max secsize */); fdc->state = DEVIDLE; + /* reset controller, turn motor off, clear fdout mirror reg */ outb(fdc->baseport + FDOUT, ((fdc->fdout = 0))); bufq_init(&fdc->head); - /* check for each floppy drive */ - for (fdup = isa_biotab_fdc; fdup->id_driver != 0; fdup++) { - if (fdup->id_iobase != dev->id_iobase) - continue; - fdu = fdup->id_unit; - fd = &fd_data[fdu]; - if (fdu >= (NFD)) - continue; - fdsu = fdup->id_physid; - /* look up what bios thinks we have */ - switch (fdu) { - case 0: if (dev->id_flags & FDC_PRETEND_D0) - fdt = RTCFDT_144M | RTCFDT_144M_PRETENDED; - else - fdt = (rtcin(RTC_FDISKETTE) & 0xf0); - break; - case 1: fdt = ((rtcin(RTC_FDISKETTE) << 4) & 0xf0); - break; - default: fdt = RTCFDT_NONE; - break; - } - /* is there a unit? */ - if ((fdt == RTCFDT_NONE) - ) { - fd->type = NO_TYPE; - continue; - } +#ifdef FIFO_BEFORE_MOTORON + /* Hmm, this doesn't work here - is set_motor() magic? -Peter */ + if (fdc->fdct != FDC_NE765 && fdc->fdct != FDC_UNKNOWN + && enable_fifo(fdc) == 0) { + device_print_prettyname(dev); + printf("FIFO enabled, %d bytes threshold\n", fifo_threshold); + } +#endif + /* + * Probe and attach any children as were configured above. + */ + return (bus_generic_attach(dev)); +} - /* select it */ - set_motor(fdcu, fdsu, TURNON); - DELAY(1000000); /* 1 sec */ +static void +fdc_print_child(device_t me, device_t child) +{ + printf(" at %s%d drive %d", device_get_name(me), device_get_unit(me), + *(int *)device_get_ivars(child)); +} - if (ic_type == 0 && - fd_cmd(fdcu, 1, NE7CMD_VERSION, 1, &ic_type) == 0) - { -#ifdef FDC_PRINT_BOGUS_CHIPTYPE - printf("fdc%d: ", fdcu); +static int +fd_probe(device_t dev) +{ + int i; + u_int fdt, st0, st3; + struct fd_data *fd; + struct fdc_data *fdc; + fdsu_t fdsu; +#ifndef FIFO_BEFORE_MOTORON + static int fd_fifo = 0; #endif - ic_type = (u_char)ic_type; - switch( ic_type ) { - case 0x80: -#ifdef FDC_PRINT_BOGUS_CHIPTYPE - printf("NEC 765\n"); + + fdsu = *(int *)device_get_ivars(dev); /* xxx cheat a bit... */ + fd = device_get_softc(dev); + fdc = device_get_softc(device_get_parent(dev)); + + bzero(fd, sizeof *fd); + fd->dev = dev; + fd->fdc = fdc; + fd->fdsu = fdsu; + fd->fdu = device_get_unit(dev); + + /* look up what bios thinks we have */ + switch (fd->fdu) { + case 0: + if (isa_get_flags(fdc->fdc_dev) & FDC_PRETEND_D0) + fdt = RTCFDT_144M | RTCFDT_144M_PRETENDED; + else + fdt = (rtcin(RTC_FDISKETTE) & 0xf0); + break; + case 1: + fdt = ((rtcin(RTC_FDISKETTE) << 4) & 0xf0); + break; + default: + fdt = RTCFDT_NONE; + break; + } + + /* is there a unit? */ + if (fdt == RTCFDT_NONE) + return (ENXIO); + + /* select it */ + set_motor(fdc, fdsu, TURNON); + DELAY(1000000); /* 1 sec */ + +#ifndef FIFO_BEFORE_MOTORON + if (fd_fifo == 0 && fdc->fdct != FDC_NE765 && fdc->fdct != FDC_UNKNOWN + && enable_fifo(fdc) == 0) { + device_print_prettyname(device_get_parent(dev)); + printf("FIFO enabled, %d bytes threshold\n", fifo_threshold); + } + fd_fifo = 1; #endif - fdc->fdct = FDC_NE765; - break; - case 0x81: -#ifdef FDC_PRINT_BOGUS_CHIPTYPE - printf("Intel 82077\n"); -#endif - fdc->fdct = FDC_I82077; - break; - case 0x90: -#ifdef FDC_PRINT_BOGUS_CHIPTYPE - printf("NEC 72065B\n"); -#endif - fdc->fdct = FDC_NE72065; - break; - default: -#ifdef FDC_PRINT_BOGUS_CHIPTYPE - printf("unknown IC type %02x\n", ic_type); -#endif - fdc->fdct = FDC_UNKNOWN; - break; - } - if (fdc->fdct != FDC_NE765 && - fdc->fdct != FDC_UNKNOWN && - enable_fifo(fdc) == 0) { - printf("fdc%d: FIFO enabled", fdcu); - printf(", %d bytes threshold\n", - fifo_threshold); - } - } - if ((fd_cmd(fdcu, 2, NE7CMD_SENSED, fdsu, 1, &st3) == 0) && - (st3 & NE7_ST3_T0)) { - /* if at track 0, first seek inwards */ - /* seek some steps: */ - (void)fd_cmd(fdcu, 3, NE7CMD_SEEK, fdsu, 10, 0); - DELAY(300000); /* ...wait a moment... */ - (void)fd_sense_int(fdc, 0, 0); /* make ctrlr happy */ - } - /* If we're at track 0 first seek inwards. */ - if ((fd_sense_drive_status(fdc, &st3) == 0) && - (st3 & NE7_ST3_T0)) { - /* Seek some steps... */ - if (fd_cmd(fdcu, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) { - /* ...wait a moment... */ - DELAY(300000); - /* make ctrlr happy: */ - (void)fd_sense_int(fdc, 0, 0); - } + if ((fd_cmd(fdc, 2, NE7CMD_SENSED, fdsu, 1, &st3) == 0) + && (st3 & NE7_ST3_T0)) { + /* if at track 0, first seek inwards */ + /* seek some steps: */ + fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0); + DELAY(300000); /* ...wait a moment... */ + fd_sense_int(fdc, 0, 0); /* make ctrlr happy */ + } + + /* If we're at track 0 first seek inwards. */ + if ((fd_sense_drive_status(fdc, &st3) == 0) && (st3 & NE7_ST3_T0)) { + /* Seek some steps... */ + if (fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) { + /* ...wait a moment... */ + DELAY(300000); + /* make ctrlr happy: */ + fd_sense_int(fdc, 0, 0); } + } - for(i = 0; i < 2; i++) { - /* - * we must recalibrate twice, just in case the - * heads have been beyond cylinder 76, since most - * FDCs still barf when attempting to recalibrate - * more than 77 steps - */ - /* go back to 0: */ - if (fd_cmd(fdcu, 2, NE7CMD_RECAL, fdsu, 0) == 0) { - /* a second being enough for full stroke seek*/ - DELAY(i == 0? 1000000: 300000); + for (i = 0; i < 2; i++) { + /* + * we must recalibrate twice, just in case the + * heads have been beyond cylinder 76, since most + * FDCs still barf when attempting to recalibrate + * more than 77 steps + */ + /* go back to 0: */ + if (fd_cmd(fdc, 2, NE7CMD_RECAL, fdsu, 0) == 0) { + /* a second being enough for full stroke seek*/ + DELAY(i == 0 ? 1000000 : 300000); - /* anything responding? */ - if (fd_sense_int(fdc, &st0, 0) == 0 && - (st0 & NE7_ST0_EC) == 0) - break; /* already probed succesfully */ - } + /* anything responding? */ + if (fd_sense_int(fdc, &st0, 0) == 0 && + (st0 & NE7_ST0_EC) == 0) + break; /* already probed succesfully */ } + } - set_motor(fdcu, fdsu, TURNOFF); + set_motor(fdc, fdsu, TURNOFF); - if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */ - continue; + if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */ + return (ENXIO); - fd->track = FD_NO_TRACK; - fd->fdc = fdc; - fd->fdsu = fdsu; - fd->options = 0; - callout_handle_init(&fd->toffhandle); - callout_handle_init(&fd->tohandle); - printf("fd%d: ", fdu); + fd->track = FD_NO_TRACK; + fd->fdc = fdc; + fd->fdsu = fdsu; + fd->options = 0; + callout_handle_init(&fd->toffhandle); + callout_handle_init(&fd->tohandle); - switch (fdt) { - case RTCFDT_12M: - printf("1.2MB 5.25in\n"); - fd->type = FD_1200; + switch (fdt) { + case RTCFDT_12M: + device_set_desc(dev, "1200-KB 5.25\" drive"); + fd->type = FD_1200; + break; + case RTCFDT_144M | RTCFDT_144M_PRETENDED: + device_set_desc(dev, "config-pretended 1440-MB 3.5\" drive"); + fdt = RTCFDT_144M; + fd->type = FD_1440; + case RTCFDT_144M: + device_set_desc(dev, "1440-KB 3.5\" drive"); + fd->type = FD_1440; + break; + case RTCFDT_288M: + case RTCFDT_288M_1: + device_set_desc(dev, "2880-KB 3.5\" drive (in 1440-KB mode)"); + fd->type = FD_1440; + break; + case RTCFDT_360K: + device_set_desc(dev, "360-KB 5.25\" drive"); + fd->type = FD_360; + break; + case RTCFDT_720K: + printf("720-KB 3.5\" drive"); + fd->type = FD_720; + break; + default: + return (ENXIO); + } + return (0); +} + +static int +fd_attach(device_t dev) +{ + struct fd_data *fd; + + fd = device_get_softc(dev); + +#ifdef DEVFS /* XXX bitrot */ + mynor = fdu << 6; + fd->bdevs[0] = devfs_add_devswf(&fd_cdevsw, mynor, DV_BLK, + UID_ROOT, GID_OPERATOR, 0640, + "fd%d", fdu); + fd->cdevs[0] = devfs_add_devswf(&fd_cdevsw, mynor, DV_CHR, + UID_ROOT, GID_OPERATOR, 0640, + "rfd%d", fdu); + for (i = 1; i < 1 + NUMDENS; i++) { + /* + * XXX this and the lookup in Fdopen() should be + * data driven. + */ + switch (fd->type) { + case FD_360: + if (i != FD_360) + continue; break; - case RTCFDT_144M | RTCFDT_144M_PRETENDED: - printf("config-pretended "); - fdt = RTCFDT_144M; - /* fallthrough */ - case RTCFDT_144M: - printf("1.44MB 3.5in\n"); - fd->type = FD_1440; + case FD_720: + if (i != FD_720 && i != FD_800 && i != FD_820) + continue; break; - case RTCFDT_288M: - case RTCFDT_288M_1: - printf("2.88MB 3.5in - 1.44MB mode\n"); - fd->type = FD_1440; + case FD_1200: + if (i != FD_360 && i != FD_720 && i != FD_800 + && i != FD_820 && i != FD_1200 + && i != FD_1440 && i != FD_1480) + continue; break; - case RTCFDT_360K: - printf("360KB 5.25in\n"); - fd->type = FD_360; + case FD_1440: + if (i != FD_720 && i != FD_800 && i != FD_820 + && i != FD_1200 && i != FD_1440 + && i != FD_1480 && i != FD_1720) + continue; break; - case RTCFDT_720K: - printf("720KB 3.5in\n"); - fd->type = FD_720; - break; - default: - printf("unknown\n"); - fd->type = NO_TYPE; - continue; } -#ifdef DEVFS - mynor = fdu << 6; - fd->bdevs[0] = devfs_add_devswf(&fd_cdevsw, mynor, DV_BLK, - UID_ROOT, GID_OPERATOR, 0640, - "fd%d", fdu); - fd->cdevs[0] = devfs_add_devswf(&fd_cdevsw, mynor, DV_CHR, - UID_ROOT, GID_OPERATOR, 0640, - "rfd%d", fdu); - for (i = 1; i < 1 + NUMDENS; i++) { - /* - * XXX this and the lookup in Fdopen() should be - * data driven. - */ - switch (fd->type) { - case FD_360: - if (i != FD_360) - continue; - break; - case FD_720: - if (i != FD_720 && i != FD_800 && i != FD_820) - continue; - break; - case FD_1200: - if (i != FD_360 && i != FD_720 && i != FD_800 - && i != FD_820 && i != FD_1200 - && i != FD_1440 && i != FD_1480) - continue; - break; - case FD_1440: - if (i != FD_720 && i != FD_800 && i != FD_820 - && i != FD_1200 && i != FD_1440 - && i != FD_1480 && i != FD_1720) - continue; - break; - } - typesize = fd_types[i - 1].size / 2; - /* - * XXX all these conversions give bloated code and - * confusing names. - */ - if (typesize == 1476) - typesize = 1480; - if (typesize == 1722) - typesize = 1720; - typemynor = mynor | i; - fd->bdevs[i] = - devfs_add_devswf(&fd_cdevsw, typemynor, DV_BLK, - UID_ROOT, GID_OPERATOR, 0640, - "fd%d.%d", fdu, typesize); - fd->cdevs[i] = - devfs_add_devswf(&fd_cdevsw, typemynor, DV_CHR, - UID_ROOT, GID_OPERATOR, 0640, - "rfd%d.%d", fdu, typesize); - } - - for (i = 0; i < MAXPARTITIONS; i++) { - fd->bdevs[1 + NUMDENS + i] = devfs_makelink(fd->bdevs[0], - "fd%d%c", fdu, 'a' + i); - fd->cdevs[1 + NUMDENS + i] = - devfs_makelink(fd->cdevs[0], - "rfd%d%c", fdu, 'a' + i); - } -#endif /* DEVFS */ + typesize = fd_types[i - 1].size / 2; /* - * Export the drive to the devstat interface. + * XXX all these conversions give bloated code and + * confusing names. */ - devstat_add_entry(&fd->device_stats, "fd", - fdu, 512, - DEVSTAT_NO_ORDERED_TAGS, - DEVSTAT_TYPE_FLOPPY | DEVSTAT_TYPE_IF_OTHER, - DEVSTAT_PRIORITY_FD); - + if (typesize == 1476) + typesize = 1480; + if (typesize == 1722) + typesize = 1720; + typemynor = mynor | i; + fd->bdevs[i] = + devfs_add_devswf(&fd_cdevsw, typemynor, DV_BLK, + UID_ROOT, GID_OPERATOR, 0640, + "fd%d.%d", fdu, typesize); + fd->cdevs[i] = + devfs_add_devswf(&fd_cdevsw, typemynor, DV_CHR, + UID_ROOT, GID_OPERATOR, 0640, + "rfd%d.%d", fdu, typesize); } - return (1); + for (i = 0; i < MAXPARTITIONS; i++) { + fd->bdevs[1 + NUMDENS + i] = devfs_makelink(fd->bdevs[0], + "fd%d%c", fdu, 'a' + i); + fd->cdevs[1 + NUMDENS + i] = + devfs_makelink(fd->cdevs[0], + "rfd%d%c", fdu, 'a' + i); + } +#endif /* DEVFS */ + /* + * Export the drive to the devstat interface. + */ + devstat_add_entry(&fd->device_stats, device_get_name(dev), + device_get_unit(dev), 512, DEVSTAT_NO_ORDERED_TAGS, + DEVSTAT_TYPE_FLOPPY | DEVSTAT_TYPE_IF_OTHER, + DEVSTAT_PRIORITY_FD); + return (0); } - - #ifdef FDC_YE /* * this is a subset of fdattach() optimized for the Y-E Data * PCMCIA floppy drive. */ static int yeattach(struct isa_device *dev) { fdcu_t fdcu = dev->id_unit; fdc_p fdc = fdc_data + fdcu; fdsu_t fdsu = 0; /* assume 1 drive per YE controller */ fdu_t fdu; fd_p fd; int st0, st3, i; #ifdef DEVFS int mynor; int typemynor; int typesize; #endif fdc->fdcu = fdcu; /* * the FDC_PCMCIA flag is used to to indicate special PIO is used * instead of DMA */ fdc->flags = FDC_ATTACHED|FDC_PCMCIA; fdc->state = DEVIDLE; /* reset controller, turn motor off, clear fdout mirror reg */ outb(fdc->baseport + FDOUT, ((fdc->fdout = 0))); bufq_init(&fdc->head); /* * assume 2 drives/ "normal" controller */ fdu = fdcu * 2; if (fdu >= NFD) { printf("fdu %d >= NFD\n",fdu); return(0); }; fd = &fd_data[fdu]; set_motor(fdcu, fdsu, TURNON); DELAY(1000000); /* 1 sec */ fdc->fdct = FDC_NE765; if ((fd_cmd(fdcu, 2, NE7CMD_SENSED, fdsu, 1, &st3) == 0) && (st3 & NE7_ST3_T0)) { /* if at track 0, first seek inwards */ /* seek some steps: */ (void)fd_cmd(fdcu, 3, NE7CMD_SEEK, fdsu, 10, 0); DELAY(300000); /* ...wait a moment... */ (void)fd_sense_int(fdc, 0, 0); /* make ctrlr happy */ } /* If we're at track 0 first seek inwards. */ if ((fd_sense_drive_status(fdc, &st3) == 0) && (st3 & NE7_ST3_T0)) { /* Seek some steps... */ if (fd_cmd(fdcu, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) { /* ...wait a moment... */ DELAY(300000); /* make ctrlr happy: */ (void)fd_sense_int(fdc, 0, 0); } } for(i = 0; i < 2; i++) { /* * we must recalibrate twice, just in case the * heads have been beyond cylinder 76, since most * FDCs still barf when attempting to recalibrate * more than 77 steps */ /* go back to 0: */ if (fd_cmd(fdcu, 2, NE7CMD_RECAL, fdsu, 0) == 0) { /* a second being enough for full stroke seek*/ DELAY(i == 0? 1000000: 300000); /* anything responding? */ if (fd_sense_int(fdc, &st0, 0) == 0 && (st0 & NE7_ST0_EC) == 0) break; /* already probed succesfully */ } } set_motor(fdcu, fdsu, TURNOFF); if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */ return(0); fd->track = FD_NO_TRACK; fd->fdc = fdc; fd->fdsu = fdsu; fd->options = 0; printf("fdc%d: 1.44MB 3.5in PCMCIA\n", fdcu); fd->type = FD_1440; #ifdef DEVFS mynor = fdcu << 6; fd->bdevs[0] = devfs_add_devswf(&fd_cdevsw, mynor, DV_BLK, UID_ROOT, GID_OPERATOR, 0640, "fd%d", fdu); fd->cdevs[0] = devfs_add_devswf(&fd_cdevsw, mynor, DV_CHR, UID_ROOT, GID_OPERATOR, 0640, "rfd%d", fdu); /* * XXX this and the lookup in Fdopen() should be * data driven. */ typemynor = mynor | FD_1440; typesize = fd_types[FD_1440 - 1].size / 2; /* * XXX all these conversions give bloated code and * confusing names. */ if (typesize == 1476) typesize = 1480; if (typesize == 1722) typesize = 1720; fd->bdevs[FD_1440] = devfs_add_devswf(&fd_cdevsw, typemynor, DV_BLK, UID_ROOT, GID_OPERATOR, 0640, "fd%d.%d", fdu, typesize); fd->cdevs[FD_1440] = devfs_add_devswf(&fd_cdevsw, typemynor, DV_CHR, UID_ROOT, GID_OPERATOR, 0640,"rfd%d.%d", fdu, typesize); for (i = 0; i < MAXPARTITIONS; i++) { fd->bdevs[1 + NUMDENS + i] = devfs_makelink(fd->bdevs[0], "fd%d%c", fdu, 'a' + i); fd->cdevs[1 + NUMDENS + i] = devfs_makelink(fd->cdevs[0], "rfd%d%c", fdu, 'a' + i); } #endif /* DEVFS */ return (1); } #endif /****************************************************************************/ /* motor control stuff */ /* remember to not deselect the drive we're working on */ /****************************************************************************/ static void -set_motor(fdcu_t fdcu, int fdsu, int turnon) +set_motor(struct fdc_data *fdc, int fdsu, int turnon) { - int fdout = fdc_data[fdcu].fdout; + int fdout = fdc->fdout; int needspecify = 0; if(turnon) { fdout &= ~FDO_FDSEL; fdout |= (FDO_MOEN0 << fdsu) + fdsu; } else fdout &= ~(FDO_MOEN0 << fdsu); if(!turnon && (fdout & (FDO_MOEN0+FDO_MOEN1+FDO_MOEN2+FDO_MOEN3)) == 0) /* gonna turn off the last drive, put FDC to bed */ fdout &= ~ (FDO_FRST|FDO_FDMAEN); else { /* make sure controller is selected and specified */ if((fdout & (FDO_FRST|FDO_FDMAEN)) == 0) needspecify = 1; fdout |= (FDO_FRST|FDO_FDMAEN); } - outb(fdc_data[fdcu].baseport+FDOUT, fdout); - fdc_data[fdcu].fdout = fdout; + outb(fdc->baseport+FDOUT, fdout); + fdc->fdout = fdout; TRACE1("[0x%x->FDOUT]", fdout); - if(needspecify) { + if (needspecify) { /* * XXX * special case: since we have just woken up the FDC * from its sleep, we silently assume the command will * be accepted, and do not test for a timeout */ - (void)fd_cmd(fdcu, 3, NE7CMD_SPECIFY, + (void)fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), 0); - if (fdc_data[fdcu].flags & FDC_HAS_FIFO) - (void) enable_fifo(&fdc_data[fdcu]); + if (fdc->flags & FDC_HAS_FIFO) + (void) enable_fifo(fdc); } } static void -fd_turnoff(void *arg1) +fd_turnoff(void *xfd) { - fdu_t fdu = (fdu_t)arg1; int s; - fd_p fd = fd_data + fdu; + fd_p fd = xfd; - TRACE1("[fd%d: turnoff]", fdu); + TRACE1("[fd%d: turnoff]", fd->fdu); /* * Don't turn off the motor yet if the drive is active. * XXX shouldn't even schedule turnoff until drive is inactive * and nothing is queued on it. */ - if (fd->fdc->state != DEVIDLE && fd->fdc->fdu == fdu) { - fd->toffhandle = timeout(fd_turnoff, arg1, 4 * hz); + if (fd->fdc->state != DEVIDLE && fd->fdc->fdu == fd->fdu) { + fd->toffhandle = timeout(fd_turnoff, fd, 4 * hz); return; } s = splbio(); fd->flags &= ~FD_MOTOR; - set_motor(fd->fdc->fdcu, fd->fdsu, TURNOFF); + set_motor(fd->fdc, fd->fdsu, TURNOFF); splx(s); } static void -fd_motor_on(void *arg1) +fd_motor_on(void *xfd) { - fdu_t fdu = (fdu_t)arg1; int s; + fd_p fd = xfd; - fd_p fd = fd_data + fdu; s = splbio(); fd->flags &= ~FD_MOTOR_WAIT; if((fd->fdc->fd == fd) && (fd->fdc->state == MOTORWAIT)) { - fdintr(fd->fdc->fdcu); + fdc_intr(fd->fdc); } splx(s); } static void -fd_turnon(fdu_t fdu) +fd_turnon(fd_p fd) { - fd_p fd = fd_data + fdu; if(!(fd->flags & FD_MOTOR)) { fd->flags |= (FD_MOTOR + FD_MOTOR_WAIT); - set_motor(fd->fdc->fdcu, fd->fdsu, TURNON); - timeout(fd_motor_on, (caddr_t)fdu, hz); /* in 1 sec its ok */ + set_motor(fd->fdc, fd->fdsu, TURNON); + timeout(fd_motor_on, fd, hz); /* in 1 sec its ok */ } } static void fdc_reset(fdc_p fdc) { - fdcu_t fdcu = fdc->fdcu; - /* Try a reset, keep motor on */ outb(fdc->baseport + FDOUT, fdc->fdout & ~(FDO_FRST|FDO_FDMAEN)); TRACE1("[0x%x->FDOUT]", fdc->fdout & ~(FDO_FRST|FDO_FDMAEN)); DELAY(100); /* enable FDC, but defer interrupts a moment */ outb(fdc->baseport + FDOUT, fdc->fdout & ~FDO_FDMAEN); TRACE1("[0x%x->FDOUT]", fdc->fdout & ~FDO_FDMAEN); DELAY(100); outb(fdc->baseport + FDOUT, fdc->fdout); TRACE1("[0x%x->FDOUT]", fdc->fdout); /* XXX after a reset, silently believe the FDC will accept commands */ - (void)fd_cmd(fdcu, 3, NE7CMD_SPECIFY, + (void)fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), 0); if (fdc->flags & FDC_HAS_FIFO) (void) enable_fifo(fdc); } /****************************************************************************/ /* fdc in/out */ /****************************************************************************/ int -in_fdc(fdcu_t fdcu) +in_fdc(struct fdc_data *fdc) { - int baseport = fdc_data[fdcu].baseport; + int baseport = fdc->baseport; int i, j = 100000; while ((i = inb(baseport+FDSTS) & (NE7_DIO|NE7_RQM)) != (NE7_DIO|NE7_RQM) && j-- > 0) if (i == NE7_RQM) - return fdc_err(fdcu, "ready for output in input\n"); + return fdc_err(fdc, "ready for output in input\n"); if (j <= 0) - return fdc_err(fdcu, bootverbose? "input ready timeout\n": 0); + return fdc_err(fdc, bootverbose? "input ready timeout\n": 0); #ifdef FDC_DEBUG i = inb(baseport+FDDATA); TRACE1("[FDDATA->0x%x]", (unsigned char)i); return(i); #else /* !FDC_DEBUG */ return inb(baseport+FDDATA); #endif /* FDC_DEBUG */ } /* * fd_in: Like in_fdc, but allows you to see if it worked. */ static int -fd_in(fdcu_t fdcu, int *ptr) +fd_in(struct fdc_data *fdc, int *ptr) { - int baseport = fdc_data[fdcu].baseport; + int baseport = fdc->baseport; int i, j = 100000; while ((i = inb(baseport+FDSTS) & (NE7_DIO|NE7_RQM)) != (NE7_DIO|NE7_RQM) && j-- > 0) if (i == NE7_RQM) - return fdc_err(fdcu, "ready for output in input\n"); + return fdc_err(fdc, "ready for output in input\n"); if (j <= 0) - return fdc_err(fdcu, bootverbose? "input ready timeout\n": 0); + return fdc_err(fdc, bootverbose? "input ready timeout\n": 0); #ifdef FDC_DEBUG i = inb(baseport+FDDATA); TRACE1("[FDDATA->0x%x]", (unsigned char)i); *ptr = i; return 0; #else /* !FDC_DEBUG */ i = inb(baseport+FDDATA); if (ptr) *ptr = i; return 0; #endif /* FDC_DEBUG */ } int -out_fdc(fdcu_t fdcu, int x) +out_fdc(struct fdc_data *fdc, int x) { - int baseport = fdc_data[fdcu].baseport; + int baseport = fdc->baseport; int i; /* Check that the direction bit is set */ i = 100000; while ((inb(baseport+FDSTS) & NE7_DIO) && i-- > 0); - if (i <= 0) return fdc_err(fdcu, "direction bit not set\n"); + if (i <= 0) return fdc_err(fdc, "direction bit not set\n"); /* Check that the floppy controller is ready for a command */ i = 100000; while ((inb(baseport+FDSTS) & NE7_RQM) == 0 && i-- > 0); if (i <= 0) - return fdc_err(fdcu, bootverbose? "output ready timeout\n": 0); + return fdc_err(fdc, bootverbose? "output ready timeout\n": 0); /* Send the command and return */ outb(baseport+FDDATA, x); TRACE1("[0x%x->FDDATA]", x); return (0); } /****************************************************************************/ /* fdopen/fdclose */ /****************************************************************************/ int Fdopen(dev_t dev, int flags, int mode, struct proc *p) { fdu_t fdu = FDUNIT(minor(dev)); int type = FDTYPE(minor(dev)); + fd_p fd; fdc_p fdc; /* check bounds */ - if (fdu >= NFD) - return(ENXIO); - fdc = fd_data[fdu].fdc; - if ((fdc == NULL) || (fd_data[fdu].type == NO_TYPE)) - return(ENXIO); + if ((fd = devclass_get_softc(fd_devclass, fdu)) == 0) + return (ENXIO); + fdc = fd->fdc; + if ((fdc == NULL) || (fd->type == NO_TYPE)) + return (ENXIO); if (type > NUMDENS) - return(ENXIO); + return (ENXIO); if (type == 0) - type = fd_data[fdu].type; + type = fd->type; else { /* * For each type of basic drive, make sure we are trying * to open a type it can do, */ - if (type != fd_data[fdu].type) { - switch (fd_data[fdu].type) { + if (type != fd->type) { + switch (fd->type) { case FD_360: - return(ENXIO); + return (ENXIO); case FD_720: if ( type != FD_820 && type != FD_800 ) - return(ENXIO); + return (ENXIO); break; case FD_1200: switch (type) { case FD_1480: type = FD_1480in5_25; break; case FD_1440: type = FD_1440in5_25; break; case FD_820: type = FD_820in5_25; break; case FD_800: type = FD_800in5_25; break; case FD_720: type = FD_720in5_25; break; case FD_360: type = FD_360in5_25; break; default: return(ENXIO); } break; case FD_1440: if ( type != FD_1720 && type != FD_1480 && type != FD_1200 && type != FD_820 && type != FD_800 && type != FD_720 ) return(ENXIO); break; } } } - fd_data[fdu].ft = fd_types + type - 1; - fd_data[fdu].flags |= FD_OPEN; - + fd->ft = fd_types + type - 1; + fd->flags |= FD_OPEN; + device_busy(fd->dev); + device_busy(fd->fdc->fdc_dev); return 0; } int fdclose(dev_t dev, int flags, int mode, struct proc *p) { fdu_t fdu = FDUNIT(minor(dev)); + struct fd_data *fd; - fd_data[fdu].flags &= ~FD_OPEN; - fd_data[fdu].options &= ~FDOPT_NORETRY; + fd = devclass_get_softc(fd_devclass, fdu); + fd->flags &= ~FD_OPEN; + fd->options &= ~FDOPT_NORETRY; - return(0); + return (0); } static int fdread(dev_t dev, struct uio *uio, int ioflag) { return (physio(fdstrategy, NULL, dev, 1, minphys, uio)); } static int fdwrite(dev_t dev, struct uio *uio, int ioflag) { return (physio(fdstrategy, NULL, dev, 0, minphys, uio)); } /****************************************************************************/ /* fdstrategy */ /****************************************************************************/ void fdstrategy(struct buf *bp) { unsigned nblocks, blknum, cando; int s; - fdcu_t fdcu; fdu_t fdu; fdc_p fdc; fd_p fd; size_t fdblk; fdu = FDUNIT(minor(bp->b_dev)); - fd = &fd_data[fdu]; + fd = devclass_get_softc(fd_devclass, fdu); + if (fd == 0) + panic("fdstrategy: buf for nonexistent device (%#lx, %#lx)", + (u_long)major(bp->b_dev), (u_long)minor(bp->b_dev)); fdc = fd->fdc; - fdcu = fdc->fdcu; #ifdef FDC_YE if (fd->type == NO_TYPE) { bp->b_error = ENXIO; bp->b_flags |= B_ERROR; /* * I _refuse_ to use a goto */ biodone(bp); return; }; #endif fdblk = 128 << (fd->ft->secsize); if (!(bp->b_flags & B_FORMAT)) { - if ((fdu >= NFD) || (bp->b_blkno < 0)) { + if (bp->b_blkno < 0) { printf( "fd%d: fdstrat: bad request blkno = %lu, bcount = %ld\n", fdu, (u_long)bp->b_blkno, bp->b_bcount); bp->b_error = EINVAL; bp->b_flags |= B_ERROR; goto bad; } if ((bp->b_bcount % fdblk) != 0) { bp->b_error = EINVAL; bp->b_flags |= B_ERROR; goto bad; } } /* * Set up block calculations. */ if (bp->b_blkno > 20000000) { /* * Reject unreasonably high block number, prevent the * multiplication below from overflowing. */ bp->b_error = EINVAL; bp->b_flags |= B_ERROR; goto bad; } blknum = (unsigned) bp->b_blkno * DEV_BSIZE/fdblk; nblocks = fd->ft->size; bp->b_resid = 0; if (blknum + (bp->b_bcount / fdblk) > nblocks) { if (blknum <= nblocks) { cando = (nblocks - blknum) * fdblk; bp->b_resid = bp->b_bcount - cando; if (cando == 0) goto bad; /* not actually bad but EOF */ } else { bp->b_error = EINVAL; bp->b_flags |= B_ERROR; goto bad; } } bp->b_pblkno = bp->b_blkno; s = splbio(); bufqdisksort(&fdc->head, bp); - untimeout(fd_turnoff, (caddr_t)fdu, fd->toffhandle); /* a good idea */ + untimeout(fd_turnoff, fd, fd->toffhandle); /* a good idea */ /* Tell devstat we are starting on the transaction */ devstat_start_transaction(&fd->device_stats); - fdstart(fdcu); + fdstart(fdc); splx(s); return; bad: biodone(bp); } /***************************************************************\ * fdstart * * We have just queued something.. if the controller is not busy * * then simulate the case where it has just finished a command * * So that it (the interrupt routine) looks on the queue for more* * work to do and picks up what we just added. * * If the controller is already busy, we need do nothing, as it * * will pick up our work when the present work completes * \***************************************************************/ static void -fdstart(fdcu_t fdcu) +fdstart(struct fdc_data *fdc) { int s; s = splbio(); - if(fdc_data[fdcu].state == DEVIDLE) + if(fdc->state == DEVIDLE) { - fdintr(fdcu); + fdc_intr(fdc); } splx(s); } static void -fd_iotimeout(void *arg1) +fd_iotimeout(void *xfdc) { fdc_p fdc; - fdcu_t fdcu; int s; - fdcu = (fdcu_t)arg1; - fdc = fdc_data + fdcu; + fdc = xfdc; TRACE1("fd%d[fd_iotimeout()]", fdc->fdu); /* * Due to IBM's brain-dead design, the FDC has a faked ready * signal, hardwired to ready == true. Thus, any command * issued if there's no diskette in the drive will _never_ * complete, and must be aborted by resetting the FDC. * Many thanks, Big Blue! * The FDC must not be reset directly, since that would * interfere with the state machine. Instead, pretend that * the command completed but was invalid. The state machine * will reset the FDC and retry once. */ s = splbio(); fdc->status[0] = NE7_ST0_IC_IV; fdc->flags &= ~FDC_STAT_VALID; fdc->state = IOTIMEDOUT; - fdintr(fdcu); + fdc_intr(fdc); splx(s); } /* just ensure it has the right spl */ static void -fd_pseudointr(void *arg1) +fd_pseudointr(void *xfdc) { - fdcu_t fdcu = (fdcu_t)arg1; int s; s = splbio(); - fdintr(fdcu); + fdc_intr(xfdc); splx(s); } /***********************************************************************\ * fdintr * * keep calling the state machine until it returns a 0 * * ALWAYS called at SPLBIO * \***********************************************************************/ static void -fdintr(fdcu_t fdcu) +fdc_intr(void *xfdc) { - fdc_p fdc = fdc_data + fdcu; - while(fdstate(fdcu, fdc)) - ; + fdc_p fdc = xfdc; + while(fdstate(fdc)) + ; } #ifdef FDC_YE /* * magic pseudo-DMA initialization for YE FDC. Sets count and * direction */ #define SET_BCDR(wr,cnt,port) outb(port,(((cnt)-1) & 0xff)); \ outb(port+1,((wr ? 0x80 : 0) | ((((cnt)-1) >> 8) & 0x7f))) /* * fdcpio(): perform programmed IO read/write for YE PCMCIA floppy */ static int fdcpio(fdcu_t fdcu, long flags, caddr_t addr, u_int count) { u_char *cptr = (u_char *)addr; fdc_p fdc = &fdc_data[fdcu]; int io = fdc->baseport; if (flags & B_READ) { if (fdc->state != PIOREAD) { fdc->state = PIOREAD; return(0); }; SET_BCDR(0,count,io); insb(io+FDC_YE_DATAPORT,cptr,count); } else { outsb(io+FDC_YE_DATAPORT,cptr,count); SET_BCDR(0,count,io); }; return(1); } #endif /* FDC_YE */ /***********************************************************************\ * The controller state machine. * * if it returns a non zero value, it should be called again immediatly * \***********************************************************************/ static int -fdstate(fdcu_t fdcu, fdc_p fdc) +fdstate(fdc_p fdc) { int read, format, head, i, sec = 0, sectrac, st0, cyl, st3; unsigned blknum = 0, b_cylinder = 0; fdu_t fdu = fdc->fdu; fd_p fd; register struct buf *bp; struct fd_formb *finfo = NULL; size_t fdblk; bp = fdc->bp; if (bp == NULL) { bp = bufq_first(&fdc->head); if (bp != NULL) { bufq_remove(&fdc->head, bp); fdc->bp = bp; } } if (bp == NULL) { /***********************************************\ * nothing left for this controller to do * * Force into the IDLE state, * \***********************************************/ fdc->state = DEVIDLE; - if(fdc->fd) - { - printf("fd%d: unexpected valid fd pointer\n", - fdc->fdu); + if (fdc->fd) { + device_print_prettyname(fdc->fdc_dev); + printf("unexpected valid fd pointer\n"); fdc->fd = (fd_p) 0; fdc->fdu = -1; } - TRACE1("[fdc%d IDLE]", fdcu); - return(0); + TRACE1("[fdc%d IDLE]", fdc->fdcu); + return (0); } fdu = FDUNIT(minor(bp->b_dev)); - fd = fd_data + fdu; + fd = devclass_get_softc(fd_devclass, fdu); fdblk = 128 << fd->ft->secsize; - if (fdc->fd && (fd != fdc->fd)) - { - printf("fd%d: confused fd pointers\n", fdu); + if (fdc->fd && (fd != fdc->fd)) { + device_print_prettyname(fd->dev); + printf("confused fd pointers\n"); } read = bp->b_flags & B_READ; format = bp->b_flags & B_FORMAT; - if(format) { + if (format) { finfo = (struct fd_formb *)bp->b_data; fd->skip = (char *)&(finfo->fd_formb_cylno(0)) - (char *)finfo; } if (fdc->state == DOSEEK || fdc->state == SEEKCOMPLETE) { blknum = (unsigned) bp->b_pblkno * DEV_BSIZE/fdblk + fd->skip/fdblk; b_cylinder = blknum / (fd->ft->sectrac * fd->ft->heads); } TRACE1("fd%d", fdu); TRACE1("[%s]", fdstates[fdc->state]); TRACE1("(0x%x)", fd->flags); - untimeout(fd_turnoff, (caddr_t)fdu, fd->toffhandle); - fd->toffhandle = timeout(fd_turnoff, (caddr_t)fdu, 4 * hz); + untimeout(fd_turnoff, fd, fd->toffhandle); + fd->toffhandle = timeout(fd_turnoff, fd, 4 * hz); switch (fdc->state) { case DEVIDLE: case FINDWORK: /* we have found new work */ fdc->retry = 0; fd->skip = 0; fdc->fd = fd; fdc->fdu = fdu; outb(fdc->baseport+FDCTL, fd->ft->trans); TRACE1("[0x%x->FDCTL]", fd->ft->trans); /*******************************************************\ * If the next drive has a motor startup pending, then * * it will start up in its own good time * \*******************************************************/ - if(fd->flags & FD_MOTOR_WAIT) - { + if(fd->flags & FD_MOTOR_WAIT) { fdc->state = MOTORWAIT; - return(0); /* come back later */ + return (0); /* come back later */ } /*******************************************************\ * Maybe if it's not starting, it SHOULD be starting * \*******************************************************/ if (!(fd->flags & FD_MOTOR)) { fdc->state = MOTORWAIT; - fd_turnon(fdu); - return(0); + fd_turnon(fd); + return (0); } else /* at least make sure we are selected */ { - set_motor(fdcu, fd->fdsu, TURNON); + set_motor(fdc, fd->fdsu, TURNON); } if (fdc->flags & FDC_NEEDS_RESET) { fdc->state = RESETCTLR; fdc->flags &= ~FDC_NEEDS_RESET; } else fdc->state = DOSEEK; break; case DOSEEK: if (b_cylinder == (unsigned)fd->track) { fdc->state = SEEKCOMPLETE; break; } - if (fd_cmd(fdcu, 3, NE7CMD_SEEK, + if (fd_cmd(fdc, 3, NE7CMD_SEEK, fd->fdsu, b_cylinder * fd->ft->steptrac, 0)) { /* * seek command not accepted, looks like * the FDC went off to the Saints... */ fdc->retry = 6; /* try a reset */ - return(retrier(fdcu)); + return(retrier(fdc)); } fd->track = FD_NO_TRACK; fdc->state = SEEKWAIT; return(0); /* will return later */ case SEEKWAIT: /* allow heads to settle */ - timeout(fd_pseudointr, (caddr_t)fdcu, hz / 16); + timeout(fd_pseudointr, fdc, hz / 16); fdc->state = SEEKCOMPLETE; return(0); /* will return later */ case SEEKCOMPLETE : /* SEEK DONE, START DMA */ /* Make sure seek really happened*/ - if(fd->track == FD_NO_TRACK) - { + if(fd->track == FD_NO_TRACK) { int descyl = b_cylinder * fd->ft->steptrac; do { /* * This might be a "ready changed" interrupt, * which cannot really happen since the * RDY pin is hardwired to + 5 volts. This * generally indicates a "bouncing" intr * line, so do one of the following: * * When running on an enhanced FDC that is * known to not go stuck after responding * with INVALID, fetch all interrupt states * until seeing either an INVALID or a * real interrupt condition. * * When running on a dumb old NE765, give * up immediately. The controller will * provide up to four dummy RC interrupt * conditions right after reset (for the * corresponding four drives), so this is * our only chance to get notice that it * was not the FDC that caused the interrupt. */ if (fd_sense_int(fdc, &st0, &cyl) == FD_NOT_VALID) return 0; if(fdc->fdct == FDC_NE765 && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC) return 0; /* hope for a real intr */ } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC); - if (0 == descyl) - { + if (0 == descyl) { int failed = 0; /* * seek to cyl 0 requested; make sure we are * really there */ if (fd_sense_drive_status(fdc, &st3)) failed = 1; if ((st3 & NE7_ST3_T0) == 0) { printf( "fd%d: Seek to cyl 0, but not really there (ST3 = %b)\n", fdu, st3, NE7_ST3BITS); failed = 1; } - if (failed) - { + if (failed) { if(fdc->retry < 3) fdc->retry = 3; - return(retrier(fdcu)); + return (retrier(fdc)); } } - if (cyl != descyl) - { + if (cyl != descyl) { printf( "fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n", fdu, descyl, cyl, st0); if (fdc->retry < 3) fdc->retry = 3; - return(retrier(fdcu)); + return (retrier(fdc)); } } fd->track = b_cylinder; #ifdef FDC_YE if (!(fdc->flags & FDC_PCMCIA)) #endif isa_dmastart(bp->b_flags, bp->b_data+fd->skip, format ? bp->b_bcount : fdblk, fdc->dmachan); sectrac = fd->ft->sectrac; sec = blknum % (sectrac * fd->ft->heads); head = sec / sectrac; sec = sec % sectrac + 1; fd->hddrv = ((head&1)<<2)+fdu; if(format || !read) { /* make sure the drive is writable */ if(fd_sense_drive_status(fdc, &st3) != 0) { /* stuck controller? */ isa_dmadone(bp->b_flags, bp->b_data + fd->skip, format ? bp->b_bcount : fdblk, fdc->dmachan); fdc->retry = 6; /* reset the beast */ - return(retrier(fdcu)); + return (retrier(fdc)); } if(st3 & NE7_ST3_WP) { /* * XXX YES! this is ugly. * in order to force the current operation * to fail, we will have to fake an FDC * error - all error handling is done * by the retrier() */ fdc->status[0] = NE7_ST0_IC_AT; fdc->status[1] = NE7_ST1_NW; fdc->status[2] = 0; fdc->status[3] = fd->track; fdc->status[4] = head; fdc->status[5] = sec; fdc->retry = 8; /* break out immediately */ fdc->state = IOTIMEDOUT; /* not really... */ return (1); } } - if(format) - { + if (format) { #ifdef FDC_YE if (fdc->flags & FDC_PCMCIA) (void)fdcpio(fdcu,bp->b_flags, bp->b_data+fd->skip, bp->b_bcount); #endif /* formatting */ - if(fd_cmd(fdcu, 6, - NE7CMD_FORMAT, - head << 2 | fdu, + if(fd_cmd(fdc, 6, NE7CMD_FORMAT, head << 2 | fdu, finfo->fd_formb_secshift, finfo->fd_formb_nsecs, finfo->fd_formb_gaplen, - finfo->fd_formb_fillbyte, - 0)) - { + finfo->fd_formb_fillbyte, 0)) { /* controller fell over */ isa_dmadone(bp->b_flags, bp->b_data + fd->skip, format ? bp->b_bcount : fdblk, fdc->dmachan); fdc->retry = 6; - return(retrier(fdcu)); + return (retrier(fdc)); } - } - else - { + } else { #ifdef FDC_YE if (fdc->flags & FDC_PCMCIA) { /* * this seems to be necessary even when * reading data */ SET_BCDR(1,fdblk,fdc->baseport); /* * perform the write pseudo-DMA before * the WRITE command is sent */ if (!read) (void)fdcpio(fdcu,bp->b_flags, bp->b_data+fd->skip, fdblk); } #endif - if (fd_cmd(fdcu, 9, + if (fd_cmd(fdc, 9, (read ? NE7CMD_READ : NE7CMD_WRITE), head << 2 | fdu, /* head & unit */ fd->track, /* track */ head, sec, /* sector + 1 */ fd->ft->secsize, /* sector size */ sectrac, /* sectors/track */ fd->ft->gap, /* gap size */ fd->ft->datalen, /* data length */ - 0)) - { + 0)) { /* the beast is sleeping again */ isa_dmadone(bp->b_flags, bp->b_data + fd->skip, format ? bp->b_bcount : fdblk, fdc->dmachan); fdc->retry = 6; - return(retrier(fdcu)); + return (retrier(fdc)); } } #ifdef FDC_YE if (fdc->flags & FDC_PCMCIA) /* * if this is a read, then simply await interrupt * before performing PIO */ if (read && !fdcpio(fdcu,bp->b_flags, bp->b_data+fd->skip,fdblk)) { fd->tohandle = timeout(fd_iotimeout, (caddr_t)fdcu, hz); return(0); /* will return later */ }; /* * write (or format) operation will fall through and * await completion interrupt */ #endif fdc->state = IOCOMPLETE; - fd->tohandle = timeout(fd_iotimeout, (caddr_t)fdcu, hz); - return(0); /* will return later */ + fd->tohandle = timeout(fd_iotimeout, fdc, hz); + return (0); /* will return later */ #ifdef FDC_YE case PIOREAD: /* * actually perform the PIO read. The IOCOMPLETE case * removes the timeout for us. */ (void)fdcpio(fdcu,bp->b_flags,bp->b_data+fd->skip,fdblk); fdc->state = IOCOMPLETE; /* FALLTHROUGH */ #endif case IOCOMPLETE: /* IO DONE, post-analyze */ - untimeout(fd_iotimeout, (caddr_t)fdcu, fd->tohandle); + untimeout(fd_iotimeout, fdc, fd->tohandle); - if (fd_read_status(fdc, fd->fdsu)) - { + if (fd_read_status(fdc, fd->fdsu)) { isa_dmadone(bp->b_flags, bp->b_data + fd->skip, format ? bp->b_bcount : fdblk, fdc->dmachan); if (fdc->retry < 6) fdc->retry = 6; /* force a reset */ - return retrier(fdcu); + return (retrier(fdc)); } fdc->state = IOTIMEDOUT; /* FALLTHROUGH */ case IOTIMEDOUT: #ifdef FDC_YE if (!(fdc->flags & FDC_PCMCIA)) #endif isa_dmadone(bp->b_flags, bp->b_data + fd->skip, format ? bp->b_bcount : fdblk, fdc->dmachan); - if (fdc->status[0] & NE7_ST0_IC) - { + if (fdc->status[0] & NE7_ST0_IC) { if ((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT && fdc->status[1] & NE7_ST1_OR) { /* * DMA overrun. Someone hogged the bus * and didn't release it in time for the * next FDC transfer. * Just restart it, don't increment retry * count. (vak) */ fdc->state = SEEKCOMPLETE; return (1); } else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_IV && fdc->retry < 6) fdc->retry = 6; /* force a reset */ else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT && fdc->status[2] & NE7_ST2_WC && fdc->retry < 3) fdc->retry = 3; /* force recalibrate */ - return(retrier(fdcu)); + return (retrier(fdc)); } /* All OK */ fd->skip += fdblk; - if (!format && fd->skip < bp->b_bcount - bp->b_resid) - { + if (!format && fd->skip < bp->b_bcount - bp->b_resid) { /* set up next transfer */ fdc->state = DOSEEK; - } - else - { + } else { /* ALL DONE */ fd->skip = 0; fdc->bp = NULL; /* Tell devstat we have finished with the transaction */ devstat_end_transaction(&fd->device_stats, bp->b_bcount - bp->b_resid, DEVSTAT_TAG_NONE, (bp->b_flags & B_READ) ? DEVSTAT_READ : DEVSTAT_WRITE); biodone(bp); fdc->fd = (fd_p) 0; fdc->fdu = -1; fdc->state = FINDWORK; } - return(1); + return (1); case RESETCTLR: fdc_reset(fdc); fdc->retry++; fdc->state = RESETCOMPLETE; return (0); case RESETCOMPLETE: /* * Discard all the results from the reset so that they * can't cause an unexpected interrupt later. */ for (i = 0; i < 4; i++) (void)fd_sense_int(fdc, &st0, &cyl); fdc->state = STARTRECAL; /* Fall through. */ case STARTRECAL: - if(fd_cmd(fdcu, - 2, NE7CMD_RECAL, fdu, - 0)) /* Recalibrate Function */ - { + if(fd_cmd(fdc, 2, NE7CMD_RECAL, fdu, 0)) { /* arrgl */ fdc->retry = 6; - return(retrier(fdcu)); + return (retrier(fdc)); } fdc->state = RECALWAIT; - return(0); /* will return later */ + return (0); /* will return later */ case RECALWAIT: /* allow heads to settle */ - timeout(fd_pseudointr, (caddr_t)fdcu, hz / 8); + timeout(fd_pseudointr, fdc, hz / 8); fdc->state = RECALCOMPLETE; - return(0); /* will return later */ + return (0); /* will return later */ case RECALCOMPLETE: do { /* * See SEEKCOMPLETE for a comment on this: */ if (fd_sense_int(fdc, &st0, &cyl) == FD_NOT_VALID) return 0; if(fdc->fdct == FDC_NE765 && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC) return 0; /* hope for a real intr */ } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC); if ((st0 & NE7_ST0_IC) != NE7_ST0_IC_NT || cyl != 0) { if(fdc->retry > 3) /* * a recalibrate from beyond cylinder 77 * will "fail" due to the FDC limitations; * since people used to complain much about * the failure message, try not logging * this one if it seems to be the first * time in a line */ printf("fd%d: recal failed ST0 %b cyl %d\n", fdu, st0, NE7_ST0BITS, cyl); if(fdc->retry < 3) fdc->retry = 3; - return(retrier(fdcu)); + return (retrier(fdc)); } fd->track = 0; /* Seek (probably) necessary */ fdc->state = DOSEEK; - return(1); /* will return immediatly */ + return (1); /* will return immediatly */ case MOTORWAIT: if(fd->flags & FD_MOTOR_WAIT) { - return(0); /* time's not up yet */ + return (0); /* time's not up yet */ } if (fdc->flags & FDC_NEEDS_RESET) { fdc->state = RESETCTLR; fdc->flags &= ~FDC_NEEDS_RESET; } else { /* * If all motors were off, then the controller was * reset, so it has lost track of the current * cylinder. Recalibrate to handle this case. */ fdc->state = STARTRECAL; } - return(1); /* will return immediatly */ + return (1); /* will return immediatly */ default: - printf("fdc%d: Unexpected FD int->", fdcu); + device_print_prettyname(fdc->fdc_dev); + printf("unexpected FD int->"); if (fd_read_status(fdc, fd->fdsu) == 0) printf("FDC status :%x %x %x %x %x %x %x ", fdc->status[0], fdc->status[1], fdc->status[2], fdc->status[3], fdc->status[4], fdc->status[5], fdc->status[6] ); else printf("No status available "); if (fd_sense_int(fdc, &st0, &cyl) != 0) { printf("[controller is dead now]\n"); - return(0); + return (0); } printf("ST0 = %x, PCN = %x\n", st0, cyl); - return(0); + return (0); } /*XXX confusing: some branches return immediately, others end up here*/ - return(1); /* Come back immediatly to new state */ + return (1); /* Come back immediatly to new state */ } static int -retrier(fdcu) - fdcu_t fdcu; +retrier(struct fdc_data *fdc) { - fdc_p fdc = fdc_data + fdcu; register struct buf *bp; + struct fd_data *fd; + int fdu; bp = fdc->bp; - if(fd_data[FDUNIT(minor(bp->b_dev))].options & FDOPT_NORETRY) + /* XXX shouldn't this be cached somewhere? */ + fdu = FDUNIT(minor(bp->b_dev)); + fd = devclass_get_softc(fd_devclass, fdu); + if (fd->options & FDOPT_NORETRY) goto fail; - switch(fdc->retry) - { + + switch (fdc->retry) { case 0: case 1: case 2: fdc->state = SEEKCOMPLETE; break; case 3: case 4: case 5: fdc->state = STARTRECAL; break; case 6: fdc->state = RESETCTLR; break; case 7: break; default: fail: { dev_t sav_b_dev = bp->b_dev; /* Trick diskerr */ bp->b_dev = makedev(major(bp->b_dev), (FDUNIT(minor(bp->b_dev))<<3)|RAW_PART); diskerr(bp, "fd", "hard error", LOG_PRINTF, fdc->fd->skip / DEV_BSIZE, (struct disklabel *)NULL); bp->b_dev = sav_b_dev; if (fdc->flags & FDC_STAT_VALID) { printf( " (ST0 %b ST1 %b ST2 %b cyl %u hd %u sec %u)\n", fdc->status[0], NE7_ST0BITS, fdc->status[1], NE7_ST1BITS, fdc->status[2], NE7_ST2BITS, fdc->status[3], fdc->status[4], fdc->status[5]); } else printf(" (No status)\n"); } bp->b_flags |= B_ERROR; bp->b_error = EIO; bp->b_resid += bp->b_bcount - fdc->fd->skip; fdc->bp = NULL; /* Tell devstat we have finished with the transaction */ devstat_end_transaction(&fdc->fd->device_stats, bp->b_bcount - bp->b_resid, DEVSTAT_TAG_NONE, (bp->b_flags & B_READ) ? DEVSTAT_READ : DEVSTAT_WRITE); fdc->fd->skip = 0; biodone(bp); fdc->state = FINDWORK; fdc->flags |= FDC_NEEDS_RESET; fdc->fd = (fd_p) 0; fdc->fdu = -1; - return(1); + return (1); } fdc->retry++; - return(1); + return (1); } static int fdformat(dev, finfo, p) dev_t dev; struct fd_formb *finfo; struct proc *p; { fdu_t fdu; fd_p fd; struct buf *bp; int rv = 0, s; size_t fdblk; fdu = FDUNIT(minor(dev)); - fd = &fd_data[fdu]; + fd = devclass_get_softc(fd_devclass, fdu); fdblk = 128 << fd->ft->secsize; /* set up a buffer header for fdstrategy() */ bp = (struct buf *)malloc(sizeof(struct buf), M_TEMP, M_NOWAIT); if(bp == 0) return ENOBUFS; /* * keep the process from being swapped */ PHOLD(p); bzero((void *)bp, sizeof(struct buf)); bp->b_flags = B_BUSY | B_PHYS | B_FORMAT; bp->b_proc = p; /* * calculate a fake blkno, so fdstrategy() would initiate a * seek to the requested cylinder */ bp->b_blkno = (finfo->cyl * (fd->ft->sectrac * fd->ft->heads) + finfo->head * fd->ft->sectrac) * fdblk / DEV_BSIZE; bp->b_bcount = sizeof(struct fd_idfield_data) * finfo->fd_formb_nsecs; bp->b_data = (caddr_t)finfo; /* now do the format */ bp->b_dev = dev; fdstrategy(bp); /* ...and wait for it to complete */ s = splbio(); - while(!(bp->b_flags & B_DONE)) - { + while(!(bp->b_flags & B_DONE)) { rv = tsleep((caddr_t)bp, PRIBIO, "fdform", 20 * hz); - if(rv == EWOULDBLOCK) + if (rv == EWOULDBLOCK) break; } splx(s); - if(rv == EWOULDBLOCK) { + if (rv == EWOULDBLOCK) { /* timed out */ rv = EIO; biodone(bp); } - if(bp->b_flags & B_ERROR) + if (bp->b_flags & B_ERROR) rv = bp->b_error; /* * allow the process to be swapped */ PRELE(p); free(bp, M_TEMP); return rv; } /* * TODO: don't allocate buffer on stack. */ static int fdioctl(dev, cmd, addr, flag, p) dev_t dev; u_long cmd; caddr_t addr; int flag; struct proc *p; { fdu_t fdu = FDUNIT(minor(dev)); - fd_p fd = &fd_data[fdu]; + fd_p fd = devclass_get_softc(fd_devclass, fdu); size_t fdblk; struct fd_type *fdt; struct disklabel *dl; char buffer[DEV_BSIZE]; int error = 0; fdblk = 128 << fd->ft->secsize; - switch (cmd) - { + switch (cmd) { case DIOCGDINFO: bzero(buffer, sizeof (buffer)); dl = (struct disklabel *)buffer; dl->d_secsize = fdblk; - fdt = fd_data[FDUNIT(minor(dev))].ft; + fdt = fd->ft; dl->d_secpercyl = fdt->size / fdt->tracks; dl->d_type = DTYPE_FLOPPY; if (readdisklabel(dkmodpart(dev, RAW_PART), fdstrategy, dl) == NULL) error = 0; else error = EINVAL; *(struct disklabel *)addr = *dl; break; case DIOCSDINFO: if ((flag & FWRITE) == 0) error = EBADF; break; case DIOCWLABEL: if ((flag & FWRITE) == 0) error = EBADF; break; case DIOCWDINFO: - if ((flag & FWRITE) == 0) - { + if ((flag & FWRITE) == 0) { error = EBADF; break; } dl = (struct disklabel *)addr; if ((error = setdisklabel((struct disklabel *)buffer, dl, (u_long)0)) != 0) break; error = writedisklabel(dev, fdstrategy, (struct disklabel *)buffer); break; case FD_FORM: - if((flag & FWRITE) == 0) + if ((flag & FWRITE) == 0) error = EBADF; /* must be opened for writing */ - else if(((struct fd_formb *)addr)->format_version != + else if (((struct fd_formb *)addr)->format_version != FD_FORMAT_VERSION) error = EINVAL; /* wrong version of formatting prog */ else error = fdformat(dev, (struct fd_formb *)addr, p); break; case FD_GTYPE: /* get drive type */ *(struct fd_type *)addr = *fd->ft; break; case FD_STYPE: /* set drive type */ /* this is considered harmful; only allow for superuser */ - if(suser(p->p_ucred, &p->p_acflag) != 0) + if (suser(p->p_ucred, &p->p_acflag) != 0) return EPERM; *fd->ft = *(struct fd_type *)addr; break; case FD_GOPTS: /* get drive options */ *(int *)addr = fd->options; break; case FD_SOPTS: /* set drive options */ fd->options = *(int *)addr; break; default: error = ENOTTY; break; } return (error); } +static device_method_t fdc_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, fdc_probe), + DEVMETHOD(device_attach, fdc_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), -static fd_devsw_installed = 0; + /* Bus interface */ + DEVMETHOD(bus_print_child, fdc_print_child), + /* Our children never use any other bus interface methods. */ -static void fd_drvinit(void *notused ) -{ + { 0, 0 } +}; - if( ! fd_devsw_installed ) { - cdevsw_add_generic(BDEV_MAJOR,CDEV_MAJOR, &fd_cdevsw); - fd_devsw_installed = 1; - } -} +static driver_t fdc_driver = { + "fdc", + fdc_methods, + DRIVER_TYPE_BIO, + sizeof(struct fdc_data) +}; -SYSINIT(fddev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,fd_drvinit,NULL) +DRIVER_MODULE(fdc, isa, fdc_driver, fdc_devclass, 0, 0); +static device_method_t fd_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, fd_probe), + DEVMETHOD(device_attach, fd_attach), + DEVMETHOD(device_detach, bus_generic_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + DEVMETHOD(device_suspend, bus_generic_suspend), /* XXX */ + DEVMETHOD(device_resume, bus_generic_resume), /* XXX */ -#endif + { 0, 0 } +}; + +static driver_t fd_driver = { + "fd", + fd_methods, + DRIVER_TYPE_BIO, + sizeof(struct fd_data) +}; + +static struct cdevsw fd_cdevsw = { + Fdopen, fdclose, fdread, fdwrite, + fdioctl, nostop, nullreset, nodevtotty, + seltrue, nommap, fdstrategy, "fd", + NULL, -1, nodump, nopsize, + D_DISK, 0, -1 +}; + +BDEV_DRIVER_MODULE(fd, fdc, fd_driver, fd_devclass, BDEV_MAJOR, CDEV_MAJOR, + fd_cdevsw, 0, 0); + +#endif /* NFDC > 0 */ /* * Hello emacs, these are the * Local Variables: * c-indent-level: 8 * c-continued-statement-offset: 8 * c-continued-brace-offset: 0 * c-brace-offset: -8 * c-brace-imaginary-offset: 0 * c-argdecl-indent: 8 * c-label-offset: -8 * c++-hanging-braces: 1 * c++-access-specifier-offset: -8 * c++-empty-arglist-indent: 8 * c++-friend-offset: 0 * End: */ Index: head/sys/i386/isa/fdc.h =================================================================== --- head/sys/i386/isa/fdc.h (revision 45719) +++ head/sys/i386/isa/fdc.h (revision 45720) @@ -1,91 +1,95 @@ /*- * Copyright (c) 1990 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: @(#)fd.c 7.4 (Berkeley) 5/25/91 - * $Id: fdc.h,v 1.12 1998/12/12 08:16:01 imp Exp $ + * $Id: fdc.h,v 1.13 1999/01/15 09:15:27 bde Exp $ * */ enum fdc_type { FDC_NE765, FDC_I82077, FDC_NE72065, FDC_UNKNOWN = -1 }; /***********************************************************************\ * Per controller structure. * \***********************************************************************/ struct fdc_data { int fdcu; /* our unit number */ int baseport; int dmachan; int flags; #define FDC_ATTACHED 0x01 #define FDC_HASFTAPE 0x02 #define FDC_TAPE_BUSY 0x04 #define FDC_STAT_VALID 0x08 #define FDC_HAS_FIFO 0x10 #define FDC_NEEDS_RESET 0x20 #ifdef FDC_YE #define FDC_PCMCIA 0x40 #define FDC_UNLOADED 0x80 #endif struct fd_data *fd; int fdu; /* the active drive */ int state; int retry; int fdout; /* mirror of the w/o digital output reg */ u_int status[7]; /* copy of the registers */ enum fdc_type fdct; /* chip version of FDC */ int fdc_errs; /* number of logged errors */ struct buf_queue_head head; struct buf *bp; /* active buffer */ + struct resource *res_ioport, *res_irq, *res_drq; + int rid_ioport, rid_irq, rid_drq; + void *fdc_intr; + struct device *fdc_dev; }; /***********************************************************************\ * Throughout this file the following conventions will be used: * * fd is a pointer to the fd_data struct for the drive in question * * fdc is a pointer to the fdc_data struct for the controller * * fdu is the floppy drive unit number * * fdcu is the floppy controller unit number * * fdsu is the floppy drive unit number on that controller. (sub-unit) * \***********************************************************************/ typedef int fdu_t; typedef int fdcu_t; typedef int fdsu_t; typedef struct fd_data *fd_p; typedef struct fdc_data *fdc_p; typedef enum fdc_type fdc_t; #define FDUNIT(s) (((s)>>6)&03) #define FDTYPE(s) ((s)&077) Index: head/sys/i386/isa/if_cs.c =================================================================== --- head/sys/i386/isa/if_cs.c (revision 45719) +++ head/sys/i386/isa/if_cs.c (revision 45720) @@ -1,1399 +1,1396 @@ /* * Copyright (c) 1997,1998 Maxim Bolotin and Oleg Sharoiko. * 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 unmodified, 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. * */ /* - * $Id: if_cs.c,v 1.8 1999/01/12 00:27:43 eivind Exp $ + * $Id: if_cs.c,v 1.9 1999/01/28 01:59:53 dillon Exp $ * * Device driver for Crystal Semiconductor CS8920 based ethernet * adapters. By Maxim Bolotin and Oleg Sharoiko, 27-April-1997 */ /* #define CS_DEBUG */ #include "cs.h" #include "bpfilter.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #if NBPFILTER > 0 #include #endif #include #include #include #include "pnp.h" #if NPNP > 0 #include #endif #ifdef CS_USE_64K_DMA #define CS_DMA_BUFFER_SIZE 65536 #else #define CS_DMA_BUFFER_SIZE 16384 #endif /* * cs_softc: per line info and status */ static struct cs_softc { /* Ethernet common code */ struct arpcom arpcom; /* Configuration words from EEPROM */ int auto_neg_cnf; /* AutoNegotitation configuration */ int adapter_cnf; /* Adapter configuration */ int isa_config; /* ISA configuration */ int chip_type; /* Type of chip */ struct ifmedia media; /* Media information */ int nic_addr; /* Base IO address of card */ int send_cmd; int line_ctl; /* */ int send_underrun; void *recv_ring; unsigned char *buffer; int buf_len; } cs_softc[NCS]; #if NPNP > 0 static u_long cs_unit = NCS; #endif static int cs_recv_delay = 570; SYSCTL_INT(_machdep, OID_AUTO, cs_recv_delay, CTLFLAG_RW, &cs_recv_delay, 0, ""); static int cs_attach __P((struct cs_softc *, int, int)); static int cs_attach_isa __P((struct isa_device *)); static void cs_init __P((void *)); static ointhand2_t csintr; static int cs_ioctl __P((struct ifnet *, u_long, caddr_t)); static int cs_probe __P((struct isa_device *)); static int cs_cs89x0_probe __P((struct cs_softc *, u_int *, int *, int, int, int)); static void cs_start __P((struct ifnet *)); static void cs_stop __P((struct cs_softc *)); static void cs_reset __P((struct cs_softc *)); static void cs_watchdog __P((struct ifnet *)); static int cs_mediachange __P((struct ifnet *)); static void cs_mediastatus __P((struct ifnet *, struct ifmediareq *)); static int cs_mediaset __P((struct cs_softc *, int)); static void cs_write_mbufs(struct cs_softc*, struct mbuf*); static void cs_xmit_buf(struct cs_softc*); static int cs_get_packet(struct cs_softc*); static void cs_setmode(struct cs_softc*); static int get_eeprom_data(struct cs_softc *sc, int, int, int *); static int get_eeprom_cksum(int, int, int *); static int wait_eeprom_ready( struct cs_softc *); static void control_dc_dc( struct cs_softc *, int ); static int send_test_pkt( struct cs_softc * ); static int enable_tp(struct cs_softc *); static int enable_aui(struct cs_softc *); static int enable_bnc(struct cs_softc *); static int cs_duplex_auto(struct cs_softc *); struct isa_driver csdriver = { cs_probe, cs_attach_isa, CS_NAME, 0 }; static int get_eeprom_data( struct cs_softc *sc, int off, int len, int *buffer) { int i; #ifdef CS_DEBUG printf(CS_NAME":EEPROM data from %x for %x:\n", off,len); #endif for (i=0;inic_addr, PP_EECMD, (off+i)|EEPROM_READ_CMD ); if (wait_eeprom_ready(sc)<0) return -1; buffer[i] = cs_readreg (sc->nic_addr, PP_EEData); #ifdef CS_DEBUG printf("%02x %02x ",(unsigned char)buffer[i], (unsigned char)buffer[i+1]); #endif } #ifdef CS_DEBUG printf("\n"); #endif return 0; } static int get_eeprom_cksum(int off, int len, int *buffer) { int i,cksum=0; for (i=0;iadapter_cnf & A_CNF_DC_DC_POLARITY)!=0) ^ on_not_off) self_control |= HCB1; else self_control &= ~HCB1; cs_writereg( sc->nic_addr, PP_SelfCTL, self_control ); DELAY( 500000 ); } static int cs_duplex_auto(struct cs_softc *sc) { int i, error=0, unit=sc->arpcom.ac_if.if_unit; cs_writereg(sc->nic_addr, PP_AutoNegCTL, RE_NEG_NOW | ALLOW_FDX | AUTO_NEG_ENABLE ); for (i=0; cs_readreg(sc->nic_addr,PP_AutoNegST)&AUTO_NEG_BUSY; i++) { if (i > 40000) { printf(CS_NAME"%1d: full/half duplex " "auto negotiation timeout\n", unit); error = ETIMEDOUT; break; } DELAY(1000); } DELAY( 1000000 ); return error; } static int enable_tp(struct cs_softc *sc) { int unit = sc->arpcom.ac_if.if_unit; cs_writereg(sc->nic_addr, PP_LineCTL, sc->line_ctl & ~AUI_ONLY); control_dc_dc(sc, 0); DELAY( 150000 ); if ((cs_readreg(sc->nic_addr, PP_LineST) & LINK_OK)==0) { printf(CS_NAME"%1d: failed to enable TP\n", unit); return EINVAL; } return 0; } /* * XXX This was rewritten from Linux driver without any tests. */ static int send_test_pkt(struct cs_softc *sc) { char test_packet[] = { 0,0,0,0,0,0, 0,0,0,0,0,0, 0, 46, /* A 46 in network order */ 0, 0, /* DSAP=0 & SSAP=0 fields */ 0xf3, 0 /* Control (Test Req + P bit set) */ }; int i; u_char ether_address_backup[ETHER_ADDR_LEN]; for (i = 0; i < ETHER_ADDR_LEN; i++) { ether_address_backup[i] = sc->arpcom.ac_enaddr[i]; } cs_writereg(sc->nic_addr, PP_LineCTL, cs_readreg(sc->nic_addr, PP_LineCTL) | SERIAL_TX_ON ); bcopy(test_packet, sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); bcopy(test_packet+ETHER_ADDR_LEN, sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); outw(sc->nic_addr + TX_CMD_PORT, sc->send_cmd); outw(sc->nic_addr + TX_LEN_PORT, sizeof(test_packet)); /* Wait for chip to allocate memory */ DELAY(50000); if (!(cs_readreg(sc->nic_addr, PP_BusST) & READY_FOR_TX_NOW)) { for (i = 0; i < ETHER_ADDR_LEN; i++) { sc->arpcom.ac_enaddr[i] = ether_address_backup[i]; } return 0; } outsw(sc->nic_addr + TX_FRAME_PORT, test_packet, sizeof(test_packet)); DELAY(30000); if ((cs_readreg(sc->nic_addr,PP_TxEvent) & TX_SEND_OK_BITS) == TX_OK) { for (i = 0; i < ETHER_ADDR_LEN; i++) { sc->arpcom.ac_enaddr[i] = ether_address_backup[i]; } return 1; } for (i = 0; i < ETHER_ADDR_LEN; i++) { sc->arpcom.ac_enaddr[i] = ether_address_backup[i]; } return 0; } /* * XXX This was rewritten from Linux driver without any tests. */ static int enable_aui(struct cs_softc *sc) { int unit = sc->arpcom.ac_if.if_unit; control_dc_dc(sc, 0); cs_writereg(sc->nic_addr, PP_LineCTL, (sc->line_ctl & ~AUTO_AUI_10BASET) | AUI_ONLY); if (!send_test_pkt(sc)) { printf(CS_NAME"%1d failed to enable AUI\n", unit); return EINVAL; } return 0; } /* * XXX This was rewritten from Linux driver without any tests. */ static int enable_bnc(struct cs_softc *sc) { int unit = sc->arpcom.ac_if.if_unit; control_dc_dc(sc, 1); cs_writereg(sc->nic_addr, PP_LineCTL, (sc->line_ctl & ~AUTO_AUI_10BASET) | AUI_ONLY); if (!send_test_pkt(sc)) { printf(CS_NAME"%1d failed to enable BNC\n", unit); return EINVAL; } return 0; } static int cs_cs89x0_probe(struct cs_softc *sc, u_int *dev_irq, int *dev_drq, int iobase, int unit, int flags) { unsigned rev_type = 0; int i, irq=0; int eeprom_buff[CHKSUM_LEN]; int chip_type, pp_isaint, pp_isadma; char chip_revision; if ((inw(iobase+ADD_PORT) & ADD_MASK) != ADD_SIG) { /* Chip not detected. Let's try to reset it */ if (bootverbose) printf(CS_NAME"%1d: trying to reset the chip.\n", unit); outw(iobase+ADD_PORT, PP_SelfCTL); i = inw(iobase+DATA_PORT); outw(iobase+ADD_PORT, PP_SelfCTL); outw(iobase+DATA_PORT, i | POWER_ON_RESET); if ((inw(iobase+ADD_PORT) & ADD_MASK) != ADD_SIG) return 0; } outw(iobase+ADD_PORT, PP_ChipID); if (inw(iobase+DATA_PORT) != CHIP_EISA_ID_SIG) return 0; rev_type = cs_readreg(iobase, PRODUCT_ID_ADD); chip_type = rev_type & ~REVISON_BITS; chip_revision = ((rev_type & REVISON_BITS) >> 8) + 'A'; sc->nic_addr = iobase; sc->chip_type = chip_type; if(chip_type==CS8900) { pp_isaint = PP_CS8900_ISAINT; pp_isadma = PP_CS8900_ISADMA; sc->send_cmd = TX_CS8900_AFTER_ALL; } else { pp_isaint = PP_CS8920_ISAINT; pp_isadma = PP_CS8920_ISADMA; sc->send_cmd = TX_CS8920_AFTER_ALL; } /* * Clear some fields so that fail of EEPROM will left them clean */ sc->auto_neg_cnf = 0; sc->adapter_cnf = 0; sc->isa_config = 0; /* * EEPROM */ if((cs_readreg(iobase, PP_SelfST) & EEPROM_PRESENT) == 0) { printf(CS_NAME"%1d: No EEPROM, assuming defaults.\n", unit); } else { if (get_eeprom_data(sc,START_EEPROM_DATA,CHKSUM_LEN, eeprom_buff)<0) { printf(CS_NAME"%1d: EEPROM read failed, " "assuming defaults..\n", unit); } else { if (get_eeprom_cksum(START_EEPROM_DATA,CHKSUM_LEN, eeprom_buff)<0) { printf( CS_NAME"%1d: EEPROM cheksum bad, " "assuming defaults..\n", unit ); } else { sc->auto_neg_cnf = eeprom_buff[AUTO_NEG_CNF_OFFSET/2]; sc->adapter_cnf = eeprom_buff[ADAPTER_CNF_OFFSET/2]; sc->isa_config = eeprom_buff[ISA_CNF_OFFSET/2]; for (i=0; iarpcom.ac_enaddr[i*2]= eeprom_buff[i]; sc->arpcom.ac_enaddr[i*2+1]= eeprom_buff[i] >> 8; } /* * If no interrupt specified (or "?"), * use what the board tells us. */ if (*dev_irq <= 0) { irq = sc->isa_config & INT_NO_MASK; if (chip_type==CS8900) { switch(irq) { case 0: irq=10; break; case 1: irq=11; break; case 2: irq=12; break; case 3: irq=5; break; default: printf(CS_NAME"%1d: invalid irq in EEPROM.\n",unit); } if (irq!=0) *dev_irq=(u_short)(1< CS8920_NO_INTS) { printf(CS_NAME"%1d: invalid irq\n", unit); return 0; } } cs_writereg(iobase, pp_isaint, irq); } else { printf(CS_NAME"%1d: invalid irq\n", unit); return 0; } /* * Temporary disabled * if (drq>0) cs_writereg(iobase, pp_isadma, drq); else { printf( CS_NAME"%1d: incorrect drq\n", unit ); return 0; } */ if (bootverbose) printf(CS_NAME"%1d: model CS89%c0%s rev %c\n" CS_NAME"%1d: media%s%s%s\n" CS_NAME"%1d: irq %d drq %d\n", unit, chip_type==CS8900 ? '0' : '2', chip_type==CS8920M ? "M" : "", chip_revision, unit, (sc->adapter_cnf & A_CNF_10B_T) ? " TP" : "", (sc->adapter_cnf & A_CNF_AUI) ? " AUI" : "", (sc->adapter_cnf & A_CNF_10B_2) ? " BNC" : "", unit, (int)*dev_irq, (int)*dev_drq); if ((sc->adapter_cnf & A_CNF_EXTND_10B_2) && (sc->adapter_cnf & A_CNF_LOW_RX_SQUELCH)) sc->line_ctl = LOW_RX_SQUELCH; else sc->line_ctl = 0; return PP_ISAIOB; } /* * Determine if the device is present * * on entry: * a pointer to an isa_device struct * on exit: * NULL if device not found * or # of i/o addresses used (if found) */ static int cs_probe(struct isa_device *dev) { int nports; struct cs_softc *sc=&cs_softc[dev->id_unit]; nports=cs_cs89x0_probe(sc, &(dev->id_irq), &(dev->id_drq), (dev->id_iobase), (dev->id_unit), (dev->id_flags)); if (nports) return (nports); return (0); } /* * Install the interface into kernel networking data structures */ static int cs_attach(struct cs_softc *sc, int unit, int flags) { int media=0; /* struct cs_softc *sc = &cs_softc[dev->id_unit]; */ struct ifnet *ifp = &(sc->arpcom.ac_if); if (!ifp->if_name) { ifp->if_softc=sc; ifp->if_unit=unit; ifp->if_name=csdriver.name; ifp->if_output=ether_output; ifp->if_start=cs_start; ifp->if_ioctl=cs_ioctl; ifp->if_watchdog=cs_watchdog; ifp->if_init=cs_init; ifp->if_snd.ifq_maxlen= IFQ_MAXLEN; /* * MIB DATA */ /* ifp->if_linkmib=&sc->mibdata; ifp->if_linkmiblen=sizeof sc->mibdata; */ ifp->if_flags=(IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST ); /* * this code still in progress (DMA support) * sc->recv_ring=malloc(CS_DMA_BUFFER_SIZE<<1, M_DEVBUF, M_NOWAIT); if (sc->recv_ring == NULL) { log(LOG_ERR,CS_NAME "%d: Couldn't allocate memory for NIC\n", unit); return(0); } if ((sc->recv_ring-(sc->recv_ring & 0x1FFFF)) < (128*1024-CS_DMA_BUFFER_SIZE)) sc->recv_ring+=16*1024; */ sc->buffer=malloc(ETHER_MAX_LEN-ETHER_CRC_LEN,M_DEVBUF,M_NOWAIT); if (sc->buffer == NULL) { printf(CS_NAME"%d: Couldn't allocate memory for NIC\n", unit); return(0); } /* * Initialize the media structures. */ ifmedia_init(&sc->media, 0, cs_mediachange, cs_mediastatus); if (sc->adapter_cnf & A_CNF_10B_T) { ifmedia_add(&sc->media, IFM_ETHER|IFM_10_T, 0, NULL); if (sc->chip_type != CS8900) { ifmedia_add(&sc->media, IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL); ifmedia_add(&sc->media, IFM_ETHER|IFM_10_T|IFM_HDX, 0, NULL); } } if (sc->adapter_cnf & A_CNF_10B_2) ifmedia_add(&sc->media, IFM_ETHER|IFM_10_2, 0, NULL); if (sc->adapter_cnf & A_CNF_AUI) ifmedia_add(&sc->media, IFM_ETHER|IFM_10_5, 0, NULL); if (sc->adapter_cnf & A_CNF_MEDIA) ifmedia_add(&sc->media, IFM_ETHER|IFM_AUTO, 0, NULL); /* Set default media from EEPROM */ switch (sc->adapter_cnf & A_CNF_MEDIA_TYPE) { case A_CNF_MEDIA_AUTO: media = IFM_ETHER|IFM_AUTO; break; case A_CNF_MEDIA_10B_T: media = IFM_ETHER|IFM_10_T; break; case A_CNF_MEDIA_10B_2: media = IFM_ETHER|IFM_10_2; break; case A_CNF_MEDIA_AUI: media = IFM_ETHER|IFM_10_5; break; default: printf(CS_NAME"%d: adapter has no media\n", unit); } ifmedia_set(&sc->media, media); cs_mediaset(sc, media); if_attach(ifp); cs_stop( sc ); ether_ifattach(ifp); } if (bootverbose) printf(CS_NAME"%d: ethernet address %6D\n", ifp->if_unit, sc->arpcom.ac_enaddr, ":"); #if NBPFILTER > 0 bpfattach(ifp, DLT_EN10MB, sizeof (struct ether_header)); #endif return 1; } static int cs_attach_isa(struct isa_device *dev) { int unit=dev->id_unit; struct cs_softc *sc=&cs_softc[unit]; int flags=dev->id_flags; dev->id_ointr = csintr; return cs_attach(sc, unit, flags); } /* * Initialize the board */ static void cs_init(void *xsc) { struct cs_softc *sc=(struct cs_softc *)xsc; struct ifnet *ifp = &sc->arpcom.ac_if; int i, s, rx_cfg; /* address not known */ if (TAILQ_EMPTY(&ifp->if_addrhead)) /* unlikely? XXX */ return; /* * reset whatchdog timer */ ifp->if_timer=0; sc->buf_len = 0; s=splimp(); /* * Hardware initialization of cs */ /* Enable receiver and transmitter */ cs_writereg(sc->nic_addr, PP_LineCTL, cs_readreg( sc->nic_addr, PP_LineCTL ) | SERIAL_RX_ON | SERIAL_TX_ON); /* Configure the receiver mode */ cs_setmode(sc); /* * This defines what type of frames will cause interrupts * Bad frames should generate interrupts so that the driver * could track statistics of discarded packets */ rx_cfg = RX_OK_ENBL | RX_CRC_ERROR_ENBL | RX_RUNT_ENBL | RX_EXTRA_DATA_ENBL; if (sc->isa_config & STREAM_TRANSFER) rx_cfg |= RX_STREAM_ENBL; cs_writereg(sc->nic_addr, PP_RxCFG, rx_cfg); cs_writereg(sc->nic_addr, PP_TxCFG, TX_LOST_CRS_ENBL | TX_SQE_ERROR_ENBL | TX_OK_ENBL | TX_LATE_COL_ENBL | TX_JBR_ENBL | TX_ANY_COL_ENBL | TX_16_COL_ENBL); cs_writereg(sc->nic_addr, PP_BufCFG, READY_FOR_TX_ENBL | RX_MISS_COUNT_OVRFLOW_ENBL | TX_COL_COUNT_OVRFLOW_ENBL | TX_UNDERRUN_ENBL /*| RX_DMA_ENBL*/); /* Write MAC address into IA filter */ for (i=0; inic_addr, PP_IA+i*2, sc->arpcom.ac_enaddr[i*2] | (sc->arpcom.ac_enaddr[i*2+1] << 8) ); /* * Now enable everything */ /* #ifdef CS_USE_64K_DMA cs_writereg(sc->nic_addr, PP_BusCTL, ENABLE_IRQ | RX_DMA_SIZE_64K); #else cs_writereg(sc->nic_addr, PP_BusCTL, ENABLE_IRQ); #endif */ cs_writereg(sc->nic_addr, PP_BusCTL, ENABLE_IRQ); /* * Set running and clear output active flags */ sc->arpcom.ac_if.if_flags |= IFF_RUNNING; sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE; /* * Start sending process */ cs_start(ifp); (void) splx(s); } /* * Get the packet from the board and send it to the upper layer * via ether_input(). */ static int cs_get_packet(struct cs_softc *sc) { struct ifnet *ifp = &(sc->arpcom.ac_if); int iobase = sc->nic_addr, status, length; struct ether_header *eh; struct mbuf *m; #ifdef CS_DEBUG int i; #endif status = inw(iobase + RX_FRAME_PORT); length = inw(iobase + RX_FRAME_PORT); #ifdef CS_DEBUG printf(CS_NAME"%1d: rcvd: stat %x, len %d\n", ifp->if_unit, status, length); #endif if (!(status & RX_OK)) { #ifdef CS_DEBUG printf(CS_NAME"%1d: bad pkt stat %x\n", ifp->if_unit, status); #endif ifp->if_ierrors++; return -1; } MGETHDR(m, M_DONTWAIT, MT_DATA); if (m==NULL) return -1; if (length > MHLEN) { MCLGET(m, M_DONTWAIT); if (!(m->m_flags & M_EXT)) { m_freem(m); return -1; } } /* Initialize packet's header info */ m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = length; m->m_len = length; /* Get the data */ insw(iobase + RX_FRAME_PORT, m->m_data, (length+1)>>1); eh = mtod(m, struct ether_header *); #if NBPFILTER > 0 if (ifp->if_bpf) bpf_mtap(ifp, m); #endif #ifdef CS_DEBUG for (i=0;im_data+i))); printf( "\n" ); #endif if (status & (RX_IA | RX_BROADCAST) || (ifp->if_flags & IFF_MULTICAST && status & RX_HASHED)) { m->m_pkthdr.len -= sizeof(struct ether_header); m->m_len -= sizeof(struct ether_header); m->m_data += sizeof(struct ether_header); /* Feed the packet to the upper layer */ ether_input(ifp, eh, m); ifp->if_ipackets++; if (length==ETHER_MAX_LEN-ETHER_CRC_LEN) DELAY( cs_recv_delay ); } else { m_freem(m); } return 0; } /* * Software calls interrupt handler */ static void csintr_sc(struct cs_softc *sc, int unit) { struct ifnet *ifp = &(sc->arpcom.ac_if); int status; #ifdef CS_DEBUG printf(CS_NAME"%1d: Interrupt.\n", unit); #endif while ((status=cs_readword(sc->nic_addr, ISQ_PORT))) { #ifdef CS_DEBUG printf( CS_NAME"%1d:from ISQ: %04x\n", unit, status ); #endif switch (status & ISQ_EVENT_MASK) { case ISQ_RECEIVER_EVENT: cs_get_packet(sc); break; case ISQ_TRANSMITTER_EVENT: if (status & TX_OK) ifp->if_opackets++; else ifp->if_oerrors++; ifp->if_flags &= ~IFF_OACTIVE; ifp->if_timer = 0; break; case ISQ_BUFFER_EVENT: if (status & READY_FOR_TX) { ifp->if_flags &= ~IFF_OACTIVE; ifp->if_timer = 0; } if (status & TX_UNDERRUN) { ifp->if_flags &= ~IFF_OACTIVE; ifp->if_timer = 0; ifp->if_oerrors++; } break; case ISQ_RX_MISS_EVENT: ifp->if_ierrors+=(status>>6); break; case ISQ_TX_COL_EVENT: ifp->if_collisions+=(status>>6); break; } } if (!(ifp->if_flags & IFF_OACTIVE)) { cs_start(ifp); } } /* * Handle interrupts */ static void csintr(int unit) { struct cs_softc *sc = &cs_softc[unit]; csintr_sc(sc, unit); } /* * Save the data in buffer */ static void cs_write_mbufs( struct cs_softc *sc, struct mbuf *m ) { int len; struct mbuf *mp; unsigned char *data, *buf; for (mp=m, buf=sc->buffer, sc->buf_len=0; mp != NULL; mp=mp->m_next) { len = mp->m_len; /* * Ignore empty parts */ if (!len) continue; /* * Find actual data address */ data = mtod(mp, caddr_t); bcopy((caddr_t) data, (caddr_t) buf, len); buf += len; sc->buf_len += len; } } static void cs_xmit_buf( struct cs_softc *sc ) { outsw(sc->nic_addr+TX_FRAME_PORT, sc->buffer, (sc->buf_len+1)>>1); sc->buf_len = 0; } static void cs_start(struct ifnet *ifp) { int s, length; struct mbuf *m, *mp; struct cs_softc *sc = ifp->if_softc; s = splimp(); for (;;) { if (sc->buf_len) length = sc->buf_len; else { IF_DEQUEUE( &ifp->if_snd, m ); if (m==NULL) { (void) splx(s); return; } for (length=0, mp=m; mp != NULL; mp=mp->m_next) length += mp->m_len; /* Skip zero-length packets */ if (length == 0) { m_freem(m); continue; } cs_write_mbufs(sc, m); #if NBPFILTER > 0 if (ifp->if_bpf) { bpf_mtap(ifp, m); } #endif m_freem(m); } /* * Issue a SEND command */ outw(sc->nic_addr+TX_CMD_PORT, sc->send_cmd); outw(sc->nic_addr+TX_LEN_PORT, length ); /* * If there's no free space in the buffer then leave * this packet for the next time: indicate output active * and return. */ if (!(cs_readreg(sc->nic_addr, PP_BusST) & READY_FOR_TX_NOW)) { ifp->if_timer = sc->buf_len; (void) splx(s); ifp->if_flags |= IFF_OACTIVE; return; } cs_xmit_buf(sc); /* * Set the watchdog timer in case we never hear * from board again. (I don't know about correct * value for this timeout) */ ifp->if_timer = length; (void) splx(s); ifp->if_flags |= IFF_OACTIVE; return; } } /* * Stop everything on the interface */ static void cs_stop(struct cs_softc *sc) { int s = splimp(); cs_writereg(sc->nic_addr, PP_RxCFG, 0); cs_writereg(sc->nic_addr, PP_TxCFG, 0); cs_writereg(sc->nic_addr, PP_BufCFG, 0); cs_writereg(sc->nic_addr, PP_BusCTL, 0); sc->arpcom.ac_if.if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); sc->arpcom.ac_if.if_timer = 0; (void) splx(s); } /* * Reset the interface */ static void cs_reset(struct cs_softc *sc) { cs_stop(sc); cs_init(sc); } static void cs_setmode(struct cs_softc *sc) { struct ifnet *ifp = &(sc->arpcom.ac_if); int rx_ctl; /* Stop the receiver while changing filters */ cs_writereg(sc->nic_addr, PP_LineCTL, cs_readreg(sc->nic_addr, PP_LineCTL) & ~SERIAL_RX_ON); if (ifp->if_flags & IFF_PROMISC) { /* Turn on promiscuous mode. */ rx_ctl = RX_OK_ACCEPT | RX_PROM_ACCEPT; } else { if (ifp->if_flags & IFF_MULTICAST) { /* Allow receiving frames with multicast addresses */ rx_ctl = RX_IA_ACCEPT | RX_BROADCAST_ACCEPT | RX_OK_ACCEPT | RX_MULTCAST_ACCEPT; /* * Here the reconfiguration of chip's multicast * filters should be done but I've no idea about * hash transformation in this chip. If you can * add this code or describe me the transformation * I'd be very glad. */ } else { /* * Receive only good frames addressed for us and * good broadcasts. */ rx_ctl = RX_IA_ACCEPT | RX_BROADCAST_ACCEPT | RX_OK_ACCEPT; } } /* Set up the filter */ cs_writereg(sc->nic_addr, PP_RxCTL, RX_DEF_ACCEPT | rx_ctl); /* Turn on receiver */ cs_writereg(sc->nic_addr, PP_LineCTL, cs_readreg(sc->nic_addr, PP_LineCTL) | SERIAL_RX_ON); } static int cs_ioctl(register struct ifnet *ifp, u_long command, caddr_t data) { struct cs_softc *sc=ifp->if_softc; struct ifreq *ifr = (struct ifreq *)data; int s,error=0; #ifdef CS_DEBUG printf(CS_NAME"%d: ioctl(%x)\n",sc->arpcom.ac_if.if_unit,command); #endif s=splimp(); switch (command) { case SIOCSIFADDR: case SIOCGIFADDR: case SIOCSIFMTU: ether_ioctl(ifp, command, data); break; case SIOCSIFFLAGS: /* * Switch interface state between "running" and * "stopped", reflecting the UP flag. */ if (sc->arpcom.ac_if.if_flags & IFF_UP) { if ((sc->arpcom.ac_if.if_flags & IFF_RUNNING)==0) { cs_init(sc); } } else { if ((sc->arpcom.ac_if.if_flags & IFF_RUNNING)!=0) { cs_stop(sc); } } /* * Promiscuous and/or multicast flags may have changed, * so reprogram the multicast filter and/or receive mode. * * See note about multicasts in cs_setmode */ cs_setmode(sc); break; case SIOCADDMULTI: case SIOCDELMULTI: /* * Multicast list has changed; set the hardware filter * accordingly. * * See note about multicasts in cs_setmode */ cs_setmode(sc); error = 0; break; case SIOCSIFMEDIA: case SIOCGIFMEDIA: error = ifmedia_ioctl(ifp, ifr, &sc->media, command); break; default: error = EINVAL; } (void) splx(s); return error; } /* * Device timeout/watchdog routine. Entered if the device neglects to * generate an interrupt after a transmit has been started on it. */ static void cs_watchdog(struct ifnet *ifp) { struct cs_softc *sc = &cs_softc[ifp->if_unit]; ifp->if_oerrors++; log(LOG_ERR, CS_NAME"%d: device timeout\n", ifp->if_unit); /* Reset the interface */ if (ifp->if_flags & IFF_UP) cs_reset(sc); else cs_stop(sc); } static int cs_mediachange(struct ifnet *ifp) { struct cs_softc *sc = ifp->if_softc; struct ifmedia *ifm = &sc->media; if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER) return EINVAL; return cs_mediaset(sc, ifm->ifm_media); } static void cs_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr) { int line_status; struct cs_softc *sc = ifp->if_softc; ifmr->ifm_active = IFM_ETHER; line_status = cs_readreg(sc->nic_addr, PP_LineST); if (line_status & TENBASET_ON) { ifmr->ifm_active |= IFM_10_T; if (sc->chip_type != CS8900) { if (cs_readreg(sc->nic_addr, PP_AutoNegST) & FDX_ACTIVE) ifmr->ifm_active |= IFM_FDX; if (cs_readreg(sc->nic_addr, PP_AutoNegST) & HDX_ACTIVE) ifmr->ifm_active |= IFM_HDX; } ifmr->ifm_status = IFM_AVALID; if (line_status & LINK_OK) ifmr->ifm_status |= IFM_ACTIVE; } else { if (line_status & AUI_ON) { cs_writereg(sc->nic_addr, PP_SelfCTL, cs_readreg(sc->nic_addr, PP_SelfCTL) | HCB1_ENBL); if (((sc->adapter_cnf & A_CNF_DC_DC_POLARITY)!=0)^ (cs_readreg(sc->nic_addr, PP_SelfCTL)&HCB1)) ifmr->ifm_active |= IFM_10_2; else ifmr->ifm_active |= IFM_10_5; } } } static int cs_mediaset(struct cs_softc *sc, int media) { int error; /* Stop the receiver & transmitter */ cs_writereg(sc->nic_addr, PP_LineCTL, cs_readreg(sc->nic_addr, PP_LineCTL) & ~(SERIAL_RX_ON | SERIAL_TX_ON)); #ifdef CS_DEBUG printf(CS_NAME"%d: cs_setmedia(%x)\n",sc->arpcom.ac_if.if_unit,media); #endif switch (IFM_SUBTYPE(media)) { default: case IFM_AUTO: if ((error=enable_tp(sc))==0) error = cs_duplex_auto(sc); else if ((error=enable_bnc(sc)) != 0) error = enable_aui(sc); break; case IFM_10_T: if ((error=enable_tp(sc)) != 0) break; if (media & IFM_FDX) cs_duplex_full(sc); else if (media & IFM_HDX) cs_duplex_half(sc); else error = cs_duplex_auto(sc); break; case IFM_10_2: error = enable_bnc(sc); break; case IFM_10_5: error = enable_aui(sc); break; } /* * Turn the transmitter & receiver back on */ cs_writereg(sc->nic_addr, PP_LineCTL, cs_readreg( sc->nic_addr, PP_LineCTL ) | SERIAL_RX_ON | SERIAL_TX_ON); return error; } #if NPNP > 0 static struct cspnp_ids { u_long vend_id; char *id_str; } cspnp_ids[]= { { 0x4060630e, "CSC6040" }, { 0x10104d24, "IBM EtherJet" }, { 0 } }; static char *cs_pnp_probe(u_long, u_long); static void cs_pnp_attach(u_long, u_long, char *, struct isa_device *); struct pnp_device cs_pnp = { "CS8920 based PnP Ethernet", cs_pnp_probe, cs_pnp_attach, &cs_unit, &net_imask /* imask */ }; DATA_SET (pnpdevice_set, cs_pnp); struct csintr_list { struct cs_softc *sc; int unit; struct csintr_list *next; }; static struct csintr_list *csintr_head; static void csintr_pnp_add(struct cs_softc *sc, int unit); static void csintr_pnp(int unit); static void csintr_pnp_add(struct cs_softc *sc, int unit) { struct csintr_list *intr; if (!sc) return; intr = malloc (sizeof (*intr), M_DEVBUF, M_WAITOK); if (!intr) return; intr->sc = sc; intr->unit = unit; intr->next = csintr_head; csintr_head = intr; } /* * Interrupt handler for PNP installed card * We have to find the number of the card. */ static void csintr_pnp(int unit) { struct csintr_list *intr; for (intr=csintr_head; intr; intr=intr->next) { if (intr->unit == unit) csintr_sc(intr->sc, unit); break; } } static char * cs_pnp_probe(u_long csn, u_long vend_id) { struct cspnp_ids *ids; char *s=NULL; for(ids = cspnp_ids; ids->vend_id != 0; ids++) { if (vend_id == ids->vend_id) { s = ids->id_str; break; } } if (s) { struct pnp_cinfo d; int ldn = 0; read_pnp_parms(&d, ldn); if (d.enable == 0) { printf("This is a %s, but LDN %d is disabled\n", s, ldn); return NULL ; } return s; } return NULL ; } static void cs_pnp_attach(u_long csn, u_long vend_id, char *name, struct isa_device *dev) { struct pnp_cinfo d; int ldn = 0; int iobase, unit, flags; u_int irq; int drq; - struct isa_device *dvp; struct cs_softc *sc = malloc(sizeof *sc, M_DEVBUF, M_NOWAIT); if (read_pnp_parms ( &d , ldn ) == 0 ) { printf("failed to read pnp parms\n"); return; } write_pnp_parms( &d, ldn ); enable_pnp_card(); iobase = dev->id_iobase = d.port[0]; irq = dev->id_irq = (1 << d.irq[0] ); drq = dev->id_drq = d.drq[0]; dev->id_maddr = 0; dev->id_ointr = csintr_pnp; flags = dev->id_flags = 0; unit = dev->id_unit; if (dev->id_driver == NULL) { dev->id_driver = &csdriver; - dvp = find_isadev(isa_devtab_net, &csdriver, 0); - if (dvp != NULL) - dev->id_id = dvp->id_id; + dev->id_id = isa_compat_nextid(); } if (!sc) return; bzero(sc, sizeof *sc); if (cs_cs89x0_probe(sc, &irq, &drq, iobase, unit, flags) == 0 || cs_attach(sc, unit, flags) == 0) { free(sc, M_DEVBUF); } else { if ((irq != dev->id_irq) || (drq != dev->id_drq) || (iobase != dev->id_iobase) || (unit != dev->id_unit) || (flags != dev->id_flags) ) { printf("failed to pnp card parametars\n"); } } csintr_pnp_add(sc, dev->id_unit); } #endif /* NPNP */ Index: head/sys/i386/isa/if_ed.c =================================================================== --- head/sys/i386/isa/if_ed.c (revision 45719) +++ head/sys/i386/isa/if_ed.c (revision 45720) @@ -1,3522 +1,3519 @@ /* * Copyright (c) 1995, David Greenman * 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 unmodified, 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. * - * $Id: if_ed.c,v 1.149 1999/01/28 01:59:53 dillon Exp $ + * $Id: if_ed.c,v 1.150 1999/03/17 16:44:51 luigi Exp $ */ /* * Device driver for National Semiconductor DS8390/WD83C690 based ethernet * adapters. By David Greenman, 29-April-1993 * * Currently supports the Western Digital/SMC 8003 and 8013 series, * the SMC Elite Ultra (8216), the 3Com 3c503, the NE1000 and NE2000, * and a variety of similar clones. * */ #include "ed.h" #include "bpfilter.h" #include "pnp.h" #ifndef EXTRA_ED # if NPNP > 0 # define EXTRA_ED 8 # else # define EXTRA_ED 0 # endif #endif #define NEDTOT (NED + EXTRA_ED) #include #include #include #include #include #include #include #include #include #include #include #include #if NBPFILTER > 0 #include #endif #include "opt_bdg.h" #ifdef BRIDGE #include #endif #include #include #include #include #include #if NPNP > 0 #include #endif /* * ed_softc: per line info and status */ struct ed_softc { struct arpcom arpcom; /* ethernet common */ char *type_str; /* pointer to type string */ u_char vendor; /* interface vendor */ u_char type; /* interface type code */ u_char gone; /* HW missing, presumed having a good time */ u_short asic_addr; /* ASIC I/O bus address */ u_short nic_addr; /* NIC (DS8390) I/O bus address */ /* * The following 'proto' variable is part of a work-around for 8013EBT asics * being write-only. It's sort of a prototype/shadow of the real thing. */ u_char wd_laar_proto; u_char cr_proto; u_char isa16bit; /* width of access to card 0=8 or 1=16 */ int is790; /* set by the probe code if the card is 790 * based */ /* * HP PC LAN PLUS card support. */ u_short hpp_options; /* flags controlling behaviour of the HP card */ u_short hpp_id; /* software revision and other fields */ caddr_t hpp_mem_start; /* Memory-mapped IO register address */ caddr_t mem_start; /* NIC memory start address */ caddr_t mem_end; /* NIC memory end address */ u_long mem_size; /* total NIC memory size */ caddr_t mem_ring; /* start of RX ring-buffer (in NIC mem) */ u_char mem_shared; /* NIC memory is shared with host */ u_char xmit_busy; /* transmitter is busy */ u_char txb_cnt; /* number of transmit buffers */ u_char txb_inuse; /* number of TX buffers currently in-use */ u_char txb_new; /* pointer to where new buffer will be added */ u_char txb_next_tx; /* pointer to next buffer ready to xmit */ u_short txb_len[8]; /* buffered xmit buffer lengths */ u_char tx_page_start; /* first page of TX buffer area */ u_char rec_page_start; /* first page of RX ring-buffer */ u_char rec_page_stop; /* last page of RX ring-buffer */ u_char next_packet; /* pointer to next unread RX packet */ struct ifmib_iso_8802_3 mibdata; /* stuff for network mgmt */ }; static struct ed_softc ed_softc[NEDTOT]; static int ed_attach __P((struct ed_softc *, int, int)); static int ed_attach_isa __P((struct isa_device *)); static void ed_init __P((void *)); static ointhand2_t edintr; static int ed_ioctl __P((struct ifnet *, u_long, caddr_t)); static int ed_probe __P((struct isa_device *)); static void ed_start __P((struct ifnet *)); static void ed_reset __P((struct ifnet *)); static void ed_watchdog __P((struct ifnet *)); static void ed_stop __P((struct ed_softc *)); static int ed_probe_generic8390 __P((struct ed_softc *)); static int ed_probe_WD80x3 __P((struct isa_device *)); static int ed_probe_3Com __P((struct isa_device *)); static int ed_probe_Novell __P((struct isa_device *)); static int ed_probe_Novell_generic __P((struct ed_softc *, int, int, int)); static int ed_probe_HP_pclanp __P((struct isa_device *)); #include "pci.h" #if NPCI > 0 void *ed_attach_NE2000_pci __P((int, int)); #endif #include "card.h" #if NCARD > 0 static int ed_probe_pccard __P((struct isa_device *, u_char *)); #endif static void ds_getmcaf __P((struct ed_softc *, u_long *)); static void ed_get_packet __P((struct ed_softc *, char *, /* u_short */ int, int)); static __inline void ed_rint __P((struct ed_softc *)); static __inline void ed_xmit __P((struct ed_softc *)); static __inline char * ed_ring_copy __P((struct ed_softc *, char *, char *, /* u_short */ int)); static void ed_hpp_set_physical_link __P((struct ed_softc *)); static void ed_hpp_readmem __P((struct ed_softc *, int, unsigned char *, /* u_short */ int)); static u_short ed_hpp_write_mbufs __P((struct ed_softc *, struct mbuf *, int)); static void ed_pio_readmem __P((struct ed_softc *, int, unsigned char *, /* u_short */ int)); static void ed_pio_writemem __P((struct ed_softc *, char *, /* u_short */ int, /* u_short */ int)); static u_short ed_pio_write_mbufs __P((struct ed_softc *, struct mbuf *, int)); void edintr_sc __P((struct ed_softc *)); static void ed_setrcr __P((struct ed_softc *)); static u_long ds_crc __P((u_char *ep)); #if (NCARD > 0) || (NPNP > 0) #include #endif #if NCARD > 0 #include #include #include #include /* * PC-Card (PCMCIA) specific code. */ static int edinit __P((struct pccard_devinfo *)); static void edunload __P((struct pccard_devinfo *)); static int card_intr __P((struct pccard_devinfo *)); PCCARD_MODULE(ed, edinit, edunload, card_intr, 0, net_imask); /* * Initialize the device - called from Slot manager. */ static int edinit(struct pccard_devinfo *devi) { int i; u_char e; struct ed_softc *sc = &ed_softc[devi->isahd.id_unit]; /* validate unit number. */ if (devi->isahd.id_unit >= NEDTOT) return(ENODEV); /* * Probe the device. If a value is returned, the * device was found at the location. */ sc->gone = 0; if (ed_probe_pccard(&devi->isahd, devi->misc) == 0) return(ENXIO); e = 0; for (i = 0; i < ETHER_ADDR_LEN; ++i) e |= devi->misc[i]; if (e) for (i = 0; i < ETHER_ADDR_LEN; ++i) sc->arpcom.ac_enaddr[i] = devi->misc[i]; if (ed_attach_isa(&devi->isahd) == 0) return(ENXIO); return(0); } /* * edunload - unload the driver and clear the table. * XXX TODO: * This is usually called when the card is ejected, but * can be caused by a modunload of a controller driver. * The idea is to reset the driver's view of the device * and ensure that any driver entry points such as * read and write do not hang. */ static void edunload(struct pccard_devinfo *devi) { struct ed_softc *sc = &ed_softc[devi->isahd.id_unit]; struct ifnet *ifp = &sc->arpcom.ac_if; if (sc->gone) { printf("ed%d: already unloaded\n", devi->isahd.id_unit); return; } ifp->if_flags &= ~IFF_RUNNING; if_down(ifp); sc->gone = 1; printf("ed%d: unload\n", devi->isahd.id_unit); } /* * card_intr - Shared interrupt called from * front end of PC-Card handler. */ static int card_intr(struct pccard_devinfo *devi) { edintr_sc(&ed_softc[devi->isahd.id_unit]); return(1); } #endif /* NCARD > 0 */ struct isa_driver eddriver = { ed_probe, ed_attach_isa, "ed", 1 /* We are ultra sensitive */ }; /* * Interrupt conversion table for WD/SMC ASIC/83C584 * (IRQ* are defined in icu.h) */ static unsigned short ed_intr_mask[] = { IRQ9, IRQ3, IRQ5, IRQ7, IRQ10, IRQ11, IRQ15, IRQ4 }; /* * Interrupt conversion table for 83C790 */ static unsigned short ed_790_intr_mask[] = { 0, IRQ9, IRQ3, IRQ5, IRQ7, IRQ10, IRQ11, IRQ15 }; /* * Interrupt conversion table for the HP PC LAN+ */ static unsigned short ed_hpp_intr_mask[] = { 0, /* 0 */ 0, /* 1 */ 0, /* 2 */ IRQ3, /* 3 */ IRQ4, /* 4 */ IRQ5, /* 5 */ IRQ6, /* 6 */ IRQ7, /* 7 */ 0, /* 8 */ IRQ9, /* 9 */ IRQ10, /* 10 */ IRQ11, /* 11 */ IRQ12, /* 12 */ 0, /* 13 */ 0, /* 14 */ IRQ15 /* 15 */ }; /* * Determine if the device is present * * on entry: * a pointer to an isa_device struct * on exit: * NULL if device not found * or # of i/o addresses used (if found) */ static int ed_probe(isa_dev) struct isa_device *isa_dev; { int nports; nports = ed_probe_WD80x3(isa_dev); if (nports) return (nports); nports = ed_probe_3Com(isa_dev); if (nports) return (nports); nports = ed_probe_Novell(isa_dev); if (nports) return (nports); nports = ed_probe_HP_pclanp(isa_dev); if (nports) return (nports); return (0); } /* * Generic probe routine for testing for the existance of a DS8390. * Must be called after the NIC has just been reset. This routine * works by looking at certain register values that are guaranteed * to be initialized a certain way after power-up or reset. Seems * not to currently work on the 83C690. * * Specifically: * * Register reset bits set bits * Command Register (CR) TXP, STA RD2, STP * Interrupt Status (ISR) RST * Interrupt Mask (IMR) All bits * Data Control (DCR) LAS * Transmit Config. (TCR) LB1, LB0 * * We only look at the CR and ISR registers, however, because looking at * the others would require changing register pages (which would be * intrusive if this isn't an 8390). * * Return 1 if 8390 was found, 0 if not. */ static int ed_probe_generic8390(sc) struct ed_softc *sc; { if ((inb(sc->nic_addr + ED_P0_CR) & (ED_CR_RD2 | ED_CR_TXP | ED_CR_STA | ED_CR_STP)) != (ED_CR_RD2 | ED_CR_STP)) return (0); if ((inb(sc->nic_addr + ED_P0_ISR) & ED_ISR_RST) != ED_ISR_RST) return (0); return (1); } /* * Probe and vendor-specific initialization routine for SMC/WD80x3 boards */ static int ed_probe_WD80x3(isa_dev) struct isa_device *isa_dev; { struct ed_softc *sc = &ed_softc[isa_dev->id_unit]; int i; u_int memsize, maddr; u_char iptr, isa16bit, sum; sc->asic_addr = isa_dev->id_iobase; sc->nic_addr = sc->asic_addr + ED_WD_NIC_OFFSET; sc->is790 = 0; #ifdef TOSH_ETHER outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_POW); DELAY(10000); #endif /* * Attempt to do a checksum over the station address PROM. If it * fails, it's probably not a SMC/WD board. There is a problem with * this, though: some clone WD boards don't pass the checksum test. * Danpex boards for one. */ for (sum = 0, i = 0; i < 8; ++i) sum += inb(sc->asic_addr + ED_WD_PROM + i); if (sum != ED_WD_ROM_CHECKSUM_TOTAL) { /* * Checksum is invalid. This often happens with cheap WD8003E * clones. In this case, the checksum byte (the eighth byte) * seems to always be zero. */ if (inb(sc->asic_addr + ED_WD_CARD_ID) != ED_TYPE_WD8003E || inb(sc->asic_addr + ED_WD_PROM + 7) != 0) return (0); } /* reset card to force it into a known state. */ #ifdef TOSH_ETHER outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_RST | ED_WD_MSR_POW); #else outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_RST); #endif DELAY(100); outb(sc->asic_addr + ED_WD_MSR, inb(sc->asic_addr + ED_WD_MSR) & ~ED_WD_MSR_RST); /* wait in the case this card is reading its EEROM */ DELAY(5000); sc->vendor = ED_VENDOR_WD_SMC; sc->type = inb(sc->asic_addr + ED_WD_CARD_ID); /* * Set initial values for width/size. */ memsize = 8192; isa16bit = 0; switch (sc->type) { case ED_TYPE_WD8003S: sc->type_str = "WD8003S"; break; case ED_TYPE_WD8003E: sc->type_str = "WD8003E"; break; case ED_TYPE_WD8003EB: sc->type_str = "WD8003EB"; break; case ED_TYPE_WD8003W: sc->type_str = "WD8003W"; break; case ED_TYPE_WD8013EBT: sc->type_str = "WD8013EBT"; memsize = 16384; isa16bit = 1; break; case ED_TYPE_WD8013W: sc->type_str = "WD8013W"; memsize = 16384; isa16bit = 1; break; case ED_TYPE_WD8013EP: /* also WD8003EP */ if (inb(sc->asic_addr + ED_WD_ICR) & ED_WD_ICR_16BIT) { isa16bit = 1; memsize = 16384; sc->type_str = "WD8013EP"; } else { sc->type_str = "WD8003EP"; } break; case ED_TYPE_WD8013WC: sc->type_str = "WD8013WC"; memsize = 16384; isa16bit = 1; break; case ED_TYPE_WD8013EBP: sc->type_str = "WD8013EBP"; memsize = 16384; isa16bit = 1; break; case ED_TYPE_WD8013EPC: sc->type_str = "WD8013EPC"; memsize = 16384; isa16bit = 1; break; case ED_TYPE_SMC8216C: /* 8216 has 16K shared mem -- 8416 has 8K */ case ED_TYPE_SMC8216T: if (sc->type == ED_TYPE_SMC8216C) { sc->type_str = "SMC8216/SMC8216C"; } else { sc->type_str = "SMC8216T"; } outb(sc->asic_addr + ED_WD790_HWR, inb(sc->asic_addr + ED_WD790_HWR) | ED_WD790_HWR_SWH); switch (inb(sc->asic_addr + ED_WD790_RAR) & ED_WD790_RAR_SZ64) { case ED_WD790_RAR_SZ64: memsize = 65536; break; case ED_WD790_RAR_SZ32: memsize = 32768; break; case ED_WD790_RAR_SZ16: memsize = 16384; break; case ED_WD790_RAR_SZ8: /* 8216 has 16K shared mem -- 8416 has 8K */ if (sc->type == ED_TYPE_SMC8216C) { sc->type_str = "SMC8416C/SMC8416BT"; } else { sc->type_str = "SMC8416T"; } memsize = 8192; break; } outb(sc->asic_addr + ED_WD790_HWR, inb(sc->asic_addr + ED_WD790_HWR) & ~ED_WD790_HWR_SWH); isa16bit = 1; sc->is790 = 1; break; #ifdef TOSH_ETHER case ED_TYPE_TOSHIBA1: sc->type_str = "Toshiba1"; memsize = 32768; isa16bit = 1; break; case ED_TYPE_TOSHIBA4: sc->type_str = "Toshiba4"; memsize = 32768; isa16bit = 1; break; #endif default: sc->type_str = ""; break; } /* * Make some adjustments to initial values depending on what is found * in the ICR. */ if (isa16bit && (sc->type != ED_TYPE_WD8013EBT) #ifdef TOSH_ETHER && (sc->type != ED_TYPE_TOSHIBA1) && (sc->type != ED_TYPE_TOSHIBA4) #endif && ((inb(sc->asic_addr + ED_WD_ICR) & ED_WD_ICR_16BIT) == 0)) { isa16bit = 0; memsize = 8192; } #if ED_DEBUG printf("type = %x type_str=%s isa16bit=%d memsize=%d id_msize=%d\n", sc->type, sc->type_str, isa16bit, memsize, isa_dev->id_msize); for (i = 0; i < 8; i++) printf("%x -> %x\n", i, inb(sc->asic_addr + i)); #endif /* * Allow the user to override the autoconfiguration */ if (isa_dev->id_msize) memsize = isa_dev->id_msize; maddr = (u_int) isa_dev->id_maddr & 0xffffff; if (maddr < 0xa0000 || maddr + memsize > 0x1000000) { printf("ed%d: Invalid ISA memory address range configured: 0x%x - 0x%x\n", isa_dev->id_unit, maddr, maddr + memsize); return 0; } /* * (note that if the user specifies both of the following flags that * '8bit' mode intentionally has precedence) */ if (isa_dev->id_flags & ED_FLAGS_FORCE_16BIT_MODE) isa16bit = 1; if (isa_dev->id_flags & ED_FLAGS_FORCE_8BIT_MODE) isa16bit = 0; /* * If possible, get the assigned interrupt number from the card and * use it. */ if ((sc->type & ED_WD_SOFTCONFIG) && (!sc->is790)) { /* * Assemble together the encoded interrupt number. */ iptr = (inb(isa_dev->id_iobase + ED_WD_ICR) & ED_WD_ICR_IR2) | ((inb(isa_dev->id_iobase + ED_WD_IRR) & (ED_WD_IRR_IR0 | ED_WD_IRR_IR1)) >> 5); /* * If no interrupt specified (or "?"), use what the board tells us. */ if (isa_dev->id_irq <= 0) isa_dev->id_irq = ed_intr_mask[iptr]; /* * Enable the interrupt. */ outb(isa_dev->id_iobase + ED_WD_IRR, inb(isa_dev->id_iobase + ED_WD_IRR) | ED_WD_IRR_IEN); } if (sc->is790) { outb(isa_dev->id_iobase + ED_WD790_HWR, inb(isa_dev->id_iobase + ED_WD790_HWR) | ED_WD790_HWR_SWH); iptr = (((inb(isa_dev->id_iobase + ED_WD790_GCR) & ED_WD790_GCR_IR2) >> 4) | (inb(isa_dev->id_iobase + ED_WD790_GCR) & (ED_WD790_GCR_IR1 | ED_WD790_GCR_IR0)) >> 2); outb(isa_dev->id_iobase + ED_WD790_HWR, inb(isa_dev->id_iobase + ED_WD790_HWR) & ~ED_WD790_HWR_SWH); /* * If no interrupt specified (or "?"), use what the board tells us. */ if (isa_dev->id_irq <= 0) isa_dev->id_irq = ed_790_intr_mask[iptr]; /* * Enable interrupts. */ outb(isa_dev->id_iobase + ED_WD790_ICR, inb(isa_dev->id_iobase + ED_WD790_ICR) | ED_WD790_ICR_EIL); } if (isa_dev->id_irq <= 0) { printf("ed%d: %s cards don't support auto-detected/assigned interrupts.\n", isa_dev->id_unit, sc->type_str); return (0); } sc->isa16bit = isa16bit; sc->mem_shared = 1; isa_dev->id_msize = memsize; sc->mem_start = (caddr_t) isa_dev->id_maddr; /* * allocate one xmit buffer if < 16k, two buffers otherwise */ if ((memsize < 16384) || (isa_dev->id_flags & ED_FLAGS_NO_MULTI_BUFFERING)) { sc->txb_cnt = 1; } else { sc->txb_cnt = 2; } sc->tx_page_start = ED_WD_PAGE_OFFSET; sc->rec_page_start = ED_WD_PAGE_OFFSET + ED_TXBUF_SIZE * sc->txb_cnt; sc->rec_page_stop = ED_WD_PAGE_OFFSET + memsize / ED_PAGE_SIZE; sc->mem_ring = sc->mem_start + (ED_PAGE_SIZE * sc->rec_page_start); sc->mem_size = memsize; sc->mem_end = sc->mem_start + memsize; /* * Get station address from on-board ROM */ for (i = 0; i < ETHER_ADDR_LEN; ++i) sc->arpcom.ac_enaddr[i] = inb(sc->asic_addr + ED_WD_PROM + i); /* * Set upper address bits and 8/16 bit access to shared memory. */ if (isa16bit) { if (sc->is790) { sc->wd_laar_proto = inb(sc->asic_addr + ED_WD_LAAR); } else { sc->wd_laar_proto = ED_WD_LAAR_L16EN | ((kvtop(sc->mem_start) >> 19) & ED_WD_LAAR_ADDRHI); } /* * Enable 16bit access */ outb(sc->asic_addr + ED_WD_LAAR, sc->wd_laar_proto | ED_WD_LAAR_M16EN); } else { if (((sc->type & ED_WD_SOFTCONFIG) || #ifdef TOSH_ETHER (sc->type == ED_TYPE_TOSHIBA1) || (sc->type == ED_TYPE_TOSHIBA4) || #endif (sc->type == ED_TYPE_WD8013EBT)) && (!sc->is790)) { sc->wd_laar_proto = (kvtop(sc->mem_start) >> 19) & ED_WD_LAAR_ADDRHI; outb(sc->asic_addr + ED_WD_LAAR, sc->wd_laar_proto); } } /* * Set address and enable interface shared memory. */ if (!sc->is790) { #ifdef TOSH_ETHER outb(sc->asic_addr + ED_WD_MSR + 1, ((kvtop(sc->mem_start) >> 8) & 0xe0) | 4); outb(sc->asic_addr + ED_WD_MSR + 2, ((kvtop(sc->mem_start) >> 16) & 0x0f)); outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_MENB | ED_WD_MSR_POW); #else outb(sc->asic_addr + ED_WD_MSR, ((kvtop(sc->mem_start) >> 13) & ED_WD_MSR_ADDR) | ED_WD_MSR_MENB); #endif sc->cr_proto = ED_CR_RD2; } else { outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_MENB); outb(sc->asic_addr + ED_WD790_HWR, (inb(sc->asic_addr + ED_WD790_HWR) | ED_WD790_HWR_SWH)); outb(sc->asic_addr + ED_WD790_RAR, ((kvtop(sc->mem_start) >> 13) & 0x0f) | ((kvtop(sc->mem_start) >> 11) & 0x40) | (inb(sc->asic_addr + ED_WD790_RAR) & 0xb0)); outb(sc->asic_addr + ED_WD790_HWR, (inb(sc->asic_addr + ED_WD790_HWR) & ~ED_WD790_HWR_SWH)); sc->cr_proto = 0; } #if 0 printf("starting memory performance test at 0x%x, size %d...\n", sc->mem_start, memsize*16384); for (i = 0; i < 16384; i++) bzero(sc->mem_start, memsize); printf("***DONE***\n"); #endif /* * Now zero memory and verify that it is clear */ bzero(sc->mem_start, memsize); for (i = 0; i < memsize; ++i) { if (sc->mem_start[i]) { printf("ed%d: failed to clear shared memory at %lx - check configuration\n", isa_dev->id_unit, kvtop(sc->mem_start + i)); /* * Disable 16 bit access to shared memory */ if (isa16bit) { if (sc->is790) { outb(sc->asic_addr + ED_WD_MSR, 0x00); } outb(sc->asic_addr + ED_WD_LAAR, sc->wd_laar_proto & ~ED_WD_LAAR_M16EN); } return (0); } } /* * Disable 16bit access to shared memory - we leave it * disabled so that 1) machines reboot properly when the board * is set 16 bit mode and there are conflicting 8bit * devices/ROMS in the same 128k address space as this boards * shared memory. and 2) so that other 8 bit devices with * shared memory can be used in this 128k region, too. */ if (isa16bit) { if (sc->is790) { outb(sc->asic_addr + ED_WD_MSR, 0x00); } outb(sc->asic_addr + ED_WD_LAAR, sc->wd_laar_proto & ~ED_WD_LAAR_M16EN); } return (ED_WD_IO_PORTS); } /* * Probe and vendor-specific initialization routine for 3Com 3c503 boards */ static int ed_probe_3Com(isa_dev) struct isa_device *isa_dev; { struct ed_softc *sc = &ed_softc[isa_dev->id_unit]; int i; u_int memsize; u_char isa16bit; sc->asic_addr = isa_dev->id_iobase + ED_3COM_ASIC_OFFSET; sc->nic_addr = isa_dev->id_iobase + ED_3COM_NIC_OFFSET; /* * Verify that the kernel configured I/O address matches the board * configured address */ switch (inb(sc->asic_addr + ED_3COM_BCFR)) { case ED_3COM_BCFR_300: if (isa_dev->id_iobase != 0x300) return (0); break; case ED_3COM_BCFR_310: if (isa_dev->id_iobase != 0x310) return (0); break; case ED_3COM_BCFR_330: if (isa_dev->id_iobase != 0x330) return (0); break; case ED_3COM_BCFR_350: if (isa_dev->id_iobase != 0x350) return (0); break; case ED_3COM_BCFR_250: if (isa_dev->id_iobase != 0x250) return (0); break; case ED_3COM_BCFR_280: if (isa_dev->id_iobase != 0x280) return (0); break; case ED_3COM_BCFR_2A0: if (isa_dev->id_iobase != 0x2a0) return (0); break; case ED_3COM_BCFR_2E0: if (isa_dev->id_iobase != 0x2e0) return (0); break; default: return (0); } /* * Verify that the kernel shared memory address matches the board * configured address. */ switch (inb(sc->asic_addr + ED_3COM_PCFR)) { case ED_3COM_PCFR_DC000: if (kvtop(isa_dev->id_maddr) != 0xdc000) return (0); break; case ED_3COM_PCFR_D8000: if (kvtop(isa_dev->id_maddr) != 0xd8000) return (0); break; case ED_3COM_PCFR_CC000: if (kvtop(isa_dev->id_maddr) != 0xcc000) return (0); break; case ED_3COM_PCFR_C8000: if (kvtop(isa_dev->id_maddr) != 0xc8000) return (0); break; default: return (0); } /* * Reset NIC and ASIC. Enable on-board transceiver throughout reset * sequence because it'll lock up if the cable isn't connected if we * don't. */ outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_RST | ED_3COM_CR_XSEL); /* * Wait for a while, then un-reset it */ DELAY(50); /* * The 3Com ASIC defaults to rather strange settings for the CR after * a reset - it's important to set it again after the following outb * (this is done when we map the PROM below). */ outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_XSEL); /* * Wait a bit for the NIC to recover from the reset */ DELAY(5000); sc->vendor = ED_VENDOR_3COM; sc->type_str = "3c503"; sc->mem_shared = 1; sc->cr_proto = ED_CR_RD2; /* * Hmmm...a 16bit 3Com board has 16k of memory, but only an 8k window * to it. */ memsize = 8192; /* * Get station address from on-board ROM */ /* * First, map ethernet address PROM over the top of where the NIC * registers normally appear. */ outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_EALO | ED_3COM_CR_XSEL); for (i = 0; i < ETHER_ADDR_LEN; ++i) sc->arpcom.ac_enaddr[i] = inb(sc->nic_addr + i); /* * Unmap PROM - select NIC registers. The proper setting of the * tranceiver is set in ed_init so that the attach code is given a * chance to set the default based on a compile-time config option */ outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_XSEL); /* * Determine if this is an 8bit or 16bit board */ /* * select page 0 registers */ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_STP); /* * Attempt to clear WTS bit. If it doesn't clear, then this is a 16bit * board. */ outb(sc->nic_addr + ED_P0_DCR, 0); /* * select page 2 registers */ outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_2 | ED_CR_RD2 | ED_CR_STP); /* * The 3c503 forces the WTS bit to a one if this is a 16bit board */ if (inb(sc->nic_addr + ED_P2_DCR) & ED_DCR_WTS) isa16bit = 1; else isa16bit = 0; /* * select page 0 registers */ outb(sc->nic_addr + ED_P2_CR, ED_CR_RD2 | ED_CR_STP); sc->mem_start = (caddr_t) isa_dev->id_maddr; sc->mem_size = memsize; sc->mem_end = sc->mem_start + memsize; /* * We have an entire 8k window to put the transmit buffers on the * 16bit boards. But since the 16bit 3c503's shared memory is only * fast enough to overlap the loading of one full-size packet, trying * to load more than 2 buffers can actually leave the transmitter idle * during the load. So 2 seems the best value. (Although a mix of * variable-sized packets might change this assumption. Nonetheless, * we optimize for linear transfers of same-size packets.) */ if (isa16bit) { if (isa_dev->id_flags & ED_FLAGS_NO_MULTI_BUFFERING) sc->txb_cnt = 1; else sc->txb_cnt = 2; sc->tx_page_start = ED_3COM_TX_PAGE_OFFSET_16BIT; sc->rec_page_start = ED_3COM_RX_PAGE_OFFSET_16BIT; sc->rec_page_stop = memsize / ED_PAGE_SIZE + ED_3COM_RX_PAGE_OFFSET_16BIT; sc->mem_ring = sc->mem_start; } else { sc->txb_cnt = 1; sc->tx_page_start = ED_3COM_TX_PAGE_OFFSET_8BIT; sc->rec_page_start = ED_TXBUF_SIZE + ED_3COM_TX_PAGE_OFFSET_8BIT; sc->rec_page_stop = memsize / ED_PAGE_SIZE + ED_3COM_TX_PAGE_OFFSET_8BIT; sc->mem_ring = sc->mem_start + (ED_PAGE_SIZE * ED_TXBUF_SIZE); } sc->isa16bit = isa16bit; /* * Initialize GA page start/stop registers. Probably only needed if * doing DMA, but what the hell. */ outb(sc->asic_addr + ED_3COM_PSTR, sc->rec_page_start); outb(sc->asic_addr + ED_3COM_PSPR, sc->rec_page_stop); /* * Set IRQ. 3c503 only allows a choice of irq 2-5. */ switch (isa_dev->id_irq) { case IRQ2: outb(sc->asic_addr + ED_3COM_IDCFR, ED_3COM_IDCFR_IRQ2); break; case IRQ3: outb(sc->asic_addr + ED_3COM_IDCFR, ED_3COM_IDCFR_IRQ3); break; case IRQ4: outb(sc->asic_addr + ED_3COM_IDCFR, ED_3COM_IDCFR_IRQ4); break; case IRQ5: outb(sc->asic_addr + ED_3COM_IDCFR, ED_3COM_IDCFR_IRQ5); break; default: printf("ed%d: Invalid irq configuration (%d) must be 3-5,9 for 3c503\n", isa_dev->id_unit, ffs(isa_dev->id_irq) - 1); return (0); } /* * Initialize GA configuration register. Set bank and enable shared * mem. */ outb(sc->asic_addr + ED_3COM_GACFR, ED_3COM_GACFR_RSEL | ED_3COM_GACFR_MBS0); /* * Initialize "Vector Pointer" registers. These gawd-awful things are * compared to 20 bits of the address on ISA, and if they match, the * shared memory is disabled. We set them to 0xffff0...allegedly the * reset vector. */ outb(sc->asic_addr + ED_3COM_VPTR2, 0xff); outb(sc->asic_addr + ED_3COM_VPTR1, 0xff); outb(sc->asic_addr + ED_3COM_VPTR0, 0x00); /* * Zero memory and verify that it is clear */ bzero(sc->mem_start, memsize); for (i = 0; i < memsize; ++i) if (sc->mem_start[i]) { printf("ed%d: failed to clear shared memory at %lx - check configuration\n", isa_dev->id_unit, kvtop(sc->mem_start + i)); return (0); } isa_dev->id_msize = memsize; return (ED_3COM_IO_PORTS); } /* * Probe and vendor-specific initialization routine for NE1000/2000 boards */ static int ed_probe_Novell_generic(sc, port, unit, flags) struct ed_softc *sc; int port; int unit; int flags; { u_int memsize, n; u_char romdata[16], tmp; static char test_pattern[32] = "THIS is A memory TEST pattern"; char test_buffer[32]; sc->asic_addr = port + ED_NOVELL_ASIC_OFFSET; sc->nic_addr = port + ED_NOVELL_NIC_OFFSET; /* XXX - do Novell-specific probe here */ /* Reset the board */ #ifdef GWETHER outb(sc->asic_addr + ED_NOVELL_RESET, 0); DELAY(200); #endif /* GWETHER */ tmp = inb(sc->asic_addr + ED_NOVELL_RESET); /* * I don't know if this is necessary; probably cruft leftover from * Clarkson packet driver code. Doesn't do a thing on the boards I've * tested. -DG [note that a outb(0x84, 0) seems to work here, and is * non-invasive...but some boards don't seem to reset and I don't have * complete documentation on what the 'right' thing to do is...so we * do the invasive thing for now. Yuck.] */ outb(sc->asic_addr + ED_NOVELL_RESET, tmp); DELAY(5000); /* * This is needed because some NE clones apparently don't reset the * NIC properly (or the NIC chip doesn't reset fully on power-up) XXX * - this makes the probe invasive! ...Done against my better * judgement. -DLG */ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_STP); DELAY(5000); /* Make sure that we really have an 8390 based board */ if (!ed_probe_generic8390(sc)) return (0); sc->vendor = ED_VENDOR_NOVELL; sc->mem_shared = 0; sc->cr_proto = ED_CR_RD2; /* * Test the ability to read and write to the NIC memory. This has the * side affect of determining if this is an NE1000 or an NE2000. */ /* * This prevents packets from being stored in the NIC memory when the * readmem routine turns on the start bit in the CR. */ outb(sc->nic_addr + ED_P0_RCR, ED_RCR_MON); /* Temporarily initialize DCR for byte operations */ outb(sc->nic_addr + ED_P0_DCR, ED_DCR_FT1 | ED_DCR_LS); outb(sc->nic_addr + ED_P0_PSTART, 8192 / ED_PAGE_SIZE); outb(sc->nic_addr + ED_P0_PSTOP, 16384 / ED_PAGE_SIZE); sc->isa16bit = 0; /* * Write a test pattern in byte mode. If this fails, then there * probably isn't any memory at 8k - which likely means that the board * is an NE2000. */ ed_pio_writemem(sc, test_pattern, 8192, sizeof(test_pattern)); ed_pio_readmem(sc, 8192, test_buffer, sizeof(test_pattern)); if (bcmp(test_pattern, test_buffer, sizeof(test_pattern))) { /* not an NE1000 - try NE2000 */ outb(sc->nic_addr + ED_P0_DCR, ED_DCR_WTS | ED_DCR_FT1 | ED_DCR_LS); outb(sc->nic_addr + ED_P0_PSTART, 16384 / ED_PAGE_SIZE); outb(sc->nic_addr + ED_P0_PSTOP, 32768 / ED_PAGE_SIZE); sc->isa16bit = 1; /* * Write a test pattern in word mode. If this also fails, then * we don't know what this board is. */ ed_pio_writemem(sc, test_pattern, 16384, sizeof(test_pattern)); ed_pio_readmem(sc, 16384, test_buffer, sizeof(test_pattern)); if (bcmp(test_pattern, test_buffer, sizeof(test_pattern))) return (0); /* not an NE2000 either */ sc->type = ED_TYPE_NE2000; sc->type_str = "NE2000"; } else { sc->type = ED_TYPE_NE1000; sc->type_str = "NE1000"; } /* 8k of memory plus an additional 8k if 16bit */ memsize = 8192 + sc->isa16bit * 8192; #if 0 /* probably not useful - NE boards only come two ways */ /* allow kernel config file overrides */ if (isa_dev->id_msize) memsize = isa_dev->id_msize; #endif sc->mem_size = memsize; /* NIC memory doesn't start at zero on an NE board */ /* The start address is tied to the bus width */ sc->mem_start = (char *) 8192 + sc->isa16bit * 8192; sc->mem_end = sc->mem_start + memsize; sc->tx_page_start = memsize / ED_PAGE_SIZE; #ifdef GWETHER { int x, i, mstart = 0, msize = 0; char pbuf0[ED_PAGE_SIZE], pbuf[ED_PAGE_SIZE], tbuf[ED_PAGE_SIZE]; for (i = 0; i < ED_PAGE_SIZE; i++) pbuf0[i] = 0; /* Clear all the memory. */ for (x = 1; x < 256; x++) ed_pio_writemem(sc, pbuf0, x * 256, ED_PAGE_SIZE); /* Search for the start of RAM. */ for (x = 1; x < 256; x++) { ed_pio_readmem(sc, x * 256, tbuf, ED_PAGE_SIZE); if (bcmp(pbuf0, tbuf, ED_PAGE_SIZE) == 0) { for (i = 0; i < ED_PAGE_SIZE; i++) pbuf[i] = 255 - x; ed_pio_writemem(sc, pbuf, x * 256, ED_PAGE_SIZE); ed_pio_readmem(sc, x * 256, tbuf, ED_PAGE_SIZE); if (bcmp(pbuf, tbuf, ED_PAGE_SIZE) == 0) { mstart = x * ED_PAGE_SIZE; msize = ED_PAGE_SIZE; break; } } } if (mstart == 0) { printf("ed%d: Cannot find start of RAM.\n", unit); return 0; } /* Search for the start of RAM. */ for (x = (mstart / ED_PAGE_SIZE) + 1; x < 256; x++) { ed_pio_readmem(sc, x * 256, tbuf, ED_PAGE_SIZE); if (bcmp(pbuf0, tbuf, ED_PAGE_SIZE) == 0) { for (i = 0; i < ED_PAGE_SIZE; i++) pbuf[i] = 255 - x; ed_pio_writemem(sc, pbuf, x * 256, ED_PAGE_SIZE); ed_pio_readmem(sc, x * 256, tbuf, ED_PAGE_SIZE); if (bcmp(pbuf, tbuf, ED_PAGE_SIZE) == 0) msize += ED_PAGE_SIZE; else { break; } } else { break; } } if (msize == 0) { printf("ed%d: Cannot find any RAM, start : %d, x = %d.\n", unit, mstart, x); return 0; } printf("ed%d: RAM start at %d, size : %d.\n", unit, mstart, msize); sc->mem_size = msize; sc->mem_start = (char *) mstart; sc->mem_end = (char *) (msize + mstart); sc->tx_page_start = mstart / ED_PAGE_SIZE; } #endif /* GWETHER */ /* * Use one xmit buffer if < 16k, two buffers otherwise (if not told * otherwise). */ if ((memsize < 16384) || (flags & ED_FLAGS_NO_MULTI_BUFFERING)) sc->txb_cnt = 1; else sc->txb_cnt = 2; sc->rec_page_start = sc->tx_page_start + sc->txb_cnt * ED_TXBUF_SIZE; sc->rec_page_stop = sc->tx_page_start + memsize / ED_PAGE_SIZE; sc->mem_ring = sc->mem_start + sc->txb_cnt * ED_PAGE_SIZE * ED_TXBUF_SIZE; ed_pio_readmem(sc, 0, romdata, 16); for (n = 0; n < ETHER_ADDR_LEN; n++) sc->arpcom.ac_enaddr[n] = romdata[n * (sc->isa16bit + 1)]; #ifdef GWETHER if (sc->arpcom.ac_enaddr[2] == 0x86) { sc->type_str = "Gateway AT"; } #endif /* GWETHER */ /* clear any pending interrupts that might have occurred above */ outb(sc->nic_addr + ED_P0_ISR, 0xff); return (ED_NOVELL_IO_PORTS); } static int ed_probe_Novell(isa_dev) struct isa_device *isa_dev; { struct ed_softc *sc = &ed_softc[isa_dev->id_unit]; isa_dev->id_maddr = 0; return ed_probe_Novell_generic(sc, isa_dev->id_iobase, isa_dev->id_unit, isa_dev->id_flags); } #if NCARD > 0 /* * Probe framework for pccards. Replicates the standard framework, * minus the pccard driver registration and ignores the ether address * supplied (from the CIS), relying on the probe to find it instead. */ static int ed_probe_pccard(isa_dev, ether) struct isa_device *isa_dev; u_char *ether; { int nports; nports = ed_probe_WD80x3(isa_dev); if (nports) return (nports); nports = ed_probe_Novell(isa_dev); if (nports) return (nports); return (0); } #endif /* NCARD > 0 */ #define ED_HPP_TEST_SIZE 16 /* * Probe and vendor specific initialization for the HP PC Lan+ Cards. * (HP Part nos: 27247B and 27252A). * * The card has an asic wrapper around a DS8390 core. The asic handles * host accesses and offers both standard register IO and memory mapped * IO. Memory mapped I/O allows better performance at the expense of greater * chance of an incompatibility with existing ISA cards. * * The card has a few caveats: it isn't tolerant of byte wide accesses, only * short (16 bit) or word (32 bit) accesses are allowed. Some card revisions * don't allow 32 bit accesses; these are indicated by a bit in the software * ID register (see if_edreg.h). * * Other caveats are: we should read the MAC address only when the card * is inactive. * * For more information; please consult the CRYNWR packet driver. * * The AUI port is turned on using the "link2" option on the ifconfig * command line. */ static int ed_probe_HP_pclanp(isa_dev) struct isa_device *isa_dev; { struct ed_softc *sc = &ed_softc[isa_dev->id_unit]; int n; /* temp var */ int memsize; /* mem on board */ u_char checksum; /* checksum of board address */ u_char irq; /* board configured IRQ */ char test_pattern[ED_HPP_TEST_SIZE]; /* read/write areas for */ char test_buffer[ED_HPP_TEST_SIZE]; /* probing card */ /* Fill in basic information */ sc->asic_addr = isa_dev->id_iobase + ED_HPP_ASIC_OFFSET; sc->nic_addr = isa_dev->id_iobase + ED_HPP_NIC_OFFSET; sc->is790 = 0; sc->isa16bit = 0; /* the 8390 core needs to be in byte mode */ /* * Look for the HP PCLAN+ signature: "0x50,0x48,0x00,0x53" */ if ((inb(sc->asic_addr + ED_HPP_ID) != 0x50) || (inb(sc->asic_addr + ED_HPP_ID + 1) != 0x48) || ((inb(sc->asic_addr + ED_HPP_ID + 2) & 0xF0) != 0) || (inb(sc->asic_addr + ED_HPP_ID + 3) != 0x53)) return 0; /* * Read the MAC address and verify checksum on the address. */ outw(sc->asic_addr + ED_HPP_PAGING, ED_HPP_PAGE_MAC); for (n = 0, checksum = 0; n < ETHER_ADDR_LEN; n++) checksum += (sc->arpcom.ac_enaddr[n] = inb(sc->asic_addr + ED_HPP_MAC_ADDR + n)); checksum += inb(sc->asic_addr + ED_HPP_MAC_ADDR + ETHER_ADDR_LEN); if (checksum != 0xFF) return 0; /* * Verify that the software model number is 0. */ outw(sc->asic_addr + ED_HPP_PAGING, ED_HPP_PAGE_ID); if (((sc->hpp_id = inw(sc->asic_addr + ED_HPP_PAGE_4)) & ED_HPP_ID_SOFT_MODEL_MASK) != 0x0000) return 0; /* * Read in and save the current options configured on card. */ sc->hpp_options = inw(sc->asic_addr + ED_HPP_OPTION); sc->hpp_options |= (ED_HPP_OPTION_NIC_RESET | ED_HPP_OPTION_CHIP_RESET | ED_HPP_OPTION_ENABLE_IRQ); /* * Reset the chip. This requires writing to the option register * so take care to preserve the other bits. */ outw(sc->asic_addr + ED_HPP_OPTION, (sc->hpp_options & ~(ED_HPP_OPTION_NIC_RESET | ED_HPP_OPTION_CHIP_RESET))); DELAY(5000); /* wait for chip reset to complete */ outw(sc->asic_addr + ED_HPP_OPTION, (sc->hpp_options | (ED_HPP_OPTION_NIC_RESET | ED_HPP_OPTION_CHIP_RESET | ED_HPP_OPTION_ENABLE_IRQ))); DELAY(5000); if (!(inb(sc->nic_addr + ED_P0_ISR) & ED_ISR_RST)) return 0; /* reset did not complete */ /* * Read out configuration information. */ outw(sc->asic_addr + ED_HPP_PAGING, ED_HPP_PAGE_HW); irq = inb(sc->asic_addr + ED_HPP_HW_IRQ); /* * Check for impossible IRQ. */ if (irq >= (sizeof(ed_hpp_intr_mask) / sizeof(ed_hpp_intr_mask[0]))) return 0; /* * If the kernel IRQ was specified with a '?' use the cards idea * of the IRQ. If the kernel IRQ was explicitly specified, it * should match that of the hardware. */ if (isa_dev->id_irq <= 0) isa_dev->id_irq = ed_hpp_intr_mask[irq]; else if (isa_dev->id_irq != ed_hpp_intr_mask[irq]) return 0; /* * Fill in softconfig info. */ sc->vendor = ED_VENDOR_HP; sc->type = ED_TYPE_HP_PCLANPLUS; sc->type_str = "HP-PCLAN+"; sc->mem_shared = 0; /* we DON'T have dual ported RAM */ sc->mem_start = 0; /* we use offsets inside the card RAM */ sc->hpp_mem_start = NULL;/* no memory mapped I/O by default */ /* * Check if memory mapping of the I/O registers possible. */ if (sc->hpp_options & ED_HPP_OPTION_MEM_ENABLE) { u_long mem_addr; /* * determine the memory address from the board. */ outw(sc->asic_addr + ED_HPP_PAGING, ED_HPP_PAGE_HW); mem_addr = (inw(sc->asic_addr + ED_HPP_HW_MEM_MAP) << 8); /* * Check that the kernel specified start of memory and * hardware's idea of it match. */ if (mem_addr != kvtop(isa_dev->id_maddr)) return 0; sc->hpp_mem_start = isa_dev->id_maddr; } /* * The board has 32KB of memory. Is there a way to determine * this programmatically? */ memsize = 32768; /* * Fill in the rest of the soft config structure. */ /* * The transmit page index. */ sc->tx_page_start = ED_HPP_TX_PAGE_OFFSET; if (isa_dev->id_flags & ED_FLAGS_NO_MULTI_BUFFERING) sc->txb_cnt = 1; else sc->txb_cnt = 2; /* * Memory description */ sc->mem_size = memsize; sc->mem_ring = sc->mem_start + (sc->txb_cnt * ED_PAGE_SIZE * ED_TXBUF_SIZE); sc->mem_end = sc->mem_start + sc->mem_size; /* * Receive area starts after the transmit area and * continues till the end of memory. */ sc->rec_page_start = sc->tx_page_start + (sc->txb_cnt * ED_TXBUF_SIZE); sc->rec_page_stop = (sc->mem_size / ED_PAGE_SIZE); sc->cr_proto = 0; /* value works */ /* * Set the wrap registers for string I/O reads. */ outw(sc->asic_addr + ED_HPP_PAGING, ED_HPP_PAGE_HW); outw(sc->asic_addr + ED_HPP_HW_WRAP, ((sc->rec_page_start / ED_PAGE_SIZE) | (((sc->rec_page_stop / ED_PAGE_SIZE) - 1) << 8))); /* * Reset the register page to normal operation. */ outw(sc->asic_addr + ED_HPP_PAGING, ED_HPP_PAGE_PERF); /* * Verify that we can read/write from adapter memory. * Create test pattern. */ for (n = 0; n < ED_HPP_TEST_SIZE; n++) { test_pattern[n] = (n*n) ^ ~n; } #undef ED_HPP_TEST_SIZE /* * Check that the memory is accessible thru the I/O ports. * Write out the contents of "test_pattern", read back * into "test_buffer" and compare the two for any * mismatch. */ for (n = 0; n < (32768 / ED_PAGE_SIZE); n ++) { ed_pio_writemem(sc, test_pattern, (n * ED_PAGE_SIZE), sizeof(test_pattern)); ed_pio_readmem(sc, (n * ED_PAGE_SIZE), test_buffer, sizeof(test_pattern)); if (bcmp(test_pattern, test_buffer, sizeof(test_pattern))) return 0; } return (ED_HPP_IO_PORTS); } /* * HP PC Lan+ : Set the physical link to use AUI or TP/TL. */ void ed_hpp_set_physical_link(struct ed_softc *sc) { struct ifnet *ifp = &sc->arpcom.ac_if; int lan_page; outw(sc->asic_addr + ED_HPP_PAGING, ED_HPP_PAGE_LAN); lan_page = inw(sc->asic_addr + ED_HPP_PAGE_0); if (ifp->if_flags & IFF_ALTPHYS) { /* * Use the AUI port. */ lan_page |= ED_HPP_LAN_AUI; outw(sc->asic_addr + ED_HPP_PAGING, ED_HPP_PAGE_LAN); outw(sc->asic_addr + ED_HPP_PAGE_0, lan_page); } else { /* * Use the ThinLan interface */ lan_page &= ~ED_HPP_LAN_AUI; outw(sc->asic_addr + ED_HPP_PAGING, ED_HPP_PAGE_LAN); outw(sc->asic_addr + ED_HPP_PAGE_0, lan_page); } /* * Wait for the lan card to re-initialize itself */ DELAY(150000); /* wait 150 ms */ /* * Restore normal pages. */ outw(sc->asic_addr + ED_HPP_PAGING, ED_HPP_PAGE_PERF); } /* * Install interface into kernel networking data structures */ static int ed_attach(sc, unit, flags) struct ed_softc *sc; int unit; int flags; { struct ifnet *ifp = &sc->arpcom.ac_if; /* * Set interface to stopped condition (reset) */ ed_stop(sc); if (!ifp->if_name) { /* * Initialize ifnet structure */ ifp->if_softc = sc; ifp->if_unit = unit; ifp->if_name = "ed"; ifp->if_output = ether_output; ifp->if_start = ed_start; ifp->if_ioctl = ed_ioctl; ifp->if_watchdog = ed_watchdog; ifp->if_init = ed_init; ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; ifp->if_linkmib = &sc->mibdata; ifp->if_linkmiblen = sizeof sc->mibdata; /* * XXX - should do a better job. */ if (sc->is790) sc->mibdata.dot3StatsEtherChipSet = DOT3CHIPSET(dot3VendorWesternDigital, dot3ChipSetWesternDigital83C790); else sc->mibdata.dot3StatsEtherChipSet = DOT3CHIPSET(dot3VendorNational, dot3ChipSetNational8390); sc->mibdata.dot3Compliance = DOT3COMPLIANCE_COLLS; /* * Set default state for ALTPHYS flag (used to disable the * tranceiver for AUI operation), based on compile-time * config option. */ if (flags & ED_FLAGS_DISABLE_TRANCEIVER) ifp->if_flags = (IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST | IFF_ALTPHYS); else ifp->if_flags = (IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST); /* * Attach the interface */ if_attach(ifp); ether_ifattach(ifp); } /* device attach does transition from UNCONFIGURED to IDLE state */ /* * Print additional info when attached */ printf("%s%d: address %6D, ", ifp->if_name, ifp->if_unit, sc->arpcom.ac_enaddr, ":"); if (sc->type_str && (*sc->type_str != 0)) printf("type %s ", sc->type_str); else printf("type unknown (0x%x) ", sc->type); if (sc->vendor == ED_VENDOR_HP) printf("(%s %s IO)", (sc->hpp_id & ED_HPP_ID_16_BIT_ACCESS) ? "16-bit" : "32-bit", sc->hpp_mem_start ? "memory mapped" : "regular"); else printf("%s ", sc->isa16bit ? "(16 bit)" : "(8 bit)"); printf("%s\n", (((sc->vendor == ED_VENDOR_3COM) || (sc->vendor == ED_VENDOR_HP)) && (ifp->if_flags & IFF_ALTPHYS)) ? " tranceiver disabled" : ""); /* * If BPF is in the kernel, call the attach for it */ #if NBPFILTER > 0 bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); #endif return 1; } static int ed_attach_isa(isa_dev) struct isa_device *isa_dev; { int unit = isa_dev->id_unit; struct ed_softc *sc = &ed_softc[unit]; int flags = isa_dev->id_flags; isa_dev->id_ointr = edintr; return ed_attach(sc, unit, flags); } #if NPCI > 0 void * ed_attach_NE2000_pci(unit, port) int unit; int port; { struct ed_softc *sc = malloc(sizeof *sc, M_DEVBUF, M_NOWAIT); int isa_flags = 0; if (!sc) return sc; bzero(sc, sizeof *sc); if (ed_probe_Novell_generic(sc, port, unit, isa_flags) == 0 || ed_attach(sc, unit, isa_flags) == 0) { free(sc, M_DEVBUF); return NULL; } return sc; } #endif /* * Reset interface. */ static void ed_reset(ifp) struct ifnet *ifp; { struct ed_softc *sc = ifp->if_softc; int s; if (sc->gone) return; s = splimp(); /* * Stop interface and re-initialize. */ ed_stop(sc); ed_init(sc); (void) splx(s); } /* * Take interface offline. */ static void ed_stop(sc) struct ed_softc *sc; { int n = 5000; if (sc->gone) return; /* * Stop everything on the interface, and select page 0 registers. */ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_STP); /* * Wait for interface to enter stopped state, but limit # of checks to * 'n' (about 5ms). It shouldn't even take 5us on modern DS8390's, but * just in case it's an old one. */ while (((inb(sc->nic_addr + ED_P0_ISR) & ED_ISR_RST) == 0) && --n); } /* * Device timeout/watchdog routine. Entered if the device neglects to * generate an interrupt after a transmit has been started on it. */ static void ed_watchdog(ifp) struct ifnet *ifp; { struct ed_softc *sc = ifp->if_softc; if (sc->gone) return; log(LOG_ERR, "ed%d: device timeout\n", ifp->if_unit); ifp->if_oerrors++; ed_reset(ifp); } /* * Initialize device. */ static void ed_init(xsc) void *xsc; { struct ed_softc *sc = xsc; struct ifnet *ifp = &sc->arpcom.ac_if; int i, s; if (sc->gone) return; /* address not known */ if (TAILQ_EMPTY(&ifp->if_addrhead)) /* unlikely? XXX */ return; /* * Initialize the NIC in the exact order outlined in the NS manual. * This init procedure is "mandatory"...don't change what or when * things happen. */ s = splimp(); /* reset transmitter flags */ sc->xmit_busy = 0; ifp->if_timer = 0; sc->txb_inuse = 0; sc->txb_new = 0; sc->txb_next_tx = 0; /* This variable is used below - don't move this assignment */ sc->next_packet = sc->rec_page_start + 1; /* * Set interface for page 0, Remote DMA complete, Stopped */ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_STP); if (sc->isa16bit) { /* * Set FIFO threshold to 8, No auto-init Remote DMA, byte * order=80x86, word-wide DMA xfers, */ outb(sc->nic_addr + ED_P0_DCR, ED_DCR_FT1 | ED_DCR_WTS | ED_DCR_LS); } else { /* * Same as above, but byte-wide DMA xfers */ outb(sc->nic_addr + ED_P0_DCR, ED_DCR_FT1 | ED_DCR_LS); } /* * Clear Remote Byte Count Registers */ outb(sc->nic_addr + ED_P0_RBCR0, 0); outb(sc->nic_addr + ED_P0_RBCR1, 0); /* * For the moment, don't store incoming packets in memory. */ outb(sc->nic_addr + ED_P0_RCR, ED_RCR_MON); /* * Place NIC in internal loopback mode */ outb(sc->nic_addr + ED_P0_TCR, ED_TCR_LB0); /* * Initialize transmit/receive (ring-buffer) Page Start */ outb(sc->nic_addr + ED_P0_TPSR, sc->tx_page_start); outb(sc->nic_addr + ED_P0_PSTART, sc->rec_page_start); /* Set lower bits of byte addressable framing to 0 */ if (sc->is790) outb(sc->nic_addr + 0x09, 0); /* * Initialize Receiver (ring-buffer) Page Stop and Boundry */ outb(sc->nic_addr + ED_P0_PSTOP, sc->rec_page_stop); outb(sc->nic_addr + ED_P0_BNRY, sc->rec_page_start); /* * Clear all interrupts. A '1' in each bit position clears the * corresponding flag. */ outb(sc->nic_addr + ED_P0_ISR, 0xff); /* * Enable the following interrupts: receive/transmit complete, * receive/transmit error, and Receiver OverWrite. * * Counter overflow and Remote DMA complete are *not* enabled. */ outb(sc->nic_addr + ED_P0_IMR, ED_IMR_PRXE | ED_IMR_PTXE | ED_IMR_RXEE | ED_IMR_TXEE | ED_IMR_OVWE); /* * Program Command Register for page 1 */ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_PAGE_1 | ED_CR_STP); /* * Copy out our station address */ for (i = 0; i < ETHER_ADDR_LEN; ++i) outb(sc->nic_addr + ED_P1_PAR0 + i, sc->arpcom.ac_enaddr[i]); /* * Set Current Page pointer to next_packet (initialized above) */ outb(sc->nic_addr + ED_P1_CURR, sc->next_packet); /* * Program Receiver Configuration Register and multicast filter. CR is * set to page 0 on return. */ ed_setrcr(sc); /* * Take interface out of loopback */ outb(sc->nic_addr + ED_P0_TCR, 0); /* * If this is a 3Com board, the tranceiver must be software enabled * (there is no settable hardware default). */ if (sc->vendor == ED_VENDOR_3COM) { if (ifp->if_flags & IFF_ALTPHYS) { outb(sc->asic_addr + ED_3COM_CR, 0); } else { outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_XSEL); } } /* * Set 'running' flag, and clear output active flag. */ ifp->if_flags |= IFF_RUNNING; ifp->if_flags &= ~IFF_OACTIVE; /* * ...and attempt to start output */ ed_start(ifp); (void) splx(s); } /* * This routine actually starts the transmission on the interface */ static __inline void ed_xmit(sc) struct ed_softc *sc; { struct ifnet *ifp = (struct ifnet *)sc; unsigned short len; if (sc->gone) return; len = sc->txb_len[sc->txb_next_tx]; /* * Set NIC for page 0 register access */ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_STA); /* * Set TX buffer start page */ outb(sc->nic_addr + ED_P0_TPSR, sc->tx_page_start + sc->txb_next_tx * ED_TXBUF_SIZE); /* * Set TX length */ outb(sc->nic_addr + ED_P0_TBCR0, len); outb(sc->nic_addr + ED_P0_TBCR1, len >> 8); /* * Set page 0, Remote DMA complete, Transmit Packet, and *Start* */ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_TXP | ED_CR_STA); sc->xmit_busy = 1; /* * Point to next transmit buffer slot and wrap if necessary. */ sc->txb_next_tx++; if (sc->txb_next_tx == sc->txb_cnt) sc->txb_next_tx = 0; /* * Set a timer just in case we never hear from the board again */ ifp->if_timer = 2; } /* * Start output on interface. * We make two assumptions here: * 1) that the current priority is set to splimp _before_ this code * is called *and* is returned to the appropriate priority after * return * 2) that the IFF_OACTIVE flag is checked before this code is called * (i.e. that the output part of the interface is idle) */ static void ed_start(ifp) struct ifnet *ifp; { struct ed_softc *sc = ifp->if_softc; struct mbuf *m0, *m; caddr_t buffer; int len; if (sc->gone) { printf("ed_start(%p) GONE\n",ifp); return; } outloop: /* * First, see if there are buffered packets and an idle transmitter - * should never happen at this point. */ if (sc->txb_inuse && (sc->xmit_busy == 0)) { printf("ed: packets buffered, but transmitter idle\n"); ed_xmit(sc); } /* * See if there is room to put another packet in the buffer. */ if (sc->txb_inuse == sc->txb_cnt) { /* * No room. Indicate this to the outside world and exit. */ ifp->if_flags |= IFF_OACTIVE; return; } IF_DEQUEUE(&ifp->if_snd, m); if (m == 0) { /* * We are using the !OACTIVE flag to indicate to the outside * world that we can accept an additional packet rather than * that the transmitter is _actually_ active. Indeed, the * transmitter may be active, but if we haven't filled all the * buffers with data then we still want to accept more. */ ifp->if_flags &= ~IFF_OACTIVE; return; } /* * Copy the mbuf chain into the transmit buffer */ m0 = m; /* txb_new points to next open buffer slot */ buffer = sc->mem_start + (sc->txb_new * ED_TXBUF_SIZE * ED_PAGE_SIZE); if (sc->mem_shared) { /* * Special case setup for 16 bit boards... */ if (sc->isa16bit) { switch (sc->vendor) { /* * For 16bit 3Com boards (which have 16k of * memory), we have the xmit buffers in a * different page of memory ('page 0') - so * change pages. */ case ED_VENDOR_3COM: outb(sc->asic_addr + ED_3COM_GACFR, ED_3COM_GACFR_RSEL); break; /* * Enable 16bit access to shared memory on * WD/SMC boards. */ case ED_VENDOR_WD_SMC:{ outb(sc->asic_addr + ED_WD_LAAR, sc->wd_laar_proto | ED_WD_LAAR_M16EN); if (sc->is790) { outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_MENB); } break; } } } for (len = 0; m != 0; m = m->m_next) { bcopy(mtod(m, caddr_t), buffer, m->m_len); buffer += m->m_len; len += m->m_len; } /* * Restore previous shared memory access */ if (sc->isa16bit) { switch (sc->vendor) { case ED_VENDOR_3COM: outb(sc->asic_addr + ED_3COM_GACFR, ED_3COM_GACFR_RSEL | ED_3COM_GACFR_MBS0); break; case ED_VENDOR_WD_SMC:{ if (sc->is790) { outb(sc->asic_addr + ED_WD_MSR, 0x00); } outb(sc->asic_addr + ED_WD_LAAR, sc->wd_laar_proto & ~ED_WD_LAAR_M16EN); break; } } } } else { len = ed_pio_write_mbufs(sc, m, (int)buffer); if (len == 0) goto outloop; } sc->txb_len[sc->txb_new] = max(len, (ETHER_MIN_LEN-ETHER_CRC_LEN)); sc->txb_inuse++; /* * Point to next buffer slot and wrap if necessary. */ sc->txb_new++; if (sc->txb_new == sc->txb_cnt) sc->txb_new = 0; if (sc->xmit_busy == 0) ed_xmit(sc); /* * Tap off here if there is a bpf listener. */ #if NBPFILTER > 0 if (ifp->if_bpf) { bpf_mtap(ifp, m0); } #endif m_freem(m0); /* * Loop back to the top to possibly buffer more packets */ goto outloop; } /* * Ethernet interface receiver interrupt. */ static __inline void ed_rint(sc) struct ed_softc *sc; { struct ifnet *ifp = &sc->arpcom.ac_if; u_char boundry; u_short len; struct ed_ring packet_hdr; char *packet_ptr; if (sc->gone) return; /* * Set NIC to page 1 registers to get 'current' pointer */ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_PAGE_1 | ED_CR_STA); /* * 'sc->next_packet' is the logical beginning of the ring-buffer - * i.e. it points to where new data has been buffered. The 'CURR' * (current) register points to the logical end of the ring-buffer - * i.e. it points to where additional new data will be added. We loop * here until the logical beginning equals the logical end (or in * other words, until the ring-buffer is empty). */ while (sc->next_packet != inb(sc->nic_addr + ED_P1_CURR)) { /* get pointer to this buffer's header structure */ packet_ptr = sc->mem_ring + (sc->next_packet - sc->rec_page_start) * ED_PAGE_SIZE; /* * The byte count includes a 4 byte header that was added by * the NIC. */ if (sc->mem_shared) packet_hdr = *(struct ed_ring *) packet_ptr; else ed_pio_readmem(sc, (int)packet_ptr, (char *) &packet_hdr, sizeof(packet_hdr)); len = packet_hdr.count; if (len > (ETHER_MAX_LEN - ETHER_CRC_LEN + sizeof(struct ed_ring)) || len < (ETHER_MIN_LEN - ETHER_CRC_LEN + sizeof(struct ed_ring))) { /* * Length is a wild value. There's a good chance that * this was caused by the NIC being old and buggy. * The bug is that the length low byte is duplicated in * the high byte. Try to recalculate the length based on * the pointer to the next packet. */ /* * NOTE: sc->next_packet is pointing at the current packet. */ len &= ED_PAGE_SIZE - 1; /* preserve offset into page */ if (packet_hdr.next_packet >= sc->next_packet) { len += (packet_hdr.next_packet - sc->next_packet) * ED_PAGE_SIZE; } else { len += ((packet_hdr.next_packet - sc->rec_page_start) + (sc->rec_page_stop - sc->next_packet)) * ED_PAGE_SIZE; } /* * because buffers are aligned on 256-byte boundary, * the length computed above is off by 256 in almost * all cases. Fix it... */ if (len & 0xff) len -= 256 ; if (len > (ETHER_MAX_LEN - ETHER_CRC_LEN + sizeof(struct ed_ring))) sc->mibdata.dot3StatsFrameTooLongs++; } /* * Be fairly liberal about what we allow as a "reasonable" length * so that a [crufty] packet will make it to BPF (and can thus * be analyzed). Note that all that is really important is that * we have a length that will fit into one mbuf cluster or less; * the upper layer protocols can then figure out the length from * their own length field(s). */ if ((len > sizeof(struct ed_ring)) && (len <= MCLBYTES) && (packet_hdr.next_packet >= sc->rec_page_start) && (packet_hdr.next_packet < sc->rec_page_stop)) { /* * Go get packet. */ ed_get_packet(sc, packet_ptr + sizeof(struct ed_ring), len - sizeof(struct ed_ring), packet_hdr.rsr & ED_RSR_PHY); ifp->if_ipackets++; } else { /* * Really BAD. The ring pointers are corrupted. */ log(LOG_ERR, "ed%d: NIC memory corrupt - invalid packet length %d\n", ifp->if_unit, len); ifp->if_ierrors++; ed_reset(ifp); return; } /* * Update next packet pointer */ sc->next_packet = packet_hdr.next_packet; /* * Update NIC boundry pointer - being careful to keep it one * buffer behind. (as recommended by NS databook) */ boundry = sc->next_packet - 1; if (boundry < sc->rec_page_start) boundry = sc->rec_page_stop - 1; /* * Set NIC to page 0 registers to update boundry register */ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_STA); outb(sc->nic_addr + ED_P0_BNRY, boundry); /* * Set NIC to page 1 registers before looping to top (prepare * to get 'CURR' current pointer) */ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_PAGE_1 | ED_CR_STA); } } /* * Ethernet interface interrupt processor */ void edintr_sc(sc) struct ed_softc *sc; { struct ifnet *ifp = (struct ifnet *)sc; u_char isr; if (sc->gone) return; /* * Set NIC to page 0 registers */ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_STA); /* * loop until there are no more new interrupts */ while ((isr = inb(sc->nic_addr + ED_P0_ISR)) != 0) { /* * reset all the bits that we are 'acknowledging' by writing a * '1' to each bit position that was set (writing a '1' * *clears* the bit) */ outb(sc->nic_addr + ED_P0_ISR, isr); /* * Handle transmitter interrupts. Handle these first because * the receiver will reset the board under some conditions. */ if (isr & (ED_ISR_PTX | ED_ISR_TXE)) { u_char collisions = inb(sc->nic_addr + ED_P0_NCR) & 0x0f; /* * Check for transmit error. If a TX completed with an * error, we end up throwing the packet away. Really * the only error that is possible is excessive * collisions, and in this case it is best to allow * the automatic mechanisms of TCP to backoff the * flow. Of course, with UDP we're screwed, but this * is expected when a network is heavily loaded. */ (void) inb(sc->nic_addr + ED_P0_TSR); if (isr & ED_ISR_TXE) { u_char tsr; /* * Excessive collisions (16) */ tsr = inb(sc->nic_addr + ED_P0_TSR); if ((tsr & ED_TSR_ABT) && (collisions == 0)) { /* * When collisions total 16, the * P0_NCR will indicate 0, and the * TSR_ABT is set. */ collisions = 16; sc->mibdata.dot3StatsExcessiveCollisions++; sc->mibdata.dot3StatsCollFrequencies[15]++; } if (tsr & ED_TSR_OWC) sc->mibdata.dot3StatsLateCollisions++; if (tsr & ED_TSR_CDH) sc->mibdata.dot3StatsSQETestErrors++; if (tsr & ED_TSR_CRS) sc->mibdata.dot3StatsCarrierSenseErrors++; if (tsr & ED_TSR_FU) sc->mibdata.dot3StatsInternalMacTransmitErrors++; /* * update output errors counter */ ifp->if_oerrors++; } else { /* * Update total number of successfully * transmitted packets. */ ifp->if_opackets++; } /* * reset tx busy and output active flags */ sc->xmit_busy = 0; ifp->if_flags &= ~IFF_OACTIVE; /* * clear watchdog timer */ ifp->if_timer = 0; /* * Add in total number of collisions on last * transmission. */ ifp->if_collisions += collisions; switch(collisions) { case 0: case 16: break; case 1: sc->mibdata.dot3StatsSingleCollisionFrames++; sc->mibdata.dot3StatsCollFrequencies[0]++; break; default: sc->mibdata.dot3StatsMultipleCollisionFrames++; sc->mibdata. dot3StatsCollFrequencies[collisions-1] ++; break; } /* * Decrement buffer in-use count if not zero (can only * be zero if a transmitter interrupt occured while * not actually transmitting). If data is ready to * transmit, start it transmitting, otherwise defer * until after handling receiver */ if (sc->txb_inuse && --sc->txb_inuse) ed_xmit(sc); } /* * Handle receiver interrupts */ if (isr & (ED_ISR_PRX | ED_ISR_RXE | ED_ISR_OVW)) { /* * Overwrite warning. In order to make sure that a * lockup of the local DMA hasn't occurred, we reset * and re-init the NIC. The NSC manual suggests only a * partial reset/re-init is necessary - but some chips * seem to want more. The DMA lockup has been seen * only with early rev chips - Methinks this bug was * fixed in later revs. -DG */ if (isr & ED_ISR_OVW) { ifp->if_ierrors++; #ifdef DIAGNOSTIC log(LOG_WARNING, "ed%d: warning - receiver ring buffer overrun\n", ifp->if_unit); #endif /* * Stop/reset/re-init NIC */ ed_reset(ifp); } else { /* * Receiver Error. One or more of: CRC error, * frame alignment error FIFO overrun, or * missed packet. */ if (isr & ED_ISR_RXE) { u_char rsr; rsr = inb(sc->nic_addr + ED_P0_RSR); if (rsr & ED_RSR_CRC) sc->mibdata.dot3StatsFCSErrors++; if (rsr & ED_RSR_FAE) sc->mibdata.dot3StatsAlignmentErrors++; if (rsr & ED_RSR_FO) sc->mibdata.dot3StatsInternalMacReceiveErrors++; ifp->if_ierrors++; #ifdef ED_DEBUG printf("ed%d: receive error %x\n", ifp->if_unit, inb(sc->nic_addr + ED_P0_RSR)); #endif } /* * Go get the packet(s) XXX - Doing this on an * error is dubious because there shouldn't be * any data to get (we've configured the * interface to not accept packets with * errors). */ /* * Enable 16bit access to shared memory first * on WD/SMC boards. */ if (sc->isa16bit && (sc->vendor == ED_VENDOR_WD_SMC)) { outb(sc->asic_addr + ED_WD_LAAR, sc->wd_laar_proto | ED_WD_LAAR_M16EN); if (sc->is790) { outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_MENB); } } ed_rint(sc); /* disable 16bit access */ if (sc->isa16bit && (sc->vendor == ED_VENDOR_WD_SMC)) { if (sc->is790) { outb(sc->asic_addr + ED_WD_MSR, 0x00); } outb(sc->asic_addr + ED_WD_LAAR, sc->wd_laar_proto & ~ED_WD_LAAR_M16EN); } } } /* * If it looks like the transmitter can take more data, * attempt to start output on the interface. This is done * after handling the receiver to give the receiver priority. */ if ((ifp->if_flags & IFF_OACTIVE) == 0) ed_start(ifp); /* * return NIC CR to standard state: page 0, remote DMA * complete, start (toggling the TXP bit off, even if was just * set in the transmit routine, is *okay* - it is 'edge' * triggered from low to high) */ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_STA); /* * If the Network Talley Counters overflow, read them to reset * them. It appears that old 8390's won't clear the ISR flag * otherwise - resulting in an infinite loop. */ if (isr & ED_ISR_CNT) { (void) inb(sc->nic_addr + ED_P0_CNTR0); (void) inb(sc->nic_addr + ED_P0_CNTR1); (void) inb(sc->nic_addr + ED_P0_CNTR2); } } } static void edintr(unit) int unit; { edintr_sc (&ed_softc[unit]); } /* * Process an ioctl request. This code needs some work - it looks * pretty ugly. */ static int ed_ioctl(ifp, command, data) register struct ifnet *ifp; u_long command; caddr_t data; { struct ed_softc *sc = ifp->if_softc; int s, error = 0; if (sc->gone) { ifp->if_flags &= ~IFF_RUNNING; return ENXIO; } s = splimp(); switch (command) { case SIOCSIFADDR: case SIOCGIFADDR: case SIOCSIFMTU: error = ether_ioctl(ifp, command, data); break; case SIOCSIFFLAGS: /* * If the interface is marked up and stopped, then start it. * If it is marked down and running, then stop it. */ if (ifp->if_flags & IFF_UP) { if ((ifp->if_flags & IFF_RUNNING) == 0) ed_init(sc); } else { if (ifp->if_flags & IFF_RUNNING) { ed_stop(sc); ifp->if_flags &= ~IFF_RUNNING; } } #if NBPFILTER > 0 /* * Promiscuous flag may have changed, so reprogram the RCR. */ ed_setrcr(sc); #endif /* * An unfortunate hack to provide the (required) software * control of the tranceiver for 3Com boards. The ALTPHYS flag * disables the tranceiver if set. */ if (sc->vendor == ED_VENDOR_3COM) { if (ifp->if_flags & IFF_ALTPHYS) { outb(sc->asic_addr + ED_3COM_CR, 0); } else { outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_XSEL); } } else if (sc->vendor == ED_VENDOR_HP) ed_hpp_set_physical_link(sc); break; case SIOCADDMULTI: case SIOCDELMULTI: /* * Multicast list has changed; set the hardware filter * accordingly. */ ed_setrcr(sc); error = 0; break; default: error = EINVAL; } (void) splx(s); return (error); } /* * Given a source and destination address, copy 'amount' of a packet from * the ring buffer into a linear destination buffer. Takes into account * ring-wrap. */ static __inline char * ed_ring_copy(sc, src, dst, amount) struct ed_softc *sc; char *src; char *dst; u_short amount; { u_short tmp_amount; /* does copy wrap to lower addr in ring buffer? */ if (src + amount > sc->mem_end) { tmp_amount = sc->mem_end - src; /* copy amount up to end of NIC memory */ if (sc->mem_shared) bcopy(src, dst, tmp_amount); else ed_pio_readmem(sc, (int)src, dst, tmp_amount); amount -= tmp_amount; src = sc->mem_ring; dst += tmp_amount; } if (sc->mem_shared) bcopy(src, dst, amount); else ed_pio_readmem(sc, (int)src, dst, amount); return (src + amount); } /* * Retreive packet from shared memory and send to the next level up via * ether_input(). If there is a BPF listener, give a copy to BPF, too. */ static void ed_get_packet(sc, buf, len, multicast) struct ed_softc *sc; char *buf; u_short len; int multicast; { struct ether_header *eh; struct mbuf *m; /* Allocate a header mbuf */ MGETHDR(m, M_DONTWAIT, MT_DATA); if (m == NULL) return; m->m_pkthdr.rcvif = &sc->arpcom.ac_if; m->m_pkthdr.len = m->m_len = len; /* * We always put the received packet in a single buffer - * either with just an mbuf header or in a cluster attached * to the header. The +2 is to compensate for the alignment * fixup below. */ if ((len + 2) > MHLEN) { /* Attach an mbuf cluster */ MCLGET(m, M_DONTWAIT); /* Insist on getting a cluster */ if ((m->m_flags & M_EXT) == 0) { m_freem(m); return; } } /* * The +2 is to longword align the start of the real packet. * This is important for NFS. */ m->m_data += 2; eh = mtod(m, struct ether_header *); #ifdef BRIDGE /* * Get link layer header, invoke brige_in, then * depending on the outcome of the test fetch the rest of the * packet and either pass up or call bdg_forward. */ if (do_bridge) { struct ifnet *ifp ; int need_more = 1 ; /* in case not bpf */ #if NBPFILTER > 0 if (sc->arpcom.ac_if.if_bpf) { need_more = 0 ; ed_ring_copy(sc, buf, (char *)eh, len); bpf_mtap(&sc->arpcom.ac_if, m); } else #endif ed_ring_copy(sc, buf, (char *)eh, 14); ifp = bridge_in(m); if (ifp == BDG_DROP) { m_freem(m); return ; } /* else fetch rest of pkt and continue */ if (need_more && len > 14) ed_ring_copy(sc, buf+14, (char *)(eh+1), len - 14); if (ifp != BDG_LOCAL ) bdg_forward(&m, ifp); /* not local, need forwarding */ if (ifp == BDG_LOCAL || ifp == BDG_BCAST || ifp == BDG_MCAST) goto getit ; /* not local and not multicast, just drop it */ if (m) m_freem(m); return ; } #endif /* * Get packet, including link layer address, from interface. */ ed_ring_copy(sc, buf, (char *)eh, len); #if NBPFILTER > 0 /* * Check if there's a BPF listener on this interface. If so, hand off * the raw packet to bpf. */ if (sc->arpcom.ac_if.if_bpf) bpf_mtap(&sc->arpcom.ac_if, m); #endif /* * If we are in promiscuous mode, we have to check whether * this packet is really for us. */ if ((sc->arpcom.ac_if.if_flags & IFF_PROMISC) && bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr, sizeof(eh->ether_dhost)) != 0 && multicast == 0) { m_freem(m); return; } getit: /* * Remove link layer address. */ m->m_pkthdr.len = m->m_len = len - sizeof(struct ether_header); m->m_data += sizeof(struct ether_header); ether_input(&sc->arpcom.ac_if, eh, m); return; } /* * Supporting routines */ /* * Given a NIC memory source address and a host memory destination * address, copy 'amount' from NIC to host using Programmed I/O. * The 'amount' is rounded up to a word - okay as long as mbufs * are word sized. * This routine is currently Novell-specific. */ static void ed_pio_readmem(sc, src, dst, amount) struct ed_softc *sc; int src; unsigned char *dst; unsigned short amount; { /* HP cards need special handling */ if (sc->vendor == ED_VENDOR_HP && sc->type == ED_TYPE_HP_PCLANPLUS) { ed_hpp_readmem(sc, src, dst, amount); return; } /* Regular Novell cards */ /* select page 0 registers */ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_STA); /* round up to a word */ if (amount & 1) ++amount; /* set up DMA byte count */ outb(sc->nic_addr + ED_P0_RBCR0, amount); outb(sc->nic_addr + ED_P0_RBCR1, amount >> 8); /* set up source address in NIC mem */ outb(sc->nic_addr + ED_P0_RSAR0, src); outb(sc->nic_addr + ED_P0_RSAR1, src >> 8); outb(sc->nic_addr + ED_P0_CR, ED_CR_RD0 | ED_CR_STA); if (sc->isa16bit) { insw(sc->asic_addr + ED_NOVELL_DATA, dst, amount / 2); } else insb(sc->asic_addr + ED_NOVELL_DATA, dst, amount); } /* * Stripped down routine for writing a linear buffer to NIC memory. * Only used in the probe routine to test the memory. 'len' must * be even. */ static void ed_pio_writemem(sc, src, dst, len) struct ed_softc *sc; char *src; unsigned short dst; unsigned short len; { int maxwait = 200; /* about 240us */ if (sc->vendor == ED_VENDOR_NOVELL) { /* select page 0 registers */ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_STA); /* reset remote DMA complete flag */ outb(sc->nic_addr + ED_P0_ISR, ED_ISR_RDC); /* set up DMA byte count */ outb(sc->nic_addr + ED_P0_RBCR0, len); outb(sc->nic_addr + ED_P0_RBCR1, len >> 8); /* set up destination address in NIC mem */ outb(sc->nic_addr + ED_P0_RSAR0, dst); outb(sc->nic_addr + ED_P0_RSAR1, dst >> 8); /* set remote DMA write */ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD1 | ED_CR_STA); if (sc->isa16bit) outsw(sc->asic_addr + ED_NOVELL_DATA, src, len / 2); else outsb(sc->asic_addr + ED_NOVELL_DATA, src, len); /* * Wait for remote DMA complete. This is necessary because on the * transmit side, data is handled internally by the NIC in bursts and * we can't start another remote DMA until this one completes. Not * waiting causes really bad things to happen - like the NIC * irrecoverably jamming the ISA bus. */ while (((inb(sc->nic_addr + ED_P0_ISR) & ED_ISR_RDC) != ED_ISR_RDC) && --maxwait); } else if ((sc->vendor == ED_VENDOR_HP) && (sc->type == ED_TYPE_HP_PCLANPLUS)) { /* HP PCLAN+ */ /* reset remote DMA complete flag */ outb(sc->nic_addr + ED_P0_ISR, ED_ISR_RDC); /* program the write address in RAM */ outw(sc->asic_addr + ED_HPP_PAGE_0, dst); if (sc->hpp_mem_start) { u_short *s = (u_short *) src; volatile u_short *d = (u_short *) sc->hpp_mem_start; u_short *const fence = s + (len >> 1); /* * Enable memory mapped access. */ outw(sc->asic_addr + ED_HPP_OPTION, sc->hpp_options & ~(ED_HPP_OPTION_MEM_DISABLE | ED_HPP_OPTION_BOOT_ROM_ENB)); /* * Copy to NIC memory. */ while (s < fence) *d = *s++; /* * Restore Boot ROM access. */ outw(sc->asic_addr + ED_HPP_OPTION, sc->hpp_options); } else { /* write data using I/O writes */ outsw(sc->asic_addr + ED_HPP_PAGE_4, src, len / 2); } } } /* * Write an mbuf chain to the destination NIC memory address using * programmed I/O. */ static u_short ed_pio_write_mbufs(sc, m, dst) struct ed_softc *sc; struct mbuf *m; int dst; { struct ifnet *ifp = (struct ifnet *)sc; unsigned short total_len, dma_len; struct mbuf *mp; int maxwait = 200; /* about 240us */ /* HP PC Lan+ cards need special handling */ if ((sc->vendor == ED_VENDOR_HP) && (sc->type == ED_TYPE_HP_PCLANPLUS)) { return ed_hpp_write_mbufs(sc, m, dst); } /* First, count up the total number of bytes to copy */ for (total_len = 0, mp = m; mp; mp = mp->m_next) total_len += mp->m_len; dma_len = total_len; if (sc->isa16bit && (dma_len & 1)) dma_len++; /* select page 0 registers */ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_STA); /* reset remote DMA complete flag */ outb(sc->nic_addr + ED_P0_ISR, ED_ISR_RDC); /* set up DMA byte count */ outb(sc->nic_addr + ED_P0_RBCR0, dma_len); outb(sc->nic_addr + ED_P0_RBCR1, dma_len >> 8); /* set up destination address in NIC mem */ outb(sc->nic_addr + ED_P0_RSAR0, dst); outb(sc->nic_addr + ED_P0_RSAR1, dst >> 8); /* set remote DMA write */ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD1 | ED_CR_STA); /* * Transfer the mbuf chain to the NIC memory. * 16-bit cards require that data be transferred as words, and only words. * So that case requires some extra code to patch over odd-length mbufs. */ if (!sc->isa16bit) { /* NE1000s are easy */ while (m) { if (m->m_len) { outsb(sc->asic_addr + ED_NOVELL_DATA, m->m_data, m->m_len); } m = m->m_next; } } else { /* NE2000s are a pain */ unsigned char *data; int len, wantbyte; unsigned char savebyte[2]; wantbyte = 0; while (m) { len = m->m_len; if (len) { data = mtod(m, caddr_t); /* finish the last word */ if (wantbyte) { savebyte[1] = *data; outw(sc->asic_addr + ED_NOVELL_DATA, *(u_short *)savebyte); data++; len--; wantbyte = 0; } /* output contiguous words */ if (len > 1) { outsw(sc->asic_addr + ED_NOVELL_DATA, data, len >> 1); data += len & ~1; len &= 1; } /* save last byte, if necessary */ if (len == 1) { savebyte[0] = *data; wantbyte = 1; } } m = m->m_next; } /* spit last byte */ if (wantbyte) { outw(sc->asic_addr + ED_NOVELL_DATA, *(u_short *)savebyte); } } /* * Wait for remote DMA complete. This is necessary because on the * transmit side, data is handled internally by the NIC in bursts and * we can't start another remote DMA until this one completes. Not * waiting causes really bad things to happen - like the NIC * irrecoverably jamming the ISA bus. */ while (((inb(sc->nic_addr + ED_P0_ISR) & ED_ISR_RDC) != ED_ISR_RDC) && --maxwait); if (!maxwait) { log(LOG_WARNING, "ed%d: remote transmit DMA failed to complete\n", ifp->if_unit); ed_reset(ifp); return(0); } return (total_len); } /* * Support routines to handle the HP PC Lan+ card. */ /* * HP PC Lan+: Read from NIC memory, using either PIO or memory mapped * IO. */ static void ed_hpp_readmem(sc, src, dst, amount) struct ed_softc *sc; unsigned short src; unsigned char *dst; unsigned short amount; { int use_32bit_access = !(sc->hpp_id & ED_HPP_ID_16_BIT_ACCESS); /* Program the source address in RAM */ outw(sc->asic_addr + ED_HPP_PAGE_2, src); /* * The HP PC Lan+ card supports word reads as well as * a memory mapped i/o port that is aliased to every * even address on the board. */ if (sc->hpp_mem_start) { /* Enable memory mapped access. */ outw(sc->asic_addr + ED_HPP_OPTION, sc->hpp_options & ~(ED_HPP_OPTION_MEM_DISABLE | ED_HPP_OPTION_BOOT_ROM_ENB)); if (use_32bit_access && (amount > 3)) { u_long *dl = (u_long *) dst; volatile u_long *const sl = (u_long *) sc->hpp_mem_start; u_long *const fence = dl + (amount >> 2); /* Copy out NIC data. We could probably write this as a `movsl'. The currently generated code is lousy. */ while (dl < fence) *dl++ = *sl; dst += (amount & ~3); amount &= 3; } /* Finish off any words left, as a series of short reads */ if (amount > 1) { u_short *d = (u_short *) dst; volatile u_short *const s = (u_short *) sc->hpp_mem_start; u_short *const fence = d + (amount >> 1); /* Copy out NIC data. */ while (d < fence) *d++ = *s; dst += (amount & ~1); amount &= 1; } /* * read in a byte; however we need to always read 16 bits * at a time or the hardware gets into a funny state */ if (amount == 1) { /* need to read in a short and copy LSB */ volatile u_short *const s = (volatile u_short *) sc->hpp_mem_start; *dst = (*s) & 0xFF; } /* Restore Boot ROM access. */ outw(sc->asic_addr + ED_HPP_OPTION, sc->hpp_options); } else { /* Read in data using the I/O port */ if (use_32bit_access && (amount > 3)) { insl(sc->asic_addr + ED_HPP_PAGE_4, dst, amount >> 2); dst += (amount & ~3); amount &= 3; } if (amount > 1) { insw(sc->asic_addr + ED_HPP_PAGE_4, dst, amount >> 1); dst += (amount & ~1); amount &= 1; } if (amount == 1) { /* read in a short and keep the LSB */ *dst = inw(sc->asic_addr + ED_HPP_PAGE_4) & 0xFF; } } } /* * Write to HP PC Lan+ NIC memory. Access to the NIC can be by using * outsw() or via the memory mapped interface to the same register. * Writes have to be in word units; byte accesses won't work and may cause * the NIC to behave wierdly. Long word accesses are permitted if the ASIC * allows it. */ static u_short ed_hpp_write_mbufs(struct ed_softc *sc, struct mbuf *m, int dst) { int len, wantbyte; unsigned short total_len; unsigned char savebyte[2]; volatile u_short * const d = (volatile u_short *) sc->hpp_mem_start; int use_32bit_accesses = !(sc->hpp_id & ED_HPP_ID_16_BIT_ACCESS); /* select page 0 registers */ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_STA); /* reset remote DMA complete flag */ outb(sc->nic_addr + ED_P0_ISR, ED_ISR_RDC); /* program the write address in RAM */ outw(sc->asic_addr + ED_HPP_PAGE_0, dst); if (sc->hpp_mem_start) /* enable memory mapped I/O */ outw(sc->asic_addr + ED_HPP_OPTION, sc->hpp_options & ~(ED_HPP_OPTION_MEM_DISABLE | ED_HPP_OPTION_BOOT_ROM_ENB)); wantbyte = 0; total_len = 0; if (sc->hpp_mem_start) { /* Memory mapped I/O port */ while (m) { total_len += (len = m->m_len); if (len) { caddr_t data = mtod(m, caddr_t); /* finish the last word of the previous mbuf */ if (wantbyte) { savebyte[1] = *data; *d = *((ushort *) savebyte); data++; len--; wantbyte = 0; } /* output contiguous words */ if ((len > 3) && (use_32bit_accesses)) { volatile u_long *const dl = (volatile u_long *) d; u_long *sl = (u_long *) data; u_long *fence = sl + (len >> 2); while (sl < fence) *dl = *sl++; data += (len & ~3); len &= 3; } /* finish off remain 16 bit writes */ if (len > 1) { u_short *s = (u_short *) data; u_short *fence = s + (len >> 1); while (s < fence) *d = *s++; data += (len & ~1); len &= 1; } /* save last byte if needed */ if ((wantbyte = (len == 1)) != 0) savebyte[0] = *data; } m = m->m_next; /* to next mbuf */ } if (wantbyte) /* write last byte */ *d = *((u_short *) savebyte); } else { /* use programmed I/O */ while (m) { total_len += (len = m->m_len); if (len) { caddr_t data = mtod(m, caddr_t); /* finish the last word of the previous mbuf */ if (wantbyte) { savebyte[1] = *data; outw(sc->asic_addr + ED_HPP_PAGE_4, *((u_short *)savebyte)); data++; len--; wantbyte = 0; } /* output contiguous words */ if ((len > 3) && use_32bit_accesses) { outsl(sc->asic_addr + ED_HPP_PAGE_4, data, len >> 2); data += (len & ~3); len &= 3; } /* finish off remaining 16 bit accesses */ if (len > 1) { outsw(sc->asic_addr + ED_HPP_PAGE_4, data, len >> 1); data += (len & ~1); len &= 1; } if ((wantbyte = (len == 1)) != 0) savebyte[0] = *data; } /* if len != 0 */ m = m->m_next; } if (wantbyte) /* spit last byte */ outw(sc->asic_addr + ED_HPP_PAGE_4, *(u_short *)savebyte); } if (sc->hpp_mem_start) /* turn off memory mapped i/o */ outw(sc->asic_addr + ED_HPP_OPTION, sc->hpp_options); return (total_len); } static void ed_setrcr(sc) struct ed_softc *sc; { struct ifnet *ifp = (struct ifnet *)sc; int i; /* set page 1 registers */ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_PAGE_1 | ED_CR_STP); if (ifp->if_flags & IFF_PROMISC) { /* * Reconfigure the multicast filter. */ for (i = 0; i < 8; i++) outb(sc->nic_addr + ED_P1_MAR0 + i, 0xff); /* * And turn on promiscuous mode. Also enable reception of * runts and packets with CRC & alignment errors. */ /* Set page 0 registers */ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_STP); outb(sc->nic_addr + ED_P0_RCR, ED_RCR_PRO | ED_RCR_AM | ED_RCR_AB | ED_RCR_AR | ED_RCR_SEP); } else { /* set up multicast addresses and filter modes */ if (ifp->if_flags & IFF_MULTICAST) { u_long mcaf[2]; if (ifp->if_flags & IFF_ALLMULTI) { mcaf[0] = 0xffffffff; mcaf[1] = 0xffffffff; } else ds_getmcaf(sc, mcaf); /* * Set multicast filter on chip. */ for (i = 0; i < 8; i++) outb(sc->nic_addr + ED_P1_MAR0 + i, ((u_char *) mcaf)[i]); /* Set page 0 registers */ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_STP); outb(sc->nic_addr + ED_P0_RCR, ED_RCR_AM | ED_RCR_AB); } else { /* * Initialize multicast address hashing registers to * not accept multicasts. */ for (i = 0; i < 8; ++i) outb(sc->nic_addr + ED_P1_MAR0 + i, 0x00); /* Set page 0 registers */ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_STP); outb(sc->nic_addr + ED_P0_RCR, ED_RCR_AB); } } /* * Start interface. */ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_STA); } /* * Compute crc for ethernet address */ static u_long ds_crc(ep) u_char *ep; { #define POLYNOMIAL 0x04c11db6 register u_long crc = 0xffffffffL; register int carry, i, j; register u_char b; for (i = 6; --i >= 0;) { b = *ep++; for (j = 8; --j >= 0;) { carry = ((crc & 0x80000000L) ? 1 : 0) ^ (b & 0x01); crc <<= 1; b >>= 1; if (carry) crc = ((crc ^ POLYNOMIAL) | carry); } } return crc; #undef POLYNOMIAL } /* * Compute the multicast address filter from the * list of multicast addresses we need to listen to. */ static void ds_getmcaf(sc, mcaf) struct ed_softc *sc; u_long *mcaf; { register u_int index; register u_char *af = (u_char *) mcaf; struct ifmultiaddr *ifma; mcaf[0] = 0; mcaf[1] = 0; for (ifma = sc->arpcom.ac_if.if_multiaddrs.lh_first; ifma; ifma = ifma->ifma_link.le_next) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; index = ds_crc(LLADDR((struct sockaddr_dl *)ifma->ifma_addr)) >> 26; af[index >> 3] |= 1 << (index & 7); } } /* * support PnP cards if we are using 'em */ #if NPNP > 0 static pnpid_t edpnp_ids[] = { { 0xd680d041, "NE2000"}, { 0 } }; static char *edpnp_probe(u_long csn, u_long vend_id); static void edpnp_attach(u_long csn, u_long vend_id, char *name, struct isa_device *dev); static u_long nedpnp = NED; static struct pnp_device edpnp = { "edpnp", edpnp_probe, edpnp_attach, &nedpnp, &net_imask }; DATA_SET (pnpdevice_set, edpnp); static char * edpnp_probe(u_long csn, u_long vend_id) { pnpid_t *id; char *s = NULL; for(id = edpnp_ids; id->vend_id != 0; id++) { if (vend_id == id->vend_id) { s = id->id_str; break; } } if (s) { struct pnp_cinfo d; read_pnp_parms(&d, 0); if (d.enable == 0 || d.flags & 1) { printf("CSN %lu is disabled.\n", csn); return (NULL); } } return (s); } static void edpnp_attach(u_long csn, u_long vend_id, char *name, struct isa_device *dev) { struct pnp_cinfo d; - struct isa_device *dvp; if (dev->id_unit >= NEDTOT) return; if (read_pnp_parms(&d, 0) == 0) { printf("failed to read pnp parms\n"); return; } write_pnp_parms(&d, 0); enable_pnp_card(); dev->id_iobase = d.port[0]; dev->id_irq = (1 << d.irq[0]); dev->id_ointr = edintr; dev->id_drq = -1; if (dev->id_driver == NULL) { dev->id_driver = &eddriver; - dvp = find_isadev(isa_devtab_net, &eddriver, 0); - if (dvp != NULL) - dev->id_id = dvp->id_id; + dev->id_id = isa_compat_nextid(); } if ((dev->id_alive = ed_probe(dev)) != 0) ed_attach_isa(dev); else printf("ed%d: probe failed\n", dev->id_unit); } #endif Index: head/sys/i386/isa/intr_machdep.c =================================================================== --- head/sys/i386/isa/intr_machdep.c (revision 45719) +++ head/sys/i386/isa/intr_machdep.c (revision 45720) @@ -1,516 +1,518 @@ /*- * Copyright (c) 1991 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * William Jolitz. * * 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: @(#)isa.c 7.2 (Berkeley) 5/13/91 - * $Id: intr_machdep.c,v 1.16 1999/01/08 19:17:48 bde Exp $ + * $Id: intr_machdep.c,v 1.17 1999/04/14 14:26:36 bde Exp $ */ #include "opt_auto_eoi.h" #include #ifndef SMP #include #endif #include #include #include #include #include #if defined(APIC_IO) #include #include /** FAST_HI */ #endif /* APIC_IO */ #include #ifdef PC98 #include #include #include #else #include #endif #include #include #include #ifdef APIC_IO #include #endif /* XXX should be in suitable include files */ #ifdef PC98 #define ICU_IMR_OFFSET 2 /* IO_ICU{1,2} + 2 */ #define ICU_SLAVEID 7 #else #define ICU_IMR_OFFSET 1 /* IO_ICU{1,2} + 1 */ #define ICU_SLAVEID 2 #endif #ifdef APIC_IO /* * This is to accommodate "mixed-mode" programming for * motherboards that don't connect the 8254 to the IO APIC. */ #define AUTO_EOI_1 1 #endif #define NR_INTRNAMES (1 + ICU_LEN + 2 * ICU_LEN) u_long *intr_countp[ICU_LEN]; inthand2_t *intr_handler[ICU_LEN]; u_int intr_mask[ICU_LEN]; static u_int* intr_mptr[ICU_LEN]; void *intr_unit[ICU_LEN]; static inthand_t *fastintr[ICU_LEN] = { &IDTVEC(fastintr0), &IDTVEC(fastintr1), &IDTVEC(fastintr2), &IDTVEC(fastintr3), &IDTVEC(fastintr4), &IDTVEC(fastintr5), &IDTVEC(fastintr6), &IDTVEC(fastintr7), &IDTVEC(fastintr8), &IDTVEC(fastintr9), &IDTVEC(fastintr10), &IDTVEC(fastintr11), &IDTVEC(fastintr12), &IDTVEC(fastintr13), &IDTVEC(fastintr14), &IDTVEC(fastintr15) #if defined(APIC_IO) , &IDTVEC(fastintr16), &IDTVEC(fastintr17), &IDTVEC(fastintr18), &IDTVEC(fastintr19), &IDTVEC(fastintr20), &IDTVEC(fastintr21), &IDTVEC(fastintr22), &IDTVEC(fastintr23) #endif /* APIC_IO */ }; static inthand_t *slowintr[ICU_LEN] = { &IDTVEC(intr0), &IDTVEC(intr1), &IDTVEC(intr2), &IDTVEC(intr3), &IDTVEC(intr4), &IDTVEC(intr5), &IDTVEC(intr6), &IDTVEC(intr7), &IDTVEC(intr8), &IDTVEC(intr9), &IDTVEC(intr10), &IDTVEC(intr11), &IDTVEC(intr12), &IDTVEC(intr13), &IDTVEC(intr14), &IDTVEC(intr15) #if defined(APIC_IO) , &IDTVEC(intr16), &IDTVEC(intr17), &IDTVEC(intr18), &IDTVEC(intr19), &IDTVEC(intr20), &IDTVEC(intr21), &IDTVEC(intr22), &IDTVEC(intr23) #endif /* APIC_IO */ }; static inthand2_t isa_strayintr; #ifdef PC98 #define NMI_PARITY 0x04 #define NMI_EPARITY 0x02 #else #define NMI_PARITY (1 << 7) #define NMI_IOCHAN (1 << 6) #define ENMI_WATCHDOG (1 << 7) #define ENMI_BUSTIMER (1 << 6) #define ENMI_IOSTATUS (1 << 5) #endif /* * Handle a NMI, possibly a machine check. * return true to panic system, false to ignore. */ int isa_nmi(cd) int cd; { #ifdef PC98 int port = inb(0x33); if (epson_machine_id == 0x20) epson_outb(0xc16, epson_inb(0xc16) | 0x1); if (port & NMI_PARITY) { panic("BASE RAM parity error, likely hardware failure."); } else if (port & NMI_EPARITY) { panic("EXTENDED RAM parity error, likely hardware failure."); } else { printf("\nNMI Resume ??\n"); return(0); } #else /* IBM-PC */ int isa_port = inb(0x61); int eisa_port = inb(0x461); if (isa_port & NMI_PARITY) panic("RAM parity error, likely hardware failure."); if (isa_port & NMI_IOCHAN) panic("I/O channel check, likely hardware failure."); /* * On a real EISA machine, this will never happen. However it can * happen on ISA machines which implement XT style floating point * error handling (very rare). Save them from a meaningless panic. */ if (eisa_port == 0xff) return(0); if (eisa_port & ENMI_WATCHDOG) panic("EISA watchdog timer expired, likely hardware failure."); if (eisa_port & ENMI_BUSTIMER) panic("EISA bus timeout, likely hardware failure."); if (eisa_port & ENMI_IOSTATUS) panic("EISA I/O port status error."); printf("\nNMI ISA %x, EISA %x\n", isa_port, eisa_port); return(0); #endif } /* * Fill in default interrupt table (in case of spuruious interrupt * during configuration of kernel, setup interrupt control unit */ void isa_defaultirq() { int i; /* icu vectors */ for (i = 0; i < ICU_LEN; i++) icu_unset(i, (inthand2_t *)NULL); /* initialize 8259's */ outb(IO_ICU1, 0x11); /* reset; program device, four bytes */ outb(IO_ICU1+ICU_IMR_OFFSET, NRSVIDT); /* starting at this vector index */ outb(IO_ICU1+ICU_IMR_OFFSET, IRQ_SLAVE); /* slave on line 7 */ #ifdef PC98 #ifdef AUTO_EOI_1 outb(IO_ICU1+ICU_IMR_OFFSET, 0x1f); /* (master) auto EOI, 8086 mode */ #else outb(IO_ICU1+ICU_IMR_OFFSET, 0x1d); /* (master) 8086 mode */ #endif #else /* IBM-PC */ #ifdef AUTO_EOI_1 outb(IO_ICU1+ICU_IMR_OFFSET, 2 | 1); /* auto EOI, 8086 mode */ #else outb(IO_ICU1+ICU_IMR_OFFSET, 1); /* 8086 mode */ #endif #endif /* PC98 */ outb(IO_ICU1+ICU_IMR_OFFSET, 0xff); /* leave interrupts masked */ outb(IO_ICU1, 0x0a); /* default to IRR on read */ #ifndef PC98 outb(IO_ICU1, 0xc0 | (3 - 1)); /* pri order 3-7, 0-2 (com2 first) */ #endif /* !PC98 */ outb(IO_ICU2, 0x11); /* reset; program device, four bytes */ outb(IO_ICU2+ICU_IMR_OFFSET, NRSVIDT+8); /* staring at this vector index */ outb(IO_ICU2+ICU_IMR_OFFSET, ICU_SLAVEID); /* my slave id is 7 */ #ifdef PC98 outb(IO_ICU2+ICU_IMR_OFFSET,9); /* 8086 mode */ #else /* IBM-PC */ #ifdef AUTO_EOI_2 outb(IO_ICU2+ICU_IMR_OFFSET, 2 | 1); /* auto EOI, 8086 mode */ #else outb(IO_ICU2+ICU_IMR_OFFSET,1); /* 8086 mode */ #endif #endif /* PC98 */ outb(IO_ICU2+ICU_IMR_OFFSET, 0xff); /* leave interrupts masked */ outb(IO_ICU2, 0x0a); /* default to IRR on read */ } /* * Caught a stray interrupt, notify */ static void isa_strayintr(vcookiep) void *vcookiep; { int intr = (void **)vcookiep - &intr_unit[0]; /* DON'T BOTHER FOR NOW! */ /* for some reason, we get bursts of intr #7, even if not enabled! */ /* * Well the reason you got bursts of intr #7 is because someone * raised an interrupt line and dropped it before the 8259 could * prioritize it. This is documented in the intel data book. This * means you have BAD hardware! I have changed this so that only * the first 5 get logged, then it quits logging them, and puts * out a special message. rgrimes 3/25/1993 */ /* * XXX TODO print a different message for #7 if it is for a * glitch. Glitches can be distinguished from real #7's by * testing that the in-service bit is _not_ set. The test * must be done before sending an EOI so it can't be done if * we are using AUTO_EOI_1. */ if (intrcnt[1 + intr] <= 5) log(LOG_ERR, "stray irq %d\n", intr); if (intrcnt[1 + intr] == 5) log(LOG_CRIT, "too many stray irq %d's; not logging any more\n", intr); } /* * Return a bitmap of the current interrupt requests. This is 8259-specific * and is only suitable for use at probe time. */ intrmask_t isa_irq_pending() { u_char irr1; u_char irr2; irr1 = inb(IO_ICU1); irr2 = inb(IO_ICU2); return ((irr2 << 8) | irr1); } int update_intr_masks(void) { int intr, n=0; u_int mask,*maskptr; for (intr=0; intr < ICU_LEN; intr ++) { #if defined(APIC_IO) /* no 8259 SLAVE to ignore */ #else if (intr==ICU_SLAVEID) continue; /* ignore 8259 SLAVE output */ #endif /* APIC_IO */ maskptr = intr_mptr[intr]; if (!maskptr) continue; *maskptr |= 1 << intr; mask = *maskptr; if (mask != intr_mask[intr]) { #if 0 printf ("intr_mask[%2d] old=%08x new=%08x ptr=%p.\n", intr, intr_mask[intr], mask, maskptr); #endif intr_mask[intr]=mask; n++; } } return (n); } static const char * isa_get_nameunit(int id) { static char buf[32]; struct isa_device *dp; if (id == -1) return ("pci"); /* XXX may also be eisa */ if (id == 0) return ("clk0"); /* XXX may also be sloppy driver */ if (id == 1) return ("rtc0"); +#if 0 for (dp = isa_devtab_bio; dp->id_driver != NULL; dp++) if (dp->id_id == id) goto found_device; for (dp = isa_devtab_cam; dp->id_driver != NULL; dp++) if (dp->id_id == id) goto found_device; for (dp = isa_devtab_net; dp->id_driver != NULL; dp++) if (dp->id_id == id) goto found_device; for (dp = isa_devtab_null; dp->id_driver != NULL; dp++) if (dp->id_id == id) goto found_device; for (dp = isa_devtab_tty; dp->id_driver != NULL; dp++) if (dp->id_id == id) goto found_device; +#endif return "???"; found_device: snprintf(buf, sizeof(buf), "%s%d", dp->id_driver->name, dp->id_unit); return (buf); } void update_intrname(int intr, int device_id) { char buf[32]; char *cp; const char *name; int name_index, off, strayintr; /* * Initialise strings for bitbucket and stray interrupt counters. * These have statically allocated indices 0 and 1 through ICU_LEN. */ if (intrnames[0] == '\0') { off = sprintf(intrnames, "???") + 1; for (strayintr = 0; strayintr < ICU_LEN; strayintr++) off += sprintf(intrnames + off, "stray irq%d", strayintr) + 1; } name = isa_get_nameunit(device_id); if (snprintf(buf, sizeof(buf), "%s irq%d", name, intr) >= sizeof(buf)) goto use_bitbucket; /* * Search for `buf' in `intrnames'. In the usual case when it is * not found, append it to the end if there is enough space (the \0 * terminator for the previous string, if any, becomes a separator). */ for (cp = intrnames, name_index = 0; cp != eintrnames && name_index < NR_INTRNAMES; cp += strlen(cp) + 1, name_index++) { if (*cp == '\0') { if (strlen(buf) >= eintrnames - cp) break; strcpy(cp, buf); goto found; } if (strcmp(cp, buf) == 0) goto found; } use_bitbucket: printf("update_intrname: counting %s irq%d as %s\n", name, intr, intrnames); name_index = 0; found: intr_countp[intr] = &intrcnt[name_index]; } int icu_setup(int intr, inthand2_t *handler, void *arg, u_int *maskptr, int flags) { #ifdef FAST_HI int select; /* the select register is 8 bits */ int vector; u_int32_t value; /* the window register is 32 bits */ #endif /* FAST_HI */ u_long ef; u_int mask = (maskptr ? *maskptr : 0); #if defined(APIC_IO) if ((u_int)intr >= ICU_LEN) /* no 8259 SLAVE to ignore */ #else if ((u_int)intr >= ICU_LEN || intr == ICU_SLAVEID) #endif /* APIC_IO */ if (intr_handler[intr] != isa_strayintr) return (EBUSY); ef = read_eflags(); disable_intr(); intr_handler[intr] = handler; intr_mptr[intr] = maskptr; intr_mask[intr] = mask | (1 << intr); intr_unit[intr] = arg; #ifdef FAST_HI if (flags & INTR_FAST) { vector = TPR_FAST_INTS + intr; setidt(vector, fastintr[intr], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); } else { vector = TPR_SLOW_INTS + intr; #ifdef APIC_INTR_REORDER #ifdef APIC_INTR_HIGHPRI_CLOCK /* XXX: Hack (kludge?) for more accurate clock. */ if (intr == apic_8254_intr || intr == 8) { vector = TPR_FAST_INTS + intr; } #endif #endif setidt(vector, slowintr[intr], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); } #ifdef APIC_INTR_REORDER set_lapic_isrloc(intr, vector); #endif /* * Reprogram the vector in the IO APIC. */ if (int_to_apicintpin[intr].ioapic >= 0) { select = int_to_apicintpin[intr].redirindex; value = io_apic_read(int_to_apicintpin[intr].ioapic, select) & ~IOART_INTVEC; io_apic_write(int_to_apicintpin[intr].ioapic, select, value | vector); } #else setidt(ICU_OFFSET + intr, flags & INTR_FAST ? fastintr[intr] : slowintr[intr], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); #endif /* FAST_HI */ INTREN(1 << intr); MPINTR_UNLOCK(); write_eflags(ef); return (0); } void register_imask(dvp, mask) struct isa_device *dvp; u_int mask; { if (dvp->id_alive && dvp->id_irq) { int intr; intr = ffs(dvp->id_irq) - 1; intr_mask[intr] = mask | (1 <= ICU_LEN || handler != intr_handler[intr]) return (EINVAL); INTRDIS(1 << intr); ef = read_eflags(); disable_intr(); intr_countp[intr] = &intrcnt[1 + intr]; intr_handler[intr] = isa_strayintr; intr_mptr[intr] = NULL; intr_mask[intr] = HWI_MASK | SWI_MASK; intr_unit[intr] = &intr_unit[intr]; #ifdef FAST_HI_XXX /* XXX how do I re-create dvp here? */ setidt(flags & INTR_FAST ? TPR_FAST_INTS + intr : TPR_SLOW_INTS + intr, slowintr[intr], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); #else /* FAST_HI */ #ifdef APIC_INTR_REORDER set_lapic_isrloc(intr, ICU_OFFSET + intr); #endif setidt(ICU_OFFSET + intr, slowintr[intr], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); #endif /* FAST_HI */ MPINTR_UNLOCK(); write_eflags(ef); return (0); } Index: head/sys/i386/isa/isa.c =================================================================== --- head/sys/i386/isa/isa.c (revision 45719) +++ head/sys/i386/isa/isa.c (revision 45720) @@ -1,1073 +1,623 @@ /*- - * Copyright (c) 1991 The Regents of the University of California. + * Copyright (c) 1998 Doug Rabson * All rights reserved. * - * This code is derived from software contributed to Berkeley by - * William Jolitz. - * * 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 + * 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 REGENTS OR CONTRIBUTORS BE LIABLE + * 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. * - * from: @(#)isa.c 7.2 (Berkeley) 5/13/91 - * $Id: isa.c,v 1.117 1998/11/29 15:42:40 phk Exp $ + * $Id: isa.c,v 1.4 1998/09/16 08:23:51 dfr Exp $ */ /* - * code to manage AT bus + * Modifications for Intel architecture by Garrett A. Wollman. + * Copyright 1998 Massachusetts Institute of Technology * - * 92/08/18 Frank P. MacLachlan (fpm@crash.cts.com): - * Fixed uninitialized variable problem and added code to deal - * with DMA page boundaries in isa_dmarangecheck(). Fixed word - * mode DMA count compution and reorganized DMA setup code in - * isa_dmastart() + * Permission to use, copy, modify, and distribute this software and + * its documentation for any purpose and without fee is hereby + * granted, provided that both the above copyright notice and this + * permission notice appear in all copies, that both the above + * copyright notice and this permission notice appear in all + * supporting documentation, and that the name of M.I.T. not be used + * in advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. M.I.T. makes + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS + * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT + * SHALL M.I.T. 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. */ #include #include -#include +#include +#include #include -#include -#include -#ifdef APIC_IO -#include -#endif /* APIC_IO */ -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include -#include +#include -#include "pnp.h" -#if NPNP > 0 -#include -#endif +#include -/* -** Register definitions for DMA controller 1 (channels 0..3): -*/ -#define DMA1_CHN(c) (IO_DMA1 + 1*(2*(c))) /* addr reg for channel c */ -#define DMA1_SMSK (IO_DMA1 + 1*10) /* single mask register */ -#define DMA1_MODE (IO_DMA1 + 1*11) /* mode register */ -#define DMA1_FFC (IO_DMA1 + 1*12) /* clear first/last FF */ +MALLOC_DEFINE(M_ISADEV, "isadev", "ISA device"); /* -** Register definitions for DMA controller 2 (channels 4..7): -*/ -#define DMA2_CHN(c) (IO_DMA2 + 2*(2*(c))) /* addr reg for channel c */ -#define DMA2_SMSK (IO_DMA2 + 2*10) /* single mask register */ -#define DMA2_MODE (IO_DMA2 + 2*11) /* mode register */ -#define DMA2_FFC (IO_DMA2 + 2*12) /* clear first/last FF */ + * The structure used to attach devices to the isa bus. + */ +struct isa_device { + short id_port[ISA_NPORT_IVARS]; + u_short id_portsize[ISA_NPORT_IVARS]; + vm_offset_t id_maddr[ISA_NMEM_IVARS]; + vm_size_t id_msize[ISA_NMEM_IVARS]; + int id_irq[ISA_NIRQ_IVARS]; + int id_drq[ISA_NDRQ_IVARS]; + int id_flags; + struct resource *id_portres[ISA_NPORT_IVARS]; + struct resource *id_memres[ISA_NMEM_IVARS]; + struct resource *id_irqres[ISA_NIRQ_IVARS]; + struct resource *id_drqres[ISA_NDRQ_IVARS]; +}; -static void config_isadev __P((struct isa_device *isdp, u_int *mp)); -static void config_isadev_c __P((struct isa_device *isdp, u_int *mp, - int reconfig)); -static void conflict __P((struct isa_device *dvp, struct isa_device *tmpdvp, - int item, char const *whatnot, char const *reason, - char const *format)); -static int haveseen __P((struct isa_device *dvp, struct isa_device *tmpdvp, - u_int checkbits)); -static int isa_dmarangecheck __P((caddr_t va, u_int length, int chan)); +#define DEVTOISA(dev) ((struct isa_device *) device_get_ivars(dev)) -/* - * print a conflict message - */ +static devclass_t isa_devclass; + static void -conflict(dvp, tmpdvp, item, whatnot, reason, format) - struct isa_device *dvp; - struct isa_device *tmpdvp; - int item; - char const *whatnot; - char const *reason; - char const *format; +isa_add_device(device_t dev, const char *name, int unit) { - printf("%s%d not %sed due to %s conflict with %s%d at ", - dvp->id_driver->name, dvp->id_unit, whatnot, reason, - tmpdvp->id_driver->name, tmpdvp->id_unit); - printf(format, item); - printf("\n"); -} + struct isa_device *idev; + device_t child; + int sensitive, t; + static device_t last_sensitive; -/* - * Check to see if things are already in use, like IRQ's, I/O addresses - * and Memory addresses. - */ -static int -haveseen(dvp, tmpdvp, checkbits) - struct isa_device *dvp; - struct isa_device *tmpdvp; - u_int checkbits; -{ - /* - * Ignore all conflicts except IRQ ones if conflicts are allowed. - */ - if (dvp->id_conflicts) - checkbits &= ~(CC_DRQ | CC_IOADDR | CC_MEMADDR); - /* - * Only check against devices that have already been found. - */ - if (tmpdvp->id_alive) { - char const *whatnot; + if (resource_int_value(name, unit, "sensitive", &sensitive) != 0) + sensitive = 0; - /* - * Check for device driver & unit conflict; just drop probing - * a device which has already probed true. This is usually - * not strictly a conflict, but rather the case of somebody - * having specified several mutually exclusive configurations - * for a single device. - */ - if (tmpdvp->id_driver == dvp->id_driver && - tmpdvp->id_unit == dvp->id_unit) { - return 1; - } + idev = malloc(sizeof(struct isa_device), M_ISADEV, M_NOWAIT); + if (!idev) + return; + bzero(idev, sizeof *idev); - whatnot = checkbits & CC_ATTACH ? "attach" : "prob"; - /* - * Check for I/O address conflict. We can only check the - * starting address of the device against the range of the - * device that has already been probed since we do not - * know how many I/O addresses this device uses. - */ - if (checkbits & CC_IOADDR && tmpdvp->id_alive != -1) { - if ((dvp->id_iobase >= tmpdvp->id_iobase) && - (dvp->id_iobase <= - (tmpdvp->id_iobase + tmpdvp->id_alive - 1))) { - if (!(checkbits & CC_QUIET)) - conflict(dvp, tmpdvp, dvp->id_iobase, - whatnot, "I/O address", "0x%x"); - return 1; - } - } - /* - * Check for Memory address conflict. We can check for - * range overlap, but it will not catch all cases since the - * driver may adjust the msize paramater during probe, for - * now we just check that the starting address does not - * fall within any allocated region. - * XXX could add a second check after the probe for overlap, - * since at that time we would know the full range. - * XXX KERNBASE is a hack, we should have vaddr in the table! - */ - if (checkbits & CC_MEMADDR && tmpdvp->id_maddr) { - if ((KERNBASE + dvp->id_maddr >= tmpdvp->id_maddr) && - (KERNBASE + dvp->id_maddr <= - (tmpdvp->id_maddr + tmpdvp->id_msize - 1))) { - if (!(checkbits & CC_QUIET)) - conflict(dvp, tmpdvp, - (int)dvp->id_maddr, whatnot, - "maddr", "0x%x"); - return 1; - } - } - /* - * Check for IRQ conflicts. - */ - if (checkbits & CC_IRQ && tmpdvp->id_irq) { - if (tmpdvp->id_irq == dvp->id_irq) { - if (!(checkbits & CC_QUIET)) - conflict(dvp, tmpdvp, - ffs(dvp->id_irq) - 1, whatnot, - "irq", "%d"); - return 1; - } - } - /* - * Check for DRQ conflicts. - */ - if (checkbits & CC_DRQ && tmpdvp->id_drq != -1) { - if (tmpdvp->id_drq == dvp->id_drq) { - if (!(checkbits & CC_QUIET)) - conflict(dvp, tmpdvp, dvp->id_drq, - whatnot, "drq", "%d"); - return 1; - } - } - } - return 0; -} + if (resource_int_value(name, unit, "port", &t) == 0) + idev->id_port[0] = t; + else + idev->id_port[0] = -1; + idev->id_port[1] = 0; -#ifdef RESOURCE_CHECK -#include + if (resource_int_value(name, unit, "portsize", &t) == 0) + idev->id_portsize[0] = t; + else + idev->id_portsize[0] = 0; + idev->id_portsize[1] = 0; -static int -checkone (struct isa_device *dvp, int type, addr_t low, addr_t high, - char *resname, char *resfmt, int attaching) -{ - int result = 0; - if (bootverbose) { - if (low == high) - printf("\tcheck %s: 0x%x\n", resname, low); - else - printf("\tcheck %s: 0x%x to 0x%x\n", - resname, low, high); - } - if (resource_check(type, RESF_NONE, low, high) != NULL) { - char *whatnot = attaching ? "attach" : "prob"; - static struct isa_device dummydev; - static struct isa_driver dummydrv; - struct isa_device *tmpdvp = &dummydev; + if (resource_int_value(name, unit, "maddr", &t) == 0) + idev->id_maddr[0] = t; + else + idev->id_maddr[0] = 0; + idev->id_maddr[1] = 0; - dummydev.id_driver = &dummydrv; - dummydev.id_unit = 0; - dummydrv.name = "pci"; - conflict(dvp, tmpdvp, low, whatnot, resname, resfmt); - result = 1; - } else if (attaching) { - if (low == high) - printf("\tregister %s: 0x%x\n", resname, low); - else - printf("\tregister %s: 0x%x to 0x%x\n", - resname, low, high); - resource_claim(dvp, type, RESF_NONE, low, high); - } - return (result); -} + if (resource_int_value(name, unit, "msize", &t) == 0) + idev->id_msize[0] = t; + else + idev->id_msize[0] = 0; + idev->id_msize[1] = 0; -static int -check_pciconflict(struct isa_device *dvp, int checkbits) -{ - int result = 0; - int attaching = (checkbits & CC_ATTACH) != 0; + if (resource_int_value(name, unit, "flags", &t) == 0) + idev->id_flags = t; + else + idev->id_flags = 0; - if (checkbits & CC_MEMADDR) { - long maddr = dvp->id_maddr; - long msize = dvp->id_msize; - if (msize > 0) { - if (checkone(dvp, REST_MEM, maddr, maddr + msize - 1, - "maddr", "0x%x", attaching) != 0) { - result = 1; - attaching = 0; - } - } - } - if (checkbits & CC_IOADDR) { - unsigned iobase = dvp->id_iobase; - unsigned iosize = dvp->id_alive; - if (iosize == -1) - iosize = 1; /* XXX can't do much about this ... */ - if (iosize > 0) { - if (checkone(dvp, REST_PORT, iobase, iobase + iosize -1, - "I/O address", "0x%x", attaching) != 0) { - result = 1; - attaching = 0; - } - } - } - if (checkbits & CC_IRQ) { - int irq = ffs(dvp->id_irq) - 1; - if (irq >= 0) { - if (checkone(dvp, REST_INT, irq, irq, - "irq", "%d", attaching) != 0) { - result = 1; - attaching = 0; - } - } - } - if (checkbits & CC_DRQ) { - int drq = dvp->id_drq; - if (drq >= 0) { - if (checkone(dvp, REST_DMA, drq, drq, - "drq", "%d", attaching) != 0) { - result = 1; - attaching = 0; - } - } - } - if (result != 0) - resource_free (dvp); - return (result); -} -#endif /* RESOURCE_CHECK */ + if (resource_int_value(name, unit, "irq", &t) == 0) + idev->id_irq[0] = t; + else + idev->id_irq[0] = -1; + idev->id_irq[1] = -1; -/* - * Search through all the isa_devtab_* tables looking for anything that - * conflicts with the current device. - */ -int -haveseen_isadev(dvp, checkbits) - struct isa_device *dvp; - u_int checkbits; -{ -#if NPNP > 0 - struct pnp_dlist_node *nod; -#endif - struct isa_device *tmpdvp; - int status = 0; + if (resource_int_value(name, unit, "drq", &t) == 0) + idev->id_drq[0] = t; + else + idev->id_drq[0] = -1; + idev->id_drq[1] = -1; - for (tmpdvp = isa_devtab_tty; tmpdvp->id_driver; tmpdvp++) { - status |= haveseen(dvp, tmpdvp, checkbits); - if (status) - return status; - } - for (tmpdvp = isa_devtab_bio; tmpdvp->id_driver; tmpdvp++) { - status |= haveseen(dvp, tmpdvp, checkbits); - if (status) - return status; - } - for (tmpdvp = isa_devtab_net; tmpdvp->id_driver; tmpdvp++) { - status |= haveseen(dvp, tmpdvp, checkbits); - if (status) - return status; - } - for (tmpdvp = isa_devtab_cam; tmpdvp->id_driver; tmpdvp++) { - status |= haveseen(dvp, tmpdvp, checkbits); - if (status) - return status; - } - for (tmpdvp = isa_devtab_null; tmpdvp->id_driver; tmpdvp++) { - status |= haveseen(dvp, tmpdvp, checkbits); - if (status) - return status; - } -#if NPNP > 0 - for (nod = pnp_device_list; nod != NULL; nod = nod->next) - if (status |= haveseen(dvp, &(nod->dev), checkbits)) - return status; -#endif -#ifdef RESOURCE_CHECK - if (!dvp->id_conflicts) - status = check_pciconflict(dvp, checkbits); - else if (bootverbose) - printf("\tnot checking for resource conflicts ...\n"); -#endif /* RESOURCE_CHECK */ - return(status); + if (sensitive) + child = device_add_child_after(dev, last_sensitive, name, + unit, idev); + else + child = device_add_child(dev, name, unit, idev); + if (child == 0) + return; + else if (sensitive) + last_sensitive = child; + + if (resource_int_value(name, unit, "disabled", &t) == 0 && t != 0) + device_disable(child); } /* - * Configure all ISA devices + * At 'probe' time, we add all the devices which we know about to the + * bus. The generic attach routine will probe and attach them if they + * are alive. */ -void -isa_configure() +static int +isa_probe(device_t dev) { - struct isa_device *dvp; + int i; + static char buf[] = "isaXXX"; - printf("Probing for devices on the ISA bus:\n"); - /* First probe all the sensitive probes */ - for (dvp = isa_devtab_tty; dvp->id_driver; dvp++) - if (dvp->id_driver->sensitive_hw) - config_isadev(dvp, &tty_imask); - for (dvp = isa_devtab_bio; dvp->id_driver; dvp++) - if (dvp->id_driver->sensitive_hw) - config_isadev(dvp, &bio_imask); - for (dvp = isa_devtab_net; dvp->id_driver; dvp++) - if (dvp->id_driver->sensitive_hw) - config_isadev(dvp, &net_imask); - for (dvp = isa_devtab_cam; dvp->id_driver; dvp++) - if (dvp->id_driver->sensitive_hw) - config_isadev(dvp, &cam_imask); - for (dvp = isa_devtab_null; dvp->id_driver; dvp++) - if (dvp->id_driver->sensitive_hw) - config_isadev(dvp, (u_int *)NULL); + device_set_desc(dev, "ISA bus"); - /* Then all the bad ones */ - for (dvp = isa_devtab_tty; dvp->id_driver; dvp++) - if (!dvp->id_driver->sensitive_hw) - config_isadev(dvp, &tty_imask); - for (dvp = isa_devtab_bio; dvp->id_driver; dvp++) - if (!dvp->id_driver->sensitive_hw) - config_isadev(dvp, &bio_imask); - for (dvp = isa_devtab_net; dvp->id_driver; dvp++) - if (!dvp->id_driver->sensitive_hw) - config_isadev(dvp, &net_imask); - for (dvp = isa_devtab_cam; dvp->id_driver; dvp++) - if (!dvp->id_driver->sensitive_hw) - config_isadev(dvp, &cam_imask); - for (dvp = isa_devtab_null; dvp->id_driver; dvp++) - if (!dvp->id_driver->sensitive_hw) - config_isadev(dvp, (u_int *)NULL); + /* + * Add all devices configured to be attached to isa0. + */ + sprintf(buf, "isa%d", device_get_unit(dev)); + for (i = resource_query_string(-1, "at", buf); + i != -1; + i = resource_query_string(i, "at", buf)) { + isa_add_device(dev, resource_query_name(i), + resource_query_unit(i)); + } -/* - * XXX we should really add the tty device to net_imask when the line is - * switched to SLIPDISC, and then remove it when it is switched away from - * SLIPDISC. No need to block out ALL ttys during a splimp when only one - * of them is running slip. - * - * XXX actually, blocking all ttys during a splimp doesn't matter so much - * with sio because the serial interrupt layer doesn't use tty_imask. Only - * non-serial ttys suffer. It's more stupid that ALL 'net's are blocked - * during spltty. - */ -#include "sl.h" -#if NSL > 0 - net_imask |= tty_imask; - tty_imask = net_imask; -#endif - - if (bootverbose) - printf("imasks: bio %x, tty %x, net %x\n", - bio_imask, tty_imask, net_imask); - /* - * Finish initializing intr_mask[]. Note that the partly - * constructed masks aren't actually used since we're at splhigh. - * For fully dynamic initialization, register_intr() and - * icu_unset() will have to adjust the masks for _all_ - * interrupts and for tty_imask, etc. + * and isa? */ - for (dvp = isa_devtab_tty; dvp->id_driver; dvp++) - register_imask(dvp, tty_imask); - for (dvp = isa_devtab_bio; dvp->id_driver; dvp++) - register_imask(dvp, bio_imask); - for (dvp = isa_devtab_net; dvp->id_driver; dvp++) - register_imask(dvp, net_imask); - for (dvp = isa_devtab_cam; dvp->id_driver; dvp++) - register_imask(dvp, cam_imask); - for (dvp = isa_devtab_null; dvp->id_driver; dvp++) - register_imask(dvp, SWI_CLOCK_MASK); -} + for (i = resource_query_string(-1, "at", "isa"); + i != -1; + i = resource_query_string(i, "at", "isa")) { + isa_add_device(dev, resource_query_name(i), + resource_query_unit(i)); + } -/* - * Configure an ISA device. - */ -static void -config_isadev(isdp, mp) - struct isa_device *isdp; - u_int *mp; -{ - config_isadev_c(isdp, mp, 0); + isa_wrap_old_drivers(); + + return 0; } -void -reconfig_isadev(isdp, mp) - struct isa_device *isdp; - u_int *mp; +extern device_t isa_bus_device; + +static int +isa_attach(device_t dev) { - config_isadev_c(isdp, mp, 1); + /* + * Arrange for bus_generic_attach(dev) to be called later. + */ + isa_bus_device = dev; + return 0; } static void -config_isadev_c(isdp, mp, reconfig) - struct isa_device *isdp; - u_int *mp; - int reconfig; +isa_print_child(device_t bus, device_t dev) { - u_int checkbits; - int id_alive; - int last_alive; - struct isa_driver *dp = isdp->id_driver; + struct isa_device *id = DEVTOISA(dev); - if (!isdp->id_enabled) { - if (bootverbose) - printf("%s%d: disabled, not probed.\n", dp->name, isdp->id_unit); - return; + if (id->id_port[0] > 0 || id->id_port[1] > 0 + || id->id_maddr[0] > 0 || id->id_maddr[1] > 0 + || id->id_irq[0] >= 0 || id->id_irq[1] >= 0 + || id->id_drq[0] >= 0 || id->id_drq[1] >= 0) + printf(" at"); + if (id->id_port[0] > 0 && id->id_port[1] > 0) { + printf(" ports %#x", (u_int)id->id_port[0]); + if (id->id_portsize[0] > 1) + printf("-%#x", (u_int)(id->id_port[0] + + id->id_portsize[0] - 1)); + printf(" and %#x", (u_int)id->id_port[1]); + if (id->id_portsize[1] > 1) + printf("-%#x", (u_int)(id->id_port[1] + + id->id_portsize[1] - 1)); + } else if (id->id_port[0] > 0) { + printf(" port %#x", (u_int)id->id_port[0]); + if (id->id_portsize[0] > 1) + printf("-%#x", (u_int)(id->id_port[0] + + id->id_portsize[0] - 1)); + } else if (id->id_port[1] > 0) { + printf(" port %#x", (u_int)id->id_port[1]); + if (id->id_portsize[1] > 1) + printf("-%#x", (u_int)(id->id_port[1] + + id->id_portsize[1] - 1)); } - checkbits = CC_DRQ | CC_IOADDR | CC_MEMADDR; - if (!reconfig && haveseen_isadev(isdp, checkbits)) - return; - if (!reconfig && isdp->id_maddr) { - isdp->id_maddr -= ISA_HOLE_START; - isdp->id_maddr += atdevbase; + if (id->id_maddr[0] && id->id_maddr[1]) { + printf(" iomem %#x", (u_int)id->id_maddr[0]); + if (id->id_msize[0]) + printf("-%#x", (u_int)(id->id_maddr[0] + + id->id_msize[0] - 1)); + printf(" and %#x", (u_int)id->id_maddr[1]); + if (id->id_msize[1]) + printf("-%#x", (u_int)(id->id_maddr[1] + + id->id_msize[1] - 1)); + } else if (id->id_maddr[0]) { + printf(" iomem %#x", (u_int)id->id_maddr[0]); + if (id->id_msize[0]) + printf("-%#x", (u_int)(id->id_maddr[0] + + id->id_msize[0] - 1)); + } else if (id->id_maddr[1]) { + printf(" iomem %#x", (u_int)id->id_maddr[1]); + if (id->id_msize[1]) + printf("-%#x", (u_int)(id->id_maddr[1] + + id->id_msize[1] - 1)); } - if (reconfig) { - last_alive = isdp->id_alive; - isdp->id_reconfig = 1; - } - else { - last_alive = 0; - isdp->id_reconfig = 0; - } - id_alive = (*dp->probe)(isdp); - if (id_alive) { - /* - * Only print the I/O address range if id_alive != -1 - * Right now this is a temporary fix just for the new - * NPX code so that if it finds a 486 that can use trap - * 16 it will not report I/O addresses. - * Rod Grimes 04/26/94 - */ - if (!isdp->id_reconfig) { - printf("%s%d", dp->name, isdp->id_unit); - if (id_alive != -1) { - if (isdp->id_iobase == -1) - printf(" at"); - else { - printf(" at 0x%x", isdp->id_iobase); - if (isdp->id_iobase + id_alive - 1 != - isdp->id_iobase) { - printf("-0x%x", - isdp->id_iobase + id_alive - 1); - } - } - } - if (isdp->id_irq) - printf(" irq %d", ffs(isdp->id_irq) - 1); - if (isdp->id_drq != -1) - printf(" drq %d", isdp->id_drq); - if (isdp->id_maddr) - printf(" maddr 0x%lx", kvtop(isdp->id_maddr)); - if (isdp->id_msize) - printf(" msize %d", isdp->id_msize); - if (isdp->id_flags) - printf(" flags 0x%x", isdp->id_flags); - if (isdp->id_iobase && !(isdp->id_iobase & 0xf300)) { - printf(" on motherboard"); - } else if (isdp->id_iobase >= 0x1000 && - !(isdp->id_iobase & 0x300)) { - printf (" on eisa slot %d", - isdp->id_iobase >> 12); - } else { - printf (" on isa"); - } - printf("\n"); - /* - * Check for conflicts again. The driver may have - * changed *dvp. We should weaken the early check - * since the driver may have been able to change - * *dvp to avoid conflicts if given a chance. We - * already skip the early check for IRQs and force - * a check for IRQs in the next group of checks. - */ - checkbits |= CC_ATTACH | CC_IRQ; - if (haveseen_isadev(isdp, checkbits)) - return; - isdp->id_alive = id_alive; - } - (*dp->attach)(isdp); - if (isdp->id_irq != 0 && isdp->id_intr == NULL) - printf("%s%d: irq with no handler\n", - dp->name, isdp->id_unit); - if (isdp->id_irq != 0 && isdp->id_intr != NULL) { -#ifdef APIC_IO - /* - * Some motherboards use upper IRQs for traditional - * ISA INTerrupt sources. In particular we have - * seen the secondary IDE connected to IRQ20. - * This code detects and fixes this situation. - */ - u_int apic_mask; - int rirq; + if (id->id_irq[0] >= 0 && id->id_irq[1] >= 0) + printf(" irqs %d and %d", id->id_irq[0], id->id_irq[1]); + else if (id->id_irq[0] >= 0) + printf(" irq %d", id->id_irq[0]); + else if (id->id_irq[1] >= 0) + printf(" irq %d", id->id_irq[1]); + if (id->id_drq[0] >= 0 && id->id_drq[1] >= 0) + printf(" drqs %d and %d", id->id_drq[0], id->id_drq[1]); + else if (id->id_drq[0] >= 0) + printf(" drq %d", id->id_drq[0]); + else if (id->id_drq[1] >= 0) + printf(" drq %d", id->id_drq[1]); - apic_mask = isa_apic_mask(isdp->id_irq); - if (apic_mask != isdp->id_irq) { - rirq = ffs(isdp->id_irq) - 1; - isdp->id_irq = apic_mask; - undirect_isa_irq(rirq); /* free for ISA */ - } -#endif /* APIC_IO */ - register_intr(ffs(isdp->id_irq) - 1, isdp->id_id, - isdp->id_ri_flags, isdp->id_intr, - mp, isdp->id_unit); - } - } else { - if (isdp->id_reconfig) { - (*dp->attach)(isdp); /* reconfiguration attach */ - } - if (!last_alive) { - if (!isdp->id_reconfig) { - printf("%s%d not found", - dp->name, isdp->id_unit); - if (isdp->id_iobase != -1) - printf(" at 0x%x", isdp->id_iobase); - printf("\n"); - } - } else { -#if 0 - /* This code has not been tested.... */ - if (isdp->id_irq != 0 && isdp->id_intr != NULL) { - icu_unset(ffs(isdp->id_irq) - 1, - isdp->id_intr); - if (mp) - INTRUNMASK(*mp, isdp->id_irq); - } -#else - printf ("icu_unset() not supported here ...\n"); -#endif - } - } + if (id->id_flags) + printf(" flags %#x", id->id_flags); + + printf(" on %s%d", + device_get_name(bus), device_get_unit(bus)); } -static caddr_t dma_bouncebuf[8]; -static u_int dma_bouncebufsize[8]; -static u_int8_t dma_bounced = 0; -static u_int8_t dma_busy = 0; /* Used in isa_dmastart() */ -static u_int8_t dma_inuse = 0; /* User for acquire/release */ -static u_int8_t dma_auto_mode = 0; - -#define VALID_DMA_MASK (7) - -/* high byte of address is stored in this port for i-th dma channel */ -static int dmapageport[8] = { 0x87, 0x83, 0x81, 0x82, 0x8f, 0x8b, 0x89, 0x8a }; - -/* - * Setup a DMA channel's bounce buffer. - */ -void -isa_dmainit(chan, bouncebufsize) - int chan; - u_int bouncebufsize; +static int +isa_read_ivar(device_t bus, device_t dev, int index, uintptr_t * result) { - void *buf; + struct isa_device* idev = DEVTOISA(dev); -#ifdef DIAGNOSTIC - if (chan & ~VALID_DMA_MASK) - panic("isa_dmainit: channel out of range"); - - if (dma_bouncebuf[chan] != NULL) - panic("isa_dmainit: impossible request"); -#endif - - dma_bouncebufsize[chan] = bouncebufsize; - - /* Try malloc() first. It works better if it works. */ - buf = malloc(bouncebufsize, M_DEVBUF, M_NOWAIT); - if (buf != NULL) { - if (isa_dmarangecheck(buf, bouncebufsize, chan) == 0) { - dma_bouncebuf[chan] = buf; - return; - } - free(buf, M_DEVBUF); + switch (index) { + case ISA_IVAR_PORT_0: + *result = idev->id_port[0]; + break; + case ISA_IVAR_PORT_1: + *result = idev->id_port[1]; + break; + case ISA_IVAR_PORTSIZE_0: + *result = idev->id_portsize[0]; + break; + case ISA_IVAR_PORTSIZE_1: + *result = idev->id_portsize[1]; + break; + case ISA_IVAR_MADDR_0: + *result = idev->id_maddr[0]; + break; + case ISA_IVAR_MADDR_1: + *result = idev->id_maddr[1]; + break; + case ISA_IVAR_MSIZE_0: + *result = idev->id_msize[0]; + break; + case ISA_IVAR_MSIZE_1: + *result = idev->id_msize[1]; + break; + case ISA_IVAR_IRQ_0: + *result = idev->id_irq[0]; + break; + case ISA_IVAR_IRQ_1: + *result = idev->id_irq[1]; + break; + case ISA_IVAR_DRQ_0: + *result = idev->id_drq[0]; + break; + case ISA_IVAR_DRQ_1: + *result = idev->id_drq[1]; + break; + case ISA_IVAR_FLAGS: + *result = idev->id_flags; + break; } - buf = contigmalloc(bouncebufsize, M_DEVBUF, M_NOWAIT, 0ul, 0xfffffful, - 1ul, chan & 4 ? 0x20000ul : 0x10000ul); - if (buf == NULL) - printf("isa_dmainit(%d, %d) failed\n", chan, bouncebufsize); - else - dma_bouncebuf[chan] = buf; + return ENOENT; } /* - * Register a DMA channel's usage. Usually called from a device driver - * in open() or during its initialization. + * XXX -- this interface is pretty much irrelevant in the presence of + * BUS_ALLOC_RESOURCE / BUS_RELEASE_RESOURCE (at least for the ivars which + * are defined at this point). */ -int -isa_dma_acquire(chan) - int chan; +static int +isa_write_ivar(device_t bus, device_t dev, + int index, uintptr_t value) { -#ifdef DIAGNOSTIC - if (chan & ~VALID_DMA_MASK) - panic("isa_dma_acquire: channel out of range"); -#endif + struct isa_device* idev = DEVTOISA(dev); - if (dma_inuse & (1 << chan)) { - printf("isa_dma_acquire: channel %d already in use\n", chan); - return (EBUSY); + switch (index) { + case ISA_IVAR_PORT_0: + idev->id_port[0] = value; + break; + case ISA_IVAR_PORT_1: + idev->id_port[1] = value; + break; + case ISA_IVAR_PORTSIZE_0: + idev->id_portsize[0] = value; + break; + case ISA_IVAR_PORTSIZE_1: + idev->id_portsize[1] = value; + break; + case ISA_IVAR_MADDR_0: + idev->id_maddr[0] = value; + break; + case ISA_IVAR_MADDR_1: + idev->id_maddr[1] = value; + break; + case ISA_IVAR_MSIZE_0: + idev->id_msize[0] = value; + break; + case ISA_IVAR_MSIZE_1: + idev->id_msize[1] = value; + break; + case ISA_IVAR_IRQ_0: + idev->id_irq[0] = value; + break; + case ISA_IVAR_IRQ_1: + idev->id_irq[1] = value; + break; + case ISA_IVAR_DRQ_0: + idev->id_drq[0] = value; + break; + case ISA_IVAR_DRQ_1: + idev->id_drq[1] = value; + break; + case ISA_IVAR_FLAGS: + idev->id_flags = value; + break; + default: + return (ENOENT); } - dma_inuse |= (1 << chan); - dma_auto_mode &= ~(1 << chan); - return (0); } /* - * Unregister a DMA channel's usage. Usually called from a device driver - * during close() or during its shutdown. + * This implementation simply passes the request up to the parent + * bus, which in our case is the special i386 nexus, substituting any + * configured values if the caller defaulted. We can get away with + * this because there is no special mapping for ISA resources on an Intel + * platform. When porting this code to another architecture, it may be + * necessary to interpose a mapping layer here. */ -void -isa_dma_release(chan) - int chan; +static struct resource * +isa_alloc_resource(device_t bus, device_t child, int type, int *rid, + u_long start, u_long end, u_long count, u_int flags) { -#ifdef DIAGNOSTIC - if (chan & ~VALID_DMA_MASK) - panic("isa_dma_release: channel out of range"); + int isdefault; + struct resource *rv, **rvp = 0; + struct isa_device *id = DEVTOISA(child); - if ((dma_inuse & (1 << chan)) == 0) - printf("isa_dma_release: channel %d not in use\n", chan); -#endif - - if (dma_busy & (1 << chan)) { - dma_busy &= ~(1 << chan); - /* - * XXX We should also do "dma_bounced &= (1 << chan);" - * because we are acting on behalf of isa_dmadone() which - * was not called to end the last DMA operation. This does - * not matter now, but it may in the future. + if (child) { + /* + * If this is our child, then use the isa_device to find + * defaults and to record results. */ - } + if (device_get_devclass(device_get_parent(child)) == isa_devclass) + id = DEVTOISA(child); + else + id = NULL; + } else + id = NULL; + isdefault = (id != NULL && start == 0UL && end == ~0UL && *rid == 0); + if (*rid > 1) + return 0; - dma_inuse &= ~(1 << chan); - dma_auto_mode &= ~(1 << chan); -} + switch (type) { + case SYS_RES_IRQ: + if (isdefault && id->id_irq[0] >= 0) { + start = id->id_irq[0]; + end = id->id_irq[0]; + count = 1; + } + if (id) + rvp = &id->id_irqres[*rid]; + break; -/* - * isa_dmacascade(): program 8237 DMA controller channel to accept - * external dma control by a board. - */ -void -isa_dmacascade(chan) - int chan; -{ -#ifdef DIAGNOSTIC - if (chan & ~VALID_DMA_MASK) - panic("isa_dmacascade: channel out of range"); -#endif + case SYS_RES_DRQ: + if (isdefault && id->id_drq[0] >= 0) { + start = id->id_drq[0]; + end = id->id_drq[0]; + count = 1; + } + if (id) + rvp = &id->id_drqres[*rid]; + break; - /* set dma channel mode, and set dma channel mode */ - if ((chan & 4) == 0) { - outb(DMA1_MODE, DMA37MD_CASCADE | chan); - outb(DMA1_SMSK, chan); - } else { - outb(DMA2_MODE, DMA37MD_CASCADE | (chan & 3)); - outb(DMA2_SMSK, chan & 3); - } -} + case SYS_RES_MEMORY: + if (isdefault && id->id_maddr[0]) { + start = id->id_maddr[0]; + count = max(count, (u_long)id->id_msize[0]); + end = id->id_maddr[0] + count; + } + if (id) + rvp = &id->id_memres[*rid]; + break; -/* - * isa_dmastart(): program 8237 DMA controller channel, avoid page alignment - * problems by using a bounce buffer. - */ -void -isa_dmastart(int flags, caddr_t addr, u_int nbytes, int chan) -{ - vm_offset_t phys; - int waport; - caddr_t newaddr; + case SYS_RES_IOPORT: + if (isdefault && id->id_port[0]) { + start = id->id_port[0]; + count = max(count, (u_long)id->id_portsize[0]); + end = id->id_port[0] + count; + } + if (id) + rvp = &id->id_portres[*rid]; + break; -#ifdef DIAGNOSTIC - if (chan & ~VALID_DMA_MASK) - panic("isa_dmastart: channel out of range"); + default: + return 0; + } - if ((chan < 4 && nbytes > (1<<16)) - || (chan >= 4 && (nbytes > (1<<17) || (u_int)addr & 1))) - panic("isa_dmastart: impossible request"); + /* + * If the client attempts to reallocate a resource without + * releasing what was there previously, die horribly so that + * he knows how he !@#$ed up. + */ + if (rvp && *rvp != 0) + panic("%s%d: (%d, %d) not free for %s%d\n", + device_get_name(bus), device_get_unit(bus), + type, *rid, + device_get_name(child), device_get_unit(child)); - if ((dma_inuse & (1 << chan)) == 0) - printf("isa_dmastart: channel %d not acquired\n", chan); -#endif - -#if 0 /* - * XXX This should be checked, but drivers like ad1848 only call - * isa_dmastart() once because they use Auto DMA mode. If we - * leave this in, drivers that do this will print this continuously. + * nexus_alloc_resource had better not change *rid... */ - if (dma_busy & (1 << chan)) - printf("isa_dmastart: channel %d busy\n", chan); -#endif + rv = BUS_ALLOC_RESOURCE(device_get_parent(bus), child, type, rid, + start, end, count, flags); + if (rvp && (*rvp = rv) != 0) { + switch (type) { + case SYS_RES_MEMORY: + id->id_maddr[*rid] = rv->r_start; + id->id_msize[*rid] = count; + break; + case SYS_RES_IOPORT: + id->id_port[*rid] = rv->r_start; + id->id_portsize[*rid] = count; + break; + case SYS_RES_IRQ: + id->id_irq[*rid] = rv->r_start; + break; + case SYS_RES_DRQ: + id->id_drq[*rid] = rv->r_start; + break; + } + } + return rv; +} - dma_busy |= (1 << chan); +static int +isa_release_resource(device_t bus, device_t child, int type, int rid, + struct resource *r) +{ + int rv; + struct isa_device *id = DEVTOISA(child); - if (isa_dmarangecheck(addr, nbytes, chan)) { - if (dma_bouncebuf[chan] == NULL - || dma_bouncebufsize[chan] < nbytes) - panic("isa_dmastart: bad bounce buffer"); - dma_bounced |= (1 << chan); - newaddr = dma_bouncebuf[chan]; + if (rid > 1) + return EINVAL; - /* copy bounce buffer on write */ - if (!(flags & B_READ)) - bcopy(addr, newaddr, nbytes); - addr = newaddr; + switch (type) { + case SYS_RES_IRQ: + case SYS_RES_DRQ: + case SYS_RES_IOPORT: + case SYS_RES_MEMORY: + break; + default: + return (ENOENT); } - /* translate to physical */ - phys = pmap_extract(pmap_kernel(), (vm_offset_t)addr); + rv = BUS_RELEASE_RESOURCE(device_get_parent(bus), child, type, rid, r); - if (flags & B_RAW) { - dma_auto_mode |= (1 << chan); - } else { - dma_auto_mode &= ~(1 << chan); +#if 0 + if (rv) { + /* Kludge, isa as a child of pci doesn't have mapping regs */ + printf("WARNING: isa_release_resource: BUS_RELEASE_RESOURCE() failed: %d\n", rv); + rv = 0; } +#endif - if ((chan & 4) == 0) { - /* - * Program one of DMA channels 0..3. These are - * byte mode channels. - */ - /* set dma channel mode, and reset address ff */ + if (rv == 0) { + switch (type) { + case SYS_RES_IRQ: + id->id_irqres[rid] = 0; + id->id_irq[rid] = -1; + break; - /* If B_RAW flag is set, then use autoinitialise mode */ - if (flags & B_RAW) { - if (flags & B_READ) - outb(DMA1_MODE, DMA37MD_AUTO|DMA37MD_WRITE|chan); - else - outb(DMA1_MODE, DMA37MD_AUTO|DMA37MD_READ|chan); - } - else - if (flags & B_READ) - outb(DMA1_MODE, DMA37MD_SINGLE|DMA37MD_WRITE|chan); - else - outb(DMA1_MODE, DMA37MD_SINGLE|DMA37MD_READ|chan); - outb(DMA1_FFC, 0); + case SYS_RES_DRQ: + id->id_drqres[rid] = 0; + id->id_drq[rid] = -1; + break; - /* send start address */ - waport = DMA1_CHN(chan); - outb(waport, phys); - outb(waport, phys>>8); - outb(dmapageport[chan], phys>>16); + case SYS_RES_MEMORY: + id->id_memres[rid] = 0; + id->id_maddr[rid] = 0; + id->id_msize[rid] = 0; + break; - /* send count */ - outb(waport + 1, --nbytes); - outb(waport + 1, nbytes>>8); + case SYS_RES_IOPORT: + id->id_portres[rid] = 0; + id->id_port[rid] = 0; + id->id_portsize[rid] = 0; + break; - /* unmask channel */ - outb(DMA1_SMSK, chan); - } else { - /* - * Program one of DMA channels 4..7. These are - * word mode channels. - */ - /* set dma channel mode, and reset address ff */ - - /* If B_RAW flag is set, then use autoinitialise mode */ - if (flags & B_RAW) { - if (flags & B_READ) - outb(DMA2_MODE, DMA37MD_AUTO|DMA37MD_WRITE|(chan&3)); - else - outb(DMA2_MODE, DMA37MD_AUTO|DMA37MD_READ|(chan&3)); + default: + return ENOENT; } - else - if (flags & B_READ) - outb(DMA2_MODE, DMA37MD_SINGLE|DMA37MD_WRITE|(chan&3)); - else - outb(DMA2_MODE, DMA37MD_SINGLE|DMA37MD_READ|(chan&3)); - outb(DMA2_FFC, 0); - - /* send start address */ - waport = DMA2_CHN(chan - 4); - outb(waport, phys>>1); - outb(waport, phys>>9); - outb(dmapageport[chan], phys>>16); - - /* send count */ - nbytes >>= 1; - outb(waport + 2, --nbytes); - outb(waport + 2, nbytes>>8); - - /* unmask channel */ - outb(DMA2_SMSK, chan & 3); } -} -void -isa_dmadone(int flags, caddr_t addr, int nbytes, int chan) -{ -#ifdef DIAGNOSTIC - if (chan & ~VALID_DMA_MASK) - panic("isa_dmadone: channel out of range"); - - if ((dma_inuse & (1 << chan)) == 0) - printf("isa_dmadone: channel %d not acquired\n", chan); -#endif - - if (((dma_busy & (1 << chan)) == 0) && - (dma_auto_mode & (1 << chan)) == 0 ) - printf("isa_dmadone: channel %d not busy\n", chan); - - if ((dma_auto_mode & (1 << chan)) == 0) - outb(chan & 4 ? DMA2_SMSK : DMA1_SMSK, (chan & 3) | 4); - - if (dma_bounced & (1 << chan)) { - /* copy bounce buffer on read */ - if (flags & B_READ) - bcopy(dma_bouncebuf[chan], addr, nbytes); - - dma_bounced &= ~(1 << chan); - } - dma_busy &= ~(1 << chan); + return rv; } /* - * Check for problems with the address range of a DMA transfer - * (non-contiguous physical pages, outside of bus address space, - * crossing DMA page boundaries). - * Return true if special handling needed. + * We can't use the bus_generic_* versions of these methods because those + * methods always pass the bus param as the requesting device, and we need + * to pass the child (the i386 nexus knows about this and is prepared to + * deal). */ - static int -isa_dmarangecheck(caddr_t va, u_int length, int chan) +isa_setup_intr(device_t bus, device_t child, struct resource *r, + void (*ihand)(void *), void *arg, void **cookiep) { - vm_offset_t phys, priorpage = 0, endva; - u_int dma_pgmsk = (chan & 4) ? ~(128*1024-1) : ~(64*1024-1); - - endva = (vm_offset_t)round_page((vm_offset_t)va + length); - for (; va < (caddr_t) endva ; va += PAGE_SIZE) { - phys = trunc_page(pmap_extract(pmap_kernel(), (vm_offset_t)va)); -#define ISARAM_END RAM_END - if (phys == 0) - panic("isa_dmacheck: no physical page present"); - if (phys >= ISARAM_END) - return (1); - if (priorpage) { - if (priorpage + PAGE_SIZE != phys) - return (1); - /* check if crossing a DMA page boundary */ - if (((u_int)priorpage ^ (u_int)phys) & dma_pgmsk) - return (1); - } - priorpage = phys; - } - return (0); + return (BUS_SETUP_INTR(device_get_parent(bus), child, r, ihand, arg, + cookiep)); } -/* - * Query the progress of a transfer on a DMA channel. - * - * To avoid having to interrupt a transfer in progress, we sample - * each of the high and low databytes twice, and apply the following - * logic to determine the correct count. - * - * Reads are performed with interrupts disabled, thus it is to be - * expected that the time between reads is very small. At most - * one rollover in the low count byte can be expected within the - * four reads that are performed. - * - * There are three gaps in which a rollover can occur : - * - * - read low1 - * gap1 - * - read high1 - * gap2 - * - read low2 - * gap3 - * - read high2 - * - * If a rollover occurs in gap1 or gap2, the low2 value will be - * greater than the low1 value. In this case, low2 and high2 are a - * corresponding pair. - * - * In any other case, low1 and high1 can be considered to be correct. - * - * The function returns the number of bytes remaining in the transfer, - * or -1 if the channel requested is not active. - * - */ -int -isa_dmastatus(int chan) +static int +isa_teardown_intr(device_t bus, device_t child, struct resource *r, + void *cookie) { - u_long cnt = 0; - int ffport, waport; - u_long low1, high1, low2, high2; - - /* channel active? */ - if ((dma_inuse & (1 << chan)) == 0) { - printf("isa_dmastatus: channel %d not active\n", chan); - return(-1); - } - /* channel busy? */ - - if (((dma_busy & (1 << chan)) == 0) && - (dma_auto_mode & (1 << chan)) == 0 ) { - printf("chan %d not busy\n", chan); - return -2 ; - } - if (chan < 4) { /* low DMA controller */ - ffport = DMA1_FFC; - waport = DMA1_CHN(chan) + 1; - } else { /* high DMA controller */ - ffport = DMA2_FFC; - waport = DMA2_CHN(chan - 4) + 2; - } - - disable_intr(); /* no interrupts Mr Jones! */ - outb(ffport, 0); /* clear register LSB flipflop */ - low1 = inb(waport); - high1 = inb(waport); - outb(ffport, 0); /* clear again */ - low2 = inb(waport); - high2 = inb(waport); - enable_intr(); /* enable interrupts again */ - - /* - * Now decide if a wrap has tried to skew our results. - * Note that after TC, the count will read 0xffff, while we want - * to return zero, so we add and then mask to compensate. - */ - if (low1 >= low2) { - cnt = (low1 + (high1 << 8) + 1) & 0xffff; - } else { - cnt = (low2 + (high2 << 8) + 1) & 0xffff; - } - - if (chan >= 4) /* high channels move words */ - cnt *= 2; - return(cnt); + return (BUS_TEARDOWN_INTR(device_get_parent(bus), child, r, cookie)); } -/* - * Stop a DMA transfer currently in progress. - */ -int -isa_dmastop(int chan) -{ - if ((dma_inuse & (1 << chan)) == 0) - printf("isa_dmastop: channel %d not acquired\n", chan); +static device_method_t isa_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, isa_probe), + DEVMETHOD(device_attach, isa_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), - if (((dma_busy & (1 << chan)) == 0) && - ((dma_auto_mode & (1 << chan)) == 0)) { - printf("chan %d not busy\n", chan); - return -2 ; - } - - if ((chan & 4) == 0) { - outb(DMA1_SMSK, (chan & 3) | 4 /* disable mask */); - } else { - outb(DMA2_SMSK, (chan & 3) | 4 /* disable mask */); - } - return(isa_dmastatus(chan)); -} + /* Bus interface */ + DEVMETHOD(bus_print_child, isa_print_child), + DEVMETHOD(bus_read_ivar, isa_read_ivar), + DEVMETHOD(bus_write_ivar, isa_write_ivar), + DEVMETHOD(bus_alloc_resource, isa_alloc_resource), + DEVMETHOD(bus_release_resource, isa_release_resource), + DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), + DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), + DEVMETHOD(bus_setup_intr, isa_setup_intr), + DEVMETHOD(bus_teardown_intr, isa_teardown_intr), -/* - * Find the highest priority enabled display device. Since we can't - * distinguish display devices from ttys, depend on display devices - * being sensitive and before sensitive non-display devices (if any) - * in isa_devtab_tty. - * - * XXX we should add capability flags IAMDISPLAY and ISUPPORTCONSOLES. - */ -struct isa_device * -find_display() -{ - struct isa_device *dvp; + { 0, 0 } +}; - for (dvp = isa_devtab_tty; dvp->id_driver != NULL; dvp++) - if (dvp->id_driver->sensitive_hw && dvp->id_enabled) - return (dvp); - return (NULL); -} +static driver_t isa_driver = { + "isa", + isa_methods, + DRIVER_TYPE_MISC, + 1, /* no softc */ +}; /* - * find an ISA device in a given isa_devtab_* table, given - * the table to search, the expected id_driver entry, and the unit number. - * - * this function is defined in isa_device.h, and this location is debatable; - * i put it there because it's useless w/o, and directly operates on - * the other stuff in that file. - * + * ISA can be attached to a PCI-ISA bridge or directly to the nexus. */ - -struct isa_device * -find_isadev(table, driverp, unit) - struct isa_device *table; - struct isa_driver *driverp; - int unit; -{ - if (driverp == NULL) /* sanity check */ - return (NULL); - - while ((table->id_driver != driverp) || (table->id_unit != unit)) { - if (table->id_driver == 0) - return NULL; - - table++; - } - - return (table); -} +DRIVER_MODULE(isa, isab, isa_driver, isa_devclass, 0, 0); +DRIVER_MODULE(isa, nexus, isa_driver, isa_devclass, 0, 0); Index: head/sys/i386/isa/isa_compat.c =================================================================== --- head/sys/i386/isa/isa_compat.c (nonexistent) +++ head/sys/i386/isa/isa_compat.c (revision 45720) @@ -0,0 +1,255 @@ +/*- + * Copyright (c) 1998 Doug Rabson + * 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. + * + * $Id$ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +struct isa_compat_resources { + struct resource *ports; + struct resource *memory; + struct resource *drq; + struct resource *irq; +}; + +int +isa_compat_nextid(void) +{ + static int id = 2; /* id_id of -1, 0 and 1 are "used" */ + + return id++; +} + +static void +isa_compat_alloc_resources(device_t dev, struct isa_compat_resources *res) +{ + int rid; + + if (isa_get_port(dev) != -1) { + rid = 0; + res->ports = bus_alloc_resource(dev, SYS_RES_IOPORT, + &rid, 0ul, ~0ul, 1, + RF_ACTIVE); + if (res->ports == NULL) + printf("isa_compat: didn't get ports for %s\n", + device_get_name(dev)); + } else + res->ports = 0; + + if (isa_get_maddr(dev) != 0) { + rid = 0; + res->memory = bus_alloc_resource(dev, SYS_RES_MEMORY, + &rid, 0ul, ~0ul, 1, + RF_ACTIVE); + if (res->memory == NULL) + printf("isa_compat: didn't get memory for %s\n", + device_get_name(dev)); + } else + res->memory = 0; + + if (isa_get_drq(dev) != -1) { + rid = 0; + res->drq = bus_alloc_resource(dev, SYS_RES_DRQ, + &rid, 0ul, ~0ul, 1, + RF_ACTIVE); + if (res->drq == NULL) + printf("isa_compat: didn't get drq for %s\n", + device_get_name(dev)); + } else + res->drq = 0; + + if (isa_get_irq(dev) != -1) { + rid = 0; + res->irq = bus_alloc_resource(dev, SYS_RES_IRQ, + &rid, 0ul, ~0ul, 1, + RF_SHAREABLE | RF_ACTIVE); + if (res->irq == NULL) + printf("isa_compat: didn't get irq for %s\n", + device_get_name(dev)); + } else + res->irq = 0; +} + +static void +isa_compat_release_resources(device_t dev, struct isa_compat_resources *res) +{ + if (res->ports) { + bus_release_resource(dev, SYS_RES_IOPORT, 0, res->ports); + res->ports = 0; + } + if (res->memory) { + bus_release_resource(dev, SYS_RES_MEMORY, 0, res->memory); + res->memory = 0; + } + if (res->drq) { + bus_release_resource(dev, SYS_RES_DRQ, 0, res->drq); + res->drq = 0; + } + if (res->irq) { + bus_release_resource(dev, SYS_RES_IRQ, 0, res->irq); + res->irq = 0; + } +} + +static int +isa_compat_probe(device_t dev) +{ + struct isa_device *dvp = device_get_softc(dev); + struct isa_compat_resources res; + + bzero(&res, sizeof(res)); + /* + * Fill in the isa_device fields. + */ + dvp->id_id = isa_compat_nextid(); + dvp->id_driver = device_get_driver(dev)->priv; + dvp->id_iobase = isa_get_port(dev); + dvp->id_irq = (1 << isa_get_irq(dev)); + dvp->id_drq = isa_get_drq(dev); + dvp->id_maddr = (void *)isa_get_maddr(dev); + dvp->id_msize = isa_get_msize(dev); + dvp->id_unit = device_get_unit(dev); + dvp->id_flags = isa_get_flags(dev); + dvp->id_enabled = device_is_enabled(dev); + + /* + * Do the wrapped probe. + */ + if (dvp->id_driver->probe) { + int portsize; + isa_compat_alloc_resources(dev, &res); + if (res.memory) + dvp->id_maddr = rman_get_virtual(res.memory); + else + dvp->id_maddr = 0; + portsize = dvp->id_driver->probe(dvp); + isa_compat_release_resources(dev, &res); + if (portsize != 0) { + if (portsize > 0) + isa_set_portsize(dev, portsize); + if (dvp->id_iobase != isa_get_port(dev)) + isa_set_port(dev, dvp->id_iobase); + if (dvp->id_irq != (1 << isa_get_irq(dev))) + isa_set_irq(dev, ffs(dvp->id_irq) - 1); + if (dvp->id_drq != isa_get_drq(dev)) + isa_set_drq(dev, dvp->id_drq); + if (dvp->id_maddr != (void *) isa_get_maddr(dev)) + isa_set_maddr(dev, (int) dvp->id_maddr); + if (dvp->id_msize != isa_get_msize(dev)) + isa_set_msize(dev, dvp->id_msize); + return 0; + } + } + return ENXIO; +} + +static int +isa_compat_attach(device_t dev) +{ + struct isa_device *dvp = device_get_softc(dev); + struct isa_compat_resources res; + int error; + + bzero(&res, sizeof(res)); + isa_compat_alloc_resources(dev, &res); + if (dvp->id_driver->attach) + dvp->id_driver->attach(dvp); + if (res.irq && dvp->id_irq && dvp->id_intr) { + void *ih; + + error = BUS_SETUP_INTR(device_get_parent(dev), dev, + res.irq, + dvp->id_intr, + (void *)(uintptr_t)dvp->id_unit, + &ih); + if (error) + printf("isa_compat_attach: failed to setup intr: %d\n", + error); + } + return 0; +} + +static device_method_t isa_compat_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, isa_compat_probe), + DEVMETHOD(device_attach, isa_compat_attach), + + { 0, 0 } +}; + +/* + * Create a new style driver around each old isa driver. + */ +void +isa_wrap_old_drivers(void) +{ + int i; + struct old_isa_driver *op; + devclass_t isa_devclass = devclass_find("isa"); + + for (i = 0, op = &old_drivers[0]; i < old_drivers_count; i++, op++) { + driver_t *driver; + driver = malloc(sizeof(driver_t), M_DEVBUF, M_NOWAIT); + if (!driver) + continue; + bzero(driver, sizeof(driver_t)); + driver->name = op->driver->name; + driver->methods = isa_compat_methods; + driver->type = op->type; + driver->softc = sizeof(struct isa_device *); + driver->priv = op->driver; + devclass_add_driver(isa_devclass, driver); + } +} + +int +haveseen_isadev(dvp, checkbits) + struct isa_device *dvp; + u_int checkbits; +{ + printf("haveseen_isadev() called - FIXME!\n"); + return 0; +} + +void +reconfig_isadev(isdp, mp) + struct isa_device *isdp; + u_int *mp; +{ + printf("reconfig_isadev() called - FIXME!\n"); +} Property changes on: head/sys/i386/isa/isa_compat.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: head/sys/i386/isa/isa_compat.h =================================================================== --- head/sys/i386/isa/isa_compat.h (nonexistent) +++ head/sys/i386/isa/isa_compat.h (revision 45720) @@ -0,0 +1,378 @@ +/*- + * Copyright (c) 1998 Doug Rabson + * 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. + * + * $Id$ + */ + +#include "vt.h" +#include "bt.h" +#include "adv.h" +#include "aha.h" +#include "wdc.h" +#include "mse.h" +#include "ar.h" +#include "cs.h" +#include "cx.h" +#include "ed.h" +#include "el.h" +#include "ep.h" +#include "ex.h" +#include "fe.h" +#include "ie.h" +#include "le.h" +#include "lnc.h" +#include "rdp.h" +#include "sr.h" +#include "wl.h" +#include "ze.h" +#include "zp.h" +#include "oltr.h" +#include "pas.h" +#include "sb.h" +#include "sbxvi.h" +#include "sbmidi.h" +#include "awe.h" +#include "gus.h" +#include "mss.h" +#include "css.h" +#include "sscape.h" +#include "trix.h" +#include "opl.h" +#include "mpu.h" +#include "uart.h" +#include "pca.h" +#include "mcd.h" +#include "scd.h" +#include "matcd.h" +#include "wt.h" +#include "ctx.h" +#include "spigot.h" +#include "gp.h" +#include "gsc.h" +#include "joy.h" +#include "cy.h" +#include "dgb.h" +#include "dgm.h" +#include "labpc.h" +#include "rc.h" +#include "rp.h" +#include "tw.h" +#include "si.h" +#include "asc.h" +#include "stl.h" +#include "stli.h" +#include "loran.h" +#include "pcf.h" +#include "isic.h" +#include "tina.h" +#include "ppc.h" + +struct old_isa_driver { + int type; + struct isa_driver *driver; +}; + +extern struct isa_driver vtdriver; +extern struct isa_driver btdriver; +extern struct isa_driver advdriver; +extern struct isa_driver ahadriver; +extern struct isa_driver wdcdriver; +extern struct isa_driver msedriver; +extern struct isa_driver ardriver; +extern struct isa_driver csdriver; +extern struct isa_driver cxdriver; +extern struct isa_driver eddriver; +extern struct isa_driver eldriver; +extern struct isa_driver epdriver; +extern struct isa_driver exdriver; +extern struct isa_driver fedriver; +extern struct isa_driver iedriver; +extern struct isa_driver ledriver; +extern struct isa_driver lncdriver; +extern struct isa_driver rdpdriver; +extern struct isa_driver srdriver; +extern struct isa_driver wldriver; +extern struct isa_driver zedriver; +extern struct isa_driver zpdriver; +extern struct isa_driver oltrdriver; +extern struct isa_driver pasdriver; +extern struct isa_driver sbdriver; +extern struct isa_driver sbxvidriver; +extern struct isa_driver sbmididriver; +extern struct isa_driver awedriver; +extern struct isa_driver gusdriver; +extern struct isa_driver mssdriver; +extern struct isa_driver cssdriver; +extern struct isa_driver sscapedriver; +extern struct isa_driver trixdriver; +extern struct isa_driver sscape_mssdriver; +extern struct isa_driver opldriver; +extern struct isa_driver mpudriver; +extern struct isa_driver uartdriver; +extern struct isa_driver pcadriver; +extern struct isa_driver mcddriver; +extern struct isa_driver scddriver; +extern struct isa_driver matcddriver; +extern struct isa_driver wtdriver; +extern struct isa_driver ctxdriver; +extern struct isa_driver spigotdriver; +extern struct isa_driver gpdriver; +extern struct isa_driver gscdriver; +extern struct isa_driver joydriver; +extern struct isa_driver cydriver; +extern struct isa_driver dgbdriver; +extern struct isa_driver dgmdriver; +extern struct isa_driver labpcdriver; +extern struct isa_driver rcdriver; +extern struct isa_driver rpdriver; +extern struct isa_driver twdriver; +extern struct isa_driver sidriver; +extern struct isa_driver ascdriver; +extern struct isa_driver stldriver; +extern struct isa_driver stlidriver; +extern struct isa_driver lorandriver; +extern struct isa_driver pcfdriver; +extern struct isa_driver isicdriver; +extern struct isa_driver tinadriver; +extern struct isa_driver ppcdriver; + + +static struct old_isa_driver old_drivers[] = { + +/* Sensitive TTY */ + +/* Sensitive BIO */ + +/* Sensitive NET */ +#if NED > 0 + { DRIVER_TYPE_NET, &eddriver }, +#endif +#if NFE > 0 + { DRIVER_TYPE_NET, &fedriver }, +#endif +#if NRDP > 0 + { DRIVER_TYPE_NET, &rdpdriver }, +#endif + +/* Sensitive CAM */ + +/* TTY */ + +#if NVT > 0 + { DRIVER_TYPE_TTY, &vtdriver }, +#endif +#if NMSE > 0 + { DRIVER_TYPE_TTY, &msedriver }, +#endif +#if NPCA > 0 + { DRIVER_TYPE_TTY, &pcadriver }, +#endif +#if NGP > 0 + { DRIVER_TYPE_TTY, &gpdriver }, +#endif +#if NGSC > 0 + { DRIVER_TYPE_TTY, &gscdriver }, +#endif +#if NCY > 0 + { DRIVER_TYPE_TTY, &cydriver }, +#endif +#if NDGB > 0 + { DRIVER_TYPE_TTY, &dgbdriver }, +#endif +#if NDGM > 0 + { DRIVER_TYPE_TTY, &dgmdriver }, +#endif +#if NLABPC > 0 + { DRIVER_TYPE_TTY, &labpcdriver }, +#endif +#if NRCD > 0 + { DRIVER_TYPE_TTY, &rcdriver }, +#endif +#if NRP > 0 + { DRIVER_TYPE_TTY, &rpdriver }, +#endif +#if NTW > 0 + { DRIVER_TYPE_TTY, &twdriver }, +#endif +#if NSI > 0 + { DRIVER_TYPE_TTY, &sidriver }, +#endif +#if NASC > 0 + { DRIVER_TYPE_TTY, &ascdriver }, +#endif +#if NSTL > 0 + { DRIVER_TYPE_TTY, &stldriver }, +#endif +#if NSTLI > 0 + { DRIVER_TYPE_TTY, &stlidriver }, +#endif +#if NLORAN > 0 + { DRIVER_TYPE_TTY, &lorandriver }, +#endif +#if NPPC > 0 + { DRIVER_TYPE_TTY, &ppcdriver }, +#endif + +/* BIO */ + +#if NWDC > 0 + { DRIVER_TYPE_BIO, &wdcdriver }, +#endif +#if NMCD > 0 + { DRIVER_TYPE_BIO, &mcddriver }, +#endif +#if NSCD > 0 + { DRIVER_TYPE_BIO, &scddriver }, +#endif +#if NMATCD > 0 + { DRIVER_TYPE_BIO, &matcddriver }, +#endif +#if NWT > 0 + { DRIVER_TYPE_BIO, &wtdriver }, +#endif + +/* NET */ + +#if NIE > 0 + { DRIVER_TYPE_NET, &iedriver }, +#endif +#if NEP > 0 + { DRIVER_TYPE_NET, &epdriver }, +#endif +#if NEX > 0 + { DRIVER_TYPE_NET, &exdriver }, +#endif +#if NLE > 0 + { DRIVER_TYPE_NET, &ledriver }, +#endif +#if NLNC > 0 + { DRIVER_TYPE_NET, &lncdriver }, +#endif +#if NZE > 0 + { DRIVER_TYPE_NET, &zedriver }, +#endif +#if NZP > 0 + { DRIVER_TYPE_NET, &zpdriver }, +#endif +#if NCS > 0 + { DRIVER_TYPE_NET, &csdriver }, +#endif +#if NAR > 0 + { DRIVER_TYPE_NET, &ardriver }, +#endif +#if NCX > 0 + { DRIVER_TYPE_NET, &cxdriver }, +#endif +#if NEL > 0 + { DRIVER_TYPE_NET, &eldriver }, +#endif +#if NSR > 0 + { DRIVER_TYPE_NET, &srdriver }, +#endif +#if NWL > 0 + { DRIVER_TYPE_NET, &wldriver }, +#endif +#if NPCF > 0 + { DRIVER_TYPE_NET, &pcfdriver }, +#endif +#if NISIC > 0 + { DRIVER_TYPE_NET, &isicdriver }, +#endif +#if NTINA > 0 + { DRIVER_TYPE_NET, &tinadriver }, +#endif + +/* CAM */ + +#if NBT > 0 + { DRIVER_TYPE_CAM, &btdriver }, +#endif +#if NADV > 0 + { DRIVER_TYPE_CAM, &advdriver }, +#endif +#if NAHA > 0 + { DRIVER_TYPE_CAM, &ahadriver }, +#endif + +/* MISC */ + +#if NOLTR > 0 + { DRIVER_TYPE_MISC, &oltrdriver }, +#endif +#if NPAS > 0 + { DRIVER_TYPE_MISC, &pasdriver }, +#endif +#if NSB > 0 + { DRIVER_TYPE_MISC, &sbdriver }, +#endif +#if NSBVXI > 0 + { DRIVER_TYPE_MISC, &sbxvidriver }, +#endif +#if NSBMIDI > 0 + { DRIVER_TYPE_MISC, &sbmididriver }, +#endif +#if NAWE > 0 + { DRIVER_TYPE_MISC, &awedriver }, +#endif +#if NGUS > 0 + { DRIVER_TYPE_MISC, &gusdriver }, +#endif +#if NMSS > 0 + { DRIVER_TYPE_MISC, &mssdriver }, +#endif +#if NCSS > 0 + { DRIVER_TYPE_MISC, &cssdriver }, +#endif +#if NSSCAPE > 0 + { DRIVER_TYPE_MISC, &sscapedriver }, +#endif +#if NTRIX > 0 + { DRIVER_TYPE_MISC, &trixdriver }, +#endif +#if NSSCAPE > 0 + { DRIVER_TYPE_MISC, &sscape_mssdriver }, +#endif +#if NOPL > 0 + { DRIVER_TYPE_MISC, &opldriver }, +#endif +#if NMPU > 0 + { DRIVER_TYPE_MISC, &mpudriver }, +#endif +#if NUART > 0 + { DRIVER_TYPE_MISC, &uartdriver }, +#endif +#if NCTX > 0 + { DRIVER_TYPE_MISC, &ctxdriver }, +#endif +#if NSPIGOT > 0 + { DRIVER_TYPE_MISC, &spigotdriver }, +#endif +#if NJOY > 0 + { DRIVER_TYPE_MISC, &joydriver }, +#endif + +}; + +#define old_drivers_count (sizeof(old_drivers) / sizeof(old_drivers[0])) Property changes on: head/sys/i386/isa/isa_compat.h ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: head/sys/i386/isa/isa_device.h =================================================================== --- head/sys/i386/isa/isa_device.h (revision 45719) +++ head/sys/i386/isa/isa_device.h (revision 45720) @@ -1,135 +1,118 @@ /*- * 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: @(#)isa_device.h 7.1 (Berkeley) 5/9/91 - * $Id: isa_device.h,v 1.56 1998/10/22 05:58:39 bde Exp $ + * $Id: isa_device.h,v 1.57 1999/01/17 06:33:43 bde Exp $ */ #ifndef _I386_ISA_ISA_DEVICE_H_ #define _I386_ISA_ISA_DEVICE_H_ +#ifdef KERNEL +#include +#endif + /* * ISA Bus Autoconfiguration */ typedef void ointhand2_t __P((int unit)); /* * Per device structure. * * XXX Note: id_conflicts should either become an array of things we're * specifically allowed to conflict with or be subsumed into some * more powerful mechanism for detecting and dealing with multiple types * of non-fatal conflict. -jkh XXX */ struct isa_device { int id_id; /* device id */ struct isa_driver *id_driver; int id_iobase; /* base i/o address */ u_int id_irq; /* interrupt request */ int id_drq; /* DMA request */ caddr_t id_maddr; /* physical i/o memory address on bus (if any)*/ int id_msize; /* size of i/o memory */ union { inthand2_t *id_i; ointhand2_t *id_oi; } id_iu; /* interrupt interface routine */ #define id_intr id_iu.id_i #define id_ointr id_iu.id_oi int id_unit; /* unit number */ int id_flags; /* flags */ int id_scsiid; /* scsi id if needed */ int id_alive; /* device is present */ #define RI_FAST 1 /* fast interrupt handler */ u_int id_ri_flags; /* flags for register_intr() */ int id_reconfig; /* hot eject device support (such as PCMCIA) */ int id_enabled; /* is device enabled */ int id_conflicts; /* we're allowed to conflict with things */ struct isa_device *id_next; /* used in isa_devlist in userconfig() */ }; /* * Bits to specify the type and amount of conflict checking. */ #define CC_ATTACH (1 << 0) #define CC_DRQ (1 << 1) #define CC_IOADDR (1 << 2) #define CC_IRQ (1 << 3) #define CC_MEMADDR (1 << 4) #define CC_QUIET (1 << 5) /* * Per-driver structure. * * Each device driver defines entries for a set of routines * as well as an array of types which are acceptable to it. * These are used at boot time by the configuration program. */ struct isa_driver { int (*probe) __P((struct isa_device *idp)); /* test whether device is present */ int (*attach) __P((struct isa_device *idp)); /* setup driver for a device */ char *name; /* device name */ int sensitive_hw; /* true if other probes confuse us */ }; #ifdef KERNEL -extern struct isa_device isa_biotab_fdc[]; -extern struct isa_device isa_biotab_wdc[]; -extern struct isa_device isa_devtab_bio[]; -extern struct isa_device isa_devtab_net[]; -extern struct isa_device isa_devtab_cam[]; -extern struct isa_device isa_devtab_null[]; -extern struct isa_device isa_devtab_tty[]; - -struct isa_device * - find_display __P((void)); -struct isa_device * - find_isadev __P((struct isa_device *table, struct isa_driver *driverp, - int unit)); int haveseen_isadev __P((struct isa_device *dvp, u_int checkbits)); -void isa_configure __P((void)); -void isa_dmacascade __P((int chan)); -void isa_dmadone __P((int flags, caddr_t addr, int nbytes, int chan)); -void isa_dmainit __P((int chan, u_int bouncebufsize)); -void isa_dmastart __P((int flags, caddr_t addr, u_int nbytes, int chan)); -int isa_dma_acquire __P((int chan)); -void isa_dma_release __P((int chan)); -int isa_dmastatus __P((int chan)); -int isa_dmastop __P((int chan)); void reconfig_isadev __P((struct isa_device *isdp, u_int *mp)); +int isa_compat_nextid __P((void)); #endif /* KERNEL */ #endif /* !_I386_ISA_ISA_DEVICE_H_ */ Index: head/sys/i386/isa/isa_dma.c =================================================================== --- head/sys/i386/isa/isa_dma.c (nonexistent) +++ head/sys/i386/isa/isa_dma.c (revision 45720) @@ -0,0 +1,510 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * William Jolitz. + * + * 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: @(#)isa.c 7.2 (Berkeley) 5/13/91 + * $Id: isa.c,v 1.117 1998/11/29 15:42:40 phk Exp $ + */ + +/* + * code to manage AT bus + * + * 92/08/18 Frank P. MacLachlan (fpm@crash.cts.com): + * Fixed uninitialized variable problem and added code to deal + * with DMA page boundaries in isa_dmarangecheck(). Fixed word + * mode DMA count compution and reorganized DMA setup code in + * isa_dmastart() + */ + +#include +#include +#include +#include +#include +#include +#ifdef APIC_IO +#include +#endif /* APIC_IO */ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "pnp.h" +#if NPNP > 0 +#include +#endif + +/* +** Register definitions for DMA controller 1 (channels 0..3): +*/ +#define DMA1_CHN(c) (IO_DMA1 + 1*(2*(c))) /* addr reg for channel c */ +#define DMA1_SMSK (IO_DMA1 + 1*10) /* single mask register */ +#define DMA1_MODE (IO_DMA1 + 1*11) /* mode register */ +#define DMA1_FFC (IO_DMA1 + 1*12) /* clear first/last FF */ + +/* +** Register definitions for DMA controller 2 (channels 4..7): +*/ +#define DMA2_CHN(c) (IO_DMA2 + 2*(2*(c))) /* addr reg for channel c */ +#define DMA2_SMSK (IO_DMA2 + 2*10) /* single mask register */ +#define DMA2_MODE (IO_DMA2 + 2*11) /* mode register */ +#define DMA2_FFC (IO_DMA2 + 2*12) /* clear first/last FF */ + +static int isa_dmarangecheck __P((caddr_t va, u_int length, int chan)); + +static caddr_t dma_bouncebuf[8]; +static u_int dma_bouncebufsize[8]; +static u_int8_t dma_bounced = 0; +static u_int8_t dma_busy = 0; /* Used in isa_dmastart() */ +static u_int8_t dma_inuse = 0; /* User for acquire/release */ +static u_int8_t dma_auto_mode = 0; + +#define VALID_DMA_MASK (7) + +/* high byte of address is stored in this port for i-th dma channel */ +static int dmapageport[8] = { 0x87, 0x83, 0x81, 0x82, 0x8f, 0x8b, 0x89, 0x8a }; + +/* + * Setup a DMA channel's bounce buffer. + */ +void +isa_dmainit(chan, bouncebufsize) + int chan; + u_int bouncebufsize; +{ + void *buf; + +#ifdef DIAGNOSTIC + if (chan & ~VALID_DMA_MASK) + panic("isa_dmainit: channel out of range"); + + if (dma_bouncebuf[chan] != NULL) + panic("isa_dmainit: impossible request"); +#endif + + dma_bouncebufsize[chan] = bouncebufsize; + + /* Try malloc() first. It works better if it works. */ + buf = malloc(bouncebufsize, M_DEVBUF, M_NOWAIT); + if (buf != NULL) { + if (isa_dmarangecheck(buf, bouncebufsize, chan) == 0) { + dma_bouncebuf[chan] = buf; + return; + } + free(buf, M_DEVBUF); + } + buf = contigmalloc(bouncebufsize, M_DEVBUF, M_NOWAIT, 0ul, 0xfffffful, + 1ul, chan & 4 ? 0x20000ul : 0x10000ul); + if (buf == NULL) + printf("isa_dmainit(%d, %d) failed\n", chan, bouncebufsize); + else + dma_bouncebuf[chan] = buf; +} + +/* + * Register a DMA channel's usage. Usually called from a device driver + * in open() or during its initialization. + */ +int +isa_dma_acquire(chan) + int chan; +{ +#ifdef DIAGNOSTIC + if (chan & ~VALID_DMA_MASK) + panic("isa_dma_acquire: channel out of range"); +#endif + + if (dma_inuse & (1 << chan)) { + printf("isa_dma_acquire: channel %d already in use\n", chan); + return (EBUSY); + } + dma_inuse |= (1 << chan); + dma_auto_mode &= ~(1 << chan); + + return (0); +} + +/* + * Unregister a DMA channel's usage. Usually called from a device driver + * during close() or during its shutdown. + */ +void +isa_dma_release(chan) + int chan; +{ +#ifdef DIAGNOSTIC + if (chan & ~VALID_DMA_MASK) + panic("isa_dma_release: channel out of range"); + + if ((dma_inuse & (1 << chan)) == 0) + printf("isa_dma_release: channel %d not in use\n", chan); +#endif + + if (dma_busy & (1 << chan)) { + dma_busy &= ~(1 << chan); + /* + * XXX We should also do "dma_bounced &= (1 << chan);" + * because we are acting on behalf of isa_dmadone() which + * was not called to end the last DMA operation. This does + * not matter now, but it may in the future. + */ + } + + dma_inuse &= ~(1 << chan); + dma_auto_mode &= ~(1 << chan); +} + +/* + * isa_dmacascade(): program 8237 DMA controller channel to accept + * external dma control by a board. + */ +void +isa_dmacascade(chan) + int chan; +{ +#ifdef DIAGNOSTIC + if (chan & ~VALID_DMA_MASK) + panic("isa_dmacascade: channel out of range"); +#endif + + /* set dma channel mode, and set dma channel mode */ + if ((chan & 4) == 0) { + outb(DMA1_MODE, DMA37MD_CASCADE | chan); + outb(DMA1_SMSK, chan); + } else { + outb(DMA2_MODE, DMA37MD_CASCADE | (chan & 3)); + outb(DMA2_SMSK, chan & 3); + } +} + +/* + * isa_dmastart(): program 8237 DMA controller channel, avoid page alignment + * problems by using a bounce buffer. + */ +void +isa_dmastart(int flags, caddr_t addr, u_int nbytes, int chan) +{ + vm_offset_t phys; + int waport; + caddr_t newaddr; + +#ifdef DIAGNOSTIC + if (chan & ~VALID_DMA_MASK) + panic("isa_dmastart: channel out of range"); + + if ((chan < 4 && nbytes > (1<<16)) + || (chan >= 4 && (nbytes > (1<<17) || (u_int)addr & 1))) + panic("isa_dmastart: impossible request"); + + if ((dma_inuse & (1 << chan)) == 0) + printf("isa_dmastart: channel %d not acquired\n", chan); +#endif + +#if 0 + /* + * XXX This should be checked, but drivers like ad1848 only call + * isa_dmastart() once because they use Auto DMA mode. If we + * leave this in, drivers that do this will print this continuously. + */ + if (dma_busy & (1 << chan)) + printf("isa_dmastart: channel %d busy\n", chan); +#endif + + dma_busy |= (1 << chan); + + if (isa_dmarangecheck(addr, nbytes, chan)) { + if (dma_bouncebuf[chan] == NULL + || dma_bouncebufsize[chan] < nbytes) + panic("isa_dmastart: bad bounce buffer"); + dma_bounced |= (1 << chan); + newaddr = dma_bouncebuf[chan]; + + /* copy bounce buffer on write */ + if (!(flags & B_READ)) + bcopy(addr, newaddr, nbytes); + addr = newaddr; + } + + /* translate to physical */ + phys = pmap_extract(pmap_kernel(), (vm_offset_t)addr); + + if (flags & B_RAW) { + dma_auto_mode |= (1 << chan); + } else { + dma_auto_mode &= ~(1 << chan); + } + + if ((chan & 4) == 0) { + /* + * Program one of DMA channels 0..3. These are + * byte mode channels. + */ + /* set dma channel mode, and reset address ff */ + + /* If B_RAW flag is set, then use autoinitialise mode */ + if (flags & B_RAW) { + if (flags & B_READ) + outb(DMA1_MODE, DMA37MD_AUTO|DMA37MD_WRITE|chan); + else + outb(DMA1_MODE, DMA37MD_AUTO|DMA37MD_READ|chan); + } + else + if (flags & B_READ) + outb(DMA1_MODE, DMA37MD_SINGLE|DMA37MD_WRITE|chan); + else + outb(DMA1_MODE, DMA37MD_SINGLE|DMA37MD_READ|chan); + outb(DMA1_FFC, 0); + + /* send start address */ + waport = DMA1_CHN(chan); + outb(waport, phys); + outb(waport, phys>>8); + outb(dmapageport[chan], phys>>16); + + /* send count */ + outb(waport + 1, --nbytes); + outb(waport + 1, nbytes>>8); + + /* unmask channel */ + outb(DMA1_SMSK, chan); + } else { + /* + * Program one of DMA channels 4..7. These are + * word mode channels. + */ + /* set dma channel mode, and reset address ff */ + + /* If B_RAW flag is set, then use autoinitialise mode */ + if (flags & B_RAW) { + if (flags & B_READ) + outb(DMA2_MODE, DMA37MD_AUTO|DMA37MD_WRITE|(chan&3)); + else + outb(DMA2_MODE, DMA37MD_AUTO|DMA37MD_READ|(chan&3)); + } + else + if (flags & B_READ) + outb(DMA2_MODE, DMA37MD_SINGLE|DMA37MD_WRITE|(chan&3)); + else + outb(DMA2_MODE, DMA37MD_SINGLE|DMA37MD_READ|(chan&3)); + outb(DMA2_FFC, 0); + + /* send start address */ + waport = DMA2_CHN(chan - 4); + outb(waport, phys>>1); + outb(waport, phys>>9); + outb(dmapageport[chan], phys>>16); + + /* send count */ + nbytes >>= 1; + outb(waport + 2, --nbytes); + outb(waport + 2, nbytes>>8); + + /* unmask channel */ + outb(DMA2_SMSK, chan & 3); + } +} + +void +isa_dmadone(int flags, caddr_t addr, int nbytes, int chan) +{ +#ifdef DIAGNOSTIC + if (chan & ~VALID_DMA_MASK) + panic("isa_dmadone: channel out of range"); + + if ((dma_inuse & (1 << chan)) == 0) + printf("isa_dmadone: channel %d not acquired\n", chan); +#endif + + if (((dma_busy & (1 << chan)) == 0) && + (dma_auto_mode & (1 << chan)) == 0 ) + printf("isa_dmadone: channel %d not busy\n", chan); + + if ((dma_auto_mode & (1 << chan)) == 0) + outb(chan & 4 ? DMA2_SMSK : DMA1_SMSK, (chan & 3) | 4); + + if (dma_bounced & (1 << chan)) { + /* copy bounce buffer on read */ + if (flags & B_READ) + bcopy(dma_bouncebuf[chan], addr, nbytes); + + dma_bounced &= ~(1 << chan); + } + dma_busy &= ~(1 << chan); +} + +/* + * Check for problems with the address range of a DMA transfer + * (non-contiguous physical pages, outside of bus address space, + * crossing DMA page boundaries). + * Return true if special handling needed. + */ + +static int +isa_dmarangecheck(caddr_t va, u_int length, int chan) +{ + vm_offset_t phys, priorpage = 0, endva; + u_int dma_pgmsk = (chan & 4) ? ~(128*1024-1) : ~(64*1024-1); + + endva = (vm_offset_t)round_page((vm_offset_t)va + length); + for (; va < (caddr_t) endva ; va += PAGE_SIZE) { + phys = trunc_page(pmap_extract(pmap_kernel(), (vm_offset_t)va)); +#define ISARAM_END RAM_END + if (phys == 0) + panic("isa_dmacheck: no physical page present"); + if (phys >= ISARAM_END) + return (1); + if (priorpage) { + if (priorpage + PAGE_SIZE != phys) + return (1); + /* check if crossing a DMA page boundary */ + if (((u_int)priorpage ^ (u_int)phys) & dma_pgmsk) + return (1); + } + priorpage = phys; + } + return (0); +} + +/* + * Query the progress of a transfer on a DMA channel. + * + * To avoid having to interrupt a transfer in progress, we sample + * each of the high and low databytes twice, and apply the following + * logic to determine the correct count. + * + * Reads are performed with interrupts disabled, thus it is to be + * expected that the time between reads is very small. At most + * one rollover in the low count byte can be expected within the + * four reads that are performed. + * + * There are three gaps in which a rollover can occur : + * + * - read low1 + * gap1 + * - read high1 + * gap2 + * - read low2 + * gap3 + * - read high2 + * + * If a rollover occurs in gap1 or gap2, the low2 value will be + * greater than the low1 value. In this case, low2 and high2 are a + * corresponding pair. + * + * In any other case, low1 and high1 can be considered to be correct. + * + * The function returns the number of bytes remaining in the transfer, + * or -1 if the channel requested is not active. + * + */ +int +isa_dmastatus(int chan) +{ + u_long cnt = 0; + int ffport, waport; + u_long low1, high1, low2, high2; + + /* channel active? */ + if ((dma_inuse & (1 << chan)) == 0) { + printf("isa_dmastatus: channel %d not active\n", chan); + return(-1); + } + /* channel busy? */ + + if (((dma_busy & (1 << chan)) == 0) && + (dma_auto_mode & (1 << chan)) == 0 ) { + printf("chan %d not busy\n", chan); + return -2 ; + } + if (chan < 4) { /* low DMA controller */ + ffport = DMA1_FFC; + waport = DMA1_CHN(chan) + 1; + } else { /* high DMA controller */ + ffport = DMA2_FFC; + waport = DMA2_CHN(chan - 4) + 2; + } + + disable_intr(); /* no interrupts Mr Jones! */ + outb(ffport, 0); /* clear register LSB flipflop */ + low1 = inb(waport); + high1 = inb(waport); + outb(ffport, 0); /* clear again */ + low2 = inb(waport); + high2 = inb(waport); + enable_intr(); /* enable interrupts again */ + + /* + * Now decide if a wrap has tried to skew our results. + * Note that after TC, the count will read 0xffff, while we want + * to return zero, so we add and then mask to compensate. + */ + if (low1 >= low2) { + cnt = (low1 + (high1 << 8) + 1) & 0xffff; + } else { + cnt = (low2 + (high2 << 8) + 1) & 0xffff; + } + + if (chan >= 4) /* high channels move words */ + cnt *= 2; + return(cnt); +} + +/* + * Stop a DMA transfer currently in progress. + */ +int +isa_dmastop(int chan) +{ + if ((dma_inuse & (1 << chan)) == 0) + printf("isa_dmastop: channel %d not acquired\n", chan); + + if (((dma_busy & (1 << chan)) == 0) && + ((dma_auto_mode & (1 << chan)) == 0)) { + printf("chan %d not busy\n", chan); + return -2 ; + } + + if ((chan & 4) == 0) { + outb(DMA1_SMSK, (chan & 3) | 4 /* disable mask */); + } else { + outb(DMA2_SMSK, (chan & 3) | 4 /* disable mask */); + } + return(isa_dmastatus(chan)); +} Property changes on: head/sys/i386/isa/isa_dma.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: head/sys/i386/isa/isa_dma.h =================================================================== --- head/sys/i386/isa/isa_dma.h (nonexistent) +++ head/sys/i386/isa/isa_dma.h (revision 45720) @@ -0,0 +1,51 @@ +/*- + * 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: @(#)isa_device.h 7.1 (Berkeley) 5/9/91 + * $Id: isa_device.h,v 1.57 1999/01/17 06:33:43 bde Exp $ + */ + +#ifndef _I386_ISA_ISA_DMA_H_ +#define _I386_ISA_ISA_DMA_H_ + +#ifdef KERNEL +void isa_dmacascade __P((int chan)); +void isa_dmadone __P((int flags, caddr_t addr, int nbytes, int chan)); +void isa_dmainit __P((int chan, u_int bouncebufsize)); +void isa_dmastart __P((int flags, caddr_t addr, u_int nbytes, int chan)); +int isa_dma_acquire __P((int chan)); +void isa_dma_release __P((int chan)); +int isa_dmastatus __P((int chan)); +int isa_dmastop __P((int chan)); +#endif /* KERNEL */ + +#endif /* !_I386_ISA_ISA_DMA_H_ */ Property changes on: head/sys/i386/isa/isa_dma.h ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: head/sys/i386/isa/nmi.c =================================================================== --- head/sys/i386/isa/nmi.c (revision 45719) +++ head/sys/i386/isa/nmi.c (revision 45720) @@ -1,516 +1,518 @@ /*- * Copyright (c) 1991 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * William Jolitz. * * 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: @(#)isa.c 7.2 (Berkeley) 5/13/91 - * $Id: intr_machdep.c,v 1.16 1999/01/08 19:17:48 bde Exp $ + * $Id: intr_machdep.c,v 1.17 1999/04/14 14:26:36 bde Exp $ */ #include "opt_auto_eoi.h" #include #ifndef SMP #include #endif #include #include #include #include #include #if defined(APIC_IO) #include #include /** FAST_HI */ #endif /* APIC_IO */ #include #ifdef PC98 #include #include #include #else #include #endif #include #include #include #ifdef APIC_IO #include #endif /* XXX should be in suitable include files */ #ifdef PC98 #define ICU_IMR_OFFSET 2 /* IO_ICU{1,2} + 2 */ #define ICU_SLAVEID 7 #else #define ICU_IMR_OFFSET 1 /* IO_ICU{1,2} + 1 */ #define ICU_SLAVEID 2 #endif #ifdef APIC_IO /* * This is to accommodate "mixed-mode" programming for * motherboards that don't connect the 8254 to the IO APIC. */ #define AUTO_EOI_1 1 #endif #define NR_INTRNAMES (1 + ICU_LEN + 2 * ICU_LEN) u_long *intr_countp[ICU_LEN]; inthand2_t *intr_handler[ICU_LEN]; u_int intr_mask[ICU_LEN]; static u_int* intr_mptr[ICU_LEN]; void *intr_unit[ICU_LEN]; static inthand_t *fastintr[ICU_LEN] = { &IDTVEC(fastintr0), &IDTVEC(fastintr1), &IDTVEC(fastintr2), &IDTVEC(fastintr3), &IDTVEC(fastintr4), &IDTVEC(fastintr5), &IDTVEC(fastintr6), &IDTVEC(fastintr7), &IDTVEC(fastintr8), &IDTVEC(fastintr9), &IDTVEC(fastintr10), &IDTVEC(fastintr11), &IDTVEC(fastintr12), &IDTVEC(fastintr13), &IDTVEC(fastintr14), &IDTVEC(fastintr15) #if defined(APIC_IO) , &IDTVEC(fastintr16), &IDTVEC(fastintr17), &IDTVEC(fastintr18), &IDTVEC(fastintr19), &IDTVEC(fastintr20), &IDTVEC(fastintr21), &IDTVEC(fastintr22), &IDTVEC(fastintr23) #endif /* APIC_IO */ }; static inthand_t *slowintr[ICU_LEN] = { &IDTVEC(intr0), &IDTVEC(intr1), &IDTVEC(intr2), &IDTVEC(intr3), &IDTVEC(intr4), &IDTVEC(intr5), &IDTVEC(intr6), &IDTVEC(intr7), &IDTVEC(intr8), &IDTVEC(intr9), &IDTVEC(intr10), &IDTVEC(intr11), &IDTVEC(intr12), &IDTVEC(intr13), &IDTVEC(intr14), &IDTVEC(intr15) #if defined(APIC_IO) , &IDTVEC(intr16), &IDTVEC(intr17), &IDTVEC(intr18), &IDTVEC(intr19), &IDTVEC(intr20), &IDTVEC(intr21), &IDTVEC(intr22), &IDTVEC(intr23) #endif /* APIC_IO */ }; static inthand2_t isa_strayintr; #ifdef PC98 #define NMI_PARITY 0x04 #define NMI_EPARITY 0x02 #else #define NMI_PARITY (1 << 7) #define NMI_IOCHAN (1 << 6) #define ENMI_WATCHDOG (1 << 7) #define ENMI_BUSTIMER (1 << 6) #define ENMI_IOSTATUS (1 << 5) #endif /* * Handle a NMI, possibly a machine check. * return true to panic system, false to ignore. */ int isa_nmi(cd) int cd; { #ifdef PC98 int port = inb(0x33); if (epson_machine_id == 0x20) epson_outb(0xc16, epson_inb(0xc16) | 0x1); if (port & NMI_PARITY) { panic("BASE RAM parity error, likely hardware failure."); } else if (port & NMI_EPARITY) { panic("EXTENDED RAM parity error, likely hardware failure."); } else { printf("\nNMI Resume ??\n"); return(0); } #else /* IBM-PC */ int isa_port = inb(0x61); int eisa_port = inb(0x461); if (isa_port & NMI_PARITY) panic("RAM parity error, likely hardware failure."); if (isa_port & NMI_IOCHAN) panic("I/O channel check, likely hardware failure."); /* * On a real EISA machine, this will never happen. However it can * happen on ISA machines which implement XT style floating point * error handling (very rare). Save them from a meaningless panic. */ if (eisa_port == 0xff) return(0); if (eisa_port & ENMI_WATCHDOG) panic("EISA watchdog timer expired, likely hardware failure."); if (eisa_port & ENMI_BUSTIMER) panic("EISA bus timeout, likely hardware failure."); if (eisa_port & ENMI_IOSTATUS) panic("EISA I/O port status error."); printf("\nNMI ISA %x, EISA %x\n", isa_port, eisa_port); return(0); #endif } /* * Fill in default interrupt table (in case of spuruious interrupt * during configuration of kernel, setup interrupt control unit */ void isa_defaultirq() { int i; /* icu vectors */ for (i = 0; i < ICU_LEN; i++) icu_unset(i, (inthand2_t *)NULL); /* initialize 8259's */ outb(IO_ICU1, 0x11); /* reset; program device, four bytes */ outb(IO_ICU1+ICU_IMR_OFFSET, NRSVIDT); /* starting at this vector index */ outb(IO_ICU1+ICU_IMR_OFFSET, IRQ_SLAVE); /* slave on line 7 */ #ifdef PC98 #ifdef AUTO_EOI_1 outb(IO_ICU1+ICU_IMR_OFFSET, 0x1f); /* (master) auto EOI, 8086 mode */ #else outb(IO_ICU1+ICU_IMR_OFFSET, 0x1d); /* (master) 8086 mode */ #endif #else /* IBM-PC */ #ifdef AUTO_EOI_1 outb(IO_ICU1+ICU_IMR_OFFSET, 2 | 1); /* auto EOI, 8086 mode */ #else outb(IO_ICU1+ICU_IMR_OFFSET, 1); /* 8086 mode */ #endif #endif /* PC98 */ outb(IO_ICU1+ICU_IMR_OFFSET, 0xff); /* leave interrupts masked */ outb(IO_ICU1, 0x0a); /* default to IRR on read */ #ifndef PC98 outb(IO_ICU1, 0xc0 | (3 - 1)); /* pri order 3-7, 0-2 (com2 first) */ #endif /* !PC98 */ outb(IO_ICU2, 0x11); /* reset; program device, four bytes */ outb(IO_ICU2+ICU_IMR_OFFSET, NRSVIDT+8); /* staring at this vector index */ outb(IO_ICU2+ICU_IMR_OFFSET, ICU_SLAVEID); /* my slave id is 7 */ #ifdef PC98 outb(IO_ICU2+ICU_IMR_OFFSET,9); /* 8086 mode */ #else /* IBM-PC */ #ifdef AUTO_EOI_2 outb(IO_ICU2+ICU_IMR_OFFSET, 2 | 1); /* auto EOI, 8086 mode */ #else outb(IO_ICU2+ICU_IMR_OFFSET,1); /* 8086 mode */ #endif #endif /* PC98 */ outb(IO_ICU2+ICU_IMR_OFFSET, 0xff); /* leave interrupts masked */ outb(IO_ICU2, 0x0a); /* default to IRR on read */ } /* * Caught a stray interrupt, notify */ static void isa_strayintr(vcookiep) void *vcookiep; { int intr = (void **)vcookiep - &intr_unit[0]; /* DON'T BOTHER FOR NOW! */ /* for some reason, we get bursts of intr #7, even if not enabled! */ /* * Well the reason you got bursts of intr #7 is because someone * raised an interrupt line and dropped it before the 8259 could * prioritize it. This is documented in the intel data book. This * means you have BAD hardware! I have changed this so that only * the first 5 get logged, then it quits logging them, and puts * out a special message. rgrimes 3/25/1993 */ /* * XXX TODO print a different message for #7 if it is for a * glitch. Glitches can be distinguished from real #7's by * testing that the in-service bit is _not_ set. The test * must be done before sending an EOI so it can't be done if * we are using AUTO_EOI_1. */ if (intrcnt[1 + intr] <= 5) log(LOG_ERR, "stray irq %d\n", intr); if (intrcnt[1 + intr] == 5) log(LOG_CRIT, "too many stray irq %d's; not logging any more\n", intr); } /* * Return a bitmap of the current interrupt requests. This is 8259-specific * and is only suitable for use at probe time. */ intrmask_t isa_irq_pending() { u_char irr1; u_char irr2; irr1 = inb(IO_ICU1); irr2 = inb(IO_ICU2); return ((irr2 << 8) | irr1); } int update_intr_masks(void) { int intr, n=0; u_int mask,*maskptr; for (intr=0; intr < ICU_LEN; intr ++) { #if defined(APIC_IO) /* no 8259 SLAVE to ignore */ #else if (intr==ICU_SLAVEID) continue; /* ignore 8259 SLAVE output */ #endif /* APIC_IO */ maskptr = intr_mptr[intr]; if (!maskptr) continue; *maskptr |= 1 << intr; mask = *maskptr; if (mask != intr_mask[intr]) { #if 0 printf ("intr_mask[%2d] old=%08x new=%08x ptr=%p.\n", intr, intr_mask[intr], mask, maskptr); #endif intr_mask[intr]=mask; n++; } } return (n); } static const char * isa_get_nameunit(int id) { static char buf[32]; struct isa_device *dp; if (id == -1) return ("pci"); /* XXX may also be eisa */ if (id == 0) return ("clk0"); /* XXX may also be sloppy driver */ if (id == 1) return ("rtc0"); +#if 0 for (dp = isa_devtab_bio; dp->id_driver != NULL; dp++) if (dp->id_id == id) goto found_device; for (dp = isa_devtab_cam; dp->id_driver != NULL; dp++) if (dp->id_id == id) goto found_device; for (dp = isa_devtab_net; dp->id_driver != NULL; dp++) if (dp->id_id == id) goto found_device; for (dp = isa_devtab_null; dp->id_driver != NULL; dp++) if (dp->id_id == id) goto found_device; for (dp = isa_devtab_tty; dp->id_driver != NULL; dp++) if (dp->id_id == id) goto found_device; +#endif return "???"; found_device: snprintf(buf, sizeof(buf), "%s%d", dp->id_driver->name, dp->id_unit); return (buf); } void update_intrname(int intr, int device_id) { char buf[32]; char *cp; const char *name; int name_index, off, strayintr; /* * Initialise strings for bitbucket and stray interrupt counters. * These have statically allocated indices 0 and 1 through ICU_LEN. */ if (intrnames[0] == '\0') { off = sprintf(intrnames, "???") + 1; for (strayintr = 0; strayintr < ICU_LEN; strayintr++) off += sprintf(intrnames + off, "stray irq%d", strayintr) + 1; } name = isa_get_nameunit(device_id); if (snprintf(buf, sizeof(buf), "%s irq%d", name, intr) >= sizeof(buf)) goto use_bitbucket; /* * Search for `buf' in `intrnames'. In the usual case when it is * not found, append it to the end if there is enough space (the \0 * terminator for the previous string, if any, becomes a separator). */ for (cp = intrnames, name_index = 0; cp != eintrnames && name_index < NR_INTRNAMES; cp += strlen(cp) + 1, name_index++) { if (*cp == '\0') { if (strlen(buf) >= eintrnames - cp) break; strcpy(cp, buf); goto found; } if (strcmp(cp, buf) == 0) goto found; } use_bitbucket: printf("update_intrname: counting %s irq%d as %s\n", name, intr, intrnames); name_index = 0; found: intr_countp[intr] = &intrcnt[name_index]; } int icu_setup(int intr, inthand2_t *handler, void *arg, u_int *maskptr, int flags) { #ifdef FAST_HI int select; /* the select register is 8 bits */ int vector; u_int32_t value; /* the window register is 32 bits */ #endif /* FAST_HI */ u_long ef; u_int mask = (maskptr ? *maskptr : 0); #if defined(APIC_IO) if ((u_int)intr >= ICU_LEN) /* no 8259 SLAVE to ignore */ #else if ((u_int)intr >= ICU_LEN || intr == ICU_SLAVEID) #endif /* APIC_IO */ if (intr_handler[intr] != isa_strayintr) return (EBUSY); ef = read_eflags(); disable_intr(); intr_handler[intr] = handler; intr_mptr[intr] = maskptr; intr_mask[intr] = mask | (1 << intr); intr_unit[intr] = arg; #ifdef FAST_HI if (flags & INTR_FAST) { vector = TPR_FAST_INTS + intr; setidt(vector, fastintr[intr], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); } else { vector = TPR_SLOW_INTS + intr; #ifdef APIC_INTR_REORDER #ifdef APIC_INTR_HIGHPRI_CLOCK /* XXX: Hack (kludge?) for more accurate clock. */ if (intr == apic_8254_intr || intr == 8) { vector = TPR_FAST_INTS + intr; } #endif #endif setidt(vector, slowintr[intr], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); } #ifdef APIC_INTR_REORDER set_lapic_isrloc(intr, vector); #endif /* * Reprogram the vector in the IO APIC. */ if (int_to_apicintpin[intr].ioapic >= 0) { select = int_to_apicintpin[intr].redirindex; value = io_apic_read(int_to_apicintpin[intr].ioapic, select) & ~IOART_INTVEC; io_apic_write(int_to_apicintpin[intr].ioapic, select, value | vector); } #else setidt(ICU_OFFSET + intr, flags & INTR_FAST ? fastintr[intr] : slowintr[intr], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); #endif /* FAST_HI */ INTREN(1 << intr); MPINTR_UNLOCK(); write_eflags(ef); return (0); } void register_imask(dvp, mask) struct isa_device *dvp; u_int mask; { if (dvp->id_alive && dvp->id_irq) { int intr; intr = ffs(dvp->id_irq) - 1; intr_mask[intr] = mask | (1 <= ICU_LEN || handler != intr_handler[intr]) return (EINVAL); INTRDIS(1 << intr); ef = read_eflags(); disable_intr(); intr_countp[intr] = &intrcnt[1 + intr]; intr_handler[intr] = isa_strayintr; intr_mptr[intr] = NULL; intr_mask[intr] = HWI_MASK | SWI_MASK; intr_unit[intr] = &intr_unit[intr]; #ifdef FAST_HI_XXX /* XXX how do I re-create dvp here? */ setidt(flags & INTR_FAST ? TPR_FAST_INTS + intr : TPR_SLOW_INTS + intr, slowintr[intr], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); #else /* FAST_HI */ #ifdef APIC_INTR_REORDER set_lapic_isrloc(intr, ICU_OFFSET + intr); #endif setidt(ICU_OFFSET + intr, slowintr[intr], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); #endif /* FAST_HI */ MPINTR_UNLOCK(); write_eflags(ef); return (0); } Index: head/sys/i386/isa/npx.c =================================================================== --- head/sys/i386/isa/npx.c (revision 45719) +++ head/sys/i386/isa/npx.c (revision 45720) @@ -1,689 +1,730 @@ /*- * 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 - * $Id: npx.c,v 1.65 1999/01/08 16:29:59 bde Exp $ + * $Id: npx.c,v 1.66 1999/03/28 23:28:18 dt Exp $ */ #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 -#include /* * 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) -/* XXX - should be in header file. */ -ointhand2_t npxintr; - #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 npxattach __P((struct isa_device *dvp)); -static int npxprobe __P((struct isa_device *dvp)); -static int npxprobe1 __P((struct isa_device *dvp)); +static int npx_attach __P((device_t dev)); + void npx_intr __P((void *)); +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 */ -struct isa_driver npxdriver = { - npxprobe, npxattach, "npx", -}; - 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; #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\ " __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\ " __XSTRING(CNAME(probetrap)) ": \n\ ss \n\ incl " __XSTRING(CNAME(npx_traps_while_probing)) " \n\ fnclex \n\ iret \n\ "); #endif /* SMP */ /* * 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 -npxprobe(dvp) - struct isa_device *dvp; +npx_probe(dev) + device_t dev; { -#ifdef SMP +/*#ifdef SMP*/ +#if 1 - return npxprobe1(dvp); + 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. */ - npx_intrno = NRSVIDT + ffs(dvp->id_irq) - 1; + npx_intrno = NRSVIDT + 13; 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 | dvp->id_irq)); - outb(IO_ICU2 + 1, ~(dvp->id_irq >> 8)); + outb(IO_ICU1 + 1, ~IRQ_SLAVE); + outb(IO_ICU2 + 1, ~(1 << (13 - 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 = npxprobe1(dvp); + 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 -npxprobe1(dvp) - struct isa_device *dvp; +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 - +/*#ifdef SMP*/ +#if 1 /* * Exception 16 MUST work for SMP. */ npx_irq13 = 0; npx_ex16 = hw_float = npx_exists = 1; - dvp->id_irq = 0; /* zap the interrupt */ - /* - * special return value to flag that we do not - * actually use any I/O registers - */ - return (-1); + device_set_desc(dev, "math processor"); + return (0); -#else /* SMP */ +#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; - dvp->id_irq = 0; /* zap the interrupt */ - /* - * special return value to flag that we do not - * actually use any I/O registers - */ - return (-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 |= dvp->id_irq; - return (IO_NPXSIZE); + npx0_imask |= (1 << 13); + + /* + * 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, 13, 13, + 1, RF_ACTIVE); + if (r == 0) + panic("npx: can't get IRQ"); + BUS_SETUP_INTR(device_get_parent(dev), + dev, r, 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. */ - dvp->id_irq = 0; - /* - * special return value to flag that we do not - * actually use any I/O registers - */ - return (-1); - + return (0); #endif /* SMP */ } /* * Attach routine - announce which it is, and wire into system */ int -npxattach(dvp) - struct isa_device *dvp; +npx_attach(dev) + device_t dev; { - dvp->id_ointr = npxintr; + int flags; - /* The caller has printed "irq 13" for the npx_irq13 case. */ - if (!npx_irq13) { - printf("npx%d: ", dvp->id_unit); + device_print_prettyname(dev); + if (npx_irq13) { + printf("using IRQ 13 interface\n"); + } else { if (npx_ex16) printf("INT 16 interface\n"); #if defined(MATH_EMULATE) || defined(GPL_MATH_EMULATE) else if (npx_exists) { printf("error reporting broken; using 387 emulator\n"); hw_float = npx_exists = 0; } else printf("387 emulator\n"); #else else printf("no 387 emulator in kernel!\n"); #endif } npxinit(__INITIAL_NPXCW__); #ifdef I586_CPU + if (resource_int_value("npx", 0, "flags", &flags) != 0) + flags = 0; + if (cpu_class == CPUCLASS_586 && npx_ex16 && timezero("i586_bzero()", i586_bzero) < timezero("bzero()", bzero) * 4 / 5) { - if (!(dvp->id_flags & NPX_DISABLE_I586_OPTIMIZED_BCOPY)) { + if (!(flags & NPX_DISABLE_I586_OPTIMIZED_BCOPY)) { bcopy_vector = i586_bcopy; ovbcopy_vector = i586_bcopy; } - if (!(dvp->id_flags & NPX_DISABLE_I586_OPTIMIZED_BZERO)) + if (!(flags & NPX_DISABLE_I586_OPTIMIZED_BZERO)) bzero = i586_bzero; - if (!(dvp->id_flags & NPX_DISABLE_I586_OPTIMIZED_COPYIO)) { + if (!(flags & NPX_DISABLE_I586_OPTIMIZED_COPYIO)) { copyin_vector = i586_copyin; copyout_vector = i586_copyout; } } #endif - return (1); /* XXX unused */ + 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 } /* * 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. * * XXX there is no standard way to tell SIGFPE handlers about the error * state. The old interface: * * void handler(int sig, int code, struct sigcontext *scp); * * is broken because it is non-ANSI and because the FP state is not in * struct sigcontext. * * 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 -npxintr(unit) - int unit; +npx_intr(dummy) + void *dummy; { int code; 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); fnclex(); /* * Pass exception to process. */ - frame = (struct intrframe *)&unit; /* XXX */ + 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 = (struct trapframe *)&frame->if_es; #ifdef notyet /* * Encode the appropriate code for detailed information on * this exception. */ code = XXX_ENCODE(curpcb->pcb_savefpu.sv_ex_sw); #else code = 0; /* XXX */ #endif 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_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, + DRIVER_TYPE_MISC, + 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 */ Index: head/sys/i386/isa/rp.c =================================================================== --- head/sys/i386/isa/rp.c (revision 45719) +++ head/sys/i386/isa/rp.c (revision 45720) @@ -1,2053 +1,2045 @@ /* * Copyright (c) Comtrol Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted prodived that the follwoing conditions * are met. * 1. Redistributions of source code must retain the above copyright * notive, this list of conditions and the following disclainer. * 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 prodided 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 Comtrol Corporation. * 4. The name of Comtrol Corporation may not be used to endorse or * promote products derived from this software without specific * prior written permission. * * THIS SOFTWARE IS PROVIDED BY COMTROL CORPORATION ``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 COMTROL CORPORATION 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, LIFE 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. */ /* * rp.c - for RocketPort FreeBSD */ #include "opt_compat.h" #include #include #include #include #include #include #include #include #include #include #define ROCKET_C #include #include #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif static Byte_t RData[RDATASIZE] = { 0x00, 0x09, 0xf6, 0x82, 0x02, 0x09, 0x86, 0xfb, 0x04, 0x09, 0x00, 0x0a, 0x06, 0x09, 0x01, 0x0a, 0x08, 0x09, 0x8a, 0x13, 0x0a, 0x09, 0xc5, 0x11, 0x0c, 0x09, 0x86, 0x85, 0x0e, 0x09, 0x20, 0x0a, 0x10, 0x09, 0x21, 0x0a, 0x12, 0x09, 0x41, 0xff, 0x14, 0x09, 0x82, 0x00, 0x16, 0x09, 0x82, 0x7b, 0x18, 0x09, 0x8a, 0x7d, 0x1a, 0x09, 0x88, 0x81, 0x1c, 0x09, 0x86, 0x7a, 0x1e, 0x09, 0x84, 0x81, 0x20, 0x09, 0x82, 0x7c, 0x22, 0x09, 0x0a, 0x0a }; static Byte_t RRegData[RREGDATASIZE]= { 0x00, 0x09, 0xf6, 0x82, /* 00: Stop Rx processor */ 0x08, 0x09, 0x8a, 0x13, /* 04: Tx software flow control */ 0x0a, 0x09, 0xc5, 0x11, /* 08: XON char */ 0x0c, 0x09, 0x86, 0x85, /* 0c: XANY */ 0x12, 0x09, 0x41, 0xff, /* 10: Rx mask char */ 0x14, 0x09, 0x82, 0x00, /* 14: Compare/Ignore #0 */ 0x16, 0x09, 0x82, 0x7b, /* 18: Compare #1 */ 0x18, 0x09, 0x8a, 0x7d, /* 1c: Compare #2 */ 0x1a, 0x09, 0x88, 0x81, /* 20: Interrupt #1 */ 0x1c, 0x09, 0x86, 0x7a, /* 24: Ignore/Replace #1 */ 0x1e, 0x09, 0x84, 0x81, /* 28: Interrupt #2 */ 0x20, 0x09, 0x82, 0x7c, /* 2c: Ignore/Replace #2 */ 0x22, 0x09, 0x0a, 0x0a /* 30: Rx FIFO Enable */ }; static CONTROLLER_T sController[CTL_SIZE] = { {-1,-1,0,0,0,0,0,0,0,0,0,{0,0,0,0},{0,0,0,0},{-1,-1,-1,-1},{0,0,0,0}}, {-1,-1,0,0,0,0,0,0,0,0,0,{0,0,0,0},{0,0,0,0},{-1,-1,-1,-1},{0,0,0,0}}, {-1,-1,0,0,0,0,0,0,0,0,0,{0,0,0,0},{0,0,0,0},{-1,-1,-1,-1},{0,0,0,0}}, {-1,-1,0,0,0,0,0,0,0,0,0,{0,0,0,0},{0,0,0,0},{-1,-1,-1,-1},{0,0,0,0}} }; #if 0 /* IRQ number to MUDBAC register 2 mapping */ Byte_t sIRQMap[16] = { 0,0,0,0x10,0x20,0x30,0,0,0,0x40,0x50,0x60,0x70,0,0,0x80 }; #endif static Byte_t sBitMapClrTbl[8] = { 0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f }; static Byte_t sBitMapSetTbl[8] = { 0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80 }; /*************************************************************************** Function: sInitController Purpose: Initialization of controller global registers and controller structure. Call: sInitController(CtlP,CtlNum,MudbacIO,AiopIOList,AiopIOListSize, IRQNum,Frequency,PeriodicOnly) CONTROLLER_T *CtlP; Ptr to controller structure int CtlNum; Controller number ByteIO_t MudbacIO; Mudbac base I/O address. ByteIO_t *AiopIOList; List of I/O addresses for each AIOP. This list must be in the order the AIOPs will be found on the controller. Once an AIOP in the list is not found, it is assumed that there are no more AIOPs on the controller. int AiopIOListSize; Number of addresses in AiopIOList int IRQNum; Interrupt Request number. Can be any of the following: 0: Disable global interrupts 3: IRQ 3 4: IRQ 4 5: IRQ 5 9: IRQ 9 10: IRQ 10 11: IRQ 11 12: IRQ 12 15: IRQ 15 Byte_t Frequency: A flag identifying the frequency of the periodic interrupt, can be any one of the following: FREQ_DIS - periodic interrupt disabled FREQ_137HZ - 137 Hertz FREQ_69HZ - 69 Hertz FREQ_34HZ - 34 Hertz FREQ_17HZ - 17 Hertz FREQ_9HZ - 9 Hertz FREQ_4HZ - 4 Hertz If IRQNum is set to 0 the Frequency parameter is overidden, it is forced to a value of FREQ_DIS. int PeriodicOnly: TRUE if all interrupts except the periodic interrupt are to be blocked. FALSE is both the periodic interrupt and other channel interrupts are allowed. If IRQNum is set to 0 the PeriodicOnly parameter is overidden, it is forced to a value of FALSE. Return: int: Number of AIOPs on the controller, or CTLID_NULL if controller initialization failed. Comments: If periodic interrupts are to be disabled but AIOP interrupts are allowed, set Frequency to FREQ_DIS and PeriodicOnly to FALSE. If interrupts are to be completely disabled set IRQNum to 0. Setting Frequency to FREQ_DIS and PeriodicOnly to TRUE is an invalid combination. This function performs initialization of global interrupt modes, but it does not actually enable global interrupts. To enable and disable global interrupts use functions sEnGlobalInt() and sDisGlobalInt(). Enabling of global interrupts is normally not done until all other initializations are complete. Even if interrupts are globally enabled, they must also be individually enabled for each channel that is to generate interrupts. Warnings: No range checking on any of the parameters is done. No context switches are allowed while executing this function. After this function all AIOPs on the controller are disabled, they can be enabled with sEnAiop(). */ int sInitController( CONTROLLER_T *CtlP, int CtlNum, ByteIO_t MudbacIO, ByteIO_t *AiopIOList, int AiopIOListSize, int IRQNum, Byte_t Frequency, int PeriodicOnly) { int i; ByteIO_t io; CtlP->CtlNum = CtlNum; CtlP->BusType = isISA; CtlP->CtlID = CTLID_0001; /* controller release 1 */ CtlP->MBaseIO = MudbacIO; CtlP->MReg1IO = MudbacIO + 1; CtlP->MReg2IO = MudbacIO + 2; CtlP->MReg3IO = MudbacIO + 3; #if 1 CtlP->MReg2 = 0; /* interrupt disable */ CtlP->MReg3 = 0; /* no periodic interrupts */ #else if(sIRQMap[IRQNum] == 0) /* interrupts globally disabled */ { CtlP->MReg2 = 0; /* interrupt disable */ CtlP->MReg3 = 0; /* no periodic interrupts */ } else { CtlP->MReg2 = sIRQMap[IRQNum]; /* set IRQ number */ CtlP->MReg3 = Frequency; /* set frequency */ if(PeriodicOnly) /* periodic interrupt only */ { CtlP->MReg3 |= PERIODIC_ONLY; } } #endif sOutB(CtlP->MReg2IO,CtlP->MReg2); sOutB(CtlP->MReg3IO,CtlP->MReg3); sControllerEOI(CtlP); /* clear EOI if warm init */ /* Init AIOPs */ CtlP->NumAiop = 0; for(i=0; i < AiopIOListSize; i++) { io = AiopIOList[i]; CtlP->AiopIO[i] = (WordIO_t)io; CtlP->AiopIntChanIO[i] = io + _INT_CHAN; sOutB(CtlP->MReg2IO,CtlP->MReg2 | (i & 0x03)); /* AIOP index */ sOutB(MudbacIO,(Byte_t)(io >> 6)); /* set up AIOP I/O in MUDBAC */ sEnAiop(CtlP,i); /* enable the AIOP */ CtlP->AiopID[i] = sReadAiopID(io); /* read AIOP ID */ if(CtlP->AiopID[i] == AIOPID_NULL) /* if AIOP does not exist */ { sDisAiop(CtlP,i); /* disable AIOP */ break; /* done looking for AIOPs */ } CtlP->AiopNumChan[i] = sReadAiopNumChan((WordIO_t)io); /* num channels in AIOP */ sOutW((WordIO_t)io + _INDX_ADDR,_CLK_PRE); /* clock prescaler */ sOutB(io + _INDX_DATA,CLOCK_PRESC); CtlP->NumAiop++; /* bump count of AIOPs */ sDisAiop(CtlP,i); /* disable AIOP */ } if(CtlP->NumAiop == 0) return(-1); else return(CtlP->NumAiop); } int sPCIInitController( CONTROLLER_T *CtlP, int CtlNum, ByteIO_t *AiopIOList, int AiopIOListSize, int IRQNum, Byte_t Frequency, int PeriodicOnly) { int i; ByteIO_t io; CtlP->CtlNum = CtlNum; CtlP->BusType = isPCI; CtlP->CtlID = CTLID_0001; /* controller release 1 */ CtlP->PCIIO = (WordIO_t)((ByteIO_t)AiopIOList[0] + _PCI_INT_FUNC); sPCIControllerEOI(CtlP); /* Init AIOPs */ CtlP->NumAiop = 0; for(i=0; i < AiopIOListSize; i++) { io = AiopIOList[i]; CtlP->AiopIO[i] = (WordIO_t)io; CtlP->AiopIntChanIO[i] = io + _INT_CHAN; CtlP->AiopID[i] = sReadAiopID(io); /* read AIOP ID */ if(CtlP->AiopID[i] == AIOPID_NULL) /* if AIOP does not exist */ { break; /* done looking for AIOPs */ } CtlP->AiopNumChan[i] = sReadAiopNumChan((WordIO_t)io); /* num channels in AIOP */ sOutW((WordIO_t)io + _INDX_ADDR,_CLK_PRE); /* clock prescaler */ sOutB(io + _INDX_DATA,CLOCK_PRESC); CtlP->NumAiop++; /* bump count of AIOPs */ } if(CtlP->NumAiop == 0) return(-1); else return(CtlP->NumAiop); } /*************************************************************************** Function: sReadAiopID Purpose: Read the AIOP idenfication number directly from an AIOP. Call: sReadAiopID(io) ByteIO_t io: AIOP base I/O address Return: int: Flag AIOPID_XXXX if a valid AIOP is found, where X is replace by an identifying number. Flag AIOPID_NULL if no valid AIOP is found Warnings: No context switches are allowed while executing this function. */ int sReadAiopID(ByteIO_t io) { Byte_t AiopID; /* ID byte from AIOP */ sOutB(io + _CMD_REG,RESET_ALL); /* reset AIOP */ sOutB(io + _CMD_REG,0x0); AiopID = sInB(io + _CHN_STAT0) & 0x07; if(AiopID == 0x06) return(1); else /* AIOP does not exist */ return(-1); } /*************************************************************************** Function: sReadAiopNumChan Purpose: Read the number of channels available in an AIOP directly from an AIOP. Call: sReadAiopNumChan(io) WordIO_t io: AIOP base I/O address Return: int: The number of channels available Comments: The number of channels is determined by write/reads from identical offsets within the SRAM address spaces for channels 0 and 4. If the channel 4 space is mirrored to channel 0 it is a 4 channel AIOP, otherwise it is an 8 channel. Warnings: No context switches are allowed while executing this function. */ int sReadAiopNumChan(WordIO_t io) { Word_t x; sOutDW((DWordIO_t)io + _INDX_ADDR,0x12340000L); /* write to chan 0 SRAM */ sOutW(io + _INDX_ADDR,0); /* read from SRAM, chan 0 */ x = sInW(io + _INDX_DATA); sOutW(io + _INDX_ADDR,0x4000); /* read from SRAM, chan 4 */ if(x != sInW(io + _INDX_DATA)) /* if different must be 8 chan */ return(8); else return(4); } /*************************************************************************** Function: sInitChan Purpose: Initialization of a channel and channel structure Call: sInitChan(CtlP,ChP,AiopNum,ChanNum) CONTROLLER_T *CtlP; Ptr to controller structure CHANNEL_T *ChP; Ptr to channel structure int AiopNum; AIOP number within controller int ChanNum; Channel number within AIOP Return: int: TRUE if initialization succeeded, FALSE if it fails because channel number exceeds number of channels available in AIOP. Comments: This function must be called before a channel can be used. Warnings: No range checking on any of the parameters is done. No context switches are allowed while executing this function. */ int sInitChan( CONTROLLER_T *CtlP, CHANNEL_T *ChP, int AiopNum, int ChanNum) { int i; WordIO_t AiopIO; WordIO_t ChIOOff; Byte_t *ChR; Word_t ChOff; static Byte_t R[4]; if(ChanNum >= CtlP->AiopNumChan[AiopNum]) return(FALSE); /* exceeds num chans in AIOP */ /* Channel, AIOP, and controller identifiers */ ChP->CtlP = CtlP; ChP->ChanID = CtlP->AiopID[AiopNum]; ChP->AiopNum = AiopNum; ChP->ChanNum = ChanNum; /* Global direct addresses */ AiopIO = CtlP->AiopIO[AiopNum]; ChP->Cmd = (ByteIO_t)AiopIO + _CMD_REG; ChP->IntChan = (ByteIO_t)AiopIO + _INT_CHAN; ChP->IntMask = (ByteIO_t)AiopIO + _INT_MASK; ChP->IndexAddr = (DWordIO_t)AiopIO + _INDX_ADDR; ChP->IndexData = AiopIO + _INDX_DATA; /* Channel direct addresses */ ChIOOff = AiopIO + ChP->ChanNum * 2; ChP->TxRxData = ChIOOff + _TD0; ChP->ChanStat = ChIOOff + _CHN_STAT0; ChP->TxRxCount = ChIOOff + _FIFO_CNT0; ChP->IntID = (ByteIO_t)AiopIO + ChP->ChanNum + _INT_ID0; /* Initialize the channel from the RData array */ for(i=0; i < RDATASIZE; i+=4) { R[0] = RData[i]; R[1] = RData[i+1] + 0x10 * ChanNum; R[2] = RData[i+2]; R[3] = RData[i+3]; sOutDW(ChP->IndexAddr,*((DWord_t *)&R[0])); } ChR = ChP->R; for(i=0; i < RREGDATASIZE; i+=4) { ChR[i] = RRegData[i]; ChR[i+1] = RRegData[i+1] + 0x10 * ChanNum; ChR[i+2] = RRegData[i+2]; ChR[i+3] = RRegData[i+3]; } /* Indexed registers */ ChOff = (Word_t)ChanNum * 0x1000; ChP->BaudDiv[0] = (Byte_t)(ChOff + _BAUD); ChP->BaudDiv[1] = (Byte_t)((ChOff + _BAUD) >> 8); ChP->BaudDiv[2] = (Byte_t)BRD9600; ChP->BaudDiv[3] = (Byte_t)(BRD9600 >> 8); sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->BaudDiv[0]); ChP->TxControl[0] = (Byte_t)(ChOff + _TX_CTRL); ChP->TxControl[1] = (Byte_t)((ChOff + _TX_CTRL) >> 8); ChP->TxControl[2] = 0; ChP->TxControl[3] = 0; sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->TxControl[0]); ChP->RxControl[0] = (Byte_t)(ChOff + _RX_CTRL); ChP->RxControl[1] = (Byte_t)((ChOff + _RX_CTRL) >> 8); ChP->RxControl[2] = 0; ChP->RxControl[3] = 0; sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->RxControl[0]); ChP->TxEnables[0] = (Byte_t)(ChOff + _TX_ENBLS); ChP->TxEnables[1] = (Byte_t)((ChOff + _TX_ENBLS) >> 8); ChP->TxEnables[2] = 0; ChP->TxEnables[3] = 0; sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->TxEnables[0]); ChP->TxCompare[0] = (Byte_t)(ChOff + _TXCMP1); ChP->TxCompare[1] = (Byte_t)((ChOff + _TXCMP1) >> 8); ChP->TxCompare[2] = 0; ChP->TxCompare[3] = 0; sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->TxCompare[0]); ChP->TxReplace1[0] = (Byte_t)(ChOff + _TXREP1B1); ChP->TxReplace1[1] = (Byte_t)((ChOff + _TXREP1B1) >> 8); ChP->TxReplace1[2] = 0; ChP->TxReplace1[3] = 0; sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->TxReplace1[0]); ChP->TxReplace2[0] = (Byte_t)(ChOff + _TXREP2); ChP->TxReplace2[1] = (Byte_t)((ChOff + _TXREP2) >> 8); ChP->TxReplace2[2] = 0; ChP->TxReplace2[3] = 0; sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->TxReplace2[0]); ChP->TxFIFOPtrs = ChOff + _TXF_OUTP; ChP->TxFIFO = ChOff + _TX_FIFO; sOutB(ChP->Cmd,(Byte_t)ChanNum | RESTXFCNT); /* apply reset Tx FIFO count */ sOutB(ChP->Cmd,(Byte_t)ChanNum); /* remove reset Tx FIFO count */ sOutW((WordIO_t)ChP->IndexAddr,ChP->TxFIFOPtrs); /* clear Tx in/out ptrs */ sOutW(ChP->IndexData,0); ChP->RxFIFOPtrs = ChOff + _RXF_OUTP; ChP->RxFIFO = ChOff + _RX_FIFO; sOutB(ChP->Cmd,(Byte_t)ChanNum | RESRXFCNT); /* apply reset Rx FIFO count */ sOutB(ChP->Cmd,(Byte_t)ChanNum); /* remove reset Rx FIFO count */ sOutW((WordIO_t)ChP->IndexAddr,ChP->RxFIFOPtrs); /* clear Rx out ptr */ sOutW(ChP->IndexData,0); sOutW((WordIO_t)ChP->IndexAddr,ChP->RxFIFOPtrs + 2); /* clear Rx in ptr */ sOutW(ChP->IndexData,0); ChP->TxPrioCnt = ChOff + _TXP_CNT; sOutW((WordIO_t)ChP->IndexAddr,ChP->TxPrioCnt); sOutB(ChP->IndexData,0); ChP->TxPrioPtr = ChOff + _TXP_PNTR; sOutW((WordIO_t)ChP->IndexAddr,ChP->TxPrioPtr); sOutB(ChP->IndexData,0); ChP->TxPrioBuf = ChOff + _TXP_BUF; sEnRxProcessor(ChP); /* start the Rx processor */ return(TRUE); } /*************************************************************************** Function: sStopRxProcessor Purpose: Stop the receive processor from processing a channel. Call: sStopRxProcessor(ChP) CHANNEL_T *ChP; Ptr to channel structure Comments: The receive processor can be started again with sStartRxProcessor(). This function causes the receive processor to skip over the stopped channel. It does not stop it from processing other channels. Warnings: No context switches are allowed while executing this function. Do not leave the receive processor stopped for more than one character time. After calling this function a delay of 4 uS is required to ensure that the receive processor is no longer processing this channel. */ void sStopRxProcessor(CHANNEL_T *ChP) { Byte_t R[4]; R[0] = ChP->R[0]; R[1] = ChP->R[1]; R[2] = 0x0a; R[3] = ChP->R[3]; sOutDW(ChP->IndexAddr,*(DWord_t *)&R[0]); } /*************************************************************************** Function: sFlushRxFIFO Purpose: Flush the Rx FIFO Call: sFlushRxFIFO(ChP) CHANNEL_T *ChP; Ptr to channel structure Return: void Comments: To prevent data from being enqueued or dequeued in the Tx FIFO while it is being flushed the receive processor is stopped and the transmitter is disabled. After these operations a 4 uS delay is done before clearing the pointers to allow the receive processor to stop. These items are handled inside this function. Warnings: No context switches are allowed while executing this function. */ void sFlushRxFIFO(CHANNEL_T *ChP) { int i; Byte_t Ch; /* channel number within AIOP */ int RxFIFOEnabled; /* TRUE if Rx FIFO enabled */ if(sGetRxCnt(ChP) == 0) /* Rx FIFO empty */ return; /* don't need to flush */ RxFIFOEnabled = FALSE; if(ChP->R[0x32] == 0x08) /* Rx FIFO is enabled */ { RxFIFOEnabled = TRUE; sDisRxFIFO(ChP); /* disable it */ for(i=0; i < 2000/200; i++) /* delay 2 uS to allow proc to disable FIFO*/ sInB(ChP->IntChan); /* depends on bus i/o timing */ } sGetChanStatus(ChP); /* clear any pending Rx errors in chan stat */ Ch = (Byte_t)sGetChanNum(ChP); sOutB(ChP->Cmd,Ch | RESRXFCNT); /* apply reset Rx FIFO count */ sOutB(ChP->Cmd,Ch); /* remove reset Rx FIFO count */ sOutW((WordIO_t)ChP->IndexAddr,ChP->RxFIFOPtrs); /* clear Rx out ptr */ sOutW(ChP->IndexData,0); sOutW((WordIO_t)ChP->IndexAddr,ChP->RxFIFOPtrs + 2); /* clear Rx in ptr */ sOutW(ChP->IndexData,0); if(RxFIFOEnabled) sEnRxFIFO(ChP); /* enable Rx FIFO */ } /*************************************************************************** Function: sFlushTxFIFO Purpose: Flush the Tx FIFO Call: sFlushTxFIFO(ChP) CHANNEL_T *ChP; Ptr to channel structure Return: void Comments: To prevent data from being enqueued or dequeued in the Tx FIFO while it is being flushed the receive processor is stopped and the transmitter is disabled. After these operations a 4 uS delay is done before clearing the pointers to allow the receive processor to stop. These items are handled inside this function. Warnings: No context switches are allowed while executing this function. */ void sFlushTxFIFO(CHANNEL_T *ChP) { int i; Byte_t Ch; /* channel number within AIOP */ int TxEnabled; /* TRUE if transmitter enabled */ if(sGetTxCnt(ChP) == 0) /* Tx FIFO empty */ return; /* don't need to flush */ TxEnabled = FALSE; if(ChP->TxControl[3] & TX_ENABLE) { TxEnabled = TRUE; sDisTransmit(ChP); /* disable transmitter */ } sStopRxProcessor(ChP); /* stop Rx processor */ for(i = 0; i < 4000/200; i++) /* delay 4 uS to allow proc to stop */ sInB(ChP->IntChan); /* depends on bus i/o timing */ Ch = (Byte_t)sGetChanNum(ChP); sOutB(ChP->Cmd,Ch | RESTXFCNT); /* apply reset Tx FIFO count */ sOutB(ChP->Cmd,Ch); /* remove reset Tx FIFO count */ sOutW((WordIO_t)ChP->IndexAddr,ChP->TxFIFOPtrs); /* clear Tx in/out ptrs */ sOutW(ChP->IndexData,0); if(TxEnabled) sEnTransmit(ChP); /* enable transmitter */ sStartRxProcessor(ChP); /* restart Rx processor */ } /*************************************************************************** Function: sWriteTxPrioByte Purpose: Write a byte of priority transmit data to a channel Call: sWriteTxPrioByte(ChP,Data) CHANNEL_T *ChP; Ptr to channel structure Byte_t Data; The transmit data byte Return: int: 1 if the bytes is successfully written, otherwise 0. Comments: The priority byte is transmitted before any data in the Tx FIFO. Warnings: No context switches are allowed while executing this function. */ int sWriteTxPrioByte(CHANNEL_T *ChP, Byte_t Data) { Byte_t DWBuf[4]; /* buffer for double word writes */ Word_t *WordPtr; /* must be far because Win SS != DS */ register DWordIO_t IndexAddr; if(sGetTxCnt(ChP) > 1) /* write it to Tx priority buffer */ { IndexAddr = ChP->IndexAddr; sOutW((WordIO_t)IndexAddr,ChP->TxPrioCnt); /* get priority buffer status */ if(sInB((ByteIO_t)ChP->IndexData) & PRI_PEND) /* priority buffer busy */ return(0); /* nothing sent */ WordPtr = (Word_t *)(&DWBuf[0]); *WordPtr = ChP->TxPrioBuf; /* data byte address */ DWBuf[2] = Data; /* data byte value */ sOutDW(IndexAddr,*((DWord_t *)(&DWBuf[0]))); /* write it out */ *WordPtr = ChP->TxPrioCnt; /* Tx priority count address */ DWBuf[2] = PRI_PEND + 1; /* indicate 1 byte pending */ DWBuf[3] = 0; /* priority buffer pointer */ sOutDW(IndexAddr,*((DWord_t *)(&DWBuf[0]))); /* write it out */ } else /* write it to Tx FIFO */ { sWriteTxByte(sGetTxRxDataIO(ChP),Data); } return(1); /* 1 byte sent */ } /*************************************************************************** Function: sEnInterrupts Purpose: Enable one or more interrupts for a channel Call: sEnInterrupts(ChP,Flags) CHANNEL_T *ChP; Ptr to channel structure Word_t Flags: Interrupt enable flags, can be any combination of the following flags: TXINT_EN: Interrupt on Tx FIFO empty RXINT_EN: Interrupt on Rx FIFO at trigger level (see sSetRxTrigger()) SRCINT_EN: Interrupt on SRC (Special Rx Condition) MCINT_EN: Interrupt on modem input change CHANINT_EN: Allow channel interrupt signal to the AIOP's Interrupt Channel Register. Return: void Comments: If an interrupt enable flag is set in Flags, that interrupt will be enabled. If an interrupt enable flag is not set in Flags, that interrupt will not be changed. Interrupts can be disabled with function sDisInterrupts(). This function sets the appropriate bit for the channel in the AIOP's Interrupt Mask Register if the CHANINT_EN flag is set. This allows this channel's bit to be set in the AIOP's Interrupt Channel Register. Interrupts must also be globally enabled before channel interrupts will be passed on to the host. This is done with function sEnGlobalInt(). In some cases it may be desirable to disable interrupts globally but enable channel interrupts. This would allow the global interrupt status register to be used to determine which AIOPs need service. */ void sEnInterrupts(CHANNEL_T *ChP,Word_t Flags) { Byte_t Mask; /* Interrupt Mask Register */ ChP->RxControl[2] |= ((Byte_t)Flags & (RXINT_EN | SRCINT_EN | MCINT_EN)); sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->RxControl[0]); ChP->TxControl[2] |= ((Byte_t)Flags & TXINT_EN); sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->TxControl[0]); if(Flags & CHANINT_EN) { Mask = sInB(ChP->IntMask) | sBitMapSetTbl[ChP->ChanNum]; sOutB(ChP->IntMask,Mask); } } /*************************************************************************** Function: sDisInterrupts Purpose: Disable one or more interrupts for a channel Call: sDisInterrupts(ChP,Flags) CHANNEL_T *ChP; Ptr to channel structure Word_t Flags: Interrupt flags, can be any combination of the following flags: TXINT_EN: Interrupt on Tx FIFO empty RXINT_EN: Interrupt on Rx FIFO at trigger level (see sSetRxTrigger()) SRCINT_EN: Interrupt on SRC (Special Rx Condition) MCINT_EN: Interrupt on modem input change CHANINT_EN: Disable channel interrupt signal to the AIOP's Interrupt Channel Register. Return: void Comments: If an interrupt flag is set in Flags, that interrupt will be disabled. If an interrupt flag is not set in Flags, that interrupt will not be changed. Interrupts can be enabled with function sEnInterrupts(). This function clears the appropriate bit for the channel in the AIOP's Interrupt Mask Register if the CHANINT_EN flag is set. This blocks this channel's bit from being set in the AIOP's Interrupt Channel Register. */ void sDisInterrupts(CHANNEL_T *ChP,Word_t Flags) { Byte_t Mask; /* Interrupt Mask Register */ ChP->RxControl[2] &= ~((Byte_t)Flags & (RXINT_EN | SRCINT_EN | MCINT_EN)); sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->RxControl[0]); ChP->TxControl[2] &= ~((Byte_t)Flags & TXINT_EN); sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->TxControl[0]); if(Flags & CHANINT_EN) { Mask = sInB(ChP->IntMask) & sBitMapClrTbl[ChP->ChanNum]; sOutB(ChP->IntMask,Mask); } } /********************************************************************* Begin FreeBsd-specific driver code **********************************************************************/ static int rpprobe __P((struct isa_device *)); static int rpattach __P((struct isa_device *)); static const char* rp_pciprobe(pcici_t tag, pcidi_t type); static void rp_pciattach(pcici_t tag, int unit); static u_long rp_pcicount; static struct pci_device rp_pcidevice = { "rp", rp_pciprobe, rp_pciattach, &rp_pcicount, NULL }; DATA_SET (pcidevice_set, rp_pcidevice); static timeout_t rpdtrwakeup; struct isa_driver rpdriver = { rpprobe, rpattach, "rp" }; static char driver_name[] = "rp"; static d_open_t rpopen; static d_close_t rpclose; static d_read_t rpread; static d_write_t rpwrite; static d_ioctl_t rpioctl; static d_stop_t rpstop; static d_devtotty_t rpdevtotty; #define CDEV_MAJOR 81 static struct cdevsw rp_cdevsw = { rpopen, rpclose, rpread, rpwrite, rpioctl, rpstop, noreset, rpdevtotty, ttpoll, nommap, NULL, driver_name, NULL, -1, nodump, nopsize, D_TTY, }; static int rp_controller_port = 0; static int rp_num_ports_open = 0; static int ndevs = 0; static int minor_to_unit[128]; #if 0 static struct tty rp_tty[128]; #endif static int rp_num_ports[4]; /* Number of ports on each controller */ #define _INLINE_ __inline #define POLL_INTERVAL 1 #define CALLOUT_MASK 0x80 #define CONTROL_MASK 0x60 #define CONTROL_INIT_STATE 0x20 #define CONTROL_LOCK_STATE 0x40 #define DEV_UNIT(dev) (MINOR_TO_UNIT(minor(dev)) #define MINOR_MAGIC_MASK (CALLOUT_MASK | CONTROL_MASK) #define MINOR_MAGIC(dev) ((minor(dev)) & ~MINOR_MAGIC_MASK) #define IS_CALLOUT(dev) (minor(dev) & CALLOUT_MASK) #define IS_CONTROL(dev) (minor(dev) & CONTROL_MASK) #define RP_ISMULTIPORT(dev) ((dev)->id_flags & 0x1) #define RP_MPMASTER(dev) (((dev)->id_flags >> 8) & 0xff) #define RP_NOTAST4(dev) ((dev)->id_flags & 0x04) static struct rp_port *p_rp_addr[4]; static struct rp_port *p_rp_table[MAX_RP_PORTS]; #define rp_addr(unit) (p_rp_addr[unit]) #define rp_table(port) (p_rp_table[port]) /* * The top-level routines begin here */ int rpselect __P((dev_t, int, struct proc *)); static int rpparam __P((struct tty *, struct termios *)); static void rpstart __P((struct tty *)); static void rphardclose __P((struct rp_port *)); #define rpmap nomap #define rpreset noreset #define rpstrategy nostrategy static void rp_disc_optim __P((struct tty *tp, struct termios *t, struct rp_port *rp)); static _INLINE_ void rp_do_receive(struct rp_port *rp, struct tty *tp, CHANNEL_t *cp, unsigned int ChanStatus) { int spl; unsigned int CharNStat; int ToRecv, ch; ToRecv = sGetRxCnt(cp); if(ToRecv == 0) return; /* If status indicates there are errored characters in the FIFO, then enter status mode (a word in FIFO holds characters and status) */ if(ChanStatus & (RXFOVERFL | RXBREAK | RXFRAME | RXPARITY)) { if(!(ChanStatus & STATMODE)) { ChanStatus |= STATMODE; sEnRxStatusMode(cp); } } /* if we previously entered status mode then read down the FIFO one word at a time, pulling apart the character and the status. Update error counters depending on status. */ if(ChanStatus & STATMODE) { while(ToRecv) { if(tp->t_state & TS_TBLOCK) { break; } CharNStat = sInW(sGetTxRxDataIO(cp)); ch = CharNStat & 0xff; if((CharNStat & STMBREAK) || (CharNStat & STMFRAMEH)) ch |= TTY_FE; else if (CharNStat & STMPARITYH) ch |= TTY_PE; else if (CharNStat & STMRCVROVRH) rp->rp_overflows++; (*linesw[tp->t_line].l_rint)(ch, tp); ToRecv--; } /* After emtying FIFO in status mode, turn off status mode */ if(sGetRxCnt(cp) == 0) sDisRxStatusMode(cp); } else { while (ToRecv) { if(tp->t_state & TS_TBLOCK) { break; } ch = (u_char) sInB(sGetTxRxDataIO(cp)); spl = spltty(); (*linesw[tp->t_line].l_rint)(ch, tp); splx(spl); ToRecv--; } } } static _INLINE_ void rp_handle_port(struct rp_port *rp) { CHANNEL_t *cp; struct tty *tp; unsigned int IntMask, ChanStatus; /* int oldcts; */ if(!rp) return; cp = &rp->rp_channel; tp = rp->rp_tty; IntMask = sGetChanIntID(cp); IntMask = IntMask & rp->rp_intmask; ChanStatus = sGetChanStatus(cp); if(IntMask & RXF_TRIG) if(!(tp->t_state & TS_TBLOCK) && (tp->t_state & TS_CARR_ON) && (tp->t_state & TS_ISOPEN)) { rp_do_receive(rp, tp, cp, ChanStatus); } if(IntMask & DELTA_CD) { if(ChanStatus & CD_ACT) { if(!(tp->t_state & TS_CARR_ON) ) { (void)(*linesw[tp->t_line].l_modem)(tp, 1); } } else { if((tp->t_state & TS_CARR_ON)) { (void)(*linesw[tp->t_line].l_modem)(tp, 0); if((*linesw[tp->t_line].l_modem)(tp, 0) == 0) { rphardclose(rp); } } } } /* oldcts = rp->rp_cts; rp->rp_cts = ((ChanStatus & CTS_ACT) != 0); if(oldcts != rp->rp_cts) { printf("CTS change (now %s)... on port %d\n", rp->rp_cts ? "on" : "off", rp->rp_port); } */ } static void rp_do_poll(void *not_used) { CONTROLLER_t *ctl; struct rp_port *rp; struct tty *tp; int unit, aiop, ch, line, count; unsigned char CtlMask, AiopMask; for(unit = 0; unit <= ndevs; unit++) { rp = rp_addr(unit); ctl = rp->rp_ctlp; if(ctl->BusType == isPCI) CtlMask = sPCIGetControllerIntStatus(ctl); else CtlMask = sGetControllerIntStatus(ctl); for(aiop=0; CtlMask; CtlMask >>=1, aiop++) { if(CtlMask & 1) { AiopMask = sGetAiopIntStatus(ctl, aiop); for(ch = 0; AiopMask; AiopMask >>=1, ch++) { if(AiopMask & 1) { line = (unit << 5) | (aiop << 3) | ch; rp = rp_table(line); rp_handle_port(rp); } } } } for(line = 0, rp = rp_addr(unit); line < rp_num_ports[unit]; line++, rp++) { tp = rp->rp_tty; if((tp->t_state & TS_BUSY) && (tp->t_state & TS_ISOPEN)) { count = sGetTxCnt(&rp->rp_channel); if(count == 0) tp->t_state &= ~(TS_BUSY); if(!(tp->t_state & TS_TTSTOP) && (count <= rp->rp_restart)) { (*linesw[tp->t_line].l_start)(tp); } } } } if(rp_num_ports_open) timeout(rp_do_poll, (void *)NULL, POLL_INTERVAL); } static const char* rp_pciprobe(pcici_t tag, pcidi_t type) { int vendor_id; vendor_id = type & 0xffff; switch(vendor_id) case 0x11fe: return("rp"); return(NULL); } static int rpprobe(dev) struct isa_device *dev; { int controller, unit; int aiop, num_aiops; unsigned int aiopio[MAX_AIOPS_PER_BOARD]; CONTROLLER_t *ctlp; unit = dev->id_unit; if (dev->id_unit >= 4) { printf("rpprobe: unit number %d invalid.\n", dev->id_unit); return 1; } printf("probing for RocketPort(ISA) unit %d\n", unit); if (rp_controller_port) controller = rp_controller_port; else { controller = dev->id_iobase + 0x40; } for (aiop=0; aiopid_iobase + (aiop * 0x400); ctlp = sCtlNumToCtlPtr(dev->id_unit); num_aiops = sInitController(ctlp, dev->id_unit, controller + ((unit-rp_pcicount)*0x400), aiopio, MAX_AIOPS_PER_BOARD, 0, FREQ_DIS, 0); if (num_aiops <= 0) { printf("board%d init failed\n", unit); return 0; } if (rp_controller_port) { dev->id_msize = 64; } else { dev->id_msize = 68; rp_controller_port = controller; } dev->id_irq = 0; return 1; } static void rp_pciattach(pcici_t tag, int unit) { dev_t rp_dev; int success, oldspl; u_short iobase; int num_ports, num_chan, num_aiops; int aiop, chan, port; int ChanStatus, line, i, count; unsigned int aiopio[MAX_AIOPS_PER_BOARD]; struct rp_port *rp; struct tty *tty; CONTROLLER_t *ctlp; success = pci_map_port(tag, 0x10, &iobase); if(!success) printf("ioaddr mapping failed for RocketPort(PCI)\n"); for(aiop=0; aiop < MAX_AIOPS_PER_BOARD; aiop++) aiopio[aiop] = iobase + (aiop * 0x40); ctlp = sCtlNumToCtlPtr(unit); num_aiops = sPCIInitController(ctlp, unit, aiopio, MAX_AIOPS_PER_BOARD, 0, FREQ_DIS, 0); num_ports = 0; for(aiop=0; aiop < num_aiops; aiop++) { sResetAiopByNum(ctlp, aiop); num_ports += sGetAiopNumChan(ctlp, aiop); } printf("RocketPort%d = %d ports\n", unit, num_ports); rp_num_ports[unit] = num_ports; rp = (struct rp_port *) malloc(sizeof(struct rp_port) * num_ports, M_TTYS, M_NOWAIT); if(rp == 0) { printf("rp_attach: Could not malloc rp_ports structures\n"); return; } count = unit * 32; /* board times max ports per card SG */ for(i=count;i < (count + rp_num_ports[unit]);i++) minor_to_unit[i] = unit; bzero(rp, sizeof(struct rp_port) * num_ports); tty = (struct tty *) malloc(sizeof(struct tty) * num_ports, M_TTYS, M_NOWAIT); if(tty == 0) { printf("rp_attach: Could not malloc tty structures\n"); return; } bzero(tty, sizeof(struct tty) * num_ports); oldspl = spltty(); rp_addr(unit) = rp; splx(oldspl); rp_dev = makedev(CDEV_MAJOR, unit); cdevsw_add(&rp_dev, &rp_cdevsw, NULL); port = 0; for(aiop=0; aiop < num_aiops; aiop++) { num_chan = sGetAiopNumChan(ctlp, aiop); for(chan=0; chan < num_chan; chan++, port++, rp++, tty++) { rp->rp_tty = tty; rp->rp_port = port; rp->rp_ctlp = ctlp; rp->rp_unit = unit; rp->rp_chan = chan; rp->rp_aiop = aiop; tty->t_line = 0; /* tty->t_termios = deftermios; */ rp->dtr_wait = 3 * hz; rp->it_in.c_iflag = 0; rp->it_in.c_oflag = 0; rp->it_in.c_cflag = TTYDEF_CFLAG; rp->it_in.c_lflag = 0; termioschars(&rp->it_in); /* termioschars(&tty->t_termios); */ rp->it_in.c_ispeed = rp->it_in.c_ospeed = TTYDEF_SPEED; rp->it_out = rp->it_in; rp->rp_intmask = RXF_TRIG | TXFIFO_MT | SRC_INT | DELTA_CD | DELTA_CTS | DELTA_DSR; ChanStatus = sGetChanStatus(&rp->rp_channel); if(sInitChan(ctlp, &rp->rp_channel, aiop, chan) == 0) { printf("RocketPort sInitChan(%d, %d, %d) failed \n", unit, aiop, chan); return; } ChanStatus = sGetChanStatus(&rp->rp_channel); rp->rp_cts = (ChanStatus & CTS_ACT) != 0; line = (unit << 5) | (aiop << 3) | chan; rp_table(line) = rp; /* devfs_add_devswf(&rp_cdevsw, port, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "ttyR%r", port); devfs_add_devswf(&rp_cdevsw, port | CONTROL_INIT_STATE, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "ttyRi%r", port); */ } } } static int rpattach(dev) struct isa_device *dev; { - struct isa_device *idev; dev_t rp_dev; int iobase, unit, /*rpmajor,*/ oldspl; int num_ports, num_chan, num_aiops; int aiop, chan, port; int ChanStatus, line, i, count; unsigned int aiopio[MAX_AIOPS_PER_BOARD]; struct rp_port *rp; struct tty *tty; CONTROLLER_t *ctlp; iobase = dev->id_iobase; unit = dev->id_unit; ndevs = unit; for(aiop=0; aiop < MAX_AIOPS_PER_BOARD; aiop++) aiopio[aiop] = iobase + (aiop * 0x400); ctlp = sCtlNumToCtlPtr(unit); num_aiops = sInitController(ctlp, unit, rp_controller_port + ((unit-rp_pcicount) * 0x400), aiopio, MAX_AIOPS_PER_BOARD, 0, FREQ_DIS, 0); num_ports = 0; for(aiop=0; aiop < num_aiops; aiop++) { sResetAiopByNum(ctlp, aiop); sEnAiop(ctlp, aiop); num_ports += sGetAiopNumChan(ctlp, aiop); } printf("RocketPort%d = %d ports\n", unit, num_ports); rp_num_ports[unit] = num_ports; rp = (struct rp_port *) malloc(sizeof(struct rp_port) * num_ports, M_TTYS, M_NOWAIT); if(rp == 0) { printf("rp_attach: Could not malloc rp_ports structures\n"); return(0); } count = unit * 32; /* board # times max ports per card SG */ for(i=count;i < (count + rp_num_ports[unit]);i++) minor_to_unit[i] = unit; bzero(rp, sizeof(struct rp_port) * num_ports); tty = (struct tty *) malloc(sizeof(struct tty) * num_ports, M_TTYS, M_NOWAIT); if(tty == 0) { printf("rp_attach: Could not malloc tty structures\n"); return(0); } bzero(tty, sizeof(struct tty) * num_ports); oldspl = spltty(); rp_addr(unit) = rp; splx(oldspl); rp_dev = makedev(CDEV_MAJOR, unit); cdevsw_add(&rp_dev, &rp_cdevsw, NULL); port = 0; for(aiop=0; aiop < num_aiops; aiop++) { num_chan = sGetAiopNumChan(ctlp, aiop); for(chan=0; chan < num_chan; chan++, port++, rp++, tty++) { rp->rp_tty = tty; rp->rp_port = port; rp->rp_ctlp = ctlp; rp->rp_unit = unit; rp->rp_chan = chan; rp->rp_aiop = aiop; tty->t_line = 0; /* tty->t_termios = deftermios; */ rp->dtr_wait = 3 * hz; rp->it_in.c_iflag = 0; rp->it_in.c_oflag = 0; rp->it_in.c_cflag = TTYDEF_CFLAG; rp->it_in.c_lflag = 0; termioschars(&rp->it_in); /* termioschars(&tty->t_termios); */ rp->it_in.c_ispeed = rp->it_in.c_ospeed = TTYDEF_SPEED; rp->it_out = rp->it_in; rp->rp_intmask = RXF_TRIG | TXFIFO_MT | SRC_INT | DELTA_CD | DELTA_CTS | DELTA_DSR; ChanStatus = sGetChanStatus(&rp->rp_channel); if(sInitChan(ctlp, &rp->rp_channel, aiop, chan) == 0) { printf("RocketPort sInitChan(%d, %d, %d) failed \n", unit, aiop, chan); return(0); } ChanStatus = sGetChanStatus(&rp->rp_channel); rp->rp_cts = (ChanStatus & CTS_ACT) != 0; line = (unit << 5) | (aiop << 3) | chan; rp_table(line) = rp; } } - idev = find_isadev(isa_devtab_tty, &rpdriver, - RP_MPMASTER(dev) + rp_pcicount); - if(idev == NULL) { - printf("rp%d: master device %d not configured\n", - dev->id_unit, RP_MPMASTER(dev)); - } -/* printf("COOL!! Device is found!!\n"); for(rpmajor=0;rpmajor> 16) -1) * 32); /* SG */ port = (minor(dev) & 0x1f); /* SG */ mynor = (port + umynor); /* SG */ unit = minor_to_unit[mynor]; if(IS_CONTROL(dev)) return(0); rp = rp_addr(unit) + port; /* rp->rp_tty = &rp_tty[rp->rp_port]; */ tp = rp->rp_tty; oldspl = spltty(); open_top: while(rp->state & ~SET_DTR) { error = tsleep(&rp->dtr_wait, TTIPRI | PCATCH, "rpdtr", 0); if(error != 0) goto out; } if(tp->t_state & TS_ISOPEN) { if(IS_CALLOUT(dev)) { if(!rp->active_out) { error = EBUSY; goto out; } } else { if(rp->active_out) { if(flag & O_NONBLOCK) { error = EBUSY; goto out; } error = tsleep(&rp->active_out, TTIPRI | PCATCH, "rpbi", 0); if(error != 0) goto out; goto open_top; } } if(tp->t_state & TS_XCLUDE && suser(p->p_ucred, &p->p_acflag)) { splx(oldspl); return(EBUSY); } } else { tp->t_dev = dev; tp->t_param = rpparam; tp->t_oproc = rpstart; tp->t_line = 0; tp->t_termios = IS_CALLOUT(dev) ? rp->it_out : rp->it_in; flags = 0; flags |= SET_RTS; flags |= SET_DTR; rp->rp_channel.TxControl[3] = ((rp->rp_channel.TxControl[3] & ~(SET_RTS | SET_DTR)) | flags); sOutDW(rp->rp_channel.IndexAddr, *(DWord_t *) &(rp->rp_channel.TxControl[0])); sSetRxTrigger(&rp->rp_channel, TRIG_1); sDisRxStatusMode(&rp->rp_channel); sFlushRxFIFO(&rp->rp_channel); sFlushTxFIFO(&rp->rp_channel); sEnInterrupts(&rp->rp_channel, (TXINT_EN|MCINT_EN|RXINT_EN|SRCINT_EN|CHANINT_EN)); sSetRxTrigger(&rp->rp_channel, TRIG_1); sDisRxStatusMode(&rp->rp_channel); sClrTxXOFF(&rp->rp_channel); /* sDisRTSFlowCtl(&rp->rp_channel); sDisCTSFlowCtl(&rp->rp_channel); */ sDisTxSoftFlowCtl(&rp->rp_channel); sStartRxProcessor(&rp->rp_channel); sEnRxFIFO(&rp->rp_channel); sEnTransmit(&rp->rp_channel); /* sSetDTR(&rp->rp_channel); sSetRTS(&rp->rp_channel); */ ++rp->wopeners; error = rpparam(tp, &tp->t_termios); --rp->wopeners; if(error != 0) { splx(oldspl); return(error); } rp_num_ports_open++; IntMask = sGetChanIntID(&rp->rp_channel); IntMask = IntMask & rp->rp_intmask; ChanStatus = sGetChanStatus(&rp->rp_channel); if((IntMask & DELTA_CD) || IS_CALLOUT(dev)) { if((ChanStatus & CD_ACT) || IS_CALLOUT(dev)) { (void)(*linesw[tp->t_line].l_modem)(tp, 1); } } if(rp_num_ports_open == 1) timeout(rp_do_poll, (void *)NULL, POLL_INTERVAL); } if(!(flag&O_NONBLOCK) && !(tp->t_cflag&CLOCAL) && !(tp->t_state & TS_CARR_ON) && !(IS_CALLOUT(dev))) { ++rp->wopeners; error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "rpdcd", 0); --rp->wopeners; if(error != 0) goto out; goto open_top; } error = (*linesw[tp->t_line].l_open)(dev, tp); rp_disc_optim(tp, &tp->t_termios, rp); if(tp->t_state & TS_ISOPEN && IS_CALLOUT(dev)) rp->active_out = TRUE; /* if(rp_num_ports_open == 1) timeout(rp_do_poll, (void *)NULL, POLL_INTERVAL); */ out: splx(oldspl); if(!(tp->t_state & TS_ISOPEN) && rp->wopeners == 0) { rphardclose(rp); } return(error); } int rpclose(dev, flag, mode, p) dev_t dev; int flag, mode; struct proc *p; { int oldspl, unit, mynor, umynor, port; /* SG */ struct rp_port *rp; struct tty *tp; CHANNEL_t *cp; umynor = (((minor(dev) >> 16) -1) * 32); /* SG */ port = (minor(dev) & 0x1f); /* SG */ mynor = (port + umynor); /* SG */ unit = minor_to_unit[mynor]; /* SG */ if(IS_CONTROL(dev)) return(0); rp = rp_addr(unit) + port; cp = &rp->rp_channel; tp = rp->rp_tty; oldspl = spltty(); (*linesw[tp->t_line].l_close)(tp, flag); rp_disc_optim(tp, &tp->t_termios, rp); rpstop(tp, FREAD | FWRITE); rphardclose(rp); tp->t_state &= ~TS_BUSY; ttyclose(tp); splx(oldspl); return(0); } static void rphardclose(struct rp_port *rp) { int mynor; struct tty *tp; CHANNEL_t *cp; cp = &rp->rp_channel; tp = rp->rp_tty; mynor = MINOR_MAGIC(tp->t_dev); sFlushRxFIFO(cp); sFlushTxFIFO(cp); sDisTransmit(cp); sDisInterrupts(cp, TXINT_EN|MCINT_EN|RXINT_EN|SRCINT_EN|CHANINT_EN); sDisRTSFlowCtl(cp); sDisCTSFlowCtl(cp); sDisTxSoftFlowCtl(cp); sClrTxXOFF(cp); if(tp->t_cflag&HUPCL || !(tp->t_state&TS_ISOPEN) || !rp->active_out) { sClrDTR(cp); } if(IS_CALLOUT(tp->t_dev)) { sClrDTR(cp); } if(rp->dtr_wait != 0) { timeout(rpdtrwakeup, rp, rp->dtr_wait); rp->state |= ~SET_DTR; } rp->active_out = FALSE; wakeup(&rp->active_out); wakeup(TSA_CARR_ON(tp)); } static int rpread(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { struct rp_port *rp; struct tty *tp; int unit, mynor, umynor, port, error = 0; /* SG */ umynor = (((minor(dev) >> 16) -1) * 32); /* SG */ port = (minor(dev) & 0x1f); /* SG */ mynor = (port + umynor); /* SG */ unit = minor_to_unit[mynor]; /* SG */ if(IS_CONTROL(dev)) return(ENODEV); rp = rp_addr(unit) + port; tp = rp->rp_tty; error = (*linesw[tp->t_line].l_read)(tp, uio, flag); return(error); } static int rpwrite(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { struct rp_port *rp; struct tty *tp; int unit, mynor, port, umynor, error = 0; /* SG */ umynor = (((minor(dev) >> 16) -1) * 32); /* SG */ port = (minor(dev) & 0x1f); /* SG */ mynor = (port + umynor); /* SG */ unit = minor_to_unit[mynor]; /* SG */ if(IS_CONTROL(dev)) return(ENODEV); rp = rp_addr(unit) + port; tp = rp->rp_tty; while(rp->rp_disable_writes) { rp->rp_waiting = 1; if(error = ttysleep(tp, (caddr_t)rp, TTOPRI|PCATCH, "rp_write", 0)) { return(error); } } error = (*linesw[tp->t_line].l_write)(tp, uio, flag); return error; } static void rpdtrwakeup(void *chan) { struct rp_port *rp; rp = (struct rp_port *)chan; rp->state &= SET_DTR; wakeup(&rp->dtr_wait); } int rpioctl(dev, cmd, data, flag, p) dev_t dev; u_long cmd; caddr_t data; int flag; struct proc *p; { struct rp_port *rp; CHANNEL_t *cp; struct tty *tp; int unit, mynor, port, umynor; /* SG */ int oldspl; int error = 0; int arg, flags, result, ChanStatus; int oldcmd; struct termios term, *t; umynor = (((minor(dev) >> 16) -1) * 32); /* SG */ port = (minor(dev) & 0x1f); /* SG */ mynor = (port + umynor); /* SG */ unit = minor_to_unit[mynor]; rp = rp_addr(unit) + port; if(IS_CONTROL(dev)) { struct termios *ct; switch (IS_CONTROL(dev)) { case CONTROL_INIT_STATE: ct = IS_CALLOUT(dev) ? &rp->it_out : &rp->it_in; break; case CONTROL_LOCK_STATE: ct = IS_CALLOUT(dev) ? &rp->lt_out : &rp->lt_in; break; default: return(ENODEV); /* /dev/nodev */ } switch (cmd) { case TIOCSETA: error = suser(p->p_ucred, &p->p_acflag); if(error != 0) return(error); *ct = *(struct termios *)data; return(0); case TIOCGETA: *(struct termios *)data = *ct; return(0); case TIOCGETD: *(int *)data = TTYDISC; return(0); case TIOCGWINSZ: bzero(data, sizeof(struct winsize)); return(0); default: return(ENOTTY); } } tp = rp->rp_tty; cp = &rp->rp_channel; #if defined(COMPAT_43) || defined(COMPAT_SUNOS) term = tp->t_termios; oldcmd = cmd; error = ttsetcompat(tp, &cmd, data, &term); if(error != 0) return(error); if(cmd != oldcmd) { data = (caddr_t)&term; } #endif if((cmd == TIOCSETA) || (cmd == TIOCSETAW) || (cmd == TIOCSETAF)) { int cc; struct termios *dt = (struct termios *)data; struct termios *lt = IS_CALLOUT(dev) ? &rp->lt_out : &rp->lt_in; dt->c_iflag = (tp->t_iflag & lt->c_iflag) | (dt->c_iflag & ~lt->c_iflag); dt->c_oflag = (tp->t_oflag & lt->c_oflag) | (dt->c_oflag & ~lt->c_oflag); dt->c_cflag = (tp->t_cflag & lt->c_cflag) | (dt->c_cflag & ~lt->c_cflag); dt->c_lflag = (tp->t_lflag & lt->c_lflag) | (dt->c_lflag & ~lt->c_lflag); for(cc = 0; cc < NCCS; ++cc) if(lt->c_cc[cc] = tp->t_cc[cc]) dt->c_cc[cc] = tp->t_cc[cc]; if(lt->c_ispeed != 0) dt->c_ispeed = tp->t_ispeed; if(lt->c_ospeed != 0) dt->c_ospeed = tp->t_ospeed; } t = &tp->t_termios; error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); if(error != ENOIOCTL) { return(error); } oldspl = spltty(); flags = rp->rp_channel.TxControl[3]; error = ttioctl(tp, cmd, data, flag); flags = rp->rp_channel.TxControl[3]; rp_disc_optim(tp, &tp->t_termios, rp); if(error != ENOIOCTL) { splx(oldspl); return(error); } switch(cmd) { case TIOCSBRK: sSendBreak(&rp->rp_channel); break; case TIOCCBRK: sClrBreak(&rp->rp_channel); break; case TIOCSDTR: sSetDTR(&rp->rp_channel); sSetRTS(&rp->rp_channel); break; case TIOCCDTR: sClrDTR(&rp->rp_channel); break; case TIOCMSET: arg = *(int *) data; flags = 0; if(arg & TIOCM_RTS) flags |= SET_RTS; if(arg & TIOCM_DTR) flags |= SET_DTR; rp->rp_channel.TxControl[3] = ((rp->rp_channel.TxControl[3] & ~(SET_RTS | SET_DTR)) | flags); sOutDW(rp->rp_channel.IndexAddr, *(DWord_t *) &(rp->rp_channel.TxControl[0])); break; case TIOCMBIS: arg = *(int *) data; flags = 0; if(arg & TIOCM_RTS) flags |= SET_RTS; if(arg & TIOCM_DTR) flags |= SET_DTR; rp->rp_channel.TxControl[3] |= flags; sOutDW(rp->rp_channel.IndexAddr, *(DWord_t *) &(rp->rp_channel.TxControl[0])); break; case TIOCMBIC: arg = *(int *) data; flags = 0; if(arg & TIOCM_RTS) flags |= SET_RTS; if(arg & TIOCM_DTR) flags |= SET_DTR; rp->rp_channel.TxControl[3] &= ~flags; sOutDW(rp->rp_channel.IndexAddr, *(DWord_t *) &(rp->rp_channel.TxControl[0])); break; case TIOCMGET: ChanStatus = sGetChanStatusLo(&rp->rp_channel); flags = rp->rp_channel.TxControl[3]; result = TIOCM_LE; /* always on while open for some reason */ result |= (((flags & SET_DTR) ? TIOCM_DTR : 0) | ((flags & SET_RTS) ? TIOCM_RTS : 0) | ((ChanStatus & CD_ACT) ? TIOCM_CAR : 0) | ((ChanStatus & DSR_ACT) ? TIOCM_DSR : 0) | ((ChanStatus & CTS_ACT) ? TIOCM_CTS : 0)); if(rp->rp_channel.RxControl[2] & RTSFC_EN) { result |= TIOCM_RTS; } *(int *)data = result; break; case TIOCMSDTRWAIT: error = suser(p->p_ucred, &p->p_acflag); if(error != 0) { splx(oldspl); return(error); } rp->dtr_wait = *(int *)data * hz/100; break; case TIOCMGDTRWAIT: *(int *)data = rp->dtr_wait * 100/hz; break; default: splx(oldspl); return ENOTTY; } splx(oldspl); return(0); } static struct speedtab baud_table[] = { B0, 0, B50, BRD50, B75, BRD75, B110, BRD110, B134, BRD134, B150, BRD150, B200, BRD200, B300, BRD300, B600, BRD600, B1200, BRD1200, B1800, BRD1800, B2400, BRD2400, B4800, BRD4800, B9600, BRD9600, B19200, BRD19200, B38400, BRD38400, B7200, BRD7200, B14400, BRD14400, B57600, BRD57600, B76800, BRD76800, B115200, BRD115200, B230400, BRD230400, -1, -1 }; static int rpparam(tp, t) struct tty *tp; struct termios *t; { struct rp_port *rp; CHANNEL_t *cp; int unit, mynor, port, umynor; /* SG */ int oldspl, cflag, iflag, oflag, lflag; int ospeed; umynor = (((minor(tp->t_dev) >> 16) -1) * 32); /* SG */ port = (minor(tp->t_dev) & 0x1f); /* SG */ mynor = (port + umynor); /* SG */ unit = minor_to_unit[mynor]; rp = rp_addr(unit) + port; cp = &rp->rp_channel; oldspl = spltty(); cflag = t->c_cflag; iflag = t->c_iflag; oflag = t->c_oflag; lflag = t->c_lflag; ospeed = ttspeedtab(t->c_ispeed, baud_table); if(ospeed < 0 || t->c_ispeed != t->c_ospeed) return(EINVAL); tp->t_ispeed = t->c_ispeed; tp->t_ospeed = t->c_ospeed; tp->t_cflag = cflag; tp->t_iflag = iflag; tp->t_oflag = oflag; tp->t_lflag = lflag; if(t->c_ospeed == 0) { sClrDTR(cp); return(0); } rp->rp_fifo_lw = ((t->c_ospeed*2) / 1000) +1; /* Set baud rate ----- we only pay attention to ispeed */ sSetDTR(cp); sSetRTS(cp); sSetBaud(cp, ospeed); if(cflag & CSTOPB) { sSetStop2(cp); } else { sSetStop1(cp); } if(cflag & PARENB) { sEnParity(cp); if(cflag & PARODD) { sSetOddParity(cp); } else { sSetEvenParity(cp); } } else { sDisParity(cp); } if((cflag & CSIZE) == CS8) { sSetData8(cp); rp->rp_imask = 0xFF; } else { sSetData7(cp); rp->rp_imask = 0x7F; } if(iflag & ISTRIP) { rp->rp_imask &= 0x7F; } if(cflag & CLOCAL) { rp->rp_intmask &= ~DELTA_CD; } else { rp->rp_intmask |= DELTA_CD; } /* Put flow control stuff here */ if(cflag & CCTS_OFLOW) { sEnCTSFlowCtl(cp); } else { sDisCTSFlowCtl(cp); } if(cflag & CRTS_IFLOW) { rp->rp_rts_iflow = 1; } else { rp->rp_rts_iflow = 0; } if(cflag & CRTS_IFLOW) { sEnRTSFlowCtl(cp); } else { sDisRTSFlowCtl(cp); } rp_disc_optim(tp, t, rp); if((cflag & CLOCAL) || (sGetChanStatusLo(cp) & CD_ACT)) { tp->t_state |= TS_CARR_ON; wakeup(TSA_CARR_ON(tp)); } /* tp->t_state |= TS_CAN_BYPASS_L_RINT; flags = rp->rp_channel.TxControl[3]; if(flags & SET_DTR) else if(flags & SET_RTS) else */ splx(oldspl); return(0); } static void rp_disc_optim(tp, t, rp) struct tty *tp; struct termios *t; struct rp_port *rp; { if(!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON)) &&(!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK)) &&(!(t->c_iflag & PARMRK) ||(t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK)) && !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN)) && linesw[tp->t_line].l_rint == ttyinput) tp->t_state |= TS_CAN_BYPASS_L_RINT; else tp->t_state &= ~TS_CAN_BYPASS_L_RINT; } static void rpstart(tp) struct tty *tp; { struct rp_port *rp; CHANNEL_t *cp; struct clist *qp; int unit, mynor, port, umynor; /* SG */ char ch, flags; int spl, xmit_fifo_room; int count; umynor = (((minor(tp->t_dev) >> 16) -1) * 32); /* SG */ port = (minor(tp->t_dev) & 0x1f); /* SG */ mynor = (port + umynor); /* SG */ unit = minor_to_unit[mynor]; rp = rp_addr(unit) + port; cp = &rp->rp_channel; flags = rp->rp_channel.TxControl[3]; spl = spltty(); if(tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) { ttwwakeup(tp); splx(spl); return; } if(rp->rp_xmit_stopped) { sEnTransmit(cp); rp->rp_xmit_stopped = 0; } count = sGetTxCnt(cp); if(tp->t_outq.c_cc == 0) { if((tp->t_state & TS_BUSY) && (count == 0)) { tp->t_state &= ~TS_BUSY; } ttwwakeup(tp); splx(spl); return; } xmit_fifo_room = TXFIFO_SIZE - sGetTxCnt(cp); qp = &tp->t_outq; count = 0; if(xmit_fifo_room > 0 && qp->c_cc > 0) { tp->t_state |= TS_BUSY; } while(xmit_fifo_room > 0 && qp->c_cc > 0) { ch = getc(qp); sOutB(sGetTxRxDataIO(cp), ch); xmit_fifo_room--; count++; } rp->rp_restart = (qp->c_cc > 0) ? rp->rp_fifo_lw : 0; ttwwakeup(tp); splx(spl); } static void rpstop(tp, flag) register struct tty *tp; int flag; { struct rp_port *rp; CHANNEL_t *cp; int unit, mynor, port, umynor; /* SG */ int spl; umynor = (((minor(tp->t_dev) >> 16) -1) * 32); /* SG */ port = (minor(tp->t_dev) & 0x1f); /* SG */ mynor = (port + umynor); /* SG */ unit = minor_to_unit[mynor]; rp = rp_addr(unit) + port; cp = &rp->rp_channel; spl = spltty(); if(tp->t_state & TS_BUSY) { if((tp->t_state&TS_TTSTOP) == 0) { sFlushTxFIFO(cp); } else { if(rp->rp_xmit_stopped == 0) { sDisTransmit(cp); rp->rp_xmit_stopped = 1; } } } splx(spl); rpstart(tp); } int rpselect(dev, flag, p) dev_t dev; int flag; struct proc *p; { return(0); } struct tty * rpdevtotty(dev_t dev) { struct rp_port *rp; int unit, port, mynor, umynor; /* SG */ umynor = (((minor(dev) >> 16) -1) * 32); /* SG */ port = (minor(dev) & 0x1f); /* SG */ mynor = (port + umynor); /* SG */ unit = minor_to_unit[mynor]; /* SG */ if(IS_CONTROL(dev)) return(NULL); rp = rp_addr(unit) + port; return(rp->rp_tty); } Index: head/sys/i386/isa/snd/sound.c =================================================================== --- head/sys/i386/isa/snd/sound.c (revision 45719) +++ head/sys/i386/isa/snd/sound.c (revision 45720) @@ -1,1520 +1,1517 @@ /* * snd/sound.c * * Main sound driver for FreeBSD. This file provides the main * entry points for probe/attach and all i/o demultiplexing, including * default routines for generic devices. * * (C) 1997 Luigi Rizzo (luigi@iet.unipi.it) * * 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. * * * For each card type a template "snddev_info" structure contains * all the relevant parameters, both for configuration and runtime. * * In this file we build tables of pointers to the descriptors for * the various supported cards. The generic probe routine scans * the table(s) looking for a matching entry, then invokes the * board-specific probe routine. If successful, a pointer to the * correct snddev_info is stored in snddev_last_probed, for subsequent * use in the attach routine. The generic attach routine copies * the template to a permanent descriptor (pcm_info[unit] and * friends), initializes all generic parameters, and calls the * board-specific attach routine. * * On device calls, the generic routines do the checks on unit and * device parameters, then call the board-specific routines if * available, or try to perform the task using the default code. * */ #include "opt_devfs.h" #include #ifdef DEVFS #include #endif /* DEVFS */ #if NPCM > 0 /* from "pcm.h" via disgusting #include in snd/sound.h */ extern struct isa_driver pcmdriver ; #define SNDSTAT_BUF_SIZE 4000 static char status_buf[SNDSTAT_BUF_SIZE] ; static int status_len = 0 ; static void init_status(snddev_info *d); static d_open_t sndopen; static d_close_t sndclose; static d_ioctl_t sndioctl; static d_read_t sndread; static d_write_t sndwrite; static d_mmap_t sndmmap; #define CDEV_MAJOR 30 static struct cdevsw snd_cdevsw = { sndopen, sndclose, sndread, sndwrite, sndioctl, nostop, noreset, nodevtotty, sndselect, sndmmap, nostrategy, "snd", NULL, -1, }; /* * descriptors for active devices. * */ snddev_info pcm_info[NPCM_MAX] ; snddev_info midi_info[NPCM_MAX] ; snddev_info synth_info[NPCM_MAX] ; u_long nsnd = NPCM ; /* total number of sound devices */ /* * Hooks for APM support, but code not operational yet. */ #include "apm.h" #include #if NAPM > 0 static int sound_suspend(void *arg) { /* * I think i can safely do nothing here and * reserve all the work for wakeup time */ printf("Called APM sound suspend hook for unit %d\n", (int)arg); return 0 ; } static int sound_resume(void *arg) { snddev_info *d = NULL ; d = &pcm_info[(int)arg] ; /* * reinitialize card registers. * Flush buffers and reinitialize DMA channels. * If a write was pending, pretend it is done * (and issue any wakeup we need). * If a read is pending, restart it. */ if (d->bd_id == MD_YM0020) { DDB(printf("setting up yamaha registers\n")); outb(0x370, 6 /* dma config */ ) ; if (FULL_DUPLEX(d)) outb(0x371, 0xa9 ); /* use both dma chans */ else outb(0x371, 0x8b ); /* use low dma chan */ } printf("Called APM sound resume hook for unit %d\n", (int)arg); return 0 ; } static void init_sound_apm(int unit) { struct apmhook *ap; ap = malloc(sizeof *ap, M_DEVBUF, M_NOWAIT); bzero(ap, sizeof *ap); ap->ah_fun = sound_resume; ap->ah_arg = (void *)unit; ap->ah_name = "pcm resume handler"; ap->ah_order = APM_MID_ORDER; apm_hook_establish(APM_HOOK_RESUME, ap); ap = malloc(sizeof *ap, M_DEVBUF, M_NOWAIT); bzero(ap, sizeof *ap); ap->ah_fun = sound_suspend; ap->ah_arg = (void *)unit; ap->ah_name = "pcm suspend handler"; ap->ah_order = APM_MID_ORDER; apm_hook_establish(APM_HOOK_SUSPEND, ap); } #endif /* NAPM */ /* * the probe routine can only return an int to the upper layer. Hence, * it leaves the pointer to the last successfully * probed device descriptor in snddev_last_probed */ snddev_info *snddev_last_probed = NULL ; static snddev_info * generic_snd_probe(struct isa_device * dev, snddev_info **p[], char *s); /* * here are the lists of known cards. Similar cards (e.g. all * sb clones, all mss clones, ... are in the same array. * All lists of devices of the same type (eg. all pcm, all midi...) * are in the same array. * Each probe for a device type gets the pointer to the main array * and then scans the sublists. * * XXX should use DATA_SET to create a linker set for sb_devs and other * such structures. */ extern snddev_info sb_op_desc; extern snddev_info mss_op_desc; static snddev_info *sb_devs[] = { /* all SB clones */ &sb_op_desc, NULL, } ; static snddev_info *mss_devs[] = { /* all MSS clones */ &mss_op_desc, NULL, } ; static snddev_info **pcm_devslist[] = { /* all pcm devices */ mss_devs, sb_devs, NULL } ; int pcmprobe(struct isa_device * dev) { bzero(&pcm_info[dev->id_unit], sizeof(pcm_info[dev->id_unit]) ); return generic_snd_probe(dev, pcm_devslist, "pcm") ? 1 : 0 ; } static snddev_info **midi_devslist[] = {/* all midi devices */ NULL } ; int midiprobe(struct isa_device * dev) { bzero(&midi_info[dev->id_unit], sizeof(midi_info[dev->id_unit]) ); return 0 ; return generic_snd_probe(dev, midi_devslist, "midi") ? 1 : 0 ; } int synthprobe(struct isa_device * dev) { bzero(&synth_info[dev->id_unit], sizeof(synth_info[dev->id_unit]) ); return 0 ; } /* * this is the ISA part of the generic attach routine */ int pcmattach(struct isa_device * dev) { snddev_info *d = NULL ; - struct isa_device *dvp; int stat = 0; dev->id_ointr = pcmintr; if ( (dev->id_unit >= NPCM_MAX) || /* too many devs */ (snddev_last_probed == NULL) || /* last probe failed */ (snddev_last_probed->attach==NULL) ) /* no attach routine */ return 0 ; /* fail */ /* * default initialization: copy generic parameters for the routine, * initialize from the isa_device structure, and allocate memory. * If everything succeeds, then call the attach routine for * further initialization. */ pcm_info[dev->id_unit] = *snddev_last_probed ; d = &pcm_info[dev->id_unit] ; d->io_base = dev->id_iobase ; d->irq = ffs(dev->id_irq) - 1 ; d->dbuf_out.chan = dev->id_drq ; if (dev->id_flags != -1 && dev->id_flags & DV_F_DUAL_DMA && (dev->id_flags & DV_F_DRQ_MASK) != 4 ) /* enable dma2 */ d->dbuf_in.chan = dev->id_flags & DV_F_DRQ_MASK ; else d->dbuf_in.chan = d->dbuf_out.chan ; #if 1 /* does this cause trouble with PnP cards ? */ if (d->bd_id == 0) d->bd_id = (dev->id_flags & DV_F_DEV_MASK) >> DV_F_DEV_SHIFT ; #endif d->status_ptr = 0; /* * Allocates memory and initializes the dma structs properly. We * use independent buffers for each channel. For the time being, * this is done independently of the dma setting. In future * revisions, if we see that we have a single dma, we might decide * to use a single buffer to save memory. */ alloc_dbuf( &(d->dbuf_out), d->bufsize ); alloc_dbuf( &(d->dbuf_in), d->bufsize ); isa_dma_acquire(d->dbuf_out.chan); if (FULL_DUPLEX(d)) isa_dma_acquire(d->dbuf_in.chan); /* * should try and find a suitable value for id_id, otherwise * the interrupt is not registered and dispatched properly. * This is important for PnP devices, where "dev" is built on * the fly and many field are not initialized. */ if (dev->id_driver == NULL) { dev->id_driver = &pcmdriver ; - dvp=find_isadev(isa_devtab_tty, &pcmdriver, 0); - if (dvp) - dev->id_id = dvp->id_id; + dev->id_id = isa_compat_nextid(); } /* * call the generic part of the attach */ pcminit(d, dev->id_unit); /* * and finally, call the device attach routine * XXX I should probably use d->attach(dev) */ stat = snddev_last_probed->attach(dev); #if 0 /* * XXX hooks for synt support. Try probe and attach... */ if (d->synth_base && opl3_probe(dev) ) { opl3_attach(dev); } #endif snddev_last_probed = NULL ; return stat ; } /* * This is the generic init routine */ int pcminit(snddev_info *d, int unit) { #ifdef DEVFS void *cookie; #endif dev_t isadev; isadev = makedev(CDEV_MAJOR, 0); cdevsw_add(&isadev, &snd_cdevsw, NULL); /* * initialize standard parameters for the device. This can be * overridden by device-specific configurations but better do * here the generic things. */ d->magic = MAGIC(unit); /* debugging... */ d->play_speed = d->rec_speed = 8000 ; d->play_blocksize = d->rec_blocksize = 2048 ; d->play_fmt = d->rec_fmt = AFMT_MU_LAW ; #ifdef DEVFS #ifndef GID_GAMES #define GID_SND UID_ROOT #else #define GID_SND GID_GAMES /* i am not really sure this is a good one. */ #endif #define UID_SND UID_ROOT #define PERM_SND 0660 /* * XXX remember to store the returned tokens if you want to * be able to remove the device later * * Make links to first successfully probed unit. * Attempts by later devices to make these links will fail. */ cookie = devfs_add_devswf(&snd_cdevsw, (unit << 4) | SND_DEV_DSP, DV_CHR, UID_SND, GID_SND, PERM_SND, "dsp%r", unit); if (cookie) devfs_makelink(cookie, "dsp"); cookie = devfs_add_devswf(&snd_cdevsw, (unit << 4) | SND_DEV_DSP16, DV_CHR, UID_SND, GID_SND, PERM_SND, "dspW%r", unit); if (cookie) devfs_makelink(cookie, "dspW"); cookie = devfs_add_devswf(&snd_cdevsw, (unit << 4) | SND_DEV_AUDIO, DV_CHR, UID_SND, GID_SND, PERM_SND, "audio%r", unit); if (cookie) devfs_makelink(cookie, "audio"); cookie = devfs_add_devswf(&snd_cdevsw, (unit << 4) | SND_DEV_CTL, DV_CHR, UID_SND, GID_SND, PERM_SND, "mixer%r", unit); if (cookie) devfs_makelink(cookie, "mixer"); cookie = devfs_add_devswf(&snd_cdevsw, (unit << 4) | SND_DEV_STATUS, DV_CHR, UID_SND, GID_SND, PERM_SND, "sndstat%r", unit); if (cookie) devfs_makelink(cookie, "sndstat"); #if 0 /* these two are still unsupported... */ cookie = devfs_add_devswf(&snd_cdevsw, (unit << 4) | SND_DEV_MIDIN, DV_CHR, UID_SND, GID_SND, PERM_SND, "midi%r", unit); if (cookie) devfs_makelink(cookie, "midi"); cookie = devfs_add_devswf(&snd_cdevsw, (unit << 4) | SND_DEV_SYNTH, DV_CHR, UID_SND, GID_SND, PERM_SND, "sequencer%r", unit); if (cookie) devfs_makelink(cookie, "sequencer"); #endif #endif /* DEVFS */ #if NAPM > 0 init_sound_apm(unit); #endif return 0 ; } int midiattach(struct isa_device * dev) { return 0 ; } int synthattach(struct isa_device * dev) { return 0 ; } struct isa_driver pcmdriver = { pcmprobe, pcmattach, "pcm" } ; struct isa_driver mididriver = { midiprobe, midiattach, "midi" } ; struct isa_driver synthdriver = { synthprobe, synthattach, "synth" } ; void pcmintr(int unit) { DEB(printf("__/\\/ pcmintr -- unit %d\n", unit)); pcm_info[unit].interrupts++; if (pcm_info[unit].isr) pcm_info[unit].isr(unit); #if 0 /* these do not exist at the moment. */ if (midi_info[unit].isr) midi_info[unit].isr(unit); if (synth_info[unit].isr) synth_info[unit].isr(unit); #endif } static snddev_info * generic_snd_probe(struct isa_device * dev, snddev_info **p[], char *s) { snddev_info **q ; struct isa_device saved_dev ; snddev_last_probed = NULL ; saved_dev = *dev ; /* the probe routine might alter parameters */ /* * XXX todo: should try to match flags with device type. */ for ( ; p[0] != NULL ; p++ ) for ( q = *p ; q[0] ; q++ ) if (q[0]->probe && q[0]->probe(dev)) return (snddev_last_probed = q[0]) ; else *dev = saved_dev ; return NULL ; } /* * a small utility function which, given a device number, returns * a pointer to the associated snddev_info struct, and sets the unit * number. */ static snddev_info * get_snddev_info(dev_t dev, int *unit) { int u; snddev_info *d = NULL ; dev = minor(dev); u = dev >> 4 ; if (unit) *unit = u ; if (u >= NPCM_MAX || ( pcm_info[u].io_base == 0 && (dev & 0x0f) != SND_DEV_STATUS)) { int i; for (i = 0 ; i < NPCM_MAX ; i++) if (pcm_info[i].io_base) break ; if (i != NPCM_MAX) printf("pcm%d: unit not configured, perhaps you want pcm%d ?\n", u, i); else printf("no pcm units configured\b"); return NULL ; } switch(dev & 0x0f) { case SND_DEV_CTL : /* /dev/mixer handled by pcm */ case SND_DEV_STATUS : /* /dev/sndstat handled by pcm */ case SND_DEV_SNDPROC : /* /dev/sndproc handled by pcm */ case SND_DEV_DSP : case SND_DEV_DSP16 : case SND_DEV_AUDIO : case SND_DEV_SEQ : /* XXX when enabled... */ d = & pcm_info[u] ; break ; case SND_DEV_SEQ2 : case SND_DEV_MIDIN: default: printf("unsupported subdevice %d\n", dev & 0xf); return NULL ; } return d ; } /* * here are the switches for the main functions. The switches do * all necessary checks on the device number to make sure * that the device is configured. They also provide some default * functionalities so that device-specific drivers have to deal * only with special cases. */ static int sndopen(dev_t i_dev, int flags, int mode, struct proc * p) { int dev, unit ; snddev_info *d; dev = minor(i_dev); d = get_snddev_info(dev, &unit); DEB(printf("open snd%d subdev %d flags 0x%08x mode 0x%08x\n", unit, dev & 0xf, flags, mode)); if (d == NULL) return (ENXIO) ; switch(dev & 0x0f) { case SND_DEV_SEQ: /* sequencer. Hack... */ #if 0 /* XXX hook for opl3 support */ if (d->synth_base) return opl3_open(i_dev, flags, mode, p); else #endif return ENXIO ; case SND_DEV_CTL : /* mixer ... */ return 0 ; /* always succeed */ case SND_DEV_STATUS : /* implemented right here */ init_status(&pcm_info[unit]); d->status_ptr = 0 ; return 0 ; default: if (d->open == NULL) { printf("open: unit %d not configured, perhaps you want unit %d ?\n", unit, unit+1 ); return (ENXIO) ; } else return d->open(i_dev, flags, mode, p); } return ENXIO ; } static int sndclose(dev_t i_dev, int flags, int mode, struct proc * p) { int dev, unit ; snddev_info *d; dev = minor(i_dev); d = get_snddev_info(dev, &unit); DEB(printf("close snd%d subdev %d\n", unit, dev & 0xf)); if (d == NULL) return (ENXIO) ; switch(dev & 0xf) { /* only those for which close makes sense */ case SND_DEV_SEQ: #if 0 /* XXX hook for opl3 support */ if (d->synth_base) return opl3_close(i_dev, flags, mode, p); else #endif return ENXIO ; case SND_DEV_AUDIO : case SND_DEV_DSP : case SND_DEV_DSP16 : if (d->close) return d->close(i_dev, flags, mode, p); } return 0 ; } static int sndread(dev_t i_dev, struct uio * buf, int flag) { int ret, dev, unit; snddev_info *d ; u_long s; dev = minor(i_dev); d = get_snddev_info(dev, &unit); DEB(printf("read snd%d subdev %d flag 0x%08x\n", unit, dev & 0xf, flag)); if (d == NULL) return ENXIO ; if ( (dev & 0x0f) == SND_DEV_STATUS ) { int l, c; u_char *p; l = buf->uio_resid; s=spltty(); c = status_len - d->status_ptr ; if (c < 0) /* should not happen! */ c = 0 ; if (c < l) l = c ; p = status_buf + d->status_ptr ; d->status_ptr += l ; splx(s); return uiomove(p, l, buf) ; } /* * XXX read from the ad1816 with a single DMA channel is unsupported. * This is really not the place for machine-dependent functions, * a proper device routine will be supplied in the future - luigi */ if ((d->bd_id == MD_AD1816) && (!(FULL_DUPLEX(d)))) return EIO; if (d->read) /* device-specific read */ return d->read(i_dev, buf, flag); /* * the generic read routine. device-specific stuff should only * be in the dma-handling procedures. */ s = spltty(); if ( d->flags & SND_F_READING ) { /* another reader is in, deny request */ splx(s); DDB(printf("read denied, another reader is in\n")); /* * sleep for a while to avoid killing the machine. */ tsleep( (void *)s, PZERO, "sndar", hz ) ; return EBUSY ; } if ( ! FULL_DUPLEX(d) ) { /* half duplex */ if ( d->flags & SND_F_WRITING ) { /* another writer is in, deny request */ splx(s); DDB(printf("read denied, half duplex and a writer is in\n")); tsleep( (void *)s, PZERO, "sndaw", hz ) ; return EBUSY ; } while ( d->dbuf_out.dl ) { /* * we have a pending dma operation, post a read request * and wait for the write to complete. */ d->flags |= SND_F_READING ; DEB(printf("sndread: sleeping waiting for write to end\n")); ret = tsleep( (caddr_t)&(d->dbuf_out), PRIBIO | PCATCH , "sndrdw", hz ) ; if (ret == ERESTART || ret == EINTR) { d->flags &= ~SND_F_READING ; splx(s); return EINTR ; } } } d->flags |= SND_F_READING ; splx(s); return dsp_read_body(d, buf); } static int sndwrite(dev_t i_dev, struct uio * buf, int flag) { int ret, dev, unit; snddev_info *d; u_long s; dev = minor(i_dev); d = get_snddev_info(dev, &unit); DEB(printf("write snd%d subdev %d flag 0x%08x\n", unit, dev & 0xf, flag)); if (d == NULL) return (ENXIO) ; switch( dev & 0x0f) { /* only writeable devices */ case SND_DEV_MIDIN: /* XXX is this writable ? */ case SND_DEV_SEQ : case SND_DEV_SEQ2 : case SND_DEV_DSP : case SND_DEV_DSP16 : case SND_DEV_AUDIO : break ; default: return EPERM ; /* for non-writeable devices ; */ } if (d->write) return d->write(i_dev, buf, flag); /* * Otherwise, use the generic write routine. device-specific * stuff should only be in the dma-handling procedures. */ s = spltty(); if ( d->flags & SND_F_WRITING ) { /* another writer is in, deny request */ splx(s); DDB(printf("write denied, another writer is in\n")); tsleep( (void *)s, PZERO , "sndaw", hz ) ; return EBUSY ; } if ( ! FULL_DUPLEX(d) ) { /* half duplex */ if ( d->flags & SND_F_READING ) { /* another reader is in, deny request */ splx(s); DDB(printf("write denied, half duplex and a reader is in\n")); tsleep( (void *)s, PZERO, "sndar", hz ) ; return EBUSY ; } while ( d->dbuf_in.dl ) { /* * we have a pending read dma. Post a write request * and wait for the read to complete (in fact I could * abort the read dma... */ d->flags |= SND_F_WRITING ; DEB(printf("sndwrite: sleeping waiting for read to end\n")); ret = tsleep( (caddr_t)&(d->dbuf_out), PRIBIO | PCATCH , "sndwr", hz ) ; if (ret == ERESTART || ret == EINTR) { d->flags &= ~SND_F_WRITING ; splx(s); return EINTR ; } } } d->flags |= SND_F_WRITING ; splx(s); return dsp_write_body(d, buf); } /* * generic sound ioctl. Functions of the default driver can be * overridden by the device-specific ioctl call. * If a device-specific call returns ENOSYS (Function not implemented), * the default driver is called. Otherwise, the returned value * is passed up. * * The default handler, for many parameters, sets the value in the * descriptor, sets SND_F_INIT, and calls the callback function with * reason INIT. If successful, the callback returns 1 and the caller * can update the parameter. */ static int sndioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc * p) { int ret = ENOSYS, dev, unit ; snddev_info *d; u_long s; dev = minor(i_dev); d = get_snddev_info(dev, &unit); if (d == NULL) return (ENXIO) ; if ( (dev & 0x0f) == SND_DEV_SEQ ) { /* sequencer. Hack... */ #if 0 if (d->synth_base) return opl3_ioctl(i_dev, cmd, arg, mode, p) ; else #endif return ENXIO ; } if (d->ioctl) ret = d->ioctl(dev, cmd, arg, mode, p); if (ret != ENOSYS) return ret ; /* * pass control to the default ioctl handler. Set ret to 0 now. */ ret = 0 ; /* * The linux ioctl interface for the sound driver has a thousand * different calls, and it is unpractical to put the names in * the switch(). So we have some tests before for common routines, * such as the ones related to the mixer. But we really ought * to redesign the interface! * * Reading from the mixer just requires to look at the cached * copy in d->mix_levels[dev], so this routine should cover * practically all needs for mixer reading. */ if ( (cmd & MIXER_READ(0)) == MIXER_READ(0) && (cmd & 0xff) < 32 ) { int dev = cmd & 0x1f ; if ( d->mix_devs & (1<mix_levels[dev]; return 0 ; } else return EINVAL ; } /* * all routines are called with int. blocked. Make sure that * ints are re-enabled when calling slow or blocking functions! */ s = spltty(); switch(cmd) { /* * we start with the new ioctl interface. */ case AIONWRITE : /* how many bytes can write ? */ if (d->dbuf_out.dl) { if (d->special_dma) d->callback(d, SND_CB_WR | SND_CB_DMAUPDATE) ; else dsp_wr_dmaupdate(&(d->dbuf_out)); } *(int *)arg = d->dbuf_out.fl; break; case AIOSSIZE : /* set the current blocksize */ { struct snd_size *p = (struct snd_size *)arg; if (p->play_size <= 1 && p->rec_size <= 1) { /* means no blocks */ d->flags &= ~SND_F_HAS_SIZE ; } else { RANGE (p->play_size, 40, d->dbuf_out.bufsize /4); d->play_blocksize = p->play_size & ~3 ; RANGE (p->rec_size, 40, d->dbuf_in.bufsize /4); d->rec_blocksize = p->rec_size & ~3 ; d->flags |= SND_F_HAS_SIZE ; } } splx(s); ask_init(d); /* FALLTHROUGH */ case AIOGSIZE : /* get the current blocksize */ { struct snd_size *p = (struct snd_size *)arg; p->play_size = d->play_blocksize ; p->rec_size = d->rec_blocksize ; } break ; case AIOSFMT : { snd_chan_param *p = (snd_chan_param *)arg; d->play_speed = p->play_rate; d->rec_speed = p->play_rate; /* XXX one speed allowed */ if (p->play_format & AFMT_STEREO) d->flags |= SND_F_STEREO ; else d->flags &= ~SND_F_STEREO ; d->play_fmt = p->play_format & ~AFMT_STEREO ; d->rec_fmt = p->rec_format & ~AFMT_STEREO ; } splx(s); if (!ask_init(d)) break ; /* could not reinit */ /* FALLTHROUGH */ case AIOGFMT : { snd_chan_param *p = (snd_chan_param *)arg; p->play_rate = d->play_speed; p->rec_rate = d->rec_speed; p->play_format = d->play_fmt; p->rec_format = d->rec_fmt; if (d->flags & SND_F_STEREO) { p->play_format |= AFMT_STEREO ; p->rec_format |= AFMT_STEREO ; } } break; case AIOGCAP : /* get capabilities */ /* this should really be implemented by the driver */ { snd_capabilities *p = (snd_capabilities *)arg; p->rate_min = 5000; p->rate_max = 48000; /* default */ p->bufsize = d->bufsize; p->formats = d->audio_fmt; /* default */ p->mixers = 1 ; /* default: one mixer */ p->inputs = d->mix_devs ; p->left = p->right = 255 ; } break ; case AIOSTOP: if (*(int *)arg == AIOSYNC_PLAY) /* play */ *(int *)arg = dsp_wrabort(d, 1 /* restart */); else if (*(int *)arg == AIOSYNC_CAPTURE) *(int *)arg = dsp_rdabort(d, 1 /* restart */); else { splx(s); printf("AIOSTOP: bad channel 0x%x\n", *(int *)arg); *(int *)arg = 0 ; } break ; case AIOSYNC: printf("AIOSYNC chan 0x%03lx pos %lu unimplemented\n", ((snd_sync_parm *)arg)->chan, ((snd_sync_parm *)arg)->pos); break; /* * here follow the standard ioctls (filio.h etc.) */ case FIONREAD : /* get # bytes to read */ if ( d->dbuf_in.dl ) { if (d->special_dma) d->callback(d, SND_CB_RD | SND_CB_DMAUPDATE) ; else dsp_rd_dmaupdate(&(d->dbuf_in)); } *(int *)arg = d->dbuf_in.rl; break; case FIOASYNC: /*set/clear async i/o */ DEB( printf("FIOASYNC\n") ; ) break; case SNDCTL_DSP_NONBLOCK : case FIONBIO : /* set/clear non-blocking i/o */ if ( *(int *)arg == 0 ) d->flags &= ~SND_F_NBIO ; else d->flags |= SND_F_NBIO ; break ; /* * Finally, here is the linux-compatible ioctl interface */ #define THE_REAL_SNDCTL_DSP_GETBLKSIZE _IOWR('P', 4, int) case THE_REAL_SNDCTL_DSP_GETBLKSIZE: case SNDCTL_DSP_GETBLKSIZE: *(int *) arg = d->play_blocksize ; break ; case SNDCTL_DSP_SETBLKSIZE : { int t = *(int *)arg; if (t <= 1) { /* means no blocks */ d->flags &= ~SND_F_HAS_SIZE ; } else { RANGE (t, 40, d->dbuf_out.bufsize /4); d->play_blocksize = d->rec_blocksize = t & ~3 ; /* align to multiple of 4 */ d->flags |= SND_F_HAS_SIZE ; } } splx(s); ask_init(d); break ; case SNDCTL_DSP_RESET: DEB(printf("dsp reset\n")); dsp_wrabort(d, 1 /* restart */); dsp_rdabort(d, 1 /* restart */); break ; case SNDCTL_DSP_SYNC: DEB(printf("dsp sync\n")); splx(s); snd_sync(d, 1, d->dbuf_out.bufsize - 4); /* DMA does not start with <4 bytes */ break ; case SNDCTL_DSP_SPEED: d->play_speed = d->rec_speed = *(int *)arg ; splx(s); if (ask_init(d)) *(int *)arg = d->play_speed ; break ; case SNDCTL_DSP_STEREO: if ( *(int *)arg == 0 ) d->flags &= ~SND_F_STEREO ; /* mono */ else if ( *(int *)arg == 1 ) d->flags |= SND_F_STEREO ; /* stereo */ else { printf("dsp stereo: %d is invalid, assuming 1\n", *(int *)arg ); d->flags |= SND_F_STEREO ; /* stereo */ } splx(s); if (ask_init(d)) *(int *)arg = (d->flags & SND_F_STEREO) ? 1 : 0 ; break ; case SOUND_PCM_WRITE_CHANNELS: if ( *(int *)arg == 1) d->flags &= ~SND_F_STEREO ; /* mono */ else if ( *(int *)arg == 2) d->flags |= SND_F_STEREO ; /* stereo */ else { ret = EINVAL ; break ; } splx(s); if (ask_init(d)) *(int *)arg = (d->flags & SND_F_STEREO) ? 2 : 1 ; break ; case SOUND_PCM_READ_RATE: *(int *)arg = d->play_speed; break ; case SOUND_PCM_READ_CHANNELS: *(int *)arg = (d->flags & SND_F_STEREO) ? 2 : 1; break ; case SNDCTL_DSP_GETFMTS: /* returns a mask of supported fmts */ *(int *)arg = (int)d->audio_fmt ; break ; case SNDCTL_DSP_SETFMT: /* sets _one_ format */ /* * when some card (SB16) is opened RDONLY or WRONLY, * only one of the fields is set, the other becomes 0. * This makes it possible to select DMA channels at runtime. */ if (d->play_fmt) d->play_fmt = *(int *)arg ; if (d->rec_fmt) d->rec_fmt = *(int *)arg ; splx(s); if (ask_init(d)) *(int *)arg = d->play_fmt ; break ; case SNDCTL_DSP_SUBDIVIDE: /* XXX watch out, this is RW! */ DEB(printf("SNDCTL_DSP_SUBDIVIDE yet unimplemented\n");) break; case SNDCTL_DSP_SETFRAGMENT: /* XXX watch out, this is RW! */ DEB(printf("SNDCTL_DSP_SETFRAGMENT 0x%08x\n", *(int *)arg)); { int bytes, count; bytes = *(int *)arg & 0xffff ; count = ( *(int *)arg >> 16) & 0xffff ; if (bytes > 15) bytes = 15 ; bytes = 1 << bytes ; if (bytes <= 1) { /* means no blocks */ d->flags &= ~SND_F_HAS_SIZE ; } else { RANGE (bytes, 40, d->dbuf_out.bufsize /4); d->play_blocksize = d->rec_blocksize = bytes & ~3 ; /* align to multiple of 4 */ d->flags |= SND_F_HAS_SIZE ; } splx(s); ask_init(d); #if 0 /* XXX todo: set the buffer size to the # of fragments */ count = d->dbuf_in.bufsize / d->play_blocksize ; bytes = ffs(d->play_blocksize) - 1; /* * don't change arg, since it's fake anyways and some * programs might fail if we do. */ *(int *)arg = (count << 16) | bytes ; #endif } break ; case SNDCTL_DSP_GETISPACE: /* return space available in the input queue */ { audio_buf_info *a = (audio_buf_info *)arg; snd_dbuf *b = &(d->dbuf_in); if (b->dl) { if (d->special_dma) d->callback(d, SND_CB_RD | SND_CB_DMAUPDATE) ; else dsp_rd_dmaupdate( b ); } a->bytes = d->dbuf_in.fl ; a->fragments = 1 ; a->fragstotal = b->bufsize / d->rec_blocksize ; a->fragsize = d->rec_blocksize ; } break ; case SNDCTL_DSP_GETOSPACE: /* return space available in the output queue */ { audio_buf_info *a = (audio_buf_info *)arg; snd_dbuf *b = &(d->dbuf_out); if (b->dl) { if (d->special_dma) d->callback(d, SND_CB_WR | SND_CB_DMAUPDATE) ; else dsp_wr_dmaupdate( b ); } a->bytes = d->dbuf_out.fl ; a->fragments = 1 ; a->fragstotal = b->bufsize / d->play_blocksize ; a->fragsize = d->play_blocksize ; } break ; case SNDCTL_DSP_GETIPTR: { count_info *a = (count_info *)arg; snd_dbuf *b = &(d->dbuf_in); if (b->dl) { if (d->special_dma) d->callback(d, SND_CB_RD | SND_CB_DMAUPDATE) ; else dsp_rd_dmaupdate( b ); } a->bytes = b->total; a->blocks = (b->total - b->prev_total + d->rec_blocksize -1 ) / d->rec_blocksize ; a->ptr = b->fp ; /* XXX not sure... */ b->prev_total = b->total ; } break; case SNDCTL_DSP_GETOPTR: { count_info *a = (count_info *)arg; snd_dbuf *b = &(d->dbuf_out); if (b->dl) { if (d->special_dma) d->callback(d, SND_CB_WR | SND_CB_DMAUPDATE) ; else dsp_wr_dmaupdate( b ); } a->bytes = b->total; a->blocks = (b->total - b->prev_total /* +d->play_blocksize -1*/ ) / d->play_blocksize ; a->ptr = b->rp ; /* XXX not sure... */ b->prev_total = b->total ; } break; case SNDCTL_DSP_GETCAPS : *(int *) arg = 0x0 ; /* revision */ if (FULL_DUPLEX(d)) *(int *) arg |= DSP_CAP_DUPLEX ; *(int *) arg |= DSP_CAP_REALTIME ; break ; case SOUND_PCM_READ_BITS: if (d->play_fmt == AFMT_S16_LE) *(int *) arg = 16 ; else *(int *) arg = 8 ; break ; /* * mixer calls */ case SOUND_MIXER_READ_DEVMASK : case SOUND_MIXER_READ_CAPS : case SOUND_MIXER_READ_STEREODEVS : *(int *)arg = d->mix_devs; break ; case SOUND_MIXER_READ_RECMASK : *(int *)arg = d->mix_rec_devs; break ; case SOUND_MIXER_READ_RECSRC : *(int *)arg = d->mix_recsrc ; break; default: DEB(printf("default ioctl snd%d subdev %d fn 0x%08x fail\n", unit, dev & 0xf, cmd)); ret = EINVAL; break ; } splx(s); return ret ; } /* * we use the name 'select', but the new "poll" interface this is * really sndpoll. Second arg for poll is not "rw" but "events" */ int sndselect(dev_t i_dev, int rw, struct proc * p) { int dev, unit, c = 1 /* default: success */ ; snddev_info *d ; u_long flags; dev = minor(i_dev); d = get_snddev_info(dev, &unit); DEB(printf("sndselect dev 0x%04x rw 0x%08x\n",i_dev, rw)); if (d == NULL ) /* should not happen! */ return (ENXIO) ; if (d->select == NULL) return ( (rw & (POLLIN|POLLOUT|POLLRDNORM|POLLWRNORM)) | POLLHUP); else if (d->select != sndselect ) return d->select(i_dev, rw, p); else { /* handle it here with the generic code */ /* * if the user selected a block size, then we want to use the * device as a block device, and select will return ready when * we have a full block. * In all other cases, select will return when 1 byte is ready. */ int lim = 1; int revents = 0 ; if (rw & (POLLOUT | POLLWRNORM) ) { if ( d->flags & SND_F_HAS_SIZE ) lim = d->play_blocksize ; /* XXX fix the test here for half duplex devices */ if (1 /* write is compatible with current mode */) { flags = spltty(); if (d->dbuf_out.dl) { if (d->special_dma) d->callback(d, SND_CB_WR | SND_CB_DMAUPDATE) ; else dsp_wr_dmaupdate(&(d->dbuf_out)); } c = d->dbuf_out.fl ; if (c < lim) /* no space available */ selrecord(p, & (d->wsel)); else revents |= rw & (POLLOUT | POLLWRNORM); splx(flags); } } if (rw & (POLLIN | POLLRDNORM)) { if ( d->flags & SND_F_HAS_SIZE ) lim = d->rec_blocksize ; /* XXX fix the test here */ if (1 /* read is compatible with current mode */) { flags = spltty(); if ( d->dbuf_in.dl == 0 ) /* dma idle, restart it */ dsp_rdintr(d); else { if (d->special_dma) d->callback(d, SND_CB_RD | SND_CB_DMAUPDATE) ; else dsp_rd_dmaupdate(&(d->dbuf_in)); } c = d->dbuf_in.rl ; if (c < lim) /* no data available */ selrecord(p, & (d->rsel)); else revents |= rw & (POLLIN | POLLRDNORM); splx(flags); } DEB(printf("sndselect on read: %d >= %d flags 0x%08x\n", c, lim, d->flags)); return c < lim ? 0 : 1 ; } return revents; } return ENXIO ; /* notreached */ } /* * The mmap interface allows access to the play and read buffer, * plus the device descriptor. * The various blocks are accessible at the following offsets: * * 0x00000000 ( 0 ) : write buffer ; * 0x01000000 (16 MB) : read buffer ; * 0x02000000 (32 MB) : device descriptor (dangerous!) * * WARNING: the mmap routines assume memory areas are aligned. This * is true (probably) for the dma buffers, but likely false for the * device descriptor. As a consequence, we do not know where it is * located in the requested area. */ #include #include #include #include #include #include static int sndmmap(dev_t dev, vm_offset_t offset, int nprot) { snddev_info *d = get_snddev_info(dev, NULL); DEB(printf("sndmmap d 0x%p dev 0x%04x ofs 0x%08x nprot 0x%08x\n", d, dev, offset, nprot)); if (d == NULL || nprot & PROT_EXEC) return -1 ; /* forbidden */ if (offset >= d->dbuf_out.bufsize && (nprot & PROT_WRITE) ) return -1 ; /* can only write to the first block */ if (offset < d->dbuf_out.bufsize) return i386_btop(vtophys(d->dbuf_out.buf + offset)); offset -= 1 << 24; if ( (offset >= 0) && (offset < d->dbuf_in.bufsize)) return i386_btop(vtophys(d->dbuf_in.buf + offset)); offset -= 1 << 24; if ( (offset >= 0) && (offset < 0x2000)) { return i386_btop(vtophys( ((int)d & ~0xfff) + offset)); } return -1 ; } /* * ask_init sets the init flag in the device descriptor, and * possibly calls the appropriate callback routine, returning 1 * if the callback was successful. This enables ioctls handler for * rw parameters to read back the updated value. * Since the init callback can be slow, ask_init() should be called * with interrupts enabled. */ int ask_init(snddev_info *d) { u_long s; if ( d->callback == NULL ) return 0 ; s = spltty(); if ( d->flags & SND_F_PENDING_IO || d->dbuf_out.dl || d->dbuf_in.dl ) { /* cannot do it now, record the request and return */ d->flags |= SND_F_INIT ; splx(s); return 0 ; } else { splx(s); d->callback(d, SND_CB_INIT ); return 1; } } /* * these are the functions for the soundstat device. We copy parameters * from the device info structure to static variables, and from there * back to the structure when done. */ static void init_status(snddev_info *d) { /* * Write the status information to the status_buf and update * status_len. There is a limit of SNDSTAT_BUF_SIZE bytes for the data. */ int i; if (status_len != 0) /* only do init once */ return ; snprintf(status_buf, sizeof(status_buf), "FreeBSD Audio Driver (981002) " __DATE__ " " __TIME__ "\n" "Installed devices:\n"); for (i = 0; i < NPCM_MAX; i++) { if (pcm_info[i].open) snprintf(status_buf + strlen(status_buf), sizeof(status_buf) - strlen(status_buf), "pcm%d: <%s> at 0x%x irq %d dma %d:%d\n", i, pcm_info[i].name, pcm_info[i].io_base, pcm_info[i].irq, pcm_info[i].dbuf_out.chan, pcm_info[i].dbuf_in.chan); if (midi_info[i].open) snprintf(status_buf + strlen(status_buf), sizeof(status_buf) - strlen(status_buf), "midi%d: <%s> at 0x%x irq %d dma %d:%d\n", i, midi_info[i].name, midi_info[i].io_base, midi_info[i].irq, midi_info[i].dbuf_out.chan, midi_info[i].dbuf_in.chan); if (pcm_info[i].synth_base) { char *s = "???"; switch (pcm_info[i].synth_type) { case 2 : s = "OPL2"; break; case 3 : s = "OPL3"; break; case 4 : s = "OPL4"; break; } snprintf(status_buf + strlen(status_buf), sizeof(status_buf) - strlen(status_buf), "sequencer%d: <%s> at 0x%x (not functional)\n", i, s, pcm_info[i].synth_base); } } status_len = strlen(status_buf) ; } /* * finally, some "libraries" */ /* * isa_dmastatus1() is a wrapper for isa_dmastatus(), which * might return -1 or -2 in some cases (errors). Since for the * user code it is more comfortable not to check for these cases, * negative values are mapped back to 0 (which is reasonable). */ int isa_dmastatus1(int channel) { int r = isa_dmastatus(channel); if (r<0) r = 0; return r; } /* * snd_conflict scans already-attached boards to see if * the current address is conflicting with one of the already * assigned ones. Returns 1 if a conflict is detected. */ int snd_conflict(int io_base) { int i; for (i=0; i< NPCM_MAX ; i++) { if ( (io_base == pcm_info[i].io_base ) || (io_base == pcm_info[i].alt_base ) || (io_base == pcm_info[i].conf_base) || (io_base == pcm_info[i].mix_base ) || (io_base == pcm_info[i].midi_base) || (io_base == pcm_info[i].synth_base) ) { BVDDB(printf("device at 0x%x already attached as unit %d\n", io_base, i);) return 1 ; } } return 0; } void snd_set_blocksize(snddev_info *d) { int tmp ; /* * compute the sample size, and possibly * set the blocksize so as to guarantee approx 1/4s * between callbacks. */ tmp = 1 ; if (d->flags & SND_F_STEREO) tmp += tmp; if (d->play_fmt & (AFMT_S16_LE|AFMT_U16_LE)) tmp += tmp; d->dbuf_out.sample_size = tmp ; tmp = tmp * d->play_speed; if ( (d->flags & SND_F_HAS_SIZE) == 0) { d->play_blocksize = (tmp / 4) & ~3; /* 0.25s, aligned to 4 */ RANGE (d->play_blocksize, 1024, (d->bufsize / 4) & ~3); } tmp = 1 ; if (d->flags & SND_F_STEREO) tmp += tmp; if (d->rec_fmt & (AFMT_S16_LE|AFMT_U16_LE)) tmp += tmp; tmp = tmp * d->rec_speed; d->dbuf_in.sample_size = tmp ; if ( (d->flags & SND_F_HAS_SIZE) == 0) { d->rec_blocksize = (tmp / 4) & ~3; /* 0.25s, aligned to 4 */ RANGE (d->rec_blocksize, 1024, (d->bufsize / 4) & ~3); } } /* * The various mixers use a variety of bitmasks etc. The Voxware * driver had a very nice technique to describe a mixer and interface * to it. A table defines, for each channel, which register, bits, * offset, polarity to use. This procedure creates the new value * using the table and the old value. */ void change_bits(mixer_tab *t, u_char *regval, int dev, int chn, int newval) { u_char mask; int shift; DEB(printf("ch_bits dev %d ch %d val %d old 0x%02x " "r %d p %d bit %d off %d\n", dev, chn, newval, *regval, (*t)[dev][chn].regno, (*t)[dev][chn].polarity, (*t)[dev][chn].nbits, (*t)[dev][chn].bitoffs ) ); if ( (*t)[dev][chn].polarity == 1) /* reverse */ newval = 100 - newval ; mask = (1 << (*t)[dev][chn].nbits) - 1; newval = (int) ((newval * mask) + 50) / 100; /* Scale it */ shift = (*t)[dev][chn].bitoffs /*- (*t)[dev][LEFT_CHN].nbits + 1*/; *regval &= ~(mask << shift); /* Filter out the previous value */ *regval |= (newval & mask) << shift; /* Set the new value */ } /* * code for translating between U8 and ULAW. Needed to support * /dev/audio on the SoundBlaster. Actually, we would also need * ulaw -> 16 bits (for the soundblaster as well, when used in * full-duplex) */ void translate_bytes (u_char *table, u_char *buff, int n) { u_long i; if (n <= 0) return; for (i = 0; i < n; ++i) buff[i] = table[buff[i]]; } #endif /* NPCM > 0 */ Index: head/sys/i386/isa/sound/ad1848.c =================================================================== --- head/sys/i386/isa/sound/ad1848.c (revision 45719) +++ head/sys/i386/isa/sound/ad1848.c (revision 45720) @@ -1,1858 +1,1863 @@ /* * sound/ad1848.c * * Modified by Luigi Rizzo (luigi@iet.unipi.it) * * The low level driver for the AD1848/CS4248 codec chip which is used for * example in the MS Sound System. * * The CS4231 which is used in the GUS MAX and some other cards is upwards * compatible with AD1848 and this driver is able to drive it. * * CS4231A and AD1845 are upward compatible with CS4231. However the new * features of these chips are different. * * CS4232 is a PnP audio chip which contains a CS4231A (and SB, MPU). CS4232A is * an improved version of CS4232. * * CS4236 is also a PnP audio chip similar to the 4232 * * OPTi931 is another high-end 1848-type chip. It differs in the use * of the high 16 registers and configuration stuff. Luckily, being a * PnP device, we can avoid black magic to identify the chip and be * sure of its identity. * * Copyright by Hannu Savolainen 1994, 1995 * * 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. * * Modified: Riccardo Facchetti 24 Mar 1995 - Added the Audio Excel DSP 16 * initialization routine. */ #define DEB(x) #define DEB1(x) #include #if defined(CONFIG_AD1848) #include #include #if defined(CONFIG_CS4232) extern struct isa_driver cssdriver; #else extern struct isa_driver mssdriver; #endif extern void IwaveStopDma(BYTE path); typedef struct { int base; int irq; int dual_dma; /* 1, when two DMA channels allocated */ u_char MCE_bit; u_char saved_regs[16]; int speed; u_char speed_bits; int channels; int audio_format; u_char format_bits; u_long xfer_count; int irq_mode; int intr_active; int opened; char *chip_name; int mode; #define MD_1848 1 #define MD_4231 2 #define MD_4231A 3 #define MD_4236 4 #define MD_1845 5 #define MD_MAXMODE 6 /* Mixer parameters */ int recmask; int supported_devices; int supported_rec_devices; u_short levels[32]; int dev_no; volatile u_long timer_ticks; int timer_running; int irq_ok; sound_os_info *osp; } ad1848_info; static int nr_ad1848_devs = 0; static volatile char irq2dev[17] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; static int timer_installed = -1; static int mute_flag = 0; static char mixer2codec[MAX_MIXER_DEV] = {0}; static int ad_format_mask[MD_MAXMODE /* devc->mode */ ] = { /* 0 - none */ 0, /* 1 - AD1848 */ AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW, /* * AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_U16_LE | * AFMT_IMA_ADPCM, */ /* 2 - CS4231 */ AFMT_U8 | AFMT_S16_LE | AFMT_U16_LE, /* * AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_U16_LE | * AFMT_IMA_ADPCM, */ /* 3 - CS4231A */ AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW, /* 4 - AD1845 */ AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW, /* 5 - CS4236 */ AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW, }; static ad1848_info dev_info[MAX_AUDIO_DEV]; #define io_Index_Addr(d) ((d)->base) #define io_Indexed_Data(d) ((d)->base+1) #define io_Status(d) ((d)->base+2) #define io_Polled_IO(d) ((d)->base+3) static int ad1848_open(int dev, int mode); static void ad1848_close(int dev); static int ad1848_ioctl(int dev, u_int cmd, ioctl_arg arg, int local); static void ad1848_output_block(int dev, u_long buf, int count, int intrflag, int dma_restart); static void ad1848_start_input(int dev, u_long buf, int count, int intrflag, int dma_restart); static int ad1848_prepare_for_IO(int dev, int bsize, int bcount); static void ad1848_reset(int dev); static void ad1848_halt(int dev); static void ad1848_halt_input(int dev); static void ad1848_halt_output(int dev); static void ad1848_trigger(int dev, int bits); static int ad1848_tmr_install(int dev); static void ad1848_tmr_reprogram(int dev); /* * AD_WAIT_INIT waits if we are initializing the board and we cannot modify * its settings */ #define AD_WAIT_INIT(x) {int t=x; while(t>0 && inb(devc->base) == 0x80) t-- ; } short ipri_to_irq(u_short ipri); void adintr(unit) { +#if 1 + /* this isn't ideal but should work */ + ad1848_interrupt(-1); +#else static short unit_to_irq[4] = {9, -1, -1, -1}; struct isa_device *dev; if (unit_to_irq[unit] > 0) ad1848_interrupt(unit_to_irq[unit]); else { #if defined(CONFIG_CS4232) dev = find_isadev(isa_devtab_null, &cssdriver, unit); #else dev = find_isadev(isa_devtab_null, &mssdriver, unit); #endif if (!dev) printf("ad1848: Couldn't determine unit\n"); else { unit_to_irq[unit] = ipri_to_irq(dev->id_irq); ad1848_interrupt(unit_to_irq[unit]); } } +#endif } static int ad_read(ad1848_info * devc, int reg) { u_long flags; int x; AD_WAIT_INIT(900000); flags = splhigh(); outb(io_Index_Addr(devc), (u_char) (reg & 0xff) | devc->MCE_bit); x = inb(io_Indexed_Data(devc)); splx(flags); return x; } static void ad_write(ad1848_info * devc, int reg, u_char data) { u_long flags; AD_WAIT_INIT(90000); flags = splhigh(); outb(io_Index_Addr(devc), (u_char) (reg & 0xff) | devc->MCE_bit); outb(io_Indexed_Data(devc), (u_char) (data & 0xff)); splx(flags); } static void wait_for_calibration(ad1848_info * devc) { int timeout = 0; /* * Wait until the auto calibration process has finished. * * 1) Wait until the chip becomes ready (reads don't return 0x80). * 2) Wait until the ACI bit of I11 gets on and then off. */ AD_WAIT_INIT(100000); if (inb(devc->base) & 0x80) printf("ad1848: Auto calibration timed out(1).\n"); timeout = 100; while (timeout > 0 && !(ad_read(devc, 11) & 0x20)) timeout--; if (!(ad_read(devc, 11) & 0x20)) return; timeout = 20000; while (timeout > 0 && ad_read(devc, 11) & 0x20) timeout--; if (ad_read(devc, 11) & 0x20) printf("ad1848: Auto calibration timed out(3).\n"); } static void ad_mute(ad1848_info * devc) { int i; u_char prev; mute_flag = 1; /* * Save old register settings and mute output channels */ for (i = 6; i < 8; i++) { prev = devc->saved_regs[i] = ad_read(devc, i); ad_write(devc, i, prev | 0x80); } } static void ad_unmute(ad1848_info * devc) { int i; mute_flag = 0; /* * Restore back old volume registers (unmute) */ for (i = 6; i < 8; i++) ad_write(devc, i, devc->saved_regs[i] & ~0x80); } static void ad_enter_MCE(ad1848_info * devc) { u_long flags; AD_WAIT_INIT(1000); devc->MCE_bit = 0x40; flags = splhigh(); if ( ( inb(io_Index_Addr(devc)) & 0x40) == 0 ) outb(io_Index_Addr(devc), devc->MCE_bit); splx(flags); } static void ad_leave_MCE(ad1848_info * devc) { u_long flags; u_char prev; AD_WAIT_INIT(1000); flags = splhigh(); devc->MCE_bit = 0x00; prev = inb(io_Index_Addr(devc)); /* XXX the next call is redundant ? */ outb(io_Index_Addr(devc), 0x00); /* Clear the MCE bit */ if ((prev & 0x40) == 0) { /* Not in MCE mode */ splx(flags); return; } outb(io_Index_Addr(devc), 0x00); /* Clear the MCE bit */ wait_for_calibration(devc); splx(flags); } static int ad1848_set_recmask(ad1848_info * devc, int mask) { u_char recdev; int i, n; mask &= devc->supported_rec_devices; n = 0; for (i = 0; i < 32; i++)/* Count selected device bits */ if (mask & (1 << i)) n++; if (n == 0) mask = SOUND_MASK_MIC; else if (n != 1) { /* Too many devices selected */ mask &= ~devc->recmask; /* Filter out active settings */ n = 0; for (i = 0; i < 32; i++) /* Count selected device bits */ if (mask & (1 << i)) n++; if (n != 1) mask = SOUND_MASK_MIC; } switch (mask) { case SOUND_MASK_MIC: recdev = 2; break; case SOUND_MASK_LINE: case SOUND_MASK_LINE3: recdev = 0; break; case SOUND_MASK_CD: case SOUND_MASK_LINE1: recdev = 1; break; case SOUND_MASK_IMIX: recdev = 3; break; default: mask = SOUND_MASK_MIC; recdev = 2; } recdev <<= 6; ad_write(devc, 0, (ad_read(devc, 0) & 0x3f) | recdev); ad_write(devc, 1, (ad_read(devc, 1) & 0x3f) | recdev); devc->recmask = mask; return mask; } static void change_bits(u_char *regval, int dev, int chn, int newval) { u_char mask; int shift; if (mix_devices[dev][chn].polarity == 1) /* Reverse */ newval = 100 - newval; mask = (1 << mix_devices[dev][chn].nbits) - 1; shift = mix_devices[dev][chn].bitpos; newval = (int) ((newval * mask) + 50) / 100; /* Scale it */ *regval &= ~(mask << shift); /* Clear bits */ *regval |= (newval & mask) << shift; /* Set new value */ } static int ad1848_mixer_get(ad1848_info * devc, int dev) { if (!((1 << dev) & devc->supported_devices)) return -(EINVAL); return devc->levels[dev]; } #define CLMICI 0x00781601 #define CRMICI 0x00791701 static int ad1848_mixer_set(ad1848_info * devc, int dev, int value) { int left = value & 0x000000ff; int right = (value & 0x0000ff00) >> 8; int retvol; int regoffs; u_char val; /* u_char clci, crmici, clmici, clici, crici; */ if (left > 100) left = 100; if (right > 100) right = 100; if (mix_devices[dev][RIGHT_CHN].nbits == 0) /* Mono control */ right = left; retvol = left | (right << 8); /* Scale volumes */ left = mix_cvt[left]; right = mix_cvt[right]; /* Scale it again */ left = mix_cvt[left]; right = mix_cvt[right]; if (dev > 31) return -(EINVAL); if (!(devc->supported_devices & (1 << dev))) return -(EINVAL); if (mix_devices[dev][LEFT_CHN].nbits == 0) return -(EINVAL); devc->levels[dev] = retvol; /* * Set the left channel */ /* IwaveCodecMode(CODEC_MODE3); Default codec mode */ regoffs = mix_devices[dev][LEFT_CHN].regno; val = ad_read(devc, regoffs); change_bits(&val, dev, LEFT_CHN, left); ad_write(devc, regoffs, val); devc->saved_regs[regoffs] = val; /* * Set the right channel */ if (mix_devices[dev][RIGHT_CHN].nbits == 0) return retvol; /* Was just a mono channel */ regoffs = mix_devices[dev][RIGHT_CHN].regno; val = ad_read(devc, regoffs); change_bits(&val, dev, RIGHT_CHN, right); ad_write(devc, regoffs, val); devc->saved_regs[regoffs] = val; return retvol; } static void ad1848_mixer_reset(ad1848_info * devc) { int i; devc->recmask = 0; if (devc->mode != MD_1848) devc->supported_devices = MODE2_MIXER_DEVICES; else devc->supported_devices = MODE1_MIXER_DEVICES; devc->supported_rec_devices = MODE1_REC_DEVICES; for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) if (devc->supported_devices & (1 << i)) ad1848_mixer_set(devc, i, default_mixer_levels[i]); ad1848_set_recmask(devc, SOUND_MASK_MIC); } static int ad1848_mixer_ioctl(int dev, u_int cmd, ioctl_arg arg) { ad1848_info *devc; int codec_dev = mixer2codec[dev]; if (!codec_dev) return -(ENXIO); codec_dev--; devc = (ad1848_info *) audio_devs[codec_dev]->devc; if (((cmd >> 8) & 0xff) == 'M') { if (cmd & IOC_IN) switch (cmd & 0xff) { case SOUND_MIXER_RECSRC: return *(int *) arg = ad1848_set_recmask(devc, (*(int *) arg)); break; default: return *(int *) arg = ad1848_mixer_set(devc, cmd & 0xff, (*(int *) arg)); } else switch (cmd & 0xff) { /* Return parameters */ case SOUND_MIXER_RECSRC: return *(int *) arg = devc->recmask; break; case SOUND_MIXER_DEVMASK: return *(int *) arg = devc->supported_devices; break; case SOUND_MIXER_STEREODEVS: return *(int *) arg = devc->supported_devices & ~(SOUND_MASK_SPEAKER | SOUND_MASK_IMIX); break; case SOUND_MIXER_RECMASK: return *(int *) arg = devc->supported_rec_devices; break; case SOUND_MIXER_CAPS: return *(int *) arg = SOUND_CAP_EXCL_INPUT; break; default: return *(int *) arg = ad1848_mixer_get(devc, cmd & 0xff); } } else return -(EINVAL); } static struct audio_operations ad1848_pcm_operations[MAX_AUDIO_DEV] = { { "Generic AD1848 codec", /* DMA_AUTOMODE | DMA_DUPLEX, */ DMA_AUTOMODE, AFMT_U8, /* Will be set later */ NULL, ad1848_open, ad1848_close, ad1848_output_block, ad1848_start_input, ad1848_ioctl, ad1848_prepare_for_IO, ad1848_prepare_for_IO, ad1848_reset, ad1848_halt, NULL, NULL, ad1848_halt_input, ad1848_halt_output, ad1848_trigger } }; static struct mixer_operations ad1848_mixer_operations = { "AD1848/CS4248/CS4231/CS4236", ad1848_mixer_ioctl }; static int ad1848_open(int dev, int mode) { ad1848_info *devc = NULL; u_long flags; int otherside = audio_devs[dev]->otherside; if (dev < 0 || dev >= num_audiodevs) return -(ENXIO); if (otherside != -1) { if (audio_devs[otherside]->busy) return -(EBUSY); } if (audio_devs[dev]->busy) return -(EBUSY); devc = (ad1848_info *) audio_devs[dev]->devc; flags = splhigh(); if (audio_devs[dev]->busy) { splx(flags); return -(EBUSY); } devc->dual_dma = 0; if (audio_devs[dev]->flags & DMA_DUPLEX) { devc->dual_dma = 1; } devc->intr_active = 0; audio_devs[dev]->busy = 1; devc->irq_mode = 0; ad1848_trigger(dev, 0); splx(flags); /* * Mute output until the playback really starts. This decreases * clicking. */ ad_mute(devc); return 0; } static void ad1848_close(int dev) { u_long flags; ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; int otherside = audio_devs[dev]->otherside; if (otherside != -1) { if (audio_devs[otherside]->busy) return; } DEB(printf("ad1848_close(void)\n")); flags = splhigh(); ad_mute(devc); ad_write(devc, 9, ad_read(devc, 9) & ~0x1); outb(io_Status(devc), 0); /* Clear interrupt status */ /* * ad_write (devc, 15,0); ad_write (devc, 14,0); */ devc->irq_mode &= ~PCM_ENABLE_OUTPUT; devc->intr_active = 0; ad1848_reset(dev); devc->opened = 0; devc->irq_mode = 0; audio_devs[dev]->busy = 0; ad_unmute(devc); splx(flags); } static int set_speed(ad1848_info * devc, int arg) { /* * The sampling speed is encoded in the least significant nible of * I8. The LSB selects the clock source (0=24.576 MHz, 1=16.9344 Mhz) * and other three bits select the divisor (indirectly): * * The available speeds are in the following table. Keep the speeds in * the increasing order. */ typedef struct { int speed; u_char bits; } speed_struct; static speed_struct speed_table[] = { {5510, (0 << 1) | 1}, {5510, (0 << 1) | 1}, {6620, (7 << 1) | 1}, {8000, (0 << 1) | 0}, {9600, (7 << 1) | 0}, {11025, (1 << 1) | 1}, {16000, (1 << 1) | 0}, {18900, (2 << 1) | 1}, {22050, (3 << 1) | 1}, {27420, (2 << 1) | 0}, {32000, (3 << 1) | 0}, {33075, (6 << 1) | 1}, {37800, (4 << 1) | 1}, {44100, (5 << 1) | 1}, {48000, (6 << 1) | 0} }; int i, n, selected = -1; n = sizeof(speed_table) / sizeof(speed_struct); if (devc->mode == MD_1845) { /* AD1845 has different timer than others */ RANGE (arg, 4000, 50000) ; devc->speed = arg; devc->speed_bits = speed_table[selected].bits; return devc->speed; } if (arg < speed_table[0].speed) selected = 0; if (arg > speed_table[n - 1].speed) selected = n - 1; for (i = 1 /* really */ ; selected == -1 && i < n; i++) if (speed_table[i].speed == arg) selected = i; else if (speed_table[i].speed > arg) { int diff1, diff2; diff1 = arg - speed_table[i - 1].speed; diff2 = speed_table[i].speed - arg; if (diff1 < diff2) selected = i - 1; else selected = i; } if (selected == -1) { printf("ad1848: Can't find speed???\n"); selected = 3; } devc->speed = speed_table[selected].speed; devc->speed_bits = speed_table[selected].bits; return devc->speed; } static int set_channels(ad1848_info * devc, int arg) { if (arg != 1 && arg != 2) return devc->channels; devc->channels = arg; return arg; } static int set_format(ad1848_info * devc, int arg) { static struct format_tbl { int format; u_char bits; } format2bits[] = { { 0, 0 } , { AFMT_MU_LAW, 1 } , { AFMT_A_LAW, 3 } , { AFMT_IMA_ADPCM, 5 } , { AFMT_U8, 0 } , { AFMT_S16_LE, 2 } , { AFMT_S16_BE, 6 } , { AFMT_S8, 0 } , { AFMT_U16_LE, 0 } , { AFMT_U16_BE, 0 } }; int i, n = sizeof(format2bits) / sizeof(struct format_tbl); if (!(arg & ad_format_mask[devc->mode])) arg = AFMT_U8; devc->audio_format = arg; for (i = 0; i < n; i++) if (format2bits[i].format == arg) { if ((devc->format_bits = format2bits[i].bits) == 0) return devc->audio_format = AFMT_U8; /* Was not supported */ return arg; } /* Still hanging here. Something must be terribly wrong */ devc->format_bits = 0; return devc->audio_format = AFMT_U8; } /* XXX check what is arg, (int) or *(int *) lr970705 */ static int ad1848_ioctl(int dev, u_int cmd, ioctl_arg arg, int local) { ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; switch (cmd) { case SOUND_PCM_WRITE_RATE: if (local) return set_speed(devc, (int) arg); return *(int *) arg = set_speed(devc, (*(int *) arg)); case SOUND_PCM_READ_RATE: if (local) return devc->speed; return *(int *) arg = devc->speed; case SNDCTL_DSP_STEREO: if (local) return set_channels(devc, (int) arg + 1) - 1; return *(int *) arg = set_channels(devc, (*(int *) arg) + 1) - 1; case SOUND_PCM_WRITE_CHANNELS: if (local) return set_channels(devc, (int) arg); return *(int *) arg = set_channels(devc, (*(int *) arg)); case SOUND_PCM_READ_CHANNELS: if (local) return devc->channels; return *(int *) arg = devc->channels; case SNDCTL_DSP_SAMPLESIZE: if (local) return set_format(devc, (int) arg); return *(int *) arg = set_format(devc, (*(int *) arg)); case SOUND_PCM_READ_BITS: if (local) return devc->audio_format; return *(int *) arg = devc->audio_format; case FIOASYNC: if (local) return 1; return *(int *) arg = 1; case FIONBIO: if (local) return 1; return *(int *) arg = 1; default:; } return -(EINVAL); } static void ad1848_output_block(int dev, u_long buf, int count, int intrflag, int dma_restart) { u_long flags, cnt; ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; cnt = count; if (devc->audio_format == AFMT_IMA_ADPCM) { cnt /= 4; } else { if (devc->audio_format & (AFMT_S16_LE | AFMT_S16_BE)) /* 16 bit data */ cnt >>= 1; } if (devc->channels > 1) cnt >>= 1; cnt--; if (mute_flag) ad_unmute(devc); if ( devc->irq_mode & PCM_ENABLE_OUTPUT && audio_devs[dev]->flags & DMA_AUTOMODE && intrflag && cnt == devc->xfer_count) { devc->irq_mode |= PCM_ENABLE_OUTPUT; devc->intr_active = 1; } flags = splhigh(); if (dma_restart) { DMAbuf_start_dma(dev, buf, count, 1); } ad_write(devc, 15, (u_char) (cnt & 0xff)); ad_write(devc, 14, (u_char) ((cnt >> 8) & 0xff)); devc->xfer_count = cnt; devc->irq_mode |= PCM_ENABLE_OUTPUT; devc->intr_active = 1; splx(flags); } static void ad1848_start_input(int dev, u_long buf, int count, int intrflag, int dma_restart) { u_long flags, cnt; ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; cnt = count; if (devc->audio_format == AFMT_IMA_ADPCM) cnt /= 4; else if (devc->audio_format & (AFMT_S16_LE | AFMT_S16_BE)) /* 16 bit data */ cnt >>= 1; if (devc->channels > 1) cnt >>= 1; cnt--; if ( devc->irq_mode & PCM_ENABLE_INPUT && audio_devs[dev]->flags & DMA_AUTOMODE && intrflag && cnt == devc->xfer_count) { devc->irq_mode |= PCM_ENABLE_INPUT; devc->intr_active = 1; return; /* Auto DMA mode on. No need to react */ } flags = splhigh(); if (dma_restart) { /* ad1848_halt (dev); */ DMAbuf_start_dma(dev, buf, count, 0); } if (devc->mode == MD_1848 || !devc->dual_dma) {/* Single DMA chan. mode */ ad_write(devc, 15, (u_char) (cnt & 0xff)); ad_write(devc, 14, (u_char) ((cnt >> 8) & 0xff)); } else { /* Dual DMA channel mode */ ad_write(devc, 31, (u_char) (cnt & 0xff)); ad_write(devc, 30, (u_char) ((cnt >> 8) & 0xff)); } /* ad_write (devc, 9, ad_read (devc, 9) | 0x02); *//* Capture enable */ ad_unmute(devc); devc->xfer_count = cnt; devc->irq_mode |= PCM_ENABLE_INPUT; devc->intr_active = 1; splx(flags); } static int ad1848_prepare_for_IO(int dev, int bsize, int bcount) { u_char fs, old_fs; u_long flags; ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; if (devc->irq_mode) return 0; fs = devc->speed_bits | (devc->format_bits << 5); if (devc->channels > 1) fs |= 0x10; old_fs = fs; flags = splhigh(); if (devc->mode == MD_1845) { /* Use alternate speed select regs */ fs &= 0xf0; /* Mask off the rate select bits */ ad_write(devc, 22, (devc->speed >> 8) & 0xff); /* Speed MSB */ ad_write(devc, 23, devc->speed & 0xff); /* Speed LSB */ } ad_enter_MCE(devc); /* Enables changes to the format select reg */ ad_write(devc, 8, fs); /* * Write to I8 starts resyncronization. Wait until it completes. */ AD_WAIT_INIT(10000); /* * If mode == 2 (CS4231), set I28 also. It's the capture format * register. */ if (devc->mode != MD_1848) { ad_write(devc, 28, fs); /* * Write to I28 starts resyncronization. Wait until it completes. */ AD_WAIT_INIT(10000); } ad_write(devc, 9, ad_read(devc, 9) & ~0x08); ad_leave_MCE(devc); splx(flags); devc->xfer_count = 0; #ifdef CONFIG_SEQUENCER if (dev == timer_installed && devc->timer_running) if ((fs & 0x01) != (old_fs & 0x01)) { ad1848_tmr_reprogram(dev); } #endif return 0; } static void ad1848_reset(int dev) { ad1848_halt(dev); } static void ad1848_halt(int dev) { ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; u_long flags; int timeout; flags = splhigh(); ad_mute(devc); ad_write(devc, 9, ad_read(devc, 9) & ~0x03); /* Stop DMA */ ad_write(devc, 14, 0); /* Clear DMA counter */ ad_write(devc, 15, 0); /* Clear DMA counter */ if (devc->mode != MD_1848) { ad_write(devc, 30, 0); /* Clear DMA counter */ ad_write(devc, 31, 0); /* Clear DMA counter */ } for (timeout = 0; timeout < 1000 && !(inb(io_Status(devc)) & 0x01); timeout++); /* Wait for interrupt */ outb(io_Status(devc), 0); /* Clear interrupt status */ devc->irq_mode = 0; /* DMAbuf_reset_dma (dev); */ splx(flags); } static void ad1848_halt_input(int dev) { ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; u_long flags; u_char playing; if (devc->mode == MD_1848) { ad1848_halt(dev); return; } playing = ad_read(devc, 9); if (!(playing & 0x2)) return; flags = splhigh(); ad_mute(devc); ad_write(devc, 9, playing & ~0x02); /* Stop capture */ outb(io_Status(devc), 0); /* Clear interrupt status */ outb(io_Status(devc), 0); /* Clear interrupt status */ devc->irq_mode &= ~PCM_ENABLE_INPUT; splx(flags); } static void ad1848_halt_output(int dev) { ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; u_long flags; u_char playing; playing = ad_read(devc, 9); if (!(playing & 0x1)) { devc->irq_mode &= ~PCM_ENABLE_OUTPUT; return; } /* IwaveStopDma(PLAYBACK); */ if (devc->mode == MD_1848) { ad1848_halt(dev); return; } flags = splhigh(); /* ad_mute (devc); */ ad_write(devc, 9, playing & ~0x1); outb(io_Status(devc), 0); /* Clear interrupt status */ /* * ad_write (devc, 15,0); ad_write (devc, 14,0); */ devc->irq_mode &= ~PCM_ENABLE_OUTPUT; splx(flags); } static void ad1848_trigger(int dev, int state) { ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; u_long flags; u_char tmp; flags = splhigh(); state &= devc->irq_mode; tmp = ad_read(devc, 9) & ~0x03; if (state & PCM_ENABLE_INPUT) tmp |= 0x02; if (state & PCM_ENABLE_OUTPUT) { tmp |= 0x01; } ad_write(devc, 9, tmp); splx(flags); } int ad1848_detect(int io_base, int *ad_flags, sound_os_info * osp) { static int last_probe_addr=0, last_result=0; /* to avoid multiple probes*/ int i; ad1848_info *devc = &dev_info[nr_ad1848_devs]; u_char tmp, tmp1, tmp2 ; DDB(printf("ad1848_detect(%x)\n", io_base)); if (io_base == last_probe_addr) return last_result; else { last_result = 0; /* default value for detect */ last_probe_addr = io_base ; } if (ad_flags) *ad_flags = 0; if (nr_ad1848_devs >= MAX_AUDIO_DEV) { DDB(printf("ad1848 detect error - step 0\n")); return 0 ; } devc->base = io_base; devc->irq_ok = 0; devc->timer_running = 0; devc->MCE_bit = 0x40; devc->irq = 0; devc->opened = 0; devc->chip_name = "AD1848"; devc->mode = MD_1848; /* AD1848 or CS4248 */ devc->osp = osp; /* * Check that the I/O address is in use. * * The bit 0x80 of the base I/O port is known to be 0 after the chip has * performed its power on initialization. Just assume this has * happened before the OS is starting. * * If the I/O address is unused, it typically returns 0xff. */ DDB(printf("ad1848_detect() - step A\n")); if ((inb(devc->base) & 0x80) != 0x00) { /* Not a AD1848 */ DDB(printf("ad1848 detect error - step A," " inb(base) = 0x%02x, want 0XXX.XXXX\n", inb(devc->base))); return 0; } /* * Test if it's possible to change contents of the indirect * registers. Registers 0 and 1 are ADC volume registers. The bit * 0x10 is read only so try to avoid using it. */ DDB(printf("ad1848_detect() - step B, test indirect register\n")); ad_write(devc, 0, 0xaa); ad_write(devc, 1, 0x45);/* 0x55 with bit 0x10 clear */ tmp1 = ad_read(devc, 0) ; tmp2 = ad_read(devc, 1) ; if ( tmp1 != 0xaa || tmp2 != 0x45) { DDB(printf("ad1848 detect error - step B (0x%02x/0x%02x) want 0xaa/0x45\n", tmp1, tmp2)); return 0; } DDB(printf("ad1848_detect() - step C\n")); ad_write(devc, 0, 0x45); ad_write(devc, 1, 0xaa); tmp1 = ad_read(devc, 0) ; tmp2 = ad_read(devc, 1) ; if (tmp1 != 0x45 || tmp2 != 0xaa) { DDB(printf("ad1848 detect error - step C (%x/%x)\n", tmp1, tmp2)); return 0; } /* * The indirect register I12 has some read only bits. Lets try to * change them. */ DDB(printf("ad1848_detect() - step D, last 4 bits of I12 readonly\n")); tmp = ad_read(devc, 12); ad_write(devc, 12, (~tmp) & 0x0f); tmp1 = ad_read(devc, 12); if ((tmp & 0x0f) != (tmp1 & 0x0f)) { DDB(printf("ad1848 detect error - step D, I12 (0x%02x was 0x%02x)\n", tmp1, tmp)); return 0; } /* * NOTE! Last 4 bits of the reg I12 tell the chip revision. * 0x01=RevB * 0x0A=RevC. also CS4231/CS4231A and OPTi931 */ /* * The original AD1848/CS4248 has just 15 indirect registers. This * means that I0 and I16 should return the same value (etc.). Ensure * that the Mode2 enable bit of I12 is 0. Otherwise this test fails * with CS4231. */ DDB(printf("ad1848_detect() - step F\n")); ad_write(devc, 12, 0); /* Mode2=disabled */ for (i = 0; i < 16; i++) if ((tmp1 = ad_read(devc, i)) != (tmp2 = ad_read(devc, i + 16))) { DDB(printf("ad1848 detect warning - step F(I%d/0x%02x/0x%02x)\n", i, tmp1, tmp2)); /* * note - this seems to fail on the 4232 on I11. So we just break * rather than fail. */ break ; /* return 0; */ } /* * Try to switch the chip to mode2 (CS4231) by setting the MODE2 bit * (0x40). The bit 0x80 is always 1 in CS4248 and CS4231. * * On the OPTi931, however, I12 is readonly and only contains the * chip revision ID (as in the CS4231A). The upper bits return 0. */ DDB(printf("ad1848_detect() - step G\n")); ad_write(devc, 12, 0x40); /* Set mode2, clear 0x80 */ tmp1 = ad_read(devc, 12); if (tmp1 & 0x80) { if (ad_flags) *ad_flags |= AD_F_CS4248; devc->chip_name = "CS4248"; /* Our best knowledge just now */ } if ((tmp1 & 0xf0) == 0x00) { printf("this should be an OPTi931\n"); } else if ((tmp1 & 0xc0) == 0xC0) { /* * The 4231 has bit7=1 always, and bit6 we just set to 1. * We want to check that this is really a CS4231 * Verify that setting I0 doesn't change I16. */ DDB(printf("ad1848_detect() - step H\n")); ad_write(devc, 16, 0); /* Set I16 to known value */ ad_write(devc, 0, 0x45); if ((tmp1 = ad_read(devc, 16)) != 0x45) { /* No change -> CS4231? */ ad_write(devc, 0, 0xaa); if ((tmp1 = ad_read(devc, 16)) == 0xaa) { /* Rotten bits? */ DDB(printf("ad1848 detect error - step H(%x)\n", tmp1)); return 0; } /* * Verify that some bits of I25 are read only. */ DDB(printf("ad1848_detect() - step I\n")); tmp1 = ad_read(devc, 25); /* Original bits */ ad_write(devc, 25, ~tmp1); /* Invert all bits */ if ((ad_read(devc, 25) & 0xe7) == (tmp1 & 0xe7)) { int id; /* * It's at least CS4231 */ devc->chip_name = "CS4231"; devc->mode = MD_4231; /* * It could be an AD1845 or CS4231A as well. * CS4231 and AD1845 report the same revision info in I25 * while the CS4231A reports different. */ DDB(printf("ad1848_detect() - step I\n")); id = ad_read(devc, 25) & 0xe7; /* * b7-b5 = version number; * 100 : all CS4231 * 101 : CS4231A * * b2-b0 = chip id; */ switch (id) { case 0xa0: devc->chip_name = "CS4231A"; devc->mode = MD_4231A; break; case 0xa2: devc->chip_name = "CS4232"; devc->mode = MD_4231A; break; case 0xb2: /* strange: the 4231 data sheet says b4-b3 are XX * so this should be the same as 0xa2 */ devc->chip_name = "CS4232A"; devc->mode = MD_4231A; break; case 0x80: /* * It must be a CS4231 or AD1845. The register I23 * of CS4231 is undefined and it appears to be read * only. AD1845 uses I23 for setting sample rate. * Assume the chip is AD1845 if I23 is changeable. */ tmp = ad_read(devc, 23); ad_write(devc, 23, ~tmp); if (ad_read(devc, 23) != tmp) { /* AD1845 ? */ devc->chip_name = "AD1845"; devc->mode = MD_1845; } ad_write(devc, 23, tmp); /* Restore */ break; case 0x83: /* CS4236 */ case 0x03: /* Mutant CS4236 on Intel PR440fx board */ devc->chip_name = "CS4236"; devc->mode = MD_4236; break; default: /* Assume CS4231 */ printf("unknown id 0x%02x, assuming CS4231\n", id); devc->mode = MD_4231; } } ad_write(devc, 25, tmp1); /* Restore bits */ DDB(printf("ad1848_detect() - step K\n")); } } DDB(printf("ad1848_detect() - step L\n")); if (ad_flags) { if (devc->mode != MD_1848) *ad_flags |= AD_F_CS4231; } DDB(printf("ad1848_detect() - Detected OK\n")); return (last_result = 1); } void ad1848_init(char *name, int io_base, int irq, int dma_playback, int dma_capture, int share_dma, sound_os_info * osp) { /* * NOTE! If irq < 0, there is another driver which has allocated the * IRQ so that this driver doesn't need to allocate/deallocate it. * The actually used IRQ is ABS(irq). */ /* * Initial values for the indirect registers of CS4248/AD1848. */ static int init_values[] = { 0xa8, /* MIXOUTL: src:mic, +20dB, gain +12dB */ 0xa8, /* MIXOUTR: src:mic, +20dB, gain +12dB */ 0x08, /* CDL Input: mute, +6dB */ 0x08, /* CDR Input: mute, +6dB */ 0x08, /* FML Input: mute, +6dB */ 0x08, /* FMR Input: mute, +6dB */ 0x80, /* DAC-L Input: enable, 0dB */ 0x80, /* DAC-R Input: enable, 0dB */ /* 0xa8, 0xa8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, */ 0x00, /* 8bit, lin, uns, mono, 8KHz */ 0x0c, /* dma-cap, dma-pb, autocal, single dma, disable cap/pb */ 0x02, /* int enable */ 0x00, /* clear error status */ 0x8a, /* rev. id (low bytes readonly) */ 0x00, 0x00, /* playback upper base count */ 0x00, /* playback lower base count */ /* Positions 16 to 31 just for CS4231 and newer devices */ /* I16-I17: alt. feature enable on the 4231, but AUXL Input * on the OPTi931 (where the features are set elsewhere */ 0x81, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; int i, my_dev; ad1848_info *devc = &dev_info[nr_ad1848_devs]; if (!ad1848_detect(io_base, NULL, osp)) return; devc->irq = (irq > 0) ? irq : 0; devc->opened = 0; devc->timer_ticks = 0; devc->osp = osp; if (nr_ad1848_devs != 0) { bcopy((char *) &ad1848_pcm_operations[0], (char *) &ad1848_pcm_operations[nr_ad1848_devs], sizeof(struct audio_operations)); } for (i = 0; i < 16; i++) ad_write(devc, i, init_values[i]); ad_mute(devc); /* Initialize some variables */ ad_unmute(devc); /* Leave it unmuted now */ if (devc->mode > MD_1848) { if (dma_capture == dma_playback || dma_capture == -1 || dma_playback == -1) { ad_write(devc, 9, ad_read(devc, 9) | 0x04); /* Single DMA mode */ ad1848_pcm_operations[nr_ad1848_devs].flags &= ~DMA_DUPLEX; } else { ad_write(devc, 9, ad_read(devc, 9) & ~0x04); /* Dual DMA mode */ ad1848_pcm_operations[nr_ad1848_devs].flags |= DMA_DUPLEX; } ad_write(devc, 12, ad_read(devc, 12) | 0x40); /* Mode2 = enabled */ for (i = 16; i < 32; i++) ad_write(devc, i, init_values[i]); if (devc->mode == MD_4231A) { /* Enable full * calibration */ ad_write(devc, 9, init_values[9] | 0x18); } if (devc->mode == MD_1845) { /* Alternate freq select enabled */ ad_write(devc, 27, init_values[27] | 0x08); } } else { ad1848_pcm_operations[nr_ad1848_devs].flags &= ~DMA_DUPLEX; ad_write(devc, 9, ad_read(devc, 9) | 0x04); /* Single DMA mode */ } outb(io_Status(devc), 0); /* Clear pending interrupts */ if (name != NULL && name[0] != 0) snprintf(ad1848_pcm_operations[nr_ad1848_devs].name, sizeof(ad1848_pcm_operations[nr_ad1848_devs].name), "%s (%s)", name, devc->chip_name); else snprintf(ad1848_pcm_operations[nr_ad1848_devs].name, sizeof(ad1848_pcm_operations[nr_ad1848_devs].name), "Generic audio codec (%s)", devc->chip_name); conf_printf2(ad1848_pcm_operations[nr_ad1848_devs].name, devc->base, devc->irq, dma_playback, dma_capture); /* ad1848_pcm_operations[nr_ad1848_devs].flags |= DMA_AUTOMODE ; */ if (num_audiodevs < MAX_AUDIO_DEV) { audio_devs[my_dev = num_audiodevs++] = &ad1848_pcm_operations[nr_ad1848_devs]; if (irq > 0) { audio_devs[my_dev]->devc = devc; irq2dev[irq] = my_dev; if (snd_set_irq_handler(devc->irq, ad1848_interrupt, devc->osp)<0) { printf("ad1848: IRQ in use\n"); } #ifdef NO_IRQ_TEST if (devc->mode != MD_1848) { int x; u_char tmp = ad_read(devc, 16); devc->timer_ticks = 0; ad_write(devc, 21, 0x00); /* Timer msb */ ad_write(devc, 20, 0x10); /* Timer lsb */ ad_write(devc, 16, tmp | 0x40); /* Enable timer */ for (x = 0; x < 100000 && devc->timer_ticks == 0; x++); ad_write(devc, 16, tmp & ~0x40); /* Disable timer */ if (devc->timer_ticks == 0) printf("[IRQ conflict???]"); else devc->irq_ok = 1; } else devc->irq_ok = 1; /* Couldn't test. assume it's OK */ #else devc->irq_ok = 1; #endif } else if (irq < 0) irq2dev[-irq] = devc->dev_no = my_dev; audio_devs[my_dev]->otherside = -1 ; audio_devs[my_dev]->flags |= DMA_AUTOMODE; audio_devs[my_dev]->dmachan1 = dma_playback; audio_devs[my_dev]->dmachan2 = dma_capture; audio_devs[my_dev]->buffsize = DSP_BUFFSIZE; audio_devs[my_dev]->devc = devc; audio_devs[my_dev]->format_mask = ad_format_mask[devc->mode]; nr_ad1848_devs++; #ifdef CONFIG_SEQUENCER if (devc->mode != MD_1848 && devc->irq_ok) ad1848_tmr_install(my_dev); #endif /* * Toggle the MCE bit. It completes the initialization phase. */ ad_enter_MCE(devc); /* In case the bit was off */ ad_leave_MCE(devc); if (num_mixers < MAX_MIXER_DEV) { mixer2codec[num_mixers] = my_dev + 1; audio_devs[my_dev]->mixer_dev = num_mixers; mixer_devs[num_mixers++] = &ad1848_mixer_operations; ad1848_mixer_reset(devc); } } else printf("AD1848: Too many PCM devices available\n"); } void ad1848_interrupt(int irq) { u_char status; ad1848_info *devc; int dev; if (irq < 0 || irq > 15) dev = -1; else dev = irq2dev[irq]; if (dev < 0 || dev >= num_audiodevs) { for (irq = 0; irq < 17; irq++) if (irq2dev[irq] != -1) break; if (irq > 15) { printf("ad1848.c: Bogus interrupt %d\n", irq); return; } dev = irq2dev[irq]; } devc = (ad1848_info *) audio_devs[dev]->devc; status = inb(io_Status(devc)); if (status & 0x01) { /* we have an interrupt */ int alt_stat = 0xff ; if (devc->mode != MD_1848) { /* * high-end devices have full-duplex dma and timer. * the exact reason for the interrupt is in reg. I24. * For old devices, we fake the interrupt bits, and * determine the real reason basing on the device mode. */ alt_stat = ad_read(devc, 24); if (alt_stat & 0x40) { /* Timer interrupt */ devc->timer_ticks++; #ifdef CONFIG_SEQUENCER if (timer_installed == dev && devc->timer_running) sound_timer_interrupt(); #endif } } outb(io_Status(devc), 0); /* Clear interrupt status */ if (audio_devs[dev]->busy) { if (devc->irq_mode & PCM_ENABLE_OUTPUT && alt_stat & 0x10) DMAbuf_outputintr(dev, 1); if (devc->irq_mode & PCM_ENABLE_INPUT && alt_stat & 0x20) DMAbuf_inputintr(dev); } } } /* * Some extra code for the MS Sound System */ #ifdef amancio void check_opl3(int base, struct address_info * hw_config) { if (!opl3_detect(base, hw_config->osp)) return; opl3_init(0, base, hw_config->osp); } #endif /* * this is the probe routine. Note, it is not necessary to * go through this for PnP devices, since they are already * indentified precisely using their PnP id. * */ int probe_mss(struct address_info * hw_config) { u_char tmp; DDB(printf("Entered probe_mss(io 0x%x, type %d)\n", hw_config->io_base, hw_config->card_subtype)); if (hw_config->card_subtype == 1) { /* Has no IRQ/DMA registers */ /* check_opl3(0x388, hw_config); */ goto probe_ms_end; } #if defined(CONFIG_AEDSP16) && defined(AEDSP16_MSS) /* * Initialize Audio Excel DSP 16 to MSS: before any operation we must * enable MSS I/O ports. */ InitAEDSP16_MSS(hw_config); #endif /* * Check if the IO port returns valid signature. The original MS * Sound system returns 0x04 while some cards (AudioTriX Pro for * example) return 0x00 or 0x0f. */ if ((tmp = inb(hw_config->io_base + 3)) == 0xff) { /* Bus float */ DDB(printf("I/O address inactive (%x), force type 1\n", tmp)); hw_config->card_subtype = 1 ; goto probe_ms_end; } if ((tmp & 0x3f) != 0x04 && (tmp & 0x3f) != 0x0f && (tmp & 0x3f) != 0x00) { DDB(printf("No MSS signature detected on port 0x%x (0x%x)\n", hw_config->io_base, inb(hw_config->io_base + 3))); return 0; } if (hw_config->irq > 11) { printf("MSS: Bad IRQ %d\n", hw_config->irq); return 0; } if (hw_config->dma != 0 && hw_config->dma != 1 && hw_config->dma != 3) { printf("MSS: Bad DMA %d\n", hw_config->dma); return 0; } /* * Check that DMA0 is not in use with a 8 bit board. */ if (hw_config->dma == 0 && inb(hw_config->io_base + 3) & 0x80) { printf("MSS: Can't use DMA0 with a 8 bit card/slot\n"); return 0; } if (hw_config->irq > 7 && hw_config->irq != 9 && inb(hw_config->io_base + 3) & 0x80) { printf("MSS: Can't use IRQ%d with a 8 bit card/slot\n", hw_config->irq); return 0; } probe_ms_end: return ad1848_detect(hw_config->io_base + 4, NULL, hw_config->osp); } void attach_mss(struct address_info * hw_config) { #if 0 /* * XXX do we really need to detect it again ? - lr970712 */ if (!ad1848_detect(hw_config->io_base + 4, NULL, hw_config->osp)) return ; #endif if (hw_config->card_subtype == 1) { /* Has no IRQ/DMA registers */ ad1848_init("MS Sound System1", hw_config->io_base + 4, hw_config->irq, hw_config->dma, hw_config->dma2, 0, hw_config->osp); } else { /* * Set the IRQ and DMA addresses. */ #ifdef PC98 static char interrupt_bits[13] = { -1, -1, -1, 0x08, -1, 0x10, -1, -1, -1, -1, 0x18, -1, 0x20 }; #else static char interrupt_bits[12] = { -1, -1, -1, -1, -1, -1, -1, 0x08, -1, 0x10, 0x18, 0x20 }; #endif static char dma_bits[4] = { 1, 2, 0, 3 }; int config_port = hw_config->io_base + 0; int version_port = hw_config->io_base + 3; char bits = interrupt_bits[hw_config->irq]; if (bits == -1) return ; outb(config_port, bits | 0x40); if ((inb(version_port) & 0x40) == 0) printf("[IRQ Conflict?]"); /* Write IRQ+DMA setup */ outb(config_port, bits | dma_bits[hw_config->dma]); ad1848_init("MS Sound System0", hw_config->io_base + 4, hw_config->irq, hw_config->dma, hw_config->dma, 0, hw_config->osp); } return ; } /* * WSS compatible PnP codec support. * XXX I doubt it works now - lr970712 */ int probe_pnp_ad1848(struct address_info * hw_config) { return ad1848_detect(hw_config->io_base, NULL, hw_config->osp); } void attach_pnp_ad1848(struct address_info * hw_config) { ad1848_init(hw_config->name, hw_config->io_base, hw_config->irq, hw_config->dma, hw_config->dma2, 0, hw_config->osp); } #ifdef CONFIG_SEQUENCER /* * Timer stuff (for /dev/music). */ static u_int current_interval = 0; static u_int ad1848_tmr_start(int dev, u_int usecs) { u_long flags; ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; u_long xtal_nsecs; /* nanoseconds per xtal oscillaror tick */ u_long divider; flags = splhigh(); /* * Length of the timer interval (in nanoseconds) depends on the * selected crystal oscillator. Check this from bit 0x01 of I8. * * AD1845 has just one oscillator which has cycle time of 10.050 us * (when a 24.576 MHz xtal oscillator is used). * * Convert requested interval to nanoseconds before computing the timer * divider. */ if (devc->mode == MD_1845) xtal_nsecs = 10050; else if (ad_read(devc, 8) & 0x01) xtal_nsecs = 9920; else xtal_nsecs = 9969; divider = (usecs * 1000 + xtal_nsecs / 2) / xtal_nsecs; if (divider < 100) /* Don't allow shorter intervals than about 1ms */ divider = 100; if (divider > 65535) /* Overflow check */ divider = 65535; ad_write(devc, 21, (divider >> 8) & 0xff); /* Set upper bits */ ad_write(devc, 20, divider & 0xff); /* Set lower bits */ ad_write(devc, 16, ad_read(devc, 16) | 0x40); /* Start the timer */ devc->timer_running = 1; splx(flags); return current_interval = (divider * xtal_nsecs + 500) / 1000; } static void ad1848_tmr_reprogram(int dev) { /* * Audio driver has changed sampling rate so that a different xtal * oscillator was selected. We have to reprogram the timer rate. */ ad1848_tmr_start(dev, current_interval); sound_timer_syncinterval(current_interval); } static void ad1848_tmr_disable(int dev) { u_long flags; ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; flags = splhigh(); ad_write(devc, 16, ad_read(devc, 16) & ~0x40); devc->timer_running = 0; splx(flags); } static void ad1848_tmr_restart(int dev) { u_long flags; ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; if (current_interval == 0) return; flags = splhigh(); ad_write(devc, 16, ad_read(devc, 16) | 0x40); devc->timer_running = 1; splx(flags); } static struct sound_lowlev_timer ad1848_tmr = { 0, ad1848_tmr_start, ad1848_tmr_disable, ad1848_tmr_restart }; static int ad1848_tmr_install(int dev) { if (timer_installed != -1) return 0; /* Don't install another timer */ timer_installed = ad1848_tmr.dev = dev; sound_timer_init(&ad1848_tmr, audio_devs[dev]->name); return 1; } #endif #endif Index: head/sys/i386/isa/wd.c =================================================================== --- head/sys/i386/isa/wd.c (revision 45719) +++ head/sys/i386/isa/wd.c (revision 45720) @@ -1,2404 +1,2413 @@ /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * William Jolitz. * * 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: @(#)wd.c 7.2 (Berkeley) 5/9/91 - * $Id: wd.c,v 1.192 1999/04/13 19:38:11 peter Exp $ + * $Id: wd.c,v 1.193 1999/04/13 20:22:30 peter Exp $ */ /* TODO: * o Bump error count after timeout. * o Satisfy ATA timing in all cases. * o Finish merging berry/sos timeout code (bump error count...). * o Merge/fix TIH/NetBSD bad144 code. * o Don't use polling except for initialization. Need to * reorganize the state machine. Then "extra" interrupts * shouldn't happen (except maybe one for initialization). * o Fix disklabel, boot and driver inconsistencies with * bad144 in standard versions. * o Support extended DOS partitions. * o Support swapping to DOS partitions. * o Handle bad sectors, clustering, disklabelling, DOS * partitions and swapping driver-independently. Use * i386/dkbad.c for bad sectors. Swapping will need new * driver entries for polled reinit and polled write). */ #include "wd.h" #ifdef NWDC #undef NWDC #endif #include "wdc.h" #if NWDC > 0 #include "opt_devfs.h" #include "opt_hw_wdog.h" #include "opt_ide_delay.h" #include #include #include #include #include +#include #include #include #include #include #include #ifdef DEVFS #include #endif /*DEVFS*/ #include #include #include #include #include #include #include #include #include #include #include #include extern void wdstart(int ctrlr); #ifdef IDE_DELAY #define TIMEOUT IDE_DELAY #else #define TIMEOUT 10000 #endif #define RETRIES 5 /* number of retries before giving up */ #define RECOVERYTIME 500000 /* usec for controller to recover after err */ #define MAXTRANSFER 255 /* max size of transfer in sectors */ /* correct max is 256 but some controllers */ /* can't handle that in all cases */ #define WDOPT_32BIT 0x8000 #define WDOPT_SLEEPHACK 0x4000 #define WDOPT_DMA 0x2000 #define WDOPT_LBA 0x1000 #define WDOPT_FORCEHD(x) (((x)&0x0f00)>>8) #define WDOPT_MULTIMASK 0x00ff /* * This biotab field doubles as a field for the physical unit number on * the controller. */ #define id_physid id_scsiid /* * Drive states. Used to initialize drive. */ #define CLOSED 0 /* disk is closed. */ #define WANTOPEN 1 /* open requested, not started */ #define RECAL 2 /* doing restore */ #define OPEN 3 /* done with open */ #define PRIMARY 0 /* * Disk geometry. A small part of struct disklabel. * XXX disklabel.5 contains an old clone of disklabel.h. */ struct diskgeom { u_long d_secsize; /* # of bytes per sector */ u_long d_nsectors; /* # of data sectors per track */ u_long d_ntracks; /* # of tracks per cylinder */ u_long d_ncylinders; /* # of data cylinders per unit */ u_long d_secpercyl; /* # of data sectors per cylinder */ u_long d_secperunit; /* # of data sectors per unit */ u_long d_precompcyl; /* XXX always 0 */ }; /* * The structure of a disk drive. */ struct disk { u_int dk_bc; /* byte count left */ short dk_skip; /* blocks already transferred */ int dk_ctrlr; /* physical controller number */ int dk_ctrlr_cmd640;/* controller number for CMD640 quirk */ u_int32_t dk_unit; /* physical unit number */ u_int32_t dk_lunit; /* logical unit number */ u_int32_t dk_interface; /* interface (two ctrlrs per interface) */ char dk_state; /* control state */ u_char dk_status; /* copy of status reg. */ u_char dk_error; /* copy of error reg. */ u_char dk_timeout; /* countdown to next timeout */ u_int32_t dk_port; /* i/o port base */ u_int32_t dk_altport; /* altstatus port base */ #ifdef DEVFS void *dk_bdev; /* devfs token for whole disk */ void *dk_cdev; /* devfs token for raw whole disk */ #endif /* DEVFS */ u_long cfg_flags; /* configured characteristics */ short dk_flags; /* drive characteristics found */ #define DKFL_SINGLE 0x00004 /* sector at a time mode */ #define DKFL_ERROR 0x00008 /* processing a disk error */ #define DKFL_LABELLING 0x00080 /* readdisklabel() in progress */ #define DKFL_32BIT 0x00100 /* use 32-bit i/o mode */ #define DKFL_MULTI 0x00200 /* use multi-i/o mode */ #define DKFL_BADSCAN 0x00400 /* report all errors */ #define DKFL_USEDMA 0x00800 /* use DMA for data transfers */ #define DKFL_DMA 0x01000 /* using DMA on this transfer-- DKFL_SINGLE * overrides this */ #define DKFL_LBA 0x02000 /* use LBA for data transfers */ struct wdparams dk_params; /* ESDI/IDE drive/controller parameters */ unsigned int dk_multi; /* multi transfers */ int dk_currentiosize; /* current io size */ struct diskgeom dk_dd; /* device configuration data */ struct diskslices *dk_slices; /* virtual drives */ void *dk_dmacookie; /* handle for DMA services */ struct devstat dk_stats; /* devstat entry */ }; #define WD_COUNT_RETRIES static int wdtest = 0; static struct disk *wddrives[NWD]; /* table of units */ static struct buf_queue_head drive_queue[NWD]; /* head of queue per drive */ static struct { int b_active; } wdutab[NWD]; /* static struct buf wdtab[NWDC]; */ static struct { struct buf_queue_head controller_queue; int b_errcnt; int b_active; } wdtab[NWDC]; struct wddma wddma[NWDC]; #ifdef notyet static struct buf rwdbuf[NWD]; /* buffers for raw IO */ #endif static int wdprobe(struct isa_device *dvp); static int wdattach(struct isa_device *dvp); static void wdustart(struct disk *du); static int wdcontrol(struct buf *bp); static int wdcommand(struct disk *du, u_int cylinder, u_int head, u_int sector, u_int count, u_int command); static int wdsetctlr(struct disk *du); #if 0 static int wdwsetctlr(struct disk *du); #endif static int wdsetmode(int mode, void *wdinfo); static int wdgetctlr(struct disk *du); static void wderror(struct buf *bp, struct disk *du, char *mesg); static void wdflushirq(struct disk *du, int old_ipl); static int wdreset(struct disk *du); static void wdsleep(int ctrlr, char *wmesg); static void wdstrategy1(struct buf *bp); static timeout_t wdtimeout; static int wdunwedge(struct disk *du); static int wdwait(struct disk *du, u_char bits_wanted, int timeout); struct isa_driver wdcdriver = { wdprobe, wdattach, "wdc", }; static d_open_t wdopen; static d_read_t wdread; static d_write_t wdwrite; static d_close_t wdclose; static d_strategy_t wdstrategy; static d_ioctl_t wdioctl; static d_dump_t wddump; static d_psize_t wdsize; #define CDEV_MAJOR 3 #define BDEV_MAJOR 0 static struct cdevsw wd_cdevsw = { wdopen, wdclose, wdread, wdwrite, wdioctl, nostop, nullreset, nodevtotty, seltrue, nommap, wdstrategy, "wd", NULL, -1, wddump, wdsize, D_DISK, 0, -1 }; static int atapictrlr; static int eide_quirks; /* * Here we use the pci-subsystem to find out, whether there is * a cmd640b-chip attached on this pci-bus. This public routine * will be called by ide_pci.c */ void wdc_pci(int quirks) { eide_quirks = quirks; } /* * Probe for controller. */ static int wdprobe(struct isa_device *dvp) { int unit = dvp->id_unit; int interface; struct disk *du; if (unit >= NWDC) return (0); du = malloc(sizeof *du, M_TEMP, M_NOWAIT); if (du == NULL) return (0); bzero(du, sizeof *du); du->dk_ctrlr = dvp->id_unit; interface = du->dk_ctrlr / 2; du->dk_interface = interface; du->dk_port = dvp->id_iobase; if (wddma[interface].wdd_candma != NULL) { du->dk_dmacookie = wddma[interface].wdd_candma(dvp->id_iobase, du->dk_ctrlr, du->dk_unit); du->dk_altport = wddma[interface].wdd_altiobase(du->dk_dmacookie); } if (du->dk_altport == 0) du->dk_altport = du->dk_port + wd_ctlr; /* check if we have registers that work */ outb(du->dk_port + wd_sdh, WDSD_IBM); /* set unit 0 */ outb(du->dk_port + wd_cyl_lo, 0xa5); /* wd_cyl_lo is read/write */ if (inb(du->dk_port + wd_cyl_lo) == 0xff) { /* XXX too weak */ /* There is no master, try the ATAPI slave. */ du->dk_unit = 1; outb(du->dk_port + wd_sdh, WDSD_IBM | 0x10); outb(du->dk_port + wd_cyl_lo, 0xa5); if (inb(du->dk_port + wd_cyl_lo) == 0xff) goto nodevice; } if (wdreset(du) == 0) goto reset_ok; /* test for ATAPI signature */ outb(du->dk_port + wd_sdh, WDSD_IBM); /* master */ if (inb(du->dk_port + wd_cyl_lo) == 0x14 && inb(du->dk_port + wd_cyl_hi) == 0xeb) goto reset_ok; du->dk_unit = 1; outb(du->dk_port + wd_sdh, WDSD_IBM | 0x10); /* slave */ if (inb(du->dk_port + wd_cyl_lo) == 0x14 && inb(du->dk_port + wd_cyl_hi) == 0xeb) goto reset_ok; DELAY(RECOVERYTIME); if (wdreset(du) != 0) { goto nodevice; } reset_ok: /* execute a controller only command */ if (wdcommand(du, 0, 0, 0, 0, WDCC_DIAGNOSE) != 0 || wdwait(du, 0, TIMEOUT) < 0) { goto nodevice; } /* * drive(s) did not time out during diagnostic : * Get error status and check that both drives are OK. * Table 9-2 of ATA specs suggests that we must check for * a value of 0x01 * * Strangely, some controllers will return a status of * 0x81 (drive 0 OK, drive 1 failure), and then when * the DRV bit is set, return status of 0x01 (OK) for * drive 2. (This seems to contradict the ATA spec.) */ du->dk_error = inb(du->dk_port + wd_error); if(du->dk_error != 0x01 && du->dk_error != 0) { if(du->dk_error & 0x80) { /* drive 1 failure */ /* first set the DRV bit */ u_int sdh; sdh = inb(du->dk_port+ wd_sdh); sdh = sdh | 0x10; outb(du->dk_port+ wd_sdh, sdh); /* Wait, to make sure drv 1 has completed diags */ if ( wdwait(du, 0, TIMEOUT) < 0) goto nodevice; /* Get status for drive 1 */ du->dk_error = inb(du->dk_port + wd_error); /* printf("Error (drv 1) : %x\n", du->dk_error); */ /* * Sometimes (apparently mostly with ATAPI * drives involved) 0x81 really means 0x81 * (drive 0 OK, drive 1 failed). */ if(du->dk_error != 0x01 && du->dk_error != 0x81) goto nodevice; } else /* drive 0 fail */ goto nodevice; } free(du, M_TEMP); return (IO_WDCSIZE); nodevice: free(du, M_TEMP); return (0); } /* * Attach each drive if possible. */ static int wdattach(struct isa_device *dvp) { #if defined(DEVFS) int mynor; #endif - u_int unit, lunit; - struct isa_device *wdup; + int unit, lunit, flags, i; struct disk *du; struct wdparams *wp; + static char buf[] = "wdcXXX"; dvp->id_intr = wdintr; if (dvp->id_unit >= NWDC) return (0); if (eide_quirks & Q_CMD640B) { if (dvp->id_unit == PRIMARY) { printf("wdc0: CMD640B workaround enabled\n"); bufq_init(&wdtab[PRIMARY].controller_queue); } } else bufq_init(&wdtab[dvp->id_unit].controller_queue); - for (wdup = isa_biotab_wdc; wdup->id_driver != 0; wdup++) { - if (wdup->id_iobase != dvp->id_iobase) + sprintf(buf, "wdc%d", dvp->id_unit); + for (i = resource_query_string(-1, "at", buf); + i != -1; + i = resource_query_string(i, "at", buf)) { + if (strcmp(resource_query_name(i), "wd")) + /* Avoid a bit of foot shooting. */ continue; - lunit = wdup->id_unit; + + lunit = resource_query_unit(i); if (lunit >= NWD) continue; - unit = wdup->id_physid; + if (resource_int_value("wd", lunit, "drive", &unit) != 0) + continue; + if (resource_int_value("wd", lunit, "flags", &flags) != 0) + flags = 0; du = malloc(sizeof *du, M_TEMP, M_NOWAIT); if (du == NULL) continue; if (wddrives[lunit] != NULL) panic("drive attached twice"); wddrives[lunit] = du; bufq_init(&drive_queue[lunit]); bzero(du, sizeof *du); du->dk_ctrlr = dvp->id_unit; if (eide_quirks & Q_CMD640B) { du->dk_ctrlr_cmd640 = PRIMARY; } else { du->dk_ctrlr_cmd640 = du->dk_ctrlr; } du->dk_unit = unit; du->dk_lunit = lunit; du->dk_port = dvp->id_iobase; du->dk_altport = du->dk_port + wd_ctlr; /* * Use the individual device flags or the controller * flags. */ - du->cfg_flags = wdup->id_flags | + du->cfg_flags = flags | ((dvp->id_flags) >> (16 * unit)); if (wdgetctlr(du) == 0) { /* * Print out description of drive. * wdp_model may not be null terminated. */ printf("wdc%d: unit %d (wd%d): <%.*s>", dvp->id_unit, unit, lunit, (int)sizeof(du->dk_params.wdp_model), du->dk_params.wdp_model); if (du->dk_flags & DKFL_LBA) printf(", LBA"); if (du->dk_flags & DKFL_USEDMA) printf(", DMA"); if (du->dk_flags & DKFL_32BIT) printf(", 32-bit"); if (du->dk_multi > 1) printf(", multi-block-%d", du->dk_multi); if (du->cfg_flags & WDOPT_SLEEPHACK) printf(", sleep-hack"); printf("\n"); if (du->dk_params.wdp_heads == 0) printf("wd%d: size unknown, using %s values\n", lunit, du->dk_dd.d_secperunit > 17 ? "BIOS" : "fake"); printf( "wd%d: %luMB (%lu sectors), " "%lu cyls, %lu heads, %lu S/T, %lu B/S\n", lunit, du->dk_dd.d_secperunit / ((1024L * 1024L) / du->dk_dd.d_secsize), du->dk_dd.d_secperunit, du->dk_dd.d_ncylinders, du->dk_dd.d_ntracks, du->dk_dd.d_nsectors, du->dk_dd.d_secsize); if (bootverbose) { wp = &du->dk_params; printf( "wd%d: ATA INQUIRE valid = %04x, " "dmamword = %04x, apio = %04x, " "udma = %04x\n", du->dk_lunit, wp->wdp_atavalid, wp->wdp_dmamword, wp->wdp_eidepiomodes, wp->wdp_udmamode); } /* * Start timeout routine for this drive. * XXX timeout should be per controller. */ wdtimeout(du); #ifdef DEVFS mynor = dkmakeminor(lunit, WHOLE_DISK_SLICE, RAW_PART); du->dk_bdev = devfs_add_devswf(&wd_cdevsw, mynor, DV_BLK, UID_ROOT, GID_OPERATOR, 0640, "wd%d", lunit); du->dk_cdev = devfs_add_devswf(&wd_cdevsw, mynor, DV_CHR, UID_ROOT, GID_OPERATOR, 0640, "rwd%d", lunit); #endif /* * Export the drive to the devstat interface. */ devstat_add_entry(&du->dk_stats, "wd", lunit, du->dk_dd.d_secsize, DEVSTAT_NO_ORDERED_TAGS, DEVSTAT_TYPE_DIRECT | DEVSTAT_TYPE_IF_IDE, DEVSTAT_PRIORITY_WD); } else { free(du, M_TEMP); wddrives[lunit] = NULL; } } /* * Probe all free IDE units, searching for ATAPI drives. */ for (unit=0; unit<2; ++unit) { for (lunit=0; lunitdk_ctrlr == dvp->id_unit && wddrives[lunit]->dk_unit == unit) goto next; if (atapi_attach (dvp->id_unit, unit, dvp->id_iobase)) atapictrlr = dvp->id_unit; next: ; } /* * Discard any interrupts generated by wdgetctlr(). wdflushirq() * doesn't work now because the ambient ipl is too high. */ if (eide_quirks & Q_CMD640B) { wdtab[PRIMARY].b_active = 2; } else { wdtab[dvp->id_unit].b_active = 2; } return (1); } static int wdread(dev_t dev, struct uio *uio, int ioflag) { return (physio(wdstrategy, NULL, dev, 1, minphys, uio)); } static int wdwrite(dev_t dev, struct uio *uio, int ioflag) { return (physio(wdstrategy, NULL, dev, 0, minphys, uio)); } /* Read/write routine for a buffer. Finds the proper unit, range checks * arguments, and schedules the transfer. Does not wait for the transfer * to complete. Multi-page transfers are supported. All I/O requests must * be a multiple of a sector in length. */ void wdstrategy(register struct buf *bp) { struct disk *du; int lunit = dkunit(bp->b_dev); int s; /* valid unit, controller, and request? */ if (lunit >= NWD || bp->b_blkno < 0 || (du = wddrives[lunit]) == NULL || bp->b_bcount % DEV_BSIZE != 0) { bp->b_error = EINVAL; bp->b_flags |= B_ERROR; goto done; } /* * Do bounds checking, adjust transfer, and set b_pblkno. */ if (dscheck(bp, du->dk_slices) <= 0) goto done; /* * Check for *any* block on this transfer being on the bad block list * if it is, then flag the block as a transfer that requires * bad block handling. Also, used as a hint for low level disksort * clustering code to keep from coalescing a bad transfer into * a normal transfer. Single block transfers for a large number of * blocks associated with a cluster I/O are undesirable. * * XXX the old disksort() doesn't look at B_BAD. Coalescing _is_ * desirable. We should split the results at bad blocks just * like we should split them at MAXTRANSFER boundaries. */ if (dsgetbad(bp->b_dev, du->dk_slices) != NULL) { long *badsect = dsgetbad(bp->b_dev, du->dk_slices)->bi_bad; int i; int nsecs = howmany(bp->b_bcount, DEV_BSIZE); /* XXX pblkno is too physical. */ daddr_t nspblkno = bp->b_pblkno - du->dk_slices->dss_slices[dkslice(bp->b_dev)].ds_offset; int blkend = nspblkno + nsecs; for (i = 0; badsect[i] != -1 && badsect[i] < blkend; i++) { if (badsect[i] >= nspblkno) { bp->b_flags |= B_BAD; break; } } } /* queue transfer on drive, activate drive and controller if idle */ s = splbio(); /* Pick up changes made by readdisklabel(). */ if (du->dk_flags & DKFL_LABELLING && du->dk_state > RECAL) { wdsleep(du->dk_ctrlr, "wdlab"); du->dk_state = WANTOPEN; } bufqdisksort(&drive_queue[lunit], bp); if (wdutab[lunit].b_active == 0) wdustart(du); /* start drive */ if (wdtab[du->dk_ctrlr_cmd640].b_active == 0) wdstart(du->dk_ctrlr); /* start controller */ /* Tell devstat that we have started a transaction on this drive */ devstat_start_transaction(&du->dk_stats); splx(s); return; done: /* toss transfer, we're done early */ biodone(bp); } static void wdstrategy1(struct buf *bp) { /* * XXX - do something to make wdstrategy() but not this block while * we're doing dsinit() and dsioctl(). */ wdstrategy(bp); } /* * Routine to queue a command to the controller. The unit's * request is linked into the active list for the controller. * If the controller is idle, the transfer is started. */ static void wdustart(register struct disk *du) { register struct buf *bp; int ctrlr = du->dk_ctrlr_cmd640; /* unit already active? */ if (wdutab[du->dk_lunit].b_active) return; bp = bufq_first(&drive_queue[du->dk_lunit]); if (bp == NULL) { /* yes, an assign */ return; } /* * store away which device we came from. */ bp->b_driver1 = du; bufq_remove(&drive_queue[du->dk_lunit], bp); /* link onto controller queue */ bufq_insert_tail(&wdtab[ctrlr].controller_queue, bp); /* mark the drive unit as busy */ wdutab[du->dk_lunit].b_active = 1; } /* * Controller startup routine. This does the calculation, and starts * a single-sector read or write operation. Called to start a transfer, * or from the interrupt routine to continue a multi-sector transfer. * RESTRICTIONS: * 1. The transfer length must be an exact multiple of the sector size. */ void wdstart(int ctrlr) { register struct disk *du; register struct buf *bp; struct diskgeom *lp; /* XXX sic */ long blknum; long secpertrk, secpercyl; u_int lunit; u_int count; int ctrlr_atapi; if (eide_quirks & Q_CMD640B) { ctrlr = PRIMARY; ctrlr_atapi = atapictrlr; } else { ctrlr_atapi = ctrlr; } if (wdtab[ctrlr].b_active == 2) wdtab[ctrlr].b_active = 0; if (wdtab[ctrlr].b_active) return; /* is there a drive for the controller to do a transfer with? */ bp = bufq_first(&wdtab[ctrlr].controller_queue); if (bp == NULL) { if (atapi_start && atapi_start (ctrlr_atapi)) /* mark controller active in ATAPI mode */ wdtab[ctrlr].b_active = 3; return; } /* obtain controller and drive information */ lunit = dkunit(bp->b_dev); du = wddrives[lunit]; /* if not really a transfer, do control operations specially */ if (du->dk_state < OPEN) { if (du->dk_state != WANTOPEN) printf("wd%d: wdstart: weird dk_state %d\n", du->dk_lunit, du->dk_state); if (wdcontrol(bp) != 0) printf("wd%d: wdstart: wdcontrol returned nonzero, state = %d\n", du->dk_lunit, du->dk_state); return; } /* calculate transfer details */ blknum = bp->b_pblkno + du->dk_skip; #ifdef WDDEBUG if (du->dk_skip == 0) printf("wd%d: wdstart: %s %d@%d; map ", lunit, (bp->b_flags & B_READ) ? "read" : "write", bp->b_bcount, blknum); else printf(" %d)%x", du->dk_skip, inb(du->dk_altport)); #endif lp = &du->dk_dd; secpertrk = lp->d_nsectors; secpercyl = lp->d_secpercyl; if (du->dk_skip == 0) { du->dk_bc = bp->b_bcount; if (bp->b_flags & B_BAD /* * XXX handle large transfers inefficiently instead * of crashing on them. */ || howmany(du->dk_bc, DEV_BSIZE) > MAXTRANSFER) du->dk_flags |= DKFL_SINGLE; } if (du->dk_flags & DKFL_SINGLE && dsgetbad(bp->b_dev, du->dk_slices) != NULL) { /* XXX */ u_long ds_offset = du->dk_slices->dss_slices[dkslice(bp->b_dev)].ds_offset; blknum = transbad144(dsgetbad(bp->b_dev, du->dk_slices), blknum - ds_offset) + ds_offset; } wdtab[ctrlr].b_active = 1; /* mark controller active */ /* if starting a multisector transfer, or doing single transfers */ if (du->dk_skip == 0 || (du->dk_flags & DKFL_SINGLE)) { u_int command; u_int count1; long cylin, head, sector; if (du->dk_flags & DKFL_LBA) { sector = (blknum >> 0) & 0xff; cylin = (blknum >> 8) & 0xffff; head = ((blknum >> 24) & 0xf) | WDSD_LBA; } else { cylin = blknum / secpercyl; head = (blknum % secpercyl) / secpertrk; sector = blknum % secpertrk; } /* * XXX this looks like an attempt to skip bad sectors * on write. */ if (wdtab[ctrlr].b_errcnt && (bp->b_flags & B_READ) == 0) du->dk_bc += DEV_BSIZE; count1 = howmany( du->dk_bc, DEV_BSIZE); du->dk_flags &= ~DKFL_MULTI; #ifdef B_FORMAT if (bp->b_flags & B_FORMAT) { command = WDCC_FORMAT; count1 = lp->d_nsectors; sector = lp->d_gap3 - 1; /* + 1 later */ } else #endif { if (du->dk_flags & DKFL_SINGLE) { command = (bp->b_flags & B_READ) ? WDCC_READ : WDCC_WRITE; count1 = 1; du->dk_currentiosize = 1; } else { if((du->dk_flags & DKFL_USEDMA) && wddma[du->dk_interface].wdd_dmaverify(du->dk_dmacookie, (void *)((int)bp->b_data + du->dk_skip * DEV_BSIZE), du->dk_bc, bp->b_flags & B_READ)) { du->dk_flags |= DKFL_DMA; if( bp->b_flags & B_READ) command = WDCC_READ_DMA; else command = WDCC_WRITE_DMA; du->dk_currentiosize = count1; } else if( (count1 > 1) && (du->dk_multi > 1)) { du->dk_flags |= DKFL_MULTI; if( bp->b_flags & B_READ) { command = WDCC_READ_MULTI; } else { command = WDCC_WRITE_MULTI; } du->dk_currentiosize = du->dk_multi; if( du->dk_currentiosize > count1) du->dk_currentiosize = count1; } else { if( bp->b_flags & B_READ) { command = WDCC_READ; } else { command = WDCC_WRITE; } du->dk_currentiosize = 1; } } } /* * XXX this loop may never terminate. The code to handle * counting down of retries and eventually failing the i/o * is in wdintr() and we can't get there from here. */ if (wdtest != 0) { if (--wdtest == 0) { wdtest = 100; printf("dummy wdunwedge\n"); wdunwedge(du); } } if ((du->dk_flags & (DKFL_DMA|DKFL_SINGLE)) == DKFL_DMA) { wddma[du->dk_interface].wdd_dmaprep(du->dk_dmacookie, (void *)((int)bp->b_data + du->dk_skip * DEV_BSIZE), du->dk_bc, bp->b_flags & B_READ); } while (wdcommand(du, cylin, head, sector, count1, command) != 0) { wderror(bp, du, "wdstart: timeout waiting to give command"); wdunwedge(du); } #ifdef WDDEBUG printf("cylin %ld head %ld sector %ld addr %x sts %x\n", cylin, head, sector, (int)bp->b_data + du->dk_skip * DEV_BSIZE, inb(du->dk_altport)); #endif } /* * Schedule wdtimeout() to wake up after a few seconds. Retrying * unmarked bad blocks can take 3 seconds! Then it is not good that * we retry 5 times. * * On the first try, we give it 10 seconds, for drives that may need * to spin up. * * XXX wdtimeout() doesn't increment the error count so we may loop * forever. More seriously, the loop isn't forever but causes a * crash. * * TODO fix b_resid bug elsewhere (fd.c....). Fix short but positive * counts being discarded after there is an error (in physio I * think). Discarding them would be OK if the (special) file offset * was not advanced. */ if (wdtab[ctrlr].b_errcnt == 0) du->dk_timeout = 1 + 10; else du->dk_timeout = 1 + 3; /* if this is a DMA op, start DMA and go away until it's done. */ if ((du->dk_flags & (DKFL_DMA|DKFL_SINGLE)) == DKFL_DMA) { wddma[du->dk_interface].wdd_dmastart(du->dk_dmacookie); return; } /* If this is a read operation, just go away until it's done. */ if (bp->b_flags & B_READ) return; /* Ready to send data? */ if (wdwait(du, WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ, TIMEOUT) < 0) { wderror(bp, du, "wdstart: timeout waiting for DRQ"); /* * XXX what do we do now? If we've just issued the command, * then we can treat this failure the same as a command * failure. But if we are continuing a multi-sector write, * the command was issued ages ago, so we can't simply * restart it. * * XXX we waste a lot of time unnecessarily translating block * numbers to cylin/head/sector for continued i/o's. */ } count = 1; if( du->dk_flags & DKFL_MULTI) { count = howmany(du->dk_bc, DEV_BSIZE); if( count > du->dk_multi) count = du->dk_multi; if( du->dk_currentiosize > count) du->dk_currentiosize = count; } if (du->dk_flags & DKFL_32BIT) outsl(du->dk_port + wd_data, (void *)((int)bp->b_data + du->dk_skip * DEV_BSIZE), (count * DEV_BSIZE) / sizeof(long)); else outsw(du->dk_port + wd_data, (void *)((int)bp->b_data + du->dk_skip * DEV_BSIZE), (count * DEV_BSIZE) / sizeof(short)); du->dk_bc -= DEV_BSIZE * count; } /* Interrupt routine for the controller. Acknowledge the interrupt, check for * errors on the current operation, mark it done if necessary, and start * the next request. Also check for a partially done transfer, and * continue with the next chunk if so. */ void wdintr(void *unitnum) { register struct disk *du; register struct buf *bp; int dmastat = 0; /* Shut up GCC */ int unit = (int)unitnum; int ctrlr_atapi; if (eide_quirks & Q_CMD640B) { unit = PRIMARY; ctrlr_atapi = atapictrlr; } else { ctrlr_atapi = unit; } if (wdtab[unit].b_active == 2) return; /* intr in wdflushirq() */ if (!wdtab[unit].b_active) { #ifdef WDDEBUG /* * These happen mostly because the power-mgt part of the * bios shuts us down, and we just manage to see the * interrupt from the "SLEEP" command. */ printf("wdc%d: extra interrupt\n", unit); #endif return; } if (wdtab[unit].b_active == 3) { /* process an ATAPI interrupt */ if (atapi_intr && atapi_intr (ctrlr_atapi)) /* ATAPI op continues */ return; /* controller is free, start new op */ wdtab[unit].b_active = 0; wdstart (unit); return; } bp = bufq_first(&wdtab[unit].controller_queue); du = wddrives[dkunit(bp->b_dev)]; /* finish off DMA */ if ((du->dk_flags & (DKFL_DMA|DKFL_SINGLE)) == DKFL_DMA) { /* XXX SMP boxes sometimes generate an early intr. Why? */ if ((wddma[du->dk_interface].wdd_dmastatus(du->dk_dmacookie) & WDDS_INTERRUPT) == 0) return; dmastat = wddma[du->dk_interface].wdd_dmadone(du->dk_dmacookie); } du->dk_timeout = 0; /* check drive status/failure */ if (wdwait(du, 0, TIMEOUT) < 0) { wderror(bp, du, "wdintr: timeout waiting for status"); du->dk_status |= WDCS_ERR; /* XXX */ } /* is it not a transfer, but a control operation? */ if (du->dk_state < OPEN) { wdtab[unit].b_active = 0; switch (wdcontrol(bp)) { case 0: return; case 1: wdstart(unit); return; case 2: goto done; } } /* have we an error? */ if ((du->dk_status & (WDCS_ERR | WDCS_ECCCOR)) || (((du->dk_flags & (DKFL_DMA|DKFL_SINGLE)) == DKFL_DMA) && dmastat != WDDS_INTERRUPT)) { unsigned int errstat; oops: /* * XXX bogus inb() here */ errstat = inb(du->dk_port + wd_error); if(((du->dk_flags & (DKFL_DMA|DKFL_SINGLE)) == DKFL_DMA) && (errstat & WDERR_ABORT)) { wderror(bp, du, "reverting to PIO mode"); du->dk_flags &= ~DKFL_USEDMA; } else if((du->dk_flags & DKFL_MULTI) && (errstat & WDERR_ABORT)) { wderror(bp, du, "reverting to non-multi sector mode"); du->dk_multi = 1; } if (!(du->dk_status & (WDCS_ERR | WDCS_ECCCOR)) && (((du->dk_flags & (DKFL_DMA|DKFL_SINGLE)) == DKFL_DMA) && (dmastat != WDDS_INTERRUPT))) printf("wd%d: DMA failure, DMA status %b\n", du->dk_lunit, dmastat, WDDS_BITS); #ifdef WDDEBUG wderror(bp, du, "wdintr"); #endif if ((du->dk_flags & DKFL_SINGLE) == 0) { du->dk_flags |= DKFL_ERROR; goto outt; } #ifdef B_FORMAT if (bp->b_flags & B_FORMAT) { bp->b_error = EIO; bp->b_flags |= B_ERROR; goto done; } #endif if (du->dk_flags & DKFL_BADSCAN) { bp->b_error = EIO; bp->b_flags |= B_ERROR; } else if (du->dk_status & WDCS_ERR) { if (++wdtab[unit].b_errcnt < RETRIES) { wdtab[unit].b_active = 0; } else { wderror(bp, du, "hard error"); bp->b_error = EIO; bp->b_flags |= B_ERROR; /* flag the error */ } } else if (du->dk_status & WDCS_ECCCOR) wderror(bp, du, "soft ecc"); } /* * If this was a successful read operation, fetch the data. */ if (((bp->b_flags & (B_READ | B_ERROR)) == B_READ) && !((du->dk_flags & (DKFL_DMA|DKFL_SINGLE)) == DKFL_DMA) && wdtab[unit].b_active) { u_int chk, dummy, multisize; multisize = chk = du->dk_currentiosize * DEV_BSIZE; if( du->dk_bc < chk) { chk = du->dk_bc; if( ((chk + DEV_BSIZE - 1) / DEV_BSIZE) < du->dk_currentiosize) { du->dk_currentiosize = (chk + DEV_BSIZE - 1) / DEV_BSIZE; multisize = du->dk_currentiosize * DEV_BSIZE; } } /* ready to receive data? */ if ((du->dk_status & (WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ)) != (WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ)) wderror(bp, du, "wdintr: read intr arrived early"); if (wdwait(du, WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ, TIMEOUT) != 0) { wderror(bp, du, "wdintr: read error detected late"); goto oops; } /* suck in data */ if( du->dk_flags & DKFL_32BIT) insl(du->dk_port + wd_data, (void *)((int)bp->b_data + du->dk_skip * DEV_BSIZE), chk / sizeof(long)); else insw(du->dk_port + wd_data, (void *)((int)bp->b_data + du->dk_skip * DEV_BSIZE), chk / sizeof(short)); du->dk_bc -= chk; /* XXX for obsolete fractional sector reads. */ while (chk < multisize) { insw(du->dk_port + wd_data, &dummy, 1); chk += sizeof(short); } } /* final cleanup on DMA */ if (((bp->b_flags & B_ERROR) == 0) && ((du->dk_flags & (DKFL_DMA|DKFL_SINGLE)) == DKFL_DMA) && wdtab[unit].b_active) { int iosize; iosize = du->dk_currentiosize * DEV_BSIZE; du->dk_bc -= iosize; } outt: if (wdtab[unit].b_active) { if ((bp->b_flags & B_ERROR) == 0) { du->dk_skip += du->dk_currentiosize;/* add to successful sectors */ if (wdtab[unit].b_errcnt) wderror(bp, du, "soft error"); wdtab[unit].b_errcnt = 0; /* see if more to transfer */ if (du->dk_bc > 0 && (du->dk_flags & DKFL_ERROR) == 0) { if( (du->dk_flags & DKFL_SINGLE) || ((bp->b_flags & B_READ) == 0)) { wdtab[unit].b_active = 0; wdstart(unit); } else { du->dk_timeout = 1 + 3; } return; /* next chunk is started */ } else if ((du->dk_flags & (DKFL_SINGLE | DKFL_ERROR)) == DKFL_ERROR) { du->dk_skip = 0; du->dk_flags &= ~DKFL_ERROR; du->dk_flags |= DKFL_SINGLE; wdtab[unit].b_active = 0; wdstart(unit); return; /* redo xfer sector by sector */ } } done: ; /* done with this transfer, with or without error */ du->dk_flags &= ~(DKFL_SINGLE|DKFL_DMA); bufq_remove( &wdtab[unit].controller_queue, bp); wdtab[unit].b_errcnt = 0; bp->b_resid = bp->b_bcount - du->dk_skip * DEV_BSIZE; wdutab[du->dk_lunit].b_active = 0; du->dk_skip = 0; /* Update device stats */ devstat_end_transaction(&du->dk_stats, bp->b_bcount - bp->b_resid, DEVSTAT_TAG_NONE, (bp->b_flags & B_READ) ? DEVSTAT_READ : DEVSTAT_WRITE); biodone(bp); } /* controller idle */ wdtab[unit].b_active = 0; /* anything more on drive queue? */ wdustart(du); /* anything more for controller to do? */ wdstart(unit); } /* * Initialize a drive. */ int wdopen(dev_t dev, int flags, int fmt, struct proc *p) { register unsigned int lunit; register struct disk *du; int error; lunit = dkunit(dev); if (lunit >= NWD || dktype(dev) != 0) return (ENXIO); du = wddrives[lunit]; if (du == NULL) return (ENXIO); /* Finish flushing IRQs left over from wdattach(). */ if (wdtab[du->dk_ctrlr_cmd640].b_active == 2) wdtab[du->dk_ctrlr_cmd640].b_active = 0; du->dk_flags &= ~DKFL_BADSCAN; /* spin waiting for anybody else reading the disk label */ while (du->dk_flags & DKFL_LABELLING) tsleep((caddr_t)&du->dk_flags, PZERO - 1, "wdopen", 1); #if 1 wdsleep(du->dk_ctrlr, "wdopn1"); du->dk_flags |= DKFL_LABELLING; du->dk_state = WANTOPEN; { struct disklabel label; bzero(&label, sizeof label); label.d_secsize = du->dk_dd.d_secsize; label.d_nsectors = du->dk_dd.d_nsectors; label.d_ntracks = du->dk_dd.d_ntracks; label.d_ncylinders = du->dk_dd.d_ncylinders; label.d_secpercyl = du->dk_dd.d_secpercyl; label.d_secperunit = du->dk_dd.d_secperunit; error = dsopen("wd", dev, fmt, 0, &du->dk_slices, &label, wdstrategy1, (ds_setgeom_t *)NULL, &wd_cdevsw); } du->dk_flags &= ~DKFL_LABELLING; wdsleep(du->dk_ctrlr, "wdopn2"); return (error); #else if ((du->dk_flags & DKFL_BSDLABEL) == 0) { /* * wdtab[ctrlr].b_active != 0 implies XXX applicable now ?? * drive_queue[lunit].b_act == NULL (?) XXX applicable now ?? * so the following guards most things (until the next i/o). * It doesn't guard against a new i/o starting and being * affected by the label being changed. Sigh. */ wdsleep(du->dk_ctrlr, "wdopn1"); du->dk_flags |= DKFL_LABELLING; du->dk_state = WANTOPEN; error = dsinit(dkmodpart(dev, RAW_PART), wdstrategy, &du->dk_dd, &du->dk_slices); if (error != 0) { du->dk_flags &= ~DKFL_LABELLING; return (error); } /* XXX check value returned by wdwsetctlr(). */ wdwsetctlr(du); if (dkslice(dev) == WHOLE_DISK_SLICE) { dsopen(dev, fmt, du->dk_slices); return (0); } /* * Read label using RAW_PART partition. * * If the drive has an MBR, then the current geometry (from * wdgetctlr()) is used to read it; then the BIOS/DOS * geometry is inferred and used to read the label off the * 'c' partition. Otherwise the label is read using the * current geometry. The label gives the final geometry. * If bad sector handling is enabled, then this geometry * is used to read the bad sector table. The geometry * changes occur inside readdisklabel() and are propagated * to the driver by resetting the state machine. * * XXX can now handle changes directly since dsinit() doesn't * do too much. */ msg = correct_readdisklabel(dkmodpart(dev, RAW_PART), wdstrategy, &du->dk_dd); /* XXX check value returned by wdwsetctlr(). */ wdwsetctlr(du); if (msg == NULL && du->dk_dd.d_flags & D_BADSECT) msg = readbad144(dkmodpart(dev, RAW_PART), wdstrategy, &du->dk_dd, &du->dk_bad); du->dk_flags &= ~DKFL_LABELLING; if (msg != NULL) { log(LOG_WARNING, "wd%d: cannot find label (%s)\n", lunit, msg); if (part != RAW_PART) return (EINVAL); /* XXX needs translation */ /* * Soon return. This is how slices without labels * are allowed. They only work on the raw partition. */ } else { unsigned long newsize, offset, size; #if 0 /* * Force RAW_PART partition to be the whole disk. */ offset = du->dk_dd.d_partitions[RAW_PART].p_offset; if (offset != 0) { printf( "wd%d: changing offset of '%c' partition from %lu to 0\n", du->dk_lunit, 'a' + RAW_PART, offset); du->dk_dd.d_partitions[RAW_PART].p_offset = 0; } size = du->dk_dd.d_partitions[RAW_PART].p_size; newsize = du->dk_dd.d_secperunit; /* XXX */ if (size != newsize) { printf( "wd%d: changing size of '%c' partition from %lu to %lu\n", du->dk_lunit, 'a' + RAW_PART, size, newsize); du->dk_dd.d_partitions[RAW_PART].p_size = newsize; } #endif } /* Pick up changes made by readdisklabel(). */ wdsleep(du->dk_ctrlr, "wdopn2"); du->dk_state = WANTOPEN; } /* * Warn if a partion is opened that overlaps another partition which * is open unless one is the "raw" partition (whole disk). */ if ((du->dk_openpart & mask) == 0 && part != RAW_PART) { int start, end; pp = &du->dk_dd.d_partitions[part]; start = pp->p_offset; end = pp->p_offset + pp->p_size; for (pp = du->dk_dd.d_partitions; pp < &du->dk_dd.d_partitions[du->dk_dd.d_npartitions]; pp++) { if (pp->p_offset + pp->p_size <= start || pp->p_offset >= end) continue; if (pp - du->dk_dd.d_partitions == RAW_PART) continue; if (du->dk_openpart & (1 << (pp - du->dk_dd.d_partitions))) log(LOG_WARNING, "wd%d%c: overlaps open partition (%c)\n", lunit, part + 'a', pp - du->dk_dd.d_partitions + 'a'); } } if (part >= du->dk_dd.d_npartitions && part != RAW_PART) return (ENXIO); dsopen(dev, fmt, du->dk_slices); return (0); #endif } /* * Implement operations other than read/write. * Called from wdstart or wdintr during opens and formats. * Uses finite-state-machine to track progress of operation in progress. * Returns 0 if operation still in progress, 1 if completed, 2 if error. */ static int wdcontrol(register struct buf *bp) { register struct disk *du; int ctrlr; du = wddrives[dkunit(bp->b_dev)]; ctrlr = du->dk_ctrlr_cmd640; switch (du->dk_state) { case WANTOPEN: tryagainrecal: wdtab[ctrlr].b_active = 1; if (wdcommand(du, 0, 0, 0, 0, WDCC_RESTORE | WD_STEP) != 0) { wderror(bp, du, "wdcontrol: wdcommand failed"); goto maybe_retry; } du->dk_state = RECAL; return (0); case RECAL: if (du->dk_status & WDCS_ERR || wdsetctlr(du) != 0) { wderror(bp, du, "wdcontrol: recal failed"); maybe_retry: if (du->dk_status & WDCS_ERR) wdunwedge(du); du->dk_state = WANTOPEN; if (++wdtab[ctrlr].b_errcnt < RETRIES) goto tryagainrecal; bp->b_error = ENXIO; /* XXX needs translation */ bp->b_flags |= B_ERROR; return (2); } wdtab[ctrlr].b_errcnt = 0; du->dk_state = OPEN; /* * The rest of the initialization can be done by normal * means. */ return (1); } panic("wdcontrol"); return (2); } /* * Wait uninterruptibly until controller is not busy, then send it a command. * The wait usually terminates immediately because we waited for the previous * command to terminate. */ static int wdcommand(struct disk *du, u_int cylinder, u_int head, u_int sector, u_int count, u_int command) { u_int wdc; wdc = du->dk_port; if (du->cfg_flags & WDOPT_SLEEPHACK) { /* OK, so the APM bios has put the disk into SLEEP mode, * how can we tell ? Uhm, we can't. There is no * standardized way of finding out, and the only way to * wake it up is to reset it. Bummer. * * All the many and varied versions of the IDE/ATA standard * explicitly tells us not to look at these registers if * the disk is in SLEEP mode. Well, too bad really, we * have to find out if it's in sleep mode before we can * avoid reading the registers. * * I have reason to belive that most disks will return * either 0xff or 0x00 in all but the status register * when in SLEEP mode, but I have yet to see one return * 0x00, so we don't check for that yet. * * The check for WDCS_BUSY is for the case where the * bios spins up the disk for us, but doesn't initialize * it correctly /phk */ if(inb(wdc + wd_precomp) + inb(wdc + wd_cyl_lo) + inb(wdc + wd_cyl_hi) + inb(wdc + wd_sdh) + inb(wdc + wd_sector) + inb(wdc + wd_seccnt) == 6 * 0xff) { if (bootverbose) printf("wd(%d,%d): disk aSLEEP\n", du->dk_ctrlr, du->dk_unit); wdunwedge(du); } else if(inb(wdc + wd_status) == WDCS_BUSY) { if (bootverbose) printf("wd(%d,%d): disk is BUSY\n", du->dk_ctrlr, du->dk_unit); wdunwedge(du); } } if (wdwait(du, 0, TIMEOUT) < 0) return (1); if( command == WDCC_FEATURES) { outb(wdc + wd_sdh, WDSD_IBM | (du->dk_unit << 4) | head); outb(wdc + wd_features, count); if ( count == WDFEA_SETXFER ) outb(wdc + wd_seccnt, sector); } else { outb(wdc + wd_precomp, du->dk_dd.d_precompcyl / 4); outb(wdc + wd_cyl_lo, cylinder); outb(wdc + wd_cyl_hi, cylinder >> 8); outb(wdc + wd_sdh, WDSD_IBM | (du->dk_unit << 4) | head); if (head & WDSD_LBA) outb(wdc + wd_sector, sector); else outb(wdc + wd_sector, sector + 1); outb(wdc + wd_seccnt, count); } if (wdwait(du, (command == WDCC_DIAGNOSE || command == WDCC_IDC) ? 0 : WDCS_READY, TIMEOUT) < 0) return (1); outb(wdc + wd_command, command); return (0); } static void wdsetmulti(struct disk *du) { /* * The config option flags low 8 bits define the maximum multi-block * transfer size. If the user wants the maximum that the drive * is capable of, just set the low bits of the config option to * 0x00ff. */ if ((du->cfg_flags & WDOPT_MULTIMASK) != 0 && (du->dk_multi > 1)) { int configval = du->cfg_flags & WDOPT_MULTIMASK; du->dk_multi = min(du->dk_multi, configval); if (wdcommand(du, 0, 0, 0, du->dk_multi, WDCC_SET_MULTI)) { du->dk_multi = 1; } else { if (wdwait(du, WDCS_READY, TIMEOUT) < 0) { du->dk_multi = 1; } } } else { du->dk_multi = 1; } } /* * issue IDC to drive to tell it just what geometry it is to be. */ static int wdsetctlr(struct disk *du) { int error = 0; #ifdef WDDEBUG printf("wd(%d,%d): wdsetctlr: C %lu H %lu S %lu\n", du->dk_ctrlr, du->dk_unit, du->dk_dd.d_ncylinders, du->dk_dd.d_ntracks, du->dk_dd.d_nsectors); #endif if (!(du->dk_flags & DKFL_LBA)) { if (du->dk_dd.d_ntracks == 0 || du->dk_dd.d_ntracks > 16) { struct wdparams *wp; printf("wd%d: can't handle %lu heads from partition table ", du->dk_lunit, du->dk_dd.d_ntracks); /* obtain parameters */ wp = &du->dk_params; if (wp->wdp_heads > 0 && wp->wdp_heads <= 16) { printf("(controller value %u restored)\n", wp->wdp_heads); du->dk_dd.d_ntracks = wp->wdp_heads; } else { printf("(truncating to 16)\n"); du->dk_dd.d_ntracks = 16; } } if (du->dk_dd.d_nsectors == 0 || du->dk_dd.d_nsectors > 255) { printf("wd%d: cannot handle %lu sectors (max 255)\n", du->dk_lunit, du->dk_dd.d_nsectors); error = 1; } if (error) { wdtab[du->dk_ctrlr_cmd640].b_errcnt += RETRIES; return (1); } if (wdcommand(du, du->dk_dd.d_ncylinders, du->dk_dd.d_ntracks - 1, 0, du->dk_dd.d_nsectors, WDCC_IDC) != 0 || wdwait(du, WDCS_READY, TIMEOUT) < 0) { wderror((struct buf *)NULL, du, "wdsetctlr failed"); return (1); } } wdsetmulti(du); #ifdef NOTYET /* set read caching and write caching */ wdcommand(du, 0, 0, 0, WDFEA_RCACHE, WDCC_FEATURES); wdwait(du, WDCS_READY, TIMEOUT); wdcommand(du, 0, 0, 0, WDFEA_WCACHE, WDCC_FEATURES); wdwait(du, WDCS_READY, TIMEOUT); #endif return (0); } #if 0 /* * Wait until driver is inactive, then set up controller. */ static int wdwsetctlr(struct disk *du) { int stat; int x; wdsleep(du->dk_ctrlr, "wdwset"); x = splbio(); stat = wdsetctlr(du); wdflushirq(du, x); splx(x); return (stat); } #endif /* * gross little callback function for wdddma interface. returns 1 for * success, 0 for failure. */ static int wdsetmode(int mode, void *wdinfo) { int i; struct disk *du; du = wdinfo; if (bootverbose) printf("wd%d: wdsetmode() setting transfer mode to %02x\n", du->dk_lunit, mode); i = wdcommand(du, 0, 0, mode, WDFEA_SETXFER, WDCC_FEATURES) == 0 && wdwait(du, WDCS_READY, TIMEOUT) == 0; return i; } /* * issue READP to drive to ask it what it is. */ static int wdgetctlr(struct disk *du) { int i; char tb[DEV_BSIZE], tb2[DEV_BSIZE]; struct wdparams *wp = NULL; u_long flags = du->cfg_flags; again: if (wdcommand(du, 0, 0, 0, 0, WDCC_READP) != 0 || wdwait(du, WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ, TIMEOUT) != 0) { /* * if we failed on the second try, assume non-32bit */ if( du->dk_flags & DKFL_32BIT) goto failed; /* XXX need to check error status after final transfer. */ /* * Old drives don't support WDCC_READP. Try a seek to 0. * Some IDE controllers return trash if there is no drive * attached, so first test that the drive can be selected. * This also avoids long waits for nonexistent drives. */ if (wdwait(du, 0, TIMEOUT) < 0) return (1); outb(du->dk_port + wd_sdh, WDSD_IBM | (du->dk_unit << 4)); DELAY(5000); /* usually unnecessary; drive select is fast */ /* * Do this twice: may get a false WDCS_READY the first time. */ inb(du->dk_port + wd_status); if ((inb(du->dk_port + wd_status) & (WDCS_BUSY | WDCS_READY)) != WDCS_READY || wdcommand(du, 0, 0, 0, 0, WDCC_RESTORE | WD_STEP) != 0 || wdwait(du, WDCS_READY | WDCS_SEEKCMPLT, TIMEOUT) != 0) return (1); if (du->dk_unit == bootinfo.bi_n_bios_used) { du->dk_dd.d_secsize = DEV_BSIZE; du->dk_dd.d_nsectors = bootinfo.bi_bios_geom[du->dk_unit] & 0xff; du->dk_dd.d_ntracks = ((bootinfo.bi_bios_geom[du->dk_unit] >> 8) & 0xff) + 1; /* XXX Why 2 ? */ du->dk_dd.d_ncylinders = (bootinfo.bi_bios_geom[du->dk_unit] >> 16) + 2; du->dk_dd.d_secpercyl = du->dk_dd.d_ntracks * du->dk_dd.d_nsectors; du->dk_dd.d_secperunit = du->dk_dd.d_secpercyl * du->dk_dd.d_ncylinders; #if 0 du->dk_dd.d_partitions[WDRAW].p_size = du->dk_dd.d_secperunit; du->dk_dd.d_type = DTYPE_ST506; du->dk_dd.d_subtype |= DSTYPE_GEOMETRY; strncpy(du->dk_dd.d_typename, "Bios geometry", sizeof du->dk_dd.d_typename); strncpy(du->dk_params.wdp_model, "ST506", sizeof du->dk_params.wdp_model); #endif bootinfo.bi_n_bios_used ++; return 0; } /* * Fake minimal drive geometry for reading the MBR. * readdisklabel() may enlarge it to read the label and the * bad sector table. */ du->dk_dd.d_secsize = DEV_BSIZE; du->dk_dd.d_nsectors = 17; du->dk_dd.d_ntracks = 1; du->dk_dd.d_ncylinders = 1; du->dk_dd.d_secpercyl = 17; du->dk_dd.d_secperunit = 17; #if 0 /* * Fake maximal drive size for writing the label. */ du->dk_dd.d_partitions[RAW_PART].p_size = 64 * 16 * 1024; /* * Fake some more of the label for printing by disklabel(1) * in case there is no real label. */ du->dk_dd.d_type = DTYPE_ST506; du->dk_dd.d_subtype |= DSTYPE_GEOMETRY; strncpy(du->dk_dd.d_typename, "Fake geometry", sizeof du->dk_dd.d_typename); #endif /* Fake the model name for printing by wdattach(). */ strncpy(du->dk_params.wdp_model, "unknown", sizeof du->dk_params.wdp_model); return (0); } /* obtain parameters */ wp = &du->dk_params; if (du->dk_flags & DKFL_32BIT) insl(du->dk_port + wd_data, tb, sizeof(tb) / sizeof(long)); else insw(du->dk_port + wd_data, tb, sizeof(tb) / sizeof(short)); /* try 32-bit data path (VLB IDE controller) */ if (flags & WDOPT_32BIT) { if (! (du->dk_flags & DKFL_32BIT)) { bcopy(tb, tb2, sizeof(struct wdparams)); du->dk_flags |= DKFL_32BIT; goto again; } /* check that we really have 32-bit controller */ if (bcmp (tb, tb2, sizeof(struct wdparams)) != 0) { failed: /* test failed, use 16-bit i/o mode */ bcopy(tb2, tb, sizeof(struct wdparams)); du->dk_flags &= ~DKFL_32BIT; } } bcopy(tb, wp, sizeof(struct wdparams)); /* shuffle string byte order */ for (i = 0; (unsigned)i < sizeof(wp->wdp_model); i += 2) { u_short *p; p = (u_short *) (wp->wdp_model + i); *p = ntohs(*p); } /* * Clean up the wdp_model by converting nulls to spaces, and * then removing the trailing spaces. */ for (i = 0; (unsigned)i < sizeof(wp->wdp_model); i++) { if (wp->wdp_model[i] == '\0') { wp->wdp_model[i] = ' '; } } for (i = sizeof(wp->wdp_model) - 1; (i >= 0 && wp->wdp_model[i] == ' '); i--) { wp->wdp_model[i] = '\0'; } /* * find out the drives maximum multi-block transfer capability */ du->dk_multi = wp->wdp_nsecperint & 0xff; wdsetmulti(du); /* * check drive's DMA capability */ if (wddma[du->dk_interface].wdd_candma) { du->dk_dmacookie = wddma[du->dk_interface].wdd_candma( du->dk_port, du->dk_ctrlr, du->dk_unit); /* does user want this? */ if ((du->cfg_flags & WDOPT_DMA) && /* have we got a DMA controller? */ du->dk_dmacookie && /* can said drive do DMA? */ wddma[du->dk_interface].wdd_dmainit(du->dk_dmacookie, wp, wdsetmode, du)) { du->dk_flags |= DKFL_USEDMA; } } else { du->dk_dmacookie = NULL; } #ifdef WDDEBUG printf( "\nwd(%d,%d): wdgetctlr: gc %x cyl %d trk %d sec %d type %d sz %d model %s\n", du->dk_ctrlr, du->dk_unit, wp->wdp_config, wp->wdp_cylinders, wp->wdp_heads, wp->wdp_sectors, wp->wdp_buffertype, wp->wdp_buffersize, wp->wdp_model); #endif /* update disklabel given drive information */ du->dk_dd.d_secsize = DEV_BSIZE; if ((du->cfg_flags & WDOPT_LBA) && wp->wdp_lbasize) { du->dk_dd.d_nsectors = 63; if (wp->wdp_lbasize < 16*63*1024) { /* <=528.4 MB */ du->dk_dd.d_ntracks = 16; } else if (wp->wdp_lbasize < 32*63*1024) { /* <=1.057 GB */ du->dk_dd.d_ntracks = 32; } else if (wp->wdp_lbasize < 64*63*1024) { /* <=2.114 GB */ du->dk_dd.d_ntracks = 64; } else if (wp->wdp_lbasize < 128*63*1024) { /* <=4.228 GB */ du->dk_dd.d_ntracks = 128; } else if (wp->wdp_lbasize < 255*63*1024) { /* <=8.422 GB */ du->dk_dd.d_ntracks = 255; } else { /* >8.422 GB */ du->dk_dd.d_ntracks = 255; /* XXX */ } du->dk_dd.d_secpercyl= du->dk_dd.d_ntracks*du->dk_dd.d_nsectors; du->dk_dd.d_ncylinders = wp->wdp_lbasize/du->dk_dd.d_secpercyl; du->dk_dd.d_secperunit = wp->wdp_lbasize; du->dk_flags |= DKFL_LBA; } else { du->dk_dd.d_ncylinders = wp->wdp_cylinders; /* +- 1 */ du->dk_dd.d_ntracks = wp->wdp_heads; du->dk_dd.d_nsectors = wp->wdp_sectors; du->dk_dd.d_secpercyl = du->dk_dd.d_ntracks * du->dk_dd.d_nsectors; du->dk_dd.d_secperunit = du->dk_dd.d_secpercyl * du->dk_dd.d_ncylinders; if (wp->wdp_cylinders == 16383 && du->dk_dd.d_secperunit < wp->wdp_lbasize) { du->dk_dd.d_secperunit = wp->wdp_lbasize; du->dk_dd.d_ncylinders = du->dk_dd.d_secperunit / du->dk_dd.d_secpercyl; } } if (WDOPT_FORCEHD(du->cfg_flags)) { du->dk_dd.d_ntracks = WDOPT_FORCEHD(du->cfg_flags); du->dk_dd.d_secpercyl = du->dk_dd.d_ntracks * du->dk_dd.d_nsectors; du->dk_dd.d_ncylinders = du->dk_dd.d_secperunit / du->dk_dd.d_secpercyl; } #if 0 du->dk_dd.d_partitions[RAW_PART].p_size = du->dk_dd.d_secperunit; /* dubious ... */ bcopy("ESDI/IDE", du->dk_dd.d_typename, 9); bcopy(wp->wdp_model + 20, du->dk_dd.d_packname, 14 - 1); /* better ... */ du->dk_dd.d_type = DTYPE_ESDI; du->dk_dd.d_subtype |= DSTYPE_GEOMETRY; #endif return (0); } int wdclose(dev_t dev, int flags, int fmt, struct proc *p) { dsclose(dev, fmt, wddrives[dkunit(dev)]->dk_slices); return (0); } int wdioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) { int lunit = dkunit(dev); register struct disk *du; int error; #ifdef notyet struct uio auio; struct iovec aiov; struct format_op *fop; #endif du = wddrives[lunit]; wdsleep(du->dk_ctrlr, "wdioct"); error = dsioctl("wd", dev, cmd, addr, flags, &du->dk_slices, wdstrategy1, (ds_setgeom_t *)NULL); if (error != ENOIOCTL) return (error); switch (cmd) { case DIOCSBADSCAN: if (*(int *)addr) du->dk_flags |= DKFL_BADSCAN; else du->dk_flags &= ~DKFL_BADSCAN; return (0); #ifdef notyet case DIOCWFORMAT: if (!(flag & FWRITE)) return (EBADF); fop = (struct format_op *)addr; aiov.iov_base = fop->df_buf; aiov.iov_len = fop->df_count; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_resid = fop->df_count; auio.uio_segflg = 0; auio.uio_offset = fop->df_startblk * du->dk_dd.d_secsize; #error /* XXX the 386BSD interface is different */ error = physio(wdformat, &rwdbuf[lunit], 0, dev, B_WRITE, minphys, &auio); fop->df_count -= auio.uio_resid; fop->df_reg[0] = du->dk_status; fop->df_reg[1] = du->dk_error; return (error); #endif default: return (ENOTTY); } } #ifdef B_FORMAT int wdformat(struct buf *bp) { bp->b_flags |= B_FORMAT; wdstrategy(bp); /* * phk put this here, better that return(wdstrategy(bp)); * XXX */ return -1; } #endif int wdsize(dev_t dev) { struct disk *du; int lunit; lunit = dkunit(dev); if (lunit >= NWD || dktype(dev) != 0) return (-1); du = wddrives[lunit]; if (du == NULL) return (-1); return (dssize(dev, &du->dk_slices, wdopen, wdclose)); } int wddump(dev_t dev) { register struct disk *du; struct disklabel *lp; long num; /* number of sectors to write */ int lunit, part; long blkoff, blknum; long blkchk, blkcnt, blknext; u_long ds_offset; u_long nblocks; static int wddoingadump = 0; long cylin, head, sector; long secpertrk, secpercyl; char *addr; /* Toss any characters present prior to dump. */ while (cncheckc() != -1) ; /* Check for acceptable device. */ /* XXX should reset to maybe allow du->dk_state < OPEN. */ lunit = dkunit(dev); /* eventually support floppies? */ part = dkpart(dev); if (lunit >= NWD || (du = wddrives[lunit]) == NULL || du->dk_state < OPEN || (lp = dsgetlabel(dev, du->dk_slices)) == NULL) return (ENXIO); /* Size of memory to dump, in disk sectors. */ num = (u_long)Maxmem * PAGE_SIZE / du->dk_dd.d_secsize; secpertrk = du->dk_dd.d_nsectors; secpercyl = du->dk_dd.d_secpercyl; nblocks = lp->d_partitions[part].p_size; blkoff = lp->d_partitions[part].p_offset; /* XXX */ ds_offset = du->dk_slices->dss_slices[dkslice(dev)].ds_offset; blkoff += ds_offset; #if 0 pg("part %x, nblocks %d, dumplo %d num %d\n", part, nblocks, dumplo, num); #endif /* Check transfer bounds against partition size. */ if (dumplo < 0 || dumplo + num > nblocks) return (EINVAL); /* Check if we are being called recursively. */ if (wddoingadump) return (EFAULT); #if 0 /* Mark controller active for if we panic during the dump. */ wdtab[du->dk_ctrlr].b_active = 1; #endif wddoingadump = 1; /* Recalibrate the drive. */ DELAY(5); /* ATA spec XXX NOT */ if (wdcommand(du, 0, 0, 0, 0, WDCC_RESTORE | WD_STEP) != 0 || wdwait(du, WDCS_READY | WDCS_SEEKCMPLT, TIMEOUT) != 0 || wdsetctlr(du) != 0) { wderror((struct buf *)NULL, du, "wddump: recalibrate failed"); return (EIO); } du->dk_flags |= DKFL_SINGLE; addr = (char *) 0; blknum = dumplo + blkoff; while (num > 0) { blkcnt = num; if (blkcnt > MAXTRANSFER) blkcnt = MAXTRANSFER; /* Keep transfer within current cylinder. */ if ((blknum + blkcnt - 1) / secpercyl != blknum / secpercyl) blkcnt = secpercyl - (blknum % secpercyl); blknext = blknum + blkcnt; /* * See if one of the sectors is in the bad sector list * (if we have one). If the first sector is bad, then * reduce the transfer to this one bad sector; if another * sector is bad, then reduce reduce the transfer to * avoid any bad sectors. */ if (du->dk_flags & DKFL_SINGLE && dsgetbad(dev, du->dk_slices) != NULL) { for (blkchk = blknum; blkchk < blknum + blkcnt; blkchk++) { daddr_t blknew; blknew = transbad144(dsgetbad(dev, du->dk_slices), blkchk - ds_offset) + ds_offset; if (blknew != blkchk) { /* Found bad block. */ blkcnt = blkchk - blknum; if (blkcnt > 0) { blknext = blknum + blkcnt; goto out; } blkcnt = 1; blknext = blknum + blkcnt; #if 1 || defined(WDDEBUG) printf("bad block %ld -> %ld\n", (long)blknum, (long)blknew); #endif break; } } } out: /* Compute disk address. */ cylin = blknum / secpercyl; head = (blknum % secpercyl) / secpertrk; sector = blknum % secpertrk; #if 0 /* Let's just talk about this first... */ pg("cylin l%d head %ld sector %ld addr 0x%x count %ld", cylin, head, sector, addr, blkcnt); #endif /* Do the write. */ if (wdcommand(du, cylin, head, sector, blkcnt, WDCC_WRITE) != 0) { wderror((struct buf *)NULL, du, "wddump: timeout waiting to to give command"); return (EIO); } while (blkcnt != 0) { if (is_physical_memory((vm_offset_t)addr)) pmap_enter(kernel_pmap, (vm_offset_t)CADDR1, trunc_page((vm_offset_t)addr), VM_PROT_READ, TRUE); else pmap_enter(kernel_pmap, (vm_offset_t)CADDR1, trunc_page(0), VM_PROT_READ, TRUE); /* Ready to send data? */ DELAY(5); /* ATA spec */ if (wdwait(du, WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ, TIMEOUT) < 0) { wderror((struct buf *)NULL, du, "wddump: timeout waiting for DRQ"); return (EIO); } if (du->dk_flags & DKFL_32BIT) outsl(du->dk_port + wd_data, CADDR1 + ((int)addr & PAGE_MASK), DEV_BSIZE / sizeof(long)); else outsw(du->dk_port + wd_data, CADDR1 + ((int)addr & PAGE_MASK), DEV_BSIZE / sizeof(short)); addr += DEV_BSIZE; /* * If we are dumping core, it may take a while. * So reassure the user and hold off any watchdogs. */ if ((unsigned)addr % (1024 * 1024) == 0) { #ifdef HW_WDOG if (wdog_tickler) (*wdog_tickler)(); #endif /* HW_WDOG */ printf("%ld ", num / (1024 * 1024 / DEV_BSIZE)); } num--; blkcnt--; } /* Wait for completion. */ DELAY(5); /* ATA spec XXX NOT */ if (wdwait(du, WDCS_READY | WDCS_SEEKCMPLT, TIMEOUT) < 0) { wderror((struct buf *)NULL, du, "wddump: timeout waiting for status"); return (EIO); } /* Check final status. */ if ((du->dk_status & (WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ | WDCS_ERR)) != (WDCS_READY | WDCS_SEEKCMPLT)) { wderror((struct buf *)NULL, du, "wddump: extra DRQ, or error"); return (EIO); } /* Update block count. */ blknum = blknext; /* Operator aborting dump? */ if (cncheckc() != -1) return (EINTR); } return (0); } static void wderror(struct buf *bp, struct disk *du, char *mesg) { if (bp == NULL) printf("wd%d: %s", du->dk_lunit, mesg); else diskerr(bp, "wd", mesg, LOG_PRINTF, du->dk_skip, dsgetlabel(bp->b_dev, du->dk_slices)); printf(" (status %b error %b)\n", du->dk_status, WDCS_BITS, du->dk_error, WDERR_BITS); } /* * Discard any interrupts that were latched by the interrupt system while * we were doing polled i/o. */ static void wdflushirq(struct disk *du, int old_ipl) { wdtab[du->dk_ctrlr_cmd640].b_active = 2; splx(old_ipl); (void)splbio(); wdtab[du->dk_ctrlr_cmd640].b_active = 0; } /* * Reset the controller. */ static int wdreset(struct disk *du) { int err = 0; if ((du->dk_flags & (DKFL_DMA|DKFL_SINGLE)) == DKFL_DMA) wddma[du->dk_interface].wdd_dmadone(du->dk_dmacookie); (void)wdwait(du, 0, TIMEOUT); outb(du->dk_altport, WDCTL_IDS | WDCTL_RST); DELAY(10 * 1000); outb(du->dk_altport, WDCTL_IDS); outb(du->dk_port + wd_sdh, WDSD_IBM | (du->dk_unit << 4)); if (wdwait(du, 0, TIMEOUT) != 0) err = 1; /* no IDE drive found */ du->dk_error = inb(du->dk_port + wd_error); if (du->dk_error != 0x01) err = 1; /* the drive is incompatible */ outb(du->dk_altport, WDCTL_4BIT); return (err); } /* * Sleep until driver is inactive. * This is used only for avoiding rare race conditions, so it is unimportant * that the sleep may be far too short or too long. */ static void wdsleep(int ctrlr, char *wmesg) { int s = splbio(); if (eide_quirks & Q_CMD640B) ctrlr = PRIMARY; while (wdtab[ctrlr].b_active) tsleep((caddr_t)&wdtab[ctrlr].b_active, PZERO - 1, wmesg, 1); splx(s); } static void wdtimeout(void *cdu) { struct disk *du; int x; static int timeouts; du = (struct disk *)cdu; x = splbio(); if (du->dk_timeout != 0 && --du->dk_timeout == 0) { if(timeouts++ <= 5) { char *msg; msg = (timeouts > 5) ? "Last time I say: interrupt timeout. Probably a portable PC." : "interrupt timeout"; wderror((struct buf *)NULL, du, msg); if (du->dk_dmacookie) printf("wd%d: wdtimeout() DMA status %b\n", du->dk_lunit, wddma[du->dk_interface].wdd_dmastatus(du->dk_dmacookie), WDDS_BITS); } wdunwedge(du); wdflushirq(du, x); du->dk_skip = 0; du->dk_flags |= DKFL_SINGLE; wdstart(du->dk_ctrlr); } timeout(wdtimeout, cdu, hz); splx(x); } /* * Reset the controller after it has become wedged. This is different from * wdreset() so that wdreset() can be used in the probe and so that this * can restore the geometry . */ static int wdunwedge(struct disk *du) { struct disk *du1; int lunit; /* Schedule other drives for recalibration. */ for (lunit = 0; lunit < NWD; lunit++) if ((du1 = wddrives[lunit]) != NULL && du1 != du && du1->dk_ctrlr == du->dk_ctrlr && du1->dk_state > WANTOPEN) du1->dk_state = WANTOPEN; DELAY(RECOVERYTIME); if (wdreset(du) == 0) { /* * XXX - recalibrate current drive now because some callers * aren't prepared to have its state change. */ if (wdcommand(du, 0, 0, 0, 0, WDCC_RESTORE | WD_STEP) == 0 && wdwait(du, WDCS_READY | WDCS_SEEKCMPLT, TIMEOUT) == 0 && wdsetctlr(du) == 0) return (0); } wderror((struct buf *)NULL, du, "wdunwedge failed"); return (1); } /* * Wait uninterruptibly until controller is not busy and either certain * status bits are set or an error has occurred. * The wait is usually short unless it is for the controller to process * an entire critical command. * Return 1 for (possibly stale) controller errors, -1 for timeout errors, * or 0 for no errors. * Return controller status in du->dk_status and, if there was a controller * error, return the error code in du->dk_error. */ #ifdef WD_COUNT_RETRIES static int min_retries[NWDC]; #endif static int wdwait(struct disk *du, u_char bits_wanted, int timeout) { int wdc; u_char status; #define POLLING 1000 wdc = du->dk_port; timeout += POLLING; /* * This delay is really too long, but does not impact the performance * as much when using the multi-sector option. Shorter delays have * caused I/O errors on some drives and system configs. This should * probably be fixed if we develop a better short term delay mechanism. */ DELAY(1); do { #ifdef WD_COUNT_RETRIES if (min_retries[du->dk_ctrlr] > timeout || min_retries[du->dk_ctrlr] == 0) min_retries[du->dk_ctrlr] = timeout; #endif du->dk_status = status = inb(wdc + wd_status); /* * Atapi drives have a very interesting feature, when attached * as a slave on the IDE bus, and there is no master. * They release the bus after getting the command. * We should reselect the drive here to get the status. */ if (status == 0xff) { outb(wdc + wd_sdh, WDSD_IBM | du->dk_unit << 4); du->dk_status = status = inb(wdc + wd_status); } if (!(status & WDCS_BUSY)) { if (status & WDCS_ERR) { du->dk_error = inb(wdc + wd_error); /* * We once returned here. This is wrong * because the error bit is apparently only * valid after the controller has interrupted * (e.g., the error bit is stale when we wait * for DRQ for writes). So we can't depend * on the error bit at all when polling for * command completion. */ } if ((status & bits_wanted) == bits_wanted) { return (status & WDCS_ERR); } } if (timeout < TIMEOUT) /* * Switch to a polling rate of about 1 KHz so that * the timeout is almost machine-independent. The * controller is taking a long time to respond, so * an extra msec won't matter. */ DELAY(1000); else DELAY(1); } while (--timeout != 0); return (-1); } static wd_devsw_installed = 0; static void wd_drvinit(void *unused) { if( ! wd_devsw_installed ) { if (wd_cdevsw.d_maxio == 0) wd_cdevsw.d_maxio = 248 * 512; cdevsw_add_generic(BDEV_MAJOR,CDEV_MAJOR, &wd_cdevsw); wd_devsw_installed = 1; } } SYSINIT(wddev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,wd_drvinit,NULL) #endif /* NWDC > 0 */ Index: head/sys/isa/atkbd_isa.c =================================================================== --- head/sys/isa/atkbd_isa.c (revision 45719) +++ head/sys/isa/atkbd_isa.c (revision 45720) @@ -1,130 +1,131 @@ /*- * Copyright (c) 1999 Kazutaka YOKOTA * 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 as * the first lines of this file unmodified. * 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 AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS BE LIABLE 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. * - * $Id: atkbd_isa.c,v 1.1 1999/01/23 16:53:27 dfr Exp $ + * $Id: atkbd_isa.c,v 1.2 1999/03/10 10:36:49 yokota Exp $ */ #include "atkbd.h" #include "opt_kbd.h" #if NATKBD > 0 #include #include #include #include #include #include +#include #include #include #include #include #include #include #include devclass_t atkbd_devclass; static int atkbdprobe(device_t dev); static int atkbdattach(device_t dev); static void atkbd_isa_intr(void *arg); static device_method_t atkbd_methods[] = { DEVMETHOD(device_probe, atkbdprobe), DEVMETHOD(device_attach, atkbdattach), { 0, 0 } }; static driver_t atkbd_driver = { ATKBD_DRIVER_NAME, atkbd_methods, DRIVER_TYPE_TTY, sizeof(atkbd_softc_t), }; static int atkbdprobe(device_t dev) { - u_long port; - u_long irq; - u_long flags; + uintptr_t port; + uintptr_t irq; + uintptr_t flags; device_set_desc(dev, "AT Keyboard"); /* obtain parameters */ BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_PORT, &port); BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_IRQ, &irq); BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_FLAGS, &flags); /* probe the device */ return atkbd_probe_unit(device_get_unit(dev), port, irq, flags); } static int atkbdattach(device_t dev) { atkbd_softc_t *sc; - u_long port; - u_long irq; - u_long flags; + uintptr_t port; + uintptr_t irq; + uintptr_t flags; struct resource *res; void *ih; int zero = 0; int error; sc = (atkbd_softc_t *)device_get_softc(dev); BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_PORT, &port); BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_IRQ, &irq); BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_FLAGS, &flags); error = atkbd_attach_unit(device_get_unit(dev), sc, port, irq, flags); if (error) return error; /* declare our interrupt handler */ res = bus_alloc_resource(dev, SYS_RES_IRQ, &zero, irq, irq, 1, RF_SHAREABLE | RF_ACTIVE); BUS_SETUP_INTR(device_get_parent(dev), dev, res, atkbd_isa_intr, sc, &ih); return 0; } static void atkbd_isa_intr(void *arg) { atkbd_softc_t *sc; sc = (atkbd_softc_t *)arg; (*kbdsw[sc->kbd->kb_index]->intr)(sc->kbd, NULL); } DRIVER_MODULE(atkbd, atkbdc, atkbd_driver, atkbd_devclass, 0, 0); #endif /* NATKBD > 0 */ Index: head/sys/isa/fd.c =================================================================== --- head/sys/isa/fd.c (revision 45719) +++ head/sys/isa/fd.c (revision 45720) @@ -1,2297 +1,2394 @@ /* * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Don Ahn. * * Libretto PCMCIA floppy support by David Horwitt (dhorwitt@ucsd.edu) * aided by the Linux floppy driver modifications from David Bateman * (dbateman@eng.uts.edu.au). * * Copyright (c) 1993, 1994 by * jc@irbs.UUCP (John Capo) * vak@zebub.msk.su (Serge Vakulenko) * ache@astral.msk.su (Andrew A. Chernov) * * Copyright (c) 1993, 1994, 1995 by * joerg_wunsch@uriah.sax.de (Joerg Wunsch) * dufault@hda.com (Peter Dufault) * * 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: @(#)fd.c 7.4 (Berkeley) 5/25/91 - * $Id: fd.c,v 1.133 1999/02/10 00:03:32 ken Exp $ + * $Id: fd.c,v 1.134 1999/04/06 03:06:51 peter Exp $ * */ #include "fd.h" #include "opt_devfs.h" #include "opt_fdc.h" #if NFDC > 0 #include #include #include +#include +#include #include -#include -#include -#include #include -#include #include +#include #include +#include #include #include -#include -#include -#include -#include -#include + +#include +#include +#include + +#include +#include +#include #include + #ifdef DEVFS #include #endif /* DEVFS */ +#include +#include +#include +#include +#include +#include + /* misuse a flag to identify format operation */ #define B_FORMAT B_XXX /* configuration flags */ #define FDC_PRETEND_D0 (1 << 0) /* pretend drive 0 to be there */ #ifdef FDC_YE #define FDC_IS_PCMCIA (1 << 1) /* if successful probe, then it's a PCMCIA device */ #endif /* internally used only, not really from CMOS: */ #define RTCFDT_144M_PRETENDED 0x1000 /* * this biotab field doubles as a field for the physical unit number * on the controller */ #define id_physid id_scsiid /* error returns for fd_cmd() */ #define FD_FAILED -1 #define FD_NOT_VALID -2 #define FDC_ERRMAX 100 /* do not log more */ #define NUMTYPES 14 #define NUMDENS (NUMTYPES - 6) /* These defines (-1) must match index for fd_types */ #define F_TAPE_TYPE 0x020 /* bit for fd_types to indicate tape */ #define NO_TYPE 0 /* must match NO_TYPE in ft.c */ #define FD_1720 1 #define FD_1480 2 #define FD_1440 3 #define FD_1200 4 #define FD_820 5 #define FD_800 6 #define FD_720 7 #define FD_360 8 #define FD_1480in5_25 9 #define FD_1440in5_25 10 #define FD_820in5_25 11 #define FD_800in5_25 12 #define FD_720in5_25 13 #define FD_360in5_25 14 static struct fd_type fd_types[NUMTYPES] = { { 21,2,0xFF,0x04,82,3444,1,FDC_500KBPS,2,0x0C,2 }, /* 1.72M in HD 3.5in */ { 18,2,0xFF,0x1B,82,2952,1,FDC_500KBPS,2,0x6C,1 }, /* 1.48M in HD 3.5in */ { 18,2,0xFF,0x1B,80,2880,1,FDC_500KBPS,2,0x6C,1 }, /* 1.44M in HD 3.5in */ { 15,2,0xFF,0x1B,80,2400,1,FDC_500KBPS,2,0x54,1 }, /* 1.2M in HD 5.25/3.5 */ { 10,2,0xFF,0x10,82,1640,1,FDC_250KBPS,2,0x2E,1 }, /* 820K in HD 3.5in */ { 10,2,0xFF,0x10,80,1600,1,FDC_250KBPS,2,0x2E,1 }, /* 800K in HD 3.5in */ { 9,2,0xFF,0x20,80,1440,1,FDC_250KBPS,2,0x50,1 }, /* 720K in HD 3.5in */ { 9,2,0xFF,0x2A,40, 720,1,FDC_250KBPS,2,0x50,1 }, /* 360K in DD 5.25in */ { 18,2,0xFF,0x02,82,2952,1,FDC_500KBPS,2,0x02,2 }, /* 1.48M in HD 5.25in */ { 18,2,0xFF,0x02,80,2880,1,FDC_500KBPS,2,0x02,2 }, /* 1.44M in HD 5.25in */ { 10,2,0xFF,0x10,82,1640,1,FDC_300KBPS,2,0x2E,1 }, /* 820K in HD 5.25in */ { 10,2,0xFF,0x10,80,1600,1,FDC_300KBPS,2,0x2E,1 }, /* 800K in HD 5.25in */ { 9,2,0xFF,0x20,80,1440,1,FDC_300KBPS,2,0x50,1 }, /* 720K in HD 5.25in */ { 9,2,0xFF,0x23,40, 720,2,FDC_300KBPS,2,0x50,1 }, /* 360K in HD 5.25in */ }; #define DRVS_PER_CTLR 2 /* 2 floppies */ /***********************************************************************\ * Per controller structure. * \***********************************************************************/ -struct fdc_data fdc_data[NFDC]; +static devclass_t fdc_devclass; /***********************************************************************\ * Per drive structure. * * N per controller (DRVS_PER_CTLR) * \***********************************************************************/ -static struct fd_data { +struct fd_data { struct fdc_data *fdc; /* pointer to controller structure */ int fdsu; /* this units number on this controller */ int type; /* Drive type (FD_1440...) */ struct fd_type *ft; /* pointer to the type descriptor */ int flags; #define FD_OPEN 0x01 /* it's open */ #define FD_ACTIVE 0x02 /* it's active */ #define FD_MOTOR 0x04 /* motor should be on */ #define FD_MOTOR_WAIT 0x08 /* motor coming up */ int skip; int hddrv; #define FD_NO_TRACK -2 int track; /* where we think the head is */ int options; /* user configurable options, see ioctl_fd.h */ struct callout_handle toffhandle; struct callout_handle tohandle; struct devstat device_stats; #ifdef DEVFS void *bdevs[1 + NUMDENS + MAXPARTITIONS]; void *cdevs[1 + NUMDENS + MAXPARTITIONS]; #endif -} fd_data[NFD]; + device_t dev; + fdu_t fdu; +}; +static devclass_t fd_devclass; /***********************************************************************\ * Throughout this file the following conventions will be used: * * fd is a pointer to the fd_data struct for the drive in question * * fdc is a pointer to the fdc_data struct for the controller * * fdu is the floppy drive unit number * * fdcu is the floppy controller unit number * * fdsu is the floppy drive unit number on that controller. (sub-unit) * \***********************************************************************/ #ifdef FDC_YE #include "card.h" static int yeattach(struct isa_device *); #endif -/* autoconfig functions */ -static int fdprobe(struct isa_device *); -static int fdattach(struct isa_device *); - /* needed for ft driver, thus exported */ -int in_fdc(fdcu_t); -int out_fdc(fdcu_t, int); +int in_fdc(struct fdc_data *); +int out_fdc(struct fdc_data *, int); /* internal functions */ -static void set_motor(fdcu_t, int, int); +static void fdc_add_device(device_t, const char *, int); +static void fdc_intr(void *); +static void set_motor(struct fdc_data *, int, int); # define TURNON 1 # define TURNOFF 0 static timeout_t fd_turnoff; static timeout_t fd_motor_on; -static void fd_turnon(fdu_t); +static void fd_turnon(struct fd_data *); static void fdc_reset(fdc_p); -static int fd_in(fdcu_t, int *); -static void fdstart(fdcu_t); +static int fd_in(struct fdc_data *, int *); +static void fdstart(struct fdc_data *); static timeout_t fd_iotimeout; static timeout_t fd_pseudointr; -static ointhand2_t fdintr; -static int fdstate(fdcu_t, fdc_p); -static int retrier(fdcu_t); +static int fdstate(struct fdc_data *); +static int retrier(struct fdc_data *); static int fdformat(dev_t, struct fd_formb *, struct proc *); static int enable_fifo(fdc_p fdc); static int fifo_threshold = 8; /* XXX: should be accessible via sysctl */ #define DEVIDLE 0 #define FINDWORK 1 #define DOSEEK 2 #define SEEKCOMPLETE 3 #define IOCOMPLETE 4 #define RECALCOMPLETE 5 #define STARTRECAL 6 #define RESETCTLR 7 #define SEEKWAIT 8 #define RECALWAIT 9 #define MOTORWAIT 10 #define IOTIMEDOUT 11 #define RESETCOMPLETE 12 #ifdef FDC_YE #define PIOREAD 13 #endif #ifdef FDC_DEBUG static char const * const fdstates[] = { "DEVIDLE", "FINDWORK", "DOSEEK", "SEEKCOMPLETE", "IOCOMPLETE", "RECALCOMPLETE", "STARTRECAL", "RESETCTLR", "SEEKWAIT", "RECALWAIT", "MOTORWAIT", "IOTIMEDOUT", "RESETCOMPLETE", #ifdef FDC_YE "PIOREAD", #endif }; /* CAUTION: fd_debug causes huge amounts of logging output */ static int volatile fd_debug = 0; #define TRACE0(arg) if(fd_debug) printf(arg) #define TRACE1(arg1, arg2) if(fd_debug) printf(arg1, arg2) #else /* FDC_DEBUG */ #define TRACE0(arg) #define TRACE1(arg1, arg2) #endif /* FDC_DEBUG */ #ifdef FDC_YE #if NCARD > 0 #include #include #include #include #include /* * PC-Card (PCMCIA) specific code. */ static int yeinit(struct pccard_devinfo *); /* init device */ static void yeunload(struct pccard_devinfo *); /* Disable driver */ static int yeintr(struct pccard_devinfo *); /* Interrupt handler */ PCCARD_MODULE(fdc, yeinit, yeunload, yeintr, 0, bio_imask); /* * this is the secret PIO data port (offset from base) */ #define FDC_YE_DATAPORT 6 /* * Initialize the device - called from Slot manager. */ static int yeinit(struct pccard_devinfo *devi) { fdc_p fdc = &fdc_data[devi->isahd.id_unit]; /* validate unit number. */ if (devi->isahd.id_unit >= NFDC) return(ENODEV); fdc->baseport = devi->isahd.id_iobase; /* * reset controller */ outb(fdc->baseport+FDOUT, 0); DELAY(100); outb(fdc->baseport+FDOUT, FDO_FRST); /* * wire into system */ if (yeattach(&devi->isahd) == 0) return(ENXIO); return(0); } /* * yeunload - unload the driver and clear the table. * XXX TODO: * This is usually called when the card is ejected, but * can be caused by a modunload of a controller driver. * The idea is to reset the driver's view of the device * and ensure that any driver entry points such as * read and write do not hang. */ static void yeunload(struct pccard_devinfo *devi) { if (fd_data[devi->isahd.id_unit].type == NO_TYPE) return; /* * this prevents Fdopen() and fdstrategy() from attempting * to access unloaded controller */ fd_data[devi->isahd.id_unit].type = NO_TYPE; printf("fdc%d: unload\n", devi->isahd.id_unit); } /* * yeintr - Shared interrupt called from * front end of PC-Card handler. */ static int yeintr(struct pccard_devinfo *devi) { fdintr((fdcu_t)devi->isahd.id_unit); return(1); } #endif /* NCARD > 0 */ #endif /* FDC_YE */ - -/* autoconfig structure */ - -struct isa_driver fdcdriver = { - fdprobe, fdattach, "fdc", -}; - static d_open_t Fdopen; /* NOTE, not fdopen */ static d_read_t fdread; static d_write_t fdwrite; static d_close_t fdclose; static d_ioctl_t fdioctl; static d_strategy_t fdstrategy; /* even if SLICE defined, these are needed for the ft support. */ #define CDEV_MAJOR 9 #define BDEV_MAJOR 2 - -static struct cdevsw fd_cdevsw = { - Fdopen, fdclose, fdread, fdwrite, - fdioctl, nostop, nullreset, nodevtotty, - seltrue, nommap, fdstrategy, "fd", - NULL, -1, nodump, nopsize, - D_DISK, 0, -1 }; - - -static struct isa_device *fdcdevs[NFDC]; - - static int -fdc_err(fdcu_t fdcu, const char *s) +fdc_err(struct fdc_data *fdc, const char *s) { - fdc_data[fdcu].fdc_errs++; - if(s) { - if(fdc_data[fdcu].fdc_errs < FDC_ERRMAX) - printf("fdc%d: %s", fdcu, s); - else if(fdc_data[fdcu].fdc_errs == FDC_ERRMAX) - printf("fdc%d: too many errors, not logging any more\n", - fdcu); + fdc->fdc_errs++; + if (s) { + if (fdc->fdc_errs < FDC_ERRMAX) { + device_print_prettyname(fdc->fdc_dev); + printf("%s", s); + } else if (fdc->fdc_errs == FDC_ERRMAX) { + device_print_prettyname(fdc->fdc_dev); + printf("too many errors, not logging any more\n"); + } } return FD_FAILED; } /* * fd_cmd: Send a command to the chip. Takes a varargs with this structure: * Unit number, * # of output bytes, output bytes as ints ..., * # of input bytes, input bytes as ints ... */ - static int -fd_cmd(fdcu_t fdcu, int n_out, ...) +fd_cmd(struct fdc_data *fdc, int n_out, ...) { u_char cmd; int n_in; int n; va_list ap; va_start(ap, n_out); cmd = (u_char)(va_arg(ap, int)); va_end(ap); va_start(ap, n_out); for (n = 0; n < n_out; n++) { - if (out_fdc(fdcu, va_arg(ap, int)) < 0) + if (out_fdc(fdc, va_arg(ap, int)) < 0) { char msg[50]; snprintf(msg, sizeof(msg), "cmd %x failed at out byte %d of %d\n", cmd, n + 1, n_out); - return fdc_err(fdcu, msg); + return fdc_err(fdc, msg); } } n_in = va_arg(ap, int); for (n = 0; n < n_in; n++) { int *ptr = va_arg(ap, int *); - if (fd_in(fdcu, ptr) < 0) + if (fd_in(fdc, ptr) < 0) { char msg[50]; snprintf(msg, sizeof(msg), "cmd %02x failed at in byte %d of %d\n", cmd, n + 1, n_in); - return fdc_err(fdcu, msg); + return fdc_err(fdc, msg); } } return 0; } static int enable_fifo(fdc_p fdc) { int i, j; if ((fdc->flags & FDC_HAS_FIFO) == 0) { /* * XXX: * Cannot use fd_cmd the normal way here, since * this might be an invalid command. Thus we send the * first byte, and check for an early turn of data directon. */ - if (out_fdc(fdc->fdcu, I8207X_CONFIGURE) < 0) - return fdc_err(fdc->fdcu, "Enable FIFO failed\n"); + if (out_fdc(fdc, I8207X_CONFIGURE) < 0) + return fdc_err(fdc, "Enable FIFO failed\n"); /* If command is invalid, return */ j = 100000; while ((i = inb(fdc->baseport + FDSTS) & (NE7_DIO | NE7_RQM)) != NE7_RQM && j-- > 0) if (i == (NE7_DIO | NE7_RQM)) { fdc_reset(fdc); return FD_FAILED; } if (j<0 || - fd_cmd(fdc->fdcu, 3, + fd_cmd(fdc, 3, 0, (fifo_threshold - 1) & 0xf, 0, 0) < 0) { fdc_reset(fdc); - return fdc_err(fdc->fdcu, "Enable FIFO failed\n"); + return fdc_err(fdc, "Enable FIFO failed\n"); } fdc->flags |= FDC_HAS_FIFO; return 0; } - if (fd_cmd(fdc->fdcu, 4, + if (fd_cmd(fdc, 4, I8207X_CONFIGURE, 0, (fifo_threshold - 1) & 0xf, 0, 0) < 0) - return fdc_err(fdc->fdcu, "Re-enable FIFO failed\n"); + return fdc_err(fdc, "Re-enable FIFO failed\n"); return 0; } static int fd_sense_drive_status(fdc_p fdc, int *st3p) { int st3; - if (fd_cmd(fdc->fdcu, 2, NE7CMD_SENSED, fdc->fdu, 1, &st3)) + if (fd_cmd(fdc, 2, NE7CMD_SENSED, fdc->fdu, 1, &st3)) { - return fdc_err(fdc->fdcu, "Sense Drive Status failed\n"); + return fdc_err(fdc, "Sense Drive Status failed\n"); } if (st3p) *st3p = st3; return 0; } static int fd_sense_int(fdc_p fdc, int *st0p, int *cylp) { - int st0, cyl; + int cyl, st0, ret; - int ret = fd_cmd(fdc->fdcu, 1, NE7CMD_SENSEI, 1, &st0); - - if (ret) - { - (void)fdc_err(fdc->fdcu, + ret = fd_cmd(fdc, 1, NE7CMD_SENSEI, 1, &st0); + if (ret) { + (void)fdc_err(fdc, "sense intr err reading stat reg 0\n"); return ret; } if (st0p) *st0p = st0; - if ((st0 & NE7_ST0_IC) == NE7_ST0_IC_IV) - { + if ((st0 & NE7_ST0_IC) == NE7_ST0_IC_IV) { /* * There doesn't seem to have been an interrupt. */ return FD_NOT_VALID; } - if (fd_in(fdc->fdcu, &cyl) < 0) - { - return fdc_err(fdc->fdcu, "can't get cyl num\n"); + if (fd_in(fdc, &cyl) < 0) { + return fdc_err(fdc, "can't get cyl num\n"); } if (cylp) *cylp = cyl; return 0; } static int fd_read_status(fdc_p fdc, int fdsu) { int i, ret; - for (i = 0; i < 7; i++) - { + for (i = 0; i < 7; i++) { /* * XXX types are poorly chosen. Only bytes can by read * from the hardware, but fdc->status[] wants u_ints and * fd_in() gives ints. */ int status; - ret = fd_in(fdc->fdcu, &status); + ret = fd_in(fdc, &status); fdc->status[i] = status; if (ret != 0) break; } if (ret == 0) fdc->flags |= FDC_STAT_VALID; else fdc->flags &= ~FDC_STAT_VALID; return ret; } /****************************************************************************/ /* autoconfiguration stuff */ /****************************************************************************/ -/* - * probe for existance of controller - */ static int -fdprobe(struct isa_device *dev) +fdc_probe(device_t dev) { - fdcu_t fdcu = dev->id_unit; - if(fdc_data[fdcu].flags & FDC_ATTACHED) - { - printf("fdc%d: unit used multiple times\n", fdcu); - return 0; + int error, i, ic_type; + struct fdc_data *fdc; + char myname[8]; /* better be long enough */ + + fdc = device_get_softc(dev); + bzero(fdc, sizeof *fdc); + fdc->fdc_dev = dev; + fdc->rid_ioport = fdc->rid_irq = fdc->rid_drq = 0; + fdc->res_ioport = fdc->res_irq = fdc->res_drq = 0; + + fdc->res_ioport = bus_alloc_resource(dev, SYS_RES_IOPORT, + &fdc->rid_ioport, 0ul, ~0ul, + IO_FDCSIZE, RF_ACTIVE); + if (fdc->res_ioport == 0) { + device_print_prettyname(dev); + printf("cannot reserve I/O port range\n"); + error = ENXIO; + goto out; } + fdc->baseport = fdc->res_ioport->r_start; - fdcdevs[fdcu] = dev; - fdc_data[fdcu].baseport = dev->id_iobase; + fdc->res_irq = bus_alloc_resource(dev, SYS_RES_IRQ, + &fdc->rid_irq, 0ul, ~0ul, 1, + RF_ACTIVE); + if (fdc->res_irq == 0) { + device_print_prettyname(dev); + printf("cannot reserve interrupt line\n"); + error = ENXIO; + goto out; + } + fdc->res_drq = bus_alloc_resource(dev, SYS_RES_DRQ, + &fdc->rid_drq, 0ul, ~0ul, 1, + RF_ACTIVE); + if (fdc->res_drq == 0) { + device_print_prettyname(dev); + printf("cannot reserve DMA request line\n"); + error = ENXIO; + goto out; + } + fdc->dmachan = fdc->res_drq->r_start; + error = BUS_SETUP_INTR(device_get_parent(dev), dev, + fdc->res_irq, fdc_intr, fdc, &fdc->fdc_intr); /* First - lets reset the floppy controller */ - outb(dev->id_iobase+FDOUT, 0); + outb(fdc->baseport + FDOUT, 0); DELAY(100); - outb(dev->id_iobase+FDOUT, FDO_FRST); + outb(fdc->baseport + FDOUT, FDO_FRST); /* see if it can handle a command */ - if (fd_cmd(fdcu, - 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), - 0)) - { - return(0); + if (fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), + NE7_SPEC_2(2, 0), 0)) { + error = ENXIO; + goto out; } + + if (fd_cmd(fdc, 1, NE7CMD_VERSION, 1, &ic_type) == 0) { + ic_type = (u_char)ic_type; + switch (ic_type) { + case 0x80: + device_set_desc(dev, "NEC 765 or clone"); + fdc->fdct = FDC_NE765; + break; + case 0x81: + device_set_desc(dev, "Intel 82077 or clone"); + fdc->fdct = FDC_I82077; + break; + case 0x90: + device_set_desc(dev, "NEC 72065B or clone"); + fdc->fdct = FDC_NE72065; + break; + default: + device_set_desc(dev, "generic floppy controller"); + fdc->fdct = FDC_UNKNOWN; + break; + } + } + + snprintf(myname, sizeof(myname), "%s%d", device_get_name(dev), + device_get_unit(dev)); + for (i = resource_query_string(-1, "at", myname); i != -1; + i = resource_query_string(i, "at", myname)) + fdc_add_device(dev, resource_query_name(i), + resource_query_unit(i)); #ifdef FDC_YE /* * don't succeed on probe; wait * for PCCARD subsystem to do it */ if (dev->id_flags & FDC_IS_PCMCIA) return(0); #endif - return (IO_FDCSIZE); + return (0); + +out: + if (fdc->fdc_intr) + BUS_TEARDOWN_INTR(device_get_parent(dev), dev, fdc->res_irq, + fdc->fdc_intr); + if (fdc->res_irq != 0) { + bus_deactivate_resource(dev, SYS_RES_IRQ, fdc->rid_irq, + fdc->res_irq); + bus_release_resource(dev, SYS_RES_IRQ, fdc->rid_irq, + fdc->res_irq); + } + if (fdc->res_ioport != 0) { + bus_deactivate_resource(dev, SYS_RES_IOPORT, fdc->rid_ioport, + fdc->res_ioport); + bus_release_resource(dev, SYS_RES_IOPORT, fdc->rid_ioport, + fdc->res_ioport); + } + if (fdc->res_drq != 0) { + bus_deactivate_resource(dev, SYS_RES_DRQ, fdc->rid_drq, + fdc->res_drq); + bus_release_resource(dev, SYS_RES_DRQ, fdc->rid_drq, + fdc->res_drq); + } + return (error); } /* - * wire controller into system, look for floppy units + * Aped dfr@freebsd.org's isa_add_device(). */ +static void +fdc_add_device(device_t dev, const char *name, int unit) +{ + int disabled, *ivar; + device_t child; + + ivar = malloc(sizeof *ivar, M_DEVBUF /* XXX */, M_NOWAIT); + if (ivar == 0) + return; + if (resource_int_value(name, unit, "drive", ivar) == 0) + *ivar = 0; + child = device_add_child(dev, name, unit, ivar); + if (child == 0) + return; + if (resource_int_value(name, unit, "disabled", &disabled) == 0) + device_disable(child); +} + static int -fdattach(struct isa_device *dev) +fdc_attach(device_t dev) { - unsigned fdt; - fdu_t fdu; - fdcu_t fdcu = dev->id_unit; - fdc_p fdc = fdc_data + fdcu; - fd_p fd; - int fdsu, st0, st3, i; - struct isa_device *fdup; - int ic_type = 0; -#ifdef DEVFS - int mynor; - int typemynor; - int typesize; -#endif + struct fdc_data *fdc = device_get_softc(dev); + fdcu_t fdcu = device_get_unit(dev); - dev->id_ointr = fdintr; fdc->fdcu = fdcu; fdc->flags |= FDC_ATTACHED; - fdc->dmachan = dev->id_drq; + /* Acquire the DMA channel forever, The driver will do the rest */ + /* XXX should integrate with rman */ isa_dma_acquire(fdc->dmachan); isa_dmainit(fdc->dmachan, 128 << 3 /* XXX max secsize */); fdc->state = DEVIDLE; + /* reset controller, turn motor off, clear fdout mirror reg */ outb(fdc->baseport + FDOUT, ((fdc->fdout = 0))); bufq_init(&fdc->head); - /* check for each floppy drive */ - for (fdup = isa_biotab_fdc; fdup->id_driver != 0; fdup++) { - if (fdup->id_iobase != dev->id_iobase) - continue; - fdu = fdup->id_unit; - fd = &fd_data[fdu]; - if (fdu >= (NFD)) - continue; - fdsu = fdup->id_physid; - /* look up what bios thinks we have */ - switch (fdu) { - case 0: if (dev->id_flags & FDC_PRETEND_D0) - fdt = RTCFDT_144M | RTCFDT_144M_PRETENDED; - else - fdt = (rtcin(RTC_FDISKETTE) & 0xf0); - break; - case 1: fdt = ((rtcin(RTC_FDISKETTE) << 4) & 0xf0); - break; - default: fdt = RTCFDT_NONE; - break; - } - /* is there a unit? */ - if ((fdt == RTCFDT_NONE) - ) { - fd->type = NO_TYPE; - continue; - } +#ifdef FIFO_BEFORE_MOTORON + /* Hmm, this doesn't work here - is set_motor() magic? -Peter */ + if (fdc->fdct != FDC_NE765 && fdc->fdct != FDC_UNKNOWN + && enable_fifo(fdc) == 0) { + device_print_prettyname(dev); + printf("FIFO enabled, %d bytes threshold\n", fifo_threshold); + } +#endif + /* + * Probe and attach any children as were configured above. + */ + return (bus_generic_attach(dev)); +} - /* select it */ - set_motor(fdcu, fdsu, TURNON); - DELAY(1000000); /* 1 sec */ +static void +fdc_print_child(device_t me, device_t child) +{ + printf(" at %s%d drive %d", device_get_name(me), device_get_unit(me), + *(int *)device_get_ivars(child)); +} - if (ic_type == 0 && - fd_cmd(fdcu, 1, NE7CMD_VERSION, 1, &ic_type) == 0) - { -#ifdef FDC_PRINT_BOGUS_CHIPTYPE - printf("fdc%d: ", fdcu); +static int +fd_probe(device_t dev) +{ + int i; + u_int fdt, st0, st3; + struct fd_data *fd; + struct fdc_data *fdc; + fdsu_t fdsu; +#ifndef FIFO_BEFORE_MOTORON + static int fd_fifo = 0; #endif - ic_type = (u_char)ic_type; - switch( ic_type ) { - case 0x80: -#ifdef FDC_PRINT_BOGUS_CHIPTYPE - printf("NEC 765\n"); + + fdsu = *(int *)device_get_ivars(dev); /* xxx cheat a bit... */ + fd = device_get_softc(dev); + fdc = device_get_softc(device_get_parent(dev)); + + bzero(fd, sizeof *fd); + fd->dev = dev; + fd->fdc = fdc; + fd->fdsu = fdsu; + fd->fdu = device_get_unit(dev); + + /* look up what bios thinks we have */ + switch (fd->fdu) { + case 0: + if (isa_get_flags(fdc->fdc_dev) & FDC_PRETEND_D0) + fdt = RTCFDT_144M | RTCFDT_144M_PRETENDED; + else + fdt = (rtcin(RTC_FDISKETTE) & 0xf0); + break; + case 1: + fdt = ((rtcin(RTC_FDISKETTE) << 4) & 0xf0); + break; + default: + fdt = RTCFDT_NONE; + break; + } + + /* is there a unit? */ + if (fdt == RTCFDT_NONE) + return (ENXIO); + + /* select it */ + set_motor(fdc, fdsu, TURNON); + DELAY(1000000); /* 1 sec */ + +#ifndef FIFO_BEFORE_MOTORON + if (fd_fifo == 0 && fdc->fdct != FDC_NE765 && fdc->fdct != FDC_UNKNOWN + && enable_fifo(fdc) == 0) { + device_print_prettyname(device_get_parent(dev)); + printf("FIFO enabled, %d bytes threshold\n", fifo_threshold); + } + fd_fifo = 1; #endif - fdc->fdct = FDC_NE765; - break; - case 0x81: -#ifdef FDC_PRINT_BOGUS_CHIPTYPE - printf("Intel 82077\n"); -#endif - fdc->fdct = FDC_I82077; - break; - case 0x90: -#ifdef FDC_PRINT_BOGUS_CHIPTYPE - printf("NEC 72065B\n"); -#endif - fdc->fdct = FDC_NE72065; - break; - default: -#ifdef FDC_PRINT_BOGUS_CHIPTYPE - printf("unknown IC type %02x\n", ic_type); -#endif - fdc->fdct = FDC_UNKNOWN; - break; - } - if (fdc->fdct != FDC_NE765 && - fdc->fdct != FDC_UNKNOWN && - enable_fifo(fdc) == 0) { - printf("fdc%d: FIFO enabled", fdcu); - printf(", %d bytes threshold\n", - fifo_threshold); - } - } - if ((fd_cmd(fdcu, 2, NE7CMD_SENSED, fdsu, 1, &st3) == 0) && - (st3 & NE7_ST3_T0)) { - /* if at track 0, first seek inwards */ - /* seek some steps: */ - (void)fd_cmd(fdcu, 3, NE7CMD_SEEK, fdsu, 10, 0); - DELAY(300000); /* ...wait a moment... */ - (void)fd_sense_int(fdc, 0, 0); /* make ctrlr happy */ - } - /* If we're at track 0 first seek inwards. */ - if ((fd_sense_drive_status(fdc, &st3) == 0) && - (st3 & NE7_ST3_T0)) { - /* Seek some steps... */ - if (fd_cmd(fdcu, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) { - /* ...wait a moment... */ - DELAY(300000); - /* make ctrlr happy: */ - (void)fd_sense_int(fdc, 0, 0); - } + if ((fd_cmd(fdc, 2, NE7CMD_SENSED, fdsu, 1, &st3) == 0) + && (st3 & NE7_ST3_T0)) { + /* if at track 0, first seek inwards */ + /* seek some steps: */ + fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0); + DELAY(300000); /* ...wait a moment... */ + fd_sense_int(fdc, 0, 0); /* make ctrlr happy */ + } + + /* If we're at track 0 first seek inwards. */ + if ((fd_sense_drive_status(fdc, &st3) == 0) && (st3 & NE7_ST3_T0)) { + /* Seek some steps... */ + if (fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) { + /* ...wait a moment... */ + DELAY(300000); + /* make ctrlr happy: */ + fd_sense_int(fdc, 0, 0); } + } - for(i = 0; i < 2; i++) { - /* - * we must recalibrate twice, just in case the - * heads have been beyond cylinder 76, since most - * FDCs still barf when attempting to recalibrate - * more than 77 steps - */ - /* go back to 0: */ - if (fd_cmd(fdcu, 2, NE7CMD_RECAL, fdsu, 0) == 0) { - /* a second being enough for full stroke seek*/ - DELAY(i == 0? 1000000: 300000); + for (i = 0; i < 2; i++) { + /* + * we must recalibrate twice, just in case the + * heads have been beyond cylinder 76, since most + * FDCs still barf when attempting to recalibrate + * more than 77 steps + */ + /* go back to 0: */ + if (fd_cmd(fdc, 2, NE7CMD_RECAL, fdsu, 0) == 0) { + /* a second being enough for full stroke seek*/ + DELAY(i == 0 ? 1000000 : 300000); - /* anything responding? */ - if (fd_sense_int(fdc, &st0, 0) == 0 && - (st0 & NE7_ST0_EC) == 0) - break; /* already probed succesfully */ - } + /* anything responding? */ + if (fd_sense_int(fdc, &st0, 0) == 0 && + (st0 & NE7_ST0_EC) == 0) + break; /* already probed succesfully */ } + } - set_motor(fdcu, fdsu, TURNOFF); + set_motor(fdc, fdsu, TURNOFF); - if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */ - continue; + if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */ + return (ENXIO); - fd->track = FD_NO_TRACK; - fd->fdc = fdc; - fd->fdsu = fdsu; - fd->options = 0; - callout_handle_init(&fd->toffhandle); - callout_handle_init(&fd->tohandle); - printf("fd%d: ", fdu); + fd->track = FD_NO_TRACK; + fd->fdc = fdc; + fd->fdsu = fdsu; + fd->options = 0; + callout_handle_init(&fd->toffhandle); + callout_handle_init(&fd->tohandle); - switch (fdt) { - case RTCFDT_12M: - printf("1.2MB 5.25in\n"); - fd->type = FD_1200; + switch (fdt) { + case RTCFDT_12M: + device_set_desc(dev, "1200-KB 5.25\" drive"); + fd->type = FD_1200; + break; + case RTCFDT_144M | RTCFDT_144M_PRETENDED: + device_set_desc(dev, "config-pretended 1440-MB 3.5\" drive"); + fdt = RTCFDT_144M; + fd->type = FD_1440; + case RTCFDT_144M: + device_set_desc(dev, "1440-KB 3.5\" drive"); + fd->type = FD_1440; + break; + case RTCFDT_288M: + case RTCFDT_288M_1: + device_set_desc(dev, "2880-KB 3.5\" drive (in 1440-KB mode)"); + fd->type = FD_1440; + break; + case RTCFDT_360K: + device_set_desc(dev, "360-KB 5.25\" drive"); + fd->type = FD_360; + break; + case RTCFDT_720K: + printf("720-KB 3.5\" drive"); + fd->type = FD_720; + break; + default: + return (ENXIO); + } + return (0); +} + +static int +fd_attach(device_t dev) +{ + struct fd_data *fd; + + fd = device_get_softc(dev); + +#ifdef DEVFS /* XXX bitrot */ + mynor = fdu << 6; + fd->bdevs[0] = devfs_add_devswf(&fd_cdevsw, mynor, DV_BLK, + UID_ROOT, GID_OPERATOR, 0640, + "fd%d", fdu); + fd->cdevs[0] = devfs_add_devswf(&fd_cdevsw, mynor, DV_CHR, + UID_ROOT, GID_OPERATOR, 0640, + "rfd%d", fdu); + for (i = 1; i < 1 + NUMDENS; i++) { + /* + * XXX this and the lookup in Fdopen() should be + * data driven. + */ + switch (fd->type) { + case FD_360: + if (i != FD_360) + continue; break; - case RTCFDT_144M | RTCFDT_144M_PRETENDED: - printf("config-pretended "); - fdt = RTCFDT_144M; - /* fallthrough */ - case RTCFDT_144M: - printf("1.44MB 3.5in\n"); - fd->type = FD_1440; + case FD_720: + if (i != FD_720 && i != FD_800 && i != FD_820) + continue; break; - case RTCFDT_288M: - case RTCFDT_288M_1: - printf("2.88MB 3.5in - 1.44MB mode\n"); - fd->type = FD_1440; + case FD_1200: + if (i != FD_360 && i != FD_720 && i != FD_800 + && i != FD_820 && i != FD_1200 + && i != FD_1440 && i != FD_1480) + continue; break; - case RTCFDT_360K: - printf("360KB 5.25in\n"); - fd->type = FD_360; + case FD_1440: + if (i != FD_720 && i != FD_800 && i != FD_820 + && i != FD_1200 && i != FD_1440 + && i != FD_1480 && i != FD_1720) + continue; break; - case RTCFDT_720K: - printf("720KB 3.5in\n"); - fd->type = FD_720; - break; - default: - printf("unknown\n"); - fd->type = NO_TYPE; - continue; } -#ifdef DEVFS - mynor = fdu << 6; - fd->bdevs[0] = devfs_add_devswf(&fd_cdevsw, mynor, DV_BLK, - UID_ROOT, GID_OPERATOR, 0640, - "fd%d", fdu); - fd->cdevs[0] = devfs_add_devswf(&fd_cdevsw, mynor, DV_CHR, - UID_ROOT, GID_OPERATOR, 0640, - "rfd%d", fdu); - for (i = 1; i < 1 + NUMDENS; i++) { - /* - * XXX this and the lookup in Fdopen() should be - * data driven. - */ - switch (fd->type) { - case FD_360: - if (i != FD_360) - continue; - break; - case FD_720: - if (i != FD_720 && i != FD_800 && i != FD_820) - continue; - break; - case FD_1200: - if (i != FD_360 && i != FD_720 && i != FD_800 - && i != FD_820 && i != FD_1200 - && i != FD_1440 && i != FD_1480) - continue; - break; - case FD_1440: - if (i != FD_720 && i != FD_800 && i != FD_820 - && i != FD_1200 && i != FD_1440 - && i != FD_1480 && i != FD_1720) - continue; - break; - } - typesize = fd_types[i - 1].size / 2; - /* - * XXX all these conversions give bloated code and - * confusing names. - */ - if (typesize == 1476) - typesize = 1480; - if (typesize == 1722) - typesize = 1720; - typemynor = mynor | i; - fd->bdevs[i] = - devfs_add_devswf(&fd_cdevsw, typemynor, DV_BLK, - UID_ROOT, GID_OPERATOR, 0640, - "fd%d.%d", fdu, typesize); - fd->cdevs[i] = - devfs_add_devswf(&fd_cdevsw, typemynor, DV_CHR, - UID_ROOT, GID_OPERATOR, 0640, - "rfd%d.%d", fdu, typesize); - } - - for (i = 0; i < MAXPARTITIONS; i++) { - fd->bdevs[1 + NUMDENS + i] = devfs_makelink(fd->bdevs[0], - "fd%d%c", fdu, 'a' + i); - fd->cdevs[1 + NUMDENS + i] = - devfs_makelink(fd->cdevs[0], - "rfd%d%c", fdu, 'a' + i); - } -#endif /* DEVFS */ + typesize = fd_types[i - 1].size / 2; /* - * Export the drive to the devstat interface. + * XXX all these conversions give bloated code and + * confusing names. */ - devstat_add_entry(&fd->device_stats, "fd", - fdu, 512, - DEVSTAT_NO_ORDERED_TAGS, - DEVSTAT_TYPE_FLOPPY | DEVSTAT_TYPE_IF_OTHER, - DEVSTAT_PRIORITY_FD); - + if (typesize == 1476) + typesize = 1480; + if (typesize == 1722) + typesize = 1720; + typemynor = mynor | i; + fd->bdevs[i] = + devfs_add_devswf(&fd_cdevsw, typemynor, DV_BLK, + UID_ROOT, GID_OPERATOR, 0640, + "fd%d.%d", fdu, typesize); + fd->cdevs[i] = + devfs_add_devswf(&fd_cdevsw, typemynor, DV_CHR, + UID_ROOT, GID_OPERATOR, 0640, + "rfd%d.%d", fdu, typesize); } - return (1); + for (i = 0; i < MAXPARTITIONS; i++) { + fd->bdevs[1 + NUMDENS + i] = devfs_makelink(fd->bdevs[0], + "fd%d%c", fdu, 'a' + i); + fd->cdevs[1 + NUMDENS + i] = + devfs_makelink(fd->cdevs[0], + "rfd%d%c", fdu, 'a' + i); + } +#endif /* DEVFS */ + /* + * Export the drive to the devstat interface. + */ + devstat_add_entry(&fd->device_stats, device_get_name(dev), + device_get_unit(dev), 512, DEVSTAT_NO_ORDERED_TAGS, + DEVSTAT_TYPE_FLOPPY | DEVSTAT_TYPE_IF_OTHER, + DEVSTAT_PRIORITY_FD); + return (0); } - - #ifdef FDC_YE /* * this is a subset of fdattach() optimized for the Y-E Data * PCMCIA floppy drive. */ static int yeattach(struct isa_device *dev) { fdcu_t fdcu = dev->id_unit; fdc_p fdc = fdc_data + fdcu; fdsu_t fdsu = 0; /* assume 1 drive per YE controller */ fdu_t fdu; fd_p fd; int st0, st3, i; #ifdef DEVFS int mynor; int typemynor; int typesize; #endif fdc->fdcu = fdcu; /* * the FDC_PCMCIA flag is used to to indicate special PIO is used * instead of DMA */ fdc->flags = FDC_ATTACHED|FDC_PCMCIA; fdc->state = DEVIDLE; /* reset controller, turn motor off, clear fdout mirror reg */ outb(fdc->baseport + FDOUT, ((fdc->fdout = 0))); bufq_init(&fdc->head); /* * assume 2 drives/ "normal" controller */ fdu = fdcu * 2; if (fdu >= NFD) { printf("fdu %d >= NFD\n",fdu); return(0); }; fd = &fd_data[fdu]; set_motor(fdcu, fdsu, TURNON); DELAY(1000000); /* 1 sec */ fdc->fdct = FDC_NE765; if ((fd_cmd(fdcu, 2, NE7CMD_SENSED, fdsu, 1, &st3) == 0) && (st3 & NE7_ST3_T0)) { /* if at track 0, first seek inwards */ /* seek some steps: */ (void)fd_cmd(fdcu, 3, NE7CMD_SEEK, fdsu, 10, 0); DELAY(300000); /* ...wait a moment... */ (void)fd_sense_int(fdc, 0, 0); /* make ctrlr happy */ } /* If we're at track 0 first seek inwards. */ if ((fd_sense_drive_status(fdc, &st3) == 0) && (st3 & NE7_ST3_T0)) { /* Seek some steps... */ if (fd_cmd(fdcu, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) { /* ...wait a moment... */ DELAY(300000); /* make ctrlr happy: */ (void)fd_sense_int(fdc, 0, 0); } } for(i = 0; i < 2; i++) { /* * we must recalibrate twice, just in case the * heads have been beyond cylinder 76, since most * FDCs still barf when attempting to recalibrate * more than 77 steps */ /* go back to 0: */ if (fd_cmd(fdcu, 2, NE7CMD_RECAL, fdsu, 0) == 0) { /* a second being enough for full stroke seek*/ DELAY(i == 0? 1000000: 300000); /* anything responding? */ if (fd_sense_int(fdc, &st0, 0) == 0 && (st0 & NE7_ST0_EC) == 0) break; /* already probed succesfully */ } } set_motor(fdcu, fdsu, TURNOFF); if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */ return(0); fd->track = FD_NO_TRACK; fd->fdc = fdc; fd->fdsu = fdsu; fd->options = 0; printf("fdc%d: 1.44MB 3.5in PCMCIA\n", fdcu); fd->type = FD_1440; #ifdef DEVFS mynor = fdcu << 6; fd->bdevs[0] = devfs_add_devswf(&fd_cdevsw, mynor, DV_BLK, UID_ROOT, GID_OPERATOR, 0640, "fd%d", fdu); fd->cdevs[0] = devfs_add_devswf(&fd_cdevsw, mynor, DV_CHR, UID_ROOT, GID_OPERATOR, 0640, "rfd%d", fdu); /* * XXX this and the lookup in Fdopen() should be * data driven. */ typemynor = mynor | FD_1440; typesize = fd_types[FD_1440 - 1].size / 2; /* * XXX all these conversions give bloated code and * confusing names. */ if (typesize == 1476) typesize = 1480; if (typesize == 1722) typesize = 1720; fd->bdevs[FD_1440] = devfs_add_devswf(&fd_cdevsw, typemynor, DV_BLK, UID_ROOT, GID_OPERATOR, 0640, "fd%d.%d", fdu, typesize); fd->cdevs[FD_1440] = devfs_add_devswf(&fd_cdevsw, typemynor, DV_CHR, UID_ROOT, GID_OPERATOR, 0640,"rfd%d.%d", fdu, typesize); for (i = 0; i < MAXPARTITIONS; i++) { fd->bdevs[1 + NUMDENS + i] = devfs_makelink(fd->bdevs[0], "fd%d%c", fdu, 'a' + i); fd->cdevs[1 + NUMDENS + i] = devfs_makelink(fd->cdevs[0], "rfd%d%c", fdu, 'a' + i); } #endif /* DEVFS */ return (1); } #endif /****************************************************************************/ /* motor control stuff */ /* remember to not deselect the drive we're working on */ /****************************************************************************/ static void -set_motor(fdcu_t fdcu, int fdsu, int turnon) +set_motor(struct fdc_data *fdc, int fdsu, int turnon) { - int fdout = fdc_data[fdcu].fdout; + int fdout = fdc->fdout; int needspecify = 0; if(turnon) { fdout &= ~FDO_FDSEL; fdout |= (FDO_MOEN0 << fdsu) + fdsu; } else fdout &= ~(FDO_MOEN0 << fdsu); if(!turnon && (fdout & (FDO_MOEN0+FDO_MOEN1+FDO_MOEN2+FDO_MOEN3)) == 0) /* gonna turn off the last drive, put FDC to bed */ fdout &= ~ (FDO_FRST|FDO_FDMAEN); else { /* make sure controller is selected and specified */ if((fdout & (FDO_FRST|FDO_FDMAEN)) == 0) needspecify = 1; fdout |= (FDO_FRST|FDO_FDMAEN); } - outb(fdc_data[fdcu].baseport+FDOUT, fdout); - fdc_data[fdcu].fdout = fdout; + outb(fdc->baseport+FDOUT, fdout); + fdc->fdout = fdout; TRACE1("[0x%x->FDOUT]", fdout); - if(needspecify) { + if (needspecify) { /* * XXX * special case: since we have just woken up the FDC * from its sleep, we silently assume the command will * be accepted, and do not test for a timeout */ - (void)fd_cmd(fdcu, 3, NE7CMD_SPECIFY, + (void)fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), 0); - if (fdc_data[fdcu].flags & FDC_HAS_FIFO) - (void) enable_fifo(&fdc_data[fdcu]); + if (fdc->flags & FDC_HAS_FIFO) + (void) enable_fifo(fdc); } } static void -fd_turnoff(void *arg1) +fd_turnoff(void *xfd) { - fdu_t fdu = (fdu_t)arg1; int s; - fd_p fd = fd_data + fdu; + fd_p fd = xfd; - TRACE1("[fd%d: turnoff]", fdu); + TRACE1("[fd%d: turnoff]", fd->fdu); /* * Don't turn off the motor yet if the drive is active. * XXX shouldn't even schedule turnoff until drive is inactive * and nothing is queued on it. */ - if (fd->fdc->state != DEVIDLE && fd->fdc->fdu == fdu) { - fd->toffhandle = timeout(fd_turnoff, arg1, 4 * hz); + if (fd->fdc->state != DEVIDLE && fd->fdc->fdu == fd->fdu) { + fd->toffhandle = timeout(fd_turnoff, fd, 4 * hz); return; } s = splbio(); fd->flags &= ~FD_MOTOR; - set_motor(fd->fdc->fdcu, fd->fdsu, TURNOFF); + set_motor(fd->fdc, fd->fdsu, TURNOFF); splx(s); } static void -fd_motor_on(void *arg1) +fd_motor_on(void *xfd) { - fdu_t fdu = (fdu_t)arg1; int s; + fd_p fd = xfd; - fd_p fd = fd_data + fdu; s = splbio(); fd->flags &= ~FD_MOTOR_WAIT; if((fd->fdc->fd == fd) && (fd->fdc->state == MOTORWAIT)) { - fdintr(fd->fdc->fdcu); + fdc_intr(fd->fdc); } splx(s); } static void -fd_turnon(fdu_t fdu) +fd_turnon(fd_p fd) { - fd_p fd = fd_data + fdu; if(!(fd->flags & FD_MOTOR)) { fd->flags |= (FD_MOTOR + FD_MOTOR_WAIT); - set_motor(fd->fdc->fdcu, fd->fdsu, TURNON); - timeout(fd_motor_on, (caddr_t)fdu, hz); /* in 1 sec its ok */ + set_motor(fd->fdc, fd->fdsu, TURNON); + timeout(fd_motor_on, fd, hz); /* in 1 sec its ok */ } } static void fdc_reset(fdc_p fdc) { - fdcu_t fdcu = fdc->fdcu; - /* Try a reset, keep motor on */ outb(fdc->baseport + FDOUT, fdc->fdout & ~(FDO_FRST|FDO_FDMAEN)); TRACE1("[0x%x->FDOUT]", fdc->fdout & ~(FDO_FRST|FDO_FDMAEN)); DELAY(100); /* enable FDC, but defer interrupts a moment */ outb(fdc->baseport + FDOUT, fdc->fdout & ~FDO_FDMAEN); TRACE1("[0x%x->FDOUT]", fdc->fdout & ~FDO_FDMAEN); DELAY(100); outb(fdc->baseport + FDOUT, fdc->fdout); TRACE1("[0x%x->FDOUT]", fdc->fdout); /* XXX after a reset, silently believe the FDC will accept commands */ - (void)fd_cmd(fdcu, 3, NE7CMD_SPECIFY, + (void)fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), 0); if (fdc->flags & FDC_HAS_FIFO) (void) enable_fifo(fdc); } /****************************************************************************/ /* fdc in/out */ /****************************************************************************/ int -in_fdc(fdcu_t fdcu) +in_fdc(struct fdc_data *fdc) { - int baseport = fdc_data[fdcu].baseport; + int baseport = fdc->baseport; int i, j = 100000; while ((i = inb(baseport+FDSTS) & (NE7_DIO|NE7_RQM)) != (NE7_DIO|NE7_RQM) && j-- > 0) if (i == NE7_RQM) - return fdc_err(fdcu, "ready for output in input\n"); + return fdc_err(fdc, "ready for output in input\n"); if (j <= 0) - return fdc_err(fdcu, bootverbose? "input ready timeout\n": 0); + return fdc_err(fdc, bootverbose? "input ready timeout\n": 0); #ifdef FDC_DEBUG i = inb(baseport+FDDATA); TRACE1("[FDDATA->0x%x]", (unsigned char)i); return(i); #else /* !FDC_DEBUG */ return inb(baseport+FDDATA); #endif /* FDC_DEBUG */ } /* * fd_in: Like in_fdc, but allows you to see if it worked. */ static int -fd_in(fdcu_t fdcu, int *ptr) +fd_in(struct fdc_data *fdc, int *ptr) { - int baseport = fdc_data[fdcu].baseport; + int baseport = fdc->baseport; int i, j = 100000; while ((i = inb(baseport+FDSTS) & (NE7_DIO|NE7_RQM)) != (NE7_DIO|NE7_RQM) && j-- > 0) if (i == NE7_RQM) - return fdc_err(fdcu, "ready for output in input\n"); + return fdc_err(fdc, "ready for output in input\n"); if (j <= 0) - return fdc_err(fdcu, bootverbose? "input ready timeout\n": 0); + return fdc_err(fdc, bootverbose? "input ready timeout\n": 0); #ifdef FDC_DEBUG i = inb(baseport+FDDATA); TRACE1("[FDDATA->0x%x]", (unsigned char)i); *ptr = i; return 0; #else /* !FDC_DEBUG */ i = inb(baseport+FDDATA); if (ptr) *ptr = i; return 0; #endif /* FDC_DEBUG */ } int -out_fdc(fdcu_t fdcu, int x) +out_fdc(struct fdc_data *fdc, int x) { - int baseport = fdc_data[fdcu].baseport; + int baseport = fdc->baseport; int i; /* Check that the direction bit is set */ i = 100000; while ((inb(baseport+FDSTS) & NE7_DIO) && i-- > 0); - if (i <= 0) return fdc_err(fdcu, "direction bit not set\n"); + if (i <= 0) return fdc_err(fdc, "direction bit not set\n"); /* Check that the floppy controller is ready for a command */ i = 100000; while ((inb(baseport+FDSTS) & NE7_RQM) == 0 && i-- > 0); if (i <= 0) - return fdc_err(fdcu, bootverbose? "output ready timeout\n": 0); + return fdc_err(fdc, bootverbose? "output ready timeout\n": 0); /* Send the command and return */ outb(baseport+FDDATA, x); TRACE1("[0x%x->FDDATA]", x); return (0); } /****************************************************************************/ /* fdopen/fdclose */ /****************************************************************************/ int Fdopen(dev_t dev, int flags, int mode, struct proc *p) { fdu_t fdu = FDUNIT(minor(dev)); int type = FDTYPE(minor(dev)); + fd_p fd; fdc_p fdc; /* check bounds */ - if (fdu >= NFD) - return(ENXIO); - fdc = fd_data[fdu].fdc; - if ((fdc == NULL) || (fd_data[fdu].type == NO_TYPE)) - return(ENXIO); + if ((fd = devclass_get_softc(fd_devclass, fdu)) == 0) + return (ENXIO); + fdc = fd->fdc; + if ((fdc == NULL) || (fd->type == NO_TYPE)) + return (ENXIO); if (type > NUMDENS) - return(ENXIO); + return (ENXIO); if (type == 0) - type = fd_data[fdu].type; + type = fd->type; else { /* * For each type of basic drive, make sure we are trying * to open a type it can do, */ - if (type != fd_data[fdu].type) { - switch (fd_data[fdu].type) { + if (type != fd->type) { + switch (fd->type) { case FD_360: - return(ENXIO); + return (ENXIO); case FD_720: if ( type != FD_820 && type != FD_800 ) - return(ENXIO); + return (ENXIO); break; case FD_1200: switch (type) { case FD_1480: type = FD_1480in5_25; break; case FD_1440: type = FD_1440in5_25; break; case FD_820: type = FD_820in5_25; break; case FD_800: type = FD_800in5_25; break; case FD_720: type = FD_720in5_25; break; case FD_360: type = FD_360in5_25; break; default: return(ENXIO); } break; case FD_1440: if ( type != FD_1720 && type != FD_1480 && type != FD_1200 && type != FD_820 && type != FD_800 && type != FD_720 ) return(ENXIO); break; } } } - fd_data[fdu].ft = fd_types + type - 1; - fd_data[fdu].flags |= FD_OPEN; - + fd->ft = fd_types + type - 1; + fd->flags |= FD_OPEN; + device_busy(fd->dev); + device_busy(fd->fdc->fdc_dev); return 0; } int fdclose(dev_t dev, int flags, int mode, struct proc *p) { fdu_t fdu = FDUNIT(minor(dev)); + struct fd_data *fd; - fd_data[fdu].flags &= ~FD_OPEN; - fd_data[fdu].options &= ~FDOPT_NORETRY; + fd = devclass_get_softc(fd_devclass, fdu); + fd->flags &= ~FD_OPEN; + fd->options &= ~FDOPT_NORETRY; - return(0); + return (0); } static int fdread(dev_t dev, struct uio *uio, int ioflag) { return (physio(fdstrategy, NULL, dev, 1, minphys, uio)); } static int fdwrite(dev_t dev, struct uio *uio, int ioflag) { return (physio(fdstrategy, NULL, dev, 0, minphys, uio)); } /****************************************************************************/ /* fdstrategy */ /****************************************************************************/ void fdstrategy(struct buf *bp) { unsigned nblocks, blknum, cando; int s; - fdcu_t fdcu; fdu_t fdu; fdc_p fdc; fd_p fd; size_t fdblk; fdu = FDUNIT(minor(bp->b_dev)); - fd = &fd_data[fdu]; + fd = devclass_get_softc(fd_devclass, fdu); + if (fd == 0) + panic("fdstrategy: buf for nonexistent device (%#lx, %#lx)", + (u_long)major(bp->b_dev), (u_long)minor(bp->b_dev)); fdc = fd->fdc; - fdcu = fdc->fdcu; #ifdef FDC_YE if (fd->type == NO_TYPE) { bp->b_error = ENXIO; bp->b_flags |= B_ERROR; /* * I _refuse_ to use a goto */ biodone(bp); return; }; #endif fdblk = 128 << (fd->ft->secsize); if (!(bp->b_flags & B_FORMAT)) { - if ((fdu >= NFD) || (bp->b_blkno < 0)) { + if (bp->b_blkno < 0) { printf( "fd%d: fdstrat: bad request blkno = %lu, bcount = %ld\n", fdu, (u_long)bp->b_blkno, bp->b_bcount); bp->b_error = EINVAL; bp->b_flags |= B_ERROR; goto bad; } if ((bp->b_bcount % fdblk) != 0) { bp->b_error = EINVAL; bp->b_flags |= B_ERROR; goto bad; } } /* * Set up block calculations. */ if (bp->b_blkno > 20000000) { /* * Reject unreasonably high block number, prevent the * multiplication below from overflowing. */ bp->b_error = EINVAL; bp->b_flags |= B_ERROR; goto bad; } blknum = (unsigned) bp->b_blkno * DEV_BSIZE/fdblk; nblocks = fd->ft->size; bp->b_resid = 0; if (blknum + (bp->b_bcount / fdblk) > nblocks) { if (blknum <= nblocks) { cando = (nblocks - blknum) * fdblk; bp->b_resid = bp->b_bcount - cando; if (cando == 0) goto bad; /* not actually bad but EOF */ } else { bp->b_error = EINVAL; bp->b_flags |= B_ERROR; goto bad; } } bp->b_pblkno = bp->b_blkno; s = splbio(); bufqdisksort(&fdc->head, bp); - untimeout(fd_turnoff, (caddr_t)fdu, fd->toffhandle); /* a good idea */ + untimeout(fd_turnoff, fd, fd->toffhandle); /* a good idea */ /* Tell devstat we are starting on the transaction */ devstat_start_transaction(&fd->device_stats); - fdstart(fdcu); + fdstart(fdc); splx(s); return; bad: biodone(bp); } /***************************************************************\ * fdstart * * We have just queued something.. if the controller is not busy * * then simulate the case where it has just finished a command * * So that it (the interrupt routine) looks on the queue for more* * work to do and picks up what we just added. * * If the controller is already busy, we need do nothing, as it * * will pick up our work when the present work completes * \***************************************************************/ static void -fdstart(fdcu_t fdcu) +fdstart(struct fdc_data *fdc) { int s; s = splbio(); - if(fdc_data[fdcu].state == DEVIDLE) + if(fdc->state == DEVIDLE) { - fdintr(fdcu); + fdc_intr(fdc); } splx(s); } static void -fd_iotimeout(void *arg1) +fd_iotimeout(void *xfdc) { fdc_p fdc; - fdcu_t fdcu; int s; - fdcu = (fdcu_t)arg1; - fdc = fdc_data + fdcu; + fdc = xfdc; TRACE1("fd%d[fd_iotimeout()]", fdc->fdu); /* * Due to IBM's brain-dead design, the FDC has a faked ready * signal, hardwired to ready == true. Thus, any command * issued if there's no diskette in the drive will _never_ * complete, and must be aborted by resetting the FDC. * Many thanks, Big Blue! * The FDC must not be reset directly, since that would * interfere with the state machine. Instead, pretend that * the command completed but was invalid. The state machine * will reset the FDC and retry once. */ s = splbio(); fdc->status[0] = NE7_ST0_IC_IV; fdc->flags &= ~FDC_STAT_VALID; fdc->state = IOTIMEDOUT; - fdintr(fdcu); + fdc_intr(fdc); splx(s); } /* just ensure it has the right spl */ static void -fd_pseudointr(void *arg1) +fd_pseudointr(void *xfdc) { - fdcu_t fdcu = (fdcu_t)arg1; int s; s = splbio(); - fdintr(fdcu); + fdc_intr(xfdc); splx(s); } /***********************************************************************\ * fdintr * * keep calling the state machine until it returns a 0 * * ALWAYS called at SPLBIO * \***********************************************************************/ static void -fdintr(fdcu_t fdcu) +fdc_intr(void *xfdc) { - fdc_p fdc = fdc_data + fdcu; - while(fdstate(fdcu, fdc)) - ; + fdc_p fdc = xfdc; + while(fdstate(fdc)) + ; } #ifdef FDC_YE /* * magic pseudo-DMA initialization for YE FDC. Sets count and * direction */ #define SET_BCDR(wr,cnt,port) outb(port,(((cnt)-1) & 0xff)); \ outb(port+1,((wr ? 0x80 : 0) | ((((cnt)-1) >> 8) & 0x7f))) /* * fdcpio(): perform programmed IO read/write for YE PCMCIA floppy */ static int fdcpio(fdcu_t fdcu, long flags, caddr_t addr, u_int count) { u_char *cptr = (u_char *)addr; fdc_p fdc = &fdc_data[fdcu]; int io = fdc->baseport; if (flags & B_READ) { if (fdc->state != PIOREAD) { fdc->state = PIOREAD; return(0); }; SET_BCDR(0,count,io); insb(io+FDC_YE_DATAPORT,cptr,count); } else { outsb(io+FDC_YE_DATAPORT,cptr,count); SET_BCDR(0,count,io); }; return(1); } #endif /* FDC_YE */ /***********************************************************************\ * The controller state machine. * * if it returns a non zero value, it should be called again immediatly * \***********************************************************************/ static int -fdstate(fdcu_t fdcu, fdc_p fdc) +fdstate(fdc_p fdc) { int read, format, head, i, sec = 0, sectrac, st0, cyl, st3; unsigned blknum = 0, b_cylinder = 0; fdu_t fdu = fdc->fdu; fd_p fd; register struct buf *bp; struct fd_formb *finfo = NULL; size_t fdblk; bp = fdc->bp; if (bp == NULL) { bp = bufq_first(&fdc->head); if (bp != NULL) { bufq_remove(&fdc->head, bp); fdc->bp = bp; } } if (bp == NULL) { /***********************************************\ * nothing left for this controller to do * * Force into the IDLE state, * \***********************************************/ fdc->state = DEVIDLE; - if(fdc->fd) - { - printf("fd%d: unexpected valid fd pointer\n", - fdc->fdu); + if (fdc->fd) { + device_print_prettyname(fdc->fdc_dev); + printf("unexpected valid fd pointer\n"); fdc->fd = (fd_p) 0; fdc->fdu = -1; } - TRACE1("[fdc%d IDLE]", fdcu); - return(0); + TRACE1("[fdc%d IDLE]", fdc->fdcu); + return (0); } fdu = FDUNIT(minor(bp->b_dev)); - fd = fd_data + fdu; + fd = devclass_get_softc(fd_devclass, fdu); fdblk = 128 << fd->ft->secsize; - if (fdc->fd && (fd != fdc->fd)) - { - printf("fd%d: confused fd pointers\n", fdu); + if (fdc->fd && (fd != fdc->fd)) { + device_print_prettyname(fd->dev); + printf("confused fd pointers\n"); } read = bp->b_flags & B_READ; format = bp->b_flags & B_FORMAT; - if(format) { + if (format) { finfo = (struct fd_formb *)bp->b_data; fd->skip = (char *)&(finfo->fd_formb_cylno(0)) - (char *)finfo; } if (fdc->state == DOSEEK || fdc->state == SEEKCOMPLETE) { blknum = (unsigned) bp->b_pblkno * DEV_BSIZE/fdblk + fd->skip/fdblk; b_cylinder = blknum / (fd->ft->sectrac * fd->ft->heads); } TRACE1("fd%d", fdu); TRACE1("[%s]", fdstates[fdc->state]); TRACE1("(0x%x)", fd->flags); - untimeout(fd_turnoff, (caddr_t)fdu, fd->toffhandle); - fd->toffhandle = timeout(fd_turnoff, (caddr_t)fdu, 4 * hz); + untimeout(fd_turnoff, fd, fd->toffhandle); + fd->toffhandle = timeout(fd_turnoff, fd, 4 * hz); switch (fdc->state) { case DEVIDLE: case FINDWORK: /* we have found new work */ fdc->retry = 0; fd->skip = 0; fdc->fd = fd; fdc->fdu = fdu; outb(fdc->baseport+FDCTL, fd->ft->trans); TRACE1("[0x%x->FDCTL]", fd->ft->trans); /*******************************************************\ * If the next drive has a motor startup pending, then * * it will start up in its own good time * \*******************************************************/ - if(fd->flags & FD_MOTOR_WAIT) - { + if(fd->flags & FD_MOTOR_WAIT) { fdc->state = MOTORWAIT; - return(0); /* come back later */ + return (0); /* come back later */ } /*******************************************************\ * Maybe if it's not starting, it SHOULD be starting * \*******************************************************/ if (!(fd->flags & FD_MOTOR)) { fdc->state = MOTORWAIT; - fd_turnon(fdu); - return(0); + fd_turnon(fd); + return (0); } else /* at least make sure we are selected */ { - set_motor(fdcu, fd->fdsu, TURNON); + set_motor(fdc, fd->fdsu, TURNON); } if (fdc->flags & FDC_NEEDS_RESET) { fdc->state = RESETCTLR; fdc->flags &= ~FDC_NEEDS_RESET; } else fdc->state = DOSEEK; break; case DOSEEK: if (b_cylinder == (unsigned)fd->track) { fdc->state = SEEKCOMPLETE; break; } - if (fd_cmd(fdcu, 3, NE7CMD_SEEK, + if (fd_cmd(fdc, 3, NE7CMD_SEEK, fd->fdsu, b_cylinder * fd->ft->steptrac, 0)) { /* * seek command not accepted, looks like * the FDC went off to the Saints... */ fdc->retry = 6; /* try a reset */ - return(retrier(fdcu)); + return(retrier(fdc)); } fd->track = FD_NO_TRACK; fdc->state = SEEKWAIT; return(0); /* will return later */ case SEEKWAIT: /* allow heads to settle */ - timeout(fd_pseudointr, (caddr_t)fdcu, hz / 16); + timeout(fd_pseudointr, fdc, hz / 16); fdc->state = SEEKCOMPLETE; return(0); /* will return later */ case SEEKCOMPLETE : /* SEEK DONE, START DMA */ /* Make sure seek really happened*/ - if(fd->track == FD_NO_TRACK) - { + if(fd->track == FD_NO_TRACK) { int descyl = b_cylinder * fd->ft->steptrac; do { /* * This might be a "ready changed" interrupt, * which cannot really happen since the * RDY pin is hardwired to + 5 volts. This * generally indicates a "bouncing" intr * line, so do one of the following: * * When running on an enhanced FDC that is * known to not go stuck after responding * with INVALID, fetch all interrupt states * until seeing either an INVALID or a * real interrupt condition. * * When running on a dumb old NE765, give * up immediately. The controller will * provide up to four dummy RC interrupt * conditions right after reset (for the * corresponding four drives), so this is * our only chance to get notice that it * was not the FDC that caused the interrupt. */ if (fd_sense_int(fdc, &st0, &cyl) == FD_NOT_VALID) return 0; if(fdc->fdct == FDC_NE765 && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC) return 0; /* hope for a real intr */ } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC); - if (0 == descyl) - { + if (0 == descyl) { int failed = 0; /* * seek to cyl 0 requested; make sure we are * really there */ if (fd_sense_drive_status(fdc, &st3)) failed = 1; if ((st3 & NE7_ST3_T0) == 0) { printf( "fd%d: Seek to cyl 0, but not really there (ST3 = %b)\n", fdu, st3, NE7_ST3BITS); failed = 1; } - if (failed) - { + if (failed) { if(fdc->retry < 3) fdc->retry = 3; - return(retrier(fdcu)); + return (retrier(fdc)); } } - if (cyl != descyl) - { + if (cyl != descyl) { printf( "fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n", fdu, descyl, cyl, st0); if (fdc->retry < 3) fdc->retry = 3; - return(retrier(fdcu)); + return (retrier(fdc)); } } fd->track = b_cylinder; #ifdef FDC_YE if (!(fdc->flags & FDC_PCMCIA)) #endif isa_dmastart(bp->b_flags, bp->b_data+fd->skip, format ? bp->b_bcount : fdblk, fdc->dmachan); sectrac = fd->ft->sectrac; sec = blknum % (sectrac * fd->ft->heads); head = sec / sectrac; sec = sec % sectrac + 1; fd->hddrv = ((head&1)<<2)+fdu; if(format || !read) { /* make sure the drive is writable */ if(fd_sense_drive_status(fdc, &st3) != 0) { /* stuck controller? */ isa_dmadone(bp->b_flags, bp->b_data + fd->skip, format ? bp->b_bcount : fdblk, fdc->dmachan); fdc->retry = 6; /* reset the beast */ - return(retrier(fdcu)); + return (retrier(fdc)); } if(st3 & NE7_ST3_WP) { /* * XXX YES! this is ugly. * in order to force the current operation * to fail, we will have to fake an FDC * error - all error handling is done * by the retrier() */ fdc->status[0] = NE7_ST0_IC_AT; fdc->status[1] = NE7_ST1_NW; fdc->status[2] = 0; fdc->status[3] = fd->track; fdc->status[4] = head; fdc->status[5] = sec; fdc->retry = 8; /* break out immediately */ fdc->state = IOTIMEDOUT; /* not really... */ return (1); } } - if(format) - { + if (format) { #ifdef FDC_YE if (fdc->flags & FDC_PCMCIA) (void)fdcpio(fdcu,bp->b_flags, bp->b_data+fd->skip, bp->b_bcount); #endif /* formatting */ - if(fd_cmd(fdcu, 6, - NE7CMD_FORMAT, - head << 2 | fdu, + if(fd_cmd(fdc, 6, NE7CMD_FORMAT, head << 2 | fdu, finfo->fd_formb_secshift, finfo->fd_formb_nsecs, finfo->fd_formb_gaplen, - finfo->fd_formb_fillbyte, - 0)) - { + finfo->fd_formb_fillbyte, 0)) { /* controller fell over */ isa_dmadone(bp->b_flags, bp->b_data + fd->skip, format ? bp->b_bcount : fdblk, fdc->dmachan); fdc->retry = 6; - return(retrier(fdcu)); + return (retrier(fdc)); } - } - else - { + } else { #ifdef FDC_YE if (fdc->flags & FDC_PCMCIA) { /* * this seems to be necessary even when * reading data */ SET_BCDR(1,fdblk,fdc->baseport); /* * perform the write pseudo-DMA before * the WRITE command is sent */ if (!read) (void)fdcpio(fdcu,bp->b_flags, bp->b_data+fd->skip, fdblk); } #endif - if (fd_cmd(fdcu, 9, + if (fd_cmd(fdc, 9, (read ? NE7CMD_READ : NE7CMD_WRITE), head << 2 | fdu, /* head & unit */ fd->track, /* track */ head, sec, /* sector + 1 */ fd->ft->secsize, /* sector size */ sectrac, /* sectors/track */ fd->ft->gap, /* gap size */ fd->ft->datalen, /* data length */ - 0)) - { + 0)) { /* the beast is sleeping again */ isa_dmadone(bp->b_flags, bp->b_data + fd->skip, format ? bp->b_bcount : fdblk, fdc->dmachan); fdc->retry = 6; - return(retrier(fdcu)); + return (retrier(fdc)); } } #ifdef FDC_YE if (fdc->flags & FDC_PCMCIA) /* * if this is a read, then simply await interrupt * before performing PIO */ if (read && !fdcpio(fdcu,bp->b_flags, bp->b_data+fd->skip,fdblk)) { fd->tohandle = timeout(fd_iotimeout, (caddr_t)fdcu, hz); return(0); /* will return later */ }; /* * write (or format) operation will fall through and * await completion interrupt */ #endif fdc->state = IOCOMPLETE; - fd->tohandle = timeout(fd_iotimeout, (caddr_t)fdcu, hz); - return(0); /* will return later */ + fd->tohandle = timeout(fd_iotimeout, fdc, hz); + return (0); /* will return later */ #ifdef FDC_YE case PIOREAD: /* * actually perform the PIO read. The IOCOMPLETE case * removes the timeout for us. */ (void)fdcpio(fdcu,bp->b_flags,bp->b_data+fd->skip,fdblk); fdc->state = IOCOMPLETE; /* FALLTHROUGH */ #endif case IOCOMPLETE: /* IO DONE, post-analyze */ - untimeout(fd_iotimeout, (caddr_t)fdcu, fd->tohandle); + untimeout(fd_iotimeout, fdc, fd->tohandle); - if (fd_read_status(fdc, fd->fdsu)) - { + if (fd_read_status(fdc, fd->fdsu)) { isa_dmadone(bp->b_flags, bp->b_data + fd->skip, format ? bp->b_bcount : fdblk, fdc->dmachan); if (fdc->retry < 6) fdc->retry = 6; /* force a reset */ - return retrier(fdcu); + return (retrier(fdc)); } fdc->state = IOTIMEDOUT; /* FALLTHROUGH */ case IOTIMEDOUT: #ifdef FDC_YE if (!(fdc->flags & FDC_PCMCIA)) #endif isa_dmadone(bp->b_flags, bp->b_data + fd->skip, format ? bp->b_bcount : fdblk, fdc->dmachan); - if (fdc->status[0] & NE7_ST0_IC) - { + if (fdc->status[0] & NE7_ST0_IC) { if ((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT && fdc->status[1] & NE7_ST1_OR) { /* * DMA overrun. Someone hogged the bus * and didn't release it in time for the * next FDC transfer. * Just restart it, don't increment retry * count. (vak) */ fdc->state = SEEKCOMPLETE; return (1); } else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_IV && fdc->retry < 6) fdc->retry = 6; /* force a reset */ else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT && fdc->status[2] & NE7_ST2_WC && fdc->retry < 3) fdc->retry = 3; /* force recalibrate */ - return(retrier(fdcu)); + return (retrier(fdc)); } /* All OK */ fd->skip += fdblk; - if (!format && fd->skip < bp->b_bcount - bp->b_resid) - { + if (!format && fd->skip < bp->b_bcount - bp->b_resid) { /* set up next transfer */ fdc->state = DOSEEK; - } - else - { + } else { /* ALL DONE */ fd->skip = 0; fdc->bp = NULL; /* Tell devstat we have finished with the transaction */ devstat_end_transaction(&fd->device_stats, bp->b_bcount - bp->b_resid, DEVSTAT_TAG_NONE, (bp->b_flags & B_READ) ? DEVSTAT_READ : DEVSTAT_WRITE); biodone(bp); fdc->fd = (fd_p) 0; fdc->fdu = -1; fdc->state = FINDWORK; } - return(1); + return (1); case RESETCTLR: fdc_reset(fdc); fdc->retry++; fdc->state = RESETCOMPLETE; return (0); case RESETCOMPLETE: /* * Discard all the results from the reset so that they * can't cause an unexpected interrupt later. */ for (i = 0; i < 4; i++) (void)fd_sense_int(fdc, &st0, &cyl); fdc->state = STARTRECAL; /* Fall through. */ case STARTRECAL: - if(fd_cmd(fdcu, - 2, NE7CMD_RECAL, fdu, - 0)) /* Recalibrate Function */ - { + if(fd_cmd(fdc, 2, NE7CMD_RECAL, fdu, 0)) { /* arrgl */ fdc->retry = 6; - return(retrier(fdcu)); + return (retrier(fdc)); } fdc->state = RECALWAIT; - return(0); /* will return later */ + return (0); /* will return later */ case RECALWAIT: /* allow heads to settle */ - timeout(fd_pseudointr, (caddr_t)fdcu, hz / 8); + timeout(fd_pseudointr, fdc, hz / 8); fdc->state = RECALCOMPLETE; - return(0); /* will return later */ + return (0); /* will return later */ case RECALCOMPLETE: do { /* * See SEEKCOMPLETE for a comment on this: */ if (fd_sense_int(fdc, &st0, &cyl) == FD_NOT_VALID) return 0; if(fdc->fdct == FDC_NE765 && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC) return 0; /* hope for a real intr */ } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC); if ((st0 & NE7_ST0_IC) != NE7_ST0_IC_NT || cyl != 0) { if(fdc->retry > 3) /* * a recalibrate from beyond cylinder 77 * will "fail" due to the FDC limitations; * since people used to complain much about * the failure message, try not logging * this one if it seems to be the first * time in a line */ printf("fd%d: recal failed ST0 %b cyl %d\n", fdu, st0, NE7_ST0BITS, cyl); if(fdc->retry < 3) fdc->retry = 3; - return(retrier(fdcu)); + return (retrier(fdc)); } fd->track = 0; /* Seek (probably) necessary */ fdc->state = DOSEEK; - return(1); /* will return immediatly */ + return (1); /* will return immediatly */ case MOTORWAIT: if(fd->flags & FD_MOTOR_WAIT) { - return(0); /* time's not up yet */ + return (0); /* time's not up yet */ } if (fdc->flags & FDC_NEEDS_RESET) { fdc->state = RESETCTLR; fdc->flags &= ~FDC_NEEDS_RESET; } else { /* * If all motors were off, then the controller was * reset, so it has lost track of the current * cylinder. Recalibrate to handle this case. */ fdc->state = STARTRECAL; } - return(1); /* will return immediatly */ + return (1); /* will return immediatly */ default: - printf("fdc%d: Unexpected FD int->", fdcu); + device_print_prettyname(fdc->fdc_dev); + printf("unexpected FD int->"); if (fd_read_status(fdc, fd->fdsu) == 0) printf("FDC status :%x %x %x %x %x %x %x ", fdc->status[0], fdc->status[1], fdc->status[2], fdc->status[3], fdc->status[4], fdc->status[5], fdc->status[6] ); else printf("No status available "); if (fd_sense_int(fdc, &st0, &cyl) != 0) { printf("[controller is dead now]\n"); - return(0); + return (0); } printf("ST0 = %x, PCN = %x\n", st0, cyl); - return(0); + return (0); } /*XXX confusing: some branches return immediately, others end up here*/ - return(1); /* Come back immediatly to new state */ + return (1); /* Come back immediatly to new state */ } static int -retrier(fdcu) - fdcu_t fdcu; +retrier(struct fdc_data *fdc) { - fdc_p fdc = fdc_data + fdcu; register struct buf *bp; + struct fd_data *fd; + int fdu; bp = fdc->bp; - if(fd_data[FDUNIT(minor(bp->b_dev))].options & FDOPT_NORETRY) + /* XXX shouldn't this be cached somewhere? */ + fdu = FDUNIT(minor(bp->b_dev)); + fd = devclass_get_softc(fd_devclass, fdu); + if (fd->options & FDOPT_NORETRY) goto fail; - switch(fdc->retry) - { + + switch (fdc->retry) { case 0: case 1: case 2: fdc->state = SEEKCOMPLETE; break; case 3: case 4: case 5: fdc->state = STARTRECAL; break; case 6: fdc->state = RESETCTLR; break; case 7: break; default: fail: { dev_t sav_b_dev = bp->b_dev; /* Trick diskerr */ bp->b_dev = makedev(major(bp->b_dev), (FDUNIT(minor(bp->b_dev))<<3)|RAW_PART); diskerr(bp, "fd", "hard error", LOG_PRINTF, fdc->fd->skip / DEV_BSIZE, (struct disklabel *)NULL); bp->b_dev = sav_b_dev; if (fdc->flags & FDC_STAT_VALID) { printf( " (ST0 %b ST1 %b ST2 %b cyl %u hd %u sec %u)\n", fdc->status[0], NE7_ST0BITS, fdc->status[1], NE7_ST1BITS, fdc->status[2], NE7_ST2BITS, fdc->status[3], fdc->status[4], fdc->status[5]); } else printf(" (No status)\n"); } bp->b_flags |= B_ERROR; bp->b_error = EIO; bp->b_resid += bp->b_bcount - fdc->fd->skip; fdc->bp = NULL; /* Tell devstat we have finished with the transaction */ devstat_end_transaction(&fdc->fd->device_stats, bp->b_bcount - bp->b_resid, DEVSTAT_TAG_NONE, (bp->b_flags & B_READ) ? DEVSTAT_READ : DEVSTAT_WRITE); fdc->fd->skip = 0; biodone(bp); fdc->state = FINDWORK; fdc->flags |= FDC_NEEDS_RESET; fdc->fd = (fd_p) 0; fdc->fdu = -1; - return(1); + return (1); } fdc->retry++; - return(1); + return (1); } static int fdformat(dev, finfo, p) dev_t dev; struct fd_formb *finfo; struct proc *p; { fdu_t fdu; fd_p fd; struct buf *bp; int rv = 0, s; size_t fdblk; fdu = FDUNIT(minor(dev)); - fd = &fd_data[fdu]; + fd = devclass_get_softc(fd_devclass, fdu); fdblk = 128 << fd->ft->secsize; /* set up a buffer header for fdstrategy() */ bp = (struct buf *)malloc(sizeof(struct buf), M_TEMP, M_NOWAIT); if(bp == 0) return ENOBUFS; /* * keep the process from being swapped */ PHOLD(p); bzero((void *)bp, sizeof(struct buf)); bp->b_flags = B_BUSY | B_PHYS | B_FORMAT; bp->b_proc = p; /* * calculate a fake blkno, so fdstrategy() would initiate a * seek to the requested cylinder */ bp->b_blkno = (finfo->cyl * (fd->ft->sectrac * fd->ft->heads) + finfo->head * fd->ft->sectrac) * fdblk / DEV_BSIZE; bp->b_bcount = sizeof(struct fd_idfield_data) * finfo->fd_formb_nsecs; bp->b_data = (caddr_t)finfo; /* now do the format */ bp->b_dev = dev; fdstrategy(bp); /* ...and wait for it to complete */ s = splbio(); - while(!(bp->b_flags & B_DONE)) - { + while(!(bp->b_flags & B_DONE)) { rv = tsleep((caddr_t)bp, PRIBIO, "fdform", 20 * hz); - if(rv == EWOULDBLOCK) + if (rv == EWOULDBLOCK) break; } splx(s); - if(rv == EWOULDBLOCK) { + if (rv == EWOULDBLOCK) { /* timed out */ rv = EIO; biodone(bp); } - if(bp->b_flags & B_ERROR) + if (bp->b_flags & B_ERROR) rv = bp->b_error; /* * allow the process to be swapped */ PRELE(p); free(bp, M_TEMP); return rv; } /* * TODO: don't allocate buffer on stack. */ static int fdioctl(dev, cmd, addr, flag, p) dev_t dev; u_long cmd; caddr_t addr; int flag; struct proc *p; { fdu_t fdu = FDUNIT(minor(dev)); - fd_p fd = &fd_data[fdu]; + fd_p fd = devclass_get_softc(fd_devclass, fdu); size_t fdblk; struct fd_type *fdt; struct disklabel *dl; char buffer[DEV_BSIZE]; int error = 0; fdblk = 128 << fd->ft->secsize; - switch (cmd) - { + switch (cmd) { case DIOCGDINFO: bzero(buffer, sizeof (buffer)); dl = (struct disklabel *)buffer; dl->d_secsize = fdblk; - fdt = fd_data[FDUNIT(minor(dev))].ft; + fdt = fd->ft; dl->d_secpercyl = fdt->size / fdt->tracks; dl->d_type = DTYPE_FLOPPY; if (readdisklabel(dkmodpart(dev, RAW_PART), fdstrategy, dl) == NULL) error = 0; else error = EINVAL; *(struct disklabel *)addr = *dl; break; case DIOCSDINFO: if ((flag & FWRITE) == 0) error = EBADF; break; case DIOCWLABEL: if ((flag & FWRITE) == 0) error = EBADF; break; case DIOCWDINFO: - if ((flag & FWRITE) == 0) - { + if ((flag & FWRITE) == 0) { error = EBADF; break; } dl = (struct disklabel *)addr; if ((error = setdisklabel((struct disklabel *)buffer, dl, (u_long)0)) != 0) break; error = writedisklabel(dev, fdstrategy, (struct disklabel *)buffer); break; case FD_FORM: - if((flag & FWRITE) == 0) + if ((flag & FWRITE) == 0) error = EBADF; /* must be opened for writing */ - else if(((struct fd_formb *)addr)->format_version != + else if (((struct fd_formb *)addr)->format_version != FD_FORMAT_VERSION) error = EINVAL; /* wrong version of formatting prog */ else error = fdformat(dev, (struct fd_formb *)addr, p); break; case FD_GTYPE: /* get drive type */ *(struct fd_type *)addr = *fd->ft; break; case FD_STYPE: /* set drive type */ /* this is considered harmful; only allow for superuser */ - if(suser(p->p_ucred, &p->p_acflag) != 0) + if (suser(p->p_ucred, &p->p_acflag) != 0) return EPERM; *fd->ft = *(struct fd_type *)addr; break; case FD_GOPTS: /* get drive options */ *(int *)addr = fd->options; break; case FD_SOPTS: /* set drive options */ fd->options = *(int *)addr; break; default: error = ENOTTY; break; } return (error); } +static device_method_t fdc_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, fdc_probe), + DEVMETHOD(device_attach, fdc_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), -static fd_devsw_installed = 0; + /* Bus interface */ + DEVMETHOD(bus_print_child, fdc_print_child), + /* Our children never use any other bus interface methods. */ -static void fd_drvinit(void *notused ) -{ + { 0, 0 } +}; - if( ! fd_devsw_installed ) { - cdevsw_add_generic(BDEV_MAJOR,CDEV_MAJOR, &fd_cdevsw); - fd_devsw_installed = 1; - } -} +static driver_t fdc_driver = { + "fdc", + fdc_methods, + DRIVER_TYPE_BIO, + sizeof(struct fdc_data) +}; -SYSINIT(fddev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,fd_drvinit,NULL) +DRIVER_MODULE(fdc, isa, fdc_driver, fdc_devclass, 0, 0); +static device_method_t fd_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, fd_probe), + DEVMETHOD(device_attach, fd_attach), + DEVMETHOD(device_detach, bus_generic_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + DEVMETHOD(device_suspend, bus_generic_suspend), /* XXX */ + DEVMETHOD(device_resume, bus_generic_resume), /* XXX */ -#endif + { 0, 0 } +}; + +static driver_t fd_driver = { + "fd", + fd_methods, + DRIVER_TYPE_BIO, + sizeof(struct fd_data) +}; + +static struct cdevsw fd_cdevsw = { + Fdopen, fdclose, fdread, fdwrite, + fdioctl, nostop, nullreset, nodevtotty, + seltrue, nommap, fdstrategy, "fd", + NULL, -1, nodump, nopsize, + D_DISK, 0, -1 +}; + +BDEV_DRIVER_MODULE(fd, fdc, fd_driver, fd_devclass, BDEV_MAJOR, CDEV_MAJOR, + fd_cdevsw, 0, 0); + +#endif /* NFDC > 0 */ /* * Hello emacs, these are the * Local Variables: * c-indent-level: 8 * c-continued-statement-offset: 8 * c-continued-brace-offset: 0 * c-brace-offset: -8 * c-brace-imaginary-offset: 0 * c-argdecl-indent: 8 * c-label-offset: -8 * c++-hanging-braces: 1 * c++-access-specifier-offset: -8 * c++-empty-arglist-indent: 8 * c++-friend-offset: 0 * End: */ Index: head/sys/isa/fdc.h =================================================================== --- head/sys/isa/fdc.h (revision 45719) +++ head/sys/isa/fdc.h (revision 45720) @@ -1,91 +1,95 @@ /*- * Copyright (c) 1990 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: @(#)fd.c 7.4 (Berkeley) 5/25/91 - * $Id: fdc.h,v 1.12 1998/12/12 08:16:01 imp Exp $ + * $Id: fdc.h,v 1.13 1999/01/15 09:15:27 bde Exp $ * */ enum fdc_type { FDC_NE765, FDC_I82077, FDC_NE72065, FDC_UNKNOWN = -1 }; /***********************************************************************\ * Per controller structure. * \***********************************************************************/ struct fdc_data { int fdcu; /* our unit number */ int baseport; int dmachan; int flags; #define FDC_ATTACHED 0x01 #define FDC_HASFTAPE 0x02 #define FDC_TAPE_BUSY 0x04 #define FDC_STAT_VALID 0x08 #define FDC_HAS_FIFO 0x10 #define FDC_NEEDS_RESET 0x20 #ifdef FDC_YE #define FDC_PCMCIA 0x40 #define FDC_UNLOADED 0x80 #endif struct fd_data *fd; int fdu; /* the active drive */ int state; int retry; int fdout; /* mirror of the w/o digital output reg */ u_int status[7]; /* copy of the registers */ enum fdc_type fdct; /* chip version of FDC */ int fdc_errs; /* number of logged errors */ struct buf_queue_head head; struct buf *bp; /* active buffer */ + struct resource *res_ioport, *res_irq, *res_drq; + int rid_ioport, rid_irq, rid_drq; + void *fdc_intr; + struct device *fdc_dev; }; /***********************************************************************\ * Throughout this file the following conventions will be used: * * fd is a pointer to the fd_data struct for the drive in question * * fdc is a pointer to the fdc_data struct for the controller * * fdu is the floppy drive unit number * * fdcu is the floppy controller unit number * * fdsu is the floppy drive unit number on that controller. (sub-unit) * \***********************************************************************/ typedef int fdu_t; typedef int fdcu_t; typedef int fdsu_t; typedef struct fd_data *fd_p; typedef struct fdc_data *fdc_p; typedef enum fdc_type fdc_t; #define FDUNIT(s) (((s)>>6)&03) #define FDTYPE(s) ((s)&077) Index: head/sys/isa/isavar.h =================================================================== --- head/sys/isa/isavar.h (revision 45719) +++ head/sys/isa/isavar.h (revision 45720) @@ -1,81 +1,84 @@ /*- * Copyright (c) 1998 Doug Rabson * 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. * - * $Id: isavar.h,v 1.1 1998/08/06 08:49:09 dfr Exp $ + * $Id: isavar.h,v 1.2 1998/11/15 18:25:17 dfr Exp $ */ #define ISA_NPORT_IVARS 2 #define ISA_NMEM_IVARS 2 #define ISA_NIRQ_IVARS 2 #define ISA_NDRQ_IVARS 2 enum isa_device_ivars { ISA_IVAR_PORT, ISA_IVAR_PORT_0 = ISA_IVAR_PORT, ISA_IVAR_PORT_1, ISA_IVAR_PORTSIZE, ISA_IVAR_PORTSIZE_0 = ISA_IVAR_PORTSIZE, ISA_IVAR_PORTSIZE_1, ISA_IVAR_MADDR, ISA_IVAR_MADDR_0 = ISA_IVAR_MADDR, ISA_IVAR_MADDR_1, ISA_IVAR_MSIZE, ISA_IVAR_MSIZE_0 = ISA_IVAR_MSIZE, ISA_IVAR_MSIZE_1, ISA_IVAR_FLAGS, ISA_IVAR_IRQ, ISA_IVAR_IRQ_0 = ISA_IVAR_IRQ, ISA_IVAR_IRQ_1, ISA_IVAR_DRQ, ISA_IVAR_DRQ_0 = ISA_IVAR_DRQ, ISA_IVAR_DRQ_1 }; extern int isa_irq_pending(void); extern int isa_irq_mask(void); /* * Simplified accessors for isa devices */ #define ISA_ACCESSOR(A, B, T) \ \ static __inline T isa_get_ ## A(device_t dev) \ { \ - u_long v; \ + uintptr_t v; \ BUS_READ_IVAR(device_get_parent(dev), dev, ISA_IVAR_ ## B, &v); \ return (T) v; \ } \ \ static __inline void isa_set_ ## A(device_t dev, T t) \ { \ u_long v = (u_long) t; \ BUS_WRITE_IVAR(device_get_parent(dev), dev, ISA_IVAR_ ## B, v); \ } ISA_ACCESSOR(port, PORT, int) ISA_ACCESSOR(portsize, PORTSIZE, int) ISA_ACCESSOR(flags, FLAGS, int) ISA_ACCESSOR(irq, IRQ, int) +ISA_ACCESSOR(drq, DRQ, int) +ISA_ACCESSOR(maddr, MADDR, int) +ISA_ACCESSOR(msize, MSIZE, int) Index: head/sys/isa/psm.c =================================================================== --- head/sys/isa/psm.c (revision 45719) +++ head/sys/isa/psm.c (revision 45720) @@ -1,2231 +1,2232 @@ /*- * Copyright (c) 1992, 1993 Erik Forsberg. * Copyright (c) 1996, 1997 Kazutaka YOKOTA. * 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. * * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN * NO EVENT SHALL I 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. * - * $Id: psm.c,v 1.2 1998/11/15 18:25:17 dfr Exp $ + * $Id: psm.c,v 1.3 1999/01/23 16:53:28 dfr Exp $ */ /* * Ported to 386bsd Oct 17, 1992 * Sandi Donno, Computer Science, University of Cape Town, South Africa * Please send bug reports to sandi@cs.uct.ac.za * * Thanks are also due to Rick Macklem, rick@snowhite.cis.uoguelph.ca - * although I was only partially successful in getting the alpha release * of his "driver for the Logitech and ATI Inport Bus mice for use with * 386bsd and the X386 port" to work with my Microsoft mouse, I nevertheless * found his code to be an invaluable reference when porting this driver * to 386bsd. * * Further modifications for latest 386BSD+patchkit and port to NetBSD, * Andrew Herbert - 8 June 1993 * * Cloned from the Microsoft Bus Mouse driver, also by Erik Forsberg, by * Andrew Herbert - 12 June 1993 * * Modified for PS/2 mouse by Charles Hannum * - 13 June 1993 * * Modified for PS/2 AUX mouse by Shoji Yuen * - 24 October 1993 * * Hardware access routines and probe logic rewritten by * Kazutaka Yokota * - 3, 14, 22 October 1996. * - 12 November 1996. IOCTLs and rearranging `psmread', `psmioctl'... * - 14, 30 November 1996. Uses `kbdio.c'. * - 13 December 1996. Uses queuing version of `kbdio.c'. * - January/February 1997. Tweaked probe logic for * HiNote UltraII/Latitude/Armada laptops. * - 30 July 1997. Added APM support. * - 5 March 1997. Defined driver configuration flags (PSM_CONFIG_XXX). * Improved sync check logic. * Vendor specific support routines. */ #include "psm.h" #include "opt_devfs.h" #include "opt_psm.h" #if NPSM > 0 #include #include #include #include #include #include #include #include #include +#include #include #ifdef DEVFS #include #endif #include #include #include #include #include #include #include #include #include /* * Driver specific options: the following options may be set by * `options' statements in the kernel configuration file. */ /* debugging */ #ifndef PSM_DEBUG #define PSM_DEBUG 0 /* logging: 0: none, 1: brief, 2: verbose */ #endif /* features */ /* #define PSM_HOOKAPM hook the APM resume event */ /* #define PSM_RESETAFTERSUSPEND reset the device at the resume event */ #if NAPM <= 0 #undef PSM_HOOKAPM #endif /* NAPM */ #ifndef PSM_HOOKAPM #undef PSM_RESETAFTERSUSPEND #endif /* PSM_HOOKAPM */ /* end of driver specific options */ /* input queue */ #define PSM_BUFSIZE 960 #define PSM_SMALLBUFSIZE 240 /* operation levels */ #define PSM_LEVEL_BASE 0 #define PSM_LEVEL_STANDARD 1 #define PSM_LEVEL_NATIVE 2 #define PSM_LEVEL_MIN PSM_LEVEL_BASE #define PSM_LEVEL_MAX PSM_LEVEL_NATIVE /* some macros */ #define PSM_UNIT(dev) (minor(dev) >> 1) #define PSM_NBLOCKIO(dev) (minor(dev) & 1) #define PSM_MKMINOR(unit,block) (((unit) << 1) | ((block) ? 0:1)) #ifndef max #define max(x,y) ((x) > (y) ? (x) : (y)) #endif #ifndef min #define min(x,y) ((x) < (y) ? (x) : (y)) #endif /* ring buffer */ typedef struct ringbuf { int count; /* # of valid elements in the buffer */ int head; /* head pointer */ int tail; /* tail poiner */ unsigned char buf[PSM_BUFSIZE]; } ringbuf_t; /* driver control block */ struct psm_softc { /* Driver status information */ struct selinfo rsel; /* Process selecting for Input */ unsigned char state; /* Mouse driver state */ int config; /* driver configuration flags */ int flags; /* other flags */ KBDC kbdc; /* handle to access the keyboard controller */ int addr; /* I/O port address */ mousehw_t hw; /* hardware information */ mousemode_t mode; /* operation mode */ mousemode_t dflt_mode; /* default operation mode */ mousestatus_t status; /* accumulated mouse movement */ ringbuf_t queue; /* mouse status queue */ unsigned char ipacket[16]; /* interim input buffer */ int inputbytes; /* # of bytes in the input buffer */ int button; /* the latest button state */ #ifdef DEVFS void *devfs_token; void *b_devfs_token; #endif #ifdef PSM_HOOKAPM struct apmhook resumehook; #endif }; devclass_t psm_devclass; #define PSM_SOFTC(unit) ((struct psm_softc*)devclass_get_softc(psm_devclass, unit)) /* driver state flags (state) */ #define PSM_VALID 0x80 #define PSM_OPEN 1 /* Device is open */ #define PSM_ASLP 2 /* Waiting for mouse data */ /* driver configuration flags (config) */ #define PSM_CONFIG_RESOLUTION 0x000f /* resolution */ #define PSM_CONFIG_ACCEL 0x00f0 /* acceleration factor */ #define PSM_CONFIG_NOCHECKSYNC 0x0100 /* disable sync. test */ #define PSM_CONFIG_FLAGS (PSM_CONFIG_RESOLUTION \ | PSM_CONFIG_ACCEL \ | PSM_CONFIG_NOCHECKSYNC) /* other flags (flags) */ /* * Pass mouse data packet to the user land program `as is', even if * the mouse has vendor-specific enhanced features and uses non-standard * packet format. Otherwise manipulate the mouse data packet so that * it can be recognized by the programs which can only understand * the standard packet format. */ #define PSM_FLAGS_NATIVEMODE 0x0200 /* for backward compatibility */ #define OLD_MOUSE_GETHWINFO _IOR('M', 1, old_mousehw_t) #define OLD_MOUSE_GETMODE _IOR('M', 2, old_mousemode_t) #define OLD_MOUSE_SETMODE _IOW('M', 3, old_mousemode_t) typedef struct old_mousehw { int buttons; int iftype; int type; int hwid; } old_mousehw_t; typedef struct old_mousemode { int protocol; int rate; int resolution; int accelfactor; } old_mousemode_t; /* packet formatting function */ typedef int packetfunc_t __P((struct psm_softc *, unsigned char *, int *, int, mousestatus_t *)); /* function prototypes */ static int psmprobe __P((device_t)); static int psmattach __P((device_t)); #ifdef PSM_HOOKAPM static int psmresume __P((void *)); #endif static d_open_t psmopen; static d_close_t psmclose; static d_read_t psmread; static d_ioctl_t psmioctl; static d_poll_t psmpoll; static int enable_aux_dev __P((KBDC)); static int disable_aux_dev __P((KBDC)); static int get_mouse_status __P((KBDC, int *, int, int)); static int get_aux_id __P((KBDC)); static int set_mouse_sampling_rate __P((KBDC, int)); static int set_mouse_scaling __P((KBDC, int)); static int set_mouse_resolution __P((KBDC, int)); static int set_mouse_mode __P((KBDC)); static int get_mouse_buttons __P((KBDC)); static int is_a_mouse __P((int)); static void recover_from_error __P((KBDC)); static int restore_controller __P((KBDC, int)); static int reinitialize __P((int, mousemode_t *)); static int doopen __P((int, int)); static char *model_name(int); static void psmintr(void*); /* vendor specific features */ typedef int probefunc_t __P((struct psm_softc *)); static int mouse_id_proc1 __P((KBDC, int, int, int *)); static probefunc_t enable_groller; static probefunc_t enable_gmouse; static probefunc_t enable_aglide; static probefunc_t enable_kmouse; static probefunc_t enable_msintelli; static probefunc_t enable_mmanplus; static int tame_mouse __P((struct psm_softc *, mousestatus_t *, unsigned char *)); static struct { int model; unsigned char syncmask; int packetsize; probefunc_t *probefunc; } vendortype[] = { { MOUSE_MODEL_NET, /* Genius NetMouse */ 0xc8, MOUSE_INTELLI_PACKETSIZE, enable_gmouse, }, { MOUSE_MODEL_NETSCROLL, /* Genius NetScroll */ 0xc8, 6, enable_groller, }, { MOUSE_MODEL_GLIDEPOINT, /* ALPS GlidePoint */ 0xc0, MOUSE_PS2_PACKETSIZE, enable_aglide, }, { MOUSE_MODEL_MOUSEMANPLUS, /* Logitech MouseMan+ */ 0x08, MOUSE_PS2_PACKETSIZE, enable_mmanplus, }, { MOUSE_MODEL_THINK, /* Kensignton ThinkingMouse */ 0x80, MOUSE_PS2_PACKETSIZE, enable_kmouse, }, { MOUSE_MODEL_INTELLI, /* Microsoft IntelliMouse */ 0xc8, MOUSE_INTELLI_PACKETSIZE, enable_msintelli, }, { MOUSE_MODEL_GENERIC, 0xc0, MOUSE_PS2_PACKETSIZE, NULL, }, }; /* device driver declarateion */ static device_method_t psm_methods[] = { /* Device interface */ DEVMETHOD(device_probe, psmprobe), DEVMETHOD(device_attach, psmattach), { 0, 0 } }; static driver_t psm_driver = { "psm", psm_methods, DRIVER_TYPE_TTY, sizeof(struct psm_softc), }; #define CDEV_MAJOR 21 static struct cdevsw psm_cdevsw = { psmopen, psmclose, psmread, nowrite, /* 21 */ psmioctl, nostop, nullreset, nodevtotty, psmpoll, nommap, NULL, "psm", NULL, -1 }; /* debug message level */ static int verbose = PSM_DEBUG; /* device I/O routines */ static int enable_aux_dev(KBDC kbdc) { int res; res = send_aux_command(kbdc, PSMC_ENABLE_DEV); if (verbose >= 2) log(LOG_DEBUG, "psm: ENABLE_DEV return code:%04x\n", res); return (res == PSM_ACK); } static int disable_aux_dev(KBDC kbdc) { int res; res = send_aux_command(kbdc, PSMC_DISABLE_DEV); if (verbose >= 2) log(LOG_DEBUG, "psm: DISABLE_DEV return code:%04x\n", res); return (res == PSM_ACK); } static int get_mouse_status(KBDC kbdc, int *status, int flag, int len) { int cmd; int res; int i; switch (flag) { case 0: default: cmd = PSMC_SEND_DEV_STATUS; break; case 1: cmd = PSMC_SEND_DEV_DATA; break; } empty_aux_buffer(kbdc, 5); res = send_aux_command(kbdc, cmd); if (verbose >= 2) log(LOG_DEBUG, "psm: SEND_AUX_DEV_%s return code:%04x\n", (flag == 1) ? "DATA" : "STATUS", res); if (res != PSM_ACK) return 0; for (i = 0; i < len; ++i) { status[i] = read_aux_data(kbdc); if (status[i] < 0) break; } if (verbose) { log(LOG_DEBUG, "psm: %s %02x %02x %02x\n", (flag == 1) ? "data" : "status", status[0], status[1], status[2]); } return i; } static int get_aux_id(KBDC kbdc) { int res; int id; empty_aux_buffer(kbdc, 5); res = send_aux_command(kbdc, PSMC_SEND_DEV_ID); if (verbose >= 2) log(LOG_DEBUG, "psm: SEND_DEV_ID return code:%04x\n", res); if (res != PSM_ACK) return (-1); /* 10ms delay */ DELAY(10000); id = read_aux_data(kbdc); if (verbose >= 2) log(LOG_DEBUG, "psm: device ID: %04x\n", id); return id; } static int set_mouse_sampling_rate(KBDC kbdc, int rate) { int res; res = send_aux_command_and_data(kbdc, PSMC_SET_SAMPLING_RATE, rate); if (verbose >= 2) log(LOG_DEBUG, "psm: SET_SAMPLING_RATE (%d) %04x\n", rate, res); return ((res == PSM_ACK) ? rate : -1); } static int set_mouse_scaling(KBDC kbdc, int scale) { int res; switch (scale) { case 1: default: scale = PSMC_SET_SCALING11; break; case 2: scale = PSMC_SET_SCALING21; break; } res = send_aux_command(kbdc, scale); if (verbose >= 2) log(LOG_DEBUG, "psm: SET_SCALING%s return code:%04x\n", (scale == PSMC_SET_SCALING21) ? "21" : "11", res); return (res == PSM_ACK); } /* `val' must be 0 through PSMD_MAX_RESOLUTION */ static int set_mouse_resolution(KBDC kbdc, int val) { int res; res = send_aux_command_and_data(kbdc, PSMC_SET_RESOLUTION, val); if (verbose >= 2) log(LOG_DEBUG, "psm: SET_RESOLUTION (%d) %04x\n", val, res); return ((res == PSM_ACK) ? val : -1); } /* * NOTE: once `set_mouse_mode()' is called, the mouse device must be * re-enabled by calling `enable_aux_dev()' */ static int set_mouse_mode(KBDC kbdc) { int res; res = send_aux_command(kbdc, PSMC_SET_STREAM_MODE); if (verbose >= 2) log(LOG_DEBUG, "psm: SET_STREAM_MODE return code:%04x\n", res); return (res == PSM_ACK); } static int get_mouse_buttons(KBDC kbdc) { int c = 2; /* assume two buttons by default */ int status[3]; /* * NOTE: a special sequence to obtain Logitech Mouse specific * information: set resolution to 25 ppi, set scaling to 1:1, set * scaling to 1:1, set scaling to 1:1. Then the second byte of the * mouse status bytes is the number of available buttons. * Some manufactures also support this sequence. */ if (set_mouse_resolution(kbdc, PSMD_RES_LOW) != PSMD_RES_LOW) return c; if (set_mouse_scaling(kbdc, 1) && set_mouse_scaling(kbdc, 1) && set_mouse_scaling(kbdc, 1) && (get_mouse_status(kbdc, status, 0, 3) >= 3)) { if (status[1] != 0) return status[1]; } return c; } /* misc subroutines */ /* * Someday, I will get the complete list of valid pointing devices and * their IDs... XXX */ static int is_a_mouse(int id) { #if 0 static int valid_ids[] = { PSM_MOUSE_ID, /* mouse */ PSM_BALLPOINT_ID, /* ballpoint device */ PSM_INTELLI_ID, /* Intellimouse */ -1 /* end of table */ }; int i; for (i = 0; valid_ids[i] >= 0; ++i) if (valid_ids[i] == id) return TRUE; return FALSE; #else return TRUE; #endif } static char * model_name(int model) { static struct { int model_code; char *model_name; } models[] = { { MOUSE_MODEL_NETSCROLL, "NetScroll Mouse" }, { MOUSE_MODEL_NET, "NetMouse" }, { MOUSE_MODEL_GLIDEPOINT, "GlidePoint" }, { MOUSE_MODEL_THINK, "ThinkingMouse" }, { MOUSE_MODEL_INTELLI, "IntelliMouse" }, { MOUSE_MODEL_MOUSEMANPLUS, "MouseMan+" }, { MOUSE_MODEL_GENERIC, "Generic PS/2 mouse" }, { MOUSE_MODEL_UNKNOWN, NULL }, }; int i; for (i = 0; models[i].model_code != MOUSE_MODEL_UNKNOWN; ++i) { if (models[i].model_code == model) return models[i].model_name; } return "Unknown"; } static void recover_from_error(KBDC kbdc) { /* discard anything left in the output buffer */ empty_both_buffers(kbdc, 10); #if 0 /* * NOTE: KBDC_RESET_KBD may not restore the communication between the * keyboard and the controller. */ reset_kbd(kbdc); #else /* * NOTE: somehow diagnostic and keyboard port test commands bring the * keyboard back. */ if (!test_controller(kbdc)) log(LOG_ERR, "psm: keyboard controller failed.\n"); /* if there isn't a keyboard in the system, the following error is OK */ if (test_kbd_port(kbdc) != 0) { if (verbose) log(LOG_ERR, "psm: keyboard port failed.\n"); } #endif } static int restore_controller(KBDC kbdc, int command_byte) { empty_both_buffers(kbdc, 10); if (!set_controller_command_byte(kbdc, 0xff, command_byte)) { log(LOG_ERR, "psm: failed to restore the keyboard controller " "command byte.\n"); return FALSE; } else { return TRUE; } } /* * Re-initialize the aux port and device. The aux port must be enabled * and its interrupt must be disabled before calling this routine. * The aux device will be disabled before returning. * The keyboard controller must be locked via `kbdc_lock()' before * calling this routine. */ static int reinitialize(int unit, mousemode_t *mode) { struct psm_softc *sc = PSM_SOFTC(unit); KBDC kbdc = sc->kbdc; int stat[3]; int i; switch((i = test_aux_port(kbdc))) { case 1: /* ignore this error */ case PSM_ACK: if (verbose) log(LOG_DEBUG, "psm%d: strange result for test aux port (%d).\n", unit, i); /* fall though */ case 0: /* no error */ break; case -1: /* time out */ default: /* error */ recover_from_error(kbdc); log(LOG_ERR, "psm%d: the aux port is not functioning (%d).\n", unit, i); return FALSE; } /* * NOTE: some controllers appears to hang the `keyboard' when * the aux port doesn't exist and `PSMC_RESET_DEV' is issued. */ if (!reset_aux_dev(kbdc)) { recover_from_error(kbdc); log(LOG_ERR, "psm%d: failed to reset the aux device.\n", unit); return FALSE; } /* * both the aux port and the aux device is functioning, see * if the device can be enabled. */ if (!enable_aux_dev(kbdc) || !disable_aux_dev(kbdc)) { log(LOG_ERR, "psm%d: failed to enable the aux device.\n", unit); return FALSE; } empty_both_buffers(kbdc, 10); /* remove stray data if any */ /* FIXME: hardware ID, mouse buttons? */ /* other parameters */ for (i = 0; vendortype[i].probefunc != NULL; ++i) { if ((*vendortype[i].probefunc)(sc)) { if (verbose >= 2) log(LOG_ERR, "psm%d: found %s\n", unit, model_name(vendortype[i].model)); break; } } sc->hw.model = vendortype[i].model; sc->mode.packetsize = vendortype[i].packetsize; /* set mouse parameters */ if (mode != (mousemode_t *)NULL) { if (mode->rate > 0) mode->rate = set_mouse_sampling_rate(kbdc, mode->rate); if (mode->resolution >= 0) mode->resolution = set_mouse_resolution(kbdc, mode->resolution); set_mouse_scaling(kbdc, 1); set_mouse_mode(kbdc); } /* request a data packet and extract sync. bits */ if (get_mouse_status(kbdc, stat, 1, 3) < 3) { log(LOG_DEBUG, "psm%d: failed to get data (reinitialize).\n", unit); sc->mode.syncmask[0] = 0; } else { sc->mode.syncmask[1] = stat[0] & sc->mode.syncmask[0]; /* syncbits */ /* the NetScroll Mouse will send three more bytes... Ignore them */ empty_aux_buffer(kbdc, 5); } /* just check the status of the mouse */ if (get_mouse_status(kbdc, stat, 0, 3) < 3) log(LOG_DEBUG, "psm%d: failed to get status (reinitialize).\n", unit); return TRUE; } static int doopen(int unit, int command_byte) { struct psm_softc *sc = PSM_SOFTC(unit); int stat[3]; /* enable the mouse device */ if (!enable_aux_dev(sc->kbdc)) { /* MOUSE ERROR: failed to enable the mouse because: * 1) the mouse is faulty, * 2) the mouse has been removed(!?) * In the latter case, the keyboard may have hung, and need * recovery procedure... */ recover_from_error(sc->kbdc); #if 0 /* FIXME: we could reset the mouse here and try to enable * it again. But it will take long time and it's not a good * idea to disable the keyboard that long... */ if (!reinitialize(unit, &sc->mode) || !enable_aux_dev(sc->kbdc)) { recover_from_error(sc->kbdc); #else { #endif restore_controller(sc->kbdc, command_byte); /* mark this device is no longer available */ sc->state &= ~PSM_VALID; log(LOG_ERR, "psm%d: failed to enable the device (doopen).\n", unit); return (EIO); } } if (get_mouse_status(sc->kbdc, stat, 0, 3) < 3) log(LOG_DEBUG, "psm%d: failed to get status (doopen).\n", unit); /* enable the aux port and interrupt */ if (!set_controller_command_byte(sc->kbdc, kbdc_get_device_mask(sc->kbdc), (command_byte & KBD_KBD_CONTROL_BITS) | KBD_ENABLE_AUX_PORT | KBD_ENABLE_AUX_INT)) { /* CONTROLLER ERROR */ disable_aux_dev(sc->kbdc); restore_controller(sc->kbdc, command_byte); log(LOG_ERR, "psm%d: failed to enable the aux interrupt (doopen).\n", unit); return (EIO); } return (0); } /* psm driver entry points */ #define endprobe(v) { if (bootverbose) \ --verbose; \ kbdc_set_device_mask(sc->kbdc, mask); \ kbdc_lock(sc->kbdc, FALSE); \ free(sc, M_DEVBUF); \ return (v); \ } static int psmprobe(device_t dev) { int unit = device_get_unit(dev); struct psm_softc *sc = device_get_softc(dev); - u_long port; - u_long flags; + uintptr_t port; + uintptr_t flags; int stat[3]; int command_byte; int mask; int i; #if 0 kbdc_debug(TRUE); #endif BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_PORT, &port); BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_FLAGS, &flags); sc->addr = port; sc->kbdc = kbdc_open(sc->addr); sc->config = flags & PSM_CONFIG_FLAGS; sc->flags = 0; if (bootverbose) ++verbose; device_set_desc(dev, "PS/2 Mouse"); if (!kbdc_lock(sc->kbdc, TRUE)) { printf("psm%d: unable to lock the controller.\n", unit); if (bootverbose) --verbose; return (ENXIO); } /* * NOTE: two bits in the command byte controls the operation of the * aux port (mouse port): the aux port disable bit (bit 5) and the aux * port interrupt (IRQ 12) enable bit (bit 2). */ /* discard anything left after the keyboard initialization */ empty_both_buffers(sc->kbdc, 10); /* save the current command byte; it will be used later */ mask = kbdc_get_device_mask(sc->kbdc) & ~KBD_AUX_CONTROL_BITS; command_byte = get_controller_command_byte(sc->kbdc); if (verbose) printf("psm%d: current command byte:%04x\n", unit, command_byte); if (command_byte == -1) { /* CONTROLLER ERROR */ printf("psm%d: unable to get the current command byte value.\n", unit); endprobe(ENXIO); } /* * disable the keyboard port while probing the aux port, which must be * enabled during this routine */ if (!set_controller_command_byte(sc->kbdc, KBD_KBD_CONTROL_BITS | KBD_AUX_CONTROL_BITS, KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT | KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { /* * this is CONTROLLER ERROR; I don't know how to recover * from this error... */ restore_controller(sc->kbdc, command_byte); printf("psm%d: unable to set the command byte.\n", unit); endprobe(ENXIO); } /* * NOTE: `test_aux_port()' is designed to return with zero if the aux * port exists and is functioning. However, some controllers appears * to respond with zero even when the aux port doesn't exist. (It may * be that this is only the case when the controller DOES have the aux * port but the port is not wired on the motherboard.) The keyboard * controllers without the port, such as the original AT, are * supporsed to return with an error code or simply time out. In any * case, we have to continue probing the port even when the controller * passes this test. * * XXX: some controllers erroneously return the error code 1 when * it has the perfectly functional aux port. We have to ignore this * error code. Even if the controller HAS error with the aux port, * it will be detected later... * XXX: another incompatible controller returns PSM_ACK (0xfa)... */ switch ((i = test_aux_port(sc->kbdc))) { case 1: /* ignore this error */ case PSM_ACK: if (verbose) printf("psm%d: strange result for test aux port (%d).\n", unit, i); /* fall though */ case 0: /* no error */ break; case -1: /* time out */ default: /* error */ recover_from_error(sc->kbdc); restore_controller(sc->kbdc, command_byte); if (verbose) printf("psm%d: the aux port is not functioning (%d).\n", unit, i); endprobe(ENXIO); } /* * NOTE: some controllers appears to hang the `keyboard' when the aux * port doesn't exist and `PSMC_RESET_DEV' is issued. */ if (!reset_aux_dev(sc->kbdc)) { recover_from_error(sc->kbdc); restore_controller(sc->kbdc, command_byte); if (verbose) printf("psm%d: failed to reset the aux device.\n", unit); endprobe(ENXIO); } /* * both the aux port and the aux device is functioning, see if the * device can be enabled. NOTE: when enabled, the device will start * sending data; we shall immediately disable the device once we know * the device can be enabled. */ if (!enable_aux_dev(sc->kbdc) || !disable_aux_dev(sc->kbdc)) { /* MOUSE ERROR */ restore_controller(sc->kbdc, command_byte); if (verbose) printf("psm%d: failed to enable the aux device.\n", unit); endprobe(ENXIO); } /* save the default values after reset */ if (get_mouse_status(sc->kbdc, stat, 0, 3) >= 3) { sc->dflt_mode.rate = sc->mode.rate = stat[2]; sc->dflt_mode.resolution = sc->mode.resolution = stat[1]; } else { sc->dflt_mode.rate = sc->mode.rate = -1; sc->dflt_mode.resolution = sc->mode.resolution = -1; } /* hardware information */ sc->hw.iftype = MOUSE_IF_PS2; /* verify the device is a mouse */ sc->hw.hwid = get_aux_id(sc->kbdc); if (!is_a_mouse(sc->hw.hwid)) { restore_controller(sc->kbdc, command_byte); if (verbose) printf("psm%d: unknown device type (%d).\n", unit, sc->hw.hwid); endprobe(ENXIO); } switch (sc->hw.hwid) { case PSM_BALLPOINT_ID: sc->hw.type = MOUSE_TRACKBALL; break; case PSM_MOUSE_ID: case PSM_INTELLI_ID: sc->hw.type = MOUSE_MOUSE; break; default: sc->hw.type = MOUSE_UNKNOWN; break; } /* # of buttons */ sc->hw.buttons = get_mouse_buttons(sc->kbdc); /* other parameters */ for (i = 0; vendortype[i].probefunc != NULL; ++i) { if ((*vendortype[i].probefunc)(sc)) { if (verbose >= 2) printf("psm%d: found %s\n", unit, model_name(vendortype[i].model)); break; } } sc->hw.model = vendortype[i].model; sc->dflt_mode.level = PSM_LEVEL_BASE; sc->dflt_mode.packetsize = MOUSE_PS2_PACKETSIZE; sc->dflt_mode.accelfactor = (sc->config & PSM_CONFIG_ACCEL) >> 4; if (sc->config & PSM_CONFIG_NOCHECKSYNC) sc->dflt_mode.syncmask[0] = 0; else sc->dflt_mode.syncmask[0] = vendortype[i].syncmask; sc->dflt_mode.syncmask[1] = 0; /* syncbits */ sc->mode = sc->dflt_mode; sc->mode.packetsize = vendortype[i].packetsize; /* set mouse parameters */ i = send_aux_command(sc->kbdc, PSMC_SET_DEFAULTS); if (verbose >= 2) printf("psm%d: SET_DEFAULTS return code:%04x\n", unit, i); if (sc->config & PSM_CONFIG_RESOLUTION) { sc->mode.resolution = set_mouse_resolution(sc->kbdc, (sc->config & PSM_CONFIG_RESOLUTION) - 1); } /* request a data packet and extract sync. bits */ if (get_mouse_status(sc->kbdc, stat, 1, 3) < 3) { printf("psm%d: failed to get data.\n", unit); sc->mode.syncmask[0] = 0; } else { sc->mode.syncmask[1] = stat[0] & sc->mode.syncmask[0]; /* syncbits */ /* the NetScroll Mouse will send three more bytes... Ignore them */ empty_aux_buffer(sc->kbdc, 5); } /* just check the status of the mouse */ /* * NOTE: XXX there are some arcane controller/mouse combinations out * there, which hung the controller unless there is data transmission * after ACK from the mouse. */ if (get_mouse_status(sc->kbdc, stat, 0, 3) < 3) { printf("psm%d: failed to get status.\n", unit); } else { /* * When in its native mode, some mice operate with different * default parameters than in the PS/2 compatible mode. */ sc->dflt_mode.rate = sc->mode.rate = stat[2]; sc->dflt_mode.resolution = sc->mode.resolution = stat[1]; } /* disable the aux port for now... */ if (!set_controller_command_byte(sc->kbdc, KBD_KBD_CONTROL_BITS | KBD_AUX_CONTROL_BITS, (command_byte & KBD_KBD_CONTROL_BITS) | KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { /* * this is CONTROLLER ERROR; I don't know the proper way to * recover from this error... */ restore_controller(sc->kbdc, command_byte); printf("psm%d: unable to set the command byte.\n", unit); endprobe(ENXIO); } /* done */ kbdc_set_device_mask(sc->kbdc, mask | KBD_AUX_CONTROL_BITS); kbdc_lock(sc->kbdc, FALSE); return (0); } static int psmattach(device_t dev) { int unit = device_get_unit(dev); struct psm_softc *sc = device_get_softc(dev); void *ih; struct resource *res; - u_long irq; + uintptr_t irq; int zero = 0; if (sc == NULL) /* shouldn't happen */ return (ENXIO); /* Setup initial state */ sc->state = PSM_VALID; /* Done */ #ifdef DEVFS sc->devfs_token = devfs_add_devswf(&psm_cdevsw, PSM_MKMINOR(unit, FALSE), DV_CHR, 0, 0, 0666, "psm%d", unit); sc->b_devfs_token = devfs_add_devswf(&psm_cdevsw, PSM_MKMINOR(unit, TRUE), DV_CHR, 0, 0, 0666, "bpsm%d", unit); #endif /* DEVFS */ #ifdef PSM_HOOKAPM sc->resumehook.ah_name = "PS/2 mouse"; sc->resumehook.ah_fun = psmresume; sc->resumehook.ah_arg = (void *)unit; sc->resumehook.ah_order = APM_MID_ORDER; apm_hook_establish(APM_HOOK_RESUME , &sc->resumehook); if (verbose) printf("psm%d: APM hooks installed.\n", unit); #endif /* PSM_HOOKAPM */ if (!verbose) { printf("psm%d: model %s, device ID %d\n", unit, model_name(sc->hw.model), sc->hw.hwid); } else { printf("psm%d: model %s, device ID %d, %d buttons\n", unit, model_name(sc->hw.model), sc->hw.hwid, sc->hw.buttons); printf("psm%d: config:%08x, flags:%08x, packet size:%d\n", unit, sc->config, sc->flags, sc->mode.packetsize); printf("psm%d: syncmask:%02x, syncbits:%02x\n", unit, sc->mode.syncmask[0], sc->mode.syncmask[1]); } if (bootverbose) --verbose; BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_IRQ, &irq); res = bus_alloc_resource(dev, SYS_RES_IRQ, &zero, irq, irq, 1, RF_SHAREABLE | RF_ACTIVE); BUS_SETUP_INTR(device_get_parent(dev), dev, res, psmintr, sc, &ih); return (0); } static int psmopen(dev_t dev, int flag, int fmt, struct proc *p) { int unit = PSM_UNIT(dev); struct psm_softc *sc; int command_byte; int err; int s; /* Validate unit number */ if (unit >= NPSM) return (ENXIO); /* Get device data */ sc = PSM_SOFTC(unit); if ((sc == NULL) || (sc->state & PSM_VALID) == 0) /* the device is no longer valid/functioning */ return (ENXIO); /* Disallow multiple opens */ if (sc->state & PSM_OPEN) return (EBUSY); device_busy(devclass_get_device(psm_devclass, unit)); /* Initialize state */ sc->rsel.si_flags = 0; sc->rsel.si_pid = 0; sc->mode.level = sc->dflt_mode.level; sc->mode.protocol = sc->dflt_mode.protocol; /* flush the event queue */ sc->queue.count = 0; sc->queue.head = 0; sc->queue.tail = 0; sc->status.flags = 0; sc->status.button = 0; sc->status.obutton = 0; sc->status.dx = 0; sc->status.dy = 0; sc->status.dz = 0; sc->button = 0; /* empty input buffer */ bzero(sc->ipacket, sizeof(sc->ipacket)); sc->inputbytes = 0; /* don't let timeout routines in the keyboard driver to poll the kbdc */ if (!kbdc_lock(sc->kbdc, TRUE)) return (EIO); /* save the current controller command byte */ s = spltty(); command_byte = get_controller_command_byte(sc->kbdc); /* enable the aux port and temporalily disable the keyboard */ if ((command_byte == -1) || !set_controller_command_byte(sc->kbdc, kbdc_get_device_mask(sc->kbdc), KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT | KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { /* CONTROLLER ERROR; do you know how to get out of this? */ kbdc_lock(sc->kbdc, FALSE); splx(s); log(LOG_ERR, "psm%d: unable to set the command byte (psmopen).\n", unit); return (EIO); } /* * Now that the keyboard controller is told not to generate * the keyboard and mouse interrupts, call `splx()' to allow * the other tty interrupts. The clock interrupt may also occur, * but timeout routines will be blocked by the poll flag set * via `kbdc_lock()' */ splx(s); /* enable the mouse device */ err = doopen(unit, command_byte); /* done */ if (err == 0) sc->state |= PSM_OPEN; kbdc_lock(sc->kbdc, FALSE); return (err); } static int psmclose(dev_t dev, int flag, int fmt, struct proc *p) { int unit = PSM_UNIT(dev); struct psm_softc *sc = PSM_SOFTC(unit); int stat[3]; int command_byte; int s; /* don't let timeout routines in the keyboard driver to poll the kbdc */ if (!kbdc_lock(sc->kbdc, TRUE)) return (EIO); /* save the current controller command byte */ s = spltty(); command_byte = get_controller_command_byte(sc->kbdc); if (command_byte == -1) { kbdc_lock(sc->kbdc, FALSE); splx(s); return (EIO); } /* disable the aux interrupt and temporalily disable the keyboard */ if (!set_controller_command_byte(sc->kbdc, kbdc_get_device_mask(sc->kbdc), KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT | KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { log(LOG_ERR, "psm%d: failed to disable the aux int (psmclose).\n", PSM_UNIT(dev)); /* CONTROLLER ERROR; * NOTE: we shall force our way through. Because the only * ill effect we shall see is that we may not be able * to read ACK from the mouse, and it doesn't matter much * so long as the mouse will accept the DISABLE command. */ } splx(s); /* remove anything left in the output buffer */ empty_aux_buffer(sc->kbdc, 10); /* disable the aux device, port and interrupt */ if (sc->state & PSM_VALID) { if (!disable_aux_dev(sc->kbdc)) { /* MOUSE ERROR; * NOTE: we don't return error and continue, pretending * we have successfully disabled the device. It's OK because * the interrupt routine will discard any data from the mouse * hereafter. */ log(LOG_ERR, "psm%d: failed to disable the device (psmclose).\n", PSM_UNIT(dev)); } if (get_mouse_status(sc->kbdc, stat, 0, 3) < 3) log(LOG_DEBUG, "psm%d: failed to get status (psmclose).\n", PSM_UNIT(dev)); } if (!set_controller_command_byte(sc->kbdc, kbdc_get_device_mask(sc->kbdc), (command_byte & KBD_KBD_CONTROL_BITS) | KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { /* CONTROLLER ERROR; * we shall ignore this error; see the above comment. */ log(LOG_ERR, "psm%d: failed to disable the aux port (psmclose).\n", PSM_UNIT(dev)); } /* remove anything left in the output buffer */ empty_aux_buffer(sc->kbdc, 10); /* close is almost always successful */ sc->state &= ~PSM_OPEN; kbdc_lock(sc->kbdc, FALSE); device_unbusy(devclass_get_device(psm_devclass, unit)); return (0); } static int tame_mouse(struct psm_softc *sc, mousestatus_t *status, unsigned char *buf) { static unsigned char butmapps2[8] = { 0, MOUSE_PS2_BUTTON1DOWN, MOUSE_PS2_BUTTON2DOWN, MOUSE_PS2_BUTTON1DOWN | MOUSE_PS2_BUTTON2DOWN, MOUSE_PS2_BUTTON3DOWN, MOUSE_PS2_BUTTON1DOWN | MOUSE_PS2_BUTTON3DOWN, MOUSE_PS2_BUTTON2DOWN | MOUSE_PS2_BUTTON3DOWN, MOUSE_PS2_BUTTON1DOWN | MOUSE_PS2_BUTTON2DOWN | MOUSE_PS2_BUTTON3DOWN, }; static unsigned char butmapmsc[8] = { MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON2UP | MOUSE_MSC_BUTTON3UP, MOUSE_MSC_BUTTON2UP | MOUSE_MSC_BUTTON3UP, MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON3UP, MOUSE_MSC_BUTTON3UP, MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON2UP, MOUSE_MSC_BUTTON2UP, MOUSE_MSC_BUTTON1UP, 0, }; int mapped; int i; if (sc->mode.level == PSM_LEVEL_BASE) { mapped = status->button & ~MOUSE_BUTTON4DOWN; if (status->button & MOUSE_BUTTON4DOWN) mapped |= MOUSE_BUTTON1DOWN; status->button = mapped; buf[0] = MOUSE_PS2_SYNC | butmapps2[mapped & MOUSE_STDBUTTONS]; i = max(min(status->dx, 255), -256); if (i < 0) buf[0] |= MOUSE_PS2_XNEG; buf[1] = i; i = max(min(status->dy, 255), -256); if (i < 0) buf[0] |= MOUSE_PS2_YNEG; buf[2] = i; return MOUSE_PS2_PACKETSIZE; } else if (sc->mode.level == PSM_LEVEL_STANDARD) { buf[0] = MOUSE_MSC_SYNC | butmapmsc[status->button & MOUSE_STDBUTTONS]; i = max(min(status->dx, 255), -256); buf[1] = i >> 1; buf[3] = i - buf[1]; i = max(min(status->dy, 255), -256); buf[2] = i >> 1; buf[4] = i - buf[2]; i = max(min(status->dz, 127), -128); buf[5] = (i >> 1) & 0x7f; buf[6] = (i - (i >> 1)) & 0x7f; buf[7] = (~status->button >> 3) & 0x7f; return MOUSE_SYS_PACKETSIZE; } return sc->inputbytes;; } static int psmread(dev_t dev, struct uio *uio, int flag) { register struct psm_softc *sc = PSM_SOFTC(PSM_UNIT(dev)); unsigned char buf[PSM_SMALLBUFSIZE]; int error = 0; int s; int l; if ((sc->state & PSM_VALID) == 0) return EIO; /* block until mouse activity occured */ s = spltty(); while (sc->queue.count <= 0) { if (PSM_NBLOCKIO(dev)) { splx(s); return EWOULDBLOCK; } sc->state |= PSM_ASLP; error = tsleep((caddr_t) sc, PZERO | PCATCH, "psmrea", 0); sc->state &= ~PSM_ASLP; if (error) { splx(s); return error; } else if ((sc->state & PSM_VALID) == 0) { /* the device disappeared! */ splx(s); return EIO; } } splx(s); /* copy data to the user land */ while ((sc->queue.count > 0) && (uio->uio_resid > 0)) { s = spltty(); l = min(sc->queue.count, uio->uio_resid); if (l > sizeof(buf)) l = sizeof(buf); if (l > sizeof(sc->queue.buf) - sc->queue.head) { bcopy(&sc->queue.buf[sc->queue.head], &buf[0], sizeof(sc->queue.buf) - sc->queue.head); bcopy(&sc->queue.buf[0], &buf[sizeof(sc->queue.buf) - sc->queue.head], l - (sizeof(sc->queue.buf) - sc->queue.head)); } else { bcopy(&sc->queue.buf[sc->queue.head], &buf[0], l); } sc->queue.count -= l; sc->queue.head = (sc->queue.head + l) % sizeof(sc->queue.buf); splx(s); error = uiomove(buf, l, uio); if (error) break; } return error; } static int block_mouse_data(struct psm_softc *sc, int *c) { int s; if (!kbdc_lock(sc->kbdc, TRUE)) return EIO; s = spltty(); *c = get_controller_command_byte(sc->kbdc); if ((*c == -1) || !set_controller_command_byte(sc->kbdc, kbdc_get_device_mask(sc->kbdc), KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT | KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { /* this is CONTROLLER ERROR */ splx(s); kbdc_lock(sc->kbdc, FALSE); return EIO; } /* * The device may be in the middle of status data transmission. * The transmission will be interrupted, thus, incomplete status * data must be discarded. Although the aux interrupt is disabled * at the keyboard controller level, at most one aux interrupt * may have already been pending and a data byte is in the * output buffer; throw it away. Note that the second argument * to `empty_aux_buffer()' is zero, so that the call will just * flush the internal queue. * `psmintr()' will be invoked after `splx()' if an interrupt is * pending; it will see no data and returns immediately. */ empty_aux_buffer(sc->kbdc, 0); /* flush the queue */ read_aux_data_no_wait(sc->kbdc); /* throw away data if any */ sc->inputbytes = 0; splx(s); return 0; } static int unblock_mouse_data(struct psm_softc *sc, int c) { int error = 0; /* * We may have seen a part of status data during `set_mouse_XXX()'. * they have been queued; flush it. */ empty_aux_buffer(sc->kbdc, 0); /* restore ports and interrupt */ if (!set_controller_command_byte(sc->kbdc, kbdc_get_device_mask(sc->kbdc), c & (KBD_KBD_CONTROL_BITS | KBD_AUX_CONTROL_BITS))) { /* CONTROLLER ERROR; this is serious, we may have * been left with the inaccessible keyboard and * the disabled mouse interrupt. */ error = EIO; } kbdc_lock(sc->kbdc, FALSE); return error; } static int psmioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p) { struct psm_softc *sc = PSM_SOFTC(PSM_UNIT(dev)); mousemode_t mode; mousestatus_t status; #if (defined(MOUSE_GETVARS)) mousevar_t *var; #endif mousedata_t *data; int stat[3]; int command_byte; int error = 0; int s; /* Perform IOCTL command */ switch (cmd) { case OLD_MOUSE_GETHWINFO: s = spltty(); ((old_mousehw_t *)addr)->buttons = sc->hw.buttons; ((old_mousehw_t *)addr)->iftype = sc->hw.iftype; ((old_mousehw_t *)addr)->type = sc->hw.type; ((old_mousehw_t *)addr)->hwid = sc->hw.hwid; splx(s); break; case MOUSE_GETHWINFO: s = spltty(); *(mousehw_t *)addr = sc->hw; if (sc->mode.level == PSM_LEVEL_BASE) ((mousehw_t *)addr)->model = MOUSE_MODEL_GENERIC; splx(s); break; case OLD_MOUSE_GETMODE: s = spltty(); switch (sc->mode.level) { case PSM_LEVEL_BASE: ((old_mousemode_t *)addr)->protocol = MOUSE_PROTO_PS2; break; case PSM_LEVEL_STANDARD: ((old_mousemode_t *)addr)->protocol = MOUSE_PROTO_SYSMOUSE; break; case PSM_LEVEL_NATIVE: ((old_mousemode_t *)addr)->protocol = MOUSE_PROTO_PS2; break; } ((old_mousemode_t *)addr)->rate = sc->mode.rate; ((old_mousemode_t *)addr)->resolution = sc->mode.resolution; ((old_mousemode_t *)addr)->accelfactor = sc->mode.accelfactor; splx(s); break; case MOUSE_GETMODE: s = spltty(); *(mousemode_t *)addr = sc->mode; ((mousemode_t *)addr)->resolution = MOUSE_RES_LOW - sc->mode.resolution; switch (sc->mode.level) { case PSM_LEVEL_BASE: ((mousemode_t *)addr)->protocol = MOUSE_PROTO_PS2; ((mousemode_t *)addr)->packetsize = MOUSE_PS2_PACKETSIZE; break; case PSM_LEVEL_STANDARD: ((mousemode_t *)addr)->protocol = MOUSE_PROTO_SYSMOUSE; ((mousemode_t *)addr)->packetsize = MOUSE_SYS_PACKETSIZE; ((mousemode_t *)addr)->syncmask[0] = MOUSE_SYS_SYNCMASK; ((mousemode_t *)addr)->syncmask[1] = MOUSE_SYS_SYNC; break; case PSM_LEVEL_NATIVE: /* FIXME: this isn't quite correct... XXX */ ((mousemode_t *)addr)->protocol = MOUSE_PROTO_PS2; break; } splx(s); break; case OLD_MOUSE_SETMODE: case MOUSE_SETMODE: if (cmd == OLD_MOUSE_SETMODE) { mode.rate = ((old_mousemode_t *)addr)->rate; /* * resolution old I/F new I/F * default 0 0 * low 1 -2 * medium low 2 -3 * medium high 3 -4 * high 4 -5 */ if (((old_mousemode_t *)addr)->resolution > 0) mode.resolution = -((old_mousemode_t *)addr)->resolution - 1; mode.accelfactor = ((old_mousemode_t *)addr)->accelfactor; mode.level = -1; } else { mode = *(mousemode_t *)addr; } /* adjust and validate parameters. */ if (mode.rate > UCHAR_MAX) return EINVAL; if (mode.rate == 0) mode.rate = sc->dflt_mode.rate; else if (mode.rate == -1) /* don't change the current setting */ ; else if (mode.rate < 0) return EINVAL; if (mode.resolution >= UCHAR_MAX) return EINVAL; if (mode.resolution >= 200) mode.resolution = MOUSE_RES_HIGH; else if (mode.resolution >= 100) mode.resolution = MOUSE_RES_MEDIUMHIGH; else if (mode.resolution >= 50) mode.resolution = MOUSE_RES_MEDIUMLOW; else if (mode.resolution > 0) mode.resolution = MOUSE_RES_LOW; if (mode.resolution == MOUSE_RES_DEFAULT) mode.resolution = sc->dflt_mode.resolution; else if (mode.resolution == -1) /* don't change the current setting */ ; else if (mode.resolution < 0) /* MOUSE_RES_LOW/MEDIUM/HIGH */ mode.resolution = MOUSE_RES_LOW - mode.resolution; if (mode.level == -1) /* don't change the current setting */ mode.level = sc->mode.level; else if ((mode.level < PSM_LEVEL_MIN) || (mode.level > PSM_LEVEL_MAX)) return EINVAL; if (mode.accelfactor == -1) /* don't change the current setting */ mode.accelfactor = sc->mode.accelfactor; else if (mode.accelfactor < 0) return EINVAL; /* don't allow anybody to poll the keyboard controller */ error = block_mouse_data(sc, &command_byte); if (error) return error; /* set mouse parameters */ if (mode.rate > 0) mode.rate = set_mouse_sampling_rate(sc->kbdc, mode.rate); if (mode.resolution >= 0) mode.resolution = set_mouse_resolution(sc->kbdc, mode.resolution); set_mouse_scaling(sc->kbdc, 1); get_mouse_status(sc->kbdc, stat, 0, 3); s = spltty(); sc->mode.rate = mode.rate; sc->mode.resolution = mode.resolution; sc->mode.accelfactor = mode.accelfactor; sc->mode.level = mode.level; splx(s); unblock_mouse_data(sc, command_byte); break; case MOUSE_GETLEVEL: *(int *)addr = sc->mode.level; break; case MOUSE_SETLEVEL: if ((*(int *)addr < PSM_LEVEL_MIN) || (*(int *)addr > PSM_LEVEL_MAX)) return EINVAL; sc->mode.level = *(int *)addr; 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; #if (defined(MOUSE_GETVARS)) case MOUSE_GETVARS: var = (mousevar_t *)addr; bzero(var, sizeof(*var)); s = spltty(); var->var[0] = MOUSE_VARS_PS2_SIG; var->var[1] = sc->config; var->var[2] = sc->flags; splx(s); break; case MOUSE_SETVARS: return ENODEV; #endif /* MOUSE_GETVARS */ case MOUSE_READSTATE: case MOUSE_READDATA: data = (mousedata_t *)addr; if (data->len > sizeof(data->buf)/sizeof(data->buf[0])) return EINVAL; error = block_mouse_data(sc, &command_byte); if (error) return error; if ((data->len = get_mouse_status(sc->kbdc, data->buf, (cmd == MOUSE_READDATA) ? 1 : 0, data->len)) <= 0) error = EIO; unblock_mouse_data(sc, command_byte); break; #if (defined(MOUSE_SETRESOLUTION)) case MOUSE_SETRESOLUTION: mode.resolution = *(int *)addr; if (mode.resolution >= UCHAR_MAX) return EINVAL; else if (mode.resolution >= 200) mode.resolution = MOUSE_RES_HIGH; else if (mode.resolution >= 100) mode.resolution = MOUSE_RES_MEDIUMHIGH; else if (mode.resolution >= 50) mode.resolution = MOUSE_RES_MEDIUMLOW; else if (mode.resolution > 0) mode.resolution = MOUSE_RES_LOW; if (mode.resolution == MOUSE_RES_DEFAULT) mode.resolution = sc->dflt_mode.resolution; else if (mode.resolution == -1) mode.resolution = sc->mode.resolution; else if (mode.resolution < 0) /* MOUSE_RES_LOW/MEDIUM/HIGH */ mode.resolution = MOUSE_RES_LOW - mode.resolution; error = block_mouse_data(sc, &command_byte); if (error) return error; sc->mode.resolution = set_mouse_resolution(sc->kbdc, mode.resolution); if (sc->mode.resolution != mode.resolution) error = EIO; unblock_mouse_data(sc, command_byte); break; #endif /* MOUSE_SETRESOLUTION */ #if (defined(MOUSE_SETRATE)) case MOUSE_SETRATE: mode.rate = *(int *)addr; if (mode.rate > UCHAR_MAX) return EINVAL; if (mode.rate == 0) mode.rate = sc->dflt_mode.rate; else if (mode.rate < 0) mode.rate = sc->mode.rate; error = block_mouse_data(sc, &command_byte); if (error) return error; sc->mode.rate = set_mouse_sampling_rate(sc->kbdc, mode.rate); if (sc->mode.rate != mode.rate) error = EIO; unblock_mouse_data(sc, command_byte); break; #endif /* MOUSE_SETRATE */ #if (defined(MOUSE_SETSCALING)) case MOUSE_SETSCALING: if ((*(int *)addr <= 0) || (*(int *)addr > 2)) return EINVAL; error = block_mouse_data(sc, &command_byte); if (error) return error; if (!set_mouse_scaling(sc->kbdc, *(int *)addr)) error = EIO; unblock_mouse_data(sc, command_byte); break; #endif /* MOUSE_SETSCALING */ #if (defined(MOUSE_GETHWID)) case MOUSE_GETHWID: error = block_mouse_data(sc, &command_byte); if (error) return error; sc->hw.hwid = get_aux_id(sc->kbdc); *(int *)addr = sc->hw.hwid; unblock_mouse_data(sc, command_byte); break; #endif /* MOUSE_GETHWID */ default: return ENOTTY; } return error; } static void psmintr(void *arg) { /* * the table to turn PS/2 mouse button bits (MOUSE_PS2_BUTTON?DOWN) * into `mousestatus' button bits (MOUSE_BUTTON?DOWN). */ static int butmap[8] = { 0, MOUSE_BUTTON1DOWN, MOUSE_BUTTON3DOWN, MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN, MOUSE_BUTTON2DOWN, MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN, MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN, MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN }; register struct psm_softc *sc = arg; mousestatus_t ms; int x, y, z; int c; int l; /* read until there is nothing to read */ while((c = read_aux_data_no_wait(sc->kbdc)) != -1) { /* discard the byte if the device is not open */ if ((sc->state & PSM_OPEN) == 0) continue; /* * Check sync bits. We check for overflow bits and the bit 3 * for most mice. True, the code doesn't work if overflow * condition occurs. But we expect it rarely happens... */ if ((sc->inputbytes == 0) && ((c & sc->mode.syncmask[0]) != sc->mode.syncmask[1])) { log(LOG_DEBUG, "psmintr: out of sync (%04x != %04x).\n", c & sc->mode.syncmask[0], sc->mode.syncmask[1]); continue; } sc->ipacket[sc->inputbytes++] = c; if (sc->inputbytes < sc->mode.packetsize) continue; #if 0 log(LOG_DEBUG, "psmintr: %02x %02x %02x %02x %02x %02x\n", sc->ipacket[0], sc->ipacket[1], sc->ipacket[2], sc->ipacket[3], sc->ipacket[4], sc->ipacket[5]); #endif c = sc->ipacket[0]; /* * A kludge for Kensington device! * The MSB of the horizontal count appears to be stored in * a strange place. This kludge doesn't affect other mice * because the bit is the overflow bit which is, in most cases, * expected to be zero when we reach here. XXX */ sc->ipacket[1] |= (c & MOUSE_PS2_XOVERFLOW) ? 0x80 : 0; /* ignore the overflow bits... */ x = (c & MOUSE_PS2_XNEG) ? sc->ipacket[1] - 256 : sc->ipacket[1]; y = (c & MOUSE_PS2_YNEG) ? sc->ipacket[2] - 256 : sc->ipacket[2]; z = 0; ms.obutton = sc->button; /* previous button state */ ms.button = butmap[c & MOUSE_PS2_BUTTONS]; switch (sc->hw.model) { case MOUSE_MODEL_INTELLI: case MOUSE_MODEL_NET: /* wheel data is in the fourth byte */ z = (char)sc->ipacket[3]; break; case MOUSE_MODEL_MOUSEMANPLUS: if ((c & ~MOUSE_PS2_BUTTONS) == 0xc8) { /* the extended data packet encodes button and wheel events */ x = y = 0; z = (sc->ipacket[1] & MOUSE_PS2PLUS_ZNEG) ? (sc->ipacket[2] & 0x0f) - 16 : (sc->ipacket[2] & 0x0f); ms.button |= (sc->ipacket[2] & MOUSE_PS2PLUS_BUTTON4DOWN) ? MOUSE_BUTTON4DOWN : 0; } else { /* preserve button states */ ms.button |= ms.obutton & MOUSE_EXTBUTTONS; } break; case MOUSE_MODEL_GLIDEPOINT: /* `tapping' action */ ms.button |= ((c & MOUSE_PS2_TAP)) ? 0 : MOUSE_BUTTON4DOWN; break; case MOUSE_MODEL_NETSCROLL: /* three addtional bytes encode button and wheel events */ ms.button |= (sc->ipacket[3] & MOUSE_PS2_BUTTON3DOWN) ? MOUSE_BUTTON4DOWN : 0; z = (sc->ipacket[3] & MOUSE_PS2_XNEG) ? sc->ipacket[4] - 256 : sc->ipacket[4]; break; case MOUSE_MODEL_THINK: /* the fourth button state in the first byte */ ms.button |= (c & MOUSE_PS2_TAP) ? MOUSE_BUTTON4DOWN : 0; break; case MOUSE_MODEL_GENERIC: default: break; } /* scale values */ if (sc->mode.accelfactor >= 1) { if (x != 0) { x = x * x / sc->mode.accelfactor; if (x == 0) x = 1; if (c & MOUSE_PS2_XNEG) x = -x; } if (y != 0) { y = y * y / sc->mode.accelfactor; if (y == 0) y = 1; if (c & MOUSE_PS2_YNEG) y = -y; } } ms.dx = x; ms.dy = y; ms.dz = z; ms.flags = ((x || y || z) ? MOUSE_POSCHANGED : 0) | (ms.obutton ^ ms.button); if (sc->mode.level < PSM_LEVEL_NATIVE) sc->inputbytes = tame_mouse(sc, &ms, sc->ipacket); sc->status.flags |= ms.flags; sc->status.dx += ms.dx; sc->status.dy += ms.dy; sc->status.dz += ms.dz; sc->status.button = ms.button; sc->button = ms.button; /* queue data */ if (sc->queue.count + sc->inputbytes < sizeof(sc->queue.buf)) { l = min(sc->inputbytes, sizeof(sc->queue.buf) - sc->queue.tail); bcopy(&sc->ipacket[0], &sc->queue.buf[sc->queue.tail], l); if (sc->inputbytes > l) bcopy(&sc->ipacket[l], &sc->queue.buf[0], sc->inputbytes - l); sc->queue.tail = (sc->queue.tail + sc->inputbytes) % sizeof(sc->queue.buf); sc->queue.count += sc->inputbytes; } sc->inputbytes = 0; if (sc->state & PSM_ASLP) { sc->state &= ~PSM_ASLP; wakeup((caddr_t) sc); } selwakeup(&sc->rsel); } } static int psmpoll(dev_t dev, int events, struct proc *p) { struct psm_softc *sc = PSM_SOFTC(PSM_UNIT(dev)); int s; int revents = 0; /* Return true if a mouse event available */ s = spltty(); if (events & (POLLIN | POLLRDNORM)) if (sc->queue.count > 0) revents |= events & (POLLIN | POLLRDNORM); else selrecord(p, &sc->rsel); splx(s); return (revents); } /* vendor/model specific routines */ static int mouse_id_proc1(KBDC kbdc, int res, int scale, int *status) { if (set_mouse_resolution(kbdc, res) != res) return FALSE; if (set_mouse_scaling(kbdc, scale) && set_mouse_scaling(kbdc, scale) && set_mouse_scaling(kbdc, scale) && (get_mouse_status(kbdc, status, 0, 3) >= 3)) return TRUE; return FALSE; } #if notyet /* Logitech MouseMan Cordless II */ static int enable_lcordless(struct psm_softc *sc) { int status[3]; int ch; if (!mouse_id_proc1(sc->kbdc, PSMD_RES_HIGH, 2, status)) return FALSE; if (status[1] == PSMD_RES_HIGH) return FALSE; ch = (status[0] & 0x07) - 1; /* channel # */ if ((ch <= 0) || (ch > 4)) return FALSE; /* * status[1]: always one? * status[2]: battery status? (0-100) */ return TRUE; } #endif /* notyet */ /* Genius NetScroll Mouse */ static int enable_groller(struct psm_softc *sc) { int status[3]; /* * The special sequence to enable the fourth button and the * roller. Immediately after this sequence check status bytes. * if the mouse is NetScroll, the second and the third bytes are * '3' and 'D'. */ /* * If the mouse is an ordinary PS/2 mouse, the status bytes should * look like the following. * * byte 1 bit 7 always 0 * bit 6 stream mode (0) * bit 5 disabled (0) * bit 4 1:1 scaling (0) * bit 3 always 0 * bit 0-2 button status * byte 2 resolution (PSMD_RES_HIGH) * byte 3 report rate (?) */ if (!mouse_id_proc1(sc->kbdc, PSMD_RES_HIGH, 1, status)) return FALSE; if ((status[1] != '3') || (status[2] != 'D')) return FALSE; /* FIXME!! */ sc->hw.buttons = get_mouse_buttons(sc->kbdc); sc->hw.buttons = 4; return TRUE; } /* Genius NetMouse/NetMouse Pro */ static int enable_gmouse(struct psm_softc *sc) { int status[3]; /* * The special sequence to enable the middle, "rubber" button. * Immediately after this sequence check status bytes. * if the mouse is NetMouse, NetMouse Pro, or ASCII MIE Mouse, * the second and the third bytes are '3' and 'U'. * NOTE: NetMouse reports that it has three buttons although it has * two buttons and a rubber button. NetMouse Pro and MIE Mouse * say they have three buttons too and they do have a button on the * side... */ if (!mouse_id_proc1(sc->kbdc, PSMD_RES_HIGH, 1, status)) return FALSE; if ((status[1] != '3') || (status[2] != 'U')) return FALSE; return TRUE; } /* ALPS GlidePoint */ static int enable_aglide(struct psm_softc *sc) { int status[3]; /* * The special sequence to obtain ALPS GlidePoint specific * information. Immediately after this sequence, status bytes will * contain something interesting. * NOTE: ALPS produces several models of GlidePoint. Some of those * do not respond to this sequence, thus, cannot be detected this way. */ if (!mouse_id_proc1(sc->kbdc, PSMD_RES_LOW, 2, status)) return FALSE; if ((status[0] & 0x10) || (status[1] == PSMD_RES_LOW)) return FALSE; return TRUE; } /* Kensington ThinkingMouse/Trackball */ static int enable_kmouse(struct psm_softc *sc) { static unsigned char rate[] = { 20, 60, 40, 20, 20, 60, 40, 20, 20 }; KBDC kbdc = sc->kbdc; int status[3]; int id1; int id2; int i; id1 = get_aux_id(kbdc); if (set_mouse_sampling_rate(kbdc, 10) != 10) return FALSE; /* * The device is now in the native mode? It returns a different * ID value... */ id2 = get_aux_id(kbdc); if ((id1 == id2) || (id2 != 2)) return FALSE; if (set_mouse_resolution(kbdc, PSMD_RES_LOW) != PSMD_RES_LOW) return FALSE; #if PSM_DEBUG >= 2 /* at this point, resolution is LOW, sampling rate is 10/sec */ if (get_mouse_status(kbdc, status, 0, 3) < 3) return FALSE; #endif /* * The special sequence to enable the third and fourth buttons. * Otherwise they behave like the first and second buttons. */ for (i = 0; i < sizeof(rate)/sizeof(rate[0]); ++i) { if (set_mouse_sampling_rate(kbdc, rate[i]) != rate[i]) return FALSE; } /* * At this point, the device is using default resolution and * sampling rate for the native mode. */ if (get_mouse_status(kbdc, status, 0, 3) < 3) return FALSE; if ((status[1] == PSMD_RES_LOW) || (status[2] == rate[i - 1])) return FALSE; /* the device appears be enabled by this sequence, diable it for now */ disable_aux_dev(kbdc); empty_aux_buffer(kbdc, 5); return TRUE; } /* Logitech MouseMan+/FirstMouse+ */ static int enable_mmanplus(struct psm_softc *sc) { static char res[] = { -1, PSMD_RES_LOW, PSMD_RES_HIGH, PSMD_RES_MEDIUM_HIGH, PSMD_RES_MEDIUM_LOW, -1, PSMD_RES_HIGH, PSMD_RES_MEDIUM_LOW, PSMD_RES_MEDIUM_HIGH, PSMD_RES_HIGH, }; KBDC kbdc = sc->kbdc; int data[3]; int i; /* the special sequence to enable the fourth button and the roller. */ for (i = 0; i < sizeof(res)/sizeof(res[0]); ++i) { if (res[i] < 0) { if (!set_mouse_scaling(kbdc, 1)) return FALSE; } else { if (set_mouse_resolution(kbdc, res[i]) != res[i]) return FALSE; } } if (get_mouse_status(kbdc, data, 1, 3) < 3) return FALSE; /* * MouseMan+ and FirstMouse+ return following data. * * byte 1 0xc8 * byte 2 ?? (MouseMan+:0xc2, FirstMouse+:0xc6) * byte 3 model ID? MouseMan+:0x50, FirstMouse+:0x51 */ if ((data[0] & ~MOUSE_PS2_BUTTONS) != 0xc8) return FALSE; /* * MouseMan+ (or FirstMouse+) is now in its native mode, in which * the wheel and the fourth button events are encoded in the * special data packet. The mouse may be put in the IntelliMouse mode * if it is initialized by the IntelliMouse's method. */ return TRUE; } /* MS IntelliMouse */ static int enable_msintelli(struct psm_softc *sc) { /* * Logitech MouseMan+ and FirstMouse+ will also respond to this * probe routine and act like IntelliMouse. */ static unsigned char rate[] = { 200, 100, 80, }; KBDC kbdc = sc->kbdc; int id; int i; /* the special sequence to enable the third button and the roller. */ for (i = 0; i < sizeof(rate)/sizeof(rate[0]); ++i) { if (set_mouse_sampling_rate(kbdc, rate[i]) != rate[i]) return FALSE; } /* the device will give the genuine ID only after the above sequence */ id = get_aux_id(kbdc); if (id != PSM_INTELLI_ID) return FALSE; sc->hw.hwid = id; sc->hw.buttons = 3; return TRUE; } #ifdef PSM_HOOKAPM static int psmresume(void *dummy) { struct psm_softc *sc = psm_softc[(int)dummy]; int unit = (int)dummy; int err = 0; int s; int c; if (verbose >= 2) log(LOG_NOTICE, "psm%d: APM resume hook called.\n", unit); /* don't let anybody mess with the aux device */ if (!kbdc_lock(sc->kbdc, TRUE)) return (EIO); s = spltty(); /* save the current controller command byte */ empty_both_buffers(sc->kbdc, 10); c = get_controller_command_byte(sc->kbdc); if (verbose >= 2) log(LOG_DEBUG, "psm%d: current command byte: %04x (psmresume).\n", unit, c); /* enable the aux port but disable the aux interrupt and the keyboard */ if ((c == -1) || !set_controller_command_byte(sc->kbdc, kbdc_get_device_mask(sc->kbdc), KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT | KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { /* CONTROLLER ERROR */ splx(s); kbdc_lock(sc->kbdc, FALSE); log(LOG_ERR, "psm%d: unable to set the command byte (psmresume).\n", unit); return (EIO); } /* flush any data */ if (sc->state & PSM_VALID) { disable_aux_dev(sc->kbdc); /* this may fail; but never mind... */ empty_aux_buffer(sc->kbdc, 10); } sc->inputbytes = 0; #ifdef PSM_RESETAFTERSUSPEND /* try to detect the aux device; are you still there? */ if (reinitialize(unit, &sc->mode)) { /* yes */ sc->state |= PSM_VALID; } else { /* the device has gone! */ restore_controller(sc->kbdc, c); sc->state &= ~PSM_VALID; log(LOG_ERR, "psm%d: the aux device has gone! (psmresume).\n", unit); err = ENXIO; } #endif /* PSM_RESETAFTERSUSPEND */ splx(s); /* restore the driver state */ if ((sc->state & PSM_OPEN) && (err == 0)) { /* enable the aux device and the port again */ err = doopen(unit, c); if (err != 0) log(LOG_ERR, "psm%d: failed to enable the device (psmresume).\n", unit); } else { /* restore the keyboard port and disable the aux port */ if (!set_controller_command_byte(sc->kbdc, kbdc_get_device_mask(sc->kbdc), (c & KBD_KBD_CONTROL_BITS) | KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { /* CONTROLLER ERROR */ log(LOG_ERR, "psm%d: failed to disable the aux port (psmresume).\n", unit); err = EIO; } } /* done */ kbdc_lock(sc->kbdc, FALSE); if ((sc->state & PSM_ASLP) && !(sc->state & PSM_VALID)) { /* * Release the blocked process; it must be notified that the device * cannot be accessed anymore. */ sc->state &= ~PSM_ASLP; wakeup((caddr_t)sc); } if (verbose >= 2) log(LOG_DEBUG, "psm%d: APM resume hook exiting.\n", unit); return (err); } #endif /* PSM_HOOKAPM */ CDEV_DRIVER_MODULE(psm, atkbdc, psm_driver, psm_devclass, CDEV_MAJOR, psm_cdevsw, 0, 0); #endif /* NPSM > 0 */ Index: head/sys/isa/sio.c =================================================================== --- head/sys/isa/sio.c (revision 45719) +++ head/sys/isa/sio.c (revision 45720) @@ -1,3196 +1,3219 @@ /*- * 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. * - * $Id: sio.c,v 1.220 1999/01/19 00:21:47 peter Exp $ + * $Id: sio.c,v 1.221 1999/01/30 12:17:35 phk Exp $ * from: @(#)com.c 7.5 (Berkeley) 5/16/91 * from: i386/isa sio.c,v 1.215 */ #include "opt_comconsole.h" #include "opt_compat.h" #include "opt_ddb.h" #include "opt_devfs.h" /* #include "opt_sio.h" */ #include "sio.h" /* #include "pnp.h" */ #define NPNP 0 /* * Serial driver, based on 386BSD-0.1 com driver. * Mostly rewritten to use pseudo-DMA. * Works for National Semiconductor NS8250-NS16550AF UARTs. * COM driver, based on HP dca driver. * * Changes for PC-Card integration: * - Added PC-Card driver table and handlers */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include #include #ifdef DEVFS #include #endif #include #include #include #include #include #include #include #ifdef COM_ESP #include #endif #include #if 0 #include "card.h" #if NCARD > 0 #include #include #include #endif #if NPNP > 0 #include #endif #endif +#ifndef __i386__ #define disable_intr() 0 #define enable_intr() 0 +#endif #ifdef SMP #define disable_intr() COM_DISABLE_INTR() #define enable_intr() COM_ENABLE_INTR() #endif /* SMP */ #ifndef EXTRA_SIO #if NPNP > 0 #define EXTRA_SIO MAX_PNP_CARDS #else #define EXTRA_SIO 0 #endif #endif #define NSIOTOT (NSIO + EXTRA_SIO) #define LOTS_OF_EVENTS 64 /* helps separate urgent events from input */ #define RS_IBUFSIZE 256 #define CALLOUT_MASK 0x80 #define CONTROL_MASK 0x60 #define CONTROL_INIT_STATE 0x20 #define CONTROL_LOCK_STATE 0x40 #define DEV_TO_UNIT(dev) (MINOR_TO_UNIT(minor(dev))) #define MINOR_MAGIC_MASK (CALLOUT_MASK | CONTROL_MASK) #define MINOR_TO_UNIT(mynor) ((mynor) & ~MINOR_MAGIC_MASK) #ifdef COM_MULTIPORT /* checks in flags for multiport and which is multiport "master chip" * for a given card */ #define COM_ISMULTIPORT(flags) ((flags) & 0x01) #define COM_MPMASTER(flags) (((flags) >> 8) & 0x0ff) #define COM_NOTAST4(flags) ((flags) & 0x04) #endif /* COM_MULTIPORT */ #define COM_CONSOLE(flags) ((flags) & 0x10) #define COM_FORCECONSOLE(flags) ((flags) & 0x20) #define COM_LLCONSOLE(flags) ((flags) & 0x40) #define COM_LOSESOUTINTS(flags) ((flags) & 0x08) #define COM_NOFIFO(flags) ((flags) & 0x02) #define COM_ST16650A(flags) ((flags) & 0x20000) #define COM_C_NOPROBE (0x40000) #define COM_NOPROBE(flags) ((flags) & COM_C_NOPROBE) #define COM_C_IIR_TXRDYBUG (0x80000) #define COM_IIR_TXRDYBUG(flags) ((flags) & COM_C_IIR_TXRDYBUG) #define COM_FIFOSIZE(flags) (((flags) & 0xff000000) >> 24) #define com_scr 7 /* scratch register for 16450-16550 (R/W) */ /* * Input buffer watermarks. * The external device is asked to stop sending when the buffer exactly reaches * high water, or when the high level requests it. * The high level is notified immediately (rather than at a later clock tick) * when this watermark is reached. * The buffer size is chosen so the watermark should almost never be reached. * The low watermark is invisibly 0 since the buffer is always emptied all at * once. */ #define RS_IHIGHWATER (3 * RS_IBUFSIZE / 4) /* * com state bits. * (CS_BUSY | CS_TTGO) and (CS_BUSY | CS_TTGO | CS_ODEVREADY) must be higher * than the other bits so that they can be tested as a group without masking * off the low bits. * * The following com and tty flags correspond closely: * CS_BUSY = TS_BUSY (maintained by comstart(), siopoll() and * siostop()) * CS_TTGO = ~TS_TTSTOP (maintained by comparam() and comstart()) * CS_CTS_OFLOW = CCTS_OFLOW (maintained by comparam()) * CS_RTS_IFLOW = CRTS_IFLOW (maintained by comparam()) * TS_FLUSH is not used. * XXX I think TIOCSETA doesn't clear TS_TTSTOP when it clears IXON. * XXX CS_*FLOW should be CF_*FLOW in com->flags (control flags not state). */ #define CS_BUSY 0x80 /* output in progress */ #define CS_TTGO 0x40 /* output not stopped by XOFF */ #define CS_ODEVREADY 0x20 /* external device h/w ready (CTS) */ #define CS_CHECKMSR 1 /* check of MSR scheduled */ #define CS_CTS_OFLOW 2 /* use CTS output flow control */ #define CS_DTR_OFF 0x10 /* DTR held off */ #define CS_ODONE 4 /* output completed */ #define CS_RTS_IFLOW 8 /* use RTS input flow control */ #define CSE_BUSYCHECK 1 /* siobusycheck() scheduled */ static char const * const error_desc[] = { #define CE_OVERRUN 0 "silo overflow", #define CE_INTERRUPT_BUF_OVERFLOW 1 "interrupt-level buffer overflow", #define CE_TTY_BUF_OVERFLOW 2 "tty-level buffer overflow", }; #define CE_NTYPES 3 #define CE_RECORD(com, errnum) (++(com)->delta_error_counts[errnum]) /* types. XXX - should be elsewhere */ typedef u_int Port_t; /* hardware port */ typedef u_char bool_t; /* boolean */ /* queue of linear buffers */ struct lbq { u_char *l_head; /* next char to process */ u_char *l_tail; /* one past the last char to process */ struct lbq *l_next; /* next in queue */ bool_t l_queued; /* nonzero if queued */ }; /* com device structure */ struct com_s { u_int flags; /* Copy isa device flags */ u_char state; /* miscellaneous flag bits */ bool_t active_out; /* nonzero if the callout device is open */ u_char cfcr_image; /* copy of value written to CFCR */ #ifdef COM_ESP bool_t esp; /* is this unit a hayes esp board? */ #endif u_char extra_state; /* more flag bits, separate for order trick */ u_char fifo_image; /* copy of value written to FIFO */ bool_t hasfifo; /* nonzero for 16550 UARTs */ bool_t st16650a; /* Is a Startech 16650A or RTS/CTS compat */ bool_t loses_outints; /* nonzero if device loses output interrupts */ u_char mcr_image; /* copy of value written to MCR */ #ifdef COM_MULTIPORT bool_t multiport; /* is this unit part of a multiport device? */ #endif /* COM_MULTIPORT */ bool_t no_irq; /* nonzero if irq is not attached */ bool_t gone; /* hardware disappeared */ bool_t poll; /* nonzero if polling is required */ bool_t poll_output; /* nonzero if polling for output is required */ int unit; /* unit number */ int dtr_wait; /* time to hold DTR down on close (* 1/hz) */ u_int tx_fifo_size; u_int wopeners; /* # processes waiting for DCD in open() */ /* * The high level of the driver never reads status registers directly * because there would be too many side effects to handle conveniently. * Instead, it reads copies of the registers stored here by the * interrupt handler. */ u_char last_modem_status; /* last MSR read by intr handler */ u_char prev_modem_status; /* last MSR handled by high level */ u_char hotchar; /* ldisc-specific char to be handled ASAP */ u_char *ibuf; /* start of input buffer */ u_char *ibufend; /* end of input buffer */ u_char *ihighwater; /* threshold in input buffer */ u_char *iptr; /* next free spot in input buffer */ struct lbq obufq; /* head of queue of output buffers */ struct lbq obufs[2]; /* output buffers */ Port_t data_port; /* i/o ports */ #ifdef COM_ESP Port_t esp_port; #endif Port_t int_id_port; Port_t iobase; Port_t modem_ctl_port; Port_t line_status_port; Port_t modem_status_port; Port_t intr_ctl_port; /* Ports of IIR register */ struct tty *tp; /* cross reference */ /* Initial state. */ struct termios it_in; /* should be in struct tty */ struct termios it_out; /* Lock state. */ struct termios lt_in; /* should be in struct tty */ struct termios lt_out; bool_t do_timestamp; bool_t do_dcd_timestamp; struct timeval timestamp; struct timeval dcd_timestamp; u_long bytes_in; /* statistics */ u_long bytes_out; u_int delta_error_counts[CE_NTYPES]; u_long error_counts[CE_NTYPES]; /* * Ping-pong input buffers. The extra factor of 2 in the sizes is * to allow for an error byte for each input byte. */ #define CE_INPUT_OFFSET RS_IBUFSIZE u_char ibuf1[2 * RS_IBUFSIZE]; u_char ibuf2[2 * RS_IBUFSIZE]; /* * Data area for output buffers. Someday we should build the output * buffer queue without copying data. */ u_char obuf1[256]; u_char obuf2[256]; #ifdef DEVFS void *devfs_token_ttyd; void *devfs_token_ttyl; void *devfs_token_ttyi; void *devfs_token_cuaa; void *devfs_token_cual; void *devfs_token_cuai; #endif }; #ifdef COM_ESP static int espattach __P((struct isa_device *isdp, struct com_s *com, Port_t esp_port)); #endif static int sioattach __P((device_t dev)); static timeout_t siobusycheck; static timeout_t siodtrwakeup; static void comhardclose __P((struct com_s *com)); static void siointr1 __P((struct com_s *com)); static void siointr __P((void *arg)); static int commctl __P((struct com_s *com, int bits, int how)); static int comparam __P((struct tty *tp, struct termios *t)); static swihand_t siopoll; static int sioprobe __P((device_t dev)); static void siosettimeout __P((void)); static void comstart __P((struct tty *tp)); static timeout_t comwakeup; static void disc_optim __P((struct tty *tp, struct termios *t, struct com_s *com)); #ifdef DSI_SOFT_MODEM static int LoadSoftModem __P((int unit,int base_io, u_long size, u_char *ptr)); #endif /* DSI_SOFT_MODEM */ static char driver_name[] = "sio"; /* table and macro for fast conversion from a unit number to its com struct */ static devclass_t sio_devclass; #define com_addr(unit) ((struct com_s *) \ devclass_get_softc(sio_devclass, unit)) static device_method_t sio_methods[] = { /* Device interface */ DEVMETHOD(device_probe, sioprobe), DEVMETHOD(device_attach, sioattach), { 0, 0 } }; static driver_t sio_driver = { driver_name, sio_methods, DRIVER_TYPE_TTY, sizeof(struct com_s), }; static d_open_t sioopen; static d_close_t sioclose; static d_read_t sioread; static d_write_t siowrite; static d_ioctl_t sioioctl; static d_stop_t siostop; static d_devtotty_t siodevtotty; #define CDEV_MAJOR 28 static struct cdevsw sio_cdevsw = { sioopen, sioclose, sioread, siowrite, sioioctl, siostop, noreset, siodevtotty, ttpoll, nommap, NULL, driver_name, NULL, -1, nodump, nopsize, D_TTY, }; int comconsole = -1; static volatile speed_t comdefaultrate = CONSPEED; static volatile speed_t gdbdefaultrate = CONSPEED; static u_int com_events; /* input chars + weighted output completions */ static Port_t siocniobase; static Port_t siogdbiobase; static bool_t sio_registered; static int sio_timeout; static int sio_timeouts_until_log; static struct callout_handle sio_timeout_handle = CALLOUT_HANDLE_INITIALIZER(&sio_timeout_handle); #if 0 /* XXX */ static struct tty *sio_tty[NSIOTOT]; #else static struct tty sio_tty[NSIOTOT]; #endif static const int nsio_tty = NSIOTOT; static struct speedtab comspeedtab[] = { { 0, 0 }, { 50, COMBRD(50) }, { 75, COMBRD(75) }, { 110, COMBRD(110) }, { 134, COMBRD(134) }, { 150, COMBRD(150) }, { 200, COMBRD(200) }, { 300, COMBRD(300) }, { 600, COMBRD(600) }, { 1200, COMBRD(1200) }, { 1800, COMBRD(1800) }, { 2400, COMBRD(2400) }, { 4800, COMBRD(4800) }, { 9600, COMBRD(9600) }, { 19200, COMBRD(19200) }, { 38400, COMBRD(38400) }, { 57600, COMBRD(57600) }, { 115200, COMBRD(115200) }, { -1, -1 } }; #ifdef COM_ESP /* XXX configure this properly. */ static Port_t likely_com_ports[] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, }; static Port_t likely_esp_ports[] = { 0x140, 0x180, 0x280, 0 }; #endif /* * handle sysctl read/write requests for console speed * * In addition to setting comdefaultrate for I/O through /dev/console, * also set the initial and lock values for the /dev/ttyXX device * if there is one associated with the console. Finally, if the /dev/tty * device has already been open, change the speed on the open running port * itself. */ static int sysctl_machdep_comdefaultrate SYSCTL_HANDLER_ARGS { int error, s; speed_t newspeed; struct com_s *com; struct tty *tp; newspeed = comdefaultrate; error = sysctl_handle_opaque(oidp, &newspeed, sizeof newspeed, req); if (error || !req->newptr) return (error); comdefaultrate = newspeed; if (comconsole < 0) /* serial console not selected? */ return (0); com = com_addr(comconsole); if (!com) return (ENXIO); /* * set the initial and lock rates for /dev/ttydXX and /dev/cuaXX * (note, the lock rates really are boolean -- if non-zero, disallow * speed changes) */ com->it_in.c_ispeed = com->it_in.c_ospeed = com->lt_in.c_ispeed = com->lt_in.c_ospeed = com->it_out.c_ispeed = com->it_out.c_ospeed = com->lt_out.c_ispeed = com->lt_out.c_ospeed = comdefaultrate; /* * if we're open, change the running rate too */ tp = com->tp; if (tp && (tp->t_state & TS_ISOPEN)) { tp->t_termios.c_ispeed = tp->t_termios.c_ospeed = comdefaultrate; s = spltty(); error = comparam(tp, &tp->t_termios); splx(s); } return error; } SYSCTL_PROC(_machdep, OID_AUTO, conspeed, CTLTYPE_INT | CTLFLAG_RW, 0, 0, sysctl_machdep_comdefaultrate, "I", ""); #if NCARD > 0 /* * PC-Card (PCMCIA) specific code. */ static int sioinit __P((struct pccard_devinfo *)); static void siounload __P((struct pccard_devinfo *)); static int card_intr __P((struct pccard_devinfo *)); PCCARD_MODULE(sio, sioinit, siounload, card_intr, 0, tty_imask); /* * Initialize the device - called from Slot manager. */ int sioinit(struct pccard_devinfo *devi) { /* validate unit number. */ if (devi->isahd.id_unit >= (NSIOTOT)) return(ENODEV); /* Make sure it isn't already probed. */ if (com_addr(devi->isahd.id_unit)) return(EBUSY); /* It's already probed as serial by Upper */ devi->isahd.id_flags |= COM_C_NOPROBE; /* * Probe the device. If a value is returned, the * device was found at the location. */ if (sioprobe(&devi->isahd) == 0) return(ENXIO); if (sioattach(&devi->isahd) == 0) return(ENXIO); return(0); } /* * siounload - unload the driver and clear the table. * XXX TODO: * This is usually called when the card is ejected, but * can be caused by a modunload of a controller driver. * The idea is to reset the driver's view of the device * and ensure that any driver entry points such as * read and write do not hang. */ static void siounload(struct pccard_devinfo *devi) { struct com_s *com; if (!devi) { printf("NULL devi in siounload\n"); return; } com = com_addr(devi->isahd.id_unit); if (!com) { printf("NULL com in siounload\n"); return; } if (!com->iobase) { printf("sio%d already unloaded!\n",devi->isahd.id_unit); return; } if (com->tp && (com->tp->t_state & TS_ISOPEN)) { com->gone = 1; printf("sio%d: unload\n", devi->isahd.id_unit); com->tp->t_gen++; ttyclose(com->tp); ttwakeup(com->tp); ttwwakeup(com->tp); } else { com_addr(com->unit) = NULL; bzero(com, sizeof *com); free(com,M_TTYS); printf("sio%d: unload,gone\n", devi->isahd.id_unit); } } /* * card_intr - Shared interrupt called from * front end of PC-Card handler. */ static int card_intr(struct pccard_devinfo *devi) { struct com_s *com; COM_LOCK(); com = com_addr(devi->isahd.id_unit); if (com && !com->gone) siointr1(com_addr(devi->isahd.id_unit)); COM_UNLOCK(); return(1); } #endif /* NCARD > 0 */ #define SET_FLAG(dev, bit) isa_set_flags(dev, isa_get_flags(dev) | (bit)) #define CLR_FLAG(dev, bit) isa_set_flags(dev, isa_get_flags(dev) & ~(bit)) static int sioprobe(dev) device_t dev; { static bool_t already_init; bool_t failures[10]; int fn; device_t idev; Port_t iobase; intrmask_t irqmap[4]; intrmask_t irqs; u_char mcr_image; int result; device_t xdev; u_int flags = isa_get_flags(dev); if (!already_init) { /* * Turn off MCR_IENABLE for all likely serial ports. An unused * port with its MCR_IENABLE gate open will inhibit interrupts * from any used port that shares the interrupt vector. * XXX the gate enable is elsewhere for some multiports. */ device_t *devs; int count, i; devclass_get_devices(sio_devclass, &devs, &count); for (i = 0; i < count; i++) { xdev = devs[i]; outb(isa_get_port(xdev) + com_mcr, 0); } free(devs, M_TEMP); already_init = TRUE; } if (COM_LLCONSOLE(flags)) { printf("sio%d: reserved for low-level i/o\n", device_get_unit(dev)); return (ENXIO); } /* * If the device is on a multiport card and has an AST/4 * compatible interrupt control register, initialize this * register and prepare to leave MCR_IENABLE clear in the mcr. * Otherwise, prepare to set MCR_IENABLE in the mcr. * Point idev to the device struct giving the correct id_irq. * This is the struct for the master device if there is one. */ idev = dev; mcr_image = MCR_IENABLE; #ifdef COM_MULTIPORT if (COM_ISMULTIPORT(flags)) { idev = devclass_get_device(sio_devclass, COM_MPMASTER(flags)); if (idev == NULL) { printf("sio%d: master device %d not configured\n", device_get_unit(dev), COM_MPMASTER(flags)); isa_set_irq(dev, 0); idev = dev; } if (!COM_NOTAST4(flags)) { outb(isa_get_port(idev) + com_scr, isa_get_irq(idev) >= 0 ? 0x80 : 0); mcr_image = 0; } } #endif /* COM_MULTIPORT */ if (isa_get_irq(idev) < 0) mcr_image = 0; bzero(failures, sizeof failures); iobase = isa_get_port(dev); /* * We don't want to get actual interrupts, just masked ones. * Interrupts from this line should already be masked in the ICU, * but mask them in the processor as well in case there are some * (misconfigured) shared interrupts. */ disable_intr(); /* EXTRA DELAY? */ /* * Initialize the speed and the word size and wait long enough to * drain the maximum of 16 bytes of junk in device output queues. * The speed is undefined after a master reset and must be set * before relying on anything related to output. There may be * junk after a (very fast) soft reboot and (apparently) after * master reset. * XXX what about the UART bug avoided by waiting in comparam()? * We don't want to to wait long enough to drain at 2 bps. */ if (iobase == siocniobase) DELAY((16 + 1) * 1000000 / (comdefaultrate / 10)); else { outb(iobase + com_cfcr, CFCR_DLAB | CFCR_8BITS); outb(iobase + com_dlbl, COMBRD(SIO_TEST_SPEED) & 0xff); outb(iobase + com_dlbh, (u_int) COMBRD(SIO_TEST_SPEED) >> 8); outb(iobase + com_cfcr, CFCR_8BITS); DELAY((16 + 1) * 1000000 / (SIO_TEST_SPEED / 10)); } /* * Enable the interrupt gate and disable device interupts. This * should leave the device driving the interrupt line low and * guarantee an edge trigger if an interrupt can be generated. */ /* EXTRA DELAY? */ outb(iobase + com_mcr, mcr_image); outb(iobase + com_ier, 0); DELAY(1000); /* XXX */ irqmap[0] = isa_irq_pending(); /* * Attempt to set loopback mode so that we can send a null byte * without annoying any external device. */ /* EXTRA DELAY? */ outb(iobase + com_mcr, mcr_image | MCR_LOOPBACK); /* * Attempt to generate an output interrupt. On 8250's, setting * IER_ETXRDY generates an interrupt independent of the current * setting and independent of whether the THR is empty. On 16450's, * setting IER_ETXRDY generates an interrupt independent of the * current setting. On 16550A's, setting IER_ETXRDY only * generates an interrupt when IER_ETXRDY is not already set. */ outb(iobase + com_ier, IER_ETXRDY); /* * On some 16x50 incompatibles, setting IER_ETXRDY doesn't generate * an interrupt. They'd better generate one for actually doing * output. Loopback may be broken on the same incompatibles but * it's unlikely to do more than allow the null byte out. */ outb(iobase + com_data, 0); DELAY((1 + 2) * 1000000 / (SIO_TEST_SPEED / 10)); /* * Turn off loopback mode so that the interrupt gate works again * (MCR_IENABLE was hidden). This should leave the device driving * an interrupt line high. It doesn't matter if the interrupt * line oscillates while we are not looking at it, since interrupts * are disabled. */ /* EXTRA DELAY? */ outb(iobase + com_mcr, mcr_image); /* * It's a definitly Serial PCMCIA(16550A), but still be required * for IIR_TXRDY implementation ( Palido 321s, DC-1S... ) */ if ( COM_NOPROBE(flags) ) { /* Reading IIR register twice */ for ( fn = 0; fn < 2; fn ++ ) { DELAY(10000); failures[6] = inb(iobase + com_iir); } /* Check IIR_TXRDY clear ? */ isa_set_portsize(dev, IO_COMSIZE); result = 0; if ( failures[6] & IIR_TXRDY ) { /* Nop, Double check with clearing IER */ outb(iobase + com_ier, 0); if ( inb(iobase + com_iir) & IIR_NOPEND ) { /* Ok. we're familia this gang */ SET_FLAG(dev, COM_C_IIR_TXRDYBUG); /* Set IIR_TXRDYBUG */ } else { /* Unknow, Just omit this chip.. XXX*/ result = ENXIO; } } else { /* OK. this is well-known guys */ CLR_FLAG(dev, COM_C_IIR_TXRDYBUG); /*Clear IIR_TXRDYBUG*/ } outb(iobase + com_cfcr, CFCR_8BITS); enable_intr(); return (iobase == siocniobase ? 0 : result); } /* * Check that * o the CFCR, IER and MCR in UART hold the values written to them * (the values happen to be all distinct - this is good for * avoiding false positive tests from bus echoes). * o an output interrupt is generated and its vector is correct. * o the interrupt goes away when the IIR in the UART is read. */ /* EXTRA DELAY? */ failures[0] = inb(iobase + com_cfcr) - CFCR_8BITS; failures[1] = inb(iobase + com_ier) - IER_ETXRDY; failures[2] = inb(iobase + com_mcr) - mcr_image; DELAY(10000); /* Some internal modems need this time */ irqmap[1] = isa_irq_pending(); failures[4] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_TXRDY; DELAY(1000); /* XXX */ irqmap[2] = isa_irq_pending(); failures[6] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_NOPEND; /* * Turn off all device interrupts and check that they go off properly. * Leave MCR_IENABLE alone. For ports without a master port, it gates * the OUT2 output of the UART to * the ICU input. Closing the gate would give a floating ICU input * (unless there is another device driving at) and spurious interrupts. * (On the system that this was first tested on, the input floats high * and gives a (masked) interrupt as soon as the gate is closed.) */ outb(iobase + com_ier, 0); outb(iobase + com_cfcr, CFCR_8BITS); /* dummy to avoid bus echo */ failures[7] = inb(iobase + com_ier); DELAY(1000); /* XXX */ irqmap[3] = isa_irq_pending(); failures[9] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_NOPEND; enable_intr(); irqs = irqmap[1] & ~irqmap[0]; if (isa_get_irq(idev) >= 0 && ((1 << isa_get_irq(idev)) & irqs) == 0) printf( "sio%d: configured irq %d not in bitmap of probed irqs %#x\n", device_get_unit(dev), isa_get_irq(idev), irqs); if (bootverbose) printf("sio%d: irq maps: %#x %#x %#x %#x\n", device_get_unit(dev), irqmap[0], irqmap[1], irqmap[2], irqmap[3]); isa_set_portsize(dev, IO_COMSIZE); result = 0; for (fn = 0; fn < sizeof failures; ++fn) if (failures[fn]) { outb(iobase + com_mcr, 0); result = ENXIO; if (bootverbose) { printf("sio%d: probe failed test(s):", device_get_unit(dev)); for (fn = 0; fn < sizeof failures; ++fn) if (failures[fn]) printf(" %d", fn); printf("\n"); } break; } return (iobase == siocniobase ? 0 : result); } #ifdef COM_ESP static int espattach(isdp, com, esp_port) struct isa_device *isdp; struct com_s *com; Port_t esp_port; { u_char dips; u_char val; /* * Check the ESP-specific I/O port to see if we're an ESP * card. If not, return failure immediately. */ if ((inb(esp_port) & 0xf3) == 0) { printf(" port 0x%x is not an ESP board?\n", esp_port); return (0); } /* * We've got something that claims to be a Hayes ESP card. * Let's hope so. */ /* Get the dip-switch configuration */ outb(esp_port + ESP_CMD1, ESP_GETDIPS); dips = inb(esp_port + ESP_STATUS1); /* * Bits 0,1 of dips say which COM port we are. */ if (com->iobase == likely_com_ports[dips & 0x03]) printf(" : ESP"); else { printf(" esp_port has com %d\n", dips & 0x03); return (0); } /* * Check for ESP version 2.0 or later: bits 4,5,6 = 010. */ outb(esp_port + ESP_CMD1, ESP_GETTEST); val = inb(esp_port + ESP_STATUS1); /* clear reg 1 */ val = inb(esp_port + ESP_STATUS2); if ((val & 0x70) < 0x20) { printf("-old (%o)", val & 0x70); return (0); } /* * Check for ability to emulate 16550: bit 7 == 1 */ if ((dips & 0x80) == 0) { printf(" slave"); return (0); } /* * Okay, we seem to be a Hayes ESP card. Whee. */ com->esp = TRUE; com->esp_port = esp_port; return (1); } #endif /* COM_ESP */ static int sioattach(dev) device_t dev; { struct com_s *com; #ifdef COM_ESP Port_t *espp; #endif Port_t iobase; int s; int unit; void *ih; struct resource *res; int zero = 0; u_int flags = isa_get_flags(dev); #if 0 isdp->id_ri_flags |= RI_FAST; #endif iobase = isa_get_port(dev); unit = device_get_unit(dev); com = device_get_softc(dev); /* * sioprobe() has initialized the device registers as follows: * o cfcr = CFCR_8BITS. * It is most important that CFCR_DLAB is off, so that the * data port is not hidden when we enable interrupts. * o ier = 0. * Interrupts are only enabled when the line is open. * o mcr = MCR_IENABLE, or 0 if the port has AST/4 compatible * interrupt control register or the config specifies no irq. * Keeping MCR_DTR and MCR_RTS off might stop the external * device from sending before we are ready. */ bzero(com, sizeof *com); com->unit = unit; com->cfcr_image = CFCR_8BITS; com->dtr_wait = 3 * hz; com->loses_outints = COM_LOSESOUTINTS(flags) != 0; com->no_irq = isa_get_irq(dev) < 0; com->tx_fifo_size = 1; com->iptr = com->ibuf = com->ibuf1; com->ibufend = com->ibuf1 + RS_IBUFSIZE; com->ihighwater = com->ibuf1 + RS_IHIGHWATER; com->obufs[0].l_head = com->obuf1; com->obufs[1].l_head = com->obuf2; com->iobase = iobase; com->data_port = iobase + com_data; com->int_id_port = iobase + com_iir; com->modem_ctl_port = iobase + com_mcr; com->mcr_image = inb(com->modem_ctl_port); com->line_status_port = iobase + com_lsr; com->modem_status_port = iobase + com_msr; com->intr_ctl_port = iobase + com_ier; /* * We don't use all the flags from since they * are only relevant for logins. It's important to have echo off * initially so that the line doesn't start blathering before the * echo flag can be turned off. */ com->it_in.c_iflag = 0; com->it_in.c_oflag = 0; com->it_in.c_cflag = TTYDEF_CFLAG; com->it_in.c_lflag = 0; if (unit == comconsole) { com->it_in.c_iflag = TTYDEF_IFLAG; com->it_in.c_oflag = TTYDEF_OFLAG; com->it_in.c_cflag = TTYDEF_CFLAG | CLOCAL; com->it_in.c_lflag = TTYDEF_LFLAG; com->lt_out.c_cflag = com->lt_in.c_cflag = CLOCAL; com->lt_out.c_ispeed = com->lt_out.c_ospeed = com->lt_in.c_ispeed = com->lt_in.c_ospeed = com->it_in.c_ispeed = com->it_in.c_ospeed = comdefaultrate; } else com->it_in.c_ispeed = com->it_in.c_ospeed = TTYDEF_SPEED; termioschars(&com->it_in); com->it_out = com->it_in; /* attempt to determine UART type */ printf("sio%d: type", unit); #ifdef DSI_SOFT_MODEM if((inb(iobase+7) ^ inb(iobase+7)) & 0x80) { printf(" Digicom Systems, Inc. SoftModem"); goto determined_type; } #endif /* DSI_SOFT_MODEM */ #ifdef COM_MULTIPORT if (!COM_ISMULTIPORT(flags) && !COM_IIR_TXRDYBUG(flags)) #else if (!COM_IIR_TXRDYBUG(flags)) #endif { u_char scr; u_char scr1; u_char scr2; scr = inb(iobase + com_scr); outb(iobase + com_scr, 0xa5); scr1 = inb(iobase + com_scr); outb(iobase + com_scr, 0x5a); scr2 = inb(iobase + com_scr); outb(iobase + com_scr, scr); if (scr1 != 0xa5 || scr2 != 0x5a) { printf(" 8250"); goto determined_type; } } outb(iobase + com_fifo, FIFO_ENABLE | FIFO_RX_HIGH); DELAY(100); com->st16650a = 0; switch (inb(com->int_id_port) & IIR_FIFO_MASK) { case FIFO_RX_LOW: printf(" 16450"); break; case FIFO_RX_MEDL: printf(" 16450?"); break; case FIFO_RX_MEDH: printf(" 16550?"); break; case FIFO_RX_HIGH: if (COM_NOFIFO(flags)) { printf(" 16550A fifo disabled"); } else { com->hasfifo = TRUE; if (COM_ST16650A(flags)) { com->st16650a = 1; com->tx_fifo_size = 32; printf(" ST16650A"); } else { com->tx_fifo_size = COM_FIFOSIZE(flags); printf(" 16550A"); } } #ifdef COM_ESP for (espp = likely_esp_ports; *espp != 0; espp++) if (espattach(dev, com, *espp)) { com->tx_fifo_size = 1024; break; } #endif if (!com->st16650a) { if (!com->tx_fifo_size) com->tx_fifo_size = 16; else printf(" lookalike with %d bytes FIFO", com->tx_fifo_size); } break; } #ifdef COM_ESP if (com->esp) { /* * Set 16550 compatibility mode. * We don't use the ESP_MODE_SCALE bit to increase the * fifo trigger levels because we can't handle large * bursts of input. * XXX flow control should be set in comparam(), not here. */ outb(com->esp_port + ESP_CMD1, ESP_SETMODE); outb(com->esp_port + ESP_CMD2, ESP_MODE_RTS | ESP_MODE_FIFO); /* Set RTS/CTS flow control. */ outb(com->esp_port + ESP_CMD1, ESP_SETFLOWTYPE); outb(com->esp_port + ESP_CMD2, ESP_FLOW_RTS); outb(com->esp_port + ESP_CMD2, ESP_FLOW_CTS); /* Set flow-control levels. */ outb(com->esp_port + ESP_CMD1, ESP_SETRXFLOW); outb(com->esp_port + ESP_CMD2, HIBYTE(768)); outb(com->esp_port + ESP_CMD2, LOBYTE(768)); outb(com->esp_port + ESP_CMD2, HIBYTE(512)); outb(com->esp_port + ESP_CMD2, LOBYTE(512)); } #endif /* COM_ESP */ outb(iobase + com_fifo, 0); determined_type: ; #ifdef COM_MULTIPORT if (COM_ISMULTIPORT(flags)) { com->multiport = TRUE; printf(" (multiport"); if (unit == COM_MPMASTER(flags)) printf(" master"); printf(")"); com->no_irq = isa_get_irq(devclass_get_device (sio_devclass, COM_MPMASTER(flags))) < 0; } #endif /* COM_MULTIPORT */ if (unit == comconsole) printf(", console"); if ( COM_IIR_TXRDYBUG(flags) ) printf(" with a bogus IIR_TXRDY register"); printf("\n"); if (!sio_registered) { register_swi(SWI_TTY, siopoll); sio_registered = TRUE; } #ifdef DEVFS com->devfs_token_ttyd = devfs_add_devswf(&sio_cdevsw, unit, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "ttyd%r", unit); com->devfs_token_ttyi = devfs_add_devswf(&sio_cdevsw, unit | CONTROL_INIT_STATE, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "ttyid%r", unit); com->devfs_token_ttyl = devfs_add_devswf(&sio_cdevsw, unit | CONTROL_LOCK_STATE, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "ttyld%r", unit); com->devfs_token_cuaa = devfs_add_devswf(&sio_cdevsw, unit | CALLOUT_MASK, DV_CHR, UID_UUCP, GID_DIALER, 0660, "cuaa%r", unit); com->devfs_token_cuai = devfs_add_devswf(&sio_cdevsw, unit | CALLOUT_MASK | CONTROL_INIT_STATE, DV_CHR, UID_UUCP, GID_DIALER, 0660, "cuaia%r", unit); com->devfs_token_cual = devfs_add_devswf(&sio_cdevsw, unit | CALLOUT_MASK | CONTROL_LOCK_STATE, DV_CHR, UID_UUCP, GID_DIALER, 0660, "cuala%r", unit); #endif com->flags = isa_get_flags(dev); /* Heritate id_flags for later */ res = bus_alloc_resource(dev, SYS_RES_IRQ, &zero, 0ul, ~0ul, 1, RF_SHAREABLE | RF_ACTIVE); BUS_SETUP_INTR(device_get_parent(dev), dev, res, siointr, com, &ih); return (0); } static int sioopen(dev, flag, mode, p) dev_t dev; int flag; int mode; struct proc *p; { struct com_s *com; int error; Port_t iobase; int mynor; int s; struct tty *tp; int unit; mynor = minor(dev); unit = MINOR_TO_UNIT(mynor); if ((u_int) unit >= NSIOTOT || (com = com_addr(unit)) == NULL) return (ENXIO); if (com->gone) return (ENXIO); if (mynor & CONTROL_MASK) return (0); #if 0 /* XXX */ tp = com->tp = sio_tty[unit] = ttymalloc(sio_tty[unit]); #else tp = com->tp = &sio_tty[unit]; #endif s = spltty(); /* * We jump to this label after all non-interrupted sleeps to pick * up any changes of the device state. */ open_top: while (com->state & CS_DTR_OFF) { error = tsleep(&com->dtr_wait, TTIPRI | PCATCH, "siodtr", 0); if (com_addr(unit) == NULL) return (ENXIO); if (error != 0 || com->gone) goto out; } if (tp->t_state & TS_ISOPEN) { /* * The device is open, so everything has been initialized. * Handle conflicts. */ if (mynor & CALLOUT_MASK) { if (!com->active_out) { error = EBUSY; goto out; } } else { if (com->active_out) { if (flag & O_NONBLOCK) { error = EBUSY; goto out; } error = tsleep(&com->active_out, TTIPRI | PCATCH, "siobi", 0); if (com_addr(unit) == NULL) return (ENXIO); if (error != 0 || com->gone) goto out; goto open_top; } } if (tp->t_state & TS_XCLUDE && suser(p->p_ucred, &p->p_acflag)) { error = EBUSY; goto out; } } else { /* * The device isn't open, so there are no conflicts. * Initialize it. Initialization is done twice in many * cases: to preempt sleeping callin opens if we are * callout, and to complete a callin open after DCD rises. */ tp->t_oproc = comstart; tp->t_param = comparam; tp->t_dev = dev; tp->t_termios = mynor & CALLOUT_MASK ? com->it_out : com->it_in; tp->t_ififosize = 2 * RS_IBUFSIZE; tp->t_ispeedwat = (speed_t)-1; tp->t_ospeedwat = (speed_t)-1; (void)commctl(com, TIOCM_DTR | TIOCM_RTS, DMSET); com->poll = com->no_irq; com->poll_output = com->loses_outints; ++com->wopeners; error = comparam(tp, &tp->t_termios); --com->wopeners; if (error != 0) goto out; /* * XXX we should goto open_top if comparam() slept. */ iobase = com->iobase; if (com->hasfifo) { /* * (Re)enable and drain fifos. * * Certain SMC chips cause problems if the fifos * are enabled while input is ready. Turn off the * fifo if necessary to clear the input. We test * the input ready bit after enabling the fifos * since we've already enabled them in comparam() * and to handle races between enabling and fresh * input. */ while (TRUE) { outb(iobase + com_fifo, FIFO_RCV_RST | FIFO_XMT_RST | com->fifo_image); /* * XXX the delays are for superstitious * historical reasons. It must be less than * the character time at the maximum * supported speed (87 usec at 115200 bps * 8N1). Otherwise we might loop endlessly * if data is streaming in. We used to use * delays of 100. That usually worked * because DELAY(100) used to usually delay * for about 85 usec instead of 100. */ DELAY(50); if (!(inb(com->line_status_port) & LSR_RXRDY)) break; outb(iobase + com_fifo, 0); DELAY(50); (void) inb(com->data_port); } } disable_intr(); (void) inb(com->line_status_port); (void) inb(com->data_port); com->prev_modem_status = com->last_modem_status = inb(com->modem_status_port); if (COM_IIR_TXRDYBUG(com->flags)) { outb(com->intr_ctl_port, IER_ERXRDY | IER_ERLS | IER_EMSC); } else { outb(com->intr_ctl_port, IER_ERXRDY | IER_ETXRDY | IER_ERLS | IER_EMSC); } enable_intr(); /* * Handle initial DCD. Callout devices get a fake initial * DCD (trapdoor DCD). If we are callout, then any sleeping * callin opens get woken up and resume sleeping on "siobi" * instead of "siodcd". */ /* * XXX `mynor & CALLOUT_MASK' should be * `tp->t_cflag & (SOFT_CARRIER | TRAPDOOR_CARRIER) where * TRAPDOOR_CARRIER is the default initial state for callout * devices and SOFT_CARRIER is like CLOCAL except it hides * the true carrier. */ if (com->prev_modem_status & MSR_DCD || mynor & CALLOUT_MASK) (*linesw[tp->t_line].l_modem)(tp, 1); } /* * Wait for DCD if necessary. */ if (!(tp->t_state & TS_CARR_ON) && !(mynor & CALLOUT_MASK) && !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK)) { ++com->wopeners; error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "siodcd", 0); if (com_addr(unit) == NULL) return (ENXIO); --com->wopeners; if (error != 0 || com->gone) goto out; goto open_top; } error = (*linesw[tp->t_line].l_open)(dev, tp); disc_optim(tp, &tp->t_termios, com); if (tp->t_state & TS_ISOPEN && mynor & CALLOUT_MASK) com->active_out = TRUE; siosettimeout(); out: splx(s); if (!(tp->t_state & TS_ISOPEN) && com->wopeners == 0) comhardclose(com); return (error); } static int sioclose(dev, flag, mode, p) dev_t dev; int flag; int mode; struct proc *p; { struct com_s *com; int mynor; int s; struct tty *tp; mynor = minor(dev); if (mynor & CONTROL_MASK) return (0); com = com_addr(MINOR_TO_UNIT(mynor)); tp = com->tp; s = spltty(); (*linesw[tp->t_line].l_close)(tp, flag); disc_optim(tp, &tp->t_termios, com); siostop(tp, FREAD | FWRITE); comhardclose(com); ttyclose(tp); siosettimeout(); splx(s); if (com->gone) { printf("sio%d: gone\n", com->unit); s = spltty(); bzero(tp,sizeof *tp); bzero(com,sizeof *com); splx(s); } return (0); } static void comhardclose(com) struct com_s *com; { Port_t iobase; int s; struct tty *tp; int unit; unit = com->unit; iobase = com->iobase; s = spltty(); com->poll = FALSE; com->poll_output = FALSE; com->do_timestamp = FALSE; com->do_dcd_timestamp = FALSE; outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); { outb(iobase + com_ier, 0); tp = com->tp; if (tp->t_cflag & HUPCL /* * XXX we will miss any carrier drop between here and the * next open. Perhaps we should watch DCD even when the * port is closed; it is not sufficient to check it at * the next open because it might go up and down while * we're not watching. */ || !com->active_out && !(com->prev_modem_status & MSR_DCD) && !(com->it_in.c_cflag & CLOCAL) || !(tp->t_state & TS_ISOPEN)) { (void)commctl(com, TIOCM_DTR, DMBIC); if (com->dtr_wait != 0 && !(com->state & CS_DTR_OFF)) { timeout(siodtrwakeup, com, com->dtr_wait); com->state |= CS_DTR_OFF; } } } if (com->hasfifo) { /* * Disable fifos so that they are off after controlled * reboots. Some BIOSes fail to detect 16550s when the * fifos are enabled. */ outb(iobase + com_fifo, 0); } com->active_out = FALSE; wakeup(&com->active_out); wakeup(TSA_CARR_ON(tp)); /* restart any wopeners */ splx(s); } static int sioread(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { int mynor; int unit; struct tty *tp; mynor = minor(dev); if (mynor & CONTROL_MASK) return (ENODEV); unit = MINOR_TO_UNIT(mynor); if (com_addr(unit)->gone) return (ENODEV); tp = com_addr(unit)->tp; return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); } static int siowrite(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { int mynor; struct tty *tp; int unit; mynor = minor(dev); if (mynor & CONTROL_MASK) return (ENODEV); unit = MINOR_TO_UNIT(mynor); if (com_addr(unit)->gone) return (ENODEV); tp = com_addr(unit)->tp; /* * (XXX) We disallow virtual consoles if the physical console is * a serial port. This is in case there is a display attached that * is not the console. In that situation we don't need/want the X * server taking over the console. */ if (constty != NULL && unit == comconsole) constty = NULL; return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); } static void siobusycheck(chan) void *chan; { struct com_s *com; int s; com = (struct com_s *)chan; /* * Clear TS_BUSY if low-level output is complete. * spl locking is sufficient because siointr1() does not set CS_BUSY. * If siointr1() clears CS_BUSY after we look at it, then we'll get * called again. Reading the line status port outside of siointr1() * is safe because CS_BUSY is clear so there are no output interrupts * to lose. */ s = spltty(); if (com->state & CS_BUSY) com->extra_state &= ~CSE_BUSYCHECK; /* False alarm. */ else if ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY)) == (LSR_TSRE | LSR_TXRDY)) { com->tp->t_state &= ~TS_BUSY; ttwwakeup(com->tp); com->extra_state &= ~CSE_BUSYCHECK; } else timeout(siobusycheck, com, hz / 100); splx(s); } static void siodtrwakeup(chan) void *chan; { struct com_s *com; com = (struct com_s *)chan; com->state &= ~CS_DTR_OFF; wakeup(&com->dtr_wait); } void siointr(arg) void *arg; { #ifndef COM_MULTIPORT COM_LOCK(); siointr1((struct com_s *) arg); COM_UNLOCK(); #else /* COM_MULTIPORT */ bool_t possibly_more_intrs; /* * Loop until there is no activity on any port. This is necessary * to get an interrupt edge more than to avoid another interrupt. * If the IRQ signal is just an OR of the IRQ signals from several * devices, then the edge from one may be lost because another is * on. */ COM_LOCK(); do { possibly_more_intrs = FALSE; for (unit = 0; unit < NSIOTOT; ++unit) { com = com_addr(unit); /* * XXX COM_LOCK(); * would it work here, or be counter-productive? */ if (com != NULL && !com->gone && (inb(com->int_id_port) & IIR_IMASK) != IIR_NOPEND) { siointr1(com); possibly_more_intrs = TRUE; } /* XXX COM_UNLOCK(); */ } } while (possibly_more_intrs); COM_UNLOCK(); #endif /* COM_MULTIPORT */ } static void siointr1(com) struct com_s *com; { u_char line_status; u_char modem_status; u_char *ioptr; u_char recv_data; u_char int_ident; u_char int_ctl; u_char int_ctl_new; int_ctl = inb(com->intr_ctl_port); int_ctl_new = int_ctl; while (!com->gone) { line_status = inb(com->line_status_port); /* input event? (check first to help avoid overruns) */ while (line_status & LSR_RCV_MASK) { /* break/unnattached error bits or real input? */ if (!(line_status & LSR_RXRDY)) recv_data = 0; else recv_data = inb(com->data_port); if (line_status & (LSR_BI | LSR_FE | LSR_PE)) { /* * Don't store BI if IGNBRK or FE/PE if IGNPAR. * Otherwise, push the work to a higher level * (to handle PARMRK) if we're bypassing. * Otherwise, convert BI/FE and PE+INPCK to 0. * * This makes bypassing work right in the * usual "raw" case (IGNBRK set, and IGNPAR * and INPCK clear). * * Note: BI together with FE/PE means just BI. */ if (line_status & LSR_BI) { #if defined(DDB) && defined(BREAK_TO_DEBUGGER) if (com->unit == comconsole) { breakpoint(); goto cont; } #endif if (com->tp == NULL || com->tp->t_iflag & IGNBRK) goto cont; } else { if (com->tp == NULL || com->tp->t_iflag & IGNPAR) goto cont; } if (com->tp->t_state & TS_CAN_BYPASS_L_RINT && (line_status & (LSR_BI | LSR_FE) || com->tp->t_iflag & INPCK)) recv_data = 0; } ++com->bytes_in; if (com->hotchar != 0 && recv_data == com->hotchar) setsofttty(); ioptr = com->iptr; if (ioptr >= com->ibufend) CE_RECORD(com, CE_INTERRUPT_BUF_OVERFLOW); else { if (com->do_timestamp) microtime(&com->timestamp); ++com_events; schedsofttty(); #if 0 /* for testing input latency vs efficiency */ if (com->iptr - com->ibuf == 8) setsofttty(); #endif ioptr[0] = recv_data; ioptr[CE_INPUT_OFFSET] = line_status; com->iptr = ++ioptr; if (ioptr == com->ihighwater && com->state & CS_RTS_IFLOW) outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); if (line_status & LSR_OE) CE_RECORD(com, CE_OVERRUN); } cont: /* * "& 0x7F" is to avoid the gcc-1.40 generating a slow * jump from the top of the loop to here */ line_status = inb(com->line_status_port) & 0x7F; } /* modem status change? (always check before doing output) */ modem_status = inb(com->modem_status_port); if (modem_status != com->last_modem_status) { if (com->do_dcd_timestamp && !(com->last_modem_status & MSR_DCD) && modem_status & MSR_DCD) microtime(&com->dcd_timestamp); /* * Schedule high level to handle DCD changes. Note * that we don't use the delta bits anywhere. Some * UARTs mess them up, and it's easy to remember the * previous bits and calculate the delta. */ com->last_modem_status = modem_status; if (!(com->state & CS_CHECKMSR)) { com_events += LOTS_OF_EVENTS; com->state |= CS_CHECKMSR; setsofttty(); } /* handle CTS change immediately for crisp flow ctl */ if (com->state & CS_CTS_OFLOW) { if (modem_status & MSR_CTS) com->state |= CS_ODEVREADY; else com->state &= ~CS_ODEVREADY; } } /* output queued and everything ready? */ if (line_status & LSR_TXRDY && com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) { ioptr = com->obufq.l_head; if (com->tx_fifo_size > 1) { u_int ocount; ocount = com->obufq.l_tail - ioptr; if (ocount > com->tx_fifo_size) ocount = com->tx_fifo_size; com->bytes_out += ocount; do outb(com->data_port, *ioptr++); while (--ocount != 0); } else { outb(com->data_port, *ioptr++); ++com->bytes_out; } com->obufq.l_head = ioptr; if (COM_IIR_TXRDYBUG(com->flags)) { int_ctl_new = int_ctl | IER_ETXRDY; } if (ioptr >= com->obufq.l_tail) { struct lbq *qp; qp = com->obufq.l_next; qp->l_queued = FALSE; qp = qp->l_next; if (qp != NULL) { com->obufq.l_head = qp->l_head; com->obufq.l_tail = qp->l_tail; com->obufq.l_next = qp; } else { /* output just completed */ if ( COM_IIR_TXRDYBUG(com->flags) ) { int_ctl_new = int_ctl & ~IER_ETXRDY; } com->state &= ~CS_BUSY; } if (!(com->state & CS_ODONE)) { com_events += LOTS_OF_EVENTS; com->state |= CS_ODONE; setsofttty(); /* handle at high level ASAP */ } } if ( COM_IIR_TXRDYBUG(com->flags) && (int_ctl != int_ctl_new)) { outb(com->intr_ctl_port, int_ctl_new); } } /* finished? */ #ifndef COM_MULTIPORT if ((inb(com->int_id_port) & IIR_IMASK) == IIR_NOPEND) #endif /* COM_MULTIPORT */ return; } } static int sioioctl(dev, cmd, data, flag, p) dev_t dev; u_long cmd; caddr_t data; int flag; struct proc *p; { struct com_s *com; int error; Port_t iobase; int mynor; int s; struct tty *tp; #if defined(COMPAT_43) || defined(COMPAT_SUNOS) u_long oldcmd; struct termios term; #endif mynor = minor(dev); com = com_addr(MINOR_TO_UNIT(mynor)); if (com->gone) return (ENODEV); iobase = com->iobase; if (mynor & CONTROL_MASK) { struct termios *ct; switch (mynor & CONTROL_MASK) { case CONTROL_INIT_STATE: ct = mynor & CALLOUT_MASK ? &com->it_out : &com->it_in; break; case CONTROL_LOCK_STATE: ct = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in; break; default: return (ENODEV); /* /dev/nodev */ } switch (cmd) { case TIOCSETA: error = suser(p->p_ucred, &p->p_acflag); if (error != 0) return (error); *ct = *(struct termios *)data; return (0); case TIOCGETA: *(struct termios *)data = *ct; return (0); case TIOCGETD: *(int *)data = TTYDISC; return (0); case TIOCGWINSZ: bzero(data, sizeof(struct winsize)); return (0); #ifdef DSI_SOFT_MODEM /* * Download micro-code to Digicom modem. */ case TIOCDSIMICROCODE: { u_long l; u_char *p,*pi; pi = (u_char*)(*(caddr_t*)data); error = copyin(pi,&l,sizeof l); if(error) {return error;}; pi += sizeof l; p = malloc(l,M_TEMP,M_NOWAIT); if(!p) {return ENOBUFS;} error = copyin(pi,p,l); if(error) {free(p,M_TEMP); return error;}; if(error = LoadSoftModem( MINOR_TO_UNIT(mynor),iobase,l,p)) {free(p,M_TEMP); return error;} free(p,M_TEMP); return(0); } #endif /* DSI_SOFT_MODEM */ default: return (ENOTTY); } } tp = com->tp; #if defined(COMPAT_43) || defined(COMPAT_SUNOS) term = tp->t_termios; oldcmd = cmd; error = ttsetcompat(tp, &cmd, data, &term); if (error != 0) return (error); if (cmd != oldcmd) data = (caddr_t)&term; #endif if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { int cc; struct termios *dt = (struct termios *)data; struct termios *lt = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in; dt->c_iflag = (tp->t_iflag & lt->c_iflag) | (dt->c_iflag & ~lt->c_iflag); dt->c_oflag = (tp->t_oflag & lt->c_oflag) | (dt->c_oflag & ~lt->c_oflag); dt->c_cflag = (tp->t_cflag & lt->c_cflag) | (dt->c_cflag & ~lt->c_cflag); dt->c_lflag = (tp->t_lflag & lt->c_lflag) | (dt->c_lflag & ~lt->c_lflag); for (cc = 0; cc < NCCS; ++cc) if (lt->c_cc[cc] != 0) dt->c_cc[cc] = tp->t_cc[cc]; if (lt->c_ispeed != 0) dt->c_ispeed = tp->t_ispeed; if (lt->c_ospeed != 0) dt->c_ospeed = tp->t_ospeed; } error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); if (error != ENOIOCTL) return (error); s = spltty(); error = ttioctl(tp, cmd, data, flag); disc_optim(tp, &tp->t_termios, com); if (error != ENOIOCTL) { splx(s); return (error); } switch (cmd) { case TIOCSBRK: outb(iobase + com_cfcr, com->cfcr_image |= CFCR_SBREAK); break; case TIOCCBRK: outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); break; case TIOCSDTR: (void)commctl(com, TIOCM_DTR, DMBIS); break; case TIOCCDTR: (void)commctl(com, TIOCM_DTR, DMBIC); break; /* * XXX should disallow changing MCR_RTS if CS_RTS_IFLOW is set. The * changes get undone on the next call to comparam(). */ case TIOCMSET: (void)commctl(com, *(int *)data, DMSET); break; case TIOCMBIS: (void)commctl(com, *(int *)data, DMBIS); break; case TIOCMBIC: (void)commctl(com, *(int *)data, DMBIC); break; case TIOCMGET: *(int *)data = commctl(com, 0, DMGET); break; case TIOCMSDTRWAIT: /* must be root since the wait applies to following logins */ error = suser(p->p_ucred, &p->p_acflag); if (error != 0) { splx(s); return (error); } com->dtr_wait = *(int *)data * hz / 100; break; case TIOCMGDTRWAIT: *(int *)data = com->dtr_wait * 100 / hz; break; case TIOCTIMESTAMP: com->do_timestamp = TRUE; *(struct timeval *)data = com->timestamp; break; case TIOCDCDTIMESTAMP: com->do_dcd_timestamp = TRUE; *(struct timeval *)data = com->dcd_timestamp; break; default: splx(s); return (ENOTTY); } splx(s); return (0); } static void siopoll() { int unit; if (com_events == 0) return; repeat: for (unit = 0; unit < NSIOTOT; ++unit) { u_char *buf; struct com_s *com; u_char *ibuf; int incc; struct tty *tp; com = com_addr(unit); if (com == NULL) continue; tp = com->tp; if (tp == NULL || com->gone) { /* * Discard any events related to never-opened or * going-away devices. */ disable_intr(); incc = com->iptr - com->ibuf; com->iptr = com->ibuf; if (com->state & CS_CHECKMSR) { incc += LOTS_OF_EVENTS; com->state &= ~CS_CHECKMSR; } com_events -= incc; enable_intr(); continue; } /* switch the role of the low-level input buffers */ if (com->iptr == (ibuf = com->ibuf)) { buf = NULL; /* not used, but compiler can't tell */ incc = 0; } else { buf = ibuf; disable_intr(); incc = com->iptr - buf; com_events -= incc; if (ibuf == com->ibuf1) ibuf = com->ibuf2; else ibuf = com->ibuf1; com->ibufend = ibuf + RS_IBUFSIZE; com->ihighwater = ibuf + RS_IHIGHWATER; com->iptr = ibuf; /* * There is now room for another low-level buffer full * of input, so enable RTS if it is now disabled and * there is room in the high-level buffer. */ if ((com->state & CS_RTS_IFLOW) && !(com->mcr_image & MCR_RTS) && !(tp->t_state & TS_TBLOCK)) outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); enable_intr(); com->ibuf = ibuf; } if (com->state & CS_CHECKMSR) { u_char delta_modem_status; disable_intr(); delta_modem_status = com->last_modem_status ^ com->prev_modem_status; com->prev_modem_status = com->last_modem_status; com_events -= LOTS_OF_EVENTS; com->state &= ~CS_CHECKMSR; enable_intr(); if (delta_modem_status & MSR_DCD) (*linesw[tp->t_line].l_modem) (tp, com->prev_modem_status & MSR_DCD); } if (com->state & CS_ODONE) { disable_intr(); com_events -= LOTS_OF_EVENTS; com->state &= ~CS_ODONE; enable_intr(); if (!(com->state & CS_BUSY) && !(com->extra_state & CSE_BUSYCHECK)) { timeout(siobusycheck, com, hz / 100); com->extra_state |= CSE_BUSYCHECK; } (*linesw[tp->t_line].l_start)(tp); } if (incc <= 0 || !(tp->t_state & TS_ISOPEN) || !(tp->t_cflag & CREAD)) continue; /* * Avoid the grotesquely inefficient lineswitch routine * (ttyinput) in "raw" mode. It usually takes about 450 * instructions (that's without canonical processing or echo!). * slinput is reasonably fast (usually 40 instructions plus * call overhead). */ if (tp->t_state & TS_CAN_BYPASS_L_RINT) { if (tp->t_rawq.c_cc + incc > tp->t_ihiwat && (com->state & CS_RTS_IFLOW || tp->t_iflag & IXOFF) && !(tp->t_state & TS_TBLOCK)) ttyblock(tp); tk_nin += incc; tk_rawcc += incc; tp->t_rawcc += incc; com->delta_error_counts[CE_TTY_BUF_OVERFLOW] += b_to_q((char *)buf, incc, &tp->t_rawq); ttwakeup(tp); if (tp->t_state & TS_TTSTOP && (tp->t_iflag & IXANY || tp->t_cc[VSTART] == tp->t_cc[VSTOP])) { tp->t_state &= ~TS_TTSTOP; tp->t_lflag &= ~FLUSHO; comstart(tp); } } else { do { u_char line_status; int recv_data; line_status = (u_char) buf[CE_INPUT_OFFSET]; recv_data = (u_char) *buf++; if (line_status & (LSR_BI | LSR_FE | LSR_OE | LSR_PE)) { if (line_status & LSR_BI) recv_data |= TTY_BI; if (line_status & LSR_FE) recv_data |= TTY_FE; if (line_status & LSR_OE) recv_data |= TTY_OE; if (line_status & LSR_PE) recv_data |= TTY_PE; } (*linesw[tp->t_line].l_rint)(recv_data, tp); } while (--incc > 0); } if (com_events == 0) break; } if (com_events >= LOTS_OF_EVENTS) goto repeat; } static int comparam(tp, t) struct tty *tp; struct termios *t; { u_int cfcr; int cflag; struct com_s *com; int divisor; u_char dlbh; u_char dlbl; int error; Port_t iobase; int s; int unit; int txtimeout; /* do historical conversions */ if (t->c_ispeed == 0) t->c_ispeed = t->c_ospeed; /* check requested parameters */ divisor = ttspeedtab(t->c_ospeed, comspeedtab); if (divisor < 0 || divisor > 0 && t->c_ispeed != t->c_ospeed) return (EINVAL); /* parameters are OK, convert them to the com struct and the device */ unit = DEV_TO_UNIT(tp->t_dev); com = com_addr(unit); iobase = com->iobase; s = spltty(); if (divisor == 0) (void)commctl(com, TIOCM_DTR, DMBIC); /* hang up line */ else (void)commctl(com, TIOCM_DTR, DMBIS); cflag = t->c_cflag; switch (cflag & CSIZE) { case CS5: cfcr = CFCR_5BITS; break; case CS6: cfcr = CFCR_6BITS; break; case CS7: cfcr = CFCR_7BITS; break; default: cfcr = CFCR_8BITS; break; } if (cflag & PARENB) { cfcr |= CFCR_PENAB; if (!(cflag & PARODD)) cfcr |= CFCR_PEVEN; } if (cflag & CSTOPB) cfcr |= CFCR_STOPB; if (com->hasfifo && divisor != 0) { /* * Use a fifo trigger level low enough so that the input * latency from the fifo is less than about 16 msec and * the total latency is less than about 30 msec. These * latencies are reasonable for humans. Serial comms * protocols shouldn't expect anything better since modem * latencies are larger. */ com->fifo_image = t->c_ospeed <= 4800 ? FIFO_ENABLE : FIFO_ENABLE | FIFO_RX_HIGH; #ifdef COM_ESP /* * The Hayes ESP card needs the fifo DMA mode bit set * in compatibility mode. If not, it will interrupt * for each character received. */ if (com->esp) com->fifo_image |= FIFO_DMA_MODE; #endif outb(iobase + com_fifo, com->fifo_image); } /* * Some UARTs lock up if the divisor latch registers are selected * while the UART is doing output (they refuse to transmit anything * more until given a hard reset). Fix this by stopping filling * the device buffers and waiting for them to drain. Reading the * line status port outside of siointr1() might lose some receiver * error bits, but that is acceptable here. */ disable_intr(); retry: com->state &= ~CS_TTGO; txtimeout = tp->t_timeout; enable_intr(); while ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY)) != (LSR_TSRE | LSR_TXRDY)) { tp->t_state |= TS_SO_OCOMPLETE; error = ttysleep(tp, TSA_OCOMPLETE(tp), TTIPRI | PCATCH, "siotx", hz / 100); if ( txtimeout != 0 && (!error || error == EAGAIN) && (txtimeout -= hz / 100) <= 0 ) error = EIO; if (com->gone) error = ENODEV; if (error != 0 && error != EAGAIN) { if (!(tp->t_state & TS_TTSTOP)) { disable_intr(); com->state |= CS_TTGO; enable_intr(); } splx(s); return (error); } } disable_intr(); /* very important while com_data is hidden */ /* * XXX - clearing CS_TTGO is not sufficient to stop further output, * because siopoll() calls comstart() which usually sets it again * because TS_TTSTOP is clear. Setting TS_TTSTOP would not be * sufficient, for similar reasons. */ if ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY)) != (LSR_TSRE | LSR_TXRDY)) goto retry; if (divisor != 0) { outb(iobase + com_cfcr, cfcr | CFCR_DLAB); /* * Only set the divisor registers if they would change, * since on some 16550 incompatibles (UMC8669F), setting * them while input is arriving them loses sync until * data stops arriving. */ dlbl = divisor & 0xFF; if (inb(iobase + com_dlbl) != dlbl) outb(iobase + com_dlbl, dlbl); dlbh = (u_int) divisor >> 8; if (inb(iobase + com_dlbh) != dlbh) outb(iobase + com_dlbh, dlbh); } outb(iobase + com_cfcr, com->cfcr_image = cfcr); if (!(tp->t_state & TS_TTSTOP)) com->state |= CS_TTGO; if (cflag & CRTS_IFLOW) { if (com->st16650a) { outb(iobase + com_cfcr, 0xbf); outb(iobase + com_fifo, inb(iobase + com_fifo) | 0x40); } com->state |= CS_RTS_IFLOW; /* * If CS_RTS_IFLOW just changed from off to on, the change * needs to be propagated to MCR_RTS. This isn't urgent, * so do it later by calling comstart() instead of repeating * a lot of code from comstart() here. */ } else if (com->state & CS_RTS_IFLOW) { com->state &= ~CS_RTS_IFLOW; /* * CS_RTS_IFLOW just changed from on to off. Force MCR_RTS * on here, since comstart() won't do it later. */ outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); if (com->st16650a) { outb(iobase + com_cfcr, 0xbf); outb(iobase + com_fifo, inb(iobase + com_fifo) & ~0x40); } } /* * Set up state to handle output flow control. * XXX - worth handling MDMBUF (DCD) flow control at the lowest level? * Now has 10+ msec latency, while CTS flow has 50- usec latency. */ com->state |= CS_ODEVREADY; com->state &= ~CS_CTS_OFLOW; if (cflag & CCTS_OFLOW) { com->state |= CS_CTS_OFLOW; if (!(com->last_modem_status & MSR_CTS)) com->state &= ~CS_ODEVREADY; if (com->st16650a) { outb(iobase + com_cfcr, 0xbf); outb(iobase + com_fifo, inb(iobase + com_fifo) | 0x80); } } else { if (com->st16650a) { outb(iobase + com_cfcr, 0xbf); outb(iobase + com_fifo, inb(iobase + com_fifo) & ~0x80); } } outb(iobase + com_cfcr, com->cfcr_image); /* XXX shouldn't call functions while intrs are disabled. */ disc_optim(tp, t, com); /* * Recover from fiddling with CS_TTGO. We used to call siointr1() * unconditionally, but that defeated the careful discarding of * stale input in sioopen(). */ if (com->state >= (CS_BUSY | CS_TTGO)) siointr1(com); enable_intr(); splx(s); comstart(tp); return (0); } static void comstart(tp) struct tty *tp; { struct com_s *com; int s; int unit; unit = DEV_TO_UNIT(tp->t_dev); com = com_addr(unit); s = spltty(); disable_intr(); if (tp->t_state & TS_TTSTOP) com->state &= ~CS_TTGO; else com->state |= CS_TTGO; if (tp->t_state & TS_TBLOCK) { if (com->mcr_image & MCR_RTS && com->state & CS_RTS_IFLOW) outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); } else { if (!(com->mcr_image & MCR_RTS) && com->iptr < com->ihighwater && com->state & CS_RTS_IFLOW) outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); } enable_intr(); if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) { ttwwakeup(tp); splx(s); return; } if (tp->t_outq.c_cc != 0) { struct lbq *qp; struct lbq *next; if (!com->obufs[0].l_queued) { com->obufs[0].l_tail = com->obuf1 + q_to_b(&tp->t_outq, com->obuf1, sizeof com->obuf1); com->obufs[0].l_next = NULL; com->obufs[0].l_queued = TRUE; disable_intr(); if (com->state & CS_BUSY) { qp = com->obufq.l_next; while ((next = qp->l_next) != NULL) qp = next; qp->l_next = &com->obufs[0]; } else { com->obufq.l_head = com->obufs[0].l_head; com->obufq.l_tail = com->obufs[0].l_tail; com->obufq.l_next = &com->obufs[0]; com->state |= CS_BUSY; } enable_intr(); } if (tp->t_outq.c_cc != 0 && !com->obufs[1].l_queued) { com->obufs[1].l_tail = com->obuf2 + q_to_b(&tp->t_outq, com->obuf2, sizeof com->obuf2); com->obufs[1].l_next = NULL; com->obufs[1].l_queued = TRUE; disable_intr(); if (com->state & CS_BUSY) { qp = com->obufq.l_next; while ((next = qp->l_next) != NULL) qp = next; qp->l_next = &com->obufs[1]; } else { com->obufq.l_head = com->obufs[1].l_head; com->obufq.l_tail = com->obufs[1].l_tail; com->obufq.l_next = &com->obufs[1]; com->state |= CS_BUSY; } enable_intr(); } tp->t_state |= TS_BUSY; } disable_intr(); if (com->state >= (CS_BUSY | CS_TTGO)) siointr1(com); /* fake interrupt to start output */ enable_intr(); ttwwakeup(tp); splx(s); } static void siostop(tp, rw) struct tty *tp; int rw; { struct com_s *com; com = com_addr(DEV_TO_UNIT(tp->t_dev)); if (com->gone) return; disable_intr(); if (rw & FWRITE) { if (com->hasfifo) #ifdef COM_ESP /* XXX avoid h/w bug. */ if (!com->esp) #endif /* XXX does this flush everything? */ outb(com->iobase + com_fifo, FIFO_XMT_RST | com->fifo_image); com->obufs[0].l_queued = FALSE; com->obufs[1].l_queued = FALSE; if (com->state & CS_ODONE) com_events -= LOTS_OF_EVENTS; com->state &= ~(CS_ODONE | CS_BUSY); com->tp->t_state &= ~TS_BUSY; } if (rw & FREAD) { if (com->hasfifo) #ifdef COM_ESP /* XXX avoid h/w bug. */ if (!com->esp) #endif /* XXX does this flush everything? */ outb(com->iobase + com_fifo, FIFO_RCV_RST | com->fifo_image); com_events -= (com->iptr - com->ibuf); com->iptr = com->ibuf; } enable_intr(); comstart(tp); } static struct tty * siodevtotty(dev) dev_t dev; { int mynor; int unit; mynor = minor(dev); if (mynor & CONTROL_MASK) return (NULL); unit = MINOR_TO_UNIT(mynor); if ((u_int) unit >= NSIOTOT) return (NULL); return (&sio_tty[unit]); } static int commctl(com, bits, how) struct com_s *com; int bits; int how; { int mcr; int msr; if (how == DMGET) { bits = TIOCM_LE; /* XXX - always enabled while open */ mcr = com->mcr_image; if (mcr & MCR_DTR) bits |= TIOCM_DTR; if (mcr & MCR_RTS) bits |= TIOCM_RTS; msr = com->prev_modem_status; if (msr & MSR_CTS) bits |= TIOCM_CTS; if (msr & MSR_DCD) bits |= TIOCM_CD; if (msr & MSR_DSR) bits |= TIOCM_DSR; /* * XXX - MSR_RI is naturally volatile, and we make MSR_TERI * more volatile by reading the modem status a lot. Perhaps * we should latch both bits until the status is read here. */ if (msr & (MSR_RI | MSR_TERI)) bits |= TIOCM_RI; return (bits); } mcr = 0; if (bits & TIOCM_DTR) mcr |= MCR_DTR; if (bits & TIOCM_RTS) mcr |= MCR_RTS; if (com->gone) return(0); disable_intr(); switch (how) { case DMSET: outb(com->modem_ctl_port, com->mcr_image = mcr | (com->mcr_image & MCR_IENABLE)); break; case DMBIS: outb(com->modem_ctl_port, com->mcr_image |= mcr); break; case DMBIC: outb(com->modem_ctl_port, com->mcr_image &= ~mcr); break; } enable_intr(); return (0); } static void siosettimeout() { struct com_s *com; bool_t someopen; int unit; /* * Set our timeout period to 1 second if no polled devices are open. * Otherwise set it to max(1/200, 1/hz). * Enable timeouts iff some device is open. */ untimeout(comwakeup, (void *)NULL, sio_timeout_handle); sio_timeout = hz; someopen = FALSE; for (unit = 0; unit < NSIOTOT; ++unit) { com = com_addr(unit); if (com != NULL && com->tp != NULL && com->tp->t_state & TS_ISOPEN && !com->gone) { someopen = TRUE; if (com->poll || com->poll_output) { sio_timeout = hz > 200 ? hz / 200 : 1; break; } } } if (someopen) { sio_timeouts_until_log = hz / sio_timeout; sio_timeout_handle = timeout(comwakeup, (void *)NULL, sio_timeout); } else { /* Flush error messages, if any. */ sio_timeouts_until_log = 1; comwakeup((void *)NULL); untimeout(comwakeup, (void *)NULL, sio_timeout_handle); } } static void comwakeup(chan) void *chan; { struct com_s *com; int unit; sio_timeout_handle = timeout(comwakeup, (void *)NULL, sio_timeout); /* * Recover from lost output interrupts. * Poll any lines that don't use interrupts. */ for (unit = 0; unit < NSIOTOT; ++unit) { com = com_addr(unit); if (com != NULL && !com->gone && (com->state >= (CS_BUSY | CS_TTGO) || com->poll)) { disable_intr(); siointr1(com); enable_intr(); } } /* * Check for and log errors, but not too often. */ if (--sio_timeouts_until_log > 0) return; sio_timeouts_until_log = hz / sio_timeout; for (unit = 0; unit < NSIOTOT; ++unit) { int errnum; com = com_addr(unit); if (com == NULL) continue; if (com->gone) continue; for (errnum = 0; errnum < CE_NTYPES; ++errnum) { u_int delta; u_long total; disable_intr(); delta = com->delta_error_counts[errnum]; com->delta_error_counts[errnum] = 0; enable_intr(); if (delta == 0) continue; total = com->error_counts[errnum] += delta; log(LOG_ERR, "sio%d: %u more %s%s (total %lu)\n", unit, delta, error_desc[errnum], delta == 1 ? "" : "s", total); } } } static void disc_optim(tp, t, com) struct tty *tp; struct termios *t; struct com_s *com; { if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON)) && (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK)) && (!(t->c_iflag & PARMRK) || (t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK)) && !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN)) && linesw[tp->t_line].l_rint == ttyinput) tp->t_state |= TS_CAN_BYPASS_L_RINT; else tp->t_state &= ~TS_CAN_BYPASS_L_RINT; com->hotchar = linesw[tp->t_line].l_hotchar; } /* * Following are all routines needed for SIO to act as console */ #include struct siocnstate { u_char dlbl; u_char dlbh; u_char ier; u_char cfcr; u_char mcr; }; static speed_t siocngetspeed __P((Port_t, struct speedtab *)); static void siocnclose __P((struct siocnstate *sp, Port_t iobase)); static void siocnopen __P((struct siocnstate *sp, Port_t iobase, int speed)); static void siocntxwait __P((Port_t iobase)); +#ifdef __i386__ +/* + * XXX: sciocnget() and sciocnputc() are not declared static, as they are + * referred to from i386/i386/i386-gdbstub.c. + */ +static cn_probe_t siocnprobe; +static cn_init_t siocninit; +static cn_checkc_t siocncheckc; + cn_getc_t siocngetc; + cn_putc_t siocnputc; + +CONS_DRIVER(sio, siocnprobe, siocninit, siocngetc, siocncheckc, siocnputc); + +#endif + static void siocntxwait(iobase) Port_t iobase; { int timo; /* * Wait for any pending transmission to finish. Required to avoid * the UART lockup bug when the speed is changed, and for normal * transmits. */ timo = 100000; while ((inb(iobase + com_lsr) & (LSR_TSRE | LSR_TXRDY)) != (LSR_TSRE | LSR_TXRDY) && --timo != 0) ; } /* * Read the serial port specified and try to figure out what speed * it's currently running at. We're assuming the serial port has * been initialized and is basicly idle. This routine is only intended * to be run at system startup. * * If the value read from the serial port doesn't make sense, return 0. */ static speed_t siocngetspeed(iobase, table) Port_t iobase; struct speedtab *table; { int code; u_char dlbh; u_char dlbl; u_char cfcr; cfcr = inb(iobase + com_cfcr); outb(iobase + com_cfcr, CFCR_DLAB | cfcr); dlbl = inb(iobase + com_dlbl); dlbh = inb(iobase + com_dlbh); outb(iobase + com_cfcr, cfcr); code = dlbh << 8 | dlbl; for ( ; table->sp_speed != -1; table++) if (table->sp_code == code) return (table->sp_speed); return 0; /* didn't match anything sane */ } static void siocnopen(sp, iobase, speed) struct siocnstate *sp; Port_t iobase; int speed; { int divisor; u_char dlbh; u_char dlbl; /* * Save all the device control registers except the fifo register * and set our default ones (cs8 -parenb speed=comdefaultrate). * We can't save the fifo register since it is read-only. */ sp->ier = inb(iobase + com_ier); outb(iobase + com_ier, 0); /* spltty() doesn't stop siointr() */ siocntxwait(iobase); sp->cfcr = inb(iobase + com_cfcr); outb(iobase + com_cfcr, CFCR_DLAB | CFCR_8BITS); sp->dlbl = inb(iobase + com_dlbl); sp->dlbh = inb(iobase + com_dlbh); /* * Only set the divisor registers if they would change, since on * some 16550 incompatibles (Startech), setting them clears the * data input register. This also reduces the effects of the * UMC8669F bug. */ divisor = ttspeedtab(speed, comspeedtab); dlbl = divisor & 0xFF; if (sp->dlbl != dlbl) outb(iobase + com_dlbl, dlbl); dlbh = (u_int) divisor >> 8; if (sp->dlbh != dlbh) outb(iobase + com_dlbh, dlbh); outb(iobase + com_cfcr, CFCR_8BITS); sp->mcr = inb(iobase + com_mcr); /* * We don't want interrupts, but must be careful not to "disable" * them by clearing the MCR_IENABLE bit, since that might cause * an interrupt by floating the IRQ line. */ outb(iobase + com_mcr, (sp->mcr & MCR_IENABLE) | MCR_DTR | MCR_RTS); } static void siocnclose(sp, iobase) struct siocnstate *sp; Port_t iobase; { /* * Restore the device control registers. */ siocntxwait(iobase); outb(iobase + com_cfcr, CFCR_DLAB | CFCR_8BITS); if (sp->dlbl != inb(iobase + com_dlbl)) outb(iobase + com_dlbl, sp->dlbl); if (sp->dlbh != inb(iobase + com_dlbh)) outb(iobase + com_dlbh, sp->dlbh); outb(iobase + com_cfcr, sp->cfcr); /* * XXX damp oscillations of MCR_DTR and MCR_RTS by not restoring them. */ outb(iobase + com_mcr, sp->mcr | MCR_DTR | MCR_RTS); outb(iobase + com_ier, sp->ier); } void siocnprobe(cp) struct consdev *cp; { -#if 0 speed_t boot_speed; u_char cfcr; - struct isa_device *dvp; - int s; + int s, unit; struct siocnstate sp; /* * Find our first enabled console, if any. If it is a high-level * console device, then initialize it and return successfully. * If it is a low-level console device, then initialize it and * return unsuccessfully. It must be initialized in both cases * for early use by console drivers and debuggers. Initializing * the hardware is not necessary in all cases, since the i/o * routines initialize it on the fly, but it is necessary if * input might arrive while the hardware is switched back to an * uninitialized state. We can't handle multiple console devices * yet because our low-level routines don't take a device arg. * We trust the user to set the console flags properly so that we * don't need to probe. */ cp->cn_pri = CN_DEAD; - for (dvp = isa_devtab_tty; dvp->id_driver != NULL; dvp++) - if (dvp->id_driver == &siodriver && dvp->id_enabled - && COM_CONSOLE(dvp)) { - siocniobase = dvp->id_iobase; + + for (unit = 0; unit < 16; unit++) { /* XXX need to know how many */ + int flags; + if (resource_int_value("sio", unit, "flags", &flags)) + continue; + if (COM_CONSOLE(flags)) { + int port; + if (resource_int_value("sio", unit, "port", &port)) + continue; + siocniobase = port; s = spltty(); if (boothowto & RB_SERIAL) { boot_speed = siocngetspeed(siocniobase, comspeedtab); if (boot_speed) comdefaultrate = boot_speed; } /* * Initialize the divisor latch. We can't rely on * siocnopen() to do this the first time, since it * avoids writing to the latch if the latch appears * to have the correct value. Also, if we didn't * just read the speed from the hardware, then we * need to set the speed in hardware so that * switching it later is null. */ cfcr = inb(siocniobase + com_cfcr); outb(siocniobase + com_cfcr, CFCR_DLAB | cfcr); outb(siocniobase + com_dlbl, COMBRD(comdefaultrate) & 0xff); outb(siocniobase + com_dlbh, (u_int) COMBRD(comdefaultrate) >> 8); outb(siocniobase + com_cfcr, cfcr); siocnopen(&sp, siocniobase, comdefaultrate); splx(s); - if (!COM_LLCONSOLE(dvp)) { - cp->cn_dev = makedev(CDEV_MAJOR, dvp->id_unit); - cp->cn_pri = COM_FORCECONSOLE(dvp) + if (!COM_LLCONSOLE(flags)) { + cp->cn_dev = makedev(CDEV_MAJOR, unit); + cp->cn_pri = COM_FORCECONSOLE(flags) || boothowto & RB_SERIAL ? CN_REMOTE : CN_NORMAL; } break; } -#endif + } } +#ifdef __alpha__ + struct consdev siocons = { NULL, NULL, siocngetc, siocncheckc, siocnputc, NULL, makedev(CDEV_MAJOR, 0), CN_NORMAL, }; extern struct consdev *cn_tab; int siocnattach(port, speed) int port; int speed; { int s; u_char cfcr; struct siocnstate sp; siocniobase = port; comdefaultrate = speed; s = spltty(); /* * Initialize the divisor latch. We can't rely on * siocnopen() to do this the first time, since it * avoids writing to the latch if the latch appears * to have the correct value. Also, if we didn't * just read the speed from the hardware, then we * need to set the speed in hardware so that * switching it later is null. */ cfcr = inb(siocniobase + com_cfcr); outb(siocniobase + com_cfcr, CFCR_DLAB | cfcr); outb(siocniobase + com_dlbl, COMBRD(comdefaultrate) & 0xff); outb(siocniobase + com_dlbh, (u_int) COMBRD(comdefaultrate) >> 8); outb(siocniobase + com_cfcr, cfcr); siocnopen(&sp, siocniobase, comdefaultrate); splx(s); cn_tab = &siocons; return 0; } int siogdbattach(port, speed) int port; int speed; { int s; u_char cfcr; struct siocnstate sp; siogdbiobase = port; gdbdefaultrate = speed; s = spltty(); /* * Initialize the divisor latch. We can't rely on * siocnopen() to do this the first time, since it * avoids writing to the latch if the latch appears * to have the correct value. Also, if we didn't * just read the speed from the hardware, then we * need to set the speed in hardware so that * switching it later is null. */ cfcr = inb(siogdbiobase + com_cfcr); outb(siogdbiobase + com_cfcr, CFCR_DLAB | cfcr); outb(siogdbiobase + com_dlbl, COMBRD(gdbdefaultrate) & 0xff); outb(siogdbiobase + com_dlbh, (u_int) COMBRD(gdbdefaultrate) >> 8); outb(siogdbiobase + com_cfcr, cfcr); siocnopen(&sp, siogdbiobase, gdbdefaultrate); splx(s); return 0; } +#endif + void siocninit(cp) struct consdev *cp; { comconsole = DEV_TO_UNIT(cp->cn_dev); } int siocncheckc(dev) dev_t dev; { int c; Port_t iobase; int s; struct siocnstate sp; iobase = siocniobase; s = spltty(); siocnopen(&sp, iobase, comdefaultrate); if (inb(iobase + com_lsr) & LSR_RXRDY) c = inb(iobase + com_data); else c = -1; siocnclose(&sp, iobase); splx(s); return (c); } int siocngetc(dev) dev_t dev; { int c; Port_t iobase; int s; struct siocnstate sp; iobase = siocniobase; s = spltty(); siocnopen(&sp, iobase, comdefaultrate); while (!(inb(iobase + com_lsr) & LSR_RXRDY)) ; c = inb(iobase + com_data); siocnclose(&sp, iobase); splx(s); return (c); } void siocnputc(dev, c) dev_t dev; int c; { int s; struct siocnstate sp; s = spltty(); siocnopen(&sp, siocniobase, comdefaultrate); siocntxwait(siocniobase); outb(siocniobase + com_data, c); siocnclose(&sp, siocniobase); splx(s); } int siogdbgetc() { int c; Port_t iobase; int s; struct siocnstate sp; iobase = siogdbiobase; s = spltty(); siocnopen(&sp, iobase, gdbdefaultrate); while (!(inb(iobase + com_lsr) & LSR_RXRDY)) ; c = inb(iobase + com_data); siocnclose(&sp, iobase); splx(s); return (c); } void siogdbputc(c) int c; { int s; struct siocnstate sp; s = spltty(); siocnopen(&sp, siogdbiobase, gdbdefaultrate); siocntxwait(siogdbiobase); outb(siogdbiobase + com_data, c); siocnclose(&sp, siogdbiobase); splx(s); } #ifdef DSI_SOFT_MODEM /* * The magic code to download microcode to a "Connection 14.4+Fax" * modem from Digicom Systems Inc. Very magic. */ #define DSI_ERROR(str) { ptr = str; goto error; } static int LoadSoftModem(int unit, int base_io, u_long size, u_char *ptr) { int int_c,int_k; int data_0188, data_0187; /* * First see if it is a DSI SoftModem */ if(!((inb(base_io+7) ^ inb(base_io+7)) & 0x80)) return ENODEV; data_0188 = inb(base_io+4); data_0187 = inb(base_io+3); outb(base_io+3,0x80); outb(base_io+4,0x0C); outb(base_io+0,0x31); outb(base_io+1,0x8C); outb(base_io+7,0x10); outb(base_io+7,0x19); if(0x18 != (inb(base_io+7) & 0x1A)) DSI_ERROR("dsp bus not granted"); if(0x01 != (inb(base_io+7) & 0x01)) { outb(base_io+7,0x18); outb(base_io+7,0x19); if(0x01 != (inb(base_io+7) & 0x01)) DSI_ERROR("program mem not granted"); } int_c = 0; while(1) { if(int_c >= 7 || size <= 0x1800) break; for(int_k = 0 ; int_k < 0x800; int_k++) { outb(base_io+0,*ptr++); outb(base_io+1,*ptr++); outb(base_io+2,*ptr++); } size -= 0x1800; int_c++; } if(size > 0x1800) { outb(base_io+7,0x18); outb(base_io+7,0x19); if(0x00 != (inb(base_io+7) & 0x01)) DSI_ERROR("program data not granted"); for(int_k = 0 ; int_k < 0x800; int_k++) { outb(base_io+1,*ptr++); outb(base_io+2,0); outb(base_io+1,*ptr++); outb(base_io+2,*ptr++); } size -= 0x1800; while(size > 0x1800) { for(int_k = 0 ; int_k < 0xC00; int_k++) { outb(base_io+1,*ptr++); outb(base_io+2,*ptr++); } size -= 0x1800; } if(size < 0x1800) { for(int_k=0;int_k 0) { if(int_c == 7) { outb(base_io+7,0x18); outb(base_io+7,0x19); if(0x00 != (inb(base_io+7) & 0x01)) DSI_ERROR("program data not granted"); for(int_k = 0 ; int_k < size/3; int_k++) { outb(base_io+1,*ptr++); outb(base_io+2,0); outb(base_io+1,*ptr++); outb(base_io+2,*ptr++); } } else { for(int_k = 0 ; int_k < size/3; int_k++) { outb(base_io+0,*ptr++); outb(base_io+1,*ptr++); outb(base_io+2,*ptr++); } } } outb(base_io+7,0x11); outb(base_io+7,3); outb(base_io+4,data_0188 & 0xfb); outb(base_io+3,data_0187); return 0; error: printf("sio%d: DSI SoftModem microcode load failed: <%s>\n",unit,ptr); outb(base_io+7,0x00); \ outb(base_io+3,data_0187); \ outb(base_io+4,data_0188); \ return EIO; } #endif /* DSI_SOFT_MODEM */ /* * support PnP cards if we are using 'em */ #if NPNP > 0 static pnpid_t siopnp_ids[] = { { 0x5015f435, "MOT1550"}, { 0x8113b04e, "Supra1381"}, { 0x9012b04e, "Supra1290"}, { 0x7121b04e, "SupraExpress 56i Sp"}, { 0x11007256, "USR0011"}, { 0x30207256, "USR2030"}, { 0x31307256, "USR3031"}, { 0 } }; static char *siopnp_probe(u_long csn, u_long vend_id); static void siopnp_attach(u_long csn, u_long vend_id, char *name, struct isa_device *dev); static u_long nsiopnp = NSIO; static struct pnp_device siopnp = { "siopnp", siopnp_probe, siopnp_attach, &nsiopnp, &tty_imask }; DATA_SET (pnpdevice_set, siopnp); static char * siopnp_probe(u_long csn, u_long vend_id) { pnpid_t *id; char *s = NULL; for(id = siopnp_ids; id->vend_id != 0; id++) { if (vend_id == id->vend_id) { s = id->id_str; break; } } if (s) { struct pnp_cinfo d; read_pnp_parms(&d, 0); if (d.enable == 0 || d.flags & 1) { printf("CSN %lu is disabled.\n", csn); return (NULL); } } return (s); } static void siopnp_attach(u_long csn, u_long vend_id, char *name, struct isa_device *dev) { struct pnp_cinfo d; - struct isa_device *dvp; if (dev->id_unit >= NSIOTOT) return; if (read_pnp_parms(&d, 0) == 0) { printf("failed to read pnp parms\n"); return; } write_pnp_parms(&d, 0); enable_pnp_card(); dev->id_iobase = d.port[0]; dev->id_irq = (1 << d.irq[0]); dev->id_intr = siointr; dev->id_ri_flags = RI_FAST; dev->id_drq = -1; if (dev->id_driver == NULL) { dev->id_driver = &siodriver; - dvp = find_isadev(isa_devtab_tty, &siodriver, 0); - if (dvp != NULL) - dev->id_id = dvp->id_id; + dev->id_id = isa_compat_nextid(); } if ((dev->id_alive = sioprobe(dev)) != 0) sioattach(dev); else printf("sio%d: probe failed\n", dev->id_unit); } #endif CDEV_DRIVER_MODULE(sio, isa, sio_driver, sio_devclass, CDEV_MAJOR, sio_cdevsw, 0, 0); Index: head/sys/isa/syscons_isa.c =================================================================== --- head/sys/isa/syscons_isa.c (revision 45719) +++ head/sys/isa/syscons_isa.c (revision 45720) @@ -1,80 +1,83 @@ /*- * Copyright (c) 1999 Kazutaka YOKOTA * 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 as * the first lines of this file unmodified. * 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 AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS BE LIABLE 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. * - * $Id:$ + * $Id: syscons_isa.c,v 1.1 1999/01/23 16:53:30 dfr Exp $ */ #include "sc.h" #include "opt_syscons.h" #if NSC > 0 #include #include #include #include #include #include +#ifdef __i386__ +#include +#endif #include #include #include devclass_t sc_devclass; static int scprobe(device_t dev); static int scattach(device_t dev); static device_method_t sc_methods[] = { DEVMETHOD(device_probe, scprobe), DEVMETHOD(device_attach, scattach), { 0, 0 } }; static driver_t sc_driver = { "sc", sc_methods, DRIVER_TYPE_TTY, 1, /* XXX */ }; static int scprobe(device_t dev) { device_set_desc(dev, "System console"); return sc_probe_unit(device_get_unit(dev), isa_get_flags(dev)); } static int scattach(device_t dev) { return sc_attach_unit(device_get_unit(dev), isa_get_flags(dev)); } DRIVER_MODULE(sc, isa, sc_driver, sc_devclass, 0, 0); #endif /* NSC > 0 */ Index: head/sys/isa/vga_isa.c =================================================================== --- head/sys/isa/vga_isa.c (revision 45719) +++ head/sys/isa/vga_isa.c (revision 45720) @@ -1,2234 +1,2239 @@ /*- * Copyright (c) 1999 Kazutaka YOKOTA * Copyright (c) 1992-1998 Søren Schmidt * 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 as * the first lines of this file unmodified. * 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. 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 AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS BE LIABLE 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. * - * $Id: vga_isa.c,v 1.2 1999/02/05 11:52:08 yokota Exp $ + * $Id: vga_isa.c,v 1.3 1999/02/05 12:58:32 yokota Exp $ */ #include "vga.h" #include "opt_vga.h" #include "opt_fb.h" #include "opt_syscons.h" /* should be removed in the future, XXX */ #if NVGA > 0 #include #include #include #include #include #include #include #include #include #include #include #include -#ifndef __i386__ +#if 1 #include #include #else #include #include #endif #define DRIVER_NAME "vga" /* cdev driver declaration */ #define ISAVGA_UNIT(dev) minor(dev) #define ISAVGA_MKMINOR(unit) (unit) typedef struct isavga_softc { video_adapter_t *adp; } isavga_softc_t; -#ifndef __i386__ +#if 1 #define ISAVGA_SOFTC(unit) \ ((isavga_softc_t *)devclass_get_softc(isavga_devclass, unit)) devclass_t isavga_devclass; static int isavga_probe(device_t dev); static int isavga_attach(device_t dev); static device_method_t isavga_methods[] = { DEVMETHOD(device_probe, isavga_probe), DEVMETHOD(device_attach, isavga_attach), { 0, 0 } }; static driver_t isavga_driver = { DRIVER_NAME, isavga_methods, DRIVER_TYPE_TTY, sizeof(isavga_softc_t), }; DRIVER_MODULE(vga, isa, isavga_driver, isavga_devclass, 0, 0); #else /* __i386__ */ #define ISAVGA_SOFTC(unit) (isavga_softc[unit]) static isavga_softc_t *isavga_softc[NVGA]; static int isavga_probe(struct isa_device *dev); static int isavga_attach(struct isa_device *dev); struct isa_driver vgadriver = { isavga_probe, isavga_attach, DRIVER_NAME, 0, }; #endif /* __i386__ */ static int isavga_probe_unit(int unit, isavga_softc_t *sc, int flags); static int isavga_attach_unit(int unit, isavga_softc_t *sc, int flags); #ifdef FB_INSTALL_CDEV static d_open_t isavgaopen; static d_close_t isavgaclose; static d_read_t isavgaread; static d_ioctl_t isavgaioctl; static struct cdevsw vga_cdevsw = { isavgaopen, isavgaclose, noread, nowrite, /* ?? */ isavgaioctl, nostop, nullreset, nodevtotty, seltrue, nommap, NULL, DRIVER_NAME, NULL, -1, nodump, nopsize, }; #endif /* FB_INSTALL_CDEV */ -#ifndef __i386__ +#if 1 static int isavga_probe(device_t dev) { isavga_softc_t *sc; device_set_desc(dev, "Generic ISA VGA"); sc = device_get_softc(dev); return isavga_probe_unit(device_get_unit(dev), sc, isa_get_flags(dev)); } static int isavga_attach(device_t dev) { isavga_softc_t *sc; sc = device_get_softc(dev); return isavga_attach_unit(device_get_unit(dev), sc, isa_get_flags(dev)); } #else /* __i386__ */ static int isavga_probe(struct isa_device *dev) { isavga_softc_t *sc; int error; if (dev->id_unit >= sizeof(isavga_softc)/sizeof(isavga_softc[0])) return 0; sc = isavga_softc[dev->id_unit] = malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT); if (sc == NULL) return 0; error = isavga_probe_unit(dev->id_unit, sc, dev->id_flags); if (error) { isavga_softc[dev->id_unit] = NULL; free(sc, M_DEVBUF); return 0; } dev->id_iobase = sc->adp->va_io_base; dev->id_maddr = (caddr_t)BIOS_PADDRTOVADDR(sc->adp->va_mem_base); dev->id_msize = sc->adp->va_mem_size; return sc->adp->va_io_size; } static int isavga_attach(struct isa_device *dev) { isavga_softc_t *sc; if (dev->id_unit >= sizeof(isavga_softc)/sizeof(isavga_softc[0])) return 0; sc = isavga_softc[dev->id_unit]; if (sc == NULL) return 0; return ((isavga_attach_unit(dev->id_unit, sc, dev->id_flags)) ? 0 : 1); } #endif /* __i386__ */ static int isavga_probe_unit(int unit, isavga_softc_t *sc, int flags) { video_switch_t *sw; bzero(sc, sizeof(*sc)); sw = vid_get_switch(DRIVER_NAME); if (sw == NULL) return 0; return (*sw->probe)(unit, &sc->adp, NULL, flags); } static int isavga_attach_unit(int unit, isavga_softc_t *sc, int flags) { video_switch_t *sw; int error; sw = vid_get_switch(DRIVER_NAME); if (sw == NULL) return ENXIO; error = (*sw->init)(unit, sc->adp, flags); if (error) return ENXIO; #ifdef FB_INSTALL_CDEV /* attach a virtual frame buffer device */ error = fb_attach(makedev(0, ISAVGA_MKMINOR(unit)), scp->adp, &vga_cdevsw); if (error) return error; #endif /* FB_INSTALL_CDEV */ if (bootverbose) (*vidsw[sc->adp->va_index]->diag)(sc->adp, bootverbose); return 0; } /* LOW-LEVEL */ #include #include #define probe_done(adp) ((adp)->va_flags & V_ADP_PROBED) #define init_done(adp) ((adp)->va_flags & V_ADP_INITIALIZED) #define config_done(adp) ((adp)->va_flags & V_ADP_REGISTERED) /* for compatibility with old kernel options */ #ifdef SC_ALT_SEQACCESS #undef SC_ALT_SEQACCESS #undef VGA_ALT_SEQACCESS #define VGA_ALT_SEQACCESS 1 #endif #ifdef SLOW_VGA #undef SLOW_VGA #undef VGA_SLOW_IOACCESS #define VGA_SLOW_IOACCESS 1 #endif /* architecture dependent option */ #ifdef __alpha__ #define VGA_NO_BIOS 1 #endif /* this should really be in `rtc.h' */ #define RTC_EQUIPMENT 0x14 /* various sizes */ #define V_MODE_MAP_SIZE (M_VGA_CG320 + 1) #define V_MODE_PARAM_SIZE 64 /* video adapter state buffer */ struct adp_state { int sig; #define V_STATE_SIG 0x736f6962 u_char regs[V_MODE_PARAM_SIZE]; }; typedef struct adp_state adp_state_t; /* video adapter information */ #define DCC_MONO 0 #define DCC_CGA40 1 #define DCC_CGA80 2 #define DCC_EGAMONO 3 #define DCC_EGA40 4 #define DCC_EGA80 5 /* * NOTE: `va_window' should have a virtual address, but is initialized * with a physical address in the following table, as verify_adapter() * will perform address conversion at run-time. */ static video_adapter_t adapter_init_value[] = { /* DCC_MONO */ { 0, KD_MONO, "mda", 0, 0, 0, IO_MDA, IO_MDASIZE, MONO_CRTC, MDA_BUF_BASE, MDA_BUF_SIZE, MDA_BUF_BASE, MDA_BUF_SIZE, MDA_BUF_SIZE, 0, 0, 0, 7, 0, }, /* DCC_CGA40 */ { 0, KD_CGA, "cga", 0, 0, V_ADP_COLOR, IO_CGA, IO_CGASIZE, COLOR_CRTC, CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0, 0, 3, 0, }, /* DCC_CGA80 */ { 0, KD_CGA, "cga", 0, 0, V_ADP_COLOR, IO_CGA, IO_CGASIZE, COLOR_CRTC, CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0, 0, 3, 0, }, /* DCC_EGAMONO */ { 0, KD_EGA, "ega", 0, 0, 0, IO_MDA, 48, MONO_CRTC, EGA_BUF_BASE, EGA_BUF_SIZE, MDA_BUF_BASE, MDA_BUF_SIZE, MDA_BUF_SIZE, 0, 0, 0, 7, 0, }, /* DCC_EGA40 */ { 0, KD_EGA, "ega", 0, 0, V_ADP_COLOR, IO_MDA, 48, COLOR_CRTC, EGA_BUF_BASE, EGA_BUF_SIZE, CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0, 0, 3, 0, }, /* DCC_EGA80 */ { 0, KD_EGA, "ega", 0, 0, V_ADP_COLOR, IO_MDA, 48, COLOR_CRTC, EGA_BUF_BASE, EGA_BUF_SIZE, CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0, 0, 3, 0, }, }; static video_adapter_t biosadapter[2]; static int biosadapters = 0; /* video driver declarations */ static int vga_configure(int flags); int (*vga_sub_configure)(int flags); static int vga_nop(void); static vi_probe_t vga_probe; static vi_init_t vga_init; static vi_get_info_t vga_get_info; static vi_query_mode_t vga_query_mode; static vi_set_mode_t vga_set_mode; static vi_save_font_t vga_save_font; static vi_load_font_t vga_load_font; static vi_show_font_t vga_show_font; static vi_save_palette_t vga_save_palette; static vi_load_palette_t vga_load_palette; static vi_set_border_t vga_set_border; static vi_save_state_t vga_save_state; static vi_load_state_t vga_load_state; static vi_set_win_org_t vga_set_origin; static vi_read_hw_cursor_t vga_read_hw_cursor; static vi_set_hw_cursor_t vga_set_hw_cursor; static vi_set_hw_cursor_shape_t vga_set_hw_cursor_shape; static vi_mmap_t vga_mmap; static vi_diag_t vga_diag; static video_switch_t vgavidsw = { vga_probe, vga_init, vga_get_info, vga_query_mode, vga_set_mode, vga_save_font, vga_load_font, vga_show_font, vga_save_palette, vga_load_palette, vga_set_border, vga_save_state, vga_load_state, vga_set_origin, vga_read_hw_cursor, vga_set_hw_cursor, vga_set_hw_cursor_shape, (vi_blank_display_t *)vga_nop, vga_mmap, vga_diag, }; VIDEO_DRIVER(mda, vgavidsw, NULL); VIDEO_DRIVER(cga, vgavidsw, NULL); VIDEO_DRIVER(ega, vgavidsw, NULL); VIDEO_DRIVER(vga, vgavidsw, vga_configure); /* VGA BIOS standard video modes */ #define EOT (-1) #define NA (-2) static video_info_t bios_vmode[] = { /* CGA */ { M_B40x25, V_INFO_COLOR, 40, 25, 8, 8, 2, 1, CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0 }, { M_C40x25, V_INFO_COLOR, 40, 25, 8, 8, 4, 1, CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0 }, { M_B80x25, V_INFO_COLOR, 80, 25, 8, 8, 2, 1, CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0 }, { M_C80x25, V_INFO_COLOR, 80, 25, 8, 8, 4, 1, CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0 }, /* EGA */ { M_ENH_B40x25, V_INFO_COLOR, 40, 25, 8, 14, 2, 1, CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0 }, { M_ENH_C40x25, V_INFO_COLOR, 40, 25, 8, 14, 4, 1, CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0 }, { M_ENH_B80x25, V_INFO_COLOR, 80, 25, 8, 14, 2, 1, CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0 }, { M_ENH_C80x25, V_INFO_COLOR, 80, 25, 8, 14, 4, 1, CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0 }, /* VGA */ { M_VGA_C40x25, V_INFO_COLOR, 40, 25, 8, 16, 4, 1, CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0 }, { M_VGA_M80x25, 0, 80, 25, 8, 16, 2, 1, MDA_BUF_BASE, MDA_BUF_SIZE, MDA_BUF_SIZE, 0, 0 }, { M_VGA_C80x25, V_INFO_COLOR, 80, 25, 8, 16, 4, 1, CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0 }, /* MDA */ { M_EGAMONO80x25, 0, 80, 25, 8, 14, 2, 1, MDA_BUF_BASE, MDA_BUF_SIZE, MDA_BUF_SIZE, 0, 0 }, /* EGA */ { M_ENH_B80x43, V_INFO_COLOR, 80, 43, 8, 8, 2, 1, CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0 }, { M_ENH_C80x43, V_INFO_COLOR, 80, 43, 8, 8, 4, 1, CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0 }, /* VGA */ { M_VGA_M80x30, 0, 80, 30, 8, 16, 2, 1, MDA_BUF_BASE, MDA_BUF_SIZE, MDA_BUF_SIZE, 0, 0 }, { M_VGA_C80x30, V_INFO_COLOR, 80, 30, 8, 16, 4, 1, CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0 }, { M_VGA_M80x50, 0, 80, 50, 8, 8, 2, 1, MDA_BUF_BASE, MDA_BUF_SIZE, MDA_BUF_SIZE, 0, 0 }, { M_VGA_C80x50, V_INFO_COLOR, 80, 50, 8, 8, 4, 1, CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0 }, { M_VGA_M80x60, 0, 80, 60, 8, 8, 2, 1, MDA_BUF_BASE, MDA_BUF_SIZE, MDA_BUF_SIZE, 0, 0 }, { M_VGA_C80x60, V_INFO_COLOR, 80, 60, 8, 8, 4, 1, CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0 }, #ifndef VGA_NO_MODE_CHANGE /* CGA */ { M_BG320, V_INFO_COLOR | V_INFO_GRAPHICS, 320, 200, 8, 8, 2, 1, CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0 }, { M_CG320, V_INFO_COLOR | V_INFO_GRAPHICS, 320, 200, 8, 8, 2, 1, CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0 }, { M_BG640, V_INFO_COLOR | V_INFO_GRAPHICS, 640, 200, 8, 8, 1, 1, CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0 }, /* EGA */ { M_CG320_D, V_INFO_COLOR | V_INFO_GRAPHICS, 320, 200, 8, 8, 4, 4, GRAPHICS_BUF_BASE, GRAPHICS_BUF_SIZE, GRAPHICS_BUF_SIZE, 0, 0 }, { M_CG640_E, V_INFO_COLOR | V_INFO_GRAPHICS, 640, 200, 8, 8, 4, 4, GRAPHICS_BUF_BASE, GRAPHICS_BUF_SIZE, GRAPHICS_BUF_SIZE, 0, 0 }, { M_EGAMONOAPA, V_INFO_GRAPHICS, 640, 350, 8, 14, 4, 4, GRAPHICS_BUF_BASE, GRAPHICS_BUF_SIZE, 64*1024, 0, 0 }, { M_ENHMONOAPA2,V_INFO_GRAPHICS, 640, 350, 8, 14, 4, 4, GRAPHICS_BUF_BASE, GRAPHICS_BUF_SIZE, GRAPHICS_BUF_SIZE, 0, 0 }, { M_CG640x350, V_INFO_COLOR | V_INFO_GRAPHICS, 640, 350, 8, 14, 2, 2, GRAPHICS_BUF_BASE, GRAPHICS_BUF_SIZE, GRAPHICS_BUF_SIZE, 0, 0 }, { M_ENH_CG640, V_INFO_COLOR | V_INFO_GRAPHICS, 640, 350, 8, 14, 4, 4, GRAPHICS_BUF_BASE, GRAPHICS_BUF_SIZE, GRAPHICS_BUF_SIZE, 0, 0 }, /* VGA */ { M_BG640x480, V_INFO_COLOR | V_INFO_GRAPHICS, 640, 480, 8, 16, 4, 4, GRAPHICS_BUF_BASE, GRAPHICS_BUF_SIZE, GRAPHICS_BUF_SIZE, 0, 0 }, { M_CG640x480, V_INFO_COLOR | V_INFO_GRAPHICS, 640, 480, 8, 16, 4, 4, GRAPHICS_BUF_BASE, GRAPHICS_BUF_SIZE, GRAPHICS_BUF_SIZE, 0, 0 }, { M_VGA_CG320, V_INFO_COLOR | V_INFO_GRAPHICS, 320, 200, 8, 8, 8, 1, GRAPHICS_BUF_BASE, GRAPHICS_BUF_SIZE, GRAPHICS_BUF_SIZE, 0, 0 }, { M_VGA_MODEX, V_INFO_COLOR | V_INFO_GRAPHICS, 320, 240, 8, 8, 8, 1, GRAPHICS_BUF_BASE, GRAPHICS_BUF_SIZE, GRAPHICS_BUF_SIZE, 0, 0 }, #endif /* VGA_NO_MODE_CHANGE */ { EOT }, }; static int init_done = FALSE; static u_char *video_mode_ptr = NULL; /* EGA/VGA */ static u_char *video_mode_ptr2 = NULL; /* CGA/MDA */ static u_char *mode_map[V_MODE_MAP_SIZE]; static adp_state_t adpstate; static adp_state_t adpstate2; static int rows_offset = 1; /* local macros and functions */ #define BIOS_SADDRTOLADDR(p) ((((p) & 0xffff0000) >> 12) + ((p) & 0x0000ffff)) #if !defined(VGA_NO_BIOS) && !defined(VGA_NO_MODE_CHANGE) static void map_mode_table(u_char *map[], u_char *table, int max); #endif static void clear_mode_map(video_adapter_t *adp, u_char *map[], int max, int color); #if !defined(VGA_NO_BIOS) && !defined(VGA_NO_MODE_CHANGE) static int map_mode_num(int mode); #endif static int map_gen_mode_num(int type, int color, int mode); static int map_bios_mode_num(int type, int color, int bios_mode); static u_char *get_mode_param(int mode); #ifndef VGA_NO_BIOS static void fill_adapter_param(int code, video_adapter_t *adp); #endif static int verify_adapter(video_adapter_t *adp); static void update_adapter_info(video_adapter_t *adp, video_info_t *info); #if !defined(VGA_NO_BIOS) && !defined(VGA_NO_MODE_CHANGE) #define COMP_IDENTICAL 0 #define COMP_SIMILAR 1 #define COMP_DIFFERENT 2 static int comp_adpregs(u_char *buf1, u_char *buf2); #endif static int probe_adapters(void); #define PARAM_BUFSIZE 6 static void set_font_mode(video_adapter_t *adp, u_char *buf); static void set_normal_mode(video_adapter_t *adp, u_char *buf); static void dump_buffer(u_char *buf, size_t len); #define ISMAPPED(pa, width) \ (((pa) <= (u_long)0x1000 - (width)) \ || ((pa) >= ISA_HOLE_START && (pa) <= 0x100000 - (width))) #define prologue(adp, flag, err) \ if (!init_done || !((adp)->va_flags & (flag))) \ return (err) /* a backdoor for the console driver */ static int vga_configure(int flags) { int i; probe_adapters(); for (i = 0; i < biosadapters; ++i) { if (!probe_done(&biosadapter[i])) continue; biosadapter[i].va_flags |= V_ADP_INITIALIZED; if (!config_done(&biosadapter[i])) { if (vid_register(&biosadapter[i]) < 0) continue; biosadapter[i].va_flags |= V_ADP_REGISTERED; } } if (vga_sub_configure != NULL) (*vga_sub_configure)(flags); return biosadapters; } /* local subroutines */ #if !defined(VGA_NO_BIOS) && !defined(VGA_NO_MODE_CHANGE) /* construct the mode parameter map */ static void map_mode_table(u_char *map[], u_char *table, int max) { int i; for(i = 0; i < max; ++i) map[i] = table + i*V_MODE_PARAM_SIZE; for(; i < V_MODE_MAP_SIZE; ++i) map[i] = NULL; } #endif /* !VGA_NO_BIOS && !VGA_NO_MODE_CHANGE */ static void clear_mode_map(video_adapter_t *adp, u_char *map[], int max, int color) { video_info_t info; int i; /* * NOTE: we don't touch `bios_vmode[]' because it is shared * by all adapters. */ for(i = 0; i < max; ++i) { if (vga_get_info(adp, i, &info)) continue; if ((info.vi_flags & V_INFO_COLOR) != color) map[i] = NULL; } } #if !defined(VGA_NO_BIOS) && !defined(VGA_NO_MODE_CHANGE) /* map the non-standard video mode to a known mode number */ static int map_mode_num(int mode) { static struct { int from; int to; } mode_map[] = { { M_ENH_B80x43, M_ENH_B80x25 }, { M_ENH_C80x43, M_ENH_C80x25 }, { M_VGA_M80x30, M_VGA_M80x25 }, { M_VGA_C80x30, M_VGA_C80x25 }, { M_VGA_M80x50, M_VGA_M80x25 }, { M_VGA_C80x50, M_VGA_C80x25 }, { M_VGA_M80x60, M_VGA_M80x25 }, { M_VGA_C80x60, M_VGA_C80x25 }, { M_VGA_MODEX, M_VGA_CG320 }, }; int i; for (i = 0; i < sizeof(mode_map)/sizeof(mode_map[0]); ++i) { if (mode_map[i].from == mode) return mode_map[i].to; } return mode; } #endif /* !VGA_NO_BIOS && !VGA_NO_MODE_CHANGE */ /* map a generic video mode to a known mode number */ static int map_gen_mode_num(int type, int color, int mode) { static struct { int from; int to_color; int to_mono; } mode_map[] = { { M_TEXT_80x30, M_VGA_C80x30, M_VGA_M80x30, }, { M_TEXT_80x43, M_ENH_C80x43, M_ENH_B80x43, }, { M_TEXT_80x50, M_VGA_C80x50, M_VGA_M80x50, }, { M_TEXT_80x60, M_VGA_C80x60, M_VGA_M80x60, }, }; int i; if (mode == M_TEXT_80x25) { switch (type) { case KD_VGA: if (color) return M_VGA_C80x25; else return M_VGA_M80x25; break; case KD_EGA: if (color) return M_ENH_C80x25; else return M_EGAMONO80x25; break; case KD_CGA: return M_C80x25; case KD_MONO: case KD_HERCULES: return M_EGAMONO80x25; /* XXX: this name is confusing */ default: return -1; } } for (i = 0; i < sizeof(mode_map)/sizeof(mode_map[0]); ++i) { if (mode_map[i].from == mode) return ((color) ? mode_map[i].to_color : mode_map[i].to_mono); } return mode; } /* turn the BIOS video number into our video mode number */ static int map_bios_mode_num(int type, int color, int bios_mode) { static int cga_modes[7] = { M_B40x25, M_C40x25, /* 0, 1 */ M_B80x25, M_C80x25, /* 2, 3 */ M_BG320, M_CG320, M_BG640, }; static int ega_modes[17] = { M_ENH_B40x25, M_ENH_C40x25, /* 0, 1 */ M_ENH_B80x25, M_ENH_C80x25, /* 2, 3 */ M_BG320, M_CG320, M_BG640, M_EGAMONO80x25, /* 7 */ 8, 9, 10, 11, 12, M_CG320_D, M_CG640_E, M_ENHMONOAPA2, /* XXX: video momery > 64K */ M_ENH_CG640, /* XXX: video momery > 64K */ }; static int vga_modes[20] = { M_VGA_C40x25, M_VGA_C40x25, /* 0, 1 */ M_VGA_C80x25, M_VGA_C80x25, /* 2, 3 */ M_BG320, M_CG320, M_BG640, M_VGA_M80x25, /* 7 */ 8, 9, 10, 11, 12, M_CG320_D, M_CG640_E, M_ENHMONOAPA2, M_ENH_CG640, M_BG640x480, M_CG640x480, M_VGA_CG320, }; switch (type) { case KD_VGA: if (bios_mode < sizeof(vga_modes)/sizeof(vga_modes[0])) return vga_modes[bios_mode]; else if (color) return M_VGA_C80x25; else return M_VGA_M80x25; break; case KD_EGA: if (bios_mode < sizeof(ega_modes)/sizeof(ega_modes[0])) return ega_modes[bios_mode]; else if (color) return M_ENH_C80x25; else return M_EGAMONO80x25; break; case KD_CGA: if (bios_mode < sizeof(cga_modes)/sizeof(cga_modes[0])) return cga_modes[bios_mode]; else return M_C80x25; break; case KD_MONO: case KD_HERCULES: return M_EGAMONO80x25; /* XXX: this name is confusing */ default: break; } return -1; } /* look up a parameter table entry */ static u_char *get_mode_param(int mode) { #if !defined(VGA_NO_BIOS) && !defined(VGA_NO_MODE_CHANGE) if (mode >= V_MODE_MAP_SIZE) mode = map_mode_num(mode); #endif if ((mode >= 0) && (mode < V_MODE_MAP_SIZE)) return mode_map[mode]; else return NULL; } #ifndef VGA_NO_BIOS static void fill_adapter_param(int code, video_adapter_t *adp) { static struct { int primary; int secondary; } dcc[] = { { DCC_MONO, DCC_EGA40 /* CGA monitor */ }, { DCC_MONO, DCC_EGA80 /* CGA monitor */ }, { DCC_MONO, DCC_EGA80 /* CGA emulation */ }, { DCC_MONO, DCC_EGA80 }, { DCC_CGA40, DCC_EGAMONO }, { DCC_CGA80, DCC_EGAMONO }, { DCC_EGA40 /* CGA monitor */, DCC_MONO}, { DCC_EGA80 /* CGA monitor */, DCC_MONO}, { DCC_EGA80 /* CGA emulation */,DCC_MONO }, { DCC_EGA80, DCC_MONO }, { DCC_EGAMONO, DCC_CGA40 }, { DCC_EGAMONO, DCC_CGA40 }, }; if ((code < 0) || (code >= sizeof(dcc)/sizeof(dcc[0]))) { adp[V_ADP_PRIMARY] = adapter_init_value[DCC_MONO]; adp[V_ADP_SECONDARY] = adapter_init_value[DCC_CGA80]; } else { adp[V_ADP_PRIMARY] = adapter_init_value[dcc[code].primary]; adp[V_ADP_SECONDARY] = adapter_init_value[dcc[code].secondary]; } } #endif /* VGA_NO_BIOS */ static int verify_adapter(video_adapter_t *adp) { vm_offset_t buf; u_int16_t v; +#if !defined(VGA_NO_BIOS) && !defined(VGA_NO_MODE_CHANGE) + u_int32_t p; +#endif buf = BIOS_PADDRTOVADDR(adp->va_window); v = readw(buf); writew(buf, 0xA55A); if (readw(buf) != 0xA55A) return 1; writew(buf, v); switch (adp->va_type) { case KD_EGA: outb(adp->va_crtc_addr, 7); if (inb(adp->va_crtc_addr) == 7) { adp->va_type = KD_VGA; adp->va_name = "vga"; adp->va_flags |= V_ADP_STATESAVE | V_ADP_PALETTE; } adp->va_flags |= V_ADP_STATELOAD | V_ADP_BORDER; /* the color adapter may be in the 40x25 mode... XXX */ #if !defined(VGA_NO_BIOS) && !defined(VGA_NO_MODE_CHANGE) /* get the BIOS video mode pointer */ p = *(u_int32_t *)BIOS_PADDRTOVADDR(0x4a8); p = BIOS_SADDRTOLADDR(p); if (ISMAPPED(p, sizeof(u_int32_t))) { p = *(u_int32_t *)BIOS_PADDRTOVADDR(p); p = BIOS_SADDRTOLADDR(p); if (ISMAPPED(p, V_MODE_PARAM_SIZE)) video_mode_ptr = (u_char *)BIOS_PADDRTOVADDR(p); } #endif break; case KD_CGA: adp->va_flags |= V_ADP_COLOR | V_ADP_BORDER; /* may be in the 40x25 mode... XXX */ #if !defined(VGA_NO_BIOS) && !defined(VGA_NO_MODE_CHANGE) /* get the BIOS video mode pointer */ p = *(u_int32_t *)BIOS_PADDRTOVADDR(0x1d*4); p = BIOS_SADDRTOLADDR(p); video_mode_ptr2 = (u_char *)BIOS_PADDRTOVADDR(p); #endif break; case KD_MONO: #if !defined(VGA_NO_BIOS) && !defined(VGA_NO_MODE_CHANGE) /* get the BIOS video mode pointer */ p = *(u_int32_t *)BIOS_PADDRTOVADDR(0x1d*4); p = BIOS_SADDRTOLADDR(p); video_mode_ptr2 = (u_char *)BIOS_PADDRTOVADDR(p); #endif break; } return 0; } static void update_adapter_info(video_adapter_t *adp, video_info_t *info) { adp->va_flags &= ~V_ADP_COLOR; adp->va_flags |= (info->vi_flags & V_INFO_COLOR) ? V_ADP_COLOR : 0; adp->va_crtc_addr = (adp->va_flags & V_ADP_COLOR) ? COLOR_CRTC : MONO_CRTC; adp->va_window = BIOS_PADDRTOVADDR(info->vi_window); adp->va_window_size = info->vi_window_size; adp->va_window_gran = info->vi_window_gran; if (info->vi_buffer_size == 0) { adp->va_buffer = 0; adp->va_buffer_size = 0; } else { adp->va_buffer = BIOS_PADDRTOVADDR(info->vi_buffer); adp->va_buffer_size = info->vi_buffer_size; } if (info->vi_flags & V_INFO_GRAPHICS) { switch (info->vi_depth/info->vi_planes) { case 1: adp->va_line_width = info->vi_width/8; break; case 2: adp->va_line_width = info->vi_width/4; break; case 4: adp->va_line_width = info->vi_width/2; break; case 8: default: /* shouldn't happen */ adp->va_line_width = info->vi_width; break; } } else { adp->va_line_width = info->vi_width; } bcopy(info, &adp->va_info, sizeof(adp->va_info)); } #if !defined(VGA_NO_BIOS) && !defined(VGA_NO_MODE_CHANGE) /* compare two parameter table entries */ static int comp_adpregs(u_char *buf1, u_char *buf2) { static struct { u_char mask; } params[V_MODE_PARAM_SIZE] = { 0xff, 0x00, 0xff, /* COLS, ROWS, POINTS */ 0x00, 0x00, /* page length */ 0xfe, 0xff, 0xff, 0xff, /* sequencer registers */ 0xf3, /* misc register */ 0xff, 0xff, 0xff, 0x7f, 0xff, /* CRTC */ 0xff, 0xff, 0xff, 0x7f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x7f, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* attribute controller registers */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, /* GDC register */ 0xff, 0xff, 0xff, 0xff, }; int identical = TRUE; int i; if ((buf1 == NULL) || (buf2 == NULL)) return COMP_DIFFERENT; for (i = 0; i < sizeof(params)/sizeof(params[0]); ++i) { if (params[i].mask == 0) /* don't care */ continue; if ((buf1[i] & params[i].mask) != (buf2[i] & params[i].mask)) return COMP_DIFFERENT; if (buf1[i] != buf2[i]) identical = FALSE; } return (identical) ? COMP_IDENTICAL : COMP_SIMILAR; } #endif /* !VGA_NO_BIOS && !VGA_NO_MODE_CHANGE */ /* probe video adapters and return the number of detected adapters */ static int probe_adapters(void) { video_adapter_t *adp; video_info_t info; int i; /* do this test only once */ if (init_done) return biosadapters; init_done = TRUE; /* * Locate display adapters. * The AT architecture supports upto two adapters. `syscons' allows * the following combinations of adapters: * 1) MDA + CGA * 2) MDA + EGA/VGA color * 3) CGA + EGA/VGA mono * Note that `syscons' doesn't bother with MCGA as it is only * avaiable for low end PS/2 models which has 80286 or earlier CPUs, * thus, they are not running FreeBSD! * When there are two adapaters in the system, one becomes `primary' * and the other `secondary'. The EGA adapter has a set of DIP * switches on board for this information and the EGA BIOS copies * it in the BIOS data area BIOSDATA_VIDEOSWITCH (40:88). * The VGA BIOS has more sophisticated mechanism and has this * information in BIOSDATA_DCCINDEX (40:8a), but it also maintains * compatibility with the EGA BIOS by updating BIOSDATA_VIDEOSWITCH. */ /* * Check rtc and BIOS data area. * XXX: we don't use BIOSDATA_EQUIPMENT, since it is not a dead * copy of RTC_EQUIPMENT. Bits 4 and 5 of ETC_EQUIPMENT are * zeros for EGA and VGA. However, the EGA/VGA BIOS sets * these bits in BIOSDATA_EQUIPMENT according to the monitor * type detected. */ #ifndef VGA_NO_BIOS switch ((rtcin(RTC_EQUIPMENT) >> 4) & 3) { /* bit 4 and 5 */ case 0: /* EGA/VGA */ fill_adapter_param(readb(BIOS_PADDRTOVADDR(0x488)) & 0x0f, biosadapter); break; case 1: /* CGA 40x25 */ /* FIXME: switch to the 80x25 mode? XXX */ biosadapter[V_ADP_PRIMARY] = adapter_init_value[DCC_CGA40]; biosadapter[V_ADP_SECONDARY] = adapter_init_value[DCC_MONO]; break; case 2: /* CGA 80x25 */ biosadapter[V_ADP_PRIMARY] = adapter_init_value[DCC_CGA80]; biosadapter[V_ADP_SECONDARY] = adapter_init_value[DCC_MONO]; break; case 3: /* MDA */ biosadapter[V_ADP_PRIMARY] = adapter_init_value[DCC_MONO]; biosadapter[V_ADP_SECONDARY] = adapter_init_value[DCC_CGA80]; break; } #else /* assume EGA/VGA? XXX */ biosadapter[V_ADP_PRIMARY] = adapter_init_value[DCC_EGA80]; biosadapter[V_ADP_SECONDARY] = adapter_init_value[DCC_MONO]; #endif /* VGA_NO_BIOS */ biosadapters = 0; if (verify_adapter(&biosadapter[V_ADP_SECONDARY]) == 0) { ++biosadapters; biosadapter[V_ADP_SECONDARY].va_flags |= V_ADP_PROBED; biosadapter[V_ADP_SECONDARY].va_mode = biosadapter[V_ADP_SECONDARY].va_initial_mode = map_bios_mode_num(biosadapter[V_ADP_SECONDARY].va_type, biosadapter[V_ADP_SECONDARY].va_flags & V_ADP_COLOR, biosadapter[V_ADP_SECONDARY].va_initial_bios_mode); } else { biosadapter[V_ADP_SECONDARY].va_type = -1; } if (verify_adapter(&biosadapter[V_ADP_PRIMARY]) == 0) { ++biosadapters; biosadapter[V_ADP_PRIMARY].va_flags |= V_ADP_PROBED; #ifndef VGA_NO_BIOS biosadapter[V_ADP_PRIMARY].va_initial_bios_mode = readb(BIOS_PADDRTOVADDR(0x449)); #else biosadapter[V_ADP_PRIMARY].va_initial_bios_mode = 3; /* XXX */ #endif biosadapter[V_ADP_PRIMARY].va_mode = biosadapter[V_ADP_PRIMARY].va_initial_mode = map_bios_mode_num(biosadapter[V_ADP_PRIMARY].va_type, biosadapter[V_ADP_PRIMARY].va_flags & V_ADP_COLOR, biosadapter[V_ADP_PRIMARY].va_initial_bios_mode); } else { biosadapter[V_ADP_PRIMARY] = biosadapter[V_ADP_SECONDARY]; biosadapter[V_ADP_SECONDARY].va_type = -1; } if (biosadapters == 0) return biosadapters; biosadapter[V_ADP_PRIMARY].va_unit = V_ADP_PRIMARY; biosadapter[V_ADP_SECONDARY].va_unit = V_ADP_SECONDARY; #if 0 /* we don't need these... */ fb_init_struct(&biosadapter[V_ADP_PRIMARY], ...); fb_init_struct(&biosadapter[V_ADP_SECONDARY], ...); #endif #if 0 /* * We cannot have two video adapter of the same type; there must be * only one of color or mono adapter, or one each of them. */ if (biosadapters > 1) { if (!((biosadapter[0].va_flags ^ biosadapter[1].va_flags) & V_ADP_COLOR)) /* we have two mono or color adapters!! */ return (biosadapters = 0); } #endif /* * Ensure a zero start address. This is mainly to recover after * switching from pcvt using userconfig(). The registers are w/o * for old hardware so it's too hard to relocate the active screen * memory. * This must be done before vga_save_state() for VGA. */ outb(biosadapter[V_ADP_PRIMARY].va_crtc_addr, 12); outb(biosadapter[V_ADP_PRIMARY].va_crtc_addr + 1, 0); outb(biosadapter[V_ADP_PRIMARY].va_crtc_addr, 13); outb(biosadapter[V_ADP_PRIMARY].va_crtc_addr + 1, 0); /* the video mode parameter table in EGA/VGA BIOS */ /* NOTE: there can be only one EGA/VGA, wheather color or mono, * recognized by the video BIOS. */ if ((biosadapter[V_ADP_PRIMARY].va_type == KD_EGA) || (biosadapter[V_ADP_PRIMARY].va_type == KD_VGA)) { adp = &biosadapter[V_ADP_PRIMARY]; } else if ((biosadapter[V_ADP_SECONDARY].va_type == KD_EGA) || (biosadapter[V_ADP_SECONDARY].va_type == KD_VGA)) { adp = &biosadapter[V_ADP_SECONDARY]; } else { adp = NULL; } bzero(mode_map, sizeof(mode_map)); if (adp != NULL) { if (adp->va_type == KD_VGA) { vga_save_state(adp, &adpstate, sizeof(adpstate)); #if defined(VGA_NO_BIOS) || defined(VGA_NO_MODE_CHANGE) mode_map[adp->va_initial_mode] = adpstate.regs; rows_offset = 1; #else /* VGA_NO_BIOS || VGA_NO_MODE_CHANGE */ if (video_mode_ptr == NULL) { mode_map[adp->va_initial_mode] = adpstate.regs; rows_offset = 1; } else { /* discard the table if we are not familiar with it... */ + u_char *mp; map_mode_table(mode_map, video_mode_ptr, M_VGA_CG320 + 1); mp = get_mode_param(adp->va_initial_mode); if (mp != NULL) bcopy(mp, adpstate2.regs, sizeof(adpstate2.regs)); switch (comp_adpregs(adpstate.regs, mp)) { case COMP_IDENTICAL: /* * OK, this parameter table looks reasonably familiar * to us... */ /* * This is a kludge for Toshiba DynaBook SS433 * whose BIOS video mode table entry has the actual # * of rows at the offset 1; BIOSes from other * manufacturers store the # of rows - 1 there. XXX */ rows_offset = adpstate.regs[1] + 1 - mp[1]; break; case COMP_SIMILAR: /* * Not exactly the same, but similar enough to be * trusted. However, use the saved register values * for the initial mode and other modes which are * based on the initial mode. */ mode_map[adp->va_initial_mode] = adpstate.regs; rows_offset = adpstate.regs[1] + 1 - mp[1]; adpstate.regs[1] -= rows_offset - 1; break; case COMP_DIFFERENT: default: /* * Don't use the paramter table in BIOS. It doesn't * look familiar to us. Video mode switching is allowed * only if the new mode is the same as or based on * the initial mode. */ video_mode_ptr = NULL; bzero(mode_map, sizeof(mode_map)); mode_map[adp->va_initial_mode] = adpstate.regs; rows_offset = 1; break; } } #endif /* VGA_NO_BIOS || VGA_NO_MODE_CHANGE */ #ifndef VGA_NO_MODE_CHANGE adp->va_flags |= V_ADP_MODECHANGE; #endif #ifndef VGA_NO_FONT_LOADING adp->va_flags |= V_ADP_FONT; #endif } else if (adp->va_type == KD_EGA) { #if defined(VGA_NO_BIOS) || defined(VGA_NO_MODE_CHANGE) rows_offset = 1; #else /* VGA_NO_BIOS || VGA_NO_MODE_CHANGE */ if (video_mode_ptr == NULL) { rows_offset = 1; } else { + u_char *mp; map_mode_table(mode_map, video_mode_ptr, M_ENH_C80x25 + 1); /* XXX how can one validate the EGA table... */ mp = get_mode_param(adp->va_initial_mode); if (mp != NULL) { adp->va_flags |= V_ADP_MODECHANGE; #ifndef VGA_NO_FONT_LOADING adp->va_flags |= V_ADP_FONT; #endif rows_offset = 1; } else { /* * This is serious. We will not be able to switch video * modes at all... */ video_mode_ptr = NULL; bzero(mode_map, sizeof(mode_map)); rows_offset = 1; } } #endif /* VGA_NO_BIOS || VGA_NO_MODE_CHANGE */ } } /* remove conflicting modes if we have more than one adapter */ if (biosadapters > 1) { for (i = 0; i < biosadapters; ++i) { if (!(biosadapter[i].va_flags & V_ADP_MODECHANGE)) continue; clear_mode_map(&biosadapter[i], mode_map, M_VGA_CG320 + 1, (biosadapter[i].va_flags & V_ADP_COLOR) ? V_INFO_COLOR : 0); if ((biosadapter[i].va_type == KD_VGA) || (biosadapter[i].va_type == KD_EGA)) { biosadapter[i].va_io_base = (biosadapter[i].va_flags & V_ADP_COLOR) ? IO_VGA : IO_MDA; biosadapter[i].va_io_size = 32; } } } /* buffer address */ vga_get_info(&biosadapter[V_ADP_PRIMARY], biosadapter[V_ADP_PRIMARY].va_initial_mode, &info); update_adapter_info(&biosadapter[V_ADP_PRIMARY], &info); if (biosadapters > 1) { vga_get_info(&biosadapter[V_ADP_SECONDARY], biosadapter[V_ADP_SECONDARY].va_initial_mode, &info); update_adapter_info(&biosadapter[V_ADP_SECONDARY], &info); } /* * XXX: we should verify the following values for the primary adapter... * crtc I/O port address: *(u_int16_t *)BIOS_PADDRTOVADDR(0x463); * color/mono display: (*(u_int8_t *)BIOS_PADDRTOVADDR(0x487) & 0x02) * ? 0 : V_ADP_COLOR; * columns: *(u_int8_t *)BIOS_PADDRTOVADDR(0x44a); * rows: *(u_int8_t *)BIOS_PADDRTOVADDR(0x484); * font size: *(u_int8_t *)BIOS_PADDRTOVADDR(0x485); * buffer size: *(u_int16_t *)BIOS_PADDRTOVADDR(0x44c); */ return biosadapters; } /* entry points */ static int vga_nop(void) { return 0; } static int vga_probe(int unit, video_adapter_t **adpp, void *arg, int flags) { probe_adapters(); if (unit >= biosadapters) return ENXIO; *adpp = &biosadapter[unit]; return 0; } static int vga_init(int unit, video_adapter_t *adp, int flags) { if ((unit >= biosadapters) || (adp == NULL) || !probe_done(adp)) return ENXIO; if (!init_done(adp)) { /* nothing to do really... */ adp->va_flags |= V_ADP_INITIALIZED; } if (!config_done(adp)) { if (vid_register(adp) < 0) return ENXIO; adp->va_flags |= V_ADP_REGISTERED; } if (vga_sub_configure != NULL) (*vga_sub_configure)(0); return 0; } /* * get_info(): * Return the video_info structure of the requested video mode. * * all adapters */ static int vga_get_info(video_adapter_t *adp, int mode, video_info_t *info) { int i; if (!init_done) return 1; mode = map_gen_mode_num(adp->va_type, adp->va_flags & V_ADP_COLOR, mode); #ifndef VGA_NO_MODE_CHANGE if (adp->va_flags & V_ADP_MODECHANGE) { /* * If the parameter table entry for this mode is not found, * the mode is not supported... */ if (get_mode_param(mode) == NULL) return 1; } else #endif /* VGA_NO_MODE_CHANGE */ { /* * Even if we don't support video mode switching on this adapter, * the information on the initial (thus current) video mode * should be made available. */ if (mode != adp->va_initial_mode) return 1; } for (i = 0; bios_vmode[i].vi_mode != EOT; ++i) { if (bios_vmode[i].vi_mode == NA) continue; if (mode == bios_vmode[i].vi_mode) { *info = bios_vmode[i]; return 0; } } return 1; } /* * query_mode(): * Find a video mode matching the requested parameters. * Fields filled with 0 are considered "don't care" fields and * match any modes. * * all adapters */ static int vga_query_mode(video_adapter_t *adp, video_info_t *info) { video_info_t buf; int i; if (!init_done) return -1; for (i = 0; bios_vmode[i].vi_mode != EOT; ++i) { if (bios_vmode[i].vi_mode == NA) continue; if ((info->vi_width != 0) && (info->vi_width != bios_vmode[i].vi_width)) continue; if ((info->vi_height != 0) && (info->vi_height != bios_vmode[i].vi_height)) continue; if ((info->vi_cwidth != 0) && (info->vi_cwidth != bios_vmode[i].vi_cwidth)) continue; if ((info->vi_cheight != 0) && (info->vi_cheight != bios_vmode[i].vi_cheight)) continue; if ((info->vi_depth != 0) && (info->vi_depth != bios_vmode[i].vi_depth)) continue; if ((info->vi_planes != 0) && (info->vi_planes != bios_vmode[i].vi_planes)) continue; /* XXX: should check pixel format, memory model */ if ((info->vi_flags != 0) && (info->vi_flags != bios_vmode[i].vi_flags)) continue; /* verify if this mode is supported on this adapter */ if (vga_get_info(adp, bios_vmode[i].vi_mode, &buf)) continue; return bios_vmode[i].vi_mode; } return -1; } /* * set_mode(): * Change the video mode. * * EGA/VGA */ static int vga_set_mode(video_adapter_t *adp, int mode) { #ifndef VGA_NO_MODE_CHANGE video_info_t info; adp_state_t params; prologue(adp, V_ADP_MODECHANGE, 1); mode = map_gen_mode_num(adp->va_type, adp->va_flags & V_ADP_COLOR, mode); if (vga_get_info(adp, mode, &info)) return 1; params.sig = V_STATE_SIG; bcopy(get_mode_param(mode), params.regs, sizeof(params.regs)); switch (mode) { case M_VGA_C80x60: case M_VGA_M80x60: params.regs[2] = 0x08; params.regs[19] = 0x47; goto special_480l; case M_VGA_C80x30: case M_VGA_M80x30: params.regs[19] = 0x4f; special_480l: params.regs[9] |= 0xc0; params.regs[16] = 0x08; params.regs[17] = 0x3e; params.regs[26] = 0xea; params.regs[28] = 0xdf; params.regs[31] = 0xe7; params.regs[32] = 0x04; goto setup_mode; case M_ENH_C80x43: case M_ENH_B80x43: params.regs[28] = 87; goto special_80x50; case M_VGA_C80x50: case M_VGA_M80x50: special_80x50: params.regs[2] = 8; params.regs[19] = 7; goto setup_mode; case M_VGA_C40x25: case M_VGA_C80x25: case M_VGA_M80x25: case M_B40x25: case M_C40x25: case M_B80x25: case M_C80x25: case M_ENH_B40x25: case M_ENH_C40x25: case M_ENH_B80x25: case M_ENH_C80x25: case M_EGAMONO80x25: setup_mode: vga_load_state(adp, ¶ms); break; case M_VGA_MODEX: /* "unchain" the VGA mode */ params.regs[5-1+0x04] &= 0xf7; params.regs[5-1+0x04] |= 0x04; /* turn off doubleword mode */ params.regs[10+0x14] &= 0xbf; /* turn off word adressing */ params.regs[10+0x17] |= 0x40; /* set logical screen width */ params.regs[10+0x13] = 80; /* set 240 lines */ params.regs[10+0x11] = 0x2c; params.regs[10+0x06] = 0x0d; params.regs[10+0x07] = 0x3e; params.regs[10+0x10] = 0xea; params.regs[10+0x11] = 0xac; params.regs[10+0x12] = 0xdf; params.regs[10+0x15] = 0xe7; params.regs[10+0x16] = 0x06; /* set vertical sync polarity to reflect aspect ratio */ params.regs[9] = 0xe3; goto setup_grmode; case M_BG320: case M_CG320: case M_BG640: case M_CG320_D: case M_CG640_E: case M_CG640x350: case M_ENH_CG640: case M_BG640x480: case M_CG640x480: case M_VGA_CG320: setup_grmode: vga_load_state(adp, ¶ms); break; default: return 1; } adp->va_mode = mode; update_adapter_info(adp, &info); /* move hardware cursor out of the way */ (*vidsw[adp->va_index]->set_hw_cursor)(adp, -1, -1); return 0; #else /* VGA_NO_MODE_CHANGE */ return 1; #endif /* VGA_NO_MODE_CHANGE */ } #ifndef VGA_NO_FONT_LOADING static void set_font_mode(video_adapter_t *adp, u_char *buf) { u_char *mp; int s; s = splhigh(); /* save register values */ if (adp->va_type == KD_VGA) { outb(TSIDX, 0x02); buf[0] = inb(TSREG); outb(TSIDX, 0x04); buf[1] = inb(TSREG); outb(GDCIDX, 0x04); buf[2] = inb(GDCREG); outb(GDCIDX, 0x05); buf[3] = inb(GDCREG); outb(GDCIDX, 0x06); buf[4] = inb(GDCREG); inb(adp->va_crtc_addr + 6); outb(ATC, 0x10); buf[5] = inb(ATC + 1); } else /* if (adp->va_type == KD_EGA) */ { /* * EGA cannot be read; copy parameters from the mode parameter * table. */ mp = get_mode_param(adp->va_mode); buf[0] = mp[5 + 0x02 - 1]; buf[1] = mp[5 + 0x04 - 1]; buf[2] = mp[55 + 0x04]; buf[3] = mp[55 + 0x05]; buf[4] = mp[55 + 0x06]; buf[5] = mp[35 + 0x10]; } /* setup vga for loading fonts */ inb(adp->va_crtc_addr + 6); /* reset flip-flop */ outb(ATC, 0x10); outb(ATC, buf[5] & ~0x01); inb(adp->va_crtc_addr + 6); /* reset flip-flop */ outb(ATC, 0x20); /* enable palette */ #if VGA_SLOW_IOACCESS #ifdef VGA_ALT_SEQACCESS outb(TSIDX, 0x00); outb(TSREG, 0x01); #endif outb(TSIDX, 0x02); outb(TSREG, 0x04); outb(TSIDX, 0x04); outb(TSREG, 0x07); #ifdef VGA_ALT_SEQACCESS outb(TSIDX, 0x00); outb(TSREG, 0x03); #endif outb(GDCIDX, 0x04); outb(GDCREG, 0x02); outb(GDCIDX, 0x05); outb(GDCREG, 0x00); outb(GDCIDX, 0x06); outb(GDCREG, 0x04); #else /* VGA_SLOW_IOACCESS */ #ifdef VGA_ALT_SEQACCESS outw(TSIDX, 0x0100); #endif outw(TSIDX, 0x0402); outw(TSIDX, 0x0704); #ifdef VGA_ALT_SEQACCESS outw(TSIDX, 0x0300); #endif outw(GDCIDX, 0x0204); outw(GDCIDX, 0x0005); outw(GDCIDX, 0x0406); /* addr = a0000, 64kb */ #endif /* VGA_SLOW_IOACCESS */ splx(s); } static void set_normal_mode(video_adapter_t *adp, u_char *buf) { int s; s = splhigh(); /* setup vga for normal operation mode again */ inb(adp->va_crtc_addr + 6); /* reset flip-flop */ outb(ATC, 0x10); outb(ATC, buf[5]); inb(adp->va_crtc_addr + 6); /* reset flip-flop */ outb(ATC, 0x20); /* enable palette */ #if VGA_SLOW_IOACCESS #ifdef VGA_ALT_SEQACCESS outb(TSIDX, 0x00); outb(TSREG, 0x01); #endif outb(TSIDX, 0x02); outb(TSREG, buf[0]); outb(TSIDX, 0x04); outb(TSREG, buf[1]); #ifdef VGA_ALT_SEQACCESS outb(TSIDX, 0x00); outb(TSREG, 0x03); #endif outb(GDCIDX, 0x04); outb(GDCREG, buf[2]); outb(GDCIDX, 0x05); outb(GDCREG, buf[3]); if (adp->va_crtc_addr == MONO_CRTC) { outb(GDCIDX, 0x06); outb(GDCREG,(buf[4] & 0x03) | 0x08); } else { outb(GDCIDX, 0x06); outb(GDCREG,(buf[4] & 0x03) | 0x0c); } #else /* VGA_SLOW_IOACCESS */ #ifdef VGA_ALT_SEQACCESS outw(TSIDX, 0x0100); #endif outw(TSIDX, 0x0002 | (buf[0] << 8)); outw(TSIDX, 0x0004 | (buf[1] << 8)); #ifdef VGA_ALT_SEQACCESS outw(TSIDX, 0x0300); #endif outw(GDCIDX, 0x0004 | (buf[2] << 8)); outw(GDCIDX, 0x0005 | (buf[3] << 8)); if (adp->va_crtc_addr == MONO_CRTC) outw(GDCIDX, 0x0006 | (((buf[4] & 0x03) | 0x08)<<8)); else outw(GDCIDX, 0x0006 | (((buf[4] & 0x03) | 0x0c)<<8)); #endif /* VGA_SLOW_IOACCESS */ splx(s); } #endif /* VGA_NO_FONT_LOADING */ /* * save_font(): * Read the font data in the requested font page from the video adapter. * * EGA/VGA */ static int vga_save_font(video_adapter_t *adp, int page, int fontsize, u_char *data, int ch, int count) { #ifndef VGA_NO_FONT_LOADING u_char buf[PARAM_BUFSIZE]; u_int32_t segment; int c; #ifdef VGA_ALT_SEQACCESS int s; u_char val = 0; #endif prologue(adp, V_ADP_FONT, 1); if (fontsize < 14) { /* FONT_8 */ fontsize = 8; } else if (fontsize >= 32) { fontsize = 32; } else if (fontsize >= 16) { /* FONT_16 */ fontsize = 16; } else { /* FONT_14 */ fontsize = 14; } if (page < 0 || page >= 8) return 1; segment = FONT_BUF + 0x4000*page; if (page > 3) segment -= 0xe000; #ifdef VGA_ALT_SEQACCESS if (adp->va_type == KD_VGA) { /* what about EGA? XXX */ s = splhigh(); outb(TSIDX, 0x00); outb(TSREG, 0x01); outb(TSIDX, 0x01); val = inb(TSREG); /* disable screen */ outb(TSIDX, 0x01); outb(TSREG, val | 0x20); outb(TSIDX, 0x00); outb(TSREG, 0x03); splx(s); } #endif set_font_mode(adp, buf); if (fontsize == 32) { bcopy_fromio(segment + ch*32, data, fontsize*count); } else { for (c = ch; count > 0; ++c, --count) { bcopy_fromio(segment + c*32, data, fontsize); data += fontsize; } } set_normal_mode(adp, buf); #ifdef VGA_ALT_SEQACCESS if (adp->va_type == KD_VGA) { s = splhigh(); outb(TSIDX, 0x00); outb(TSREG, 0x01); outb(TSIDX, 0x01); outb(TSREG, val & 0xdf); /* enable screen */ outb(TSIDX, 0x00); outb(TSREG, 0x03); splx(s); } #endif return 0; #else /* VGA_NO_FONT_LOADING */ return 1; #endif /* VGA_NO_FONT_LOADING */ } /* * load_font(): * Set the font data in the requested font page. * NOTE: it appears that some recent video adapters do not support * the font page other than 0... XXX * * EGA/VGA */ static int vga_load_font(video_adapter_t *adp, int page, int fontsize, u_char *data, int ch, int count) { #ifndef VGA_NO_FONT_LOADING u_char buf[PARAM_BUFSIZE]; u_int32_t segment; int c; #ifdef VGA_ALT_SEQACCESS int s; u_char val = 0; #endif prologue(adp, V_ADP_FONT, 1); if (fontsize < 14) { /* FONT_8 */ fontsize = 8; } else if (fontsize >= 32) { fontsize = 32; } else if (fontsize >= 16) { /* FONT_16 */ fontsize = 16; } else { /* FONT_14 */ fontsize = 14; } if (page < 0 || page >= 8) return 1; segment = FONT_BUF + 0x4000*page; if (page > 3) segment -= 0xe000; #ifdef VGA_ALT_SEQACCESS if (adp->va_type == KD_VGA) { /* what about EGA? XXX */ s = splhigh(); outb(TSIDX, 0x00); outb(TSREG, 0x01); outb(TSIDX, 0x01); val = inb(TSREG); /* disable screen */ outb(TSIDX, 0x01); outb(TSREG, val | 0x20); outb(TSIDX, 0x00); outb(TSREG, 0x03); splx(s); } #endif set_font_mode(adp, buf); if (fontsize == 32) { bcopy_toio(data, segment + ch*32, fontsize*count); } else { for (c = ch; count > 0; ++c, --count) { bcopy_toio(data, segment + c*32, fontsize); data += fontsize; } } set_normal_mode(adp, buf); #ifdef VGA_ALT_SEQACCESS if (adp->va_type == KD_VGA) { s = splhigh(); outb(TSIDX, 0x00); outb(TSREG, 0x01); outb(TSIDX, 0x01); outb(TSREG, val & 0xdf); /* enable screen */ outb(TSIDX, 0x00); outb(TSREG, 0x03); splx(s); } #endif return 0; #else /* VGA_NO_FONT_LOADING */ return 1; #endif /* VGA_NO_FONT_LOADING */ } /* * show_font(): * Activate the requested font page. * NOTE: it appears that some recent video adapters do not support * the font page other than 0... XXX * * EGA/VGA */ static int vga_show_font(video_adapter_t *adp, int page) { #ifndef VGA_NO_FONT_LOADING static u_char cg[] = { 0x00, 0x05, 0x0a, 0x0f, 0x30, 0x35, 0x3a, 0x3f }; int s; prologue(adp, V_ADP_FONT, 1); if (page < 0 || page >= 8) return 1; s = splhigh(); outb(TSIDX, 0x03); outb(TSREG, cg[page]); splx(s); return 0; #else /* VGA_NO_FONT_LOADING */ return 1; #endif /* VGA_NO_FONT_LOADING */ } /* * save_palette(): * Read DAC values. The values have expressed in 8 bits. * * VGA */ static int vga_save_palette(video_adapter_t *adp, u_char *palette) { int i; prologue(adp, V_ADP_PALETTE, 1); /* * We store 8 bit values in the palette buffer, while the standard * VGA has 6 bit DAC . */ outb(PALRADR, 0x00); for (i = 0; i < 256*3; ++i) palette[i] = inb(PALDATA) << 2; inb(adp->va_crtc_addr + 6); /* reset flip/flop */ return 0; } /* * load_palette(): * Set DAC values. * * VGA */ static int vga_load_palette(video_adapter_t *adp, u_char *palette) { int i; prologue(adp, V_ADP_PALETTE, 1); outb(PIXMASK, 0xff); /* no pixelmask */ outb(PALWADR, 0x00); for (i = 0; i < 256*3; ++i) outb(PALDATA, palette[i] >> 2); inb(adp->va_crtc_addr + 6); /* reset flip/flop */ outb(ATC, 0x20); /* enable palette */ return 0; } /* * set_border(): * Change the border color. * * CGA/EGA/VGA */ static int vga_set_border(video_adapter_t *adp, int color) { prologue(adp, V_ADP_BORDER, 1); switch (adp->va_type) { case KD_EGA: case KD_VGA: inb(adp->va_crtc_addr + 6); /* reset flip-flop */ outb(ATC, 0x31); outb(ATC, color & 0xff); break; case KD_CGA: outb(adp->va_crtc_addr + 5, color & 0x0f); /* color select register */ break; case KD_MONO: case KD_HERCULES: default: break; } return 0; } /* * save_state(): * Read video register values. * NOTE: this function only reads the standard EGA/VGA registers. * any extra/extended registers of SVGA adapters are not saved. * * VGA */ static int vga_save_state(video_adapter_t *adp, void *p, size_t size) { video_info_t info; u_char *buf; int crtc_addr; int i, j; int s; if (size == 0) { /* return the required buffer size */ prologue(adp, V_ADP_STATESAVE, 0); return sizeof(adp_state_t); } else { prologue(adp, V_ADP_STATESAVE, 1); if (size < sizeof(adp_state_t)) return 1; } ((adp_state_t *)p)->sig = V_STATE_SIG; buf = ((adp_state_t *)p)->regs; bzero(buf, V_MODE_PARAM_SIZE); crtc_addr = adp->va_crtc_addr; s = splhigh(); outb(TSIDX, 0x00); outb(TSREG, 0x01); /* stop sequencer */ for (i = 0, j = 5; i < 4; i++) { outb(TSIDX, i + 1); buf[j++] = inb(TSREG); } buf[9] = inb(MISC + 10); /* dot-clock */ outb(TSIDX, 0x00); outb(TSREG, 0x03); /* start sequencer */ for (i = 0, j = 10; i < 25; i++) { /* crtc */ outb(crtc_addr, i); buf[j++] = inb(crtc_addr + 1); } for (i = 0, j = 35; i < 20; i++) { /* attribute ctrl */ inb(crtc_addr + 6); /* reset flip-flop */ outb(ATC, i); buf[j++] = inb(ATC + 1); } for (i = 0, j = 55; i < 9; i++) { /* graph data ctrl */ outb(GDCIDX, i); buf[j++] = inb(GDCREG); } inb(crtc_addr + 6); /* reset flip-flop */ outb(ATC, 0x20); /* enable palette */ splx(s); #if 1 if (vga_get_info(adp, adp->va_mode, &info) == 0) { if (info.vi_flags & V_INFO_GRAPHICS) { buf[0] = info.vi_width/info.vi_cwidth; /* COLS */ buf[1] = info.vi_height/info.vi_cheight - 1; /* ROWS */ } else { buf[0] = info.vi_width; /* COLS */ buf[1] = info.vi_height - 1; /* ROWS */ } buf[2] = info.vi_cheight; /* POINTS */ } else { /* XXX: shouldn't be happening... */ printf("vga%d: %s: failed to obtain mode info. (vga_save_state())\n", adp->va_unit, adp->va_name); } #else buf[0] = readb(BIOS_PADDRTOVADDR(0x44a)); /* COLS */ buf[1] = readb(BIOS_PADDRTOVADDR(0x484)); /* ROWS */ buf[2] = readb(BIOS_PADDRTOVADDR(0x485)); /* POINTS */ buf[3] = readb(BIOS_PADDRTOVADDR(0x44c)); buf[4] = readb(BIOS_PADDRTOVADDR(0x44d)); #endif return 0; } /* * load_state(): * Set video registers at once. * NOTE: this function only updates the standard EGA/VGA registers. * any extra/extended registers of SVGA adapters are not changed. * * EGA/VGA */ static int vga_load_state(video_adapter_t *adp, void *p) { u_char *buf; int crtc_addr; int s; int i; prologue(adp, V_ADP_STATELOAD, 1); if (((adp_state_t *)p)->sig != V_STATE_SIG) return 1; buf = ((adp_state_t *)p)->regs; crtc_addr = adp->va_crtc_addr; s = splhigh(); outb(TSIDX, 0x00); outb(TSREG, 0x01); /* stop sequencer */ for (i = 0; i < 4; ++i) { /* program sequencer */ outb(TSIDX, i + 1); outb(TSREG, buf[i + 5]); } outb(MISC, buf[9]); /* set dot-clock */ outb(TSIDX, 0x00); outb(TSREG, 0x03); /* start sequencer */ outb(crtc_addr, 0x11); outb(crtc_addr + 1, inb(crtc_addr + 1) & 0x7F); for (i = 0; i < 25; ++i) { /* program crtc */ outb(crtc_addr, i); outb(crtc_addr + 1, buf[i + 10]); } inb(crtc_addr+6); /* reset flip-flop */ for (i = 0; i < 20; ++i) { /* program attribute ctrl */ outb(ATC, i); outb(ATC, buf[i + 35]); } for (i = 0; i < 9; ++i) { /* program graph data ctrl */ outb(GDCIDX, i); outb(GDCREG, buf[i + 55]); } inb(crtc_addr + 6); /* reset flip-flop */ outb(ATC, 0x20); /* enable palette */ #ifndef VGA_NO_BIOS if (adp->va_unit == V_ADP_PRIMARY) { writeb(BIOS_PADDRTOVADDR(0x44a), buf[0]); /* COLS */ writeb(BIOS_PADDRTOVADDR(0x484), buf[1] + rows_offset - 1); /* ROWS */ writeb(BIOS_PADDRTOVADDR(0x485), buf[2]); /* POINTS */ #if 0 writeb(BIOS_PADDRTOVADDR(0x44c), buf[3]); writeb(BIOS_PADDRTOVADDR(0x44d), buf[4]); #endif } #endif /* VGA_NO_BIOS */ splx(s); return 0; } /* * set_origin(): * Change the origin (window mapping) of the banked frame buffer. */ static int vga_set_origin(video_adapter_t *adp, off_t offset) { /* * The standard video modes do not require window mapping; * always return error. */ return 1; } /* * read_hw_cursor(): * Read the position of the hardware text cursor. * * all adapters */ static int vga_read_hw_cursor(video_adapter_t *adp, int *col, int *row) { u_int16_t off; int s; if (!init_done) return 1; if (adp->va_info.vi_flags & V_INFO_GRAPHICS) return 1; s = spltty(); outb(adp->va_crtc_addr, 14); off = inb(adp->va_crtc_addr + 1); outb(adp->va_crtc_addr, 15); off = (off << 8) | inb(adp->va_crtc_addr + 1); splx(s); *row = off / adp->va_info.vi_width; *col = off % adp->va_info.vi_width; return 0; } /* * set_hw_cursor(): * Move the hardware text cursor. If col and row are both -1, * the cursor won't be shown. * * all adapters */ static int vga_set_hw_cursor(video_adapter_t *adp, int col, int row) { u_int16_t off; int s; if (!init_done) return 1; if ((col == -1) && (row == -1)) { off = -1; } else { if (adp->va_info.vi_flags & V_INFO_GRAPHICS) return 1; off = row*adp->va_info.vi_width + col; } s = spltty(); outb(adp->va_crtc_addr, 14); outb(adp->va_crtc_addr + 1, off >> 8); outb(adp->va_crtc_addr, 15); outb(adp->va_crtc_addr + 1, off & 0x00ff); splx(s); return 0; } /* * set_hw_cursor_shape(): * Change the shape of the hardware text cursor. If the height is * zero or negative, the cursor won't be shown. * * all adapters */ static int vga_set_hw_cursor_shape(video_adapter_t *adp, int base, int height, int celsize, int blink) { int s; if (!init_done) return 1; s = spltty(); switch (adp->va_type) { case KD_VGA: case KD_CGA: case KD_MONO: case KD_HERCULES: default: if (height <= 0) { /* make the cursor invisible */ outb(adp->va_crtc_addr, 10); outb(adp->va_crtc_addr + 1, 32); outb(adp->va_crtc_addr, 11); outb(adp->va_crtc_addr + 1, 0); } else { outb(adp->va_crtc_addr, 10); outb(adp->va_crtc_addr + 1, celsize - base - height); outb(adp->va_crtc_addr, 11); outb(adp->va_crtc_addr + 1, celsize - base - 1); } break; case KD_EGA: if (height <= 0) { /* make the cursor invisible */ outb(adp->va_crtc_addr, 10); outb(adp->va_crtc_addr + 1, celsize); outb(adp->va_crtc_addr, 11); outb(adp->va_crtc_addr + 1, 0); } else { outb(adp->va_crtc_addr, 10); outb(adp->va_crtc_addr + 1, celsize - base - height); outb(adp->va_crtc_addr, 11); outb(adp->va_crtc_addr + 1, celsize - base); } break; } splx(s); return 0; } /* * mmap(): * Mmap frame buffer. * * all adapters */ static int vga_mmap(video_adapter_t *adp, vm_offset_t offset) { if (offset > 0x20000 - PAGE_SIZE) return -1; #ifdef __i386__ return i386_btop((VIDEO_BUF_BASE + offset)); #endif #ifdef __alpha__ return alpha_btop((VIDEO_BUF_BASE + offset)); #endif } static void dump_buffer(u_char *buf, size_t len) { int i; for(i = 0; i < len;) { printf("%02x ", buf[i]); if ((++i % 16) == 0) printf("\n"); } } /* * diag(): * Print some information about the video adapter and video modes, * with requested level of details. * * all adapters */ static int vga_diag(video_adapter_t *adp, int level) { #if FB_DEBUG > 1 video_info_t info; #endif u_char *mp; if (!init_done) return 1; #if FB_DEBUG > 1 #ifndef VGA_NO_BIOS printf("vga: RTC equip. code:0x%02x, DCC code:0x%02x\n", rtcin(RTC_EQUIPMENT), readb(BIOS_PADDRTOVADDR(0x488))); printf("vga: CRTC:0x%x, video option:0x%02x, ", readw(BIOS_PADDRTOVADDR(0x463)), readb(BIOS_PADDRTOVADDR(0x487))); printf("rows:%d, cols:%d, font height:%d\n", readb(BIOS_PADDRTOVADDR(0x44a)), readb(BIOS_PADDRTOVADDR(0x484)) + 1, readb(BIOS_PADDRTOVADDR(0x485))); #endif /* VGA_NO_BIOS */ printf("vga: param table EGA/VGA:%p", video_mode_ptr); printf(", CGA/MDA:%p\n", video_mode_ptr2); printf("vga: rows_offset:%d\n", rows_offset); #endif /* FB_DEBUG > 1 */ fb_dump_adp_info(DRIVER_NAME, adp, level); #if FB_DEBUG > 1 if (adp->va_flags & V_ADP_MODECHANGE) { for (i = 0; bios_vmode[i].vi_mode != EOT; ++i) { if (bios_vmode[i].vi_mode == NA) continue; if (get_mode_param(bios_vmode[i].vi_mode) == NULL) continue; fb_dump_mode_info(DRIVER_NAME, adp, &bios_vmode[i], level); } } else { vga_get_info(adp, adp->va_initial_mode, &info); /* shouldn't fail */ fb_dump_mode_info(DRIVER_NAME, adp, &info, level); } #endif /* FB_DEBUG > 1 */ if ((adp->va_type != KD_EGA) && (adp->va_type != KD_VGA)) return 0; #if !defined(VGA_NO_BIOS) && !defined(VGA_NO_MODE_CHANGE) if (video_mode_ptr == NULL) printf("vga%d: %s: WARNING: video mode switching is not " "fully supported on this adapter\n", adp->va_unit, adp->va_name); #endif if (level <= 0) return 0; if (adp->va_type == KD_VGA) { printf("VGA parameters upon power-up\n"); dump_buffer(adpstate.regs, sizeof(adpstate.regs)); printf("VGA parameters in BIOS for mode %d\n", adp->va_initial_mode); dump_buffer(adpstate2.regs, sizeof(adpstate2.regs)); } mp = get_mode_param(adp->va_initial_mode); if (mp == NULL) /* this shouldn't be happening */ return 0; printf("EGA/VGA parameters to be used for mode %d\n", adp->va_initial_mode); dump_buffer(mp, V_MODE_PARAM_SIZE); return 0; } #endif /* NVGA > 0 */ Index: head/sys/kern/bus_if.m =================================================================== --- head/sys/kern/bus_if.m (revision 45719) +++ head/sys/kern/bus_if.m (revision 45720) @@ -1,150 +1,160 @@ # # Copyright (c) 1998 Doug Rabson # 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. # -# $Id: bus_if.m,v 1.5 1998/11/14 21:58:51 wollman Exp $ +# $Id: bus_if.m,v 1.6 1999/03/29 08:54:19 dfr Exp $ # INTERFACE bus; # # This is called from system code which prints out a description of a # device. It should describe the attachment that the child has with # the parent. For instance the TurboLaser bus prints which node the # device is attached to. # METHOD void print_child { device_t dev; device_t child; }; # # These two methods manage a bus specific set of instance variables of # a child device. The intention is that each different type of bus # defines a set of appropriate instance variables (such as ports and # irqs for ISA bus etc.) # # This information could be given to the child device as a struct but # that makes it hard for a bus to add or remove variables without # forcing an edit and recompile for all drivers which may not be # possible for vendor supplied binary drivers. # # Read an instance variable. Return 0 on success. # METHOD int read_ivar { device_t dev; device_t child; int index; uintptr_t *result; }; # # Write an instance variable. Return 0 on success. # METHOD int write_ivar { device_t dev; device_t child; int index; uintptr_t value; }; # # Called after the child's DEVICE_DETACH method to allow the parent # to reclaim any resources allocated on behalf of the child. # METHOD void child_detached { device_t dev; device_t child; }; + +# +# Called when a new driver is added to the devclass which owns this +# bus. The generic implementation of this method attempts to probe and +# attach any un-matched children of the bus. +# +METHOD void driver_added { + device_t dev; + driver_t *driver; +} # # Allocate a system resource attached to `dev' on behalf of `child'. # The types are defined in ; the meaning of the # resource-ID field varies from bus to bus (but *rid == 0 is always # valid if the resource type is). start and end reflect the allowable # range, and should be passed as `0UL' and `~0UL', respectively, if # the client has no range restriction. count is the number of consecutive # indices in the resource required. flags is a set of sharing flags # as defined in . # # Returns a resource or a null pointer on failure. The caller is # responsible for calling rman_activate_resource() when it actually # uses the resource. # METHOD struct resource * alloc_resource { device_t dev; device_t child; int type; int *rid; u_long start; u_long end; u_long count; u_int flags; }; METHOD int activate_resource { device_t dev; device_t child; int type; int rid; struct resource *r; }; METHOD int deactivate_resource { device_t dev; device_t child; int type; int rid; struct resource *r; }; # # Free a resource allocated by the preceding method. The `rid' value # must be the same as the one returned by BUS_ALLOC_RESOURCE (which # is not necessarily the same as the one the client passed). # METHOD int release_resource { device_t dev; device_t child; int type; int rid; struct resource *res; }; METHOD int setup_intr { device_t dev; device_t child; struct resource *irq; driver_intr_t *intr; void *arg; void **cookiep; }; METHOD int teardown_intr { device_t dev; device_t child; struct resource *irq; void *cookie; }; Index: head/sys/kern/makedevops.pl =================================================================== --- head/sys/kern/makedevops.pl (revision 45719) +++ head/sys/kern/makedevops.pl (revision 45720) @@ -1,397 +1,397 @@ #!/usr/bin/perl # # Copyright (c) 1992, 1993 # 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 @(#)vnode_if.sh 8.1 (Berkeley) 6/10/93 # From @(#)makedevops.sh 1.1 1998/06/14 13:53:12 dfr Exp $ # From @(#)makedevops.sh ?.? 1998/10/05 # # Script to produce device front-end sugar. # $debug = 0; $cfile = 0; # by default do not produce any file type $hfile = 0; $keepcurrentdir = 1; $line_width = 80; use File::Basename; # Process the command line # while ( $arg = shift @ARGV ) { if ( $arg eq '-c' ) { warn "Producing .c output files" if $debug; $cfile = 1; } elsif ( $arg eq '-h' ) { warn "Producing .h output files" if $debug; $hfile = 1; } elsif ( $arg eq '-ch' || $arg eq '-hc' ) { warn "Producing .c and .h output files" if $debug; $cfile = 1; $hfile = 1; } elsif ( $arg eq '-d' ) { $debug = 1; } elsif ( $arg eq '-p' ) { warn "Will produce files in original not in current directory" if $debug; $keepcurrentdir = 0; } elsif ( $arg eq '-l' ) { if ( $line_width = shift @ARGV and $line_width > 0 ) { warn "Line width set to $line_width" if $debug; } else { die "Please specify a valid line width after -l"; } } elsif ( $arg =~ m/\.m$/ ) { warn "Filename: $arg" if $debug; push @filenames, $arg; } else { warn "$arg ignored" if $debug; } } # Validate the command line parameters # die "usage: $0 [-d] [-p] [-c|-h] srcfile where -c produce only .c files -h produce only .h files -p use the path component in the source file for destination dir -l set line width for output files [80] -d switch on debugging " unless ($cfile or $hfile) and $#filenames != -1; # FIXME should be able to do this more easily # $tmpdir = $ENV{'TMPDIR'}; # environment variables $tmpdir = $ENV{'TMP'} if !$tmpdir; $tmpdir = $ENV{'TEMP'} if !$tmpdir; $tmpdir = '/tmp' # look for a physical directory if !$tmpdir and -d '/tmp'; $tmpdir = '/usr/tmp' if !$tmpdir and -d '/usr/tmp'; $tmpdir = '/var/tmp' if !$tmpdir and -d '/var/tmp'; $tmpdir = '.' # give up and use current dir if !$tmpdir; foreach $src ( @filenames ) { # Names of the created files $ctmpname = "$tmpdir/ctmp.$$"; $htmpname = "$tmpdir/htmp.$$"; ($name, $path, $suffix) = &fileparse($src, '.m'); $path = '.' if $keepcurrentdir; $cfilename="$path/$name.c"; $hfilename="$path/$name.h"; warn "Processing from $src to $cfile / $hfile via $ctmp / $htmp" if $debug; die "Could not open $src, $!" if !open SRC, "$src"; die "Could not open $ctmpname, $!" if $cfile and !open CFILE, ">$ctmpname"; die "Could not open $htmpname, $!" if $hfile and !open HFILE, ">$htmpname"; if ( $cfile ) { # Produce the header of the C file # print CFILE "/*\n"; print CFILE " * This file is produced automatically.\n"; print CFILE " * Do not modify anything in here by hand.\n"; print CFILE " *\n"; print CFILE " * Created from\n"; print CFILE " * $src\n"; print CFILE " * with\n"; print CFILE " * $0\n"; print CFILE " */\n"; print CFILE "\n"; print CFILE "#include \n"; print CFILE "#include \n"; print CFILE "#include \n"; print CFILE "#include \n"; } if ( $hfile ) { # Produce the header of the H file # print HFILE "/*\n"; print HFILE " * This file is produced automatically.\n"; print HFILE " * Do not modify anything in here by hand.\n"; print HFILE " *\n"; print HFILE " * Created from\n"; print HFILE " * $src\n"; print HFILE " * with\n"; print HFILE " * $0\n"; print HFILE " */\n"; print HFILE "\n"; } %methods = (); # clear list of methods $lineno = 0; $error = 0; # to signal clean up and gerror setting LINE: while ( $line = ) { $lineno++; # take special notice of include directives. # if ( $line =~ m/^#\s*include\s+(["<])([^">]+)([">]).*/i ) { warn "Included file: $1$2" . ($1 eq '<'? '>':'"') if $debug; print CFILE "#include $1$2" . ($1 eq '<'? '>':'"') . "\n" if $cfile; } $line =~ s/#.*//; # remove comments $line =~ s/^\s+//; # remove leading ... $line =~ s/\s+$//; # remove trailing whitespace if ( $line =~ m/^$/ ) { # skip empty lines # nop } elsif ( $line =~ m/^INTERFACE\s*([^\s;]*)(\s*;?)/i ) { $intname = $1; $semicolon = $2; unless ( $intname =~ m/^[a-z_][a-z0-9_]*$/ ) { warn $line if $debug; warn "$src:$lineno: Invalid interface name '$intname', use [a-z_][a-z0-9_]*"; $error = 1; last LINE; } warn "$src:$lineno: semicolon missing at end of line, no problem" if $semicolon !~ s/;$//; warn "Interface $intname" if $debug; print HFILE '#ifndef _'.$intname."_if_h_\n" if $hfile; print HFILE '#define _'.$intname."_if_h_\n\n" if $hfile; print CFILE '#include "'.$intname.'_if.h"'."\n\n" if $cfile; } elsif ( $line =~ m/^METHOD/i ) { # Get the return type function name and delete that from # the line. What is left is the possibly first function argument # if it is on the same line. # # FIXME For compatibilities sake METHOD and METHODE is accepted. # if ( !$intname ) { warn "$src:$lineno: No interface name defined"; $error = 1; last LINE; } $line =~ s/^METHODE?\s+([^{]+?)\s*{\s*//i; @ret = split m/\s+/, $1; $name = pop @ret; # last element is name of method $ret = join(" ", @ret); # return type warn "Method: name=$name return type=$ret" if $debug; if ( !$name or !$ret ) { warn $line if $debug; warn "$src:$lineno: Invalid method specification"; $error = 1; last LINE; } unless ( $name =~ m/^[a-z_][a-z_0-9]*$/ ) { warn $line if $debug; warn "$src:$lineno: Invalid method name '$name', use [a-z_][a-z0-9_]*"; $error = 1; last LINE; } if ( defined($methods{$name}) ) { warn "$src:$lineno: Duplicate method name"; $error = 1; last LINE; } $methods{$name} = 'VIS'; while ( $line !~ m/}/ and $line .= ) { $lineno++ } if ( $line !~ s/};?(.*)// ) { # remove first '}' and trailing garbage # The '}' was not there (the rest is optional), so complain warn "$src:$lineno: Premature end of file"; $error = 1; last LINE; } warn "$src:$lineno: Ignored '$1'" # warn about garbage at end of line if $debug and $1; # Create a list of variables without the types prepended # $line =~ s/^\s+//; # remove leading ... $line =~ s/\s+$//; # ... and trailing whitespace $line =~ s/\s+/ /; # remove double spaces @arguments = split m/\s*;\s*/, $line; @varnames = (); # list of varnames foreach $argument (@arguments) { next # skip argument if argument is empty if !$argument; @ar = split m/[*\s]+/, $argument; if ( $#ar == 0 ) { # only 1 word in argument? warn "$src:$lineno: no type for '$argument'"; $error = 1; last LINE; } push @varnames, $ar[-1]; # last element is name of variable }; warn 'Arguments: ' . join(', ', @arguments) . "\n" . 'Varnames: ' . join(', ', @varnames) if $debug; $mname = $intname.'_'.$name; # method name $umname = uc($mname); # uppercase method name $arguments = join(", ", @arguments); $varnames = join(", ", @varnames); if ( $hfile ) { # the method description print HFILE "extern struct device_op_desc $mname\_desc;\n"; # the method typedef print HFILE &format_line("typedef $ret $mname\_t($arguments);", $line_width, ', ', ',',' ' x length("typedef $ret $mname\_t(")) . "\n"; # the method declaration print HFILE "$mname\_t $umname;\n\n"; } if ( $cfile ) { # Print out the method desc print CFILE "struct device_op_desc $mname\_desc = {\n"; - print CFILE "\t0, \"$mname\"\n"; + print CFILE "\t0, 0, \"$mname\"\n"; print CFILE "};\n\n"; # Print out the method itself if ( 0 ) { # haven't chosen the format yet print CFILE "$ret $umname($varnames)\n"; print CFILE "\t".join(";\n\t", @arguments).";\n"; } else { print CFILE &format_line("$ret $umname($arguments)", $line_width, ', ', ',', ' ' x length("$ret $umname(")) . "\n"; } print CFILE "{\n"; print CFILE &format_line("\t$mname\_t *m = ($mname\_t *) DEVOPMETH(dev, $mname);", $line_width-8, ' = ', ' =', "\t\t") . "\n"; print CFILE "\t".($ret eq 'void'? '':'return ') . "m($varnames);\n"; print CFILE "}\n\n"; } } else { warn $line if $debug; warn "$src:$lineno: Invalid line encountered"; $error = 1; last LINE; } } # end LINE # print the final '#endif' in the header file # print HFILE "#endif /* _".$intname."_if_h_ */\n" if $hfile; close SRC; close CFILE if $cfile; close HFILE if $hfile; if ( !$error ) { if ( $cfile ) { ($rc = system("mv $ctmpname $cfilename")) and warn "mv $ctmpname $cfilename failed, $rc"; } if ( $hfile ) { ($rc = system("mv $htmpname $hfilename")) and warn "mv $htmpname $hfilename failed, $rc"; } } else { warn 'File' . ($hfile and $cfile? 's':'') . ' skipped'; ($rc = system("rm -f $htmpname $ctmpname")) and warn "rm -f $htmpname $ctmpname failed, $rc"; $gerror = 1; } } exit $gerror; sub format_line { my ($line, $maxlength, $break, $new_end, $new_start) = @_; my $rline = ""; while ( length($line) > $maxlength and ($i = rindex $line, $break, $maxlength-length($new_end)) != -1 ) { $rline .= substr($line, 0, $i) . $new_end . "\n"; $line = $new_start . substr($line, $i+length($break)); } return $rline . $line; } Index: head/sys/kern/subr_bus.c =================================================================== --- head/sys/kern/subr_bus.c (revision 45719) +++ head/sys/kern/subr_bus.c (revision 45720) @@ -1,1833 +1,2135 @@ /*- * Copyright (c) 1997,1998 Doug Rabson * 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. * - * $Id: subr_bus.c,v 1.15 1999/01/27 21:49:57 dillon Exp $ + * $Id: subr_bus.c,v 1.16 1999/03/29 08:54:20 dfr Exp $ */ #include #include #include #include #include #include #include #include #include /* for device_printf() */ #include "opt_bus.h" #ifdef BUS_DEBUG #define PDEBUG(a) (printf(__FUNCTION__ ":%d: ", __LINE__), printf a, printf("\n")) #define DEVICENAME(d) ((d)? device_get_name(d): "no device") #define DRIVERNAME(d) ((d)? d->name : "no driver") #define DEVCLANAME(d) ((d)? d->name : "no devclass") /* Produce the indenting, indent*2 spaces plus a '.' ahead of that to * prevent syslog from deleting initial spaces */ #define indentprintf(p) do { int iJ; printf("."); for (iJ=0; iJname)) { - desc->offset = methods[i].offset; - PDEBUG(("methods[%d] has the same name, %s, with offset %d", - i, desc->name, desc->offset)); + if (desc->method) { + desc->method->refs++; + return; + } + + for (m = LIST_FIRST(&methods); m; m = LIST_NEXT(m, link)) { + if (!strcmp(m->name, desc->name)) { + desc->offset = m->offset; + desc->method = m; + m->refs++; + PDEBUG(("methods %x has the same name, %s, with offset %d", + m, desc->name, desc->offset)); return; } + } - if (methods_count == methods_size) { - struct method* p; - - methods_size += 10; - p = (struct method*) malloc(methods_size * sizeof(struct method), - M_DEVBUF, M_NOWAIT); - if (!p) + m = (struct method *) malloc(sizeof(struct method) + + strlen(desc->name) + 1, + M_DEVBUF, M_NOWAIT); + if (!m) panic("register_method: out of memory"); - if (methods) { - bcopy(methods, p, methods_count * sizeof(struct method)); - free(methods, M_DEVBUF); - } - methods = p; - } - m = &methods[methods_count++]; - m->name = malloc(strlen(desc->name) + 1, M_DEVBUF, M_NOWAIT); - if (!m->name) - panic("register_method: out of memory"); + bzero(m, sizeof(struct method) + strlen(desc->name) + 1); + m->offset = next_method_offset++; + m->refs = 1; + m->name = (char*) (m + 1); strcpy(m->name, desc->name); - desc->offset = m->offset = next_method_offset++; + LIST_INSERT_HEAD(&methods, m, link); + + desc->offset = m->offset; + desc->method = m; } +static void +unregister_method(struct device_op_desc *desc) +{ + struct method *m = desc->method; + m->refs--; + if (m->refs == 0) { + LIST_REMOVE(m, link); + free(m, M_DEVBUF); + } + desc->method = 0; +} + static int error_method(void) { return ENXIO; } static struct device_ops null_ops = { 1, { error_method } }; static void compile_methods(driver_t *driver) { device_ops_t ops; struct device_method *m; int i; /* * First register any methods which need it. */ for (i = 0, m = driver->methods; m->desc; i++, m++) - if (!m->desc->offset) - register_method(m->desc); - else - PDEBUG(("offset not equal to zero, method desc %d left as is", i)); + register_method(m->desc); /* * Then allocate the compiled op table. */ ops = malloc(sizeof(struct device_ops) + (next_method_offset-1) * sizeof(devop_t), M_DEVBUF, M_NOWAIT); if (!ops) panic("compile_methods: out of memory"); + bzero(ops, sizeof(struct device_ops) + (next_method_offset-1) * sizeof(devop_t)); ops->maxoffset = next_method_offset; for (i = 0; i < next_method_offset; i++) ops->methods[i] = error_method; for (i = 0, m = driver->methods; m->desc; i++, m++) ops->methods[m->desc->offset] = m->func; PDEBUG(("%s has %d method%s, wasting %d bytes", DRIVERNAME(driver), i, (i==1?"":"s"), (next_method_offset-i)*sizeof(devop_t))); driver->ops = ops; } +static void +free_methods(driver_t *driver) +{ + int i; + struct device_method *m; + + /* + * Unregister any methods which are no longer used. + */ + for (i = 0, m = driver->methods; m->desc; i++, m++) + unregister_method(m->desc); + + /* + * Free memory and clean up. + */ + free(driver->ops, M_DEVBUF); + driver->ops = 0; +} + /* * Devclass implementation */ static devclass_list_t devclasses = TAILQ_HEAD_INITIALIZER(devclasses); static devclass_t devclass_find_internal(const char *classname, int create) { devclass_t dc; PDEBUG(("looking for %s", classname)); if (!classname) return NULL; for (dc = TAILQ_FIRST(&devclasses); dc; dc = TAILQ_NEXT(dc, link)) if (!strcmp(dc->name, classname)) return dc; PDEBUG(("%s not found%s", classname, (create? ", creating": ""))); if (create) { dc = malloc(sizeof(struct devclass) + strlen(classname) + 1, M_DEVBUF, M_NOWAIT); if (!dc) return NULL; + bzero(dc, sizeof(struct devclass) + strlen(classname) + 1); dc->name = (char*) (dc + 1); strcpy(dc->name, classname); dc->devices = NULL; dc->maxunit = 0; dc->nextunit = 0; TAILQ_INIT(&dc->drivers); TAILQ_INSERT_TAIL(&devclasses, dc, link); } return dc; } devclass_t devclass_find(const char *classname) { return devclass_find_internal(classname, FALSE); } int devclass_add_driver(devclass_t dc, driver_t *driver) { driverlink_t dl; + int i; PDEBUG(("%s", DRIVERNAME(driver))); dl = malloc(sizeof *dl, M_DEVBUF, M_NOWAIT); if (!dl) return ENOMEM; + bzero(dl, sizeof *dl); /* - * Compile the drivers methods. + * Compile the driver's methods. */ - compile_methods(driver); + if (!driver->ops) + compile_methods(driver); /* * Make sure the devclass which the driver is implementing exists. */ devclass_find_internal(driver->name, TRUE); dl->driver = driver; TAILQ_INSERT_TAIL(&dc->drivers, dl, link); + driver->refs++; + /* + * Call BUS_DRIVER_ADDED for any existing busses in this class. + */ + for (i = 0; i < dc->maxunit; i++) + if (dc->devices[i]) + BUS_DRIVER_ADDED(dc->devices[i], driver); + return 0; } int devclass_delete_driver(devclass_t busclass, driver_t *driver) { devclass_t dc = devclass_find(driver->name); driverlink_t dl; device_t dev; int i; int error; PDEBUG(("%s from devclass %s", driver->name, DEVCLANAME(busclass))); if (!dc) return 0; /* * Find the link structure in the bus' list of drivers. */ for (dl = TAILQ_FIRST(&busclass->drivers); dl; dl = TAILQ_NEXT(dl, link)) { if (dl->driver == driver) break; } if (!dl) { PDEBUG(("%s not found in %s list", driver->name, busclass->name)); return ENOENT; } /* * Disassociate from any devices. We iterate through all the * devices in the devclass of the driver and detach any which are * using the driver and which have a parent in the devclass which * we are deleting from. * * Note that since a driver can be in multiple devclasses, we * should not detach devices which are not children of devices in * the affected devclass. */ for (i = 0; i < dc->maxunit; i++) { if (dc->devices[i]) { dev = dc->devices[i]; if (dev->driver == driver && dev->parent && dev->parent->devclass == busclass) { if ((error = device_detach(dev)) != 0) return error; device_set_driver(dev, NULL); } } } TAILQ_REMOVE(&busclass->drivers, dl, link); free(dl, M_DEVBUF); + driver->refs--; + if (driver->refs == 0) + free_methods(driver); + return 0; } static driverlink_t devclass_find_driver_internal(devclass_t dc, const char *classname) { driverlink_t dl; PDEBUG(("%s in devclass %s", classname, DEVCLANAME(dc))); for (dl = TAILQ_FIRST(&dc->drivers); dl; dl = TAILQ_NEXT(dl, link)) { if (!strcmp(dl->driver->name, classname)) return dl; } PDEBUG(("not found")); return NULL; } driver_t * devclass_find_driver(devclass_t dc, const char *classname) { driverlink_t dl; dl = devclass_find_driver_internal(dc, classname); if (dl) return dl->driver; else return NULL; } const char * devclass_get_name(devclass_t dc) { return dc->name; } device_t devclass_get_device(devclass_t dc, int unit) { if (unit < 0 || unit >= dc->maxunit) return NULL; return dc->devices[unit]; } void * devclass_get_softc(devclass_t dc, int unit) { device_t dev; if (unit < 0 || unit >= dc->maxunit) return NULL; dev = dc->devices[unit]; if (!dev || dev->state < DS_ATTACHED) return NULL; return dev->softc; } int devclass_get_devices(devclass_t dc, device_t **devlistp, int *devcountp) { int i; int count; device_t *list; count = 0; for (i = 0; i < dc->maxunit; i++) if (dc->devices[i]) count++; list = malloc(count * sizeof(device_t), M_TEMP, M_NOWAIT); if (!list) return ENOMEM; + bzero(list, count * sizeof(device_t)); count = 0; for (i = 0; i < dc->maxunit; i++) if (dc->devices[i]) { list[count] = dc->devices[i]; count++; } *devlistp = list; *devcountp = count; return 0; } int devclass_get_maxunit(devclass_t dc) { return dc->maxunit; } static int devclass_alloc_unit(devclass_t dc, int *unitp) { int unit = *unitp; PDEBUG(("unit %d in devclass %s", unit, DEVCLANAME(dc))); /* * If we have been given a wired unit number, check for existing * device. */ if (unit != -1) { device_t dev; dev = devclass_get_device(dc, unit); if (dev) { printf("devclass_alloc_unit: %s%d already exists, using next available unit number\n", dc->name, unit); unit = -1; } } if (unit == -1) { unit = dc->nextunit; dc->nextunit++; } else if (dc->nextunit <= unit) dc->nextunit = unit + 1; if (unit >= dc->maxunit) { device_t *newlist; int newsize; newsize = (dc->maxunit ? 2 * dc->maxunit : MINALLOCSIZE / sizeof(device_t)); newlist = malloc(sizeof(device_t) * newsize, M_DEVBUF, M_NOWAIT); if (!newlist) return ENOMEM; bcopy(dc->devices, newlist, sizeof(device_t) * dc->maxunit); bzero(newlist + dc->maxunit, sizeof(device_t) * (newsize - dc->maxunit)); if (dc->devices) free(dc->devices, M_DEVBUF); dc->devices = newlist; dc->maxunit = newsize; } PDEBUG(("now: unit %d in devclass %s", unit, DEVCLANAME(dc))); *unitp = unit; return 0; } static int devclass_add_device(devclass_t dc, device_t dev) { int buflen, error; PDEBUG(("%s in devclass %s", DEVICENAME(dev), DEVCLANAME(dc))); buflen = strlen(dc->name) + 5; dev->nameunit = malloc(buflen, M_DEVBUF, M_NOWAIT); if (!dev->nameunit) return ENOMEM; + bzero(dev->nameunit, buflen); if ((error = devclass_alloc_unit(dc, &dev->unit)) != 0) { free(dev->nameunit, M_DEVBUF); dev->nameunit = NULL; return error; } dc->devices[dev->unit] = dev; dev->devclass = dc; snprintf(dev->nameunit, buflen, "%s%d", dc->name, dev->unit); #ifdef DEVICE_SYSCTLS device_register_oids(dev); #endif return 0; } static int devclass_delete_device(devclass_t dc, device_t dev) { if (!dc || !dev) return 0; PDEBUG(("%s in devclass %s", DEVICENAME(dev), DEVCLANAME(dc))); if (dev->devclass != dc || dc->devices[dev->unit] != dev) panic("devclass_delete_device: inconsistent device class"); dc->devices[dev->unit] = NULL; if (dev->flags & DF_WILDCARD) dev->unit = -1; dev->devclass = NULL; free(dev->nameunit, M_DEVBUF); dev->nameunit = NULL; while (dc->nextunit > 0 && dc->devices[dc->nextunit - 1] == NULL) dc->nextunit--; #ifdef DEVICE_SYSCTLS device_unregister_oids(dev); #endif return 0; } static device_t make_device(device_t parent, const char *name, int unit, void *ivars) { device_t dev; devclass_t dc; PDEBUG(("%s at %s as unit %d with%s ivars", name, DEVICENAME(parent), unit, (ivars? "":"out"))); if (name) { dc = devclass_find_internal(name, TRUE); if (!dc) { printf("make_device: can't find device class %s\n", name); return NULL; } } else dc = NULL; dev = malloc(sizeof(struct device), M_DEVBUF, M_NOWAIT); if (!dev) return 0; + bzero(dev, sizeof(struct device)); dev->parent = parent; TAILQ_INIT(&dev->children); dev->ops = &null_ops; dev->driver = NULL; dev->devclass = NULL; dev->unit = unit; dev->nameunit = NULL; dev->desc = NULL; dev->busy = 0; dev->flags = DF_ENABLED; if (unit == -1) dev->flags |= DF_WILDCARD; if (name) { dev->flags |= DF_FIXEDCLASS; devclass_add_device(dc, dev); } dev->ivars = ivars; dev->softc = NULL; dev->state = DS_NOTPRESENT; return dev; } static void device_print_child(device_t dev, device_t child) { printf("%s%d", device_get_name(child), device_get_unit(child)); if (device_is_alive(child)) { if (device_get_desc(child)) printf(": <%s>", device_get_desc(child)); BUS_PRINT_CHILD(dev, child); } else printf(" not found"); printf("\n"); } device_t device_add_child(device_t dev, const char *name, int unit, void *ivars) { device_t child; PDEBUG(("%s at %s as unit %d with%s ivars", name, DEVICENAME(dev), unit, (ivars? "":"out"))); child = make_device(dev, name, unit, ivars); if (child) TAILQ_INSERT_TAIL(&dev->children, child, link); else PDEBUG(("%s failed", name)); return child; } device_t device_add_child_after(device_t dev, device_t place, const char *name, int unit, void *ivars) { device_t child; PDEBUG(("%s at %s after %s as unit %d with%s ivars", name, DEVICENAME(dev), DEVICENAME(place), unit, (ivars? "":"out"))); child = make_device(dev, name, unit, ivars); if (place) { TAILQ_INSERT_AFTER(&dev->children, place, dev, link); } else { TAILQ_INSERT_HEAD(&dev->children, dev, link); } return child; } int device_delete_child(device_t dev, device_t child) { int error; device_t grandchild; PDEBUG(("%s from %s", DEVICENAME(child), DEVICENAME(dev))); /* remove children first */ while ( (grandchild = TAILQ_FIRST(&child->children)) ) { error = device_delete_child(child, grandchild); if (error) return error; } if ((error = device_detach(child)) != 0) return error; if (child->devclass) devclass_delete_device(child->devclass, child); TAILQ_REMOVE(&dev->children, child, link); device_set_desc(child, NULL); free(child, M_DEVBUF); return 0; } /* * Find only devices attached to this bus. */ device_t device_find_child(device_t dev, const char *classname, int unit) { devclass_t dc; device_t child; dc = devclass_find(classname); if (!dc) return NULL; child = devclass_get_device(dc, unit); if (child && child->parent == dev) return child; return NULL; } static driverlink_t first_matching_driver(devclass_t dc, device_t dev) { if (dev->devclass) return devclass_find_driver_internal(dc, dev->devclass->name); else return TAILQ_FIRST(&dc->drivers); } static driverlink_t next_matching_driver(devclass_t dc, device_t dev, driverlink_t last) { if (dev->devclass) { driverlink_t dl; for (dl = TAILQ_NEXT(last, link); dl; dl = TAILQ_NEXT(dl, link)) if (!strcmp(dev->devclass->name, dl->driver->name)) return dl; return NULL; } else return TAILQ_NEXT(last, link); } static int device_probe_child(device_t dev, device_t child) { devclass_t dc; driverlink_t dl; dc = dev->devclass; if (dc == NULL) panic("device_probe_child: parent device has no devclass"); if (child->state == DS_ALIVE) return 0; for (dl = first_matching_driver(dc, child); dl; dl = next_matching_driver(dc, child, dl)) { PDEBUG(("Trying %s", DRIVERNAME(dl->driver))); device_set_driver(child, dl->driver); if (DEVICE_PROBE(child) == 0) { if (!child->devclass) device_set_devclass(child, dl->driver->name); child->state = DS_ALIVE; return 0; } } return ENXIO; } device_t device_get_parent(device_t dev) { return dev->parent; } int device_get_children(device_t dev, device_t **devlistp, int *devcountp) { int count; device_t child; device_t *list; count = 0; for (child = TAILQ_FIRST(&dev->children); child; child = TAILQ_NEXT(child, link)) count++; list = malloc(count * sizeof(device_t), M_TEMP, M_NOWAIT); if (!list) return ENOMEM; + bzero(list, count * sizeof(device_t)); count = 0; for (child = TAILQ_FIRST(&dev->children); child; child = TAILQ_NEXT(child, link)) { list[count] = child; count++; } *devlistp = list; *devcountp = count; return 0; } driver_t * device_get_driver(device_t dev) { return dev->driver; } devclass_t device_get_devclass(device_t dev) { return dev->devclass; } const char * device_get_name(device_t dev) { if (dev->devclass) return devclass_get_name(dev->devclass); return NULL; } const char * device_get_nameunit(device_t dev) { return dev->nameunit; } int device_get_unit(device_t dev) { return dev->unit; } const char * device_get_desc(device_t dev) { return dev->desc; } void device_print_prettyname(device_t dev) { const char *name = device_get_name(dev); if (name == 0) name = "(no driver assigned)"; printf("%s%d: ", name, device_get_unit(dev)); } void device_printf(device_t dev, const char * fmt, ...) { va_list ap; device_print_prettyname(dev); va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); } static void device_set_desc_internal(device_t dev, const char* desc, int copy) { if (dev->desc && (dev->flags & DF_DESCMALLOCED)) { free(dev->desc, M_DEVBUF); dev->flags &= ~DF_DESCMALLOCED; dev->desc = NULL; } if (copy && desc) { dev->desc = malloc(strlen(desc) + 1, M_DEVBUF, M_NOWAIT); if (dev->desc) { strcpy(dev->desc, desc); dev->flags |= DF_DESCMALLOCED; } } else /* Avoid a -Wcast-qual warning */ dev->desc = (char *)(uintptr_t) desc; #ifdef DEVICE_SYSCTLS { struct sysctl_oid *oid = &dev->oid[1]; oid->oid_arg1 = dev->desc ? dev->desc : ""; oid->oid_arg2 = dev->desc ? strlen(dev->desc) : 0; } #endif } void device_set_desc(device_t dev, const char* desc) { device_set_desc_internal(dev, desc, FALSE); } void device_set_desc_copy(device_t dev, const char* desc) { device_set_desc_internal(dev, desc, TRUE); } void * device_get_softc(device_t dev) { return dev->softc; } void * device_get_ivars(device_t dev) { return dev->ivars; } device_state_t device_get_state(device_t dev) { return dev->state; } void device_enable(device_t dev) { dev->flags |= DF_ENABLED; } void device_disable(device_t dev) { dev->flags &= ~DF_ENABLED; } void device_busy(device_t dev) { if (dev->state < DS_ATTACHED) panic("device_busy: called for unattached device"); if (dev->busy == 0 && dev->parent) device_busy(dev->parent); dev->busy++; dev->state = DS_BUSY; } void device_unbusy(device_t dev) { if (dev->state != DS_BUSY) panic("device_unbusy: called for non-busy device"); dev->busy--; if (dev->busy == 0) { if (dev->parent) device_unbusy(dev->parent); dev->state = DS_ATTACHED; } } void device_quiet(device_t dev) { dev->flags |= DF_QUIET; } void device_verbose(device_t dev) { dev->flags &= ~DF_QUIET; } int device_is_quiet(device_t dev) { return (dev->flags & DF_QUIET) != 0; } int device_is_enabled(device_t dev) { return (dev->flags & DF_ENABLED) != 0; } int device_is_alive(device_t dev) { return dev->state >= DS_ALIVE; } int device_set_devclass(device_t dev, const char *classname) { devclass_t dc; if (dev->devclass) { printf("device_set_devclass: device class already set\n"); return EINVAL; } dc = devclass_find_internal(classname, TRUE); if (!dc) return ENOMEM; return devclass_add_device(dc, dev); } int device_set_driver(device_t dev, driver_t *driver) { if (dev->state >= DS_ATTACHED) return EBUSY; if (dev->driver == driver) return 0; if (dev->softc) { free(dev->softc, M_DEVBUF); dev->softc = NULL; } dev->ops = &null_ops; dev->driver = driver; if (driver) { dev->ops = driver->ops; dev->softc = malloc(driver->softc, M_DEVBUF, M_NOWAIT); if (!dev->softc) { dev->ops = &null_ops; dev->driver = NULL; return ENOMEM; } bzero(dev->softc, driver->softc); } return 0; } int device_probe_and_attach(device_t dev) { device_t bus = dev->parent; int error = 0; if (dev->state >= DS_ALIVE) return 0; if (dev->flags & DF_ENABLED) { error = device_probe_child(bus, dev); if (!error) { if (!device_is_quiet(dev)) device_print_child(bus, dev); error = DEVICE_ATTACH(dev); if (!error) dev->state = DS_ATTACHED; else { printf("device_probe_and_attach: %s%d attach returned %d\n", dev->driver->name, dev->unit, error); device_set_driver(dev, NULL); dev->state = DS_NOTPRESENT; } } } else { device_print_prettyname(dev); printf("not probed (disabled)\n"); } return error; } int device_detach(device_t dev) { int error; PDEBUG(("%s", DEVICENAME(dev))); if (dev->state == DS_BUSY) return EBUSY; if (dev->state != DS_ATTACHED) return 0; if ((error = DEVICE_DETACH(dev)) != 0) return error; if (dev->parent) BUS_CHILD_DETACHED(dev->parent, dev); if (!(dev->flags & DF_FIXEDCLASS)) devclass_delete_device(dev->devclass, dev); dev->state = DS_NOTPRESENT; device_set_driver(dev, NULL); return 0; } int device_shutdown(device_t dev) { if (dev->state < DS_ATTACHED) return 0; return DEVICE_SHUTDOWN(dev); } #ifdef DEVICE_SYSCTLS /* * Sysctl nodes for devices. */ SYSCTL_NODE(_hw, OID_AUTO, devices, CTLFLAG_RW, 0, "A list of all devices"); static int sysctl_handle_children SYSCTL_HANDLER_ARGS { device_t dev = arg1; device_t child; int first = 1, error = 0; for (child = TAILQ_FIRST(&dev->children); child; child = TAILQ_NEXT(child, link)) { if (child->nameunit) { if (!first) { error = SYSCTL_OUT(req, ",", 1); if (error) return error; } else { first = 0; } error = SYSCTL_OUT(req, child->nameunit, strlen(child->nameunit)); if (error) return error; } } error = SYSCTL_OUT(req, "", 1); return error; } static int sysctl_handle_state SYSCTL_HANDLER_ARGS { device_t dev = arg1; switch (dev->state) { case DS_NOTPRESENT: return SYSCTL_OUT(req, "notpresent", sizeof("notpresent")); case DS_ALIVE: return SYSCTL_OUT(req, "alive", sizeof("alive")); case DS_ATTACHED: return SYSCTL_OUT(req, "attached", sizeof("attached")); case DS_BUSY: return SYSCTL_OUT(req, "busy", sizeof("busy")); } return 0; } static void device_register_oids(device_t dev) { struct sysctl_oid* oid; oid = &dev->oid[0]; bzero(oid, sizeof(*oid)); oid->oid_parent = &sysctl__hw_devices_children; oid->oid_number = OID_AUTO; oid->oid_kind = CTLTYPE_NODE | CTLFLAG_RW; oid->oid_arg1 = &dev->oidlist[0]; oid->oid_arg2 = 0; oid->oid_name = dev->nameunit; oid->oid_handler = 0; oid->oid_fmt = "N"; SLIST_INIT(&dev->oidlist[0]); sysctl_register_oid(oid); oid = &dev->oid[1]; bzero(oid, sizeof(*oid)); oid->oid_parent = &dev->oidlist[0]; oid->oid_number = OID_AUTO; oid->oid_kind = CTLTYPE_STRING | CTLFLAG_RD; oid->oid_arg1 = dev->desc ? dev->desc : ""; oid->oid_arg2 = dev->desc ? strlen(dev->desc) : 0; oid->oid_name = "desc"; oid->oid_handler = sysctl_handle_string; oid->oid_fmt = "A"; sysctl_register_oid(oid); oid = &dev->oid[2]; bzero(oid, sizeof(*oid)); oid->oid_parent = &dev->oidlist[0]; oid->oid_number = OID_AUTO; oid->oid_kind = CTLTYPE_INT | CTLFLAG_RD; oid->oid_arg1 = dev; oid->oid_arg2 = 0; oid->oid_name = "children"; oid->oid_handler = sysctl_handle_children; oid->oid_fmt = "A"; sysctl_register_oid(oid); oid = &dev->oid[3]; bzero(oid, sizeof(*oid)); oid->oid_parent = &dev->oidlist[0]; oid->oid_number = OID_AUTO; oid->oid_kind = CTLTYPE_INT | CTLFLAG_RD; oid->oid_arg1 = dev; oid->oid_arg2 = 0; oid->oid_name = "state"; oid->oid_handler = sysctl_handle_state; oid->oid_fmt = "A"; sysctl_register_oid(oid); } static void device_unregister_oids(device_t dev) { sysctl_unregister_oid(&dev->oid[0]); sysctl_unregister_oid(&dev->oid[1]); sysctl_unregister_oid(&dev->oid[2]); } #endif +/*======================================*/ /* * Access functions for device resources. */ -extern struct config_device devtab[]; + +/* Supplied by config(8) in ioconf.c */ +extern struct config_device config_devtab[]; extern int devtab_count; +/* Runtime version */ +struct config_device *devtab = config_devtab; + static int +resource_new_name(char *name, int unit) +{ + struct config_device *new; + + new = malloc((devtab_count + 1) * sizeof(*new), M_TEMP, M_NOWAIT); + if (new == NULL) + return -1; + if (devtab && devtab_count > 0) + bcopy(devtab, new, devtab_count * sizeof(*new)); + bzero(&new[devtab_count], sizeof(*new)); + new[devtab_count].name = malloc(strlen(name) + 1, M_TEMP, M_NOWAIT); + if (new[devtab_count].name == NULL) { + free(new, M_TEMP); + return -1; + } + strcpy(new[devtab_count].name, name); + new[devtab_count].unit = unit; + new[devtab_count].resource_count = 0; + new[devtab_count].resources = NULL; + devtab = new; + return devtab_count++; +} + +static int +resource_new_resname(int j, char *resname, resource_type type) +{ + struct config_resource *new; + int i; + + i = devtab[j].resource_count; + new = malloc((i + 1) * sizeof(*new), M_TEMP, M_NOWAIT); + if (new == NULL) + return -1; + if (devtab[j].resources && i > 0) + bcopy(devtab[j].resources, new, i * sizeof(*new)); + bzero(&new[i], sizeof(*new)); + new[i].name = malloc(strlen(resname) + 1, M_TEMP, M_NOWAIT); + if (new[i].name == NULL) { + free(new, M_TEMP); + return -1; + } + strcpy(new[i].name, resname); + new[i].type = type; + if (devtab[j].resources) + free(devtab[j].resources, M_TEMP); + devtab[j].resources = new; + devtab[j].resource_count = i + 1; + return i; +} + +static int resource_match_string(int i, char *resname, char *value) { int j; struct config_resource *res; for (j = 0, res = devtab[i].resources; j < devtab[i].resource_count; j++, res++) if (!strcmp(res->name, resname) && res->type == RES_STRING && !strcmp(res->u.stringval, value)) - return TRUE; - return FALSE; + return j; + return -1; } static int resource_find(const char *name, int unit, char *resname, struct config_resource **result) { int i, j; struct config_resource *res; /* * First check specific instances, then generic. */ for (i = 0; i < devtab_count; i++) { if (devtab[i].unit < 0) continue; if (!strcmp(devtab[i].name, name) && devtab[i].unit == unit) { res = devtab[i].resources; for (j = 0; j < devtab[i].resource_count; j++, res++) if (!strcmp(res->name, resname)) { *result = res; return 0; } } } for (i = 0; i < devtab_count; i++) { if (devtab[i].unit >= 0) continue; if (!strcmp(devtab[i].name, name) && devtab[i].unit == unit) { res = devtab[i].resources; for (j = 0; j < devtab[i].resource_count; j++, res++) if (!strcmp(res->name, resname)) { *result = res; return 0; } } } return ENOENT; } int resource_int_value(const char *name, int unit, char *resname, int *result) { int error; struct config_resource *res; + if ((error = resource_find(name, unit, resname, &res)) != 0) return error; if (res->type != RES_INT) return EFTYPE; *result = res->u.intval; return 0; } int resource_long_value(const char *name, int unit, char *resname, long *result) { int error; struct config_resource *res; + if ((error = resource_find(name, unit, resname, &res)) != 0) return error; if (res->type != RES_LONG) return EFTYPE; *result = res->u.longval; return 0; } int resource_string_value(const char *name, int unit, char *resname, char **result) { int error; struct config_resource *res; + if ((error = resource_find(name, unit, resname, &res)) != 0) return error; if (res->type != RES_STRING) return EFTYPE; *result = res->u.stringval; return 0; } int resource_query_string(int i, char *resname, char *value) { if (i < 0) i = 0; else i = i + 1; for (; i < devtab_count; i++) - if (resource_match_string(i, resname, value)) + if (resource_match_string(i, resname, value) >= 0) return i; return -1; } +int +resource_locate(int i, char *resname) +{ + if (i < 0) + i = 0; + else + i = i + 1; + for (; i < devtab_count; i++) + if (!strcmp(devtab[i].name, resname)) + return i; + return -1; +} + +int +resource_count(void) +{ + return devtab_count; +} + char * resource_query_name(int i) { return devtab[i].name; } int resource_query_unit(int i) { return devtab[i].unit; } +static int +resource_create(char *name, int unit, char *resname, resource_type type, + struct config_resource **result) +{ + int i, j; + struct config_resource *res = NULL; + for (i = 0; i < devtab_count; i++) { + if (!strcmp(devtab[i].name, name) && devtab[i].unit == unit) { + res = devtab[i].resources; + break; + } + } + if (res == NULL) { + i = resource_new_name(name, unit); + if (i < 0) + return ENOMEM; + res = devtab[i].resources; + } + for (j = 0; j < devtab[i].resource_count; j++, res++) { + if (!strcmp(res->name, resname)) { + *result = res; + return 0; + } + } + j = resource_new_resname(i, resname, type); + if (j < 0) + return ENOMEM; + res = &devtab[i].resources[j]; + *result = res; + return 0; +} + +int +resource_set_int(int i, char *resname, int value) +{ + int error; + struct config_resource *res; + +printf("resource_set_int\n"); + if (i < 0 || i >= devtab_count) + return EINVAL; + error = resource_create(devtab[i].name, devtab[i].unit, resname, + RES_INT, &res); + if (error) + return error; + if (res->type != RES_INT) + return EFTYPE; + res->u.intval = value; + return 0; +} + +int +resource_set_long(int i, char *resname, long value) +{ + int error; + struct config_resource *res; + +printf("resource_set_long\n"); + if (i < 0 || i >= devtab_count) + return EINVAL; + error = resource_create(devtab[i].name, devtab[i].unit, resname, + RES_LONG, &res); + if (error) + return error; + if (res->type != RES_LONG) + return EFTYPE; + res->u.longval = value; + return 0; +} + +int +resource_set_string(int i, char *resname, char *value) +{ + int error; + struct config_resource *res; + +printf("resource_set_string\n"); + if (i < 0 || i >= devtab_count) + return EINVAL; + error = resource_create(devtab[i].name, devtab[i].unit, resname, + RES_STRING, &res); + if (error) + return error; + if (res->type != RES_STRING) + return EFTYPE; + if (res->u.stringval) + free(res->u.stringval, M_TEMP); + res->u.stringval = malloc(strlen(value) + 1, M_TEMP, M_NOWAIT); + if (res->u.stringval == NULL) + return ENOMEM; + strcpy(res->u.stringval, value); + return 0; +} + + +static void +resource_cfgload(void *dummy __unused) +{ + struct config_resource *res, *cfgres; + int i, j; + int error; + char *name, *resname; + int unit; + resource_type type; + char *stringval; + int config_devtab_count; + + config_devtab_count = devtab_count; + devtab = NULL; + devtab_count = 0; + + for (i = 0; i < config_devtab_count; i++) { + name = config_devtab[i].name; + unit = config_devtab[i].unit; + + for (j = 0; j < config_devtab[i].resource_count; j++) { + cfgres = config_devtab[i].resources; + resname = cfgres[j].name; + type = cfgres[j].type; + error = resource_create(name, unit, resname, type, + &res); + if (error) { + printf("create resource %s%d: error %d\n", + name, unit, error); + continue; + } + if (res->type != type) { + printf("type mismatch %s%d: %d != %d\n", + name, unit, res->type, type); + continue; + } + switch (type) { + case RES_INT: + res->u.intval = cfgres[j].u.intval; + break; + case RES_LONG: + res->u.longval = cfgres[j].u.longval; + break; + case RES_STRING: + if (res->u.stringval) + free(res->u.stringval, M_TEMP); + stringval = cfgres[j].u.stringval; + res->u.stringval = malloc(strlen(stringval) + 1, + M_TEMP, M_NOWAIT); + if (res->u.stringval == NULL) + break; + strcpy(res->u.stringval, stringval); + break; + default: + panic("unknown resource type %d\n", type); + } + } + } +} +SYSINIT(cfgload, SI_SUB_KMEM, SI_ORDER_ANY + 50, resource_cfgload, 0) + + +/*======================================*/ /* * Some useful method implementations to make life easier for bus drivers. */ int bus_generic_attach(device_t dev) { device_t child; for (child = TAILQ_FIRST(&dev->children); child; child = TAILQ_NEXT(child, link)) device_probe_and_attach(child); return 0; } int bus_generic_detach(device_t dev) { device_t child; int error; if (dev->state != DS_ATTACHED) return EBUSY; for (child = TAILQ_FIRST(&dev->children); child; child = TAILQ_NEXT(child, link)) if ((error = device_detach(child)) != 0) return error; return 0; } int bus_generic_shutdown(device_t dev) { device_t child; for (child = TAILQ_FIRST(&dev->children); child; child = TAILQ_NEXT(child, link)) - DEVICE_SHUTDOWN(child); + device_shutdown(child); return 0; } int bus_generic_suspend(device_t dev) { int error; device_t child, child2; for (child = TAILQ_FIRST(&dev->children); child; child = TAILQ_NEXT(child, link)) { error = DEVICE_SUSPEND(child); if (error) { for (child2 = TAILQ_FIRST(&dev->children); child2 && child2 != child; child2 = TAILQ_NEXT(child2, link)) DEVICE_RESUME(child2); return (error); } } return 0; } int bus_generic_resume(device_t dev) { device_t child; for (child = TAILQ_FIRST(&dev->children); child; child = TAILQ_NEXT(child, link)) { DEVICE_RESUME(child); /* if resume fails, there's nothing we can usefully do... */ } return 0; } void bus_generic_print_child(device_t dev, device_t child) { printf(" on %s%d", device_get_name(dev), device_get_unit(dev)); } int bus_generic_read_ivar(device_t dev, device_t child, int index, uintptr_t * result) { return ENOENT; } int bus_generic_write_ivar(device_t dev, device_t child, int index, uintptr_t value) { return ENOENT; } +void +bus_generic_driver_added(device_t dev, driver_t *driver) +{ + device_t child; + + for (child = TAILQ_FIRST(&dev->children); + child; child = TAILQ_NEXT(child, link)) + if (child->state == DS_NOTPRESENT) + device_probe_and_attach(child); +} + int bus_generic_setup_intr(device_t dev, device_t child, struct resource *irq, driver_intr_t *intr, void *arg, void **cookiep) { /* Propagate up the bus hierarchy until someone handles it. */ if (dev->parent) return (BUS_SETUP_INTR(dev->parent, child, irq, intr, arg, cookiep)); else return (EINVAL); } int bus_generic_teardown_intr(device_t dev, device_t child, struct resource *irq, void *cookie) { /* Propagate up the bus hierarchy until someone handles it. */ if (dev->parent) return (BUS_TEARDOWN_INTR(dev->parent, child, irq, cookie)); else return (EINVAL); } struct resource * bus_generic_alloc_resource(device_t dev, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { /* Propagate up the bus hierarchy until someone handles it. */ if (dev->parent) return (BUS_ALLOC_RESOURCE(dev->parent, child, type, rid, start, end, count, flags)); else return (NULL); } int bus_generic_release_resource(device_t dev, device_t child, int type, int rid, struct resource *r) { /* Propagate up the bus hierarchy until someone handles it. */ if (dev->parent) return (BUS_RELEASE_RESOURCE(dev->parent, child, type, rid, r)); else return (EINVAL); } int bus_generic_activate_resource(device_t dev, device_t child, int type, int rid, struct resource *r) { /* Propagate up the bus hierarchy until someone handles it. */ if (dev->parent) return (BUS_ACTIVATE_RESOURCE(dev->parent, child, type, rid, r)); else return (EINVAL); } int bus_generic_deactivate_resource(device_t dev, device_t child, int type, int rid, struct resource *r) { /* Propagate up the bus hierarchy until someone handles it. */ if (dev->parent) return (BUS_DEACTIVATE_RESOURCE(dev->parent, child, type, rid, r)); else return (EINVAL); } /* * Some convenience functions to make it easier for drivers to use the * resource-management functions. All these really do is hide the * indirection through the parent's method table, making for slightly * less-wordy code. In the future, it might make sense for this code * to maintain some sort of a list of resources allocated by each device. */ struct resource * bus_alloc_resource(device_t dev, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { if (dev->parent == 0) return (0); return (BUS_ALLOC_RESOURCE(dev->parent, dev, type, rid, start, end, count, flags)); } int bus_activate_resource(device_t dev, int type, int rid, struct resource *r) { if (dev->parent == 0) return (EINVAL); return (BUS_ACTIVATE_RESOURCE(dev->parent, dev, type, rid, r)); } int bus_deactivate_resource(device_t dev, int type, int rid, struct resource *r) { if (dev->parent == 0) return (EINVAL); return (BUS_DEACTIVATE_RESOURCE(dev->parent, dev, type, rid, r)); } int bus_release_resource(device_t dev, int type, int rid, struct resource *r) { if (dev->parent == 0) return (EINVAL); return (BUS_RELEASE_RESOURCE(dev->parent, dev, type, rid, r)); } int bus_setup_intr(device_t dev, struct resource *r, driver_intr_t handler, void *arg, void **cookiep) { if (dev->parent == 0) return (EINVAL); return (BUS_SETUP_INTR(dev->parent, dev, r, handler, arg, cookiep)); } int bus_teardown_intr(device_t dev, struct resource *r, void *cookie) { if (dev->parent == 0) return (EINVAL); return (BUS_TEARDOWN_INTR(dev->parent, dev, r, cookie)); } static void root_print_child(device_t dev, device_t child) { } static int root_setup_intr(device_t dev, device_t child, driver_intr_t *intr, void *arg, void **cookiep) { /* * If an interrupt mapping gets to here something bad has happened. */ panic("root_setup_intr"); } static device_method_t root_methods[] = { /* Device interface */ + DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, root_print_child), DEVMETHOD(bus_read_ivar, bus_generic_read_ivar), DEVMETHOD(bus_write_ivar, bus_generic_write_ivar), DEVMETHOD(bus_setup_intr, root_setup_intr), { 0, 0 } }; static driver_t root_driver = { "root", root_methods, DRIVER_TYPE_MISC, 1, /* no softc */ }; device_t root_bus; devclass_t root_devclass; static int root_bus_module_handler(module_t mod, int what, void* arg) { switch (what) { case MOD_LOAD: compile_methods(&root_driver); root_bus = make_device(NULL, "root", 0, NULL); root_bus->desc = "System root bus"; root_bus->ops = root_driver.ops; root_bus->driver = &root_driver; root_bus->state = DS_ATTACHED; root_devclass = devclass_find_internal("root", FALSE); + return 0; + + case MOD_SHUTDOWN: + device_shutdown(root_bus); return 0; } return 0; } static moduledata_t root_bus_mod = { "rootbus", root_bus_module_handler, 0 }; DECLARE_MODULE(rootbus, root_bus_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); void root_bus_configure(void) { device_t dev; PDEBUG((".")); for (dev = TAILQ_FIRST(&root_bus->children); dev; dev = TAILQ_NEXT(dev, link)) { device_probe_and_attach(dev); } } int driver_module_handler(module_t mod, int what, void *arg) { int error, i; struct driver_module_data *dmd; devclass_t bus_devclass; dmd = (struct driver_module_data *)arg; bus_devclass = devclass_find_internal(dmd->dmd_busname, TRUE); error = 0; switch (what) { case MOD_LOAD: for (i = 0; !error && i < dmd->dmd_ndrivers; i++) { PDEBUG(("Loading module: driver %s on bus %s", DRIVERNAME(dmd->dmd_drivers[i]), dmd->dmd_busname)); error = devclass_add_driver(bus_devclass, dmd->dmd_drivers[i]); } if (error) break; /* * The drivers loaded in this way are assumed to all * implement the same devclass. */ *dmd->dmd_devclass = devclass_find_internal(dmd->dmd_drivers[0]->name, TRUE); break; case MOD_UNLOAD: for (i = 0; !error && i < dmd->dmd_ndrivers; i++) { PDEBUG(("Unloading module: driver %s from bus %s", DRIVERNAME(dmd->dmd_drivers[i]), dmd->dmd_busname)); error = devclass_delete_driver(bus_devclass, dmd->dmd_drivers[i]); } break; } if (!error && dmd->dmd_chainevh) error = dmd->dmd_chainevh(mod, what, dmd->dmd_chainarg); return (error); } #ifdef BUS_DEBUG /* the _short versions avoid iteration by not calling anything that prints * more than oneliners. I love oneliners. */ static void print_method_list(device_method_t *m, int indent) { int i; if (!m) return; for (i = 0; m->desc; i++, m++) indentprintf(("method %d: %s, offset=%d\n", i, m->desc->name, m->desc->offset)); } static void print_device_ops(device_ops_t ops, int indent) { int i; int count = 0; if (!ops) return; /* we present a list of the methods that are pointing to the * error_method, but ignore the 0'th elements; it is always * error_method. */ for (i = 1; i < ops->maxoffset; i++) { if (ops->methods[i] == error_method) { if (count == 0) indentprintf(("error_method:")); printf(" %d", i); count++; } } if (count) printf("\n"); indentprintf(("(%d method%s, %d valid, %d error_method%s)\n", ops->maxoffset-1, (ops->maxoffset-1 == 1? "":"s"), ops->maxoffset-1-count, count, (count == 1? "":"'s"))); } static void print_device_short(device_t dev, int indent) { if (!dev) return; indentprintf(("device %d: <%s> %sparent,%schildren,%s%s%s%sivars,%ssoftc,busy=%d\n", dev->unit, dev->desc, (dev->parent? "":"no "), (TAILQ_EMPTY(&dev->children)? "no ":""), (dev->flags&DF_ENABLED? "enabled,":"disabled,"), (dev->flags&DF_FIXEDCLASS? "fixed,":""), (dev->flags&DF_WILDCARD? "wildcard,":""), (dev->flags&DF_DESCMALLOCED? "descmalloced,":""), (dev->ivars? "":"no "), (dev->softc? "":"no "), dev->busy)); } static void print_device(device_t dev, int indent) { if (!dev) return; print_device_short(dev, indent); indentprintf(("Parent:\n")); print_device_short(dev->parent, indent+1); indentprintf(("Methods:\n")); print_device_ops(dev->ops, indent+1); indentprintf(("Driver:\n")); print_driver_short(dev->driver, indent+1); indentprintf(("Devclass:\n")); print_devclass_short(dev->devclass, indent+1); } void print_device_tree_short(device_t dev, int indent) /* print the device and all its children (indented) */ { device_t child; if (!dev) return; print_device_short(dev, indent); for (child = TAILQ_FIRST(&dev->children); child; child = TAILQ_NEXT(child, link)) print_device_tree_short(child, indent+1); } void print_device_tree(device_t dev, int indent) /* print the device and all its children (indented) */ { device_t child; if (!dev) return; print_device(dev, indent); for (child = TAILQ_FIRST(&dev->children); child; child = TAILQ_NEXT(child, link)) print_device_tree(child, indent+1); } static void print_driver_short(driver_t *driver, int indent) { if (!driver) return; indentprintf(("driver %s: type = %s%s%s%s, softc size = %d\n", driver->name, /* yes, I know this looks silly, but going to bed at * two o'clock and having to get up at 7:30 again is silly * as well. As is sticking your head in a bucket of water. */ (driver->type == DRIVER_TYPE_TTY? "tty":""), (driver->type == DRIVER_TYPE_BIO? "bio":""), (driver->type == DRIVER_TYPE_NET? "net":""), (driver->type == DRIVER_TYPE_MISC? "misc":""), driver->softc)); } static void print_driver(driver_t *driver, int indent) { if (!driver) return; print_driver_short(driver, indent); indentprintf(("Methods:\n")); print_method_list(driver->methods, indent+1); indentprintf(("Operations:\n")); print_device_ops(driver->ops, indent+1); } static void print_driver_list(driver_list_t drivers, int indent) { driver_t *driver; for (driver = TAILQ_FIRST(&drivers); driver; driver = TAILQ_NEXT(driver, link)) print_driver(driver, indent); } static void print_devclass_short(devclass_t dc, int indent) { if ( !dc ) return; indentprintf(("devclass %s: max units = %d, next unit = %d\n", dc->name, dc->maxunit, dc->nextunit)); } static void print_devclass(devclass_t dc, int indent) { int i; if ( !dc ) return; print_devclass_short(dc, indent); indentprintf(("Drivers:\n")); print_driver_list(dc->drivers, indent+1); indentprintf(("Devices:\n")); for (i = 0; i < dc->maxunit; i++) if (dc->devices[i]) print_device(dc->devices[i], indent+1); } void print_devclass_list_short(void) { devclass_t dc; printf("Short listing of devclasses, drivers & devices:\n"); for (dc = TAILQ_FIRST(&devclasses); dc; dc = TAILQ_NEXT(dc, link)) print_devclass_short(dc, 0); } void print_devclass_list(void) { devclass_t dc; printf("Full listing of devclasses, drivers & devices:\n"); for (dc = TAILQ_FIRST(&devclasses); dc; dc = TAILQ_NEXT(dc, link)) print_devclass(dc, 0); } #endif Index: head/sys/kern/subr_rman.c =================================================================== --- head/sys/kern/subr_rman.c (revision 45719) +++ head/sys/kern/subr_rman.c (revision 45720) @@ -1,591 +1,612 @@ /* * Copyright 1998 Massachusetts Institute of Technology * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby * granted, provided that both the above copyright notice and this * permission notice appear in all copies, that both the above * copyright notice and this permission notice appear in all * supporting documentation, and that the name of M.I.T. not be used * in advertising or publicity pertaining to distribution of the * software without specific, written prior permission. M.I.T. makes * no representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied * warranty. * * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT * SHALL M.I.T. 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. * - * $Id: subr_rman.c,v 1.5 1999/03/29 08:30:17 dfr Exp $ + * $Id: subr_rman.c,v 1.6 1999/04/11 02:27:06 eivind Exp $ */ /* * The kernel resource manager. This code is responsible for keeping track * of hardware resources which are apportioned out to various drivers. * It does not actually assign those resources, and it is not expected * that end-device drivers will call into this code directly. Rather, * the code which implements the buses that those devices are attached to, * and the code which manages CPU resources, will call this code, and the * end-device drivers will make upcalls to that code to actually perform * the allocation. * * There are two sorts of resources managed by this code. The first is * the more familiar array (RMAN_ARRAY) type; resources in this class * consist of a sequence of individually-allocatable objects which have * been numbered in some well-defined order. Most of the resources * are of this type, as it is the most familiar. The second type is * called a gauge (RMAN_GAUGE), and models fungible resources (i.e., * resources in which each instance is indistinguishable from every * other instance). The principal anticipated application of gauges * is in the context of power consumption, where a bus may have a specific * power budget which all attached devices share. RMAN_GAUGE is not * implemented yet. * * For array resources, we make one simplifying assumption: two clients * sharing the same resource must use the same range of indices. That * is to say, sharing of overlapping-but-not-identical regions is not * permitted. */ #include #include #include #include #include -#include #include /* XXX debugging */ +#include +#include static MALLOC_DEFINE(M_RMAN, "rman", "Resource manager"); struct rman_head rman_head; #ifndef NULL_SIMPLELOCKS static struct simplelock rman_lock; /* mutex to protect rman_head */ #endif static int int_rman_activate_resource(struct rman *rm, struct resource *r, struct resource **whohas); +static int int_rman_deactivate_resource(struct resource *r); static int int_rman_release_resource(struct rman *rm, struct resource *r); #define CIRCLEQ_TERMCOND(var, head) (var == (void *)&(head)) int rman_init(struct rman *rm) { static int once; if (once == 0) { once = 1; TAILQ_INIT(&rman_head); simple_lock_init(&rman_lock); } if (rm->rm_type == RMAN_UNINIT) panic("rman_init"); if (rm->rm_type == RMAN_GAUGE) panic("implement RMAN_GAUGE"); CIRCLEQ_INIT(&rm->rm_list); rm->rm_slock = malloc(sizeof *rm->rm_slock, M_RMAN, M_NOWAIT); if (rm->rm_slock == 0) return ENOMEM; simple_lock_init(rm->rm_slock); simple_lock(&rman_lock); TAILQ_INSERT_TAIL(&rman_head, rm, rm_link); simple_unlock(&rman_lock); return 0; } /* * NB: this interface is not robust against programming errors which * add multiple copies of the same region. */ int rman_manage_region(struct rman *rm, u_long start, u_long end) { struct resource *r, *s; r = malloc(sizeof *r, M_RMAN, M_NOWAIT); if (r == 0) return ENOMEM; + bzero(r, sizeof *r); r->r_sharehead = 0; r->r_start = start; r->r_end = end; r->r_flags = 0; r->r_dev = 0; r->r_rm = rm; simple_lock(rm->rm_slock); for (s = rm->rm_list.cqh_first; !CIRCLEQ_TERMCOND(s, rm->rm_list) && s->r_end < r->r_start; s = s->r_link.cqe_next) ; if (CIRCLEQ_TERMCOND(s, rm->rm_list)) { CIRCLEQ_INSERT_TAIL(&rm->rm_list, r, r_link); } else { CIRCLEQ_INSERT_BEFORE(&rm->rm_list, s, r, r_link); } simple_unlock(rm->rm_slock); return 0; } int rman_fini(struct rman *rm) { struct resource *r; simple_lock(rm->rm_slock); for (r = rm->rm_list.cqh_first; !CIRCLEQ_TERMCOND(r, rm->rm_list); r = r->r_link.cqe_next) { - if (r->r_flags & RF_ALLOCATED) + if (r->r_flags & RF_ALLOCATED) { + simple_unlock(rm->rm_slock); return EBUSY; + } } /* * There really should only be one of these if we are in this * state and the code is working properly, but it can't hurt. */ for (r = rm->rm_list.cqh_first; !CIRCLEQ_TERMCOND(r, rm->rm_list); r = rm->rm_list.cqh_first) { CIRCLEQ_REMOVE(&rm->rm_list, r, r_link); free(r, M_RMAN); } simple_unlock(rm->rm_slock); simple_lock(&rman_lock); TAILQ_REMOVE(&rman_head, rm, rm_link); simple_unlock(&rman_lock); free(rm->rm_slock, M_RMAN); return 0; } struct resource * rman_reserve_resource(struct rman *rm, u_long start, u_long end, u_long count, u_int flags, struct device *dev) { u_int want_activate; struct resource *r, *s, *rv; u_long rstart, rend; rv = 0; #ifdef RMAN_DEBUG printf("rman_reserve_resource: <%s> request: [%#lx, %#lx], length " "%#lx, flags %u, device %s%d\n", rm->rm_descr, start, end, count, flags, device_get_name(dev), device_get_unit(dev)); #endif /* RMAN_DEBUG */ want_activate = (flags & RF_ACTIVE); flags &= ~RF_ACTIVE; simple_lock(rm->rm_slock); for (r = rm->rm_list.cqh_first; !CIRCLEQ_TERMCOND(r, rm->rm_list) && r->r_end < start; r = r->r_link.cqe_next) ; if (CIRCLEQ_TERMCOND(r, rm->rm_list)) { #ifdef RMAN_DEBUG printf("could not find a region\n"); #endif RMAN_DEBUG goto out; } /* * First try to find an acceptable totally-unshared region. */ for (s = r; !CIRCLEQ_TERMCOND(s, rm->rm_list); s = s->r_link.cqe_next) { #ifdef RMAN_DEBUG printf("considering [%#lx, %#lx]\n", s->r_start, s->r_end); #endif /* RMAN_DEBUG */ if (s->r_start > end) { #ifdef RMAN_DEBUG printf("s->r_start (%#lx) > end (%#lx)\n", s->r_start, end); #endif /* RMAN_DEBUG */ break; } if (s->r_flags & RF_ALLOCATED) { #ifdef RMAN_DEBUG printf("region is allocated\n"); #endif /* RMAN_DEBUG */ continue; } rstart = max(s->r_start, start); rend = min(s->r_end, max(start + count, end)); #ifdef RMAN_DEBUG printf("truncated region: [%#lx, %#lx]; size %#lx (requested %#lx)\n", rstart, rend, (rend - rstart + 1), count); #endif /* RMAN_DEBUG */ if ((rend - rstart + 1) >= count) { #ifdef RMAN_DEBUG printf("candidate region: [%#lx, %#lx], size %#lx\n", rend, rstart, (rend - rstart + 1)); #endif /* RMAN_DEBUG */ if ((s->r_end - s->r_start + 1) == count) { #ifdef RMAN_DEBUG printf("candidate region is entire chunk\n"); #endif /* RMAN_DEBUG */ rv = s; rv->r_flags |= RF_ALLOCATED; rv->r_dev = dev; goto out; } /* * If s->r_start < rstart and * s->r_end > rstart + count - 1, then * we need to split the region into three pieces * (the middle one will get returned to the user). * Otherwise, we are allocating at either the * beginning or the end of s, so we only need to * split it in two. The first case requires * two new allocations; the second requires but one. */ - rv = malloc(sizeof *r, M_RMAN, M_NOWAIT); + rv = malloc(sizeof *rv, M_RMAN, M_NOWAIT); if (rv == 0) goto out; + bzero(rv, sizeof *rv); rv->r_start = rstart; rv->r_end = rstart + count - 1; rv->r_flags = flags | RF_ALLOCATED; rv->r_dev = dev; rv->r_sharehead = 0; + rv->r_rm = rm; if (s->r_start < rv->r_start && s->r_end > rv->r_end) { #ifdef RMAN_DEBUG printf("splitting region in three parts: " "[%#lx, %#lx]; [%#lx, %#lx]; [%#lx, %#lx]\n", s->r_start, rv->r_start - 1, rv->r_start, rv->r_end, rv->r_end + 1, s->r_end); #endif /* RMAN_DEBUG */ /* * We are allocating in the middle. */ r = malloc(sizeof *r, M_RMAN, M_NOWAIT); if (r == 0) { free(rv, M_RMAN); rv = 0; goto out; } + bzero(r, sizeof *r); r->r_start = rv->r_end + 1; r->r_end = s->r_end; r->r_flags = s->r_flags; r->r_dev = 0; r->r_sharehead = 0; + r->r_rm = rm; s->r_end = rv->r_start - 1; CIRCLEQ_INSERT_AFTER(&rm->rm_list, s, rv, r_link); CIRCLEQ_INSERT_AFTER(&rm->rm_list, rv, r, r_link); } else if (s->r_start == rv->r_start) { #ifdef RMAN_DEBUG printf("allocating from the beginning\n"); #endif /* RMAN_DEBUG */ /* * We are allocating at the beginning. */ s->r_start = rv->r_end + 1; CIRCLEQ_INSERT_BEFORE(&rm->rm_list, s, rv, r_link); } else { #ifdef RMAN_DEBUG printf("allocating at the end\n"); #endif /* RMAN_DEBUG */ /* * We are allocating at the end. */ s->r_end = rv->r_start - 1; CIRCLEQ_INSERT_AFTER(&rm->rm_list, s, rv, r_link); } goto out; } } /* * Now find an acceptable shared region, if the client's requirements * allow sharing. By our implementation restriction, a candidate * region must match exactly by both size and sharing type in order * to be considered compatible with the client's request. (The * former restriction could probably be lifted without too much * additional work, but this does not seem warranted.) */ #ifdef RMAN_DEBUG printf("no unshared regions found\n"); #endif /* RMAN_DEBUG */ if ((flags & (RF_SHAREABLE | RF_TIMESHARE)) == 0) goto out; for (s = r; !CIRCLEQ_TERMCOND(s, rm->rm_list); s = s->r_link.cqe_next) { if (s->r_start > end) break; if ((s->r_flags & flags) != flags) continue; rstart = max(s->r_start, start); rend = min(s->r_end, max(start + count, end)); if (s->r_start >= start && s->r_end <= end && (s->r_end - s->r_start + 1) == count) { rv = malloc(sizeof *rv, M_RMAN, M_NOWAIT); if (rv == 0) goto out; + bzero(rv, sizeof *rv); rv->r_start = s->r_start; rv->r_end = s->r_end; rv->r_flags = s->r_flags & (RF_ALLOCATED | RF_SHAREABLE | RF_TIMESHARE); rv->r_dev = dev; rv->r_rm = rm; if (s->r_sharehead == 0) { s->r_sharehead = malloc(sizeof *s->r_sharehead, M_RMAN, M_NOWAIT); if (s->r_sharehead == 0) { free(rv, M_RMAN); rv = 0; goto out; } + bzero(s->r_sharehead, sizeof *s->r_sharehead); LIST_INIT(s->r_sharehead); LIST_INSERT_HEAD(s->r_sharehead, s, r_sharelink); s->r_flags |= RF_FIRSTSHARE; } rv->r_sharehead = s->r_sharehead; LIST_INSERT_HEAD(s->r_sharehead, rv, r_sharelink); goto out; } } /* * We couldn't find anything. */ out: /* * If the user specified RF_ACTIVE in the initial flags, * which is reflected in `want_activate', we attempt to atomically * activate the resource. If this fails, we release the resource * and indicate overall failure. (This behavior probably doesn't * make sense for RF_TIMESHARE-type resources.) */ if (rv && want_activate) { struct resource *whohas; if (int_rman_activate_resource(rm, rv, &whohas)) { int_rman_release_resource(rm, rv); rv = 0; } } simple_unlock(rm->rm_slock); return (rv); } static int int_rman_activate_resource(struct rman *rm, struct resource *r, struct resource **whohas) { struct resource *s; int ok; /* * If we are not timesharing, then there is nothing much to do. * If we already have the resource, then there is nothing at all to do. * If we are not on a sharing list with anybody else, then there is * little to do. */ if ((r->r_flags & RF_TIMESHARE) == 0 || (r->r_flags & RF_ACTIVE) != 0 || r->r_sharehead == 0) { r->r_flags |= RF_ACTIVE; return 0; } ok = 1; for (s = r->r_sharehead->lh_first; s && ok; s = s->r_sharelink.le_next) { if ((s->r_flags & RF_ACTIVE) != 0) { ok = 0; *whohas = s; } } if (ok) { r->r_flags |= RF_ACTIVE; return 0; } return EBUSY; } int rman_activate_resource(struct resource *r) { int rv; struct resource *whohas; struct rman *rm; rm = r->r_rm; simple_lock(rm->rm_slock); rv = int_rman_activate_resource(rm, r, &whohas); simple_unlock(rm->rm_slock); return rv; } int rman_await_resource(struct resource *r, int pri, int timo) { int rv, s; struct resource *whohas; struct rman *rm; rm = r->r_rm; for (;;) { simple_lock(rm->rm_slock); rv = int_rman_activate_resource(rm, r, &whohas); if (rv != EBUSY) - return (rv); + return (rv); /* returns with simplelock */ if (r->r_sharehead == 0) panic("rman_await_resource"); /* * splhigh hopefully will prevent a race between * simple_unlock and tsleep where a process * could conceivably get in and release the resource * before we have a chance to sleep on it. */ s = splhigh(); whohas->r_flags |= RF_WANTED; simple_unlock(rm->rm_slock); rv = tsleep(r->r_sharehead, pri, "rmwait", timo); if (rv) { splx(s); return rv; } simple_lock(rm->rm_slock); splx(s); } } -int -rman_deactivate_resource(struct resource *r) +static int +int_rman_deactivate_resource(struct resource *r) { struct rman *rm; rm = r->r_rm; - simple_lock(rm->rm_slock); r->r_flags &= ~RF_ACTIVE; if (r->r_flags & RF_WANTED) { r->r_flags &= ~RF_WANTED; wakeup(r->r_sharehead); } + return 0; +} + +int +rman_deactivate_resource(struct resource *r) +{ + struct rman *rm; + + rm = r->r_rm; + simple_lock(rm->rm_slock); + int_rman_deactivate_resource(r); simple_unlock(rm->rm_slock); return 0; } static int int_rman_release_resource(struct rman *rm, struct resource *r) { struct resource *s, *t; if (r->r_flags & RF_ACTIVE) - return EBUSY; + int_rman_deactivate_resource(r); /* * Check for a sharing list first. If there is one, then we don't * have to think as hard. */ if (r->r_sharehead) { /* * If a sharing list exists, then we know there are at * least two sharers. * * If we are in the main circleq, appoint someone else. */ LIST_REMOVE(r, r_sharelink); s = r->r_sharehead->lh_first; if (r->r_flags & RF_FIRSTSHARE) { s->r_flags |= RF_FIRSTSHARE; CIRCLEQ_INSERT_BEFORE(&rm->rm_list, r, s, r_link); CIRCLEQ_REMOVE(&rm->rm_list, r, r_link); } /* * Make sure that the sharing list goes away completely * if the resource is no longer being shared at all. */ if (s->r_sharelink.le_next == 0) { free(s->r_sharehead, M_RMAN); s->r_sharehead = 0; s->r_flags &= ~RF_FIRSTSHARE; } goto out; } /* * Look at the adjacent resources in the list and see if our * segment can be merged with any of them. */ s = r->r_link.cqe_prev; t = r->r_link.cqe_next; if (s != (void *)&rm->rm_list && (s->r_flags & RF_ALLOCATED) == 0 && t != (void *)&rm->rm_list && (t->r_flags & RF_ALLOCATED) == 0) { /* * Merge all three segments. */ s->r_end = t->r_end; CIRCLEQ_REMOVE(&rm->rm_list, r, r_link); CIRCLEQ_REMOVE(&rm->rm_list, t, r_link); free(t, M_RMAN); } else if (s != (void *)&rm->rm_list && (s->r_flags & RF_ALLOCATED) == 0) { /* * Merge previous segment with ours. */ s->r_end = r->r_end; CIRCLEQ_REMOVE(&rm->rm_list, r, r_link); } else if (t != (void *)&rm->rm_list && (t->r_flags & RF_ALLOCATED) == 0) { /* * Merge next segment with ours. */ t->r_start = r->r_start; CIRCLEQ_REMOVE(&rm->rm_list, r, r_link); } else { /* * At this point, we know there is nothing we * can potentially merge with, because on each * side, there is either nothing there or what is * there is still allocated. In that case, we don't * want to remove r from the list; we simply want to * change it to an unallocated region and return * without freeing anything. */ r->r_flags &= ~RF_ALLOCATED; return 0; } out: free(r, M_RMAN); return 0; } int rman_release_resource(struct resource *r) { int rv; struct rman *rm = r->r_rm; simple_lock(rm->rm_slock); rv = int_rman_release_resource(rm, r); simple_unlock(rm->rm_slock); return (rv); } Index: head/sys/net/if.c =================================================================== --- head/sys/net/if.c (revision 45719) +++ head/sys/net/if.c (revision 45720) @@ -1,1093 +1,1126 @@ /* * Copyright (c) 1980, 1986, 1993 * 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. * * @(#)if.c 8.3 (Berkeley) 1/4/94 - * $Id: if.c,v 1.65 1999/02/01 20:03:27 phk Exp $ + * $Id: if.c,v 1.66 1999/02/19 13:41:35 phk Exp $ */ #include "opt_compat.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * System initialization */ static int ifconf __P((u_long, caddr_t)); static void ifinit __P((void *)); static void if_qflush __P((struct ifqueue *)); static void if_slowtimo __P((void *)); static void link_rtrequest __P((int, struct rtentry *, struct sockaddr *)); SYSINIT(interfaces, SI_SUB_PROTO_IF, SI_ORDER_FIRST, ifinit, NULL) MALLOC_DEFINE(M_IFADDR, "ifaddr", "interface address"); MALLOC_DEFINE(M_IFMADDR, "ether_multi", "link-level multicast address"); int ifqmaxlen = IFQ_MAXLEN; struct ifnethead ifnet; /* depend on static init XXX */ /* * Network interface utility routines. * * Routines with ifa_ifwith* names take sockaddr *'s as * parameters. * * This routine assumes that it will be called at splimp() or higher. */ /* ARGSUSED*/ void ifinit(dummy) void *dummy; { register struct ifnet *ifp; for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_link.tqe_next) if (ifp->if_snd.ifq_maxlen == 0) { printf("%s%d XXX: driver didn't set ifq_maxlen\n", ifp->if_name, ifp->if_unit); ifp->if_snd.ifq_maxlen = ifqmaxlen; } if_slowtimo(0); } int if_index = 0; struct ifaddr **ifnet_addrs; /* * Attach an interface to the * list of "active" interfaces. */ void if_attach(ifp) struct ifnet *ifp; { unsigned socksize, ifasize; int namelen, masklen; char workbuf[64]; register struct sockaddr_dl *sdl; register struct ifaddr *ifa; static int if_indexlim = 8; static int inited; if (!inited) { TAILQ_INIT(&ifnet); inited = 1; } TAILQ_INSERT_TAIL(&ifnet, ifp, if_link); ifp->if_index = ++if_index; /* * XXX - * The old code would work if the interface passed a pre-existing * chain of ifaddrs to this code. We don't trust our callers to * properly initialize the tailq, however, so we no longer allow * this unlikely case. */ TAILQ_INIT(&ifp->if_addrhead); LIST_INIT(&ifp->if_multiaddrs); getmicrotime(&ifp->if_lastchange); if (ifnet_addrs == 0 || if_index >= if_indexlim) { unsigned n = (if_indexlim <<= 1) * sizeof(ifa); struct ifaddr **q = (struct ifaddr **) malloc(n, M_IFADDR, M_WAITOK); bzero((caddr_t)q, n); if (ifnet_addrs) { bcopy((caddr_t)ifnet_addrs, (caddr_t)q, n/2); free((caddr_t)ifnet_addrs, M_IFADDR); } ifnet_addrs = q; } /* * create a Link Level name for this device */ namelen = snprintf(workbuf, sizeof(workbuf), "%s%d", ifp->if_name, ifp->if_unit); #define _offsetof(t, m) ((int)((caddr_t)&((t *)0)->m)) masklen = _offsetof(struct sockaddr_dl, sdl_data[0]) + namelen; socksize = masklen + ifp->if_addrlen; #define ROUNDUP(a) (1 + (((a) - 1) | (sizeof(long) - 1))) if (socksize < sizeof(*sdl)) socksize = sizeof(*sdl); socksize = ROUNDUP(socksize); ifasize = sizeof(*ifa) + 2 * socksize; ifa = (struct ifaddr *)malloc(ifasize, M_IFADDR, M_WAITOK); if (ifa) { bzero((caddr_t)ifa, ifasize); sdl = (struct sockaddr_dl *)(ifa + 1); sdl->sdl_len = socksize; sdl->sdl_family = AF_LINK; bcopy(workbuf, sdl->sdl_data, namelen); sdl->sdl_nlen = namelen; sdl->sdl_index = ifp->if_index; sdl->sdl_type = ifp->if_type; ifnet_addrs[if_index - 1] = ifa; ifa->ifa_ifp = ifp; ifa->ifa_rtrequest = link_rtrequest; ifa->ifa_addr = (struct sockaddr *)sdl; sdl = (struct sockaddr_dl *)(socksize + (caddr_t)sdl); ifa->ifa_netmask = (struct sockaddr *)sdl; sdl->sdl_len = masklen; while (namelen != 0) sdl->sdl_data[--namelen] = 0xff; TAILQ_INSERT_HEAD(&ifp->if_addrhead, ifa, ifa_link); } } + +/* + * Detach an interface, removing it from the + * list of "active" interfaces. + */ +void +if_detach(ifp) + struct ifnet *ifp; +{ + struct ifaddr *ifa; + + /* + * Remove routes and flush queues. + */ + if_down(ifp); + + /* + * Remove address from ifnet_addrs[] and maybe decrement if_index. + * Clean up all addresses. + */ + ifnet_addrs[ifp->if_index] = 0; + while (ifnet_addrs[if_index] == 0) + if_index--; + + for (ifa = TAILQ_FIRST(&ifp->if_addrhead); ifa; + ifa = TAILQ_FIRST(&ifp->if_addrhead)) { + TAILQ_REMOVE(&ifp->if_addrhead, ifa, ifa_link); + IFAFREE(ifa); + } + + TAILQ_REMOVE(&ifnet, ifp, if_link); +} + /* * Locate an interface based on a complete address. */ /*ARGSUSED*/ struct ifaddr * ifa_ifwithaddr(addr) register struct sockaddr *addr; { register struct ifnet *ifp; register struct ifaddr *ifa; #define equal(a1, a2) \ (bcmp((caddr_t)(a1), (caddr_t)(a2), ((struct sockaddr *)(a1))->sa_len) == 0) for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_link.tqe_next) for (ifa = ifp->if_addrhead.tqh_first; ifa; ifa = ifa->ifa_link.tqe_next) { if (ifa->ifa_addr->sa_family != addr->sa_family) continue; if (equal(addr, ifa->ifa_addr)) return (ifa); if ((ifp->if_flags & IFF_BROADCAST) && ifa->ifa_broadaddr && equal(ifa->ifa_broadaddr, addr)) return (ifa); } return ((struct ifaddr *)0); } /* * Locate the point to point interface with a given destination address. */ /*ARGSUSED*/ struct ifaddr * ifa_ifwithdstaddr(addr) register struct sockaddr *addr; { register struct ifnet *ifp; register struct ifaddr *ifa; for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_link.tqe_next) if (ifp->if_flags & IFF_POINTOPOINT) for (ifa = ifp->if_addrhead.tqh_first; ifa; ifa = ifa->ifa_link.tqe_next) { if (ifa->ifa_addr->sa_family != addr->sa_family) continue; if (ifa->ifa_dstaddr && equal(addr, ifa->ifa_dstaddr)) return (ifa); } return ((struct ifaddr *)0); } /* * Find an interface on a specific network. If many, choice * is most specific found. */ struct ifaddr * ifa_ifwithnet(addr) struct sockaddr *addr; { register struct ifnet *ifp; register struct ifaddr *ifa; struct ifaddr *ifa_maybe = (struct ifaddr *) 0; u_int af = addr->sa_family; char *addr_data = addr->sa_data, *cplim; /* * AF_LINK addresses can be looked up directly by their index number, * so do that if we can. */ if (af == AF_LINK) { register struct sockaddr_dl *sdl = (struct sockaddr_dl *)addr; if (sdl->sdl_index && sdl->sdl_index <= if_index) return (ifnet_addrs[sdl->sdl_index - 1]); } /* * Scan though each interface, looking for ones that have * addresses in this address family. */ for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_link.tqe_next) { for (ifa = ifp->if_addrhead.tqh_first; ifa; ifa = ifa->ifa_link.tqe_next) { register char *cp, *cp2, *cp3; if (ifa->ifa_addr->sa_family != af) next: continue; if (ifp->if_flags & IFF_POINTOPOINT) { /* * This is a bit broken as it doesn't * take into account that the remote end may * be a single node in the network we are * looking for. * The trouble is that we don't know the * netmask for the remote end. */ if (ifa->ifa_dstaddr != 0 && equal(addr, ifa->ifa_dstaddr)) return (ifa); } else { /* * if we have a special address handler, * then use it instead of the generic one. */ if (ifa->ifa_claim_addr) { if ((*ifa->ifa_claim_addr)(ifa, addr)) { return (ifa); } else { continue; } } /* * Scan all the bits in the ifa's address. * If a bit dissagrees with what we are * looking for, mask it with the netmask * to see if it really matters. * (A byte at a time) */ if (ifa->ifa_netmask == 0) continue; cp = addr_data; cp2 = ifa->ifa_addr->sa_data; cp3 = ifa->ifa_netmask->sa_data; cplim = ifa->ifa_netmask->sa_len + (char *)ifa->ifa_netmask; while (cp3 < cplim) if ((*cp++ ^ *cp2++) & *cp3++) goto next; /* next address! */ /* * If the netmask of what we just found * is more specific than what we had before * (if we had one) then remember the new one * before continuing to search * for an even better one. */ if (ifa_maybe == 0 || rn_refines((caddr_t)ifa->ifa_netmask, (caddr_t)ifa_maybe->ifa_netmask)) ifa_maybe = ifa; } } } return (ifa_maybe); } /* * Find an interface address specific to an interface best matching * a given address. */ struct ifaddr * ifaof_ifpforaddr(addr, ifp) struct sockaddr *addr; register struct ifnet *ifp; { register struct ifaddr *ifa; register char *cp, *cp2, *cp3; register char *cplim; struct ifaddr *ifa_maybe = 0; u_int af = addr->sa_family; if (af >= AF_MAX) return (0); for (ifa = ifp->if_addrhead.tqh_first; ifa; ifa = ifa->ifa_link.tqe_next) { if (ifa->ifa_addr->sa_family != af) continue; if (ifa_maybe == 0) ifa_maybe = ifa; if (ifa->ifa_netmask == 0) { if (equal(addr, ifa->ifa_addr) || (ifa->ifa_dstaddr && equal(addr, ifa->ifa_dstaddr))) return (ifa); continue; } if (ifp->if_flags & IFF_POINTOPOINT) { if (equal(addr, ifa->ifa_dstaddr)) return (ifa); } else { cp = addr->sa_data; cp2 = ifa->ifa_addr->sa_data; cp3 = ifa->ifa_netmask->sa_data; cplim = ifa->ifa_netmask->sa_len + (char *)ifa->ifa_netmask; for (; cp3 < cplim; cp3++) if ((*cp++ ^ *cp2++) & *cp3) break; if (cp3 == cplim) return (ifa); } } return (ifa_maybe); } #include /* * Default action when installing a route with a Link Level gateway. * Lookup an appropriate real ifa to point to. * This should be moved to /sys/net/link.c eventually. */ static void link_rtrequest(cmd, rt, sa) int cmd; register struct rtentry *rt; struct sockaddr *sa; { register struct ifaddr *ifa; struct sockaddr *dst; struct ifnet *ifp; if (cmd != RTM_ADD || ((ifa = rt->rt_ifa) == 0) || ((ifp = ifa->ifa_ifp) == 0) || ((dst = rt_key(rt)) == 0)) return; ifa = ifaof_ifpforaddr(dst, ifp); if (ifa) { IFAFREE(rt->rt_ifa); rt->rt_ifa = ifa; ifa->ifa_refcnt++; if (ifa->ifa_rtrequest && ifa->ifa_rtrequest != link_rtrequest) ifa->ifa_rtrequest(cmd, rt, sa); } } /* * Mark an interface down and notify protocols of * the transition. * NOTE: must be called at splnet or eqivalent. */ void if_unroute(ifp, flag, fam) register struct ifnet *ifp; int flag, fam; { register struct ifaddr *ifa; ifp->if_flags &= ~flag; getmicrotime(&ifp->if_lastchange); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) if (fam == PF_UNSPEC || (fam == ifa->ifa_addr->sa_family)) pfctlinput(PRC_IFDOWN, ifa->ifa_addr); if_qflush(&ifp->if_snd); rt_ifmsg(ifp); } /* * Mark an interface up and notify protocols of * the transition. * NOTE: must be called at splnet or eqivalent. */ void if_route(ifp, flag, fam) register struct ifnet *ifp; int flag, fam; { register struct ifaddr *ifa; ifp->if_flags |= flag; getmicrotime(&ifp->if_lastchange); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) if (fam == PF_UNSPEC || (fam == ifa->ifa_addr->sa_family)) pfctlinput(PRC_IFUP, ifa->ifa_addr); rt_ifmsg(ifp); } /* * Mark an interface down and notify protocols of * the transition. * NOTE: must be called at splnet or eqivalent. */ void if_down(ifp) register struct ifnet *ifp; { if_unroute(ifp, IFF_UP, AF_UNSPEC); } /* * Mark an interface up and notify protocols of * the transition. * NOTE: must be called at splnet or eqivalent. */ void if_up(ifp) register struct ifnet *ifp; { if_route(ifp, IFF_UP, AF_UNSPEC); } /* * Flush an interface queue. */ static void if_qflush(ifq) register struct ifqueue *ifq; { register struct mbuf *m, *n; n = ifq->ifq_head; while ((m = n) != 0) { n = m->m_act; m_freem(m); } ifq->ifq_head = 0; ifq->ifq_tail = 0; ifq->ifq_len = 0; } /* * Handle interface watchdog timer routines. Called * from softclock, we decrement timers (if set) and * call the appropriate interface routine on expiration. */ static void if_slowtimo(arg) void *arg; { register struct ifnet *ifp; int s = splimp(); for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_link.tqe_next) { if (ifp->if_timer == 0 || --ifp->if_timer) continue; if (ifp->if_watchdog) (*ifp->if_watchdog)(ifp); } splx(s); timeout(if_slowtimo, (void *)0, hz / IFNET_SLOWHZ); } /* * Map interface name to * interface structure pointer. */ struct ifnet * ifunit(name) register char *name; { char namebuf[IFNAMSIZ + 1]; register char *cp, *cp2; char *end; register struct ifnet *ifp; int unit; unsigned len; register char c = '\0'; /* * Look for a non numeric part */ end = name + IFNAMSIZ; cp2 = namebuf; cp = name; while ((cp < end) && (c = *cp)) { if (c >= '0' && c <= '9') break; *cp2++ = c; cp++; } if ((cp == end) || (c == '\0') || (cp == name)) return ((struct ifnet *)0); *cp2 = '\0'; /* * check we have a legal number (limit to 7 digits?) */ len = cp - name + 1; for (unit = 0; ((c = *cp) >= '0') && (c <= '9') && (unit < 1000000); cp++ ) unit = (unit * 10) + (c - '0'); if (*cp != '\0') return 0; /* no trailing garbage allowed */ /* * Now search all the interfaces for this name/number */ for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_link.tqe_next) { if (bcmp(ifp->if_name, namebuf, len)) continue; if (unit == ifp->if_unit) break; } return (ifp); } /* * Interface ioctls. */ int ifioctl(so, cmd, data, p) struct socket *so; u_long cmd; caddr_t data; struct proc *p; { register struct ifnet *ifp; register struct ifreq *ifr; int error; switch (cmd) { case SIOCGIFCONF: case OSIOCGIFCONF: return (ifconf(cmd, data)); } ifr = (struct ifreq *)data; ifp = ifunit(ifr->ifr_name); if (ifp == 0) return (ENXIO); switch (cmd) { case SIOCGIFFLAGS: ifr->ifr_flags = ifp->if_flags; break; case SIOCGIFMETRIC: ifr->ifr_metric = ifp->if_metric; break; case SIOCGIFMTU: ifr->ifr_mtu = ifp->if_mtu; break; case SIOCGIFPHYS: ifr->ifr_phys = ifp->if_physical; break; case SIOCSIFFLAGS: error = suser(p->p_ucred, &p->p_acflag); if (error) return (error); ifr->ifr_prevflags = ifp->if_flags; if (ifp->if_flags & IFF_UP && (ifr->ifr_flags & IFF_UP) == 0) { int s = splimp(); if_down(ifp); splx(s); } if (ifr->ifr_flags & IFF_UP && (ifp->if_flags & IFF_UP) == 0) { int s = splimp(); if_up(ifp); splx(s); } ifp->if_flags = (ifp->if_flags & IFF_CANTCHANGE) | (ifr->ifr_flags &~ IFF_CANTCHANGE); if (ifp->if_ioctl) (void) (*ifp->if_ioctl)(ifp, cmd, data); getmicrotime(&ifp->if_lastchange); break; case SIOCSIFMETRIC: error = suser(p->p_ucred, &p->p_acflag); if (error) return (error); ifp->if_metric = ifr->ifr_metric; getmicrotime(&ifp->if_lastchange); break; case SIOCSIFPHYS: error = suser(p->p_ucred, &p->p_acflag); if (error) return error; if (!ifp->if_ioctl) return EOPNOTSUPP; error = (*ifp->if_ioctl)(ifp, cmd, data); if (error == 0) getmicrotime(&ifp->if_lastchange); return(error); case SIOCSIFMTU: error = suser(p->p_ucred, &p->p_acflag); if (error) return (error); if (ifp->if_ioctl == NULL) return (EOPNOTSUPP); /* * 72 was chosen below because it is the size of a TCP/IP * header (40) + the minimum mss (32). */ if (ifr->ifr_mtu < 72 || ifr->ifr_mtu > 65535) return (EINVAL); error = (*ifp->if_ioctl)(ifp, cmd, data); if (error == 0) getmicrotime(&ifp->if_lastchange); return(error); case SIOCADDMULTI: case SIOCDELMULTI: error = suser(p->p_ucred, &p->p_acflag); if (error) return (error); /* Don't allow group membership on non-multicast interfaces. */ if ((ifp->if_flags & IFF_MULTICAST) == 0) return EOPNOTSUPP; /* Don't let users screw up protocols' entries. */ if (ifr->ifr_addr.sa_family != AF_LINK) return EINVAL; if (cmd == SIOCADDMULTI) { struct ifmultiaddr *ifma; error = if_addmulti(ifp, &ifr->ifr_addr, &ifma); } else { error = if_delmulti(ifp, &ifr->ifr_addr); } if (error == 0) getmicrotime(&ifp->if_lastchange); return error; case SIOCSIFMEDIA: case SIOCSIFGENERIC: error = suser(p->p_ucred, &p->p_acflag); if (error) return (error); if (ifp->if_ioctl == 0) return (EOPNOTSUPP); error = (*ifp->if_ioctl)(ifp, cmd, data); if (error == 0) getmicrotime(&ifp->if_lastchange); return error; case SIOCGIFMEDIA: case SIOCGIFGENERIC: if (ifp->if_ioctl == 0) return (EOPNOTSUPP); return ((*ifp->if_ioctl)(ifp, cmd, data)); default: if (so->so_proto == 0) return (EOPNOTSUPP); #ifndef COMPAT_43 return ((*so->so_proto->pr_usrreqs->pru_control)(so, cmd, data, ifp, p)); #else { int ocmd = cmd; switch (cmd) { case SIOCSIFDSTADDR: case SIOCSIFADDR: case SIOCSIFBRDADDR: case SIOCSIFNETMASK: #if BYTE_ORDER != BIG_ENDIAN if (ifr->ifr_addr.sa_family == 0 && ifr->ifr_addr.sa_len < 16) { ifr->ifr_addr.sa_family = ifr->ifr_addr.sa_len; ifr->ifr_addr.sa_len = 16; } #else if (ifr->ifr_addr.sa_len == 0) ifr->ifr_addr.sa_len = 16; #endif break; case OSIOCGIFADDR: cmd = SIOCGIFADDR; break; case OSIOCGIFDSTADDR: cmd = SIOCGIFDSTADDR; break; case OSIOCGIFBRDADDR: cmd = SIOCGIFBRDADDR; break; case OSIOCGIFNETMASK: cmd = SIOCGIFNETMASK; } error = ((*so->so_proto->pr_usrreqs->pru_control)(so, cmd, data, ifp, p)); switch (ocmd) { case OSIOCGIFADDR: case OSIOCGIFDSTADDR: case OSIOCGIFBRDADDR: case OSIOCGIFNETMASK: *(u_short *)&ifr->ifr_addr = ifr->ifr_addr.sa_family; } return (error); } #endif } return (0); } /* * Set/clear promiscuous mode on interface ifp based on the truth value * of pswitch. The calls are reference counted so that only the first * "on" request actually has an effect, as does the final "off" request. * Results are undefined if the "off" and "on" requests are not matched. */ int ifpromisc(ifp, pswitch) struct ifnet *ifp; int pswitch; { struct ifreq ifr; int error; if (pswitch) { /* * If the device is not configured up, we cannot put it in * promiscuous mode. */ if ((ifp->if_flags & IFF_UP) == 0) return (ENETDOWN); if (ifp->if_pcount++ != 0) return (0); ifp->if_flags |= IFF_PROMISC; log(LOG_INFO, "%s%d: promiscuous mode enabled\n", ifp->if_name, ifp->if_unit); } else { if (--ifp->if_pcount > 0) return (0); ifp->if_flags &= ~IFF_PROMISC; } ifr.ifr_flags = ifp->if_flags; error = (*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, (caddr_t)&ifr); if (error == 0) rt_ifmsg(ifp); return error; } /* * Return interface configuration * of system. List may be used * in later ioctl's (above) to get * other information. */ /*ARGSUSED*/ static int ifconf(cmd, data) u_long cmd; caddr_t data; { register struct ifconf *ifc = (struct ifconf *)data; register struct ifnet *ifp = ifnet.tqh_first; register struct ifaddr *ifa; struct ifreq ifr, *ifrp; int space = ifc->ifc_len, error = 0; ifrp = ifc->ifc_req; for (; space > sizeof (ifr) && ifp; ifp = ifp->if_link.tqe_next) { char workbuf[64]; int ifnlen; ifnlen = snprintf(workbuf, sizeof(workbuf), "%s%d", ifp->if_name, ifp->if_unit); if(ifnlen + 1 > sizeof ifr.ifr_name) { error = ENAMETOOLONG; } else { strcpy(ifr.ifr_name, workbuf); } if ((ifa = ifp->if_addrhead.tqh_first) == 0) { bzero((caddr_t)&ifr.ifr_addr, sizeof(ifr.ifr_addr)); error = copyout((caddr_t)&ifr, (caddr_t)ifrp, sizeof (ifr)); if (error) break; space -= sizeof (ifr), ifrp++; } else for ( ; space > sizeof (ifr) && ifa; ifa = ifa->ifa_link.tqe_next) { register struct sockaddr *sa = ifa->ifa_addr; #ifdef COMPAT_43 if (cmd == OSIOCGIFCONF) { struct osockaddr *osa = (struct osockaddr *)&ifr.ifr_addr; ifr.ifr_addr = *sa; osa->sa_family = sa->sa_family; error = copyout((caddr_t)&ifr, (caddr_t)ifrp, sizeof (ifr)); ifrp++; } else #endif if (sa->sa_len <= sizeof(*sa)) { ifr.ifr_addr = *sa; error = copyout((caddr_t)&ifr, (caddr_t)ifrp, sizeof (ifr)); ifrp++; } else { space -= sa->sa_len - sizeof(*sa); if (space < sizeof (ifr)) break; error = copyout((caddr_t)&ifr, (caddr_t)ifrp, sizeof (ifr.ifr_name)); if (error == 0) error = copyout((caddr_t)sa, (caddr_t)&ifrp->ifr_addr, sa->sa_len); ifrp = (struct ifreq *) (sa->sa_len + (caddr_t)&ifrp->ifr_addr); } if (error) break; space -= sizeof (ifr); } } ifc->ifc_len -= space; return (error); } /* * Just like if_promisc(), but for all-multicast-reception mode. */ int if_allmulti(ifp, onswitch) struct ifnet *ifp; int onswitch; { int error = 0; int s = splimp(); if (onswitch) { if (ifp->if_amcount++ == 0) { ifp->if_flags |= IFF_ALLMULTI; error = ifp->if_ioctl(ifp, SIOCSIFFLAGS, 0); } } else { if (ifp->if_amcount > 1) { ifp->if_amcount--; } else { ifp->if_amcount = 0; ifp->if_flags &= ~IFF_ALLMULTI; error = ifp->if_ioctl(ifp, SIOCSIFFLAGS, 0); } } splx(s); if (error == 0) rt_ifmsg(ifp); return error; } /* * Add a multicast listenership to the interface in question. * The link layer provides a routine which converts */ int if_addmulti(ifp, sa, retifma) struct ifnet *ifp; /* interface to manipulate */ struct sockaddr *sa; /* address to add */ struct ifmultiaddr **retifma; { struct sockaddr *llsa, *dupsa; int error, s; struct ifmultiaddr *ifma; /* * If the matching multicast address already exists * then don't add a new one, just add a reference */ for (ifma = ifp->if_multiaddrs.lh_first; ifma; ifma = ifma->ifma_link.le_next) { if (equal(sa, ifma->ifma_addr)) { ifma->ifma_refcount++; if (retifma) *retifma = ifma; return 0; } } /* * Give the link layer a chance to accept/reject it, and also * find out which AF_LINK address this maps to, if it isn't one * already. */ if (ifp->if_resolvemulti) { error = ifp->if_resolvemulti(ifp, &llsa, sa); if (error) return error; } else { llsa = 0; } MALLOC(ifma, struct ifmultiaddr *, sizeof *ifma, M_IFMADDR, M_WAITOK); MALLOC(dupsa, struct sockaddr *, sa->sa_len, M_IFMADDR, M_WAITOK); bcopy(sa, dupsa, sa->sa_len); ifma->ifma_addr = dupsa; ifma->ifma_lladdr = llsa; ifma->ifma_ifp = ifp; ifma->ifma_refcount = 1; ifma->ifma_protospec = 0; rt_newmaddrmsg(RTM_NEWMADDR, ifma); /* * Some network interfaces can scan the address list at * interrupt time; lock them out. */ s = splimp(); LIST_INSERT_HEAD(&ifp->if_multiaddrs, ifma, ifma_link); splx(s); *retifma = ifma; if (llsa != 0) { for (ifma = ifp->if_multiaddrs.lh_first; ifma; ifma = ifma->ifma_link.le_next) { if (equal(ifma->ifma_addr, llsa)) break; } if (ifma) { ifma->ifma_refcount++; } else { MALLOC(ifma, struct ifmultiaddr *, sizeof *ifma, M_IFMADDR, M_WAITOK); MALLOC(dupsa, struct sockaddr *, llsa->sa_len, M_IFMADDR, M_WAITOK); bcopy(llsa, dupsa, llsa->sa_len); ifma->ifma_addr = dupsa; ifma->ifma_ifp = ifp; ifma->ifma_refcount = 1; s = splimp(); LIST_INSERT_HEAD(&ifp->if_multiaddrs, ifma, ifma_link); splx(s); } } /* * We are certain we have added something, so call down to the * interface to let them know about it. */ s = splimp(); ifp->if_ioctl(ifp, SIOCADDMULTI, 0); splx(s); return 0; } /* * Remove a reference to a multicast address on this interface. Yell * if the request does not match an existing membership. */ int if_delmulti(ifp, sa) struct ifnet *ifp; struct sockaddr *sa; { struct ifmultiaddr *ifma; int s; for (ifma = ifp->if_multiaddrs.lh_first; ifma; ifma = ifma->ifma_link.le_next) if (equal(sa, ifma->ifma_addr)) break; if (ifma == 0) return ENOENT; if (ifma->ifma_refcount > 1) { ifma->ifma_refcount--; return 0; } rt_newmaddrmsg(RTM_DELMADDR, ifma); sa = ifma->ifma_lladdr; s = splimp(); LIST_REMOVE(ifma, ifma_link); splx(s); free(ifma->ifma_addr, M_IFMADDR); free(ifma, M_IFMADDR); if (sa == 0) return 0; /* * Now look for the link-layer address which corresponds to * this network address. It had been squirreled away in * ifma->ifma_lladdr for this purpose (so we don't have * to call ifp->if_resolvemulti() again), and we saved that * value in sa above. If some nasty deleted the * link-layer address out from underneath us, we can deal because * the address we stored was is not the same as the one which was * in the record for the link-layer address. (So we don't complain * in that case.) */ for (ifma = ifp->if_multiaddrs.lh_first; ifma; ifma = ifma->ifma_link.le_next) if (equal(sa, ifma->ifma_addr)) break; if (ifma == 0) return 0; if (ifma->ifma_refcount > 1) { ifma->ifma_refcount--; return 0; } s = splimp(); LIST_REMOVE(ifma, ifma_link); ifp->if_ioctl(ifp, SIOCDELMULTI, 0); splx(s); free(ifma->ifma_addr, M_IFMADDR); free(sa, M_IFMADDR); free(ifma, M_IFMADDR); return 0; } struct ifmultiaddr * ifmaof_ifpforaddr(sa, ifp) struct sockaddr *sa; struct ifnet *ifp; { struct ifmultiaddr *ifma; for (ifma = ifp->if_multiaddrs.lh_first; ifma; ifma = ifma->ifma_link.le_next) if (equal(ifma->ifma_addr, sa)) break; return ifma; } SYSCTL_NODE(_net, PF_LINK, link, CTLFLAG_RW, 0, "Link layers"); SYSCTL_NODE(_net_link, 0, generic, CTLFLAG_RW, 0, "Generic link-management"); Index: head/sys/net/if_media.c =================================================================== --- head/sys/net/if_media.c (revision 45719) +++ head/sys/net/if_media.c (revision 45720) @@ -1,474 +1,487 @@ /* $NetBSD: if_media.c,v 1.1 1997/03/17 02:55:15 thorpej Exp $ */ -/* $Id: if_media.c,v 1.5 1998/02/06 12:13:48 eivind Exp $ */ +/* $Id: if_media.c,v 1.6 1998/02/09 06:09:54 eivind Exp $ */ /* * Copyright (c) 1997 * Jonathan Stone and Jason R. Thorpe. All rights reserved. * * This software is derived from information provided by Matt Thomas. * * 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 Jonathan Stone * and Jason R. Thorpe for the NetBSD Project. * 4. The names of the authors may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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. */ /* * BSD/OS-compatible network interface media selection. * * Where it is safe to do so, this code strays slightly from the BSD/OS * design. Software which uses the API (device drivers, basically) * shouldn't notice any difference. * * Many thanks to Matt Thomas for providing the information necessary * to implement this interface. */ #include #include #include #include #include #include #include /* * Compile-time options: * IFMEDIA_DEBUG: * turn on implementation-level debug printfs. * Useful for debugging newly-ported drivers. */ static struct ifmedia_entry *ifmedia_match __P((struct ifmedia *ifm, int flags, int mask)); #ifdef IFMEDIA_DEBUG int ifmedia_debug = 0; static void ifmedia_printword __P((int)); #endif /* * Initialize if_media struct for a specific interface instance. */ void ifmedia_init(ifm, dontcare_mask, change_callback, status_callback) struct ifmedia *ifm; int dontcare_mask; ifm_change_cb_t change_callback; ifm_stat_cb_t status_callback; { LIST_INIT(&ifm->ifm_list); ifm->ifm_cur = NULL; ifm->ifm_media = 0; ifm->ifm_mask = dontcare_mask; /* IF don't-care bits */ ifm->ifm_change = change_callback; ifm->ifm_status = status_callback; +} + +void +ifmedia_removeall(ifm) + struct ifmedia *ifm; +{ + struct ifmedia_entry *entry; + + for (entry = LIST_FIRST(&ifm->ifm_list); entry; + entry = LIST_FIRST(&ifm->ifm_list)) { + LIST_REMOVE(entry, ifm_list); + free(entry, M_IFADDR); + } } /* * Add a media configuration to the list of supported media * for a specific interface instance. */ void ifmedia_add(ifm, mword, data, aux) struct ifmedia *ifm; int mword; int data; void *aux; { register struct ifmedia_entry *entry; #ifdef IFMEDIA_DEBUG if (ifmedia_debug) { if (ifm == NULL) { printf("ifmedia_add: null ifm\n"); return; } printf("Adding entry for "); ifmedia_printword(mword); } #endif entry = malloc(sizeof(*entry), M_IFADDR, M_NOWAIT); if (entry == NULL) panic("ifmedia_add: can't malloc entry"); entry->ifm_media = mword; entry->ifm_data = data; entry->ifm_aux = aux; LIST_INSERT_HEAD(&ifm->ifm_list, entry, ifm_list); } /* * Add an array of media configurations to the list of * supported media for a specific interface instance. */ void ifmedia_list_add(ifm, lp, count) struct ifmedia *ifm; struct ifmedia_entry *lp; int count; { int i; for (i = 0; i < count; i++) ifmedia_add(ifm, lp[i].ifm_media, lp[i].ifm_data, lp[i].ifm_aux); } /* * Set the default active media. * * Called by device-specific code which is assumed to have already * selected the default media in hardware. We do _not_ call the * media-change callback. */ void ifmedia_set(ifm, target) struct ifmedia *ifm; int target; { struct ifmedia_entry *match; match = ifmedia_match(ifm, target, ifm->ifm_mask); if (match == NULL) { printf("ifmedia_set: no match for 0x%x/0x%x\n", target, ~ifm->ifm_mask); panic("ifmedia_set"); } ifm->ifm_cur = match; #ifdef IFMEDIA_DEBUG if (ifmedia_debug) { printf("ifmedia_set: target "); ifmedia_printword(target); printf("ifmedia_set: setting to "); ifmedia_printword(ifm->ifm_cur->ifm_media); } #endif } /* * Device-independent media ioctl support function. */ int ifmedia_ioctl(ifp, ifr, ifm, cmd) struct ifnet *ifp; struct ifreq *ifr; struct ifmedia *ifm; u_long cmd; { struct ifmedia_entry *match; struct ifmediareq *ifmr = (struct ifmediareq *) ifr; int error = 0, sticky; if (ifp == NULL || ifr == NULL || ifm == NULL) return(EINVAL); switch (cmd) { /* * Set the current media. */ case SIOCSIFMEDIA: { struct ifmedia_entry *oldentry; int oldmedia; int newmedia = ifr->ifr_media; match = ifmedia_match(ifm, newmedia, ifm->ifm_mask); if (match == NULL) { #ifdef IFMEDIA_DEBUG if (ifmedia_debug) { printf( "ifmedia_ioctl: no media found for 0x%x\n", newmedia); } #endif return (ENXIO); } /* * If no change, we're done. * XXX Automedia may invole software intervention. * Keep going in case the the connected media changed. * Similarly, if best match changed (kernel debugger?). */ if ((IFM_SUBTYPE(newmedia) != IFM_AUTO) && (newmedia == ifm->ifm_media) && (match == ifm->ifm_cur)) return 0; /* * We found a match, now make the driver switch to it. * Make sure to preserve our old media type in case the * driver can't switch. */ #ifdef IFMEDIA_DEBUG if (ifmedia_debug) { printf("ifmedia_ioctl: switching %s to ", ifp->if_xname); ifmedia_printword(match->ifm_media); } #endif oldentry = ifm->ifm_cur; oldmedia = ifm->ifm_media; ifm->ifm_cur = match; ifm->ifm_media = newmedia; error = (*ifm->ifm_change)(ifp); if (error) { ifm->ifm_cur = oldentry; ifm->ifm_media = oldmedia; } break; } /* * Get list of available media and current media on interface. */ case SIOCGIFMEDIA: { struct ifmedia_entry *ep; int *kptr, count; kptr = NULL; /* XXX gcc */ ifmr->ifm_active = ifmr->ifm_current = ifm->ifm_cur ? ifm->ifm_cur->ifm_media : IFM_NONE; ifmr->ifm_mask = ifm->ifm_mask; ifmr->ifm_status = 0; (*ifm->ifm_status)(ifp, ifmr); count = 0; ep = ifm->ifm_list.lh_first; if (ifmr->ifm_count != 0) { kptr = (int *)malloc(ifmr->ifm_count * sizeof(int), M_TEMP, M_WAITOK); /* * Get the media words from the interface's list. */ for (; ep != NULL && count < ifmr->ifm_count; ep = ep->ifm_list.le_next, count++) kptr[count] = ep->ifm_media; if (ep != NULL) error = E2BIG; /* oops! */ } /* * If there are more interfaces on the list, count * them. This allows the caller to set ifmr->ifm_count * to 0 on the first call to know how much space to * callocate. */ for (; ep != NULL; ep = ep->ifm_list.le_next) count++; /* * We do the copyout on E2BIG, because that's * just our way of telling userland that there * are more. This is the behavior I've observed * under BSD/OS 3.0 */ sticky = error; if ((error == 0 || error == E2BIG) && ifmr->ifm_count != 0) { error = copyout((caddr_t)kptr, (caddr_t)ifmr->ifm_ulist, ifmr->ifm_count * sizeof(int)); } if (error == 0) error = sticky; if (ifmr->ifm_count != 0) free(kptr, M_TEMP); ifmr->ifm_count = count; break; } default: return (EINVAL); } return (error); } /* * Find media entry matching a given ifm word. * */ static struct ifmedia_entry * ifmedia_match(ifm, target, mask) struct ifmedia *ifm; int target; int mask; { struct ifmedia_entry *match, *next; match = NULL; mask = ~mask; for (next = ifm->ifm_list.lh_first; next != NULL; next = next->ifm_list.le_next) { if ((next->ifm_media & mask) == (target & mask)) { #if defined(IFMEDIA_DEBUG) || defined(DIAGNOSTIC) if (match) { printf("ifmedia_match: multiple match for " "0x%x/0x%x\n", target, mask); } #endif match = next; } } return match; } #ifdef IFMEDIA_DEBUG struct ifmedia_description ifm_type_descriptions[] = IFM_TYPE_DESCRIPTIONS; struct ifmedia_description ifm_subtype_ethernet_descriptions[] = IFM_SUBTYPE_ETHERNET_DESCRIPTIONS; struct ifmedia_description ifm_subtype_ethernet_option_descriptions[] = IFM_SUBTYPE_ETHERNET_OPTION_DESCRIPTIONS; struct ifmedia_description ifm_subtype_tokenring_descriptions[] = IFM_SUBTYPE_TOKENRING_DESCRIPTIONS; struct ifmedia_description ifm_subtype_tokenring_option_descriptions[] = IFM_SUBTYPE_TOKENRING_OPTION_DESCRIPTIONS; struct ifmedia_description ifm_subtype_fddi_descriptions[] = IFM_SUBTYPE_FDDI_DESCRIPTIONS; struct ifmedia_description ifm_subtype_fddi_option_descriptions[] = IFM_SUBTYPE_FDDI_OPTION_DESCRIPTIONS; struct ifmedia_description ifm_subtype_shared_descriptions[] = IFM_SUBTYPE_SHARED_DESCRIPTIONS; struct ifmedia_description ifm_shared_option_descriptions[] = IFM_SHARED_OPTION_DESCRIPTIONS; struct ifmedia_type_to_subtype { struct ifmedia_description *subtypes; struct ifmedia_description *options; }; /* must be in the same order as IFM_TYPE_DESCRIPTIONS */ struct ifmedia_type_to_subtype ifmedia_types_to_subtypes[] = { { &ifm_subtype_ethernet_descriptions[0], &ifm_subtype_ethernet_option_descriptions[0] }, { &ifm_subtype_tokenring_descriptions[0], &ifm_subtype_tokenring_option_descriptions[0] }, { &ifm_subtype_fddi_descriptions[0], &ifm_subtype_fddi_option_descriptions[0] }, }; /* * print a media word. */ static void ifmedia_printword(ifmw) int ifmw; { struct ifmedia_description *desc; struct ifmedia_type_to_subtype *ttos; int seen_option = 0; /* Find the top-level interface type. */ for (desc = ifm_type_descriptions, ttos = ifmedia_types_to_subtypes; desc->ifmt_string != NULL; desc++, ttos++) if (IFM_TYPE(ifmw) == desc->ifmt_word) break; if (desc->ifmt_string == NULL) { printf("\n"); return; } printf(desc->ifmt_string); /* * Check for the shared subtype descriptions first, then the * type-specific ones. */ for (desc = ifm_subtype_shared_descriptions; desc->ifmt_string != NULL; desc++) if (IFM_SUBTYPE(ifmw) == desc->ifmt_word) goto got_subtype; for (desc = ttos->subtypes; desc->ifmt_string != NULL; desc++) if (IFM_SUBTYPE(ifmw) == desc->ifmt_word) break; if (desc->ifmt_string == NULL) { printf(" \n"); return; } got_subtype: printf(" %s", desc->ifmt_string); /* * Look for shared options. */ for (desc = ifm_shared_option_descriptions; desc->ifmt_string != NULL; desc++) { if (ifmw & desc->ifmt_word) { if (seen_option == 0) printf(" <"); printf("%s%s", seen_option++ ? "," : "", desc->ifmt_string); } } /* * Look for subtype-specific options. */ for (desc = ttos->options; desc->ifmt_string != NULL; desc++) { if (ifmw & desc->ifmt_word) { if (seen_option == 0) printf(" <"); printf("%s%s", seen_option++ ? "," : "", desc->ifmt_string); } } printf("%s\n", seen_option ? ">" : ""); } #endif /* IFMEDIA_DEBUG */ Index: head/sys/net/if_media.h =================================================================== --- head/sys/net/if_media.h (revision 45719) +++ head/sys/net/if_media.h (revision 45720) @@ -1,350 +1,353 @@ /* $NetBSD: if_media.h,v 1.3 1997/03/26 01:19:27 thorpej Exp $ */ -/* $Id: if_media.h,v 1.3 1999/02/20 11:17:59 julian Exp $ */ +/* $Id: if_media.h,v 1.5 1999/03/07 04:39:25 wpaul Exp $ */ /* * Copyright (c) 1997 * Jonathan Stone and Jason R. Thorpe. All rights reserved. * * This software is derived from information provided by Matt Thomas. * * 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 Jonathan Stone * and Jason R. Thorpe for the NetBSD Project. * 4. The names of the authors may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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. */ #ifndef _NET_IF_MEDIA_H_ #define _NET_IF_MEDIA_H_ /* * Prototypes and definitions for BSD/OS-compatible network interface * media selection. * * Where it is safe to do so, this code strays slightly from the BSD/OS * design. Software which uses the API (device drivers, basically) * shouldn't notice any difference. * * Many thanks to Matt Thomas for providing the information necessary * to implement this interface. */ #ifdef KERNEL #include /* * Driver callbacks for media status and change requests. */ typedef int (*ifm_change_cb_t) __P((struct ifnet *ifp)); typedef void (*ifm_stat_cb_t) __P((struct ifnet *ifp, struct ifmediareq *req)); /* * In-kernel representation of a single supported media type. */ struct ifmedia_entry { LIST_ENTRY(ifmedia_entry) ifm_list; int ifm_media; /* description of this media attachment */ int ifm_data; /* for driver-specific use */ void *ifm_aux; /* for driver-specific use */ }; /* * One of these goes into a network interface's softc structure. * It is used to keep general media state. */ struct ifmedia { int ifm_mask; /* mask of changes we don't care about */ int ifm_media; /* current user-set media word */ struct ifmedia_entry *ifm_cur; /* currently selected media */ LIST_HEAD(, ifmedia_entry) ifm_list; /* list of all supported media */ ifm_change_cb_t ifm_change; /* media change driver callback */ ifm_stat_cb_t ifm_status; /* media status driver callback */ }; /* Initialize an interface's struct if_media field. */ void ifmedia_init __P((struct ifmedia *ifm, int dontcare_mask, ifm_change_cb_t change_callback, ifm_stat_cb_t status_callback)); + +/* Remove all mediums from a struct ifmedia. */ +void ifmedia_removeall __P(( struct ifmedia *ifm)); /* Add one supported medium to a struct ifmedia. */ void ifmedia_add __P((struct ifmedia *ifm, int mword, int data, void *aux)); /* Add an array (of ifmedia_entry) media to a struct ifmedia. */ void ifmedia_list_add(struct ifmedia *mp, struct ifmedia_entry *lp, int count); /* Set default media type on initialization. */ void ifmedia_set __P((struct ifmedia *ifm, int mword)); /* Common ioctl function for getting/setting media, called by driver. */ int ifmedia_ioctl __P((struct ifnet *ifp, struct ifreq *ifr, struct ifmedia *ifm, u_long cmd)); #endif /*KERNEL */ /* * if_media Options word: * Bits Use * ---- ------- * 0-3 Media variant * 4 RFU * 5-7 Media type * 8-15 Type specific options * 16-19 RFU * 20-27 Shared (global) options * 28-31 Instance */ /* * Ethernet */ #define IFM_ETHER 0x00000020 #define IFM_10_T 3 /* 10BaseT - RJ45 */ #define IFM_10_2 4 /* 10Base2 - Thinnet */ #define IFM_10_5 5 /* 10Base5 - AUI */ #define IFM_100_TX 6 /* 100BaseTX - RJ45 */ #define IFM_100_FX 7 /* 100BaseFX - Fiber */ #define IFM_100_T4 8 /* 100BaseT4 - 4 pair cat 3 */ #define IFM_100_VG 9 /* 100VG-AnyLAN */ #define IFM_100_T2 10 /* 100BaseT2 */ #define IFM_1000_FX 11 /* 1000BaseFX - gigabit over fiber */ #define IFM_10_STP 12 /* 10BaseT over shielded TP */ #define IFM_10_FL 13 /* 10baseFL - Fiber */ #define IFM_1000_SX 14 /* 1000BaseSX Multi-mode Fiber */ #define IFM_1000_LX 15 /* 1000BaseLX Single-mode Fiber */ #define IFM_1000_CX 16 /* 1000BaseCX 150ohm STP */ #define IFM_1000_TX 17 /* 1000BaseTX 4 pair cat 5 */ /* * Token ring */ #define IFM_TOKEN 0x00000040 #define IFM_TOK_STP4 3 /* Shielded twisted pair 4m - DB9 */ #define IFM_TOK_STP16 4 /* Shielded twisted pair 16m - DB9 */ #define IFM_TOK_UTP4 5 /* Unshielded twisted pair 4m - RJ45 */ #define IFM_TOK_UTP16 6 /* Unshielded twisted pair 16m - RJ45 */ #define IFM_TOK_STP100 7 /* Shielded twisted pair 100m - DB9 */ #define IFM_TOK_UTP100 8 /* Unshielded twisted pair 100m - RJ45 */ #define IFM_TOK_ETR 0x00000200 /* Early token release */ #define IFM_TOK_SRCRT 0x00000400 /* Enable source routing features */ #define IFM_TOK_ALLR 0x00000800 /* All routes / Single route bcast */ #define IFM_TOK_DTR 0x00002000 /* Dedicated token ring */ #define IFM_TOK_CLASSIC 0x00004000 /* Classic token ring */ #define IFM_TOK_AUTO 0x00008000 /* Automatic Dedicate/Classic token ring */ /* * FDDI */ #define IFM_FDDI 0x00000060 #define IFM_FDDI_SMF 3 /* Single-mode fiber */ #define IFM_FDDI_MMF 4 /* Multi-mode fiber */ #define IFM_FDDI_UTP 5 /* CDDI / UTP */ #define IFM_FDDI_DA 0x00000100 /* Dual attach / single attach */ /* * Shared media sub-types */ #define IFM_AUTO 0 /* Autoselect best media */ #define IFM_MANUAL 1 /* Jumper/dipswitch selects media */ #define IFM_NONE 2 /* Deselect all media */ /* * Shared options */ #define IFM_FDX 0x00100000 /* Force full duplex */ #define IFM_HDX 0x00200000 /* Force half duplex */ #define IFM_FLAG0 0x01000000 /* Driver defined flag */ #define IFM_FLAG1 0x02000000 /* Driver defined flag */ #define IFM_FLAG2 0x04000000 /* Driver defined flag */ #define IFM_LOOP 0x08000000 /* Put hardware in loopback */ /* * Masks */ #define IFM_NMASK 0x000000e0 /* Network type */ #define IFM_TMASK 0x0000000f /* Media sub-type */ #define IFM_IMASK 0xf0000000 /* Instance */ #define IFM_ISHIFT 28 /* Instance shift */ #define IFM_OMASK 0x0000ff00 /* Type specific options */ #define IFM_GMASK 0x0ff00000 /* Global options */ /* * Status bits */ #define IFM_AVALID 0x00000001 /* Active bit valid */ #define IFM_ACTIVE 0x00000002 /* Interface attached to working net */ /* * Macros to extract various bits of information from the media word. */ #define IFM_TYPE(x) ((x) & IFM_NMASK) #define IFM_SUBTYPE(x) ((x) & IFM_TMASK) #define IFM_TYPE_OPTIONS(x) ((x) & IFM_OMASK) #define IFM_INST(x) (((x) & IFM_IMASK) >> IFM_ISHIFT) #define IFM_OPTIONS(x) ((x) & (IFM_OMASK|IFM_GMASK)) #define IFM_INST_MAX IFM_INST(IFM_IMASK) /* * Macro to create a media word. */ #define IFM_MAKEWORD(type, subtype, options, instance) \ ((type) | (subtype) | (options) | ((instance) << IFM_ISHIFT)) /* * NetBSD extension not defined in the BSDI API. This is used in various * places to get the canonical description for a given type/subtype. * * NOTE: all but the top-level type descriptions must contain NO whitespace! * Otherwise, parsing these in ifconfig(8) would be a nightmare. */ struct ifmedia_description { int ifmt_word; /* word value; may be masked */ const char *ifmt_string; /* description */ }; #define IFM_TYPE_DESCRIPTIONS { \ { IFM_ETHER, "Ethernet" }, \ { IFM_TOKEN, "Token ring" }, \ { IFM_FDDI, "FDDI" }, \ { 0, NULL }, \ } #define IFM_SUBTYPE_ETHERNET_DESCRIPTIONS { \ { IFM_10_T, "10baseT/UTP" }, \ { IFM_10_2, "10base2/BNC" }, \ { IFM_10_5, "10base5/AUI" }, \ { IFM_100_TX, "100baseTX" }, \ { IFM_100_FX, "100baseFX" }, \ { IFM_100_T4, "100baseT4" }, \ { IFM_100_VG, "100baseVG" }, \ { IFM_100_T2, "100baseT2" }, \ { IFM_1000_FX, "1000baseFX" }, \ { IFM_10_STP, "10baseSTP" }, \ { IFM_10_FL, "10baseFL" }, \ { IFM_1000_SX, "1000baseSX" }, \ { IFM_1000_LX, "1000baseLX" }, \ { IFM_1000_CX, "1000baseCX" }, \ { IFM_1000_TX, "1000baseTX" }, \ { 0, NULL }, \ } #define IFM_SUBTYPE_ETHERNET_ALIASES { \ { IFM_10_T, "UTP" }, \ { IFM_10_T, "10UTP" }, \ { IFM_10_2, "BNC" }, \ { IFM_10_2, "10BNC" }, \ { IFM_10_5, "AUI" }, \ { IFM_10_5, "10AUI" }, \ { IFM_100_TX, "100TX" }, \ { IFM_100_FX, "100FX" }, \ { IFM_100_T4, "100T4" }, \ { IFM_100_VG, "100VG" }, \ { IFM_100_T2, "100T2" }, \ { IFM_1000_FX, "1000FX" }, \ { IFM_10_STP, "10STP" }, \ { IFM_10_FL, "10FL" }, \ { IFM_1000_FX, "1000SX" }, \ { IFM_1000_FX, "1000LX" }, \ { IFM_1000_FX, "1000CX" }, \ { IFM_1000_FX, "1000TX" }, \ { 0, NULL }, \ } #define IFM_SUBTYPE_ETHERNET_OPTION_DESCRIPTIONS { \ { 0, NULL }, \ } #define IFM_SUBTYPE_TOKENRING_DESCRIPTIONS { \ { IFM_TOK_STP4, "DB9/4Mbit" }, \ { IFM_TOK_STP16, "DB9/16Mbit" }, \ { IFM_TOK_UTP4, "UTP/4Mbit" }, \ { IFM_TOK_UTP16, "UTP/16Mbit" }, \ { IFM_TOK_STP100, "STP/100Mbit" }, \ { IFM_TOK_UTP100, "UTP/100Mbit" }, \ { 0, NULL }, \ } #define IFM_SUBTYPE_TOKENRING_ALIASES { \ { IFM_TOK_STP4, "4STP" }, \ { IFM_TOK_STP16, "16STP" }, \ { IFM_TOK_UTP4, "4UTP" }, \ { IFM_TOK_UTP16, "16UTP" }, \ { IFM_TOK_STP100, "100STP" }, \ { IFM_TOK_UTP100, "100UTP" }, \ { 0, NULL }, \ } #define IFM_SUBTYPE_TOKENRING_OPTION_DESCRIPTIONS { \ { IFM_TOK_ETR, "EarlyTokenRelease" }, \ { IFM_TOK_SRCRT, "SourceRouting" }, \ { IFM_TOK_ALLR, "AllRoutes" }, \ { IFM_TOK_DTR, "Dedicated" }, \ { IFM_TOK_CLASSIC,"Classic" }, \ { IFM_TOK_AUTO, " " }, \ { 0, NULL }, \ } #define IFM_SUBTYPE_FDDI_DESCRIPTIONS { \ { IFM_FDDI_SMF, "Single-mode" }, \ { IFM_FDDI_MMF, "Multi-mode" }, \ { IFM_FDDI_UTP, "UTP" }, \ { 0, NULL }, \ } #define IFM_SUBTYPE_FDDI_ALIASES { \ { IFM_FDDI_SMF, "SMF" }, \ { IFM_FDDI_MMF, "MMF" }, \ { IFM_FDDI_UTP, "CDDI" }, \ { 0, NULL }, \ } #define IFM_SUBTYPE_FDDI_OPTION_DESCRIPTIONS { \ { IFM_FDDI_DA, "Dual-attach" }, \ { 0, NULL }, \ } #define IFM_SUBTYPE_SHARED_DESCRIPTIONS { \ { IFM_AUTO, "autoselect" }, \ { IFM_MANUAL, "manual" }, \ { IFM_NONE, "none" }, \ { 0, NULL }, \ } #define IFM_SUBTYPE_SHARED_ALIASES { \ { IFM_AUTO, "auto" }, \ { 0, NULL }, \ } #define IFM_SHARED_OPTION_DESCRIPTIONS { \ { IFM_FDX, "full-duplex" }, \ { IFM_HDX, "half-duplex" }, \ { IFM_FLAG0, "flag0" }, \ { IFM_FLAG1, "flag1" }, \ { IFM_FLAG2, "flag2" }, \ { IFM_LOOP, "hw-loopback" }, \ { 0, NULL }, \ } #endif /* _NET_IF_MEDIA_H_ */ Index: head/sys/net/if_var.h =================================================================== --- head/sys/net/if_var.h (revision 45719) +++ head/sys/net/if_var.h (revision 45720) @@ -1,334 +1,335 @@ /* * Copyright (c) 1982, 1986, 1989, 1993 * 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: @(#)if.h 8.1 (Berkeley) 6/10/93 - * $Id: if_var.h,v 1.9 1998/06/12 03:48:09 julian Exp $ + * $Id: if_var.h,v 1.10 1998/12/16 18:30:43 phk Exp $ */ #ifndef _NET_IF_VAR_H_ #define _NET_IF_VAR_H_ /* * Structures defining a network interface, providing a packet * transport mechanism (ala level 0 of the PUP protocols). * * Each interface accepts output datagrams of a specified maximum * length, and provides higher level routines with input datagrams * received from its medium. * * Output occurs when the routine if_output is called, with three parameters: * (*ifp->if_output)(ifp, m, dst, rt) * Here m is the mbuf chain to be sent and dst is the destination address. * The output routine encapsulates the supplied datagram if necessary, * and then transmits it on its medium. * * On input, each interface unwraps the data received by it, and either * places it on the input queue of a internetwork datagram routine * and posts the associated software interrupt, or passes the datagram to a raw * packet input routine. * * Routines exist for locating interfaces by their addresses * or for locating a interface on a certain network, as well as more general * routing and gateway routines maintaining information used to locate * interfaces. These routines live in the files if.c and route.c */ #ifdef __STDC__ /* * Forward structure declarations for function prototypes [sic]. */ struct mbuf; struct proc; struct rtentry; struct socket; struct ether_header; #endif #include /* get TAILQ macros */ TAILQ_HEAD(ifnethead, ifnet); /* we use TAILQs so that the order of */ TAILQ_HEAD(ifaddrhead, ifaddr); /* instantiation is preserved in the list */ LIST_HEAD(ifmultihead, ifmultiaddr); /* * Structure defining a queue for a network interface. */ struct ifqueue { struct mbuf *ifq_head; struct mbuf *ifq_tail; int ifq_len; int ifq_maxlen; int ifq_drops; }; /* * Structure defining a network interface. * * (Would like to call this struct ``if'', but C isn't PL/1.) */ struct ifnet { void *if_softc; /* pointer to driver state */ char *if_name; /* name, e.g. ``en'' or ``lo'' */ TAILQ_ENTRY(ifnet) if_link; /* all struct ifnets are chained */ struct ifaddrhead if_addrhead; /* linked list of addresses per if */ int if_pcount; /* number of promiscuous listeners */ struct bpf_if *if_bpf; /* packet filter structure */ u_short if_index; /* numeric abbreviation for this if */ short if_unit; /* sub-unit for lower level driver */ short if_timer; /* time 'til if_watchdog called */ short if_flags; /* up/down, broadcast, etc. */ int if_ipending; /* interrupts pending */ void *if_linkmib; /* link-type-specific MIB data */ size_t if_linkmiblen; /* length of above data */ struct if_data if_data; struct ifmultihead if_multiaddrs; /* multicast addresses configured */ int if_amcount; /* number of all-multicast requests */ /* procedure handles */ int (*if_output) /* output routine (enqueue) */ __P((struct ifnet *, struct mbuf *, struct sockaddr *, struct rtentry *)); void (*if_start) /* initiate output routine */ __P((struct ifnet *)); int (*if_done) /* output complete routine */ __P((struct ifnet *)); /* (XXX not used; fake prototype) */ int (*if_ioctl) /* ioctl routine */ __P((struct ifnet *, u_long, caddr_t)); void (*if_watchdog) /* timer routine */ __P((struct ifnet *)); int (*if_poll_recv) /* polled receive routine */ __P((struct ifnet *, int *)); int (*if_poll_xmit) /* polled transmit routine */ __P((struct ifnet *, int *)); void (*if_poll_intren) /* polled interrupt reenable routine */ __P((struct ifnet *)); void (*if_poll_slowinput) /* input routine for slow devices */ __P((struct ifnet *, struct mbuf *)); void (*if_init) /* Init routine */ __P((void *)); int (*if_resolvemulti) /* validate/resolve multicast */ __P((struct ifnet *, struct sockaddr **, struct sockaddr *)); struct ifqueue if_snd; /* output queue */ struct ifqueue *if_poll_slowq; /* input queue for slow devices */ }; typedef void if_init_f_t __P((void *)); #define if_mtu if_data.ifi_mtu #define if_type if_data.ifi_type #define if_physical if_data.ifi_physical #define if_addrlen if_data.ifi_addrlen #define if_hdrlen if_data.ifi_hdrlen #define if_metric if_data.ifi_metric #define if_baudrate if_data.ifi_baudrate #define if_ipackets if_data.ifi_ipackets #define if_ierrors if_data.ifi_ierrors #define if_opackets if_data.ifi_opackets #define if_oerrors if_data.ifi_oerrors #define if_collisions if_data.ifi_collisions #define if_ibytes if_data.ifi_ibytes #define if_obytes if_data.ifi_obytes #define if_imcasts if_data.ifi_imcasts #define if_omcasts if_data.ifi_omcasts #define if_iqdrops if_data.ifi_iqdrops #define if_noproto if_data.ifi_noproto #define if_lastchange if_data.ifi_lastchange #define if_recvquota if_data.ifi_recvquota #define if_xmitquota if_data.ifi_xmitquota #define if_rawoutput(if, m, sa) if_output(if, m, sa, (struct rtentry *)0) /* * Bit values in if_ipending */ #define IFI_RECV 1 /* I want to receive */ #define IFI_XMIT 2 /* I want to transmit */ /* * Output queues (ifp->if_snd) and slow device input queues (*ifp->if_slowq) * are queues of messages stored on ifqueue structures * (defined above). Entries are added to and deleted from these structures * by these macros, which should be called with ipl raised to splimp(). */ #define IF_QFULL(ifq) ((ifq)->ifq_len >= (ifq)->ifq_maxlen) #define IF_DROP(ifq) ((ifq)->ifq_drops++) #define IF_ENQUEUE(ifq, m) { \ (m)->m_nextpkt = 0; \ if ((ifq)->ifq_tail == 0) \ (ifq)->ifq_head = m; \ else \ (ifq)->ifq_tail->m_nextpkt = m; \ (ifq)->ifq_tail = m; \ (ifq)->ifq_len++; \ } #define IF_PREPEND(ifq, m) { \ (m)->m_nextpkt = (ifq)->ifq_head; \ if ((ifq)->ifq_tail == 0) \ (ifq)->ifq_tail = (m); \ (ifq)->ifq_head = (m); \ (ifq)->ifq_len++; \ } #define IF_DEQUEUE(ifq, m) { \ (m) = (ifq)->ifq_head; \ if (m) { \ if (((ifq)->ifq_head = (m)->m_nextpkt) == 0) \ (ifq)->ifq_tail = 0; \ (m)->m_nextpkt = 0; \ (ifq)->ifq_len--; \ } \ } #ifdef KERNEL #define IF_ENQ_DROP(ifq, m) if_enq_drop(ifq, m) #if defined(__GNUC__) && defined(MT_HEADER) static __inline int if_queue_drop(struct ifqueue *ifq, struct mbuf *m) { IF_DROP(ifq); return 0; } static __inline int if_enq_drop(struct ifqueue *ifq, struct mbuf *m) { if (IF_QFULL(ifq) && !if_queue_drop(ifq, m)) return 0; IF_ENQUEUE(ifq, m); return 1; } #else #ifdef MT_HEADER int if_enq_drop __P((struct ifqueue *, struct mbuf *)); #endif #endif #endif /* KERNEL */ /* * The ifaddr structure contains information about one address * of an interface. They are maintained by the different address families, * are allocated and attached when an address is set, and are linked * together so all addresses for an interface can be located. */ struct ifaddr { struct sockaddr *ifa_addr; /* address of interface */ struct sockaddr *ifa_dstaddr; /* other end of p-to-p link */ #define ifa_broadaddr ifa_dstaddr /* broadcast address interface */ struct sockaddr *ifa_netmask; /* used to determine subnet */ struct ifnet *ifa_ifp; /* back-pointer to interface */ TAILQ_ENTRY(ifaddr) ifa_link; /* queue macro glue */ void (*ifa_rtrequest) /* check or clean routes (+ or -)'d */ __P((int, struct rtentry *, struct sockaddr *)); u_short ifa_flags; /* mostly rt_flags for cloning */ short ifa_refcnt; /* references to this structure */ int ifa_metric; /* cost of going out this interface */ #ifdef notdef struct rtentry *ifa_rt; /* XXXX for ROUTETOIF ????? */ #endif int (*ifa_claim_addr) /* check if an addr goes to this if */ __P((struct ifaddr *, struct sockaddr *)); }; #define IFA_ROUTE RTF_UP /* route installed */ /* * Multicast address structure. This is analogous to the ifaddr * structure except that it keeps track of multicast addresses. * Also, the reference count here is a count of requests for this * address, not a count of pointers to this structure. */ struct ifmultiaddr { LIST_ENTRY(ifmultiaddr) ifma_link; /* queue macro glue */ struct sockaddr *ifma_addr; /* address this membership is for */ struct sockaddr *ifma_lladdr; /* link-layer translation, if any */ struct ifnet *ifma_ifp; /* back-pointer to interface */ u_int ifma_refcount; /* reference count */ void *ifma_protospec; /* protocol-specific state, if any */ }; #ifdef KERNEL #define IFAFREE(ifa) \ if ((ifa)->ifa_refcnt <= 0) \ ifafree(ifa); \ else \ (ifa)->ifa_refcnt--; extern struct ifnethead ifnet; extern int ifqmaxlen; extern struct ifnet loif[]; extern int if_index; extern struct ifaddr **ifnet_addrs; void ether_ifattach __P((struct ifnet *)); void ether_input __P((struct ifnet *, struct ether_header *, struct mbuf *)); int ether_output __P((struct ifnet *, struct mbuf *, struct sockaddr *, struct rtentry *)); int ether_ioctl __P((struct ifnet *, int, caddr_t)); int if_addmulti __P((struct ifnet *, struct sockaddr *, struct ifmultiaddr **)); int if_allmulti __P((struct ifnet *, int)); void if_attach __P((struct ifnet *)); int if_delmulti __P((struct ifnet *, struct sockaddr *)); +void if_detach __P((struct ifnet *)); void if_down __P((struct ifnet *)); void if_route __P((struct ifnet *, int flag, int fam)); void if_unroute __P((struct ifnet *, int flag, int fam)); void if_up __P((struct ifnet *)); /*void ifinit __P((void));*/ /* declared in systm.h for main() */ int ifioctl __P((struct socket *, u_long, caddr_t, struct proc *)); int ifpromisc __P((struct ifnet *, int)); struct ifnet *ifunit __P((char *)); int if_poll_recv_slow __P((struct ifnet *ifp, int *quotap)); void if_poll_xmit_slow __P((struct ifnet *ifp, int *quotap)); void if_poll_throttle __P((void)); void if_poll_unthrottle __P((void *)); void if_poll_init __P((void)); void if_poll __P((void)); struct ifaddr *ifa_ifwithaddr __P((struct sockaddr *)); struct ifaddr *ifa_ifwithdstaddr __P((struct sockaddr *)); struct ifaddr *ifa_ifwithnet __P((struct sockaddr *)); struct ifaddr *ifa_ifwithroute __P((int, struct sockaddr *, struct sockaddr *)); struct ifaddr *ifaof_ifpforaddr __P((struct sockaddr *, struct ifnet *)); void ifafree __P((struct ifaddr *)); struct ifmultiaddr *ifmaof_ifpforaddr __P((struct sockaddr *, struct ifnet *)); int if_simloop __P((struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, int hlen)); #endif /* KERNEL */ #endif /* !_NET_IF_VAR_H_ */ Index: head/sys/pci/ide_pci.c =================================================================== --- head/sys/pci/ide_pci.c (revision 45719) +++ head/sys/pci/ide_pci.c (revision 45720) @@ -1,1937 +1,1938 @@ /* * Copyright 1996 Massachusetts Institute of Technology * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby * granted, provided that both the above copyright notice and this * permission notice appear in all copies, that both the above * copyright notice and this permission notice appear in all * supporting documentation, and that the name of M.I.T. not be used * in advertising or publicity pertaining to distribution of the * software without specific, written prior permission. M.I.T. makes * no representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied * warranty. * * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT * SHALL M.I.T. 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. * - * $Id: ide_pci.c,v 1.30 1999/04/13 19:38:12 peter Exp $ + * $Id: ide_pci.c,v 1.31 1999/04/13 20:22:33 peter Exp $ */ #include "pci.h" #if NPCI > 0 #include "wd.h" #if NWDC > 0 #include #include #include #include #include #include #include #include #ifdef PC98 #include #else #include #endif #include #include #include #include #ifndef MIN #define MIN(a,b) (((a)<(b))?(a):(b)) #endif #define PROMISE_ULTRA33 0x4d33105a #define CMD640B_PCI_ID 0x06401095 struct ide_pci_cookie; /* structs vendor_fns, ide_pci_cookie are recursive */ struct vendor_fns { int (*vendor_dmainit) /* initialize DMA controller and drive */ (struct ide_pci_cookie *cookie, struct wdparams *wp, int (*wdcmd)(int, void *), void *); void (*vendor_status) /* prints off DMA timing info */ (struct ide_pci_cookie *cookie); }; /* * XXX the fact that this list keeps all kinds of info on PCI controllers * is pretty grotty-- much of this should be replaced by a proper integration * of PCI probes into the wd driver. * XXX if we're going to support native-PCI controllers, we also need to * keep the address of the IDE control block register, which is something wd.c * needs to know, which is why this info is in the wrong place. */ struct ide_pci_cookie { LIST_ENTRY(ide_pci_cookie) le; int iobase_wd; int ctlr; /* controller 0/1 on PCI IDE interface */ int unit; int iobase_bm; /* SFF-8038 control registers */ int altiobase_wd; pcici_t tag; pcidi_t type; struct ide_pci_prd *prd; struct vendor_fns vs; }; struct ide_pci_softc { LIST_HEAD(, ide_pci_cookie) cookies; }; static int generic_dmainit(struct ide_pci_cookie *cookie, struct wdparams *wp, int (*wdcmd)(int, void *), void *wdinfo); static void generic_status(struct ide_pci_cookie *cookie); static int sis_5591_dmainit(struct ide_pci_cookie *cookie, struct wdparams *wp, int (*wdcmd)(int, void *), void *wdinfo); static void sis_5591_status(struct ide_pci_cookie *cookie); static void via_571_status(struct ide_pci_cookie *cookie); static int via_571_dmainit(struct ide_pci_cookie *cookie, struct wdparams *wp, int (*wdcmd)(int, void *), void *wdinfo); static void acer_status(struct ide_pci_cookie *cookie); static int acer_dmainit(struct ide_pci_cookie *cookie, struct wdparams *wp, int (*wdcmd)(int, void *), void *wdinfo); static void intel_piix_dump_drive(char *ctlr, int sitre, int is_piix4, int word40, int word44, int word48, int word4a, int drive); static void intel_piix_status(struct ide_pci_cookie *cookie); static int intel_piix_dmainit(struct ide_pci_cookie *cookie, struct wdparams *wp, int (*wdcmd)(int, void *), void *wdinfo); static struct ide_pci_cookie * mkcookie(int iobase_wd, int ctlr, int unit, int iobase_bm, pcici_t tag, pcidi_t type, struct vendor_fns *vp, int altiobase_wd); static void ide_pci_attach(pcici_t tag, int unit); static void *ide_pci_candma(int, int, int); static int ide_pci_dmainit(void *, struct wdparams *, int (*)(int, void *), void *); static int ide_pci_dmaverify(void *, char *, u_long, int); static int ide_pci_dmasetup(void *, char *, u_long, int); static void ide_pci_dmastart(void *); static int ide_pci_dmadone(void *); static int ide_pci_status(void *); static int ide_pci_iobase(void *xcp); static int ide_pci_altiobase(void *xcp); static struct ide_pci_softc softc; static int ide_pci_softc_cookies_initted = 0; extern struct isa_driver wdcdriver; /* * PRD_ALLOC_SIZE should be something that will not be allocated across a 64k * boundary. * PRD_MAX_SEGS is defined to be the maximum number of segments required for * a transfer on an IDE drive, for an xfer that is linear in virtual memory. * PRD_BUF_SIZE is the size of the buffer needed for a PRD table. */ #define PRD_ALLOC_SIZE PAGE_SIZE #define PRD_MAX_SEGS ((256 * 512 / PAGE_SIZE) + 1) #define PRD_BUF_SIZE PRD_MAX_SEGS * 8 static void *prdbuf = 0; static void *prdbuf_next = 0; /* * Hardware specific IDE controller code. All vendor-specific code * for handling IDE timing and other chipset peculiarities should be * encapsulated here. */ /* helper funcs */ /* * nnn_mode() return the highest valid mode, or -1 if the mode class is * not supported */ static __inline int pio_mode(struct wdparams *wp) { if ((wp->wdp_atavalid & 2) == 2) { if ((wp->wdp_eidepiomodes & 2) == 2) return 4; if ((wp->wdp_eidepiomodes & 1) == 1) return 3; } return -1; } #if 0 static __inline int dma_mode(struct wdparams *wp) { /* XXX not quite sure how to verify validity on this field */ } #endif static __inline int mwdma_mode(struct wdparams *wp) { /* * XXX technically, using wdp_atavalid to test for validity of * this field is not quite correct */ if ((wp->wdp_atavalid & 2) == 2) { if ((wp->wdp_dmamword & 4) == 4) return 2; if ((wp->wdp_dmamword & 2) == 2) return 1; if ((wp->wdp_dmamword & 1) == 1) return 0; } return -1; } static __inline int udma_mode(struct wdparams *wp) { if ((wp->wdp_atavalid & 4) == 4) { if ((wp->wdp_udmamode & 4) == 4) return 2; if ((wp->wdp_udmamode & 2) == 2) return 1; if ((wp->wdp_udmamode & 1) == 1) return 0; } return -1; } /* Generic busmastering PCI-IDE */ static int generic_dmainit(struct ide_pci_cookie *cookie, struct wdparams *wp, int(*wdcmd)(int, void *), void *wdinfo) { /* * punt on the whole timing issue by looking for either a * drive programmed for both PIO4 and mDMA2 (which use similar * timing) or a drive in an UltraDMA mode (hopefully all * controllers have separate timing for UDMA). one hopes that if * the drive's DMA mode has been configured by the BIOS, the * controller's has also. * * XXX there are examples where this approach is now known to be * broken, at least on systems based on Intel chipsets. */ if ((pio_mode(wp) >= 4 && mwdma_mode(wp) >= 2) || (udma_mode(wp) >= 2)) { printf("ide_pci: generic_dmainit %04x:%d: warning, IDE controller timing not set\n", cookie->iobase_wd, cookie->unit); /* If we're here, then this controller is most likely not set for UDMA, even if the drive may be. Make the drive wise up. */ if(!wdcmd(WDDMA_MDMA2, wdinfo)) printf("generic_dmainit: could not set multiword DMA mode!\n"); return 1; } #ifdef IDE_PCI_DEBUG printf("pio_mode: %d, mwdma_mode(wp): %d, udma_mode(wp): %d\n", pio_mode(wp), mwdma_mode(wp), udma_mode(wp)); #endif return 0; } static void generic_status(struct ide_pci_cookie *cookie) { printf("generic_status: no PCI IDE timing info available\n"); } static struct vendor_fns vs_generic = { generic_dmainit, generic_status }; /* VIA Technologies "82C571" PCI-IDE controller core */ static void via_571_status(struct ide_pci_cookie *cookie) { int iobase_wd; int ctlr, unit; int iobase_bm; pcici_t tag; pcidi_t type; u_long word40[5]; int i, unitno; iobase_wd = cookie->iobase_wd; unit = cookie->unit; ctlr = cookie->ctlr; iobase_bm = cookie->iobase_bm; tag = cookie->tag; type = cookie->type; unitno = ctlr * 2 + unit; for (i=0; i<5; i++) { word40[i] = pci_conf_read(tag, i * 4 + 0x40); } if (ctlr == 0) printf("via_571_status: Primary IDE prefetch/postwrite %s/%s\n", word40[0] & 0x8000 ? "enabled" : "disabled", word40[0] & 0x4000 ? "enabled" : "disabled"); else printf("via_571_status: Secondary IDE prefetch/postwrite %s/%s\n", word40[0] & 0x2000 ? "enabled" : "disabled", word40[0] & 0x1000 ? "enabled" : "disabled"); printf("via_571_status: busmaster status read retry %s\n", (word40[1] & 0x08) ? "enabled" : "disabled"); printf("via_571_status: %s drive %d data setup=%d active=%d recovery=%d\n", unitno < 2 ? "primary" : "secondary", unitno & 1, ((u_int)(word40[3] >> ((3 - unitno) * 2)) & 3) + 1, ((u_int)(word40[2] >> (((3 - unitno) * 8) + 4)) & 0x0f) + 1, ((u_int)(word40[2] >> ((3 - unitno) * 8)) & 0x0f) + 1); if (ctlr == 0) printf("via_571_status: primary ctrl active=%d recovery=%d\n", ((u_int)(word40[3] >> 28) & 0x0f) + 1, ((u_int)(word40[2] >> 24) & 0x0f) + 1); else printf("via_571_status: secondary ctrl active=%d recovery=%d\n", ((u_int)(word40[3] >> 20) & 0x0f) + 1, ((u_int)(word40[2] >> 16) & 0x0f) + 1); /* UltraDMA dump */ { int foo; foo = word40[4] >> ((3 - unitno) * 8); printf("via_571_status: %s drive %d udma method=%d enable=%d PIOmode=%d cycle=%d\n", i < 2 ? "primary" : "secondary", i & 1, (foo >> 7) & 1, (foo >> 6) & 1, (foo >> 5) & 1, (foo & 3) + 2); } } /* * XXX timing values set here are only good for 30/33MHz buses; should deal * with slower ones too (BTW: you overclock-- you lose) */ static int via_571_dmainit(struct ide_pci_cookie *cookie, struct wdparams *wp, int(*wdcmd)(int, void *), void *wdinfo) { int r; u_long pci_revision; int unitno; pci_revision = pci_conf_read(cookie->tag, PCI_CLASS_REG) & PCI_REVISION_MASK; unitno = cookie->ctlr * 2 + cookie->unit; /* If it's a UDMA drive on a '590, set it up */ /* * XXX the revision number we check for is of dubious validity. * it's extracted from the AMD 645 datasheet. */ if (pci_revision >= 1 && udma_mode(wp) >= 2) { unsigned int word50, mask, new; word50 = pci_conf_read(cookie->tag, 0x50); /* UDMA enable by SET FEATURES, DMA cycles, cycle time 2T */ mask = 0xe3000000 >> (unitno * 8); new = 0x40000000 >> (unitno * 8); word50 &= ~mask; word50 |= new; pci_conf_write(cookie->tag, 0x50, word50); /* * With the '590, drive configuration should come *after* the * controller configuration, to make sure the controller sees * the SET FEATURES command and does the right thing. */ /* Set UDMA mode 2 on drive */ if (bootverbose) printf("via_571_dmainit: setting ultra DMA mode 2\n"); r = wdcmd(WDDMA_UDMA2, wdinfo); if (!r) { printf("via_571_dmainit: setting DMA mode failed\n"); return 0; } if (bootverbose) via_571_status(cookie); return 1; } /* otherwise, try and program it for MW DMA mode 2 */ else if (mwdma_mode(wp) >= 2 && pio_mode(wp) >= 4) { u_long workword; /* Set multiword DMA mode 2 on drive */ if (bootverbose) printf("via_571_dmainit: setting multiword DMA mode 2\n"); r = wdcmd(WDDMA_MDMA2, wdinfo); if (!r) { printf("via_571_dmainit: setting DMA mode failed\n"); return 0; } /* Configure the controller appropriately for MWDMA mode 2 */ workword = pci_conf_read(cookie->tag, 0x40); /* * enable prefetch/postwrite-- XXX may cause problems * with CD-ROMs? */ workword |= 0xc000 >> (cookie->ctlr * 2); /* FIFO configurations-- equal split, threshold 1/2 */ workword &= 0x90ffffff; workword |= 0x2a000000; pci_conf_write(cookie->tag, 0x40, workword); workword = pci_conf_read(cookie->tag, 0x44); /* enable status read retry */ workword |= 8; /* enable FIFO flush on interrupt and end of sector */ workword &= 0xff0cffff; workword |= 0x00f00000; pci_conf_write(cookie->tag, 0x44, workword); workword = pci_conf_read(cookie->tag, 0x48); /* set Mode2 timing */ workword &= ~(0xff000000 >> (unitno * 8)); workword |= 0x31000000 >> (unitno * 8); pci_conf_write(cookie->tag, 0x48, workword); /* set sector size */ pci_conf_write(cookie->tag, cookie->ctlr ? 0x68 : 0x60, 0x200); if (bootverbose) via_571_status(cookie); return 1; } return 0; } static struct vendor_fns vs_via_571 = { via_571_dmainit, via_571_status }; /* Cyrix Cx5530 Courtesy of Whistle Communications */ /* * Verify that controller can handle a dma request for cp. Should * not affect any hardware or driver state. * Special version for 5530 that allows only transfers on 16 byte boundaries.(!) * (Yes the Cyrix 5530 can only UDMA to cache-line boundaries.(bleh!)) * Luckily nearly all disk IO is to kernel bufers which are page alligned. * They may fix this in some other version of the chip, but it's in the latest * at this time (Jan 1999). */ static int cyrix_5530_dmaverify(void *xcp, char *vaddr, u_long count, int dir) { int badfu; /* * check for nonaligned or odd-length Stuff */ badfu = ((unsigned int)vaddr & 0xf) || (count & 0xf); #ifdef DIAGNOSTIC if (badfu) { printf("ide_pci: dmaverify odd vaddr or length, "); printf("vaddr = %p length = %08lx\n", (void *)vaddr, count); } #endif return (!badfu); } /* * XXX Unit number handling may be broken in the Cx5530 modules. * It has only been checked with a single drive. * 12MByte/Sec transfer rates were seen with Quantum Fireball drives * with negligable CPU usage. */ static void cyrix_5530_status(struct ide_pci_cookie *cookie) { int iobase_wd; int ctlr, unit; int iobase_bm; pcici_t tag; pcidi_t type; u_long PIO_config; u_long DMA_config; int unitno; iobase_wd = cookie->iobase_wd; unit = cookie->unit; ctlr = cookie->ctlr; iobase_bm = cookie->iobase_bm; tag = cookie->tag; type = cookie->type; unitno = ctlr * 2 + unit; /* set some values the BIOS should have set */ printf("Using 0x%x\n", cookie->iobase_bm); outl(iobase_bm + (unit * 0x10) + 0x20, 0x00040010); outl(iobase_bm + (unit * 0x10) + 0x24, 0x00911030); /* if ((ctlr == 0) && (unit == 0)) */ /* XXX */ /* outb(iobase_bm + (unit * 0x10) + BMISTA_PORT, 0xe6);*/ PIO_config = inl(iobase_bm + (unit * 0x10) + 0x20); DMA_config = inl(iobase_bm + (unit * 0x10) + 0x24); printf("cyrix_5530_status: %s:%u IDE PIO cfg: 0x%08lx\n", (ctlr ? "Secondary" : "Primary"), unit, PIO_config); printf("cyrix_5530_status: %s:%u IDE DMA cfg: 0x%08lx\n", (ctlr ? "Secondary" : "Primary"), unit, DMA_config); } /* * XXX timing values set here are only good for 30/33MHz buses; should deal * with slower ones too (BTW: you overclock-- you lose) */ static int cyrix_5530_dmainit(struct ide_pci_cookie *cookie, struct wdparams *wp, int(*wdcmd)(int, void *), void *wdinfo) { int r; u_long pci_revision; int unitno; int iobase_bm; int unit; /*cookie->unit = 0; */ /* XXX */ unit = cookie->unit; pci_revision = pci_conf_read(cookie->tag, PCI_CLASS_REG) & PCI_REVISION_MASK; unitno = cookie->ctlr * 2 + unit; iobase_bm = cookie->iobase_bm; printf("Setting using 0x%x\n", iobase_bm); if ((cookie->ctlr == 0) && (unit == 0)) /* XXX */ outb(iobase_bm + (unit * 0x10) + BMISTA_PORT, 0xe6); outl(iobase_bm + (unit * 0x10) + 0x20, 0x00040010); outl(iobase_bm + (unit * 0x10) + 0x24, 0x00911030); /* If it's a UDMA drive on a '5530, set it up */ /* * depending on what the drive can do, * set the correct modes, */ printf("wd%d: mw=0x%x, pio=0x%x, pcirev=0x%lx, udma=0x%x\n", unitno, mwdma_mode(wp), pio_mode(wp), pci_revision, udma_mode(wp)); if (/* pci_revision >= 1 && */ udma_mode(wp) >= 2) { /*outl(iobase_bm + 0x20 + (cookie->unit * 16), 0x00100010);*/ outl(iobase_bm + 0x24 + (cookie->unit * 16), 0x00911030); /* * With the Cx5530, drive configuration should come *after* the * controller configuration, to make sure the controller sees * the command and does the right thing. */ /* Set UDMA mode 2 on drive */ if (bootverbose) printf("cyrix_5530_dmainit: setting ultra DMA mode 2\n"); r = wdcmd(WDDMA_UDMA2, wdinfo); if (!r) { printf("cyrix_5530_dmainit: setting DMA mode failed\n"); return 0; } if (bootverbose) cyrix_5530_status(cookie); return 1; } /* otherwise, try and program it for MW DMA mode 2 */ else if (mwdma_mode(wp) >= 2 && pio_mode(wp) >= 4) { /* Set multiword DMA mode 2 on drive */ if (bootverbose) printf("cyrix_5530_dmainit: setting multiword DMA mode 2\n"); r = wdcmd(WDDMA_MDMA2, wdinfo); if (!r) { printf("cyrix_5530_dmainit: setting DMA mode failed\n"); return 0; } /* Configure the controller appropriately for MWDMA mode 2 */ /*outl(iobase_bm + 0x20 + (cookie->unit * 16), 0x00100010);*/ outl(iobase_bm + 0x24 + (cookie->unit * 16), 0x00002020); if (bootverbose) cyrix_5530_status(cookie); return 1; } return 0; } static struct vendor_fns vs_cyrix_5530 = { cyrix_5530_dmainit, cyrix_5530_status }; static void promise_status(struct ide_pci_cookie *cookie) { pcici_t tag; int i; u_int32_t port0_command, port0_altstatus; u_int32_t port1_command, port1_altstatus; u_int32_t dma_block; u_int32_t lat_and_interrupt; u_int32_t drivetiming; int pa, pb, mb, mc; tag = cookie->tag; port0_command = pci_conf_read(tag, 0x10); port0_altstatus = pci_conf_read(tag, 0x14); port1_command = pci_conf_read(tag, 0x18); port1_altstatus = pci_conf_read(tag, 0x1c); dma_block = pci_conf_read(tag, 0x20); lat_and_interrupt = pci_conf_read(tag, 0x3c); printf("promise_status: port0: 0x%lx, port0_alt: 0x%lx, port1: 0x%lx, port1_alt: 0x%lx\n", (u_long)port0_command, (u_long)port0_altstatus, (u_long)port1_command, (u_long)port1_altstatus); printf( "promise_status: dma control blk address: 0x%lx, int: %d, irq: %d\n", (u_long)dma_block, (u_int)(lat_and_interrupt >> 8) & 0xff, (u_int)lat_and_interrupt & 0xff); for(i=0;i<4;i+=2) { drivetiming = pci_conf_read(tag, 0x60 + i * 4); printf("drivebits%d-%d: %b\n", i, i+1, drivetiming, "\020\05Prefetch\06Iordy\07Errdy\010Sync\025DmaW\026DmaR"); pa = drivetiming & 0xf; pb = (drivetiming >> 8) & 0x1f; mb = (drivetiming >> 13) & 0x7; mc = (drivetiming >> 16) & 0xf; printf("drivetiming%d: pa: 0x%x, pb: 0x%x, mb: 0x%x, mc: 0x%x\n", i, pa, pb, mb, mc); drivetiming = pci_conf_read(tag, 0x60 + (i + 1) * 4); pa = drivetiming & 0xf; pb = (drivetiming >> 8) & 0x1f; mb = (drivetiming >> 13) & 0x7; mc = (drivetiming >> 16) & 0xf; printf("drivetiming%d: pa: 0x%x, pb: 0x%x, mb: 0x%x, mc: 0x%x\n", i + 1, pa, pb, mb, mc); } } static struct vendor_fns vs_promise = { generic_dmainit, promise_status }; /* Intel PIIX, PIIX3, and PIIX4 IDE controller subfunctions */ static void intel_piix_dump_drive(char *ctlr, int sitre, int is_piix4, int word40, int word44, int word48, int word4a, int drive) { char *ms; if (!sitre) ms = "master/slave"; else if (drive == 0) ms = "master"; else ms = "slave"; printf("intel_piix_status: %s %s sample = %d, %s recovery = %d\n", ctlr, ms, 5 - ((sitre && drive) ? ((word44 >> 2) & 3) : ((word40 >> 12) & 3)), ms, 4 - ((sitre && drive) ? ((word44 >> 0) & 3) : ((word40 >> 8) & 3))); word40 >>= (drive * 4); printf("intel_piix_status: %s %s fastDMAonly %s, pre/post %s,\n\ intel_piix_status: IORDY sampling %s,\n\ intel_piix_status: fast PIO %s%s\n", ctlr, (drive == 0) ? "master" : "slave", (word40 & 8) ? "enabled" : "disabled", (word40 & 4) ? "enabled" : "disabled", (word40 & 2) ? "enabled" : "disabled", (word40 & 1) ? "enabled" : "disabled", ((word40 & 9) == 9) ? " (overridden by fastDMAonly)" : "" ); if (is_piix4) printf("intel_piix_status: UltraDMA %s, CT/RP = %d/%d\n", word48 ? "enabled": "disabled", 4 - (word4a & 3), 6 - (word4a & 3)); } static void intel_piix_status(struct ide_pci_cookie *cookie) { int iobase_wd; int unit; int iobase_bm; pcici_t tag; pcidi_t type; int ctlr; u_long word40, word44, word48; int sitre, is_piix4; iobase_wd = cookie->iobase_wd; unit = cookie->unit; iobase_bm = cookie->iobase_bm; tag = cookie->tag; type = cookie->type; ctlr = cookie->ctlr; word40 = pci_conf_read(tag, 0x40); word44 = pci_conf_read(tag, 0x44); word48 = pci_conf_read(tag, 0x48); /* * XXX will not be right for the *next* generation of upward-compatible * intel IDE controllers... */ is_piix4 = pci_conf_read(tag, PCI_CLASS_REG) == 0x71118086; sitre = word40 & 0x4000; switch (ctlr * 2 + unit) { case 0: intel_piix_dump_drive("primary", sitre, is_piix4, word40 & 0xffff, word44 & 0x0f, word48, word48 >> 16, 0); break; case 1: intel_piix_dump_drive("primary", sitre, is_piix4, word40 & 0xffff, word44 & 0x0f, word48 >> 1, word48 >> 20, 1); break; case 2: intel_piix_dump_drive("secondary", sitre, is_piix4, (word40 >> 16) & 0xffff, (word44 >> 4) & 0x0f, word48 >> 2, word48 >> 24, 0); break; case 3: intel_piix_dump_drive("secondary", sitre, is_piix4, (word40 >> 16) & 0xffff, (word44 >> 4) & 0x0f, word48 >> 3, word48 >> 28, 1); break; default: printf("intel_piix_status: bad drive or controller number\n"); } } /* * XXX timing values set hereare only good for 30/33MHz buses; should deal * with slower ones too (BTW: you overclock-- you lose) */ static int intel_piix_dmainit(struct ide_pci_cookie *cookie, struct wdparams *wp, int(*wdcmd)(int, void *), void *wdinfo) { int r; /* If it's a UDMA drive and a PIIX4, set it up */ if (cookie->type == 0x71118086 && udma_mode(wp) >= 2) { /* Set UDMA mode 2 on controller */ int unitno, mask, new; if (bootverbose) printf("intel_piix_dmainit: setting ultra DMA mode 2\n"); r = wdcmd(WDDMA_UDMA2, wdinfo); if (!r) { printf("intel_piix_dmainit: setting DMA mode failed\n"); return 0; } unitno = cookie->ctlr * 2 + cookie->unit; mask = (1 << unitno) + (3 << (16 + unitno * 4)); new = (1 << unitno) + (2 << (16 + unitno * 4)); pci_conf_write(cookie->tag, 0x48, (pci_conf_read(cookie->tag, 0x48) & ~mask) | new); if (bootverbose) intel_piix_status(cookie); return 1; } /* * if it's an 82371FB, which can't do independent programming of * drive timing, we punt; we're not going to fuss with trying to * coordinate timing modes between drives. if this is you, get a * new motherboard. or contribute patches :) * * we do now at least see if the modes set are OK to use. this should * satisfy the majority of people, with mwdma mode2 drives. */ else if (cookie->type == 0x12308086) { u_long word40; /* can drive do PIO 4 and MW DMA 2? */ if (!(mwdma_mode(wp) >= 2 && pio_mode(wp) >= 4)) return 0; word40 = pci_conf_read(cookie->tag, 0x40); word40 >>= cookie->ctlr * 16; /* Check for timing config usable for DMA on controller */ if (!((word40 & 0x3300) == 0x2300 && ((word40 >> (cookie->unit * 4)) & 1) == 1)) return 0; /* Set multiword DMA mode 2 on drive */ if (bootverbose) printf("intel_piix_dmainit: setting multiword DMA mode 2\n"); r = wdcmd(WDDMA_MDMA2, wdinfo); if (!r) { printf("intel_piix_dmainit: setting DMA mode failed\n"); return 0; } return 1; } /* otherwise, treat it as a PIIX3 and program it for MW DMA mode 2 */ else if (mwdma_mode(wp) >= 2 && pio_mode(wp) >= 4) { u_long mask40, mask44, new40, new44; /* * If SITRE is not set, set it and copy the * appropriate bits into the secondary registers. Do * both controllers at once. */ if (((pci_conf_read(cookie->tag, 0x40) >> (16 * cookie->ctlr)) & 0x4000) == 0) { unsigned int word40, word44; word40 = pci_conf_read(cookie->tag, 0x40); /* copy bits to secondary register */ word44 = pci_conf_read(cookie->tag, 0x44); /* * I've got a Biostar motherboard with Award * BIOS that sets SITRE and secondary timing * on one controller but not the other. * Bizarre. */ if ((word40 & 0x4000) == 0) { word44 &= ~0xf; word44 |= ((word40 & 0x3000) >> 10) | ((word40 & 0x0300) >> 8); } if ((word40 & 0x40000000) == 0) { word44 &= ~0xf0; word44 |= ((word40 & 0x30000000) >> 22) | ((word40 & 0x03000000) >> 20); } /* set SITRE */ word40 |= 0x40004000; pci_conf_write(cookie->tag, 0x40, word40); pci_conf_write(cookie->tag, 0x44, word44); } /* Set multiword DMA mode 2 on drive */ if (bootverbose) printf("intel_piix_dmainit: setting multiword DMA mode 2\n"); r = wdcmd(WDDMA_MDMA2, wdinfo); if (!r) { printf("intel_piix_dmainit: setting DMA mode failed\n"); return 0; } /* * backward compatible hardware leaves us with such * twisted masses of software (aka twiddle the * extremely weird register layout on a PIIX3, setting * PIO mode 4 and MWDMA mode 2) */ if (cookie->unit == 0) { mask40 = 0x330f; new40 = 0x2307; mask44 = 0; new44 = 0; } else { mask40 = 0x00f0; new40 = 0x0070; mask44 = 0x000f; new44 = 0x000b; } if (cookie->ctlr) { mask40 <<= 16; new40 <<= 16; mask44 <<= 4; new44 <<= 4; } pci_conf_write(cookie->tag, 0x40, (pci_conf_read(cookie->tag, 0x40) & ~mask40) | new40); pci_conf_write(cookie->tag, 0x44, (pci_conf_read(cookie->tag, 0x44) & ~mask44) | new44); if (bootverbose) intel_piix_status(cookie); return 1; } return 0; } static struct vendor_fns vs_intel_piix = { intel_piix_dmainit, intel_piix_status }; static void acer_status(struct ide_pci_cookie *cookie) { /* XXX does not do anything right now */ } static int acer_dmainit(struct ide_pci_cookie *cookie, struct wdparams *wp, int(*wdcmd)(int, void *), void *wdinfo) { /* Acer Aladdin DMA setup code. UDMA looks to be sinfully easy to set on this thing - just one register. */ u_long word54 = pci_conf_read(cookie->tag, 0x54); /* Set the default Acer FIFO settings (0x55 = 13-word depth and slave operation mode 1) */ word54 |= 0x5555; /* Is this drive UDMA? Set it up if so... */ if(udma_mode(wp) >= 2) { /* This is really easy to do. Just write 0xa (enable UDMA mode with 2T timing) into the word at the right places. */ word54 |= (0xA << (16 + (cookie->ctlr * 8) + (cookie->unit * 4))); /* Now set the drive for UDMA2. */ if(!wdcmd(WDDMA_UDMA2, wdinfo)) { printf("acer_dmainit: could not set UDMA2 mode on wdc%d:%d!\n", cookie->ctlr, cookie->unit); return 0; } /* Write the new config into the registers. I'm not sure if I'm doing this in the right order. */ pci_conf_write(cookie->tag, 0x54, word54); } else if(mwdma_mode(wp) >= 2 && pio_mode(wp) >=4) { /* Otherwise, we're already set for regular DMA. */ if(!wdcmd(WDDMA_MDMA2, wdinfo)) { printf("acer_dmainit: could not set MWDMA2 mode on wdc%d:%d!\n", cookie->ctlr, cookie->unit); return 0; } return 1; } return 0; } static struct vendor_fns vs_acer = { acer_dmainit, acer_status }; /* SiS 5591 */ static void sis_5591_status(struct ide_pci_cookie *cookie) { int iobase_wd; int ctlr, unit; int iobase_bm; pcici_t tag; pcidi_t type; u_int word40[5]; int i, unitno; int DRTC, DATC; int val; iobase_wd = cookie->iobase_wd; unit = cookie->unit; ctlr = cookie->ctlr; iobase_bm = cookie->iobase_bm; tag = cookie->tag; type = cookie->type; unitno = ctlr * 2 + unit; for (i=0; i<5; i++) { word40[i] = pci_conf_read(tag, i * 4 + 0x40); } DRTC = word40[ctlr] >> (16 * unit); DATC = word40[ctlr] >> (8 + 16*unit); if (unitno == 0) { if ((word40[4] & 0x80000) == 0) { val = word40[2] & 0xf; if (val == 0) val = 12; else if (val > 11) val++; printf ("SiS 5591 status: CRTC %d PCICLK, ", val); val = (word40[2] >> 8) & 0x7; if (val == 0) val = 8 ; else if (val > 6) val = 12; printf ("CATC %d PCICLK, applies to all IDE devices\n", val); } else { printf ("SiS 5591 status: CRTC and CATC timings are per device, taken from DRTC and DATC\n"); } printf ("SiS 5591 status: burst cycles %s, fast post write control %s\n", ((word40[2] >> 16) & 0x80) ? "enabled" : "disabled", ((word40[2] >> 16) & 0x20) ? "enabled" : "disabled"); } val = DRTC & 0xf; if (val == 0) val = 12; else if (val > 11) val++; printf ("SiS 5591 status: %s drive %d DRTC %d PCICLK,", unitno < 2 ? "primary" : "secondary", unitno & 1, val); val = DATC & 0x7; if (val == 0) val = 8 ; else if (val > 6) val = 12; printf (" DATC %d PCICLK\n", val); printf ("SiS 5591 status: %s drive %d Ultra DMA %s", unitno < 2 ? "primary" : "secondary", unitno & 1, (DATC & 0x80) ? "disabled\n" : "enabled"); if ((DATC & 0x80) == 0) printf (", %d PCICLK data out\n", ((DATC >> 5) & 0x3) + 1); printf ("SiS 5591 status: %s drive %d postwrite %s, prefetch %s prefetch count is %d\n", unitno < 2 ? "primary" : "secondary", unitno & 1, ((word40[2] >> (28 + unitno)) & 1) ? "enabled" : "disabled", ((word40[2] >> (24 + unitno)) & 1) ? "enabled" : "disabled", (word40[3] >> (16 * ctlr)) & 0xffff); printf ("SiS 5591 status: %s drive %d has%s been configured for DMA\n", unitno < 2 ? "primary" : "secondary", unitno & 1, (inb(iobase_bm + BMISTA_PORT) & ((unit == 0) ? BMISTA_DMA0CAP : BMISTA_DMA1CAP)) ? " " : " not"); } static int sis_5591_dmainit(struct ide_pci_cookie *cookie, struct wdparams *wp, int(*wdcmd)(int, void *), void *wdinfo) { int r; unsigned int workword, new, mask; int ctlr, unit; int iobase_bm; pcici_t tag; int unitno; unit = cookie->unit; ctlr = cookie->ctlr; iobase_bm = cookie->iobase_bm; tag = cookie->tag; unitno = ctlr * 2 + unit; if (udma_mode(wp) >= 2) { workword = pci_conf_read(tag, ctlr * 4 + 0x40); /* These settings are a little arbitrary. They're taken from my * system, where the BIOS has already set the values, but where * we don't detect that we're initialized because the * BMISTA_DMA?CAP values aren't set by the BIOS. * 0x8000 turns on UDMA * 0x2000 sets UDMA cycle time to 2 PCI clocks for data out * 0x0300 sets DATC to 3 PCI clocks * 0x0001 sets DRTC to 1 PCI clock */ if (unit) { mask = 0x0000ffff; new = 0xa3010000; } else { mask = 0xffff0000; new = 0x0000a301; } workword &= mask; workword |= new; pci_conf_write(tag, ctlr * 4 + 0x40, workword); outb(iobase_bm + BMISTA_PORT, (inb(iobase_bm + BMISTA_PORT) | ((unit == 0) ? BMISTA_DMA0CAP : BMISTA_DMA1CAP))); if (bootverbose) printf("SiS 5591 dmainit: %s drive %d setting ultra DMA mode 2\n", unitno < 2 ? "primary" : "secondary", unitno & 1); r = wdcmd(WDDMA_UDMA2, wdinfo); if (!r) { printf("SiS 5591 dmainit: %s drive %d setting DMA mode failed\n", unitno < 2 ? "primary" : "secondary", unitno & 1); return 0; } if (bootverbose) sis_5591_status(cookie); return 1; } /* otherwise, try and program it for MW DMA mode 2 */ else if (mwdma_mode(wp) >= 2 && pio_mode(wp) >= 4) { workword = pci_conf_read(tag, ctlr * 4 + 0x40); /* These settings are a little arbitrary. They're taken from my * system, where the BIOS has already set the values, but where * we don't detect that we're initialized because the * BMISTA_DMA?CAP values aren't set by the BIOS. * 0x0300 sets DATC to 3 PCI clocks * 0x0001 sets DRTC to 1 PCI clock */ if (unit) { mask = 0x0000ffff; new = 0x03010000; } else { mask = 0xffff0000; new = 0x00000301; } workword &= mask; workword |= new; pci_conf_write(tag, ctlr * 4 + 0x40, workword); outb(iobase_bm + BMISTA_PORT, (inb(iobase_bm + BMISTA_PORT) | ((unit == 0) ? BMISTA_DMA0CAP : BMISTA_DMA1CAP))); /* Set multiword DMA mode 2 on drive */ if (bootverbose) printf("SiS 5591 dmainit: %s drive %d setting multiword DMA mode 2\n", unitno < 2 ? "primary" : "secondary", unitno & 1); r = wdcmd(WDDMA_MDMA2, wdinfo); if (!r) { printf("SiS 5591 dmainit: %s drive %d setting DMA mode failed\n", unitno < 2 ? "primary" : "secondary", unitno & 1); return 0; } if (bootverbose) sis_5591_status(cookie); return 1; } return 0; } static struct vendor_fns vs_sis_5591 = { sis_5591_dmainit, sis_5591_status }; /* Generic SFF-8038i code-- all code below here, except for PCI probes, * more or less conforms to the SFF-8038i spec as extended for PCI. * There should be no code that goes beyond that feature set below. */ /* XXX mkcookie is overloaded with too many parameters */ static struct ide_pci_cookie * mkcookie(int iobase_wd, int ctlr, int unit, int iobase_bm, pcici_t tag, pcidi_t type, struct vendor_fns *vp, int altiobase_wd) { struct ide_pci_cookie *cp; cp = malloc(sizeof *cp, M_DEVBUF, M_NOWAIT); if (!cp) return 0; cp->iobase_wd = iobase_wd; cp->ctlr = ctlr; cp->unit = unit; cp->tag = tag; cp->type = type; cp->iobase_bm = iobase_bm; cp->altiobase_wd = altiobase_wd; bcopy(vp, &cp->vs, sizeof(struct vendor_fns)); if (!prdbuf) { prdbuf = malloc(PRD_ALLOC_SIZE, M_DEVBUF, M_NOWAIT); if (!prdbuf) { FREE(cp, M_DEVBUF); return 0; } if (((int)prdbuf >> PAGE_SHIFT) ^ (((int)prdbuf + PRD_ALLOC_SIZE - 1) >> PAGE_SHIFT)) { printf("ide_pci: prdbuf straddles page boundary, no DMA\n"); FREE(cp, M_DEVBUF); FREE(prdbuf, M_DEVBUF); return 0; } prdbuf_next = prdbuf; } if (((char *)prdbuf_next + PRD_BUF_SIZE) > ((char *)prdbuf + PRD_ALLOC_SIZE)) { printf("ide_pci: mkcookie %04x:%d: no more space for PRDs, no DMA\n", iobase_wd, unit); FREE(cp, M_DEVBUF); return 0; } cp->prd = prdbuf_next; (char *)prdbuf_next += PRD_BUF_SIZE; LIST_INSERT_HEAD(&softc.cookies, cp, le); return cp; } static const char * ide_pci_probe(pcici_t tag, pcidi_t type) { u_long data; data = pci_conf_read(tag, PCI_CLASS_REG); if ((data & PCI_CLASS_MASK) == PCI_CLASS_MASS_STORAGE && ((data & PCI_SUBCLASS_MASK) == 0x00010000 || ((data & PCI_SUBCLASS_MASK) == 0x00040000))) { if (type == 0x71118086) return ("Intel PIIX4 Bus-master IDE controller"); if (type == 0x70108086) return ("Intel PIIX3 Bus-master IDE controller"); if (type == 0x12308086) return ("Intel PIIX Bus-master IDE controller"); if (type == PROMISE_ULTRA33) return ("Promise Ultra/33 IDE controller"); if (type == 0x05711106) return ("VIA 82C586x (Apollo) Bus-master IDE controller"); if (type == 0x01021078) return ("Cyrix 5530 Bus-master IDE controller"); if (type == 0x522910b9) return ("Acer Aladdin IV/V (M5229) Bus-master IDE controller"); if (type == 0x55131039) return ("SiS 5591 Bus-master IDE Controller"); if (type == CMD640B_PCI_ID) return "CMD 640B IDE controller"; if (data & 0x8000) return ("PCI IDE controller (busmaster capable)"); else return ("PCI IDE controller (not busmaster capable)"); }; return ((char*)0); } static void ide_pci_attach(pcici_t tag, int unit) { u_long class = 0, cmd; int bmista_1, bmista_2; int iobase_wd_1, iobase_wd_2, iobase_bm_1, iobase_bm_2; int altiobase_wd_1, altiobase_wd_2; struct vendor_fns *vp; pcidi_t type; struct ide_pci_cookie *cookie; int ctlridx; ctlridx = unit * 2; /* set up vendor-specific stuff */ type = pci_conf_read(tag, PCI_ID_REG); if (type != PROMISE_ULTRA33) { /* is it busmaster capable? bail if not */ class = pci_conf_read(tag, PCI_CLASS_REG); if (!(class & 0x8000)) { return; } /* is it enabled and is busmastering turned on? */ cmd = pci_conf_read(tag, PCI_COMMAND_STATUS_REG); if ((cmd & 5) != 5) { return; } } switch (type) { case 0x71118086: case 0x70108086: case 0x12308086: /* Intel PIIX, PIIX3, PIIX4 */ vp = &vs_intel_piix; break; case 0x5711106: /* VIA Apollo chipset family */ vp = &vs_via_571; break; case PROMISE_ULTRA33: /* Promise controllers */ vp = &vs_promise; break; case 0x01021078: /* cyrix 5530 */ printf("cyrix 5530\n"); vp = &vs_cyrix_5530; break; case 0x522910B9: /* Acer Aladdin IV/V (M5229) */ vp = &vs_acer; break; case 0x55131039: /* SiS 5591 */ vp = &vs_sis_5591; break; case CMD640B_PCI_ID: /* CMD 640B IDE */ wdc_pci(Q_CMD640B); /* I'm curious to know if we can disable this and remove the return */ #if 1 return; #endif vp = &vs_generic; break; default: /* everybody else */ vp = &vs_generic; break; } if (type != PROMISE_ULTRA33) { if ((class & 0x100) == 0) { iobase_wd_1 = IO_WD1; altiobase_wd_1 = iobase_wd_1 + wd_altsts; } else { iobase_wd_1 = pci_conf_read(tag, 0x10) & 0xfffc; altiobase_wd_1 = pci_conf_read(tag, 0x14) & 0xfffc; } if ((class & 0x400) == 0) { iobase_wd_2 = IO_WD2; altiobase_wd_2 = iobase_wd_2 + wd_altsts; } else { iobase_wd_2 = pci_conf_read(tag, 0x18) & 0xfffc; altiobase_wd_2 = pci_conf_read(tag, 0x1c) & 0xfffc; } } else { iobase_wd_1 = pci_conf_read(tag, 0x10) & 0xfffc; altiobase_wd_1 = pci_conf_read(tag, 0x14) & 0xfffc; iobase_wd_2 = pci_conf_read(tag, 0x18) & 0xfffc; altiobase_wd_2 = pci_conf_read(tag, 0x1c) & 0xfffc; } iobase_bm_1 = pci_conf_read(tag, 0x20) & 0xfffc; if (iobase_bm_1 == 0) { printf("ide_pci: BIOS has not configured busmaster" "I/O address,\n ide_pci: giving up\n"); return; } iobase_bm_2 = iobase_bm_1 + SFF8038_CTLR_1; wddma[unit].wdd_candma = ide_pci_candma; wddma[unit].wdd_dmainit = ide_pci_dmainit; if (type == 0x01021078 /*CYRIX_5530*/) wddma[unit].wdd_dmaverify = cyrix_5530_dmaverify; else wddma[unit].wdd_dmaverify = ide_pci_dmaverify; wddma[unit].wdd_dmaprep = ide_pci_dmasetup; wddma[unit].wdd_dmastart = ide_pci_dmastart; wddma[unit].wdd_dmadone = ide_pci_dmadone; wddma[unit].wdd_dmastatus = ide_pci_status; wddma[unit].wdd_iobase = ide_pci_iobase; wddma[unit].wdd_altiobase = ide_pci_altiobase; +#if 0 /* * This code below is mighty bogus. The config entries for the * isa_devtab_bio are plugged in before the standard ISA bios scan. * This is our "hack" way to simulate a dynamic assignment of I/O * addresses, from a PCI device to an ISA probe. Sorry :-). */ if (iobase_wd_1 != IO_WD1) { struct isa_device *dvp, *dvp1, *dvup; for( dvp = isa_devtab_bio; dvp->id_id != 0; dvp++) { if ((dvp->id_driver == &wdcdriver) && (dvp->id_iobase == 0)) { int biotabunit; biotabunit = dvp->id_unit * 2; dvp->id_iobase = iobase_wd_1; dvp1 = dvp + 1; dvp1->id_iobase = iobase_wd_2; printf("ide_pci%d: adding drives to controller %d:", unit, biotabunit); for(dvup = isa_biotab_wdc; dvup->id_id != 0; dvup++) { if (dvup->id_driver != &wdcdriver) continue; if (dvup->id_unit != biotabunit) continue; dvup->id_iobase = dvp->id_iobase; printf(" %d", dvup->id_unit); dvup++; pci_map_int(tag, wdintr, (void *) dvp->id_unit, &bio_imask); if (dvup->id_id == 0) break; if (dvup->id_unit == biotabunit + 1) { dvup->id_iobase = dvp->id_iobase; printf(" %d", dvup->id_unit); dvup++; if (dvup->id_id == 0) { iobase_wd_2 = 0; break; } } if (dvup->id_unit == biotabunit + 2) { pci_map_int(tag, wdintr, (void *) ((int) dvp->id_unit + 1), &bio_imask); dvup->id_iobase = dvp1->id_iobase; printf(" %d", dvup->id_unit); dvup++; if (dvup->id_id == 0) { break; } } if (dvup->id_unit == biotabunit + 3) { pci_map_int(tag, wdintr, (void *) ((int) dvp->id_unit + 1), &bio_imask); dvup->id_iobase = dvp1->id_iobase; printf(" %d", dvup->id_unit); } break; } printf("\n"); break; } } } - +#endif bmista_1 = inb(iobase_bm_1 + BMISTA_PORT); bmista_2 = inb(iobase_bm_2 + BMISTA_PORT); if (!ide_pci_softc_cookies_initted) { LIST_INIT(&softc.cookies); ide_pci_softc_cookies_initted = 1; } if (iobase_wd_1 != 0) { cookie = mkcookie(iobase_wd_1, ctlridx, 0, iobase_bm_1, tag, type, vp, altiobase_wd_1); if (bootverbose) vp->vendor_status(cookie); cookie = mkcookie(iobase_wd_1, ctlridx, 1, iobase_bm_1, tag, type, vp, altiobase_wd_1); if (bootverbose) { vp->vendor_status(cookie); bmista_1 = inb(iobase_bm_1 + BMISTA_PORT); bmista_2 = inb(iobase_bm_2 + BMISTA_PORT); printf("ide_pci: busmaster 0 status: %02x from port: %08x\n", bmista_1, iobase_bm_1+BMISTA_PORT); if (bmista_1 & BMISTA_DMA0CAP) printf("ide_pci: ide0:0 has been configured for DMA by BIOS\n"); if (bmista_1 & BMISTA_DMA1CAP) printf("ide_pci: ide0:1 has been configured for DMA by BIOS\n"); } } if (iobase_wd_2 != 0) { cookie = mkcookie(iobase_wd_2, ctlridx + 1, 0, iobase_bm_2, tag, type, vp, altiobase_wd_2); if (bootverbose) vp->vendor_status(cookie); cookie = mkcookie(iobase_wd_2, ctlridx + 1, 1, iobase_bm_2, tag, type, vp, altiobase_wd_2); if (bootverbose) { vp->vendor_status(cookie); bmista_1 = inb(iobase_bm_1 + BMISTA_PORT); bmista_2 = inb(iobase_bm_2 + BMISTA_PORT); printf("ide_pci: busmaster 1 status: %02x from port: %08x\n", bmista_2, iobase_bm_2+BMISTA_PORT); if (bmista_2 & BMISTA_DMA0CAP) printf("ide_pci: ide1:0 has been configured for DMA by BIOS\n"); if (bmista_2 & BMISTA_DMA1CAP) printf("ide_pci: ide1:1 has been configured for DMA by BIOS\n"); } } } static u_long ide_pci_count; static struct pci_device ide_pci_device = { "ide_pci", ide_pci_probe, ide_pci_attach, &ide_pci_count, 0 }; DATA_SET(pcidevice_set, ide_pci_device); /* * Return a cookie if we may be able to do DMA on the specified * (iobase_wd, ctlr, unit). */ static void * ide_pci_candma(int iobase_wd, int ctlr, int unit) { struct ide_pci_cookie *cp; cp = softc.cookies.lh_first; while(cp) { if (cp->ctlr == ctlr && cp->unit == unit && ((iobase_wd == 0) || (cp->iobase_wd == iobase_wd))) break; cp = cp->le.le_next; } return cp; } /* * Initialize controller and drive for DMA operation, including timing modes. * Uses data passed from the wd driver and a callback function to initialize * timing modes on the drive. */ static int ide_pci_dmainit(void *cookie, struct wdparams *wp, int(*wdcmd)(int, void *), void *wdinfo) { struct ide_pci_cookie *cp = cookie; /* * If the controller status indicates that DMA is configured already, * we flounce happily away */ if (inb(cp->iobase_bm + BMISTA_PORT) & ((cp->unit == 0) ? BMISTA_DMA0CAP : BMISTA_DMA1CAP)) return 1; /* We take a stab at it with device-dependent code */ return(cp->vs.vendor_dmainit(cp, wp, wdcmd, wdinfo)); } /* * Verify that controller can handle a dma request for cp. Should * not affect any hardware or driver state. */ static int ide_pci_dmaverify(void *xcp, char *vaddr, u_long count, int dir) { int badfu; /* * check for nonaligned or odd-length Stuff */ badfu = ((unsigned int)vaddr & 1) || (count & 1); #ifdef DIAGNOSTIC if (badfu) { printf("ide_pci: dmaverify odd vaddr or length, "); printf("vaddr = %p length = %08lx\n", (void *)vaddr, count); } #endif return (!badfu); } /* * Set up DMA for cp. It is the responsibility of the caller * to ensure that the controller is idle before this routine * is called. */ static int ide_pci_dmasetup(void *xcp, char *vaddr, u_long vcount, int dir) { struct ide_pci_cookie *cp = xcp; struct ide_pci_prd *prd; int i; u_long firstpage; u_long prd_base, prd_count; u_long nbase, ncount, nend; int iobase_bm; u_long count; #ifdef DIAGNOSTIC u_long checkcount; #endif prd = cp->prd; count = vcount; i = 0; iobase_bm = cp->iobase_bm; if (count == 0) { printf("ide_pci: dmasetup 0-length transfer, "); printf("vaddr = %p length = %08lx\n", (void *)vaddr, count); return 1; } /* Generate first PRD entry, which may be non-aligned. */ firstpage = PAGE_SIZE - ((uintptr_t)vaddr & PAGE_MASK); prd_base = vtophys(vaddr); prd_count = MIN(count, firstpage); vaddr += prd_count; count -= prd_count; /* Step through virtual pages, coalescing as needed. */ while (count) { nbase = vtophys(vaddr); ncount = MIN(count, PAGE_SIZE); nend = nbase + ncount; /* * Coalesce if physically contiguous and not crossing * 64k boundary. */ #if 0 /* * Aggregation is NOT an optimisation worth doing, * and the Cyrix UDMA controller screws itself * in some aggregated situations. * We might as well just assign each 4K page a DMA entry * as this doesn't really gain us anything to aggregate them. * This was basically copied from my agregation code in the aha * driver, but I doubt it helped much there either. [JRE] */ if ((prd_base + prd_count == nbase) && ((((nend - 1) ^ prd_base) & ~0xffff) == 0)) { prd_count += ncount; } else #endif { prd[i].prd_base = prd_base; prd[i].prd_count = (prd_count & 0xffff); i++; if (i >= PRD_MAX_SEGS) { printf("wd82371: too many segments in PRD table\n"); return 1; } prd_base = nbase; prd_count = ncount; } vaddr += ncount; count -= ncount; } /* Write last PRD entry. */ prd[i].prd_base = prd_base; prd[i].prd_count = (prd_count & 0xffff) | PRD_EOT_BIT; #ifdef DIAGNOSTIC /* sanity check the transfer for length and page-alignment, at least */ checkcount = 0; for (i = 0;; i++) { unsigned int modcount; modcount = prd[i].prd_count & 0xffffe; if (modcount == 0) modcount = 0x10000; checkcount += modcount; if (i != 0 && ((prd[i].prd_base & PAGE_MASK) != 0)) { printf("ide_pci: dmasetup() diagnostic fails-- unaligned page\n"); return 1; } if (prd[i].prd_count & PRD_EOT_BIT) break; } if (checkcount != vcount) { printf("ide_pci: dmasetup() diagnostic fails-- bad length\n"); return 1; } #endif /* Set up PRD base register */ outl(iobase_bm + BMIDTP_PORT, vtophys(prd)); /* Set direction of transfer */ outb(iobase_bm + BMICOM_PORT, (dir == B_READ) ? BMICOM_READ_WRITE : 0); /* Clear interrupt and error bits */ outb(iobase_bm + BMISTA_PORT, (inb(iobase_bm + BMISTA_PORT) | (BMISTA_INTERRUPT | BMISTA_DMA_ERROR))); return 0; } static void ide_pci_dmastart(void *xcp) { struct ide_pci_cookie *cp = xcp; int iobase_bm; iobase_bm = cp->iobase_bm; outb(iobase_bm + BMICOM_PORT, inb(iobase_bm + BMICOM_PORT) | BMICOM_STOP_START); } static int ide_pci_dmadone(void *xcp) { struct ide_pci_cookie *cp = xcp; int iobase_bm, status; status = ide_pci_status(xcp); iobase_bm = cp->iobase_bm; outb(iobase_bm + BMICOM_PORT, inb(iobase_bm + BMICOM_PORT) & ~BMICOM_STOP_START); return status; } static int ide_pci_status(void *xcp) { int iobase_bm, status, bmista; status = 0; iobase_bm = ((struct ide_pci_cookie *)xcp)->iobase_bm; bmista = inb(iobase_bm + BMISTA_PORT); if (bmista & BMISTA_INTERRUPT) status |= WDDS_INTERRUPT; if (bmista & BMISTA_DMA_ERROR) status |= WDDS_ERROR; if (bmista & BMISTA_DMA_ACTIVE) status |= WDDS_ACTIVE; return status; } static int ide_pci_altiobase(void *xcp) { struct ide_pci_cookie *cp = xcp; if (cp == 0) { return 0; } else { return cp->altiobase_wd; } } static int ide_pci_iobase(void *xcp) { struct ide_pci_cookie *cp = xcp; if (cp == 0) { return 0; } else { return cp->iobase_wd; } } #endif #endif /* NPCI > 0 */ Index: head/sys/pci/if_fxp.c =================================================================== --- head/sys/pci/if_fxp.c (revision 45719) +++ head/sys/pci/if_fxp.c (revision 45720) @@ -1,1925 +1,1995 @@ /* * Copyright (c) 1995, David Greenman * All rights reserved. * * Modifications to support NetBSD and media selection: * Copyright (c) 1997 Jason R. Thorpe. 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 unmodified, 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. * - * $Id: if_fxp.c,v 1.65 1999/03/17 16:44:53 luigi Exp $ + * $Id: if_fxp.c,v 1.66 1999/03/20 04:51:25 wes Exp $ */ /* * Intel EtherExpress Pro/100B PCI Fast Ethernet driver */ #include "bpfilter.h" #include #include #include #include #include #include #include #include #include #ifdef NS #include #include #endif #if NBPFILTER > 0 #include #endif #if defined(__NetBSD__) #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __alpha__ /* XXX */ /* XXX XXX NEED REAL DMA MAPPING SUPPORT XXX XXX */ #undef vtophys #define vtophys(va) alpha_XXX_dmamap((vm_offset_t)(va)) #endif /* __alpha__ */ #else /* __FreeBSD__ */ #include +#include +#include +#include +#include #include #include #include /* for vtophys */ #include /* for vtophys */ #include /* for DELAY */ #include #include /* for PCIM_CMD_xxx */ #include #include #endif /* __NetBSD__ */ #include "opt_bdg.h" #ifdef BRIDGE #include #include #endif /* * NOTE! On the Alpha, we have an alignment constraint. The * card DMAs the packet immediately following the RFA. However, * the first thing in the packet is a 14-byte Ethernet header. * This means that the packet is misaligned. To compensate, * we actually offset the RFA 2 bytes into the cluster. This * alignes the packet after the Ethernet header at a 32-bit * boundary. HOWEVER! This means that the RFA is misaligned! */ #define RFA_ALIGNMENT_FUDGE 2 /* * Inline function to copy a 16-bit aligned 32-bit quantity. */ static __inline void fxp_lwcopy __P((volatile u_int32_t *, volatile u_int32_t *)); static __inline void fxp_lwcopy(src, dst) volatile u_int32_t *src, *dst; { volatile u_int16_t *a = (volatile u_int16_t *)src; volatile u_int16_t *b = (volatile u_int16_t *)dst; b[0] = a[0]; b[1] = a[1]; } /* * Template for default configuration parameters. * See struct fxp_cb_config for the bit definitions. */ static u_char fxp_cb_config_template[] = { 0x0, 0x0, /* cb_status */ 0x80, 0x2, /* cb_command */ 0xff, 0xff, 0xff, 0xff, /* link_addr */ 0x16, /* 0 */ 0x8, /* 1 */ 0x0, /* 2 */ 0x0, /* 3 */ 0x0, /* 4 */ 0x80, /* 5 */ 0xb2, /* 6 */ 0x3, /* 7 */ 0x1, /* 8 */ 0x0, /* 9 */ 0x26, /* 10 */ 0x0, /* 11 */ 0x60, /* 12 */ 0x0, /* 13 */ 0xf2, /* 14 */ 0x48, /* 15 */ 0x0, /* 16 */ 0x40, /* 17 */ 0xf3, /* 18 */ 0x0, /* 19 */ 0x3f, /* 20 */ 0x5 /* 21 */ }; /* Supported media types. */ struct fxp_supported_media { const int fsm_phy; /* PHY type */ const int *fsm_media; /* the media array */ const int fsm_nmedia; /* the number of supported media */ const int fsm_defmedia; /* default media for this PHY */ }; static const int fxp_media_standard[] = { IFM_ETHER|IFM_10_T, IFM_ETHER|IFM_10_T|IFM_FDX, IFM_ETHER|IFM_100_TX, IFM_ETHER|IFM_100_TX|IFM_FDX, IFM_ETHER|IFM_AUTO, }; #define FXP_MEDIA_STANDARD_DEFMEDIA (IFM_ETHER|IFM_AUTO) static const int fxp_media_default[] = { IFM_ETHER|IFM_MANUAL, /* XXX IFM_AUTO ? */ }; #define FXP_MEDIA_DEFAULT_DEFMEDIA (IFM_ETHER|IFM_MANUAL) static const struct fxp_supported_media fxp_media[] = { { FXP_PHY_DP83840, fxp_media_standard, sizeof(fxp_media_standard) / sizeof(fxp_media_standard[0]), FXP_MEDIA_STANDARD_DEFMEDIA }, { FXP_PHY_DP83840A, fxp_media_standard, sizeof(fxp_media_standard) / sizeof(fxp_media_standard[0]), FXP_MEDIA_STANDARD_DEFMEDIA }, { FXP_PHY_82553A, fxp_media_standard, sizeof(fxp_media_standard) / sizeof(fxp_media_standard[0]), FXP_MEDIA_STANDARD_DEFMEDIA }, { FXP_PHY_82553C, fxp_media_standard, sizeof(fxp_media_standard) / sizeof(fxp_media_standard[0]), FXP_MEDIA_STANDARD_DEFMEDIA }, { FXP_PHY_82555, fxp_media_standard, sizeof(fxp_media_standard) / sizeof(fxp_media_standard[0]), FXP_MEDIA_STANDARD_DEFMEDIA }, { FXP_PHY_82555B, fxp_media_standard, sizeof(fxp_media_standard) / sizeof(fxp_media_standard[0]), FXP_MEDIA_STANDARD_DEFMEDIA }, { FXP_PHY_80C24, fxp_media_default, sizeof(fxp_media_default) / sizeof(fxp_media_default[0]), FXP_MEDIA_DEFAULT_DEFMEDIA }, }; #define NFXPMEDIA (sizeof(fxp_media) / sizeof(fxp_media[0])) static int fxp_mediachange __P((struct ifnet *)); static void fxp_mediastatus __P((struct ifnet *, struct ifmediareq *)); static void fxp_set_media __P((struct fxp_softc *, int)); static __inline void fxp_scb_wait __P((struct fxp_softc *)); static FXP_INTR_TYPE fxp_intr __P((void *)); static void fxp_start __P((struct ifnet *)); static int fxp_ioctl __P((struct ifnet *, FXP_IOCTLCMD_TYPE, caddr_t)); static void fxp_init __P((void *)); static void fxp_stop __P((struct fxp_softc *)); static void fxp_watchdog __P((struct ifnet *)); static int fxp_add_rfabuf __P((struct fxp_softc *, struct mbuf *)); static int fxp_mdi_read __P((struct fxp_softc *, int, int)); static void fxp_mdi_write __P((struct fxp_softc *, int, int, int)); static void fxp_read_eeprom __P((struct fxp_softc *, u_int16_t *, int, int)); static int fxp_attach_common __P((struct fxp_softc *, u_int8_t *)); static void fxp_stats_update __P((void *)); static void fxp_mc_setup __P((struct fxp_softc *)); /* * Set initial transmit threshold at 64 (512 bytes). This is * increased by 64 (512 bytes) at a time, to maximum of 192 * (1536 bytes), if an underrun occurs. */ static int tx_threshold = 64; /* * Number of transmit control blocks. This determines the number * of transmit buffers that can be chained in the CB list. * This must be a power of two. */ #define FXP_NTXCB 128 /* * Number of completed TX commands at which point an interrupt * will be generated to garbage collect the attached buffers. * Must be at least one less than FXP_NTXCB, and should be * enough less so that the transmitter doesn't becomes idle * during the buffer rundown (which would reduce performance). */ #define FXP_CXINT_THRESH 120 /* * TxCB list index mask. This is used to do list wrap-around. */ #define FXP_TXCB_MASK (FXP_NTXCB - 1) /* * Number of receive frame area buffers. These are large so chose * wisely. */ #define FXP_NRFABUFS 64 /* * Maximum number of seconds that the receiver can be idle before we * assume it's dead and attempt to reset it by reprogramming the * multicast filter. This is part of a work-around for a bug in the * NIC. See fxp_stats_update(). */ #define FXP_MAX_RX_IDLE 15 /* * Wait for the previous command to be accepted (but not necessarily * completed). */ static __inline void fxp_scb_wait(sc) struct fxp_softc *sc; { int i = 10000; while (CSR_READ_1(sc, FXP_CSR_SCB_COMMAND) && --i); } /************************************************************* * Operating system-specific autoconfiguration glue *************************************************************/ #if defined(__NetBSD__) #ifdef __BROKEN_INDIRECT_CONFIG static int fxp_match __P((struct device *, void *, void *)); #else static int fxp_match __P((struct device *, struct cfdata *, void *)); #endif static void fxp_attach __P((struct device *, struct device *, void *)); static void fxp_shutdown __P((void *)); /* Compensate for lack of a generic ether_ioctl() */ static int fxp_ether_ioctl __P((struct ifnet *, FXP_IOCTLCMD_TYPE, caddr_t)); #define ether_ioctl fxp_ether_ioctl struct cfattach fxp_ca = { sizeof(struct fxp_softc), fxp_match, fxp_attach }; struct cfdriver fxp_cd = { NULL, "fxp", DV_IFNET }; /* * Check if a device is an 82557. */ static int fxp_match(parent, match, aux) struct device *parent; #ifdef __BROKEN_INDIRECT_CONFIG void *match; #else struct cfdata *match; #endif void *aux; { struct pci_attach_args *pa = aux; if (PCI_VENDOR(pa->pa_id) != PCI_VENDOR_INTEL) return (0); switch (PCI_PRODUCT(pa->pa_id)) { case PCI_PRODUCT_INTEL_82557: return (1); } return (0); } static void fxp_attach(parent, self, aux) struct device *parent, *self; void *aux; { struct fxp_softc *sc = (struct fxp_softc *)self; struct pci_attach_args *pa = aux; pci_chipset_tag_t pc = pa->pa_pc; pci_intr_handle_t ih; const char *intrstr = NULL; u_int8_t enaddr[6]; struct ifnet *ifp; /* * Map control/status registers. */ if (pci_mapreg_map(pa, FXP_PCI_MMBA, PCI_MAPREG_TYPE_MEM, 0, &sc->sc_st, &sc->sc_sh, NULL, NULL)) { printf(": can't map registers\n"); return; } printf(": Intel EtherExpress Pro 10/100B Ethernet\n"); /* * Allocate our interrupt. */ if (pci_intr_map(pc, pa->pa_intrtag, pa->pa_intrpin, pa->pa_intrline, &ih)) { printf("%s: couldn't map interrupt\n", sc->sc_dev.dv_xname); return; } intrstr = pci_intr_string(pc, ih); sc->sc_ih = pci_intr_establish(pc, ih, IPL_NET, fxp_intr, sc); if (sc->sc_ih == NULL) { printf("%s: couldn't establish interrupt", sc->sc_dev.dv_xname); if (intrstr != NULL) printf(" at %s", intrstr); printf("\n"); return; } printf("%s: interrupting at %s\n", sc->sc_dev.dv_xname, intrstr); /* Do generic parts of attach. */ if (fxp_attach_common(sc, enaddr)) { /* Failed! */ return; } printf("%s: Ethernet address %s%s\n", sc->sc_dev.dv_xname, ether_sprintf(enaddr), sc->phy_10Mbps_only ? ", 10Mbps" : ""); ifp = &sc->sc_ethercom.ec_if; bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ); ifp->if_softc = sc; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = fxp_ioctl; ifp->if_start = fxp_start; ifp->if_watchdog = fxp_watchdog; /* * Attach the interface. */ if_attach(ifp); /* * Let the system queue as many packets as we have available * TX descriptors. */ ifp->if_snd.ifq_maxlen = FXP_NTXCB - 1; ether_ifattach(ifp, enaddr); #if NBPFILTER > 0 bpfattach(&sc->sc_ethercom.ec_if.if_bpf, ifp, DLT_EN10MB, sizeof(struct ether_header)); #endif /* * Add shutdown hook so that DMA is disabled prior to reboot. Not * doing do could allow DMA to corrupt kernel memory during the * reboot before the driver initializes. */ shutdownhook_establish(fxp_shutdown, sc); } /* * Device shutdown routine. Called at system shutdown after sync. The * main purpose of this routine is to shut off receiver DMA so that * kernel memory doesn't get clobbered during warmboot. */ static void fxp_shutdown(sc) void *sc; { fxp_stop((struct fxp_softc *) sc); } static int fxp_ether_ioctl(ifp, cmd, data) struct ifnet *ifp; FXP_IOCTLCMD_TYPE cmd; caddr_t data; { struct ifaddr *ifa = (struct ifaddr *) data; struct fxp_softc *sc = ifp->if_softc; switch (cmd) { case SIOCSIFADDR: ifp->if_flags |= IFF_UP; switch (ifa->ifa_addr->sa_family) { #ifdef INET case AF_INET: fxp_init(sc); arp_ifinit(ifp, ifa); break; #endif #ifdef NS case AF_NS: { register struct ns_addr *ina = &IA_SNS(ifa)->sns_addr; if (ns_nullhost(*ina)) ina->x_host = *(union ns_host *) LLADDR(ifp->if_sadl); else bcopy(ina->x_host.c_host, LLADDR(ifp->if_sadl), ifp->if_addrlen); /* Set new address. */ fxp_init(sc); break; } #endif default: fxp_init(sc); break; } break; default: return (EINVAL); } return (0); } #else /* __FreeBSD__ */ -static u_long fxp_count; -static const char *fxp_probe __P((pcici_t, pcidi_t)); -static void fxp_attach __P((pcici_t, int)); - -static void fxp_shutdown __P((int, void *)); - -static struct pci_device fxp_device = { - "fxp", - fxp_probe, - fxp_attach, - &fxp_count, - NULL -}; -DATA_SET(pcidevice_set, fxp_device); - /* * Return identification string if this is device is ours. */ -static const char * -fxp_probe(config_id, device_id) - pcici_t config_id; - pcidi_t device_id; +static int +fxp_probe(device_t dev) { - if (((device_id & 0xffff) == FXP_VENDORID_INTEL) && - ((device_id >> 16) & 0xffff) == FXP_DEVICEID_i82557) - return ("Intel EtherExpress Pro 10/100B Ethernet"); + if ((pci_get_vendor(dev) == FXP_VENDORID_INTEL) && + (pci_get_device(dev) == FXP_DEVICEID_i82557)) { + device_set_desc(dev, "Intel EtherExpress Pro 10/100B Ethernet"); + return 0; + } - return NULL; + return ENXIO; } -static void -fxp_attach(config_id, unit) - pcici_t config_id; - int unit; +static int +fxp_attach(device_t dev) { - struct fxp_softc *sc; - vm_offset_t pbase; + int error = 0; + struct fxp_softc *sc = device_get_softc(dev); struct ifnet *ifp; int s; u_long val; + int rid; - sc = malloc(sizeof(struct fxp_softc), M_DEVBUF, M_NOWAIT); - if (sc == NULL) - return; - bzero(sc, sizeof(struct fxp_softc)); callout_handle_init(&sc->stat_ch); s = splimp(); /* * Enable bus mastering. */ - val = pci_conf_read(config_id, PCI_COMMAND_STATUS_REG); + val = pci_read_config(dev, PCIR_COMMAND, 2); val |= (PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); - pci_conf_write(config_id, PCI_COMMAND_STATUS_REG, val); + pci_write_config(dev, PCIR_COMMAND, val, 2); /* * Map control/status registers. */ - if (!pci_map_mem(config_id, FXP_PCI_MMBA, - (vm_offset_t *)&sc->csr, &pbase)) { - printf("fxp%d: couldn't map memory\n", unit); + rid = FXP_PCI_MMBA; + sc->mem = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, + 0, ~0, 1, RF_ACTIVE); + if (!sc->mem) { + device_printf(dev, "could not map memory\n"); + error = ENXIO; goto fail; - } + } + sc->csr = rman_get_virtual(sc->mem); /* XXX use bus_space */ /* * Allocate our interrupt. */ - if (!pci_map_int(config_id, fxp_intr, sc, &net_imask)) { - printf("fxp%d: couldn't map interrupt\n", unit); + rid = 0; + sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, + RF_SHAREABLE | RF_ACTIVE); + if (sc->irq == NULL) { + device_printf(dev, "could not map interrupt\n"); + error = ENXIO; goto fail; } + error = bus_setup_intr(dev, sc->irq, fxp_intr, sc, &sc->ih); + if (error) { + device_printf(dev, "could not setup irq\n"); + goto fail; + } + /* Do generic parts of attach. */ if (fxp_attach_common(sc, sc->arpcom.ac_enaddr)) { /* Failed! */ - (void) pci_unmap_int(config_id); + bus_teardown_intr(dev, sc->irq, sc->ih); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq); + bus_release_resource(dev, SYS_RES_MEMORY, FXP_PCI_MMBA, sc->mem); + error = ENXIO; goto fail; } - printf("fxp%d: Ethernet address %6D%s\n", unit, + device_printf(dev, "Ethernet address %6D%s\n", sc->arpcom.ac_enaddr, ":", sc->phy_10Mbps_only ? ", 10Mbps" : ""); ifp = &sc->arpcom.ac_if; - ifp->if_unit = unit; + ifp->if_unit = device_get_unit(dev); ifp->if_name = "fxp"; ifp->if_output = ether_output; ifp->if_baudrate = 100000000; ifp->if_init = fxp_init; ifp->if_softc = sc; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = fxp_ioctl; ifp->if_start = fxp_start; ifp->if_watchdog = fxp_watchdog; /* * Attach the interface. */ if_attach(ifp); /* * Let the system queue as many packets as we have available * TX descriptors. */ ifp->if_snd.ifq_maxlen = FXP_NTXCB - 1; ether_ifattach(ifp); #if NBPFILTER > 0 bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); #endif + splx(s); + return 0; + + fail: + splx(s); + return error; +} + +/* + * Detach interface. + */ +static int +fxp_detach(device_t dev) +{ + struct fxp_softc *sc = device_get_softc(dev); + int s; + + s = splimp(); + /* - * Add shutdown hook so that DMA is disabled prior to reboot. Not - * doing do could allow DMA to corrupt kernel memory during the - * reboot before the driver initializes. + * Close down routes etc. */ - at_shutdown(fxp_shutdown, sc, SHUTDOWN_POST_SYNC); + if_detach(&sc->arpcom.ac_if); - splx(s); - return; + /* + * Stop DMA and drop transmit queue. + */ + fxp_stop(sc); - fail: - free(sc, M_DEVBUF); + /* + * Deallocate resources. + */ + bus_teardown_intr(dev, sc->irq, sc->ih); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq); + bus_release_resource(dev, SYS_RES_MEMORY, FXP_PCI_MMBA, sc->mem); + + /* + * Free all the receive buffers. + */ + if (sc->rfa_headm != NULL) + m_freem(sc->rfa_headm); + + /* + * Free all media structures. + */ + ifmedia_removeall(&sc->sc_media); + + /* + * Free anciliary structures. + */ + free(sc->cbl_base, M_DEVBUF); + free(sc->fxp_stats, M_DEVBUF); + free(sc->mcsp, M_DEVBUF); + splx(s); + + return 0; } /* * Device shutdown routine. Called at system shutdown after sync. The * main purpose of this routine is to shut off receiver DMA so that * kernel memory doesn't get clobbered during warmboot. */ -static void -fxp_shutdown(howto, sc) - int howto; - void *sc; +static int +fxp_shutdown(device_t dev) { - fxp_stop((struct fxp_softc *) sc); + /* + * Make sure that DMA is disabled prior to reboot. Not doing + * do could allow DMA to corrupt kernel memory during the + * reboot before the driver initializes. + */ + fxp_stop((struct fxp_softc *) device_get_softc(dev)); + return 0; } + +static device_method_t fxp_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, fxp_probe), + DEVMETHOD(device_attach, fxp_attach), + DEVMETHOD(device_detach, fxp_detach), + DEVMETHOD(device_shutdown, fxp_shutdown), + + { 0, 0 } +}; + +static driver_t fxp_driver = { + "fxp", + fxp_methods, + DRIVER_TYPE_NET, + sizeof(struct fxp_softc), +}; + +static devclass_t fxp_devclass; + +DRIVER_MODULE(fxp, pci, fxp_driver, fxp_devclass, 0, 0); #endif /* __NetBSD__ */ /************************************************************* * End of operating system-specific autoconfiguration glue *************************************************************/ /* * Do generic parts of attach. */ static int fxp_attach_common(sc, enaddr) struct fxp_softc *sc; u_int8_t *enaddr; { u_int16_t data; int i, nmedia, defmedia; const int *media; /* * Reset to a stable state. */ CSR_WRITE_4(sc, FXP_CSR_PORT, FXP_PORT_SELECTIVE_RESET); DELAY(10); sc->cbl_base = malloc(sizeof(struct fxp_cb_tx) * FXP_NTXCB, M_DEVBUF, M_NOWAIT); if (sc->cbl_base == NULL) goto fail; bzero(sc->cbl_base, sizeof(struct fxp_cb_tx) * FXP_NTXCB); sc->fxp_stats = malloc(sizeof(struct fxp_stats), M_DEVBUF, M_NOWAIT); if (sc->fxp_stats == NULL) goto fail; bzero(sc->fxp_stats, sizeof(struct fxp_stats)); sc->mcsp = malloc(sizeof(struct fxp_cb_mcs), M_DEVBUF, M_NOWAIT); if (sc->mcsp == NULL) goto fail; /* * Pre-allocate our receive buffers. */ for (i = 0; i < FXP_NRFABUFS; i++) { if (fxp_add_rfabuf(sc, NULL) != 0) { goto fail; } } /* * Get info about the primary PHY */ fxp_read_eeprom(sc, (u_int16_t *)&data, 6, 1); sc->phy_primary_addr = data & 0xff; sc->phy_primary_device = (data >> 8) & 0x3f; sc->phy_10Mbps_only = data >> 15; /* * Read MAC address. */ fxp_read_eeprom(sc, (u_int16_t *)enaddr, 0, 3); /* * Initialize the media structures. */ media = fxp_media_default; nmedia = sizeof(fxp_media_default) / sizeof(fxp_media_default[0]); defmedia = FXP_MEDIA_DEFAULT_DEFMEDIA; for (i = 0; i < NFXPMEDIA; i++) { if (sc->phy_primary_device == fxp_media[i].fsm_phy) { media = fxp_media[i].fsm_media; nmedia = fxp_media[i].fsm_nmedia; defmedia = fxp_media[i].fsm_defmedia; } } ifmedia_init(&sc->sc_media, 0, fxp_mediachange, fxp_mediastatus); for (i = 0; i < nmedia; i++) { if (IFM_SUBTYPE(media[i]) == IFM_100_TX && sc->phy_10Mbps_only) continue; ifmedia_add(&sc->sc_media, media[i], 0, NULL); } ifmedia_set(&sc->sc_media, defmedia); return (0); fail: printf(FXP_FORMAT ": Failed to malloc memory\n", FXP_ARGS(sc)); if (sc->cbl_base) free(sc->cbl_base, M_DEVBUF); if (sc->fxp_stats) free(sc->fxp_stats, M_DEVBUF); if (sc->mcsp) free(sc->mcsp, M_DEVBUF); /* frees entire chain */ if (sc->rfa_headm) m_freem(sc->rfa_headm); return (ENOMEM); } /* * Read from the serial EEPROM. Basically, you manually shift in * the read opcode (one bit at a time) and then shift in the address, * and then you shift out the data (all of this one bit at a time). * The word size is 16 bits, so you have to provide the address for * every 16 bits of data. */ static void fxp_read_eeprom(sc, data, offset, words) struct fxp_softc *sc; u_short *data; int offset; int words; { u_int16_t reg; int i, x; for (i = 0; i < words; i++) { CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, FXP_EEPROM_EECS); /* * Shift in read opcode. */ for (x = 3; x > 0; x--) { if (FXP_EEPROM_OPC_READ & (1 << (x - 1))) { reg = FXP_EEPROM_EECS | FXP_EEPROM_EEDI; } else { reg = FXP_EEPROM_EECS; } CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg); CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg | FXP_EEPROM_EESK); DELAY(1); CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg); DELAY(1); } /* * Shift in address. */ for (x = 6; x > 0; x--) { if ((i + offset) & (1 << (x - 1))) { reg = FXP_EEPROM_EECS | FXP_EEPROM_EEDI; } else { reg = FXP_EEPROM_EECS; } CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg); CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg | FXP_EEPROM_EESK); DELAY(1); CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg); DELAY(1); } reg = FXP_EEPROM_EECS; data[i] = 0; /* * Shift out data. */ for (x = 16; x > 0; x--) { CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg | FXP_EEPROM_EESK); DELAY(1); if (CSR_READ_2(sc, FXP_CSR_EEPROMCONTROL) & FXP_EEPROM_EEDO) data[i] |= (1 << (x - 1)); CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg); DELAY(1); } CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, 0); DELAY(1); } } /* * Start packet transmission on the interface. */ static void fxp_start(ifp) struct ifnet *ifp; { struct fxp_softc *sc = ifp->if_softc; struct fxp_cb_tx *txp; /* * See if we need to suspend xmit until the multicast filter * has been reprogrammed (which can only be done at the head * of the command chain). */ if (sc->need_mcsetup) return; txp = NULL; /* * We're finished if there is nothing more to add to the list or if * we're all filled up with buffers to transmit. * NOTE: One TxCB is reserved to guarantee that fxp_mc_setup() can add * a NOP command when needed. */ while (ifp->if_snd.ifq_head != NULL && sc->tx_queued < FXP_NTXCB - 1) { struct mbuf *m, *mb_head; int segment; /* * Grab a packet to transmit. */ IF_DEQUEUE(&ifp->if_snd, mb_head); /* * Get pointer to next available tx desc. */ txp = sc->cbl_last->next; /* * Go through each of the mbufs in the chain and initialize * the transmit buffer descriptors with the physical address * and size of the mbuf. */ tbdinit: for (m = mb_head, segment = 0; m != NULL; m = m->m_next) { if (m->m_len != 0) { if (segment == FXP_NTXSEG) break; txp->tbd[segment].tb_addr = vtophys(mtod(m, vm_offset_t)); txp->tbd[segment].tb_size = m->m_len; segment++; } } if (m != NULL) { struct mbuf *mn; /* * We ran out of segments. We have to recopy this mbuf * chain first. Bail out if we can't get the new buffers. */ MGETHDR(mn, M_DONTWAIT, MT_DATA); if (mn == NULL) { m_freem(mb_head); break; } if (mb_head->m_pkthdr.len > MHLEN) { MCLGET(mn, M_DONTWAIT); if ((mn->m_flags & M_EXT) == 0) { m_freem(mn); m_freem(mb_head); break; } } m_copydata(mb_head, 0, mb_head->m_pkthdr.len, mtod(mn, caddr_t)); mn->m_pkthdr.len = mn->m_len = mb_head->m_pkthdr.len; m_freem(mb_head); mb_head = mn; goto tbdinit; } txp->tbd_number = segment; txp->mb_head = mb_head; txp->cb_status = 0; if (sc->tx_queued != FXP_CXINT_THRESH - 1) { txp->cb_command = FXP_CB_COMMAND_XMIT | FXP_CB_COMMAND_SF | FXP_CB_COMMAND_S; } else { txp->cb_command = FXP_CB_COMMAND_XMIT | FXP_CB_COMMAND_SF | FXP_CB_COMMAND_S | FXP_CB_COMMAND_I; /* * Set a 5 second timer just in case we don't hear from the * card again. */ ifp->if_timer = 5; } txp->tx_threshold = tx_threshold; /* * Advance the end of list forward. */ sc->cbl_last->cb_command &= ~FXP_CB_COMMAND_S; sc->cbl_last = txp; /* * Advance the beginning of the list forward if there are * no other packets queued (when nothing is queued, cbl_first * sits on the last TxCB that was sent out). */ if (sc->tx_queued == 0) sc->cbl_first = txp; sc->tx_queued++; #if NBPFILTER > 0 /* * Pass packet to bpf if there is a listener. */ if (ifp->if_bpf) bpf_mtap(FXP_BPFTAP_ARG(ifp), mb_head); #endif } /* * We're finished. If we added to the list, issue a RESUME to get DMA * going again if suspended. */ if (txp != NULL) { fxp_scb_wait(sc); CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_RESUME); } } /* * Process interface interrupts. */ static FXP_INTR_TYPE fxp_intr(arg) void *arg; { struct fxp_softc *sc = arg; struct ifnet *ifp = &sc->sc_if; u_int8_t statack; #if defined(__NetBSD__) int claimed = 0; #endif while ((statack = CSR_READ_1(sc, FXP_CSR_SCB_STATACK)) != 0) { #if defined(__NetBSD__) claimed = 1; #endif /* * First ACK all the interrupts in this pass. */ CSR_WRITE_1(sc, FXP_CSR_SCB_STATACK, statack); /* * Free any finished transmit mbuf chains. */ if (statack & FXP_SCB_STATACK_CXTNO) { struct fxp_cb_tx *txp; for (txp = sc->cbl_first; sc->tx_queued && (txp->cb_status & FXP_CB_STATUS_C) != 0; txp = txp->next) { if (txp->mb_head != NULL) { m_freem(txp->mb_head); txp->mb_head = NULL; } sc->tx_queued--; } sc->cbl_first = txp; ifp->if_timer = 0; if (sc->tx_queued == 0) { if (sc->need_mcsetup) fxp_mc_setup(sc); } /* * Try to start more packets transmitting. */ if (ifp->if_snd.ifq_head != NULL) fxp_start(ifp); } /* * Process receiver interrupts. If a no-resource (RNR) * condition exists, get whatever packets we can and * re-start the receiver. */ if (statack & (FXP_SCB_STATACK_FR | FXP_SCB_STATACK_RNR)) { struct mbuf *m; struct fxp_rfa *rfa; rcvloop: m = sc->rfa_headm; rfa = (struct fxp_rfa *)(m->m_ext.ext_buf + RFA_ALIGNMENT_FUDGE); if (rfa->rfa_status & FXP_RFA_STATUS_C) { /* * Remove first packet from the chain. */ sc->rfa_headm = m->m_next; m->m_next = NULL; /* * Add a new buffer to the receive chain. * If this fails, the old buffer is recycled * instead. */ if (fxp_add_rfabuf(sc, m) == 0) { struct ether_header *eh; u_int16_t total_len; total_len = rfa->actual_size & (MCLBYTES - 1); if (total_len < sizeof(struct ether_header)) { m_freem(m); goto rcvloop; } m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = m->m_len = total_len ; eh = mtod(m, struct ether_header *); #if NBPFILTER > 0 if (ifp->if_bpf) bpf_tap(FXP_BPFTAP_ARG(ifp), mtod(m, caddr_t), total_len); #endif /* NBPFILTER > 0 */ #ifdef BRIDGE if (do_bridge) { struct ifnet *bdg_ifp ; bdg_ifp = bridge_in(m); if (bdg_ifp == BDG_DROP) goto dropit ; if (bdg_ifp != BDG_LOCAL) bdg_forward(&m, bdg_ifp); if (bdg_ifp != BDG_LOCAL && bdg_ifp != BDG_BCAST && bdg_ifp != BDG_MCAST) goto dropit ; goto getit ; } #endif /* * Only pass this packet up * if it is for us. */ if ((ifp->if_flags & IFF_PROMISC) && (rfa->rfa_status & FXP_RFA_STATUS_IAMATCH) && (eh->ether_dhost[0] & 1) == 0) { dropit: if (m) m_freem(m); goto rcvloop; } getit: m->m_data += sizeof(struct ether_header); m->m_len -= sizeof(struct ether_header); m->m_pkthdr.len = m->m_len ; ether_input(ifp, eh, m); } goto rcvloop; } if (statack & FXP_SCB_STATACK_RNR) { fxp_scb_wait(sc); CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, vtophys(sc->rfa_headm->m_ext.ext_buf) + RFA_ALIGNMENT_FUDGE); CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_RU_START); } } } #if defined(__NetBSD__) return (claimed); #endif } /* * Update packet in/out/collision statistics. The i82557 doesn't * allow you to access these counters without doing a fairly * expensive DMA to get _all_ of the statistics it maintains, so * we do this operation here only once per second. The statistics * counters in the kernel are updated from the previous dump-stats * DMA and then a new dump-stats DMA is started. The on-chip * counters are zeroed when the DMA completes. If we can't start * the DMA immediately, we don't wait - we just prepare to read * them again next time. */ static void fxp_stats_update(arg) void *arg; { struct fxp_softc *sc = arg; struct ifnet *ifp = &sc->sc_if; struct fxp_stats *sp = sc->fxp_stats; struct fxp_cb_tx *txp; int s; ifp->if_opackets += sp->tx_good; ifp->if_collisions += sp->tx_total_collisions; if (sp->rx_good) { ifp->if_ipackets += sp->rx_good; sc->rx_idle_secs = 0; } else { /* * Receiver's been idle for another second. */ sc->rx_idle_secs++; } ifp->if_ierrors += sp->rx_crc_errors + sp->rx_alignment_errors + sp->rx_rnr_errors + sp->rx_overrun_errors; /* * If any transmit underruns occured, bump up the transmit * threshold by another 512 bytes (64 * 8). */ if (sp->tx_underruns) { ifp->if_oerrors += sp->tx_underruns; if (tx_threshold < 192) tx_threshold += 64; } s = splimp(); /* * Release any xmit buffers that have completed DMA. This isn't * strictly necessary to do here, but it's advantagous for mbufs * with external storage to be released in a timely manner rather * than being defered for a potentially long time. This limits * the delay to a maximum of one second. */ for (txp = sc->cbl_first; sc->tx_queued && (txp->cb_status & FXP_CB_STATUS_C) != 0; txp = txp->next) { if (txp->mb_head != NULL) { m_freem(txp->mb_head); txp->mb_head = NULL; } sc->tx_queued--; } sc->cbl_first = txp; /* * If we haven't received any packets in FXP_MAC_RX_IDLE seconds, * then assume the receiver has locked up and attempt to clear * the condition by reprogramming the multicast filter. This is * a work-around for a bug in the 82557 where the receiver locks * up if it gets certain types of garbage in the syncronization * bits prior to the packet header. This bug is supposed to only * occur in 10Mbps mode, but has been seen to occur in 100Mbps * mode as well (perhaps due to a 10/100 speed transition). */ if (sc->rx_idle_secs > FXP_MAX_RX_IDLE) { sc->rx_idle_secs = 0; fxp_mc_setup(sc); } /* * If there is no pending command, start another stats * dump. Otherwise punt for now. */ if (CSR_READ_1(sc, FXP_CSR_SCB_COMMAND) == 0) { /* * Start another stats dump. */ CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_DUMPRESET); } else { /* * A previous command is still waiting to be accepted. * Just zero our copy of the stats and wait for the * next timer event to update them. */ sp->tx_good = 0; sp->tx_underruns = 0; sp->tx_total_collisions = 0; sp->rx_good = 0; sp->rx_crc_errors = 0; sp->rx_alignment_errors = 0; sp->rx_rnr_errors = 0; sp->rx_overrun_errors = 0; } splx(s); /* * Schedule another timeout one second from now. */ sc->stat_ch = timeout(fxp_stats_update, sc, hz); } /* * Stop the interface. Cancels the statistics updater and resets * the interface. */ static void fxp_stop(sc) struct fxp_softc *sc; { struct ifnet *ifp = &sc->sc_if; struct fxp_cb_tx *txp; int i; /* * Cancel stats updater. */ untimeout(fxp_stats_update, sc, sc->stat_ch); /* * Issue software reset */ CSR_WRITE_4(sc, FXP_CSR_PORT, FXP_PORT_SELECTIVE_RESET); DELAY(10); /* * Release any xmit buffers. */ txp = sc->cbl_base; if (txp != NULL) { for (i = 0; i < FXP_NTXCB; i++) { if (txp[i].mb_head != NULL) { m_freem(txp[i].mb_head); txp[i].mb_head = NULL; } } } sc->tx_queued = 0; /* * Free all the receive buffers then reallocate/reinitialize */ if (sc->rfa_headm != NULL) m_freem(sc->rfa_headm); sc->rfa_headm = NULL; sc->rfa_tailm = NULL; for (i = 0; i < FXP_NRFABUFS; i++) { if (fxp_add_rfabuf(sc, NULL) != 0) { /* * This "can't happen" - we're at splimp() * and we just freed all the buffers we need * above. */ panic("fxp_stop: no buffers!"); } } ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); ifp->if_timer = 0; } /* * Watchdog/transmission transmit timeout handler. Called when a * transmission is started on the interface, but no interrupt is * received before the timeout. This usually indicates that the * card has wedged for some reason. */ static void fxp_watchdog(ifp) struct ifnet *ifp; { struct fxp_softc *sc = ifp->if_softc; printf(FXP_FORMAT ": device timeout\n", FXP_ARGS(sc)); ifp->if_oerrors++; fxp_init(sc); } static void fxp_init(xsc) void *xsc; { struct fxp_softc *sc = xsc; struct ifnet *ifp = &sc->sc_if; struct fxp_cb_config *cbp; struct fxp_cb_ias *cb_ias; struct fxp_cb_tx *txp; int i, s, prm; s = splimp(); /* * Cancel any pending I/O */ fxp_stop(sc); prm = (ifp->if_flags & IFF_PROMISC) ? 1 : 0; /* * Initialize base of CBL and RFA memory. Loading with zero * sets it up for regular linear addressing. */ CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, 0); CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_BASE); fxp_scb_wait(sc); CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_RU_BASE); /* * Initialize base of dump-stats buffer. */ fxp_scb_wait(sc); CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, vtophys(sc->fxp_stats)); CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_DUMP_ADR); /* * We temporarily use memory that contains the TxCB list to * construct the config CB. The TxCB list memory is rebuilt * later. */ cbp = (struct fxp_cb_config *) sc->cbl_base; /* * This bcopy is kind of disgusting, but there are a bunch of must be * zero and must be one bits in this structure and this is the easiest * way to initialize them all to proper values. */ bcopy(fxp_cb_config_template, (volatile void *)&cbp->cb_status, sizeof(fxp_cb_config_template)); cbp->cb_status = 0; cbp->cb_command = FXP_CB_COMMAND_CONFIG | FXP_CB_COMMAND_EL; cbp->link_addr = -1; /* (no) next command */ cbp->byte_count = 22; /* (22) bytes to config */ cbp->rx_fifo_limit = 8; /* rx fifo threshold (32 bytes) */ cbp->tx_fifo_limit = 0; /* tx fifo threshold (0 bytes) */ cbp->adaptive_ifs = 0; /* (no) adaptive interframe spacing */ cbp->rx_dma_bytecount = 0; /* (no) rx DMA max */ cbp->tx_dma_bytecount = 0; /* (no) tx DMA max */ cbp->dma_bce = 0; /* (disable) dma max counters */ cbp->late_scb = 0; /* (don't) defer SCB update */ cbp->tno_int = 0; /* (disable) tx not okay interrupt */ cbp->ci_int = 1; /* interrupt on CU idle */ cbp->save_bf = prm; /* save bad frames */ cbp->disc_short_rx = !prm; /* discard short packets */ cbp->underrun_retry = 1; /* retry mode (1) on DMA underrun */ cbp->mediatype = !sc->phy_10Mbps_only; /* interface mode */ cbp->nsai = 1; /* (don't) disable source addr insert */ cbp->preamble_length = 2; /* (7 byte) preamble */ cbp->loopback = 0; /* (don't) loopback */ cbp->linear_priority = 0; /* (normal CSMA/CD operation) */ cbp->linear_pri_mode = 0; /* (wait after xmit only) */ cbp->interfrm_spacing = 6; /* (96 bits of) interframe spacing */ cbp->promiscuous = prm; /* promiscuous mode */ cbp->bcast_disable = 0; /* (don't) disable broadcasts */ cbp->crscdt = 0; /* (CRS only) */ cbp->stripping = !prm; /* truncate rx packet to byte count */ cbp->padding = 1; /* (do) pad short tx packets */ cbp->rcv_crc_xfer = 0; /* (don't) xfer CRC to host */ cbp->force_fdx = 0; /* (don't) force full duplex */ cbp->fdx_pin_en = 1; /* (enable) FDX# pin */ cbp->multi_ia = 0; /* (don't) accept multiple IAs */ cbp->mc_all = sc->all_mcasts;/* accept all multicasts */ /* * Start the config command/DMA. */ fxp_scb_wait(sc); CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, vtophys(&cbp->cb_status)); CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_START); /* ...and wait for it to complete. */ while (!(cbp->cb_status & FXP_CB_STATUS_C)); /* * Now initialize the station address. Temporarily use the TxCB * memory area like we did above for the config CB. */ cb_ias = (struct fxp_cb_ias *) sc->cbl_base; cb_ias->cb_status = 0; cb_ias->cb_command = FXP_CB_COMMAND_IAS | FXP_CB_COMMAND_EL; cb_ias->link_addr = -1; #if defined(__NetBSD__) bcopy(LLADDR(ifp->if_sadl), (void *)cb_ias->macaddr, 6); #else bcopy(sc->arpcom.ac_enaddr, (volatile void *)cb_ias->macaddr, sizeof(sc->arpcom.ac_enaddr)); #endif /* __NetBSD__ */ /* * Start the IAS (Individual Address Setup) command/DMA. */ fxp_scb_wait(sc); CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_START); /* ...and wait for it to complete. */ while (!(cb_ias->cb_status & FXP_CB_STATUS_C)); /* * Initialize transmit control block (TxCB) list. */ txp = sc->cbl_base; bzero(txp, sizeof(struct fxp_cb_tx) * FXP_NTXCB); for (i = 0; i < FXP_NTXCB; i++) { txp[i].cb_status = FXP_CB_STATUS_C | FXP_CB_STATUS_OK; txp[i].cb_command = FXP_CB_COMMAND_NOP; txp[i].link_addr = vtophys(&txp[(i + 1) & FXP_TXCB_MASK].cb_status); txp[i].tbd_array_addr = vtophys(&txp[i].tbd[0]); txp[i].next = &txp[(i + 1) & FXP_TXCB_MASK]; } /* * Set the suspend flag on the first TxCB and start the control * unit. It will execute the NOP and then suspend. */ txp->cb_command = FXP_CB_COMMAND_NOP | FXP_CB_COMMAND_S; sc->cbl_first = sc->cbl_last = txp; sc->tx_queued = 1; fxp_scb_wait(sc); CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_START); /* * Initialize receiver buffer area - RFA. */ fxp_scb_wait(sc); CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, vtophys(sc->rfa_headm->m_ext.ext_buf) + RFA_ALIGNMENT_FUDGE); CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_RU_START); /* * Set current media. */ fxp_set_media(sc, sc->sc_media.ifm_cur->ifm_media); ifp->if_flags |= IFF_RUNNING; ifp->if_flags &= ~IFF_OACTIVE; splx(s); /* * Start stats updater. */ sc->stat_ch = timeout(fxp_stats_update, sc, hz); } static void fxp_set_media(sc, media) struct fxp_softc *sc; int media; { switch (sc->phy_primary_device) { case FXP_PHY_DP83840: case FXP_PHY_DP83840A: fxp_mdi_write(sc, sc->phy_primary_addr, FXP_DP83840_PCR, fxp_mdi_read(sc, sc->phy_primary_addr, FXP_DP83840_PCR) | FXP_DP83840_PCR_LED4_MODE | /* LED4 always indicates duplex */ FXP_DP83840_PCR_F_CONNECT | /* force link disconnect bypass */ FXP_DP83840_PCR_BIT10); /* XXX I have no idea */ /* fall through */ case FXP_PHY_82553A: case FXP_PHY_82553C: /* untested */ case FXP_PHY_82555: case FXP_PHY_82555B: if (IFM_SUBTYPE(media) != IFM_AUTO) { int flags; flags = (IFM_SUBTYPE(media) == IFM_100_TX) ? FXP_PHY_BMCR_SPEED_100M : 0; flags |= (media & IFM_FDX) ? FXP_PHY_BMCR_FULLDUPLEX : 0; fxp_mdi_write(sc, sc->phy_primary_addr, FXP_PHY_BMCR, (fxp_mdi_read(sc, sc->phy_primary_addr, FXP_PHY_BMCR) & ~(FXP_PHY_BMCR_AUTOEN | FXP_PHY_BMCR_SPEED_100M | FXP_PHY_BMCR_FULLDUPLEX)) | flags); } else { fxp_mdi_write(sc, sc->phy_primary_addr, FXP_PHY_BMCR, (fxp_mdi_read(sc, sc->phy_primary_addr, FXP_PHY_BMCR) | FXP_PHY_BMCR_AUTOEN)); } break; /* * The Seeq 80c24 doesn't have a PHY programming interface, so do * nothing. */ case FXP_PHY_80C24: break; default: printf(FXP_FORMAT ": warning: unsupported PHY, type = %d, addr = %d\n", FXP_ARGS(sc), sc->phy_primary_device, sc->phy_primary_addr); } } /* * Change media according to request. */ int fxp_mediachange(ifp) struct ifnet *ifp; { struct fxp_softc *sc = ifp->if_softc; struct ifmedia *ifm = &sc->sc_media; if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER) return (EINVAL); fxp_set_media(sc, ifm->ifm_media); return (0); } /* * Notify the world which media we're using. */ void fxp_mediastatus(ifp, ifmr) struct ifnet *ifp; struct ifmediareq *ifmr; { struct fxp_softc *sc = ifp->if_softc; int flags, stsflags; switch (sc->phy_primary_device) { case FXP_PHY_82555: case FXP_PHY_82555B: case FXP_PHY_DP83840: case FXP_PHY_DP83840A: ifmr->ifm_status = IFM_AVALID; /* IFM_ACTIVE will be valid */ ifmr->ifm_active = IFM_ETHER; /* * the following is not an error. * You need to read this register twice to get current * status. This is correct documented behaviour, the * first read gets latched values. */ stsflags = fxp_mdi_read(sc, sc->phy_primary_addr, FXP_PHY_STS); stsflags = fxp_mdi_read(sc, sc->phy_primary_addr, FXP_PHY_STS); if (stsflags & FXP_PHY_STS_LINK_STS) ifmr->ifm_status |= IFM_ACTIVE; /* * If we are in auto mode, then try report the result. */ flags = fxp_mdi_read(sc, sc->phy_primary_addr, FXP_PHY_BMCR); if (flags & FXP_PHY_BMCR_AUTOEN) { ifmr->ifm_active |= IFM_AUTO; /* XXX presently 0 */ if (stsflags & FXP_PHY_STS_AUTO_DONE) { /* * Intel and National parts report * differently on what they found. */ if ((sc->phy_primary_device == FXP_PHY_82555) || (sc->phy_primary_device == FXP_PHY_82555B)) { flags = fxp_mdi_read(sc, sc->phy_primary_addr, FXP_PHY_USC); if (flags & FXP_PHY_USC_SPEED) ifmr->ifm_active |= IFM_100_TX; else ifmr->ifm_active |= IFM_10_T; if (flags & FXP_PHY_USC_DUPLEX) ifmr->ifm_active |= IFM_FDX; } else { /* it's National. only know speed */ flags = fxp_mdi_read(sc, sc->phy_primary_addr, FXP_DP83840_PAR); if (flags & FXP_DP83840_PAR_SPEED_10) ifmr->ifm_active |= IFM_10_T; else ifmr->ifm_active |= IFM_100_TX; } } } else { /* in manual mode.. just report what we were set to */ if (flags & FXP_PHY_BMCR_SPEED_100M) ifmr->ifm_active |= IFM_100_TX; else ifmr->ifm_active |= IFM_10_T; if (flags & FXP_PHY_BMCR_FULLDUPLEX) ifmr->ifm_active |= IFM_FDX; } break; case FXP_PHY_80C24: default: ifmr->ifm_active = IFM_ETHER|IFM_MANUAL; /* XXX IFM_AUTO ? */ } } /* * Add a buffer to the end of the RFA buffer list. * Return 0 if successful, 1 for failure. A failure results in * adding the 'oldm' (if non-NULL) on to the end of the list - * tossing out its old contents and recycling it. * The RFA struct is stuck at the beginning of mbuf cluster and the * data pointer is fixed up to point just past it. */ static int fxp_add_rfabuf(sc, oldm) struct fxp_softc *sc; struct mbuf *oldm; { u_int32_t v; struct mbuf *m; struct fxp_rfa *rfa, *p_rfa; MGETHDR(m, M_DONTWAIT, MT_DATA); if (m != NULL) { MCLGET(m, M_DONTWAIT); if ((m->m_flags & M_EXT) == 0) { m_freem(m); if (oldm == NULL) return 1; m = oldm; m->m_data = m->m_ext.ext_buf; } } else { if (oldm == NULL) return 1; m = oldm; m->m_data = m->m_ext.ext_buf; } /* * Move the data pointer up so that the incoming data packet * will be 32-bit aligned. */ m->m_data += RFA_ALIGNMENT_FUDGE; /* * Get a pointer to the base of the mbuf cluster and move * data start past it. */ rfa = mtod(m, struct fxp_rfa *); m->m_data += sizeof(struct fxp_rfa); rfa->size = MCLBYTES - sizeof(struct fxp_rfa) - RFA_ALIGNMENT_FUDGE; /* * Initialize the rest of the RFA. Note that since the RFA * is misaligned, we cannot store values directly. Instead, * we use an optimized, inline copy. */ rfa->rfa_status = 0; rfa->rfa_control = FXP_RFA_CONTROL_EL; rfa->actual_size = 0; v = -1; fxp_lwcopy(&v, &rfa->link_addr); fxp_lwcopy(&v, &rfa->rbd_addr); /* * If there are other buffers already on the list, attach this * one to the end by fixing up the tail to point to this one. */ if (sc->rfa_headm != NULL) { p_rfa = (struct fxp_rfa *) (sc->rfa_tailm->m_ext.ext_buf + RFA_ALIGNMENT_FUDGE); sc->rfa_tailm->m_next = m; v = vtophys(rfa); fxp_lwcopy(&v, &p_rfa->link_addr); p_rfa->rfa_control &= ~FXP_RFA_CONTROL_EL; } else { sc->rfa_headm = m; } sc->rfa_tailm = m; return (m == oldm); } static volatile int fxp_mdi_read(sc, phy, reg) struct fxp_softc *sc; int phy; int reg; { int count = 10000; int value; CSR_WRITE_4(sc, FXP_CSR_MDICONTROL, (FXP_MDI_READ << 26) | (reg << 16) | (phy << 21)); while (((value = CSR_READ_4(sc, FXP_CSR_MDICONTROL)) & 0x10000000) == 0 && count--) DELAY(10); if (count <= 0) printf(FXP_FORMAT ": fxp_mdi_read: timed out\n", FXP_ARGS(sc)); return (value & 0xffff); } static void fxp_mdi_write(sc, phy, reg, value) struct fxp_softc *sc; int phy; int reg; int value; { int count = 10000; CSR_WRITE_4(sc, FXP_CSR_MDICONTROL, (FXP_MDI_WRITE << 26) | (reg << 16) | (phy << 21) | (value & 0xffff)); while((CSR_READ_4(sc, FXP_CSR_MDICONTROL) & 0x10000000) == 0 && count--) DELAY(10); if (count <= 0) printf(FXP_FORMAT ": fxp_mdi_write: timed out\n", FXP_ARGS(sc)); } static int fxp_ioctl(ifp, command, data) struct ifnet *ifp; FXP_IOCTLCMD_TYPE command; caddr_t data; { struct fxp_softc *sc = ifp->if_softc; struct ifreq *ifr = (struct ifreq *)data; int s, error = 0; s = splimp(); switch (command) { case SIOCSIFADDR: #if !defined(__NetBSD__) case SIOCGIFADDR: case SIOCSIFMTU: #endif error = ether_ioctl(ifp, command, data); break; case SIOCSIFFLAGS: sc->all_mcasts = (ifp->if_flags & IFF_ALLMULTI) ? 1 : 0; /* * If interface is marked up and not running, then start it. * If it is marked down and running, stop it. * XXX If it's up then re-initialize it. This is so flags * such as IFF_PROMISC are handled. */ if (ifp->if_flags & IFF_UP) { fxp_init(sc); } else { if (ifp->if_flags & IFF_RUNNING) fxp_stop(sc); } break; case SIOCADDMULTI: case SIOCDELMULTI: sc->all_mcasts = (ifp->if_flags & IFF_ALLMULTI) ? 1 : 0; #if defined(__NetBSD__) error = (command == SIOCADDMULTI) ? ether_addmulti(ifr, &sc->sc_ethercom) : ether_delmulti(ifr, &sc->sc_ethercom); if (error == ENETRESET) { /* * Multicast list has changed; set the hardware * filter accordingly. */ if (!sc->all_mcasts) fxp_mc_setup(sc); /* * fxp_mc_setup() can turn on all_mcasts if we run * out of space, so check it again rather than else {}. */ if (sc->all_mcasts) fxp_init(sc); error = 0; } #else /* __FreeBSD__ */ /* * Multicast list has changed; set the hardware filter * accordingly. */ if (!sc->all_mcasts) fxp_mc_setup(sc); /* * fxp_mc_setup() can turn on sc->all_mcasts, so check it * again rather than else {}. */ if (sc->all_mcasts) fxp_init(sc); error = 0; #endif /* __NetBSD__ */ break; case SIOCSIFMEDIA: case SIOCGIFMEDIA: error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, command); break; default: error = EINVAL; } (void) splx(s); return (error); } /* * Program the multicast filter. * * We have an artificial restriction that the multicast setup command * must be the first command in the chain, so we take steps to ensure * this. By requiring this, it allows us to keep up the performance of * the pre-initialized command ring (esp. link pointers) by not actually * inserting the mcsetup command in the ring - i.e. its link pointer * points to the TxCB ring, but the mcsetup descriptor itself is not part * of it. We then can do 'CU_START' on the mcsetup descriptor and have it * lead into the regular TxCB ring when it completes. * * This function must be called at splimp. */ static void fxp_mc_setup(sc) struct fxp_softc *sc; { struct fxp_cb_mcs *mcsp = sc->mcsp; struct ifnet *ifp = &sc->sc_if; struct ifmultiaddr *ifma; int nmcasts; /* * If there are queued commands, we must wait until they are all * completed. If we are already waiting, then add a NOP command * with interrupt option so that we're notified when all commands * have been completed - fxp_start() ensures that no additional * TX commands will be added when need_mcsetup is true. */ if (sc->tx_queued) { struct fxp_cb_tx *txp; /* * need_mcsetup will be true if we are already waiting for the * NOP command to be completed (see below). In this case, bail. */ if (sc->need_mcsetup) return; sc->need_mcsetup = 1; /* * Add a NOP command with interrupt so that we are notified when all * TX commands have been processed. */ txp = sc->cbl_last->next; txp->mb_head = NULL; txp->cb_status = 0; txp->cb_command = FXP_CB_COMMAND_NOP | FXP_CB_COMMAND_S | FXP_CB_COMMAND_I; /* * Advance the end of list forward. */ sc->cbl_last->cb_command &= ~FXP_CB_COMMAND_S; sc->cbl_last = txp; sc->tx_queued++; /* * Issue a resume in case the CU has just suspended. */ fxp_scb_wait(sc); CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_RESUME); /* * Set a 5 second timer just in case we don't hear from the * card again. */ ifp->if_timer = 5; return; } sc->need_mcsetup = 0; /* * Initialize multicast setup descriptor. */ mcsp->next = sc->cbl_base; mcsp->mb_head = NULL; mcsp->cb_status = 0; mcsp->cb_command = FXP_CB_COMMAND_MCAS | FXP_CB_COMMAND_S | FXP_CB_COMMAND_I; mcsp->link_addr = vtophys(&sc->cbl_base->cb_status); nmcasts = 0; if (!sc->all_mcasts) { for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL; ifma = ifma->ifma_link.le_next) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; if (nmcasts >= MAXMCADDR) { sc->all_mcasts = 1; nmcasts = 0; break; } bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), (volatile void *) &sc->mcsp->mc_addr[nmcasts][0], 6); nmcasts++; } } mcsp->mc_cnt = nmcasts * 6; sc->cbl_first = sc->cbl_last = (struct fxp_cb_tx *) mcsp; sc->tx_queued = 1; /* * Wait until command unit is not active. This should never * be the case when nothing is queued, but make sure anyway. */ while ((CSR_READ_1(sc, FXP_CSR_SCB_RUSCUS) >> 6) == FXP_SCB_CUS_ACTIVE) ; /* * Start the multicast setup command. */ fxp_scb_wait(sc); CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, vtophys(&mcsp->cb_status)); CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_START); ifp->if_timer = 2; return; } Index: head/sys/pci/if_fxpvar.h =================================================================== --- head/sys/pci/if_fxpvar.h (revision 45719) +++ head/sys/pci/if_fxpvar.h (revision 45720) @@ -1,114 +1,117 @@ /* * Copyright (c) 1995, David Greenman * All rights reserved. * * Modifications to support NetBSD: * Copyright (c) 1997 Jason R. Thorpe. 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 unmodified, 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. * - * $Id: if_fxpvar.h,v 1.5 1998/06/07 17:12:38 dfr Exp $ + * $Id: if_fxpvar.h,v 1.6 1998/08/02 00:29:15 dg Exp $ */ /* * Misc. defintions for the Intel EtherExpress Pro/100B PCI Fast * Ethernet driver */ /* * NOTE: Elements are ordered for optimal cacheline behavior, and NOT * for functional grouping. */ struct fxp_softc { #if defined(__NetBSD__) struct device sc_dev; /* generic device structures */ void *sc_ih; /* interrupt handler cookie */ bus_space_tag_t sc_st; /* bus space tag */ bus_space_handle_t sc_sh; /* bus space handle */ struct ethercom sc_ethercom; /* ethernet common part */ #else struct arpcom arpcom; /* per-interface network data */ caddr_t csr; /* control/status registers */ + struct resource *mem; /* resource descriptor for registers */ + struct resource *irq; /* resource descriptor for interrupt */ + void *ih; /* interrupt handler cookie */ #endif /* __NetBSD__ */ struct mbuf *rfa_headm; /* first mbuf in receive frame area */ struct mbuf *rfa_tailm; /* last mbuf in receive frame area */ struct fxp_cb_tx *cbl_first; /* first active TxCB in list */ int tx_queued; /* # of active TxCB's */ int need_mcsetup; /* multicast filter needs programming */ struct fxp_cb_tx *cbl_last; /* last active TxCB in list */ struct fxp_stats *fxp_stats; /* Pointer to interface stats */ int rx_idle_secs; /* # of seconds RX has been idle */ struct callout_handle stat_ch; /* Handle for canceling our stat timeout */ struct fxp_cb_tx *cbl_base; /* base of TxCB list */ struct fxp_cb_mcs *mcsp; /* Pointer to mcast setup descriptor */ int all_mcasts; /* receive all multicasts */ struct ifmedia sc_media; /* media information */ int phy_primary_addr; /* address of primary PHY */ int phy_primary_device; /* device type of primary PHY */ int phy_10Mbps_only; /* PHY is 10Mbps-only device */ }; /* Macros to ease CSR access. */ #if defined(__NetBSD__) #define CSR_READ_1(sc, reg) \ bus_space_read_1((sc)->sc_st, (sc)->sc_sh, (reg)) #define CSR_READ_2(sc, reg) \ bus_space_read_2((sc)->sc_st, (sc)->sc_sh, (reg)) #define CSR_READ_4(sc, reg) \ bus_space_read_4((sc)->sc_st, (sc)->sc_sh, (reg)) #define CSR_WRITE_1(sc, reg, val) \ bus_space_write_1((sc)->sc_st, (sc)->sc_sh, (reg), (val)) #define CSR_WRITE_2(sc, reg, val) \ bus_space_write_2((sc)->sc_st, (sc)->sc_sh, (reg), (val)) #define CSR_WRITE_4(sc, reg, val) \ bus_space_write_4((sc)->sc_st, (sc)->sc_sh, (reg), (val)) #else #define CSR_READ_1(sc, reg) \ (*((u_int8_t *)((sc)->csr + (reg)))) #define CSR_READ_2(sc, reg) \ (*((u_int16_t *)((sc)->csr + (reg)))) #define CSR_READ_4(sc, reg) \ (*((u_int32_t *)((sc)->csr + (reg)))) #define CSR_WRITE_1(sc, reg, val) \ (*((u_int8_t *)((sc)->csr + (reg)))) = (val) #define CSR_WRITE_2(sc, reg, val) \ (*((u_int16_t *)((sc)->csr + (reg)))) = (val) #define CSR_WRITE_4(sc, reg, val) \ (*((u_int32_t *)((sc)->csr + (reg)))) = (val) #endif /* __NetBSD__ */ /* Deal with slight differences in software interfaces. */ #if defined(__NetBSD__) #define sc_if sc_ethercom.ec_if #define FXP_FORMAT "%s" #define FXP_ARGS(sc) (sc)->sc_dev.dv_xname #define FXP_INTR_TYPE int #define FXP_IOCTLCMD_TYPE u_long #define FXP_BPFTAP_ARG(ifp) (ifp)->if_bpf #else /* __FreeBSD__ */ #define sc_if arpcom.ac_if #define FXP_FORMAT "fxp%d" #define FXP_ARGS(sc) (sc)->arpcom.ac_if.if_unit #define FXP_INTR_TYPE void #define FXP_IOCTLCMD_TYPE u_long #define FXP_BPFTAP_ARG(ifp) ifp #endif /* __NetBSD__ */ Index: head/sys/pci/if_pn.c =================================================================== --- head/sys/pci/if_pn.c (revision 45719) +++ head/sys/pci/if_pn.c (revision 45720) @@ -1,2279 +1,2285 @@ /* * Copyright (c) 1997, 1998 * Bill Paul . 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 Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD * 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. * - * $Id: if_pn.c,v 1.50 1999/04/14 18:52:02 wpaul Exp $ + * $Id: if_pn.c,v 1.16 1999/04/14 19:40:07 wpaul Exp $ */ /* * 82c168/82c169 PNIC fast ethernet PCI NIC driver * * Supports various network adapters based on the Lite-On PNIC * PCI network controller chip including the LinkSys LNE100TX. * * Written by Bill Paul * Electrical Engineering Department * Columbia University, New York City */ /* * The PNIC chip is a DEC tulip clone. This driver uses much of the * same code from the driver for the Winbond chip (which is also a * tulip clone) except for the MII, EEPROM and filter programming. * * Technically we could merge support for this chip into the 'de' * driver, but it's such a mess that I'm afraid to go near it. * * The PNIC appears to support both an external MII and an internal * transceiver. I think most 100Mbps implementations use a PHY attached * the the MII. The LinkSys board that I have uses a Myson MTD972 * 100BaseTX PHY. */ #include "bpfilter.h" #include #include #include #include #include #include #include #include #include #include #include #include #if NBPFILTER > 0 #include #endif #include /* for vtophys */ #include /* for vtophys */ #include /* for DELAY */ #include #include #include #include #include #define PN_USEIOSPACE /* #define PN_BACKGROUND_AUTONEG */ #define PN_RX_BUG_WAR #include #ifndef lint static const char rcsid[] = - "$Id: if_pn.c,v 1.50 1999/04/14 18:52:02 wpaul Exp $"; + "$Id: if_pn.c,v 1.16 1999/04/14 19:40:07 wpaul Exp $"; +#endif + +#ifdef __alpha__ +#undef vtophys +#define vtophys(va) (pmap_kextract(((vm_offset_t) (va))) \ + + 1*1024*1024*1024) #endif /* * Various supported device vendors/types and their names. */ static struct pn_type pn_devs[] = { { PN_VENDORID, PN_DEVICEID_PNIC, "82c168 PNIC 10/100BaseTX" }, { PN_VENDORID, PN_DEVICEID_PNIC, "82c169 PNIC 10/100BaseTX" }, { PN_VENDORID, PN_DEVICEID_PNIC_II, "82c115 PNIC II 10/100BaseTX" }, { 0, 0, NULL } }; /* * Various supported PHY vendors/types and their names. Note that * this driver will work with pretty much any MII-compliant PHY, * so failure to positively identify the chip is not a fatal error. */ static struct pn_type pn_phys[] = { { TI_PHY_VENDORID, TI_PHY_10BT, "" }, { TI_PHY_VENDORID, TI_PHY_100VGPMI, "" }, { NS_PHY_VENDORID, NS_PHY_83840A, ""}, { LEVEL1_PHY_VENDORID, LEVEL1_PHY_LXT970, "" }, { INTEL_PHY_VENDORID, INTEL_PHY_82555, "" }, { SEEQ_PHY_VENDORID, SEEQ_PHY_80220, "" }, { 0, 0, "" } }; static unsigned long pn_count = 0; static const char *pn_probe __P((pcici_t, pcidi_t)); static void pn_attach __P((pcici_t, int)); static int pn_newbuf __P((struct pn_softc *, struct pn_chain_onefrag *)); static int pn_encap __P((struct pn_softc *, struct pn_chain *, struct mbuf *)); #ifdef PN_RX_BUG_WAR static void pn_rx_bug_war __P((struct pn_softc *, struct pn_chain_onefrag *)); #endif static void pn_rxeof __P((struct pn_softc *)); static void pn_rxeoc __P((struct pn_softc *)); static void pn_txeof __P((struct pn_softc *)); static void pn_txeoc __P((struct pn_softc *)); static void pn_intr __P((void *)); static void pn_start __P((struct ifnet *)); static int pn_ioctl __P((struct ifnet *, u_long, caddr_t)); static void pn_init __P((void *)); static void pn_stop __P((struct pn_softc *)); static void pn_watchdog __P((struct ifnet *)); static void pn_shutdown __P((int, void *)); static int pn_ifmedia_upd __P((struct ifnet *)); static void pn_ifmedia_sts __P((struct ifnet *, struct ifmediareq *)); static void pn_eeprom_getword __P((struct pn_softc *, u_int8_t, u_int16_t *)); static void pn_read_eeprom __P((struct pn_softc *, caddr_t, int, int, int)); static u_int16_t pn_phy_readreg __P((struct pn_softc *, int)); static void pn_phy_writereg __P((struct pn_softc *, u_int16_t, u_int16_t)); static void pn_autoneg_xmit __P((struct pn_softc *)); static void pn_autoneg_mii __P((struct pn_softc *, int, int)); static void pn_setmode_mii __P((struct pn_softc *, int)); static void pn_getmode_mii __P((struct pn_softc *)); static void pn_autoneg __P((struct pn_softc *, int, int)); static void pn_setmode __P((struct pn_softc *, int)); static void pn_setcfg __P((struct pn_softc *, u_int32_t)); static u_int32_t pn_calchash __P((u_int8_t *)); static void pn_setfilt __P((struct pn_softc *)); static void pn_reset __P((struct pn_softc *)); static int pn_list_rx_init __P((struct pn_softc *)); static int pn_list_tx_init __P((struct pn_softc *)); #define PN_SETBIT(sc, reg, x) \ CSR_WRITE_4(sc, reg, \ CSR_READ_4(sc, reg) | (x)) #define PN_CLRBIT(sc, reg, x) \ CSR_WRITE_4(sc, reg, \ CSR_READ_4(sc, reg) & ~(x)) /* * Read a word of data stored in the EEPROM at address 'addr.' */ static void pn_eeprom_getword(sc, addr, dest) struct pn_softc *sc; u_int8_t addr; u_int16_t *dest; { register int i; u_int32_t r; CSR_WRITE_4(sc, PN_SIOCTL, PN_EE_READ|addr); for (i = 0; i < PN_TIMEOUT; i++) { DELAY(1); r = CSR_READ_4(sc, PN_SIO); if (!(r & PN_SIO_BUSY)) { *dest = (u_int16_t)(r & 0x0000FFFF); return; } } return; } /* * Read a sequence of words from the EEPROM. */ static void pn_read_eeprom(sc, dest, off, cnt, swap) struct pn_softc *sc; caddr_t dest; int off; int cnt; int swap; { int i; u_int16_t word = 0, *ptr; for (i = 0; i < cnt; i++) { pn_eeprom_getword(sc, off + i, &word); ptr = (u_int16_t *)(dest + (i * 2)); if (swap) *ptr = ntohs(word); else *ptr = word; } return; } static u_int16_t pn_phy_readreg(sc, reg) struct pn_softc *sc; int reg; { int i; u_int32_t rval; CSR_WRITE_4(sc, PN_MII, PN_MII_READ | (sc->pn_phy_addr << 23) | (reg << 18)); for (i = 0; i < PN_TIMEOUT; i++) { DELAY(1); rval = CSR_READ_4(sc, PN_MII); if (!(rval & PN_MII_BUSY)) { if ((u_int16_t)(rval & 0x0000FFFF) == 0xFFFF) return(0); else return((u_int16_t)(rval & 0x0000FFFF)); } } return(0); } static void pn_phy_writereg(sc, reg, data) struct pn_softc *sc; u_int16_t reg; u_int16_t data; { int i; CSR_WRITE_4(sc, PN_MII, PN_MII_WRITE | (sc->pn_phy_addr << 23) | (reg << 18) | data); for (i = 0; i < PN_TIMEOUT; i++) { if (!(CSR_READ_4(sc, PN_MII) & PN_MII_BUSY)) break; } return; } #define PN_POLY 0xEDB88320 #define PN_BITS 9 static u_int32_t pn_calchash(addr) u_int8_t *addr; { u_int32_t idx, bit, data, crc; /* Compute CRC for the address value. */ crc = 0xFFFFFFFF; /* initial value */ for (idx = 0; idx < 6; idx++) { for (data = *addr++, bit = 0; bit < 8; bit++, data >>= 1) crc = (crc >> 1) ^ (((crc ^ data) & 1) ? PN_POLY : 0); } return (crc & ((1 << PN_BITS) - 1)); } /* * Initiate an autonegotiation session. */ static void pn_autoneg_xmit(sc) struct pn_softc *sc; { u_int16_t phy_sts; pn_phy_writereg(sc, PHY_BMCR, PHY_BMCR_RESET); DELAY(500); while(pn_phy_readreg(sc, PHY_BMCR) & PHY_BMCR_RESET); phy_sts = pn_phy_readreg(sc, PHY_BMCR); phy_sts |= PHY_BMCR_AUTONEGENBL|PHY_BMCR_AUTONEGRSTR; pn_phy_writereg(sc, PHY_BMCR, phy_sts); return; } /* * Invoke autonegotiation on a PHY. */ static void pn_autoneg_mii(sc, flag, verbose) struct pn_softc *sc; int flag; int verbose; { u_int16_t phy_sts = 0, media, advert, ability; struct ifnet *ifp; struct ifmedia *ifm; ifm = &sc->ifmedia; ifp = &sc->arpcom.ac_if; ifm->ifm_media = IFM_ETHER | IFM_AUTO; /* * The 100baseT4 PHY on the 3c905-T4 has the 'autoneg supported' * bit cleared in the status register, but has the 'autoneg enabled' * bit set in the control register. This is a contradiction, and * I'm not sure how to handle it. If you want to force an attempt * to autoneg for 100baseT4 PHYs, #define FORCE_AUTONEG_TFOUR * and see what happens. */ #ifndef FORCE_AUTONEG_TFOUR /* * First, see if autoneg is supported. If not, there's * no point in continuing. */ phy_sts = pn_phy_readreg(sc, PHY_BMSR); if (!(phy_sts & PHY_BMSR_CANAUTONEG)) { if (verbose) printf("pn%d: autonegotiation not supported\n", sc->pn_unit); ifm->ifm_media = IFM_ETHER|IFM_10_T|IFM_HDX; return; } #endif switch (flag) { case PN_FLAG_FORCEDELAY: /* * XXX Never use this option anywhere but in the probe * routine: making the kernel stop dead in its tracks * for three whole seconds after we've gone multi-user * is really bad manners. */ pn_autoneg_xmit(sc); DELAY(5000000); break; case PN_FLAG_SCHEDDELAY: /* * Wait for the transmitter to go idle before starting * an autoneg session, otherwise pn_start() may clobber * our timeout, and we don't want to allow transmission * during an autoneg session since that can screw it up. */ if (sc->pn_cdata.pn_tx_head != NULL) { sc->pn_want_auto = 1; return; } pn_autoneg_xmit(sc); ifp->if_timer = 5; sc->pn_autoneg = 1; sc->pn_want_auto = 0; return; break; case PN_FLAG_DELAYTIMEO: ifp->if_timer = 0; sc->pn_autoneg = 0; break; default: printf("pn%d: invalid autoneg flag: %d\n", sc->pn_unit, flag); return; } if (pn_phy_readreg(sc, PHY_BMSR) & PHY_BMSR_AUTONEGCOMP) { if (verbose) printf("pn%d: autoneg complete, ", sc->pn_unit); phy_sts = pn_phy_readreg(sc, PHY_BMSR); } else { if (verbose) printf("pn%d: autoneg not complete, ", sc->pn_unit); } media = pn_phy_readreg(sc, PHY_BMCR); /* Link is good. Report modes and set duplex mode. */ if (pn_phy_readreg(sc, PHY_BMSR) & PHY_BMSR_LINKSTAT) { if (verbose) printf("link status good "); advert = pn_phy_readreg(sc, PHY_ANAR); ability = pn_phy_readreg(sc, PHY_LPAR); if (advert & PHY_ANAR_100BT4 && ability & PHY_ANAR_100BT4) { ifm->ifm_media = IFM_ETHER|IFM_100_T4; media |= PHY_BMCR_SPEEDSEL; media &= ~PHY_BMCR_DUPLEX; printf("(100baseT4)\n"); } else if (advert & PHY_ANAR_100BTXFULL && ability & PHY_ANAR_100BTXFULL) { ifm->ifm_media = IFM_ETHER|IFM_100_TX|IFM_FDX; media |= PHY_BMCR_SPEEDSEL; media |= PHY_BMCR_DUPLEX; printf("(full-duplex, 100Mbps)\n"); } else if (advert & PHY_ANAR_100BTXHALF && ability & PHY_ANAR_100BTXHALF) { ifm->ifm_media = IFM_ETHER|IFM_100_TX|IFM_HDX; media |= PHY_BMCR_SPEEDSEL; media &= ~PHY_BMCR_DUPLEX; printf("(half-duplex, 100Mbps)\n"); } else if (advert & PHY_ANAR_10BTFULL && ability & PHY_ANAR_10BTFULL) { ifm->ifm_media = IFM_ETHER|IFM_10_T|IFM_FDX; media &= ~PHY_BMCR_SPEEDSEL; media |= PHY_BMCR_DUPLEX; printf("(full-duplex, 10Mbps)\n"); } else if (advert & PHY_ANAR_10BTHALF && ability & PHY_ANAR_10BTHALF) { ifm->ifm_media = IFM_ETHER|IFM_10_T|IFM_HDX; media &= ~PHY_BMCR_SPEEDSEL; media &= ~PHY_BMCR_DUPLEX; printf("(half-duplex, 10Mbps)\n"); } media &= ~PHY_BMCR_AUTONEGENBL; /* Set ASIC's duplex mode to match the PHY. */ pn_setcfg(sc, ifm->ifm_media); pn_phy_writereg(sc, PHY_BMCR, media); } else { if (verbose) printf("no carrier\n"); } pn_init(sc); if (sc->pn_tx_pend) { sc->pn_autoneg = 0; sc->pn_tx_pend = 0; pn_start(ifp); } return; } static void pn_getmode_mii(sc) struct pn_softc *sc; { u_int16_t bmsr; struct ifnet *ifp; ifp = &sc->arpcom.ac_if; bmsr = pn_phy_readreg(sc, PHY_BMSR); if (bootverbose) printf("pn%d: PHY status word: %x\n", sc->pn_unit, bmsr); /* fallback */ sc->ifmedia.ifm_media = IFM_ETHER|IFM_10_T|IFM_HDX; if (bmsr & PHY_BMSR_10BTHALF) { if (bootverbose) printf("pn%d: 10Mbps half-duplex mode supported\n", sc->pn_unit); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_HDX, 0, NULL); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T, 0, NULL); } if (bmsr & PHY_BMSR_10BTFULL) { if (bootverbose) printf("pn%d: 10Mbps full-duplex mode supported\n", sc->pn_unit); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL); sc->ifmedia.ifm_media = IFM_ETHER|IFM_10_T|IFM_FDX; } if (bmsr & PHY_BMSR_100BTXHALF) { if (bootverbose) printf("pn%d: 100Mbps half-duplex mode supported\n", sc->pn_unit); ifp->if_baudrate = 100000000; ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_TX, 0, NULL); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_TX|IFM_HDX, 0, NULL); sc->ifmedia.ifm_media = IFM_ETHER|IFM_100_TX|IFM_HDX; } if (bmsr & PHY_BMSR_100BTXFULL) { if (bootverbose) printf("pn%d: 100Mbps full-duplex mode supported\n", sc->pn_unit); ifp->if_baudrate = 100000000; ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_TX|IFM_FDX, 0, NULL); sc->ifmedia.ifm_media = IFM_ETHER|IFM_100_TX|IFM_FDX; } /* Some also support 100BaseT4. */ if (bmsr & PHY_BMSR_100BT4) { if (bootverbose) printf("pn%d: 100baseT4 mode supported\n", sc->pn_unit); ifp->if_baudrate = 100000000; ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_T4, 0, NULL); sc->ifmedia.ifm_media = IFM_ETHER|IFM_100_T4; #ifdef FORCE_AUTONEG_TFOUR if (bootverbose) printf("pn%d: forcing on autoneg support for BT4\n", sc->pn_unit); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_AUTO, 0 NULL): sc->ifmedia.ifm_media = IFM_ETHER|IFM_AUTO; #endif } if (bmsr & PHY_BMSR_CANAUTONEG) { if (bootverbose) printf("pn%d: autoneg supported\n", sc->pn_unit); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_AUTO, 0, NULL); sc->ifmedia.ifm_media = IFM_ETHER|IFM_AUTO; } return; } static void pn_autoneg(sc, flag, verbose) struct pn_softc *sc; int flag; int verbose; { u_int32_t nway = 0, ability; struct ifnet *ifp; struct ifmedia *ifm; ifm = &sc->ifmedia; ifp = &sc->arpcom.ac_if; ifm->ifm_media = IFM_ETHER | IFM_AUTO; switch (flag) { case PN_FLAG_FORCEDELAY: /* * XXX Never use this option anywhere but in the probe * routine: making the kernel stop dead in its tracks * for three whole seconds after we've gone multi-user * is really bad manners. */ CSR_WRITE_4(sc, PN_GEN, PN_GEN_MUSTBEONE|PN_GEN_100TX_LOOP); PN_CLRBIT(sc, PN_NWAY, PN_NWAY_AUTONEGRSTR); PN_SETBIT(sc, PN_NWAY, PN_NWAY_AUTOENB); DELAY(5000000); break; case PN_FLAG_SCHEDDELAY: /* * Wait for the transmitter to go idle before starting * an autoneg session, otherwise pn_start() may clobber * our timeout, and we don't want to allow transmission * during an autoneg session since that can screw it up. */ if (sc->pn_cdata.pn_tx_head != NULL) { sc->pn_want_auto = 1; return; } CSR_WRITE_4(sc, PN_GEN, PN_GEN_MUSTBEONE|PN_GEN_100TX_LOOP); PN_CLRBIT(sc, PN_NWAY, PN_NWAY_AUTONEGRSTR); PN_SETBIT(sc, PN_NWAY, PN_NWAY_AUTOENB); ifp->if_timer = 5; sc->pn_autoneg = 1; sc->pn_want_auto = 0; return; break; case PN_FLAG_DELAYTIMEO: ifp->if_timer = 0; sc->pn_autoneg = 0; break; default: printf("pn%d: invalid autoneg flag: %d\n", sc->pn_unit, flag); return; } if (CSR_READ_4(sc, PN_NWAY) & PN_NWAY_LPAR) { if (verbose) printf("pn%d: autoneg complete, ", sc->pn_unit); } else { if (verbose) printf("pn%d: autoneg not complete, ", sc->pn_unit); } /* Link is good. Report modes and set duplex mode. */ if (CSR_READ_4(sc, PN_ISR) & PN_ISR_LINKPASS) { if (verbose) printf("link status good "); ability = CSR_READ_4(sc, PN_NWAY); if (ability & PN_NWAY_LPAR100T4) { ifm->ifm_media = IFM_ETHER|IFM_100_T4; nway = PN_NWAY_MODE_100T4; printf("(100baseT4)\n"); } else if (ability & PN_NWAY_LPAR100FULL) { ifm->ifm_media = IFM_ETHER|IFM_100_TX|IFM_FDX; nway = PN_NWAY_MODE_100FD; printf("(full-duplex, 100Mbps)\n"); } else if (ability & PN_NWAY_LPAR100HALF) { ifm->ifm_media = IFM_ETHER|IFM_100_TX|IFM_HDX; nway = PN_NWAY_MODE_100HD; printf("(half-duplex, 100Mbps)\n"); } else if (ability & PN_NWAY_LPAR10FULL) { ifm->ifm_media = IFM_ETHER|IFM_10_T|IFM_FDX; nway = PN_NWAY_MODE_10FD; printf("(full-duplex, 10Mbps)\n"); } else if (ability & PN_NWAY_LPAR10HALF) { ifm->ifm_media = IFM_ETHER|IFM_10_T|IFM_HDX; nway = PN_NWAY_MODE_10HD; printf("(half-duplex, 10Mbps)\n"); } /* Set ASIC's duplex mode to match the PHY. */ pn_setcfg(sc, ifm->ifm_media); CSR_WRITE_4(sc, PN_NWAY, nway); } else { if (verbose) printf("no carrier\n"); } pn_init(sc); if (sc->pn_tx_pend) { sc->pn_autoneg = 0; sc->pn_tx_pend = 0; pn_start(ifp); } return; } static void pn_setmode(sc, media) struct pn_softc *sc; int media; { struct ifnet *ifp; ifp = &sc->arpcom.ac_if; /* * If an autoneg session is in progress, stop it. */ if (sc->pn_autoneg) { printf("pn%d: canceling autoneg session\n", sc->pn_unit); ifp->if_timer = sc->pn_autoneg = sc->pn_want_auto = 0; PN_CLRBIT(sc, PN_NWAY, PN_NWAY_AUTONEGRSTR); } printf("pn%d: selecting NWAY, ", sc->pn_unit); if (IFM_SUBTYPE(media) == IFM_100_T4) { printf("100Mbps/T4, half-duplex\n"); } if (IFM_SUBTYPE(media) == IFM_100_TX) { printf("100Mbps, "); } if (IFM_SUBTYPE(media) == IFM_10_T) { printf("10Mbps, "); } if ((media & IFM_GMASK) == IFM_FDX) { printf("full duplex\n"); } else { printf("half duplex\n"); } pn_setcfg(sc, media); return; } static void pn_setmode_mii(sc, media) struct pn_softc *sc; int media; { u_int16_t bmcr; struct ifnet *ifp; ifp = &sc->arpcom.ac_if; /* * If an autoneg session is in progress, stop it. */ if (sc->pn_autoneg) { printf("pn%d: canceling autoneg session\n", sc->pn_unit); ifp->if_timer = sc->pn_autoneg = sc->pn_want_auto = 0; bmcr = pn_phy_readreg(sc, PHY_BMCR); bmcr &= ~PHY_BMCR_AUTONEGENBL; pn_phy_writereg(sc, PHY_BMCR, bmcr); } printf("pn%d: selecting MII, ", sc->pn_unit); bmcr = pn_phy_readreg(sc, PHY_BMCR); bmcr &= ~(PHY_BMCR_AUTONEGENBL|PHY_BMCR_SPEEDSEL| PHY_BMCR_DUPLEX|PHY_BMCR_LOOPBK); if (IFM_SUBTYPE(media) == IFM_100_T4) { printf("100Mbps/T4, half-duplex\n"); bmcr |= PHY_BMCR_SPEEDSEL; bmcr &= ~PHY_BMCR_DUPLEX; } if (IFM_SUBTYPE(media) == IFM_100_TX) { printf("100Mbps, "); bmcr |= PHY_BMCR_SPEEDSEL; } if (IFM_SUBTYPE(media) == IFM_10_T) { printf("10Mbps, "); bmcr &= ~PHY_BMCR_SPEEDSEL; } if ((media & IFM_GMASK) == IFM_FDX) { printf("full duplex\n"); bmcr |= PHY_BMCR_DUPLEX; } else { printf("half duplex\n"); bmcr &= ~PHY_BMCR_DUPLEX; } pn_setcfg(sc, media); pn_phy_writereg(sc, PHY_BMCR, bmcr); return; } /* * Programming the receiver filter on the tulip/PNIC is gross. You * have to construct a special setup frame and download it to the * chip via the transmit DMA engine. This routine is also somewhat * gross, as the setup frame is sent synchronously rather than putting * on the transmit queue. The transmitter has to be stopped, then we * can download the frame and wait for the 'owned' bit to clear. * * We always program the chip using 'hash perfect' mode, i.e. one perfect * address (our node address) and a 512-bit hash filter for multicast * frames. We also sneak the broadcast address into the hash filter since * we need that too. */ void pn_setfilt(sc) struct pn_softc *sc; { struct pn_desc *sframe; u_int32_t h, *sp; struct ifmultiaddr *ifma; struct ifnet *ifp; int i; ifp = &sc->arpcom.ac_if; PN_CLRBIT(sc, PN_NETCFG, PN_NETCFG_TX_ON); PN_SETBIT(sc, PN_ISR, PN_ISR_TX_IDLE); sframe = &sc->pn_cdata.pn_sframe; sp = (u_int32_t *)&sc->pn_cdata.pn_sbuf; bzero((char *)sp, PN_SFRAME_LEN); sframe->pn_status = PN_TXSTAT_OWN; sframe->pn_next = vtophys(&sc->pn_ldata->pn_tx_list[0]); sframe->pn_data = vtophys(&sc->pn_cdata.pn_sbuf); sframe->pn_ctl = PN_SFRAME_LEN | PN_TXCTL_TLINK | PN_TXCTL_SETUP | PN_FILTER_HASHPERF; /* If we want promiscuous mode, set the allframes bit. */ if (ifp->if_flags & IFF_PROMISC) PN_SETBIT(sc, PN_NETCFG, PN_NETCFG_RX_PROMISC); else PN_CLRBIT(sc, PN_NETCFG, PN_NETCFG_RX_PROMISC); if (ifp->if_flags & IFF_ALLMULTI) PN_SETBIT(sc, PN_NETCFG, PN_NETCFG_RX_ALLMULTI); for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL; ifma = ifma->ifma_link.le_next) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; h = pn_calchash(LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); sp[h >> 4] |= 1 << (h & 0xF); } if (ifp->if_flags & IFF_BROADCAST) { h = pn_calchash(etherbroadcastaddr); sp[h >> 4] |= 1 << (h & 0xF); } sp[39] = ((u_int16_t *)sc->arpcom.ac_enaddr)[0]; sp[40] = ((u_int16_t *)sc->arpcom.ac_enaddr)[1]; sp[41] = ((u_int16_t *)sc->arpcom.ac_enaddr)[2]; CSR_WRITE_4(sc, PN_TXADDR, vtophys(sframe)); PN_SETBIT(sc, PN_NETCFG, PN_NETCFG_TX_ON); CSR_WRITE_4(sc, PN_TXSTART, 0xFFFFFFFF); /* * Wait for chip to clear the 'own' bit. */ for (i = 0; i < PN_TIMEOUT; i++) { DELAY(10); if (sframe->pn_status != PN_TXSTAT_OWN) break; } if (i == PN_TIMEOUT) printf("pn%d: failed to send setup frame\n", sc->pn_unit); PN_SETBIT(sc, PN_ISR, PN_ISR_TX_NOBUF|PN_ISR_TX_IDLE); return; } /* * In order to fiddle with the * 'full-duplex' and '100Mbps' bits in the netconfig register, we * first have to put the transmit and/or receive logic in the idle state. */ static void pn_setcfg(sc, media) struct pn_softc *sc; u_int32_t media; { int i, restart = 0; if (CSR_READ_4(sc, PN_NETCFG) & (PN_NETCFG_TX_ON|PN_NETCFG_RX_ON)) { restart = 1; PN_CLRBIT(sc, PN_NETCFG, (PN_NETCFG_TX_ON|PN_NETCFG_RX_ON)); for (i = 0; i < PN_TIMEOUT; i++) { DELAY(10); if ((CSR_READ_4(sc, PN_ISR) & PN_ISR_TX_IDLE) && (CSR_READ_4(sc, PN_ISR) & PN_ISR_RX_IDLE)) break; } if (i == PN_TIMEOUT) printf("pn%d: failed to force tx and " "rx to idle state\n", sc->pn_unit); } if (IFM_SUBTYPE(media) == IFM_100_TX) { PN_CLRBIT(sc, PN_NETCFG, PN_NETCFG_SPEEDSEL); if (sc->pn_pinfo == NULL) { CSR_WRITE_4(sc, PN_GEN, PN_GEN_MUSTBEONE| PN_GEN_SPEEDSEL|PN_GEN_100TX_LOOP); PN_SETBIT(sc, PN_NETCFG, PN_NETCFG_PCS| PN_NETCFG_SCRAMBLER|PN_NETCFG_MIIENB); PN_SETBIT(sc, PN_NWAY, PN_NWAY_SPEEDSEL); } } else { PN_SETBIT(sc, PN_NETCFG, PN_NETCFG_SPEEDSEL); if (sc->pn_pinfo == NULL) { CSR_WRITE_4(sc, PN_GEN, PN_GEN_MUSTBEONE|PN_GEN_100TX_LOOP); PN_CLRBIT(sc, PN_NETCFG, PN_NETCFG_PCS| PN_NETCFG_SCRAMBLER|PN_NETCFG_MIIENB); PN_CLRBIT(sc, PN_NWAY, PN_NWAY_SPEEDSEL); } } if ((media & IFM_GMASK) == IFM_FDX) { PN_SETBIT(sc, PN_NETCFG, PN_NETCFG_FULLDUPLEX); if (sc->pn_pinfo == NULL) PN_SETBIT(sc, PN_NWAY, PN_NWAY_DUPLEX); } else { PN_CLRBIT(sc, PN_NETCFG, PN_NETCFG_FULLDUPLEX); if (sc->pn_pinfo == NULL) PN_CLRBIT(sc, PN_NWAY, PN_NWAY_DUPLEX); } if (restart) PN_SETBIT(sc, PN_NETCFG, PN_NETCFG_TX_ON|PN_NETCFG_RX_ON); return; } static void pn_reset(sc) struct pn_softc *sc; { register int i; PN_SETBIT(sc, PN_BUSCTL, PN_BUSCTL_RESET); for (i = 0; i < PN_TIMEOUT; i++) { DELAY(10); if (!(CSR_READ_4(sc, PN_BUSCTL) & PN_BUSCTL_RESET)) break; } if (i == PN_TIMEOUT) printf("pn%d: reset never completed!\n", sc->pn_unit); /* Wait a little while for the chip to get its brains in order. */ DELAY(1000); return; } /* * Probe for a Lite-On PNIC chip. Check the PCI vendor and device * IDs against our list and return a device name if we find a match. */ static const char * pn_probe(config_id, device_id) pcici_t config_id; pcidi_t device_id; { struct pn_type *t; u_int32_t rev; t = pn_devs; while(t->pn_name != NULL) { if ((device_id & 0xFFFF) == t->pn_vid && ((device_id >> 16) & 0xFFFF) == t->pn_did) { if (t->pn_did == PN_DEVICEID_PNIC) { rev = pci_conf_read(config_id, PN_PCI_REVISION) & 0xFF; switch(rev & PN_REVMASK) { case PN_REVID_82C168: return(t->pn_name); break; case PN_REVID_82C169: t++; return(t->pn_name); default: printf("unknown PNIC rev: %x\n", rev); break; } } return(t->pn_name); } t++; } return(NULL); } /* * Attach the interface. Allocate softc structures, do ifmedia * setup and ethernet/BPF attach. */ static void pn_attach(config_id, unit) pcici_t config_id; int unit; { int s, i; #ifndef PN_USEIOSPACE vm_offset_t pbase, vbase; #endif u_char eaddr[ETHER_ADDR_LEN]; u_int32_t command; struct pn_softc *sc; struct ifnet *ifp; int media = IFM_ETHER|IFM_100_TX|IFM_FDX; unsigned int round; caddr_t roundptr; struct pn_type *p; u_int16_t phy_vid, phy_did, phy_sts; #ifdef PN_RX_BUG_WAR u_int32_t revision = 0; #endif s = splimp(); sc = malloc(sizeof(struct pn_softc), M_DEVBUF, M_NOWAIT); if (sc == NULL) { printf("pn%d: no memory for softc struct!\n", unit); return; } bzero(sc, sizeof(struct pn_softc)); /* * Handle power management nonsense. */ command = pci_conf_read(config_id, PN_PCI_CAPID) & 0x000000FF; if (command == 0x01) { command = pci_conf_read(config_id, PN_PCI_PWRMGMTCTRL); if (command & PN_PSTATE_MASK) { u_int32_t iobase, membase, irq; /* Save important PCI config data. */ iobase = pci_conf_read(config_id, PN_PCI_LOIO); membase = pci_conf_read(config_id, PN_PCI_LOMEM); irq = pci_conf_read(config_id, PN_PCI_INTLINE); /* Reset the power state. */ printf("pn%d: chip is in D%d power mode " "-- setting to D0\n", unit, command & PN_PSTATE_MASK); command &= 0xFFFFFFFC; pci_conf_write(config_id, PN_PCI_PWRMGMTCTRL, command); /* Restore PCI config data. */ pci_conf_write(config_id, PN_PCI_LOIO, iobase); pci_conf_write(config_id, PN_PCI_LOMEM, membase); pci_conf_write(config_id, PN_PCI_INTLINE, irq); } } /* * Map control/status registers. */ command = pci_conf_read(config_id, PCI_COMMAND_STATUS_REG); command |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); pci_conf_write(config_id, PCI_COMMAND_STATUS_REG, command); command = pci_conf_read(config_id, PCI_COMMAND_STATUS_REG); #ifdef PN_USEIOSPACE if (!(command & PCIM_CMD_PORTEN)) { printf("pn%d: failed to enable I/O ports!\n", unit); free(sc, M_DEVBUF); goto fail; } if (!pci_map_port(config_id, PN_PCI_LOIO, (u_short *)&(sc->pn_bhandle))) { printf ("pn%d: couldn't map ports\n", unit); goto fail; } #ifdef __i386__ sc->pn_btag = I386_BUS_SPACE_IO; #endif #ifdef __alpha__ sc->pn_btag = ALPHA_BUS_SPACE_IO; #endif #else if (!(command & PCIM_CMD_MEMEN)) { printf("pn%d: failed to enable memory mapping!\n", unit); goto fail; } if (!pci_map_mem(config_id, PN_PCI_LOMEM, &vbase, &pbase)) { printf ("pn%d: couldn't map memory\n", unit); goto fail; } sc->pn_bhandle = vbase; #ifdef __i386__ sc->pn_btag = I386_BUS_SPACE_MEM; #endif #ifdef __alpha__ sc->pn_btag = ALPHA_BUS_SPACE_MEM; #endif #endif /* Allocate interrupt */ if (!pci_map_int(config_id, pn_intr, sc, &net_imask)) { printf("pn%d: couldn't map interrupt\n", unit); goto fail; } /* Save the cache line size. */ sc->pn_cachesize = pci_conf_read(config_id, PN_PCI_CACHELEN) & 0xFF; /* Reset the adapter. */ pn_reset(sc); /* * Get station address from the EEPROM. */ pn_read_eeprom(sc, (caddr_t)&eaddr, 0, 3, 1); /* * A PNIC chip was detected. Inform the world. */ printf("pn%d: Ethernet address: %6D\n", unit, eaddr, ":"); sc->pn_unit = unit; bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); sc->pn_ldata_ptr = malloc(sizeof(struct pn_list_data) + 8, M_DEVBUF, M_NOWAIT); if (sc->pn_ldata_ptr == NULL) { free(sc, M_DEVBUF); printf("pn%d: no memory for list buffers!\n", unit); goto fail; } sc->pn_ldata = (struct pn_list_data *)sc->pn_ldata_ptr; round = (unsigned int)sc->pn_ldata_ptr & 0xF; roundptr = sc->pn_ldata_ptr; for (i = 0; i < 8; i++) { if (round % 8) { round++; roundptr++; } else break; } sc->pn_ldata = (struct pn_list_data *)roundptr; bzero(sc->pn_ldata, sizeof(struct pn_list_data)); #ifdef PN_RX_BUG_WAR revision = pci_conf_read(config_id, PN_PCI_REVISION) & 0x000000FF; if (revision == PN_169B_REV || revision == PN_169_REV || (revision & 0xF0) == PN_168_REV) { sc->pn_rx_war = 1; sc->pn_rx_buf = malloc(PN_RXLEN * 5, M_DEVBUF, M_NOWAIT); if (sc->pn_rx_buf == NULL) { printf("pn%d: no memory for workaround buffer\n", unit); goto fail; } } else { sc->pn_rx_war = 0; } #endif ifp = &sc->arpcom.ac_if; ifp->if_softc = sc; ifp->if_unit = unit; ifp->if_name = "pn"; ifp->if_mtu = ETHERMTU; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = pn_ioctl; ifp->if_output = ether_output; ifp->if_start = pn_start; ifp->if_watchdog = pn_watchdog; ifp->if_init = pn_init; ifp->if_baudrate = 10000000; ifp->if_snd.ifq_maxlen = PN_TX_LIST_CNT - 1; ifmedia_init(&sc->ifmedia, 0, pn_ifmedia_upd, pn_ifmedia_sts); if (bootverbose) printf("pn%d: probing for a PHY\n", sc->pn_unit); for (i = PN_PHYADDR_MIN; i < PN_PHYADDR_MAX + 1; i++) { if (bootverbose) printf("pn%d: checking address: %d\n", sc->pn_unit, i); sc->pn_phy_addr = i; pn_phy_writereg(sc, PHY_BMCR, PHY_BMCR_RESET); DELAY(500); while(pn_phy_readreg(sc, PHY_BMCR) & PHY_BMCR_RESET); if ((phy_sts = pn_phy_readreg(sc, PHY_BMSR))) break; } if (phy_sts) { phy_vid = pn_phy_readreg(sc, PHY_VENID); phy_did = pn_phy_readreg(sc, PHY_DEVID); if (bootverbose) printf("pn%d: found PHY at address %d, ", sc->pn_unit, sc->pn_phy_addr); if (bootverbose) printf("vendor id: %x device id: %x\n", phy_vid, phy_did); p = pn_phys; while(p->pn_vid) { if (phy_vid == p->pn_vid && (phy_did | 0x000F) == p->pn_did) { sc->pn_pinfo = p; break; } p++; } if (sc->pn_pinfo == NULL) sc->pn_pinfo = &pn_phys[PHY_UNKNOWN]; if (bootverbose) printf("pn%d: PHY type: %s\n", sc->pn_unit, sc->pn_pinfo->pn_name); pn_getmode_mii(sc); pn_autoneg_mii(sc, PN_FLAG_FORCEDELAY, 1); } else { ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T, 0, NULL); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_HDX, 0, NULL); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_TX, 0, NULL); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_TX|IFM_HDX, 0, NULL); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_TX|IFM_FDX, 0, NULL); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_T4, 0, NULL); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_AUTO, 0, NULL); pn_autoneg(sc, PN_FLAG_FORCEDELAY, 1); } media = sc->ifmedia.ifm_media; pn_stop(sc); ifmedia_set(&sc->ifmedia, media); /* * Call MI attach routines. */ if_attach(ifp); ether_ifattach(ifp); #if NBPFILTER > 0 bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); #endif at_shutdown(pn_shutdown, sc, SHUTDOWN_POST_SYNC); fail: splx(s); return; } /* * Initialize the transmit descriptors. */ static int pn_list_tx_init(sc) struct pn_softc *sc; { struct pn_chain_data *cd; struct pn_list_data *ld; int i; cd = &sc->pn_cdata; ld = sc->pn_ldata; for (i = 0; i < PN_TX_LIST_CNT; i++) { cd->pn_tx_chain[i].pn_ptr = &ld->pn_tx_list[i]; if (i == (PN_TX_LIST_CNT - 1)) cd->pn_tx_chain[i].pn_nextdesc = &cd->pn_tx_chain[0]; else cd->pn_tx_chain[i].pn_nextdesc = &cd->pn_tx_chain[i + 1]; } cd->pn_tx_free = &cd->pn_tx_chain[0]; cd->pn_tx_tail = cd->pn_tx_head = NULL; return(0); } /* * Initialize the RX descriptors and allocate mbufs for them. Note that * we arrange the descriptors in a closed ring, so that the last descriptor * points back to the first. */ static int pn_list_rx_init(sc) struct pn_softc *sc; { struct pn_chain_data *cd; struct pn_list_data *ld; int i; cd = &sc->pn_cdata; ld = sc->pn_ldata; for (i = 0; i < PN_RX_LIST_CNT; i++) { cd->pn_rx_chain[i].pn_ptr = (struct pn_desc *)&ld->pn_rx_list[i]; if (pn_newbuf(sc, &cd->pn_rx_chain[i]) == ENOBUFS) return(ENOBUFS); if (i == (PN_RX_LIST_CNT - 1)) { cd->pn_rx_chain[i].pn_nextdesc = &cd->pn_rx_chain[0]; ld->pn_rx_list[i].pn_next = vtophys(&ld->pn_rx_list[0]); } else { cd->pn_rx_chain[i].pn_nextdesc = &cd->pn_rx_chain[i + 1]; ld->pn_rx_list[i].pn_next = vtophys(&ld->pn_rx_list[i + 1]); } } cd->pn_rx_head = &cd->pn_rx_chain[0]; return(0); } /* * Initialize an RX descriptor and attach an MBUF cluster. * Note: the length fields are only 11 bits wide, which means the * largest size we can specify is 2047. This is important because * MCLBYTES is 2048, so we have to subtract one otherwise we'll * overflow the field and make a mess. */ static int pn_newbuf(sc, c) struct pn_softc *sc; struct pn_chain_onefrag *c; { struct mbuf *m_new = NULL; MGETHDR(m_new, M_DONTWAIT, MT_DATA); if (m_new == NULL) { printf("pn%d: no memory for rx list -- packet dropped!\n", sc->pn_unit); return(ENOBUFS); } MCLGET(m_new, M_DONTWAIT); if (!(m_new->m_flags & M_EXT)) { printf("pn%d: no memory for rx list -- packet dropped!\n", sc->pn_unit); m_freem(m_new); return(ENOBUFS); } /* * Zero the buffer. This is part of the workaround for the * promiscuous mode bug in the revision 33 PNIC chips. */ bzero((char *)mtod(m_new, char *), MCLBYTES); m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; c->pn_mbuf = m_new; c->pn_ptr->pn_status = PN_RXSTAT; c->pn_ptr->pn_data = vtophys(mtod(m_new, caddr_t)); c->pn_ptr->pn_ctl = PN_RXCTL_RLINK | PN_RXLEN; return(0); } #ifdef PN_RX_BUG_WAR /* * Grrrrr. * The PNIC chip has a terrible bug in it that manifests itself during * periods of heavy activity. The exact mode of failure if difficult to * pinpoint: sometimes it only happens in promiscuous mode, sometimes it * will happen on slow machines. The bug is that sometimes instead of * uploading one complete frame during reception, it uploads what looks * like the entire contents of its FIFO memory. The frame we want is at * the end of the whole mess, but we never know exactly how much data has * been uploaded, so salvaging the frame is hard. * * There is only one way to do it reliably, and it's disgusting. * Here's what we know: * * - We know there will always be somewhere between one and three extra * descriptors uploaded. * * - We know the desired received frame will always be at the end of the * total data upload. * * - We know the size of the desired received frame because it will be * provided in the length field of the status word in the last descriptor. * * Here's what we do: * * - When we allocate buffers for the receive ring, we bzero() them. * This means that we know that the buffer contents should be all * zeros, except for data uploaded by the chip. * * - We also force the PNIC chip to upload frames that include the * ethernet CRC at the end. * * - We gather all of the bogus frame data into a single buffer. * * - We then position a pointer at the end of this buffer and scan * backwards until we encounter the first non-zero byte of data. * This is the end of the received frame. We know we will encounter * some data at the end of the frame because the CRC will always be * there, so even if the sender transmits a packet of all zeros, * we won't be fooled. * * - We know the size of the actual received frame, so we subtract * that value from the current pointer location. This brings us * to the start of the actual received packet. * * - We copy this into an mbuf and pass it on, along with the actual * frame length. * * The performance hit is tremendous, but it beats dropping frames all * the time. */ #define PN_WHOLEFRAME (PN_RXSTAT_FIRSTFRAG|PN_RXSTAT_LASTFRAG) static void pn_rx_bug_war(sc, cur_rx) struct pn_softc *sc; struct pn_chain_onefrag *cur_rx; { struct pn_chain_onefrag *c; unsigned char *ptr; int total_len; u_int32_t rxstat = 0; c = sc->pn_rx_bug_save; ptr = sc->pn_rx_buf; bzero(ptr, sizeof(PN_RXLEN * 5)); /* Copy all the bytes from the bogus buffers. */ while ((c->pn_ptr->pn_status & PN_WHOLEFRAME) != PN_WHOLEFRAME) { rxstat = c->pn_ptr->pn_status; m_copydata(c->pn_mbuf, 0, PN_RXLEN, ptr); ptr += PN_RXLEN - 2; /* round down to 32-bit boundary */ if (c == cur_rx) break; if (rxstat & PN_RXSTAT_LASTFRAG) break; c->pn_ptr->pn_status = PN_RXSTAT; c->pn_ptr->pn_ctl = PN_RXCTL_RLINK | PN_RXLEN; bzero((char *)mtod(c->pn_mbuf, char *), MCLBYTES); c = c->pn_nextdesc; } /* Find the length of the actual receive frame. */ total_len = PN_RXBYTES(rxstat); /* Scan backwards until we hit a non-zero byte. */ while(*ptr == 0x00) ptr--; /* Round off. */ if ((u_int32_t)(ptr) & 0x3) ptr -= 1; /* Now find the start of the frame. */ ptr -= total_len; if (ptr < sc->pn_rx_buf) ptr = sc->pn_rx_buf; /* * Now copy the salvaged frame to the last mbuf and fake up * the status word to make it look like a successful * frame reception. */ m_copyback(cur_rx->pn_mbuf, 0, total_len, ptr); cur_rx->pn_mbuf->m_len = c->pn_mbuf->m_pkthdr.len = MCLBYTES; cur_rx->pn_ptr->pn_status |= PN_RXSTAT_FIRSTFRAG; return; } #endif /* * A frame has been uploaded: pass the resulting mbuf chain up to * the higher level protocols. */ static void pn_rxeof(sc) struct pn_softc *sc; { struct ether_header *eh; struct mbuf *m; struct ifnet *ifp; struct pn_chain_onefrag *cur_rx; int total_len = 0; u_int32_t rxstat; ifp = &sc->arpcom.ac_if; while(!((rxstat = sc->pn_cdata.pn_rx_head->pn_ptr->pn_status) & PN_RXSTAT_OWN)) { #ifdef __alpha__ struct mbuf *m0 = NULL; #endif cur_rx = sc->pn_cdata.pn_rx_head; sc->pn_cdata.pn_rx_head = cur_rx->pn_nextdesc; #ifdef PN_RX_BUG_WAR /* * XXX The PNIC has a nasty receiver bug that manifests * under certain conditions (sometimes only in promiscuous * mode, sometimes only on slow machines even when not in * promiscuous mode). We have to keep an eye out for the * failure condition and employ a workaround to recover * any mangled frames. */ if (sc->pn_rx_war) { if ((rxstat & PN_WHOLEFRAME) != PN_WHOLEFRAME) { if (rxstat & PN_RXSTAT_FIRSTFRAG) sc->pn_rx_bug_save = cur_rx; if ((rxstat & PN_RXSTAT_LASTFRAG) == 0) continue; pn_rx_bug_war(sc, cur_rx); rxstat = cur_rx->pn_ptr->pn_status; } } #endif /* * If an error occurs, update stats, clear the * status word and leave the mbuf cluster in place: * it should simply get re-used next time this descriptor * comes up in the ring. */ if (rxstat & PN_RXSTAT_RXERR) { ifp->if_ierrors++; if (rxstat & PN_RXSTAT_COLLSEEN) ifp->if_collisions++; cur_rx->pn_ptr->pn_status = PN_RXSTAT; cur_rx->pn_ptr->pn_ctl = PN_RXCTL_RLINK | PN_RXLEN; bzero((char *)mtod(cur_rx->pn_mbuf, char *), MCLBYTES); continue; } /* No errors; receive the packet. */ m = cur_rx->pn_mbuf; total_len = PN_RXBYTES(cur_rx->pn_ptr->pn_status); /* Trim off the CRC. */ total_len -= ETHER_CRC_LEN; /* * Try to conjure up a new mbuf cluster. If that * fails, it means we have an out of memory condition and * should leave the buffer in place and continue. This will * result in a lost packet, but there's little else we * can do in this situation. */ if (pn_newbuf(sc, cur_rx) == ENOBUFS) { ifp->if_ierrors++; cur_rx->pn_ptr->pn_status = PN_RXSTAT; cur_rx->pn_ptr->pn_ctl = PN_RXCTL_RLINK | PN_RXLEN; bzero((char *)mtod(cur_rx->pn_mbuf, char *), MCLBYTES); continue; } #ifdef __alpha__ /* * Grrrr! On the alpha platform, the start of the * packet data must be longword aligned so that ip_input() * doesn't perform any unaligned accesses when it tries * to fiddle with the IP header. But the PNIC is stupid * and wants RX buffers to start on longword boundaries. * So we can't just shift the DMA address over a few * bytes to alter the payload alignment. Instead, we * have to chop out ethernet and IP header parts of * the packet and place then in a separate mbuf with * the alignment fixed up properly. * * As if this chip wasn't broken enough already. */ MGETHDR(m0, M_DONTWAIT, MT_DATA); if (m0 == NULL) { ifp->if_ierrors++; cur_rx->pn_ptr->pn_status = PN_RXSTAT; cur_rx->pn_ptr->pn_ctl = PN_RXCTL_RLINK | PN_RXLEN; bzero((char *)mtod(cur_rx->pn_mbuf, char *), MCLBYTES); continue; } m0->m_data += 2; if (total_len <= (MHLEN - 2)) { bcopy(mtod(m, caddr_t), mtod(m0, caddr_t), total_len); m_freem(m); m = m0; m->m_pkthdr.len = m->m_len = total_len; } else { bcopy(mtod(m, caddr_t), mtod(m0, caddr_t), (MHLEN - 2)); m->m_len = total_len - (MHLEN - 2); m->m_data += (MHLEN - 2); m0->m_next = m; m0->m_len = (MHLEN - 2); m = m0; m->m_pkthdr.len = total_len; } #else m->m_pkthdr.len = m->m_len = total_len; #endif ifp->if_ipackets++; eh = mtod(m, struct ether_header *); m->m_pkthdr.rcvif = ifp; #if NBPFILTER > 0 /* * Handle BPF listeners. Let the BPF user see the packet, but * don't pass it up to the ether_input() layer unless it's * a broadcast packet, multicast packet, matches our ethernet * address or the interface is in promiscuous mode. */ if (ifp->if_bpf) { bpf_mtap(ifp, m); if (ifp->if_flags & IFF_PROMISC && (bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr, ETHER_ADDR_LEN) && (eh->ether_dhost[0] & 1) == 0)) { m_freem(m); continue; } } #endif /* Remove header from mbuf and pass it on. */ m_adj(m, sizeof(struct ether_header)); ether_input(ifp, eh, m); } return; } void pn_rxeoc(sc) struct pn_softc *sc; { pn_rxeof(sc); PN_CLRBIT(sc, PN_NETCFG, PN_NETCFG_RX_ON); CSR_WRITE_4(sc, PN_RXADDR, vtophys(sc->pn_cdata.pn_rx_head->pn_ptr)); PN_SETBIT(sc, PN_NETCFG, PN_NETCFG_RX_ON); CSR_WRITE_4(sc, PN_RXSTART, 0xFFFFFFFF); return; } /* * A frame was downloaded to the chip. It's safe for us to clean up * the list buffers. */ static void pn_txeof(sc) struct pn_softc *sc; { struct pn_chain *cur_tx; struct ifnet *ifp; ifp = &sc->arpcom.ac_if; /* Clear the timeout timer. */ ifp->if_timer = 0; if (sc->pn_cdata.pn_tx_head == NULL) return; /* * Go through our tx list and free mbufs for those * frames that have been transmitted. */ while(sc->pn_cdata.pn_tx_head->pn_mbuf != NULL) { u_int32_t txstat; cur_tx = sc->pn_cdata.pn_tx_head; txstat = PN_TXSTATUS(cur_tx); if (txstat & PN_TXSTAT_OWN) break; if (txstat & PN_TXSTAT_ERRSUM) { ifp->if_oerrors++; if (txstat & PN_TXSTAT_EXCESSCOLL) ifp->if_collisions++; if (txstat & PN_TXSTAT_LATECOLL) ifp->if_collisions++; } ifp->if_collisions += (txstat & PN_TXSTAT_COLLCNT) >> 3; ifp->if_opackets++; m_freem(cur_tx->pn_mbuf); cur_tx->pn_mbuf = NULL; if (sc->pn_cdata.pn_tx_head == sc->pn_cdata.pn_tx_tail) { sc->pn_cdata.pn_tx_head = NULL; sc->pn_cdata.pn_tx_tail = NULL; break; } sc->pn_cdata.pn_tx_head = cur_tx->pn_nextdesc; } return; } /* * TX 'end of channel' interrupt handler. */ static void pn_txeoc(sc) struct pn_softc *sc; { struct ifnet *ifp; ifp = &sc->arpcom.ac_if; ifp->if_timer = 0; if (sc->pn_cdata.pn_tx_head == NULL) { ifp->if_flags &= ~IFF_OACTIVE; sc->pn_cdata.pn_tx_tail = NULL; if (sc->pn_want_auto) { if (sc->pn_pinfo == NULL) pn_autoneg(sc, PN_FLAG_SCHEDDELAY, 1); else pn_autoneg_mii(sc, PN_FLAG_SCHEDDELAY, 1); } } return; } static void pn_intr(arg) void *arg; { struct pn_softc *sc; struct ifnet *ifp; u_int32_t status; sc = arg; ifp = &sc->arpcom.ac_if; /* Supress unwanted interrupts. */ if (!(ifp->if_flags & IFF_UP)) { pn_stop(sc); return; } /* Disable interrupts. */ CSR_WRITE_4(sc, PN_IMR, 0x00000000); for (;;) { status = CSR_READ_4(sc, PN_ISR); if (status) CSR_WRITE_4(sc, PN_ISR, status); if ((status & PN_INTRS) == 0) break; if (status & PN_ISR_RX_OK) pn_rxeof(sc); if ((status & PN_ISR_RX_WATCHDOG) || (status & PN_ISR_RX_IDLE) || (status & PN_ISR_RX_NOBUF)) pn_rxeoc(sc); if (status & PN_ISR_TX_OK) pn_txeof(sc); if (status & PN_ISR_TX_NOBUF) pn_txeoc(sc); if (status & PN_ISR_TX_IDLE) { pn_txeof(sc); if (sc->pn_cdata.pn_tx_head != NULL) { PN_SETBIT(sc, PN_NETCFG, PN_NETCFG_TX_ON); CSR_WRITE_4(sc, PN_TXSTART, 0xFFFFFFFF); } } if (status & PN_ISR_TX_UNDERRUN) { ifp->if_oerrors++; pn_txeof(sc); if (sc->pn_cdata.pn_tx_head != NULL) { PN_SETBIT(sc, PN_NETCFG, PN_NETCFG_TX_ON); CSR_WRITE_4(sc, PN_TXSTART, 0xFFFFFFFF); } } if (status & PN_ISR_BUS_ERR) { pn_reset(sc); pn_init(sc); } } /* Re-enable interrupts. */ CSR_WRITE_4(sc, PN_IMR, PN_INTRS); if (ifp->if_snd.ifq_head != NULL) { pn_start(ifp); } return; } /* * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data * pointers to the fragment pointers. */ static int pn_encap(sc, c, m_head) struct pn_softc *sc; struct pn_chain *c; struct mbuf *m_head; { int frag = 0; struct pn_desc *f = NULL; int total_len; struct mbuf *m; /* * Start packing the mbufs in this chain into * the fragment pointers. Stop when we run out * of fragments or hit the end of the mbuf chain. */ m = m_head; total_len = 0; for (m = m_head, frag = 0; m != NULL; m = m->m_next) { if (m->m_len != 0) { if (frag == PN_MAXFRAGS) break; total_len += m->m_len; f = &c->pn_ptr->pn_frag[frag]; f->pn_ctl = PN_TXCTL_TLINK | m->m_len; if (frag == 0) { f->pn_ctl |= PN_TXCTL_FIRSTFRAG; f->pn_status = 0; } else f->pn_status = PN_TXSTAT_OWN; f->pn_data = vtophys(mtod(m, vm_offset_t)); f->pn_next = vtophys(&c->pn_ptr->pn_frag[frag + 1]); frag++; } } /* * Handle special case: we used up all 16 fragments, * but we have more mbufs left in the chain. Copy the * data into an mbuf cluster. Note that we don't * bother clearing the values in the other fragment * pointers/counters; it wouldn't gain us anything, * and would waste cycles. */ if (m != NULL) { struct mbuf *m_new = NULL; MGETHDR(m_new, M_DONTWAIT, MT_DATA); if (m_new == NULL) { printf("pn%d: no memory for tx list", sc->pn_unit); return(1); } if (m_head->m_pkthdr.len > MHLEN) { MCLGET(m_new, M_DONTWAIT); if (!(m_new->m_flags & M_EXT)) { m_freem(m_new); printf("pn%d: no memory for tx list", sc->pn_unit); return(1); } } m_copydata(m_head, 0, m_head->m_pkthdr.len, mtod(m_new, caddr_t)); m_new->m_pkthdr.len = m_new->m_len = m_head->m_pkthdr.len; m_freem(m_head); m_head = m_new; f = &c->pn_ptr->pn_frag[0]; f->pn_data = vtophys(mtod(m_new, caddr_t)); f->pn_ctl = total_len = m_new->m_len; f->pn_ctl |= PN_TXCTL_TLINK|PN_TXCTL_FIRSTFRAG; frag = 1; } c->pn_mbuf = m_head; c->pn_lastdesc = frag - 1; PN_TXCTL(c) |= PN_TXCTL_LASTFRAG|PN_TXCTL_FINT; PN_TXNEXT(c) = vtophys(&c->pn_nextdesc->pn_ptr->pn_frag[0]); return(0); } /* * Main transmit routine. To avoid having to do mbuf copies, we put pointers * to the mbuf data regions directly in the transmit lists. We also save a * copy of the pointers since the transmit list fragment pointers are * physical addresses. */ static void pn_start(ifp) struct ifnet *ifp; { struct pn_softc *sc; struct mbuf *m_head = NULL; struct pn_chain *cur_tx = NULL, *start_tx; sc = ifp->if_softc; if (sc->pn_autoneg) { sc->pn_tx_pend = 1; return; } /* * Check for an available queue slot. If there are none, * punt. */ if (sc->pn_cdata.pn_tx_free->pn_mbuf != NULL) { ifp->if_flags |= IFF_OACTIVE; return; } start_tx = sc->pn_cdata.pn_tx_free; while(sc->pn_cdata.pn_tx_free->pn_mbuf == NULL) { IF_DEQUEUE(&ifp->if_snd, m_head); if (m_head == NULL) break; /* Pick a descriptor off the free list. */ cur_tx = sc->pn_cdata.pn_tx_free; sc->pn_cdata.pn_tx_free = cur_tx->pn_nextdesc; /* Pack the data into the descriptor. */ pn_encap(sc, cur_tx, m_head); if (cur_tx != start_tx) PN_TXOWN(cur_tx) = PN_TXSTAT_OWN; #if NBPFILTER > 0 /* * If there's a BPF listener, bounce a copy of this frame * to him. */ if (ifp->if_bpf) bpf_mtap(ifp, cur_tx->pn_mbuf); #endif PN_TXOWN(cur_tx) = PN_TXSTAT_OWN; CSR_WRITE_4(sc, PN_TXSTART, 0xFFFFFFFF); } /* * If there are no packets queued, bail. */ if (cur_tx == NULL) return; sc->pn_cdata.pn_tx_tail = cur_tx; if (sc->pn_cdata.pn_tx_head == NULL) sc->pn_cdata.pn_tx_head = start_tx; /* * Set a timeout in case the chip goes out to lunch. */ ifp->if_timer = 5; return; } static void pn_init(xsc) void *xsc; { struct pn_softc *sc = xsc; struct ifnet *ifp = &sc->arpcom.ac_if; u_int16_t phy_bmcr = 0; int s; if (sc->pn_autoneg) return; s = splimp(); if (sc->pn_pinfo != NULL) phy_bmcr = pn_phy_readreg(sc, PHY_BMCR); /* * Cancel pending I/O and free all RX/TX buffers. */ pn_stop(sc); pn_reset(sc); /* * Set cache alignment and burst length. */ CSR_WRITE_4(sc, PN_BUSCTL, PN_BUSCTL_MUSTBEONE|PN_BUSCTL_ARBITRATION); PN_SETBIT(sc, PN_BUSCTL, PN_BURSTLEN_16LONG); switch(sc->pn_cachesize) { case 32: PN_SETBIT(sc, PN_BUSCTL, PN_CACHEALIGN_32LONG); break; case 16: PN_SETBIT(sc, PN_BUSCTL, PN_CACHEALIGN_16LONG); break; case 8: PN_SETBIT(sc, PN_BUSCTL, PN_CACHEALIGN_8LONG); break; case 0: default: PN_SETBIT(sc, PN_BUSCTL, PN_CACHEALIGN_NONE); break; } PN_CLRBIT(sc, PN_NETCFG, PN_NETCFG_TX_IMMEDIATE); PN_CLRBIT(sc, PN_NETCFG, PN_NETCFG_NO_RXCRC); PN_CLRBIT(sc, PN_NETCFG, PN_NETCFG_HEARTBEAT); PN_CLRBIT(sc, PN_NETCFG, PN_NETCFG_STORENFWD); PN_CLRBIT(sc, PN_NETCFG, PN_NETCFG_TX_BACKOFF); PN_CLRBIT(sc, PN_NETCFG, PN_NETCFG_TX_THRESH); PN_SETBIT(sc, PN_NETCFG, PN_TXTHRESH_72BYTES); if (sc->pn_pinfo == NULL) { PN_CLRBIT(sc, PN_NETCFG, PN_NETCFG_MIIENB); PN_SETBIT(sc, PN_NETCFG, PN_NETCFG_TX_BACKOFF); } else { PN_SETBIT(sc, PN_NETCFG, PN_NETCFG_MIIENB); PN_SETBIT(sc, PN_ENDEC, PN_ENDEC_JABBERDIS); } pn_setcfg(sc, sc->ifmedia.ifm_media); /* Init circular RX list. */ if (pn_list_rx_init(sc) == ENOBUFS) { printf("pn%d: initialization failed: no " "memory for rx buffers\n", sc->pn_unit); pn_stop(sc); (void)splx(s); return; } /* * Init tx descriptors. */ pn_list_tx_init(sc); /* * Load the address of the RX list. */ CSR_WRITE_4(sc, PN_RXADDR, vtophys(sc->pn_cdata.pn_rx_head->pn_ptr)); /* * Load the RX/multicast filter. */ pn_setfilt(sc); /* * Enable interrupts. */ CSR_WRITE_4(sc, PN_IMR, PN_INTRS); CSR_WRITE_4(sc, PN_ISR, 0xFFFFFFFF); /* Enable receiver and transmitter. */ PN_SETBIT(sc, PN_NETCFG, PN_NETCFG_TX_ON|PN_NETCFG_RX_ON); CSR_WRITE_4(sc, PN_RXSTART, 0xFFFFFFFF); /* Restore state of BMCR */ if (sc->pn_pinfo != NULL) pn_phy_writereg(sc, PHY_BMCR, phy_bmcr); ifp->if_flags |= IFF_RUNNING; ifp->if_flags &= ~IFF_OACTIVE; (void)splx(s); return; } /* * Set media options. */ static int pn_ifmedia_upd(ifp) struct ifnet *ifp; { struct pn_softc *sc; struct ifmedia *ifm; sc = ifp->if_softc; ifm = &sc->ifmedia; if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER) return(EINVAL); if (IFM_SUBTYPE(ifm->ifm_media) == IFM_AUTO) { if (sc->pn_pinfo == NULL) pn_autoneg(sc, PN_FLAG_SCHEDDELAY, 1); else pn_autoneg_mii(sc, PN_FLAG_SCHEDDELAY, 1); } else { if (sc->pn_pinfo == NULL) pn_setmode(sc, ifm->ifm_media); else pn_setmode_mii(sc, ifm->ifm_media); } return(0); } /* * Report current media status. */ static void pn_ifmedia_sts(ifp, ifmr) struct ifnet *ifp; struct ifmediareq *ifmr; { struct pn_softc *sc; u_int16_t advert = 0, ability = 0; sc = ifp->if_softc; ifmr->ifm_active = IFM_ETHER; if (sc->pn_pinfo == NULL) { if (CSR_READ_4(sc, PN_NETCFG) & PN_NETCFG_SPEEDSEL) ifmr->ifm_active = IFM_ETHER|IFM_10_T; else ifmr->ifm_active = IFM_ETHER|IFM_100_TX; if (CSR_READ_4(sc, PN_NETCFG) & PN_NETCFG_FULLDUPLEX) ifmr->ifm_active |= IFM_FDX; else ifmr->ifm_active |= IFM_HDX; return; } if (!(pn_phy_readreg(sc, PHY_BMCR) & PHY_BMCR_AUTONEGENBL)) { if (pn_phy_readreg(sc, PHY_BMCR) & PHY_BMCR_SPEEDSEL) ifmr->ifm_active = IFM_ETHER|IFM_100_TX; else ifmr->ifm_active = IFM_ETHER|IFM_10_T; if (pn_phy_readreg(sc, PHY_BMCR) & PHY_BMCR_DUPLEX) ifmr->ifm_active |= IFM_FDX; else ifmr->ifm_active |= IFM_HDX; return; } ability = pn_phy_readreg(sc, PHY_LPAR); advert = pn_phy_readreg(sc, PHY_ANAR); if (advert & PHY_ANAR_100BT4 && ability & PHY_ANAR_100BT4) { ifmr->ifm_active = IFM_ETHER|IFM_100_T4; } else if (advert & PHY_ANAR_100BTXFULL && ability & PHY_ANAR_100BTXFULL) { ifmr->ifm_active = IFM_ETHER|IFM_100_TX|IFM_FDX; } else if (advert & PHY_ANAR_100BTXHALF && ability & PHY_ANAR_100BTXHALF) { ifmr->ifm_active = IFM_ETHER|IFM_100_TX|IFM_HDX; } else if (advert & PHY_ANAR_10BTFULL && ability & PHY_ANAR_10BTFULL) { ifmr->ifm_active = IFM_ETHER|IFM_10_T|IFM_FDX; } else if (advert & PHY_ANAR_10BTHALF && ability & PHY_ANAR_10BTHALF) { ifmr->ifm_active = IFM_ETHER|IFM_10_T|IFM_HDX; } return; } static int pn_ioctl(ifp, command, data) struct ifnet *ifp; u_long command; caddr_t data; { struct pn_softc *sc = ifp->if_softc; struct ifreq *ifr = (struct ifreq *) data; int s, error = 0; s = splimp(); switch(command) { case SIOCSIFADDR: case SIOCGIFADDR: case SIOCSIFMTU: error = ether_ioctl(ifp, command, data); break; case SIOCSIFFLAGS: if (ifp->if_flags & IFF_UP) { pn_init(sc); } else { if (ifp->if_flags & IFF_RUNNING) pn_stop(sc); } error = 0; break; case SIOCADDMULTI: case SIOCDELMULTI: pn_init(sc); error = 0; break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: error = ifmedia_ioctl(ifp, ifr, &sc->ifmedia, command); break; default: error = EINVAL; break; } (void)splx(s); return(error); } static void pn_watchdog(ifp) struct ifnet *ifp; { struct pn_softc *sc; sc = ifp->if_softc; if (sc->pn_autoneg) { if (sc->pn_pinfo == NULL) pn_autoneg(sc, PN_FLAG_DELAYTIMEO, 1); else pn_autoneg_mii(sc, PN_FLAG_DELAYTIMEO, 1); return; } ifp->if_oerrors++; printf("pn%d: watchdog timeout\n", sc->pn_unit); if (!(pn_phy_readreg(sc, PHY_BMSR) & PHY_BMSR_LINKSTAT)) printf("pn%d: no carrier - transceiver cable problem?\n", sc->pn_unit); pn_stop(sc); pn_reset(sc); pn_init(sc); if (ifp->if_snd.ifq_head != NULL) pn_start(ifp); return; } /* * Stop the adapter and free any mbufs allocated to the * RX and TX lists. */ static void pn_stop(sc) struct pn_softc *sc; { register int i; struct ifnet *ifp; ifp = &sc->arpcom.ac_if; ifp->if_timer = 0; PN_CLRBIT(sc, PN_NETCFG, (PN_NETCFG_RX_ON|PN_NETCFG_TX_ON)); CSR_WRITE_4(sc, PN_IMR, 0x00000000); CSR_WRITE_4(sc, PN_TXADDR, 0x00000000); CSR_WRITE_4(sc, PN_RXADDR, 0x00000000); /* * Free data in the RX lists. */ for (i = 0; i < PN_RX_LIST_CNT; i++) { if (sc->pn_cdata.pn_rx_chain[i].pn_mbuf != NULL) { m_freem(sc->pn_cdata.pn_rx_chain[i].pn_mbuf); sc->pn_cdata.pn_rx_chain[i].pn_mbuf = NULL; } } bzero((char *)&sc->pn_ldata->pn_rx_list, sizeof(sc->pn_ldata->pn_rx_list)); /* * Free the TX list buffers. */ for (i = 0; i < PN_TX_LIST_CNT; i++) { if (sc->pn_cdata.pn_tx_chain[i].pn_mbuf != NULL) { m_freem(sc->pn_cdata.pn_tx_chain[i].pn_mbuf); sc->pn_cdata.pn_tx_chain[i].pn_mbuf = NULL; } } bzero((char *)&sc->pn_ldata->pn_tx_list, sizeof(sc->pn_ldata->pn_tx_list)); ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); return; } /* * Stop all chip I/O so that the kernel's probe routines don't * get confused by errant DMAs when rebooting. */ static void pn_shutdown(howto, arg) int howto; void *arg; { struct pn_softc *sc = (struct pn_softc *)arg; pn_stop(sc); return; } static struct pci_device pn_device = { "pn", pn_probe, pn_attach, &pn_count, NULL }; DATA_SET(pcidevice_set, pn_device); Index: head/sys/pci/if_xl.c =================================================================== --- head/sys/pci/if_xl.c (revision 45719) +++ head/sys/pci/if_xl.c (revision 45720) @@ -1,2884 +1,2890 @@ /* * Copyright (c) 1997, 1998 * Bill Paul . 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 Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD * 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. * - * $Id: if_xl.c,v 1.30 1999/04/15 03:18:33 wpaul Exp $ + * $Id: if_xl.c,v 1.31 1999/04/16 01:56:06 ghelmer Exp $ */ /* * 3Com 3c90x Etherlink XL PCI NIC driver * * Supports the 3Com "boomerang" and "cyclone" PCI * bus-master chips (3c90x cards and embedded controllers) including * the following: * * 3Com 3c900-TPO 10Mbps/RJ-45 * 3Com 3c900-COMBO 10Mbps/RJ-45,AUI,BNC * 3Com 3c905-TX 10/100Mbps/RJ-45 * 3Com 3c905-T4 10/100Mbps/RJ-45 * 3Com 3c900B-TPO 10Mbps/RJ-45 * 3Com 3c900B-COMBO 10Mbps/RJ-45,AUI,BNC * 3Com 3c900B-TPC 10Mbps/RJ-45,BNC * 3Com 3c905B-COMBO 10/100Mbps/RJ-45,AUI,BNC * 3Com 3c905B-TX 10/100Mbps/RJ-45 * 3Com 3c905B-FL/FX 10/100Mbps/Fiber-optic * 3Com 3c980-TX 10/100Mbps server adapter * 3Com 3cSOHO100-TX 10/100Mbps/RJ-45 * Dell Optiplex GX1 on-board 3c918 10/100Mbps/RJ-45 * Dell Precision on-board 3c905B 10/100Mbps/RJ-45 * Dell Latitude laptop docking station embedded 3c905-TX * * Written by Bill Paul * Electrical Engineering Department * Columbia University, New York City */ /* * The 3c90x series chips use a bus-master DMA interface for transfering * packets to and from the controller chip. Some of the "vortex" cards * (3c59x) also supported a bus master mode, however for those chips * you could only DMA packets to/from a contiguous memory buffer. For * transmission this would mean copying the contents of the queued mbuf * chain into a an mbuf cluster and then DMAing the cluster. This extra * copy would sort of defeat the purpose of the bus master support for * any packet that doesn't fit into a single mbuf. * * By contrast, the 3c90x cards support a fragment-based bus master * mode where mbuf chains can be encapsulated using TX descriptors. * This is similar to other PCI chips such as the Texas Instruments * ThunderLAN and the Intel 82557/82558. * * The "vortex" driver (if_vx.c) happens to work for the "boomerang" * bus master chips because they maintain the old PIO interface for * backwards compatibility, but starting with the 3c905B and the * "cyclone" chips, the compatibility interface has been dropped. * Since using bus master DMA is a big win, we use this driver to * support the PCI "boomerang" chips even though they work with the * "vortex" driver in order to obtain better performance. * * This driver is in the /sys/pci directory because it only supports * PCI-based NICs. */ #include "bpfilter.h" #include #include #include #include #include #include #include #include #include #include #include #include #if NBPFILTER > 0 #include #endif #include "opt_bdg.h" #ifdef BRIDGE #include #endif #include /* for vtophys */ #include /* for vtophys */ #include /* for DELAY */ #include #include #include #include #include +#ifdef __alpha__ +#undef vtophys +#define vtophys(va) (pmap_kextract(((vm_offset_t) (va))) \ + + 1*1024*1024*1024) +#endif + /* * The following #define causes the code to use PIO to access the * chip's registers instead of memory mapped mode. The reason PIO mode * is on by default is that the Etherlink XL manual seems to indicate * that only the newer revision chips (3c905B) support both PIO and * memory mapped access. Since we want to be compatible with the older * bus master chips, we use PIO here. If you comment this out, the * driver will use memory mapped I/O, which may be faster but which * might not work on some devices. */ #define XL_USEIOSPACE /* * This #define controls the behavior of autonegotiation during the * bootstrap phase. It's possible to have the driver initiate an * autonegotiation session and then set a timeout which will cause the * autoneg results to be polled later, usually once the kernel has * finished booting. This is clever and all, but it can have bad side * effects in some cases, particularly where NFS is involved. For * example, if we're booting diskless with an NFS rootfs, the network * interface has to be up and running before we hit the mountroot() * code, otherwise mounting the rootfs will fail and we'll probably * panic. * * Consequently, the 'backgrounded' autoneg behavior is turned off * by default and we actually sit and wait 5 seconds for autonegotiation * to complete before proceeding with the other device probes. If you * choose to use the other behavior, you can uncomment this #define and * recompile. */ /* #define XL_BACKGROUND_AUTONEG */ #include #if !defined(lint) static const char rcsid[] = - "$Id: if_xl.c,v 1.30 1999/04/15 03:18:33 wpaul Exp $"; + "$Id: if_xl.c,v 1.31 1999/04/16 01:56:06 ghelmer Exp $"; #endif /* * Various supported device vendors/types and their names. */ static struct xl_type xl_devs[] = { { TC_VENDORID, TC_DEVICEID_BOOMERANG_10BT, "3Com 3c900-TPO Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_BOOMERANG_10BT_COMBO, "3Com 3c900-COMBO Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_BOOMERANG_10_100BT, "3Com 3c905-TX Fast Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_BOOMERANG_100BT4, "3Com 3c905-T4 Fast Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_CYCLONE_10BT, "3Com 3c900B-TPO Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_CYCLONE_10BT_COMBO, "3Com 3c900B-COMBO Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_CYCLONE_10BT_TPC, "3Com 3c900B-TPC Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_CYCLONE_10FL, "3Com 3c900B-FL Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_CYCLONE_10_100BT, "3Com 3c905B-TX Fast Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_CYCLONE_10_100BT4, "3Com 3c905B-T4 Fast Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_CYCLONE_10_100FX, "3Com 3c905B-FX/SC Fast Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_CYCLONE_10_100_COMBO, "3Com 3c905B-COMBO Fast Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_CYCLONE_10_100BT_SERV, "3Com 3c980 Fast Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_HURRICANE_SOHO100TX, "3Com 3cSOHO100-TX OfficeConnect" }, { 0, 0, NULL } }; /* * Various supported PHY vendors/types and their names. Note that * this driver will work with pretty much any MII-compliant PHY, * so failure to positively identify the chip is not a fatal error. */ static struct xl_type xl_phys[] = { { TI_PHY_VENDORID, TI_PHY_10BT, "" }, { TI_PHY_VENDORID, TI_PHY_100VGPMI, "" }, { NS_PHY_VENDORID, NS_PHY_83840A, ""}, { LEVEL1_PHY_VENDORID, LEVEL1_PHY_LXT970, "" }, { INTEL_PHY_VENDORID, INTEL_PHY_82555, "" }, { SEEQ_PHY_VENDORID, SEEQ_PHY_80220, "" }, { 0, 0, "" } }; static unsigned long xl_count = 0; static const char *xl_probe __P((pcici_t, pcidi_t)); static void xl_attach __P((pcici_t, int)); static int xl_newbuf __P((struct xl_softc *, struct xl_chain_onefrag *)); static void xl_stats_update __P((void *)); static int xl_encap __P((struct xl_softc *, struct xl_chain *, struct mbuf * )); static void xl_rxeof __P((struct xl_softc *)); static void xl_txeof __P((struct xl_softc *)); static void xl_txeoc __P((struct xl_softc *)); static void xl_intr __P((void *)); static void xl_start __P((struct ifnet *)); static int xl_ioctl __P((struct ifnet *, u_long, caddr_t)); static void xl_init __P((void *)); static void xl_stop __P((struct xl_softc *)); static void xl_watchdog __P((struct ifnet *)); static void xl_shutdown __P((int, void *)); static int xl_ifmedia_upd __P((struct ifnet *)); static void xl_ifmedia_sts __P((struct ifnet *, struct ifmediareq *)); static int xl_eeprom_wait __P((struct xl_softc *)); static int xl_read_eeprom __P((struct xl_softc *, caddr_t, int, int, int)); static void xl_mii_sync __P((struct xl_softc *)); static void xl_mii_send __P((struct xl_softc *, u_int32_t, int)); static int xl_mii_readreg __P((struct xl_softc *, struct xl_mii_frame *)); static int xl_mii_writereg __P((struct xl_softc *, struct xl_mii_frame *)); static u_int16_t xl_phy_readreg __P((struct xl_softc *, int)); static void xl_phy_writereg __P((struct xl_softc *, int, int)); static void xl_autoneg_xmit __P((struct xl_softc *)); static void xl_autoneg_mii __P((struct xl_softc *, int, int)); static void xl_setmode_mii __P((struct xl_softc *, int)); static void xl_getmode_mii __P((struct xl_softc *)); static void xl_setmode __P((struct xl_softc *, int)); static u_int8_t xl_calchash __P((caddr_t)); static void xl_setmulti __P((struct xl_softc *)); static void xl_setmulti_hash __P((struct xl_softc *)); static void xl_reset __P((struct xl_softc *)); static int xl_list_rx_init __P((struct xl_softc *)); static int xl_list_tx_init __P((struct xl_softc *)); static void xl_wait __P((struct xl_softc *)); static void xl_mediacheck __P((struct xl_softc *)); #ifdef notdef static void xl_testpacket __P((struct xl_softc *)); #endif /* * Murphy's law says that it's possible the chip can wedge and * the 'command in progress' bit may never clear. Hence, we wait * only a finite amount of time to avoid getting caught in an * infinite loop. Normally this delay routine would be a macro, * but it isn't called during normal operation so we can afford * to make it a function. */ static void xl_wait(sc) struct xl_softc *sc; { register int i; for (i = 0; i < XL_TIMEOUT; i++) { if (!(CSR_READ_2(sc, XL_STATUS) & XL_STAT_CMDBUSY)) break; } if (i == XL_TIMEOUT) printf("xl%d: command never completed!\n", sc->xl_unit); return; } /* * MII access routines are provided for adapters with external * PHYs (3c905-TX, 3c905-T4, 3c905B-T4) and those with built-in * autoneg logic that's faked up to look like a PHY (3c905B-TX). * Note: if you don't perform the MDIO operations just right, * it's possible to end up with code that works correctly with * some chips/CPUs/processor speeds/bus speeds/etc but not * with others. */ #define MII_SET(x) \ CSR_WRITE_2(sc, XL_W4_PHY_MGMT, \ CSR_READ_2(sc, XL_W4_PHY_MGMT) | x) #define MII_CLR(x) \ CSR_WRITE_2(sc, XL_W4_PHY_MGMT, \ CSR_READ_2(sc, XL_W4_PHY_MGMT) & ~x) /* * Sync the PHYs by setting data bit and strobing the clock 32 times. */ static void xl_mii_sync(sc) struct xl_softc *sc; { register int i; XL_SEL_WIN(4); MII_SET(XL_MII_DIR|XL_MII_DATA); for (i = 0; i < 32; i++) { MII_SET(XL_MII_CLK); DELAY(1); MII_CLR(XL_MII_CLK); DELAY(1); } return; } /* * Clock a series of bits through the MII. */ static void xl_mii_send(sc, bits, cnt) struct xl_softc *sc; u_int32_t bits; int cnt; { int i; XL_SEL_WIN(4); MII_CLR(XL_MII_CLK); for (i = (0x1 << (cnt - 1)); i; i >>= 1) { if (bits & i) { MII_SET(XL_MII_DATA); } else { MII_CLR(XL_MII_DATA); } DELAY(1); MII_CLR(XL_MII_CLK); DELAY(1); MII_SET(XL_MII_CLK); } } /* * Read an PHY register through the MII. */ static int xl_mii_readreg(sc, frame) struct xl_softc *sc; struct xl_mii_frame *frame; { int i, ack, s; s = splimp(); /* * Set up frame for RX. */ frame->mii_stdelim = XL_MII_STARTDELIM; frame->mii_opcode = XL_MII_READOP; frame->mii_turnaround = 0; frame->mii_data = 0; /* * Select register window 4. */ XL_SEL_WIN(4); CSR_WRITE_2(sc, XL_W4_PHY_MGMT, 0); /* * Turn on data xmit. */ MII_SET(XL_MII_DIR); xl_mii_sync(sc); /* * Send command/address info. */ xl_mii_send(sc, frame->mii_stdelim, 2); xl_mii_send(sc, frame->mii_opcode, 2); xl_mii_send(sc, frame->mii_phyaddr, 5); xl_mii_send(sc, frame->mii_regaddr, 5); /* Idle bit */ MII_CLR((XL_MII_CLK|XL_MII_DATA)); DELAY(1); MII_SET(XL_MII_CLK); DELAY(1); /* Turn off xmit. */ MII_CLR(XL_MII_DIR); /* Check for ack */ MII_CLR(XL_MII_CLK); DELAY(1); MII_SET(XL_MII_CLK); DELAY(1); ack = CSR_READ_2(sc, XL_W4_PHY_MGMT) & XL_MII_DATA; /* * Now try reading data bits. If the ack failed, we still * need to clock through 16 cycles to keep the PHY(s) in sync. */ if (ack) { for(i = 0; i < 16; i++) { MII_CLR(XL_MII_CLK); DELAY(1); MII_SET(XL_MII_CLK); DELAY(1); } goto fail; } for (i = 0x8000; i; i >>= 1) { MII_CLR(XL_MII_CLK); DELAY(1); if (!ack) { if (CSR_READ_2(sc, XL_W4_PHY_MGMT) & XL_MII_DATA) frame->mii_data |= i; DELAY(1); } MII_SET(XL_MII_CLK); DELAY(1); } fail: MII_CLR(XL_MII_CLK); DELAY(1); MII_SET(XL_MII_CLK); DELAY(1); splx(s); if (ack) return(1); return(0); } /* * Write to a PHY register through the MII. */ static int xl_mii_writereg(sc, frame) struct xl_softc *sc; struct xl_mii_frame *frame; { int s; s = splimp(); /* * Set up frame for TX. */ frame->mii_stdelim = XL_MII_STARTDELIM; frame->mii_opcode = XL_MII_WRITEOP; frame->mii_turnaround = XL_MII_TURNAROUND; /* * Select the window 4. */ XL_SEL_WIN(4); /* * Turn on data output. */ MII_SET(XL_MII_DIR); xl_mii_sync(sc); xl_mii_send(sc, frame->mii_stdelim, 2); xl_mii_send(sc, frame->mii_opcode, 2); xl_mii_send(sc, frame->mii_phyaddr, 5); xl_mii_send(sc, frame->mii_regaddr, 5); xl_mii_send(sc, frame->mii_turnaround, 2); xl_mii_send(sc, frame->mii_data, 16); /* Idle bit. */ MII_SET(XL_MII_CLK); DELAY(1); MII_CLR(XL_MII_CLK); DELAY(1); /* * Turn off xmit. */ MII_CLR(XL_MII_DIR); splx(s); return(0); } static u_int16_t xl_phy_readreg(sc, reg) struct xl_softc *sc; int reg; { struct xl_mii_frame frame; bzero((char *)&frame, sizeof(frame)); frame.mii_phyaddr = sc->xl_phy_addr; frame.mii_regaddr = reg; xl_mii_readreg(sc, &frame); return(frame.mii_data); } static void xl_phy_writereg(sc, reg, data) struct xl_softc *sc; int reg; int data; { struct xl_mii_frame frame; bzero((char *)&frame, sizeof(frame)); frame.mii_phyaddr = sc->xl_phy_addr; frame.mii_regaddr = reg; frame.mii_data = data; xl_mii_writereg(sc, &frame); return; } /* * The EEPROM is slow: give it time to come ready after issuing * it a command. */ static int xl_eeprom_wait(sc) struct xl_softc *sc; { int i; for (i = 0; i < 100; i++) { if (CSR_READ_2(sc, XL_W0_EE_CMD) & XL_EE_BUSY) DELAY(162); else break; } if (i == 100) { printf("xl%d: eeprom failed to come ready\n", sc->xl_unit); return(1); } return(0); } /* * Read a sequence of words from the EEPROM. Note that ethernet address * data is stored in the EEPROM in network byte order. */ static int xl_read_eeprom(sc, dest, off, cnt, swap) struct xl_softc *sc; caddr_t dest; int off; int cnt; int swap; { int err = 0, i; u_int16_t word = 0, *ptr; XL_SEL_WIN(0); if (xl_eeprom_wait(sc)) return(1); for (i = 0; i < cnt; i++) { CSR_WRITE_2(sc, XL_W0_EE_CMD, XL_EE_READ | (off + i)); err = xl_eeprom_wait(sc); if (err) break; word = CSR_READ_2(sc, XL_W0_EE_DATA); ptr = (u_int16_t *)(dest + (i * 2)); if (swap) *ptr = ntohs(word); else *ptr = word; } return(err ? 1 : 0); } /* * This routine is taken from the 3Com Etherlink XL manual, * page 10-7. It calculates a CRC of the supplied multicast * group address and returns the lower 8 bits, which are used * as the multicast filter position. * Note: the 3c905B currently only supports a 64-bit hash table, * which means we really only need 6 bits, but the manual indicates * that future chip revisions will have a 256-bit hash table, * hence the routine is set up to calculate 8 bits of position * info in case we need it some day. * Note II, The Sequel: _CURRENT_ versions of the 3c905B have a * 256 bit hash table. This means we have to use all 8 bits regardless. * On older cards, the upper 2 bits will be ignored. Grrrr.... */ static u_int8_t xl_calchash(addr) caddr_t addr; { u_int32_t crc, carry; int i, j; u_int8_t c; /* Compute CRC for the address value. */ crc = 0xFFFFFFFF; /* initial value */ for (i = 0; i < 6; i++) { c = *(addr + i); for (j = 0; j < 8; j++) { carry = ((crc & 0x80000000) ? 1 : 0) ^ (c & 0x01); crc <<= 1; c >>= 1; if (carry) crc = (crc ^ 0x04c11db6) | carry; } } /* return the filter bit position */ return(crc & 0x000000FF); } /* * NICs older than the 3c905B have only one multicast option, which * is to enable reception of all multicast frames. */ static void xl_setmulti(sc) struct xl_softc *sc; { struct ifnet *ifp; struct ifmultiaddr *ifma; u_int8_t rxfilt; int mcnt = 0; ifp = &sc->arpcom.ac_if; XL_SEL_WIN(5); rxfilt = CSR_READ_1(sc, XL_W5_RX_FILTER); if (ifp->if_flags & IFF_ALLMULTI) { rxfilt |= XL_RXFILTER_ALLMULTI; CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt); return; } for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL; ifma = ifma->ifma_link.le_next) mcnt++; if (mcnt) rxfilt |= XL_RXFILTER_ALLMULTI; else rxfilt &= ~XL_RXFILTER_ALLMULTI; CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt); return; } /* * 3c905B adapters have a hash filter that we can program. */ static void xl_setmulti_hash(sc) struct xl_softc *sc; { struct ifnet *ifp; int h = 0, i; struct ifmultiaddr *ifma; u_int8_t rxfilt; int mcnt = 0; ifp = &sc->arpcom.ac_if; XL_SEL_WIN(5); rxfilt = CSR_READ_1(sc, XL_W5_RX_FILTER); if (ifp->if_flags & IFF_ALLMULTI) { rxfilt |= XL_RXFILTER_ALLMULTI; CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt); return; } else rxfilt &= ~XL_RXFILTER_ALLMULTI; /* first, zot all the existing hash bits */ for (i = 0; i < XL_HASHFILT_SIZE; i++) CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_HASH|i); /* now program new ones */ for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL; ifma = ifma->ifma_link.le_next) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; h = xl_calchash(LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_HASH|XL_HASH_SET|h); mcnt++; } if (mcnt) rxfilt |= XL_RXFILTER_MULTIHASH; else rxfilt &= ~XL_RXFILTER_MULTIHASH; CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt); return; } #ifdef notdef static void xl_testpacket(sc) struct xl_softc *sc; { struct mbuf *m; struct ifnet *ifp; ifp = &sc->arpcom.ac_if; MGETHDR(m, M_DONTWAIT, MT_DATA); if (m == NULL) return; bcopy(&sc->arpcom.ac_enaddr, mtod(m, struct ether_header *)->ether_dhost, ETHER_ADDR_LEN); bcopy(&sc->arpcom.ac_enaddr, mtod(m, struct ether_header *)->ether_shost, ETHER_ADDR_LEN); mtod(m, struct ether_header *)->ether_type = htons(3); mtod(m, unsigned char *)[14] = 0; mtod(m, unsigned char *)[15] = 0; mtod(m, unsigned char *)[16] = 0xE3; m->m_len = m->m_pkthdr.len = sizeof(struct ether_header) + 3; IF_ENQUEUE(&ifp->if_snd, m); xl_start(ifp); return; } #endif /* * Initiate an autonegotiation session. */ static void xl_autoneg_xmit(sc) struct xl_softc *sc; { u_int16_t phy_sts; u_int32_t icfg; xl_reset(sc); XL_SEL_WIN(3); icfg = CSR_READ_4(sc, XL_W3_INTERNAL_CFG); icfg &= ~XL_ICFG_CONNECTOR_MASK; if (sc->xl_media & XL_MEDIAOPT_MII || sc->xl_media & XL_MEDIAOPT_BT4) icfg |= (XL_XCVR_MII << XL_ICFG_CONNECTOR_BITS); if (sc->xl_media & XL_MEDIAOPT_BTX) icfg |= (XL_XCVR_AUTO << XL_ICFG_CONNECTOR_BITS); if (sc->xl_media & XL_MEDIAOPT_BFX) icfg |= (XL_XCVR_100BFX << XL_ICFG_CONNECTOR_BITS); CSR_WRITE_4(sc, XL_W3_INTERNAL_CFG, icfg); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_STOP); xl_phy_writereg(sc, PHY_BMCR, PHY_BMCR_RESET); DELAY(500); while(xl_phy_readreg(sc, XL_PHY_GENCTL) & PHY_BMCR_RESET); phy_sts = xl_phy_readreg(sc, PHY_BMCR); phy_sts |= PHY_BMCR_AUTONEGENBL|PHY_BMCR_AUTONEGRSTR; xl_phy_writereg(sc, PHY_BMCR, phy_sts); return; } /* * Invoke autonegotiation on a PHY. Also used with the 3Com internal * autoneg logic which is mapped onto the MII. */ static void xl_autoneg_mii(sc, flag, verbose) struct xl_softc *sc; int flag; int verbose; { u_int16_t phy_sts = 0, media, advert, ability; struct ifnet *ifp; struct ifmedia *ifm; ifm = &sc->ifmedia; ifp = &sc->arpcom.ac_if; ifm->ifm_media = IFM_ETHER | IFM_AUTO; /* * The 100baseT4 PHY on the 3c905-T4 has the 'autoneg supported' * bit cleared in the status register, but has the 'autoneg enabled' * bit set in the control register. This is a contradiction, and * I'm not sure how to handle it. If you want to force an attempt * to autoneg for 100baseT4 PHYs, #define FORCE_AUTONEG_TFOUR * and see what happens. */ #ifndef FORCE_AUTONEG_TFOUR /* * First, see if autoneg is supported. If not, there's * no point in continuing. */ phy_sts = xl_phy_readreg(sc, PHY_BMSR); if (!(phy_sts & PHY_BMSR_CANAUTONEG)) { if (verbose) printf("xl%d: autonegotiation not supported\n", sc->xl_unit); ifm->ifm_media = IFM_ETHER|IFM_10_T|IFM_HDX; media = xl_phy_readreg(sc, PHY_BMCR); media &= ~PHY_BMCR_SPEEDSEL; media &= ~PHY_BMCR_DUPLEX; xl_phy_writereg(sc, PHY_BMCR, media); CSR_WRITE_1(sc, XL_W3_MAC_CTRL, (CSR_READ_1(sc, XL_W3_MAC_CTRL) & ~XL_MACCTRL_DUPLEX)); return; } #endif switch (flag) { case XL_FLAG_FORCEDELAY: /* * XXX Never use this option anywhere but in the probe * routine: making the kernel stop dead in its tracks * for three whole seconds after we've gone multi-user * is really bad manners. */ xl_autoneg_xmit(sc); DELAY(5000000); break; case XL_FLAG_SCHEDDELAY: /* * Wait for the transmitter to go idle before starting * an autoneg session, otherwise xl_start() may clobber * our timeout, and we don't want to allow transmission * during an autoneg session since that can screw it up. */ if (sc->xl_cdata.xl_tx_head != NULL) { sc->xl_want_auto = 1; return; } xl_autoneg_xmit(sc); ifp->if_timer = 5; sc->xl_autoneg = 1; sc->xl_want_auto = 0; return; break; case XL_FLAG_DELAYTIMEO: ifp->if_timer = 0; sc->xl_autoneg = 0; break; default: printf("xl%d: invalid autoneg flag: %d\n", sc->xl_unit, flag); return; } if (xl_phy_readreg(sc, PHY_BMSR) & PHY_BMSR_AUTONEGCOMP) { if (verbose) printf("xl%d: autoneg complete, ", sc->xl_unit); phy_sts = xl_phy_readreg(sc, PHY_BMSR); } else { if (verbose) printf("xl%d: autoneg not complete, ", sc->xl_unit); } media = xl_phy_readreg(sc, PHY_BMCR); /* Link is good. Report modes and set duplex mode. */ if (xl_phy_readreg(sc, PHY_BMSR) & PHY_BMSR_LINKSTAT) { if (verbose) printf("link status good "); advert = xl_phy_readreg(sc, XL_PHY_ANAR); ability = xl_phy_readreg(sc, XL_PHY_LPAR); if (advert & PHY_ANAR_100BT4 && ability & PHY_ANAR_100BT4) { ifm->ifm_media = IFM_ETHER|IFM_100_T4; media |= PHY_BMCR_SPEEDSEL; media &= ~PHY_BMCR_DUPLEX; printf("(100baseT4)\n"); } else if (advert & PHY_ANAR_100BTXFULL && ability & PHY_ANAR_100BTXFULL) { ifm->ifm_media = IFM_ETHER|IFM_100_TX|IFM_FDX; media |= PHY_BMCR_SPEEDSEL; media |= PHY_BMCR_DUPLEX; printf("(full-duplex, 100Mbps)\n"); } else if (advert & PHY_ANAR_100BTXHALF && ability & PHY_ANAR_100BTXHALF) { ifm->ifm_media = IFM_ETHER|IFM_100_TX|IFM_HDX; media |= PHY_BMCR_SPEEDSEL; media &= ~PHY_BMCR_DUPLEX; printf("(half-duplex, 100Mbps)\n"); } else if (advert & PHY_ANAR_10BTFULL && ability & PHY_ANAR_10BTFULL) { ifm->ifm_media = IFM_ETHER|IFM_10_T|IFM_FDX; media &= ~PHY_BMCR_SPEEDSEL; media |= PHY_BMCR_DUPLEX; printf("(full-duplex, 10Mbps)\n"); } else if (advert & PHY_ANAR_10BTHALF && ability & PHY_ANAR_10BTHALF) { ifm->ifm_media = IFM_ETHER|IFM_10_T|IFM_HDX; media &= ~PHY_BMCR_SPEEDSEL; media &= ~PHY_BMCR_DUPLEX; printf("(half-duplex, 10Mbps)\n"); } /* Set ASIC's duplex mode to match the PHY. */ XL_SEL_WIN(3); if (media & PHY_BMCR_DUPLEX) CSR_WRITE_1(sc, XL_W3_MAC_CTRL, XL_MACCTRL_DUPLEX); else CSR_WRITE_1(sc, XL_W3_MAC_CTRL, (CSR_READ_1(sc, XL_W3_MAC_CTRL) & ~XL_MACCTRL_DUPLEX)); xl_phy_writereg(sc, PHY_BMCR, media); } else { if (verbose) printf("no carrier (forcing half-duplex, 10Mbps)\n"); ifm->ifm_media = IFM_ETHER|IFM_10_T|IFM_HDX; media &= ~PHY_BMCR_SPEEDSEL; media &= ~PHY_BMCR_DUPLEX; xl_phy_writereg(sc, PHY_BMCR, media); CSR_WRITE_1(sc, XL_W3_MAC_CTRL, (CSR_READ_1(sc, XL_W3_MAC_CTRL) & ~XL_MACCTRL_DUPLEX)); } xl_init(sc); if (sc->xl_tx_pend) { sc->xl_autoneg = 0; sc->xl_tx_pend = 0; xl_start(ifp); } return; } static void xl_getmode_mii(sc) struct xl_softc *sc; { u_int16_t bmsr; struct ifnet *ifp; ifp = &sc->arpcom.ac_if; bmsr = xl_phy_readreg(sc, PHY_BMSR); if (bootverbose) printf("xl%d: PHY status word: %x\n", sc->xl_unit, bmsr); /* fallback */ sc->ifmedia.ifm_media = IFM_ETHER|IFM_10_T|IFM_HDX; if (bmsr & PHY_BMSR_10BTHALF) { if (bootverbose) printf("xl%d: 10Mbps half-duplex mode supported\n", sc->xl_unit); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_HDX, 0, NULL); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T, 0, NULL); } if (bmsr & PHY_BMSR_10BTFULL) { if (bootverbose) printf("xl%d: 10Mbps full-duplex mode supported\n", sc->xl_unit); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL); sc->ifmedia.ifm_media = IFM_ETHER|IFM_10_T|IFM_FDX; } if (bmsr & PHY_BMSR_100BTXHALF) { if (bootverbose) printf("xl%d: 100Mbps half-duplex mode supported\n", sc->xl_unit); ifp->if_baudrate = 100000000; ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_TX, 0, NULL); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_TX|IFM_HDX, 0, NULL); sc->ifmedia.ifm_media = IFM_ETHER|IFM_100_TX|IFM_HDX; } if (bmsr & PHY_BMSR_100BTXFULL) { if (bootverbose) printf("xl%d: 100Mbps full-duplex mode supported\n", sc->xl_unit); ifp->if_baudrate = 100000000; ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_TX|IFM_FDX, 0, NULL); sc->ifmedia.ifm_media = IFM_ETHER|IFM_100_TX|IFM_FDX; } /* Some also support 100BaseT4. */ if (bmsr & PHY_BMSR_100BT4) { if (bootverbose) printf("xl%d: 100baseT4 mode supported\n", sc->xl_unit); ifp->if_baudrate = 100000000; ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_T4, 0, NULL); sc->ifmedia.ifm_media = IFM_ETHER|IFM_100_T4; #ifdef FORCE_AUTONEG_TFOUR if (bootverbose) printf("xl%d: forcing on autoneg support for BT4\n", sc->xl_unit); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_AUTO, 0, NULL); sc->ifmedia.ifm_media = IFM_ETHER|IFM_AUTO; #endif } if (bmsr & PHY_BMSR_CANAUTONEG) { if (bootverbose) printf("xl%d: autoneg supported\n", sc->xl_unit); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_AUTO, 0, NULL); sc->ifmedia.ifm_media = IFM_ETHER|IFM_AUTO; } return; } /* * Set speed and duplex mode. */ static void xl_setmode_mii(sc, media) struct xl_softc *sc; int media; { u_int16_t bmcr; u_int32_t icfg; struct ifnet *ifp; ifp = &sc->arpcom.ac_if; /* * If an autoneg session is in progress, stop it. */ if (sc->xl_autoneg) { printf("xl%d: canceling autoneg session\n", sc->xl_unit); ifp->if_timer = sc->xl_autoneg = sc->xl_want_auto = 0; bmcr = xl_phy_readreg(sc, PHY_BMCR); bmcr &= ~PHY_BMCR_AUTONEGENBL; xl_phy_writereg(sc, PHY_BMCR, bmcr); } printf("xl%d: selecting MII, ", sc->xl_unit); XL_SEL_WIN(3); icfg = CSR_READ_4(sc, XL_W3_INTERNAL_CFG); icfg &= ~XL_ICFG_CONNECTOR_MASK; if (sc->xl_media & XL_MEDIAOPT_MII || sc->xl_media & XL_MEDIAOPT_BT4) icfg |= (XL_XCVR_MII << XL_ICFG_CONNECTOR_BITS); if (sc->xl_media & XL_MEDIAOPT_BTX) { if (sc->xl_type == XL_TYPE_905B) icfg |= (XL_XCVR_AUTO << XL_ICFG_CONNECTOR_BITS); else icfg |= (XL_XCVR_MII << XL_ICFG_CONNECTOR_BITS); } CSR_WRITE_4(sc, XL_W3_INTERNAL_CFG, icfg); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_STOP); if (IFM_SUBTYPE(media) == IFM_100_FX) { icfg |= (XL_XCVR_100BFX << XL_ICFG_CONNECTOR_BITS); CSR_WRITE_4(sc, XL_W3_INTERNAL_CFG, icfg); return; } bmcr = xl_phy_readreg(sc, PHY_BMCR); bmcr &= ~(PHY_BMCR_AUTONEGENBL|PHY_BMCR_SPEEDSEL| PHY_BMCR_DUPLEX|PHY_BMCR_LOOPBK); if (IFM_SUBTYPE(media) == IFM_100_T4) { printf("100Mbps/T4, half-duplex\n"); bmcr |= PHY_BMCR_SPEEDSEL; bmcr &= ~PHY_BMCR_DUPLEX; } if (IFM_SUBTYPE(media) == IFM_100_TX) { printf("100Mbps, "); bmcr |= PHY_BMCR_SPEEDSEL; } if (IFM_SUBTYPE(media) == IFM_10_T) { printf("10Mbps, "); bmcr &= ~PHY_BMCR_SPEEDSEL; } if ((media & IFM_GMASK) == IFM_FDX) { printf("full duplex\n"); bmcr |= PHY_BMCR_DUPLEX; XL_SEL_WIN(3); CSR_WRITE_1(sc, XL_W3_MAC_CTRL, XL_MACCTRL_DUPLEX); } else { printf("half duplex\n"); bmcr &= ~PHY_BMCR_DUPLEX; XL_SEL_WIN(3); CSR_WRITE_1(sc, XL_W3_MAC_CTRL, (CSR_READ_1(sc, XL_W3_MAC_CTRL) & ~XL_MACCTRL_DUPLEX)); } xl_phy_writereg(sc, PHY_BMCR, bmcr); return; } static void xl_setmode(sc, media) struct xl_softc *sc; int media; { u_int32_t icfg; u_int16_t mediastat; printf("xl%d: selecting ", sc->xl_unit); XL_SEL_WIN(4); mediastat = CSR_READ_2(sc, XL_W4_MEDIA_STATUS); XL_SEL_WIN(3); icfg = CSR_READ_4(sc, XL_W3_INTERNAL_CFG); if (sc->xl_media & XL_MEDIAOPT_BT) { if (IFM_SUBTYPE(media) == IFM_10_T) { printf("10baseT transceiver, "); sc->xl_xcvr = XL_XCVR_10BT; icfg &= ~XL_ICFG_CONNECTOR_MASK; icfg |= (XL_XCVR_10BT << XL_ICFG_CONNECTOR_BITS); mediastat |= XL_MEDIASTAT_LINKBEAT| XL_MEDIASTAT_JABGUARD; mediastat &= ~XL_MEDIASTAT_SQEENB; } } if (sc->xl_media & XL_MEDIAOPT_BFX) { if (IFM_SUBTYPE(media) == IFM_100_FX) { printf("100baseFX port, "); sc->xl_xcvr = XL_XCVR_100BFX; icfg &= ~XL_ICFG_CONNECTOR_MASK; icfg |= (XL_XCVR_100BFX << XL_ICFG_CONNECTOR_BITS); mediastat |= XL_MEDIASTAT_LINKBEAT; mediastat &= ~XL_MEDIASTAT_SQEENB; } } if (sc->xl_media & XL_MEDIAOPT_AUI) { if (IFM_SUBTYPE(media) == IFM_10_5) { printf("AUI port, "); sc->xl_xcvr = XL_XCVR_AUI; icfg &= ~XL_ICFG_CONNECTOR_MASK; icfg |= (XL_XCVR_AUI << XL_ICFG_CONNECTOR_BITS); mediastat &= ~(XL_MEDIASTAT_LINKBEAT| XL_MEDIASTAT_JABGUARD); mediastat |= ~XL_MEDIASTAT_SQEENB; } } if (sc->xl_media & XL_MEDIAOPT_BNC) { if (IFM_SUBTYPE(media) == IFM_10_2) { printf("BNC port, "); sc->xl_xcvr = XL_XCVR_COAX; icfg &= ~XL_ICFG_CONNECTOR_MASK; icfg |= (XL_XCVR_COAX << XL_ICFG_CONNECTOR_BITS); mediastat &= ~(XL_MEDIASTAT_LINKBEAT| XL_MEDIASTAT_JABGUARD| XL_MEDIASTAT_SQEENB); } } if ((media & IFM_GMASK) == IFM_FDX || IFM_SUBTYPE(media) == IFM_100_FX) { printf("full duplex\n"); XL_SEL_WIN(3); CSR_WRITE_1(sc, XL_W3_MAC_CTRL, XL_MACCTRL_DUPLEX); } else { printf("half duplex\n"); XL_SEL_WIN(3); CSR_WRITE_1(sc, XL_W3_MAC_CTRL, (CSR_READ_1(sc, XL_W3_MAC_CTRL) & ~XL_MACCTRL_DUPLEX)); } if (IFM_SUBTYPE(media) == IFM_10_2) CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_START); else CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_STOP); CSR_WRITE_4(sc, XL_W3_INTERNAL_CFG, icfg); XL_SEL_WIN(4); CSR_WRITE_2(sc, XL_W4_MEDIA_STATUS, mediastat); DELAY(800); XL_SEL_WIN(7); return; } static void xl_reset(sc) struct xl_softc *sc; { register int i; XL_SEL_WIN(0); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RESET); for (i = 0; i < XL_TIMEOUT; i++) { DELAY(10); if (!(CSR_READ_2(sc, XL_STATUS) & XL_STAT_CMDBUSY)) break; } if (i == XL_TIMEOUT) printf("xl%d: reset didn't complete\n", sc->xl_unit); /* Wait a little while for the chip to get its brains in order. */ DELAY(1000); return; } /* * Probe for a 3Com Etherlink XL chip. Check the PCI vendor and device * IDs against our list and return a device name if we find a match. */ static const char * xl_probe(config_id, device_id) pcici_t config_id; pcidi_t device_id; { struct xl_type *t; t = xl_devs; while(t->xl_name != NULL) { if ((device_id & 0xFFFF) == t->xl_vid && ((device_id >> 16) & 0xFFFF) == t->xl_did) { return(t->xl_name); } t++; } return(NULL); } /* * This routine is a kludge to work around possible hardware faults * or manufacturing defects that can cause the media options register * (or reset options register, as it's called for the first generation * 3cx90x adapters) to return an incorrect result. I have encountered * one Dell Latitude laptop docking station with an integrated 3c905-TX * which doesn't have any of the 'mediaopt' bits set. This screws up * the attach routine pretty badly because it doesn't know what media * to look for. If we find ourselves in this predicament, this routine * will try to guess the media options values and warn the user of a * possible manufacturing defect with his adapter/system/whatever. */ static void xl_mediacheck(sc) struct xl_softc *sc; { u_int16_t devid; /* * If some of the media options bits are set, assume they are * correct. If not, try to figure it out down below. * XXX I should check for 10baseFL, but I don't have an adapter * to test with. */ if (sc->xl_media & (XL_MEDIAOPT_MASK & ~XL_MEDIAOPT_VCO)) { /* * Check the XCVR value. If it's not in the normal range * of values, we need to fake it up here. */ if (sc->xl_xcvr <= XL_XCVR_AUTO) return; else { printf("xl%d: bogus xcvr value " "in EEPROM (%x)\n", sc->xl_unit, sc->xl_xcvr); printf("xl%d: choosing new default based " "on card type\n", sc->xl_unit); } } else { printf("xl%d: WARNING: no media options bits set in " "the media options register!!\n", sc->xl_unit); printf("xl%d: this could be a manufacturing defect in " "your adapter or system\n", sc->xl_unit); printf("xl%d: attempting to guess media type; you " "should probably consult your vendor\n", sc->xl_unit); } /* * Read the device ID from the EEPROM. * This is what's loaded into the PCI device ID register, so it has * to be correct otherwise we wouldn't have gotten this far. */ xl_read_eeprom(sc, (caddr_t)&devid, XL_EE_PRODID, 1, 0); switch(devid) { case TC_DEVICEID_BOOMERANG_10BT: /* 3c900-TP */ case TC_DEVICEID_CYCLONE_10BT: /* 3c900B-TP */ sc->xl_media = XL_MEDIAOPT_BT; sc->xl_xcvr = XL_XCVR_10BT; printf("xl%d: guessing 10BaseT transceiver\n", sc->xl_unit); break; case TC_DEVICEID_BOOMERANG_10BT_COMBO: /* 3c900-COMBO */ case TC_DEVICEID_CYCLONE_10BT_COMBO: /* 3c900B-COMBO */ sc->xl_media = XL_MEDIAOPT_BT|XL_MEDIAOPT_BNC|XL_MEDIAOPT_AUI; sc->xl_xcvr = XL_XCVR_10BT; printf("xl%d: guessing COMBO (AUI/BNC/TP)\n", sc->xl_unit); break; case TC_DEVICEID_CYCLONE_10BT_TPC: /* 3c900B-TPC */ sc->xl_media = XL_MEDIAOPT_BT|XL_MEDIAOPT_BNC; sc->xl_xcvr = XL_XCVR_10BT; printf("xl%d: guessing TPC (BNC/TP)\n", sc->xl_unit); break; case TC_DEVICEID_BOOMERANG_10_100BT: /* 3c905-TX */ sc->xl_media = XL_MEDIAOPT_MII; sc->xl_xcvr = XL_XCVR_MII; printf("xl%d: guessing MII\n", sc->xl_unit); break; case TC_DEVICEID_BOOMERANG_100BT4: /* 3c905-T4 */ case TC_DEVICEID_CYCLONE_10_100BT4: /* 3c905B-T4 */ sc->xl_media = XL_MEDIAOPT_BT4; sc->xl_xcvr = XL_XCVR_MII; printf("xl%d: guessing 100BaseT4/MII\n", sc->xl_unit); break; case TC_DEVICEID_CYCLONE_10_100BT: /* 3c905B-TX */ case TC_DEVICEID_CYCLONE_10_100BT_SERV: /* 3c980-TX */ case TC_DEVICEID_HURRICANE_SOHO100TX: /* 3cSOHO100-TX */ sc->xl_media = XL_MEDIAOPT_BTX; sc->xl_xcvr = XL_XCVR_AUTO; printf("xl%d: guessing 10/100 internal\n", sc->xl_unit); break; case TC_DEVICEID_CYCLONE_10_100_COMBO: /* 3c905B-COMBO */ sc->xl_media = XL_MEDIAOPT_BTX|XL_MEDIAOPT_BNC|XL_MEDIAOPT_AUI; sc->xl_xcvr = XL_XCVR_AUTO; printf("xl%d: guessing 10/100 plus BNC/AUI\n", sc->xl_unit); break; default: printf("xl%d: unknown device ID: %x -- " "defaulting to 10baseT\n", sc->xl_unit, devid); sc->xl_media = XL_MEDIAOPT_BT; break; } return; } /* * Attach the interface. Allocate softc structures, do ifmedia * setup and ethernet/BPF attach. */ static void xl_attach(config_id, unit) pcici_t config_id; int unit; { int s, i; #ifndef XL_USEIOSPACE vm_offset_t pbase, vbase; #endif u_char eaddr[ETHER_ADDR_LEN]; u_int32_t command; struct xl_softc *sc; struct ifnet *ifp; int media = IFM_ETHER|IFM_100_TX|IFM_FDX; unsigned int round; caddr_t roundptr; struct xl_type *p; u_int16_t phy_vid, phy_did, phy_sts; s = splimp(); sc = malloc(sizeof(struct xl_softc), M_DEVBUF, M_NOWAIT); if (sc == NULL) { printf("xl%d: no memory for softc struct!\n", unit); goto fail; } bzero(sc, sizeof(struct xl_softc)); /* * If this is a 3c905B, we have to check one extra thing. * The 905B supports power management and may be placed in * a low-power mode (D3 mode), typically by certain operating * systems which shall not be named. The PCI BIOS is supposed * to reset the NIC and bring it out of low-power mode, but * some do not. Consequently, we have to see if this chip * supports power management, and if so, make sure it's not * in low-power mode. If power management is available, the * capid byte will be 0x01. * * I _think_ that what actually happens is that the chip * loses its PCI configuration during the transition from * D3 back to D0; this means that it should be possible for * us to save the PCI iobase, membase and IRQ, put the chip * back in the D0 state, then restore the PCI config ourselves. */ command = pci_conf_read(config_id, XL_PCI_CAPID) & 0x000000FF; if (command == 0x01) { command = pci_conf_read(config_id, XL_PCI_PWRMGMTCTRL); if (command & XL_PSTATE_MASK) { u_int32_t iobase, membase, irq; /* Save important PCI config data. */ iobase = pci_conf_read(config_id, XL_PCI_LOIO); membase = pci_conf_read(config_id, XL_PCI_LOMEM); irq = pci_conf_read(config_id, XL_PCI_INTLINE); /* Reset the power state. */ printf("xl%d: chip is in D%d power mode " "-- setting to D0\n", unit, command & XL_PSTATE_MASK); command &= 0xFFFFFFFC; pci_conf_write(config_id, XL_PCI_PWRMGMTCTRL, command); /* Restore PCI config data. */ pci_conf_write(config_id, XL_PCI_LOIO, iobase); pci_conf_write(config_id, XL_PCI_LOMEM, membase); pci_conf_write(config_id, XL_PCI_INTLINE, irq); } } /* * Map control/status registers. */ command = pci_conf_read(config_id, PCI_COMMAND_STATUS_REG); command |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); pci_conf_write(config_id, PCI_COMMAND_STATUS_REG, command); command = pci_conf_read(config_id, PCI_COMMAND_STATUS_REG); #ifdef XL_USEIOSPACE if (!(command & PCIM_CMD_PORTEN)) { printf("xl%d: failed to enable I/O ports!\n", unit); free(sc, M_DEVBUF); goto fail; } if (!pci_map_port(config_id, XL_PCI_LOIO, (u_short *)&(sc->xl_bhandle))) { printf ("xl%d: couldn't map port\n", unit); printf ("xl%d: WARNING: this shouldn't happen! " "Possible PCI support code bug!", unit); printf ("xl%d: attempting to map iobase manually", unit); sc->xl_bhandle = pci_conf_read(config_id, XL_PCI_LOIO) & 0xFFFFFFE0; /*goto fail;*/ } #ifdef __i386__ sc->xl_btag = I386_BUS_SPACE_IO; #endif #ifdef __alpha__ sc->xl_btag = ALPHA_BUS_SPACE_IO; #endif #else if (!(command & PCIM_CMD_MEMEN)) { printf("xl%d: failed to enable memory mapping!\n", unit); goto fail; } if (!pci_map_mem(config_id, XL_PCI_LOMEM, &vbase, &pbase)) { printf ("xl%d: couldn't map memory\n", unit); goto fail; } sc->xl_bhandle = vbase; #ifdef __i386__ sc->xl_btag = I386_BUS_SPACE_MEM; #endif #ifdef __alpha__ sc->xl_btag = ALPHA_BUS_SPACE_MEM; #endif #endif /* Allocate interrupt */ if (!pci_map_int(config_id, xl_intr, sc, &net_imask)) { printf("xl%d: couldn't map interrupt\n", unit); goto fail; } /* Reset the adapter. */ xl_reset(sc); /* * Get station address from the EEPROM. */ if (xl_read_eeprom(sc, (caddr_t)&eaddr, XL_EE_OEM_ADR0, 3, 1)) { printf("xl%d: failed to read station address\n", sc->xl_unit); free(sc, M_DEVBUF); goto fail; } /* * A 3Com chip was detected. Inform the world. */ printf("xl%d: Ethernet address: %6D\n", unit, eaddr, ":"); sc->xl_unit = unit; callout_handle_init(&sc->xl_stat_ch); bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); sc->xl_ldata_ptr = malloc(sizeof(struct xl_list_data) + 8, M_DEVBUF, M_NOWAIT); if (sc->xl_ldata_ptr == NULL) { free(sc, M_DEVBUF); printf("xl%d: no memory for list buffers!\n", unit); goto fail; } sc->xl_ldata = (struct xl_list_data *)sc->xl_ldata_ptr; - round = (unsigned int)sc->xl_ldata_ptr & 0xF; + round = (uintptr_t)sc->xl_ldata_ptr & 0xF; roundptr = sc->xl_ldata_ptr; for (i = 0; i < 8; i++) { if (round % 8) { round++; roundptr++; } else break; } sc->xl_ldata = (struct xl_list_data *)roundptr; bzero(sc->xl_ldata, sizeof(struct xl_list_data)); ifp = &sc->arpcom.ac_if; ifp->if_softc = sc; ifp->if_unit = unit; ifp->if_name = "xl"; ifp->if_mtu = ETHERMTU; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = xl_ioctl; ifp->if_output = ether_output; ifp->if_start = xl_start; ifp->if_watchdog = xl_watchdog; ifp->if_init = xl_init; ifp->if_baudrate = 10000000; ifp->if_snd.ifq_maxlen = XL_TX_LIST_CNT - 1; /* * Figure out the card type. 3c905B adapters have the * 'supportsNoTxLength' bit set in the capabilities * word in the EEPROM. */ xl_read_eeprom(sc, (caddr_t)&sc->xl_caps, XL_EE_CAPS, 1, 0); if (sc->xl_caps & XL_CAPS_NO_TXLENGTH) sc->xl_type = XL_TYPE_905B; else sc->xl_type = XL_TYPE_90X; /* * Now we have to see what sort of media we have. * This includes probing for an MII interace and a * possible PHY. */ XL_SEL_WIN(3); sc->xl_media = CSR_READ_2(sc, XL_W3_MEDIA_OPT); if (bootverbose) printf("xl%d: media options word: %x\n", sc->xl_unit, sc->xl_media); xl_read_eeprom(sc, (char *)&sc->xl_xcvr, XL_EE_ICFG_0, 2, 0); sc->xl_xcvr &= XL_ICFG_CONNECTOR_MASK; sc->xl_xcvr >>= XL_ICFG_CONNECTOR_BITS; xl_mediacheck(sc); if (sc->xl_media & XL_MEDIAOPT_MII || sc->xl_media & XL_MEDIAOPT_BTX || sc->xl_media & XL_MEDIAOPT_BT4) { /* * In theory I shouldn't need this, but... if this * card supports an MII, either an external one or * an internal fake one, select it in the internal * config register before trying to probe it. */ u_int32_t icfg; XL_SEL_WIN(3); icfg = CSR_READ_4(sc, XL_W3_INTERNAL_CFG); icfg &= ~XL_ICFG_CONNECTOR_MASK; if (sc->xl_media & XL_MEDIAOPT_MII || sc->xl_media & XL_MEDIAOPT_BT4) icfg |= (XL_XCVR_MII << XL_ICFG_CONNECTOR_BITS); if (sc->xl_media & XL_MEDIAOPT_BTX) icfg |= (XL_XCVR_AUTO << XL_ICFG_CONNECTOR_BITS); if (sc->xl_media & XL_MEDIAOPT_BFX) icfg |= (XL_XCVR_100BFX << XL_ICFG_CONNECTOR_BITS); CSR_WRITE_4(sc, XL_W3_INTERNAL_CFG, icfg); if (bootverbose) printf("xl%d: probing for a PHY\n", sc->xl_unit); for (i = XL_PHYADDR_MIN; i < XL_PHYADDR_MAX + 1; i++) { if (bootverbose) printf("xl%d: checking address: %d\n", sc->xl_unit, i); sc->xl_phy_addr = i; xl_phy_writereg(sc, XL_PHY_GENCTL, PHY_BMCR_RESET); DELAY(500); while(xl_phy_readreg(sc, XL_PHY_GENCTL) & PHY_BMCR_RESET); if ((phy_sts = xl_phy_readreg(sc, XL_PHY_GENSTS))) break; } if (phy_sts) { phy_vid = xl_phy_readreg(sc, XL_PHY_VENID); phy_did = xl_phy_readreg(sc, XL_PHY_DEVID); if (bootverbose) printf("xl%d: found PHY at address %d, ", sc->xl_unit, sc->xl_phy_addr); if (bootverbose) printf("vendor id: %x device id: %x\n", phy_vid, phy_did); p = xl_phys; while(p->xl_vid) { if (phy_vid == p->xl_vid && (phy_did | 0x000F) == p->xl_did) { sc->xl_pinfo = p; break; } p++; } if (sc->xl_pinfo == NULL) sc->xl_pinfo = &xl_phys[PHY_UNKNOWN]; if (bootverbose) printf("xl%d: PHY type: %s\n", sc->xl_unit, sc->xl_pinfo->xl_name); } else { printf("xl%d: MII without any phy!\n", sc->xl_unit); } } /* * Do ifmedia setup. */ ifmedia_init(&sc->ifmedia, 0, xl_ifmedia_upd, xl_ifmedia_sts); if (sc->xl_media & XL_MEDIAOPT_BT) { if (bootverbose) printf("xl%d: found 10baseT\n", sc->xl_unit); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T, 0, NULL); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_HDX, 0, NULL); if (sc->xl_caps & XL_CAPS_FULL_DUPLEX) ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL); } if (sc->xl_media & XL_MEDIAOPT_AUI) { if (bootverbose) printf("xl%d: found AUI\n", sc->xl_unit); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_5, 0, NULL); } if (sc->xl_media & XL_MEDIAOPT_BNC) { if (bootverbose) printf("xl%d: found BNC\n", sc->xl_unit); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_2, 0, NULL); } /* * Technically we could use xl_getmode_mii() to scan the * modes, but the built-in BTX mode on the 3c905B implies * 10/100 full/half duplex support anyway, so why not just * do it and get it over with. */ if (sc->xl_media & XL_MEDIAOPT_BTX) { if (bootverbose) printf("xl%d: found 100baseTX\n", sc->xl_unit); ifp->if_baudrate = 100000000; ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_TX, 0, NULL); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_TX|IFM_HDX, 0, NULL); if (sc->xl_caps & XL_CAPS_FULL_DUPLEX) ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_TX|IFM_FDX, 0, NULL); if (sc->xl_pinfo != NULL) ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_AUTO, 0, NULL); } if (sc->xl_media & XL_MEDIAOPT_BFX) { if (bootverbose) printf("xl%d: found 100baseFX\n", sc->xl_unit); ifp->if_baudrate = 100000000; ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_FX, 0, NULL); } /* * If there's an MII, we have to probe its modes * separately. */ if (sc->xl_media & XL_MEDIAOPT_MII || sc->xl_media & XL_MEDIAOPT_BT4) { if (bootverbose) printf("xl%d: found MII\n", sc->xl_unit); xl_getmode_mii(sc); } /* Choose a default media. */ switch(sc->xl_xcvr) { case XL_XCVR_10BT: media = IFM_ETHER|IFM_10_T; xl_setmode(sc, media); break; case XL_XCVR_AUI: media = IFM_ETHER|IFM_10_5; xl_setmode(sc, media); break; case XL_XCVR_COAX: media = IFM_ETHER|IFM_10_2; xl_setmode(sc, media); break; case XL_XCVR_AUTO: #ifdef XL_BACKGROUND_AUTONEG xl_autoneg_mii(sc, XL_FLAG_SCHEDDELAY, 1); #else xl_autoneg_mii(sc, XL_FLAG_FORCEDELAY, 1); #endif media = sc->ifmedia.ifm_media; break; case XL_XCVR_100BTX: case XL_XCVR_MII: #ifdef XL_BACKGROUND_AUTONEG xl_autoneg_mii(sc, XL_FLAG_SCHEDDELAY, 1); #else xl_autoneg_mii(sc, XL_FLAG_FORCEDELAY, 1); #endif media = sc->ifmedia.ifm_media; break; case XL_XCVR_100BFX: media = IFM_ETHER|IFM_100_FX; break; default: printf("xl%d: unknown XCVR type: %d\n", sc->xl_unit, sc->xl_xcvr); /* * This will probably be wrong, but it prevents * the ifmedia code from panicking. */ media = IFM_ETHER|IFM_10_T; break; } ifmedia_set(&sc->ifmedia, media); /* * Call MI attach routines. */ if_attach(ifp); ether_ifattach(ifp); #if NBPFILTER > 0 bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); #endif at_shutdown(xl_shutdown, sc, SHUTDOWN_POST_SYNC); fail: splx(s); return; } /* * Initialize the transmit descriptors. */ static int xl_list_tx_init(sc) struct xl_softc *sc; { struct xl_chain_data *cd; struct xl_list_data *ld; int i; cd = &sc->xl_cdata; ld = sc->xl_ldata; for (i = 0; i < XL_TX_LIST_CNT; i++) { cd->xl_tx_chain[i].xl_ptr = &ld->xl_tx_list[i]; cd->xl_tx_chain[i].xl_unsent = 0; if (i == (XL_TX_LIST_CNT - 1)) cd->xl_tx_chain[i].xl_next = NULL; else cd->xl_tx_chain[i].xl_next = &cd->xl_tx_chain[i + 1]; } cd->xl_tx_free = &cd->xl_tx_chain[0]; cd->xl_tx_tail = cd->xl_tx_head = NULL; return(0); } /* * Initialize the RX descriptors and allocate mbufs for them. Note that * we arrange the descriptors in a closed ring, so that the last descriptor * points back to the first. */ static int xl_list_rx_init(sc) struct xl_softc *sc; { struct xl_chain_data *cd; struct xl_list_data *ld; int i; cd = &sc->xl_cdata; ld = sc->xl_ldata; for (i = 0; i < XL_RX_LIST_CNT; i++) { cd->xl_rx_chain[i].xl_ptr = (struct xl_list_onefrag *)&ld->xl_rx_list[i]; if (xl_newbuf(sc, &cd->xl_rx_chain[i]) == ENOBUFS) return(ENOBUFS); if (i == (XL_RX_LIST_CNT - 1)) { cd->xl_rx_chain[i].xl_next = &cd->xl_rx_chain[0]; ld->xl_rx_list[i].xl_next = vtophys(&ld->xl_rx_list[0]); } else { cd->xl_rx_chain[i].xl_next = &cd->xl_rx_chain[i + 1]; ld->xl_rx_list[i].xl_next = vtophys(&ld->xl_rx_list[i + 1]); } } cd->xl_rx_head = &cd->xl_rx_chain[0]; return(0); } /* * Initialize an RX descriptor and attach an MBUF cluster. */ static int xl_newbuf(sc, c) struct xl_softc *sc; struct xl_chain_onefrag *c; { struct mbuf *m_new = NULL; MGETHDR(m_new, M_DONTWAIT, MT_DATA); if (m_new == NULL) { printf("xl%d: no memory for rx list -- packet dropped!\n", sc->xl_unit); return(ENOBUFS); } MCLGET(m_new, M_DONTWAIT); if (!(m_new->m_flags & M_EXT)) { printf("xl%d: no memory for rx list -- packet dropped!\n", sc->xl_unit); m_freem(m_new); return(ENOBUFS); } #ifdef __alpha__ /* Force longword alignment for packet payload to pacify alpha. */ m_new->m_data += 2; #endif c->xl_mbuf = m_new; c->xl_ptr->xl_status = 0; c->xl_ptr->xl_frag.xl_addr = vtophys(mtod(m_new, caddr_t)); c->xl_ptr->xl_frag.xl_len = MCLBYTES | XL_LAST_FRAG; return(0); } /* * A frame has been uploaded: pass the resulting mbuf chain up to * the higher level protocols. */ static void xl_rxeof(sc) struct xl_softc *sc; { struct ether_header *eh; struct mbuf *m; struct ifnet *ifp; struct xl_chain_onefrag *cur_rx; int total_len = 0; u_int16_t rxstat; ifp = &sc->arpcom.ac_if; again: while((rxstat = sc->xl_cdata.xl_rx_head->xl_ptr->xl_status)) { cur_rx = sc->xl_cdata.xl_rx_head; sc->xl_cdata.xl_rx_head = cur_rx->xl_next; /* * If an error occurs, update stats, clear the * status word and leave the mbuf cluster in place: * it should simply get re-used next time this descriptor * comes up in the ring. */ if (rxstat & XL_RXSTAT_UP_ERROR) { ifp->if_ierrors++; cur_rx->xl_ptr->xl_status = 0; continue; } /* * If there error bit was not set, the upload complete * bit should be set which means we have a valid packet. * If not, something truly strange has happened. */ if (!(rxstat & XL_RXSTAT_UP_CMPLT)) { printf("xl%d: bad receive status -- packet dropped", sc->xl_unit); ifp->if_ierrors++; cur_rx->xl_ptr->xl_status = 0; continue; } /* No errors; receive the packet. */ m = cur_rx->xl_mbuf; total_len = cur_rx->xl_ptr->xl_status & XL_RXSTAT_LENMASK; /* * Try to conjure up a new mbuf cluster. If that * fails, it means we have an out of memory condition and * should leave the buffer in place and continue. This will * result in a lost packet, but there's little else we * can do in this situation. */ if (xl_newbuf(sc, cur_rx) == ENOBUFS) { ifp->if_ierrors++; cur_rx->xl_ptr->xl_status = 0; continue; } ifp->if_ipackets++; eh = mtod(m, struct ether_header *); m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = m->m_len = total_len; #if NBPFILTER > 0 /* * Handle BPF listeners. Let the BPF user see the packet. */ if (ifp->if_bpf) bpf_mtap(ifp, m); #endif #ifdef BRIDGE if (do_bridge) { struct ifnet *bdg_ifp ; bdg_ifp = bridge_in(m); if (bdg_ifp == BDG_DROP) goto dropit ; if (bdg_ifp != BDG_LOCAL) bdg_forward(&m, bdg_ifp); if (bdg_ifp != BDG_LOCAL && bdg_ifp != BDG_BCAST && bdg_ifp != BDG_MCAST) goto dropit ; goto getit ; } #endif /* * Don't pass packet up to the ether_input() layer unless it's * a broadcast packet, multicast packet, matches our ethernet * address or the interface is in promiscuous mode. */ if (ifp->if_flags & IFF_PROMISC && (bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr, ETHER_ADDR_LEN) && (eh->ether_dhost[0] & 1) == 0)) { #ifdef BRIDGE dropit: #endif if (m) m_freem(m); continue; } #ifdef BRIDGE getit: #endif /* Remove header from mbuf and pass it on. */ m->m_pkthdr.len = m->m_len = total_len - sizeof(struct ether_header); m->m_data += sizeof(struct ether_header); ether_input(ifp, eh, m); } /* * Handle the 'end of channel' condition. When the upload * engine hits the end of the RX ring, it will stall. This * is our cue to flush the RX ring, reload the uplist pointer * register and unstall the engine. * XXX This is actually a little goofy. With the ThunderLAN * chip, you get an interrupt when the receiver hits the end * of the receive ring, which tells you exactly when you * you need to reload the ring pointer. Here we have to * fake it. I'm mad at myself for not being clever enough * to avoid the use of a goto here. */ if (CSR_READ_4(sc, XL_UPLIST_PTR) == 0 || CSR_READ_4(sc, XL_UPLIST_STATUS) & XL_PKTSTAT_UP_STALLED) { CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_UP_STALL); xl_wait(sc); CSR_WRITE_4(sc, XL_UPLIST_PTR, vtophys(&sc->xl_ldata->xl_rx_list[0])); sc->xl_cdata.xl_rx_head = &sc->xl_cdata.xl_rx_chain[0]; CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_UP_UNSTALL); goto again; } return; } /* * A frame was downloaded to the chip. It's safe for us to clean up * the list buffers. */ static void xl_txeof(sc) struct xl_softc *sc; { struct xl_chain *cur_tx; struct ifnet *ifp; ifp = &sc->arpcom.ac_if; /* Clear the timeout timer. */ ifp->if_timer = 0; /* * Go through our tx list and free mbufs for those * frames that have been uploaded. Note: the 3c905B * sets a special bit in the status word to let us * know that a frame has been downloaded, but the * original 3c900/3c905 adapters don't do that. * Consequently, we have to use a different test if * xl_type != XL_TYPE_905B. */ while(sc->xl_cdata.xl_tx_head != NULL) { cur_tx = sc->xl_cdata.xl_tx_head; if ((sc->xl_type == XL_TYPE_905B && !(cur_tx->xl_ptr->xl_status & XL_TXSTAT_DL_COMPLETE)) || (CSR_READ_1(sc, XL_TX_STATUS) & XL_TXSTATUS_COMPLETE) || cur_tx->xl_unsent) { break; } sc->xl_cdata.xl_tx_head = cur_tx->xl_next; m_freem(cur_tx->xl_mbuf); cur_tx->xl_mbuf = NULL; ifp->if_opackets++; cur_tx->xl_next = sc->xl_cdata.xl_tx_free; sc->xl_cdata.xl_tx_free = cur_tx; } if (sc->xl_cdata.xl_tx_head == NULL) { ifp->if_flags &= ~IFF_OACTIVE; sc->xl_cdata.xl_tx_tail = NULL; if (sc->xl_want_auto) xl_autoneg_mii(sc, XL_FLAG_SCHEDDELAY, 1); } else { if (sc->xl_cdata.xl_tx_head->xl_unsent) { sc->xl_cdata.xl_tx_head->xl_unsent = 0; CSR_WRITE_4(sc, XL_DOWNLIST_PTR, vtophys(sc->xl_cdata.xl_tx_head->xl_ptr)); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_UNSTALL); } } return; } /* * TX 'end of channel' interrupt handler. Actually, we should * only get a 'TX complete' interrupt if there's a transmit error, * so this is really TX error handler. */ static void xl_txeoc(sc) struct xl_softc *sc; { u_int8_t txstat; while((txstat = CSR_READ_1(sc, XL_TX_STATUS))) { if (txstat & XL_TXSTATUS_UNDERRUN || txstat & XL_TXSTATUS_JABBER || txstat & XL_TXSTATUS_RECLAIM) { printf("xl%d: transmission error: %x\n", sc->xl_unit, txstat); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_RESET); xl_wait(sc); if (sc->xl_cdata.xl_tx_head != NULL) CSR_WRITE_4(sc, XL_DOWNLIST_PTR, vtophys(sc->xl_cdata.xl_tx_head->xl_ptr)); /* * Remember to set this for the * first generation 3c90X chips. */ CSR_WRITE_1(sc, XL_TX_FREETHRESH, XL_PACKET_SIZE >> 8); if (sc->xl_type == XL_TYPE_905B) { CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_SET_TX_RECLAIM|(XL_PACKET_SIZE >> 4)); } CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_ENABLE); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_UNSTALL); } else { CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_ENABLE); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_UNSTALL); } /* * Write an arbitrary byte to the TX_STATUS register * to clear this interrupt/error and advance to the next. */ CSR_WRITE_1(sc, XL_TX_STATUS, 0x01); } return; } static void xl_intr(arg) void *arg; { struct xl_softc *sc; struct ifnet *ifp; u_int16_t status; sc = arg; ifp = &sc->arpcom.ac_if; /* Disable interrupts. */ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ENB); for (;;) { status = CSR_READ_2(sc, XL_STATUS); if ((status & XL_INTRS) == 0) break; if (status & XL_STAT_UP_COMPLETE) { xl_rxeof(sc); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ACK|XL_STAT_UP_COMPLETE); } if (status & XL_STAT_DOWN_COMPLETE) { xl_txeof(sc); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ACK|XL_STAT_DOWN_COMPLETE); } if (status & XL_STAT_TX_COMPLETE) { ifp->if_oerrors++; xl_txeoc(sc); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ACK|XL_STAT_TX_COMPLETE); } if (status & XL_STAT_ADFAIL) { xl_reset(sc); xl_init(sc); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ACK|XL_STAT_ADFAIL); } if (status & XL_STAT_STATSOFLOW) { sc->xl_stats_no_timeout = 1; xl_stats_update(sc); sc->xl_stats_no_timeout = 0; } CSR_WRITE_2(sc, XL_STATUS, XL_CMD_INTR_ACK|XL_STAT_INTREQ| XL_STAT_INTLATCH); } /* Re-enable interrupts. */ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ENB|XL_INTRS); XL_SEL_WIN(7); if (ifp->if_snd.ifq_head != NULL) { xl_start(ifp); } return; } static void xl_stats_update(xsc) void *xsc; { struct xl_softc *sc; struct ifnet *ifp; struct xl_stats xl_stats; u_int8_t *p; int i; bzero((char *)&xl_stats, sizeof(struct xl_stats)); sc = xsc; ifp = &sc->arpcom.ac_if; p = (u_int8_t *)&xl_stats; /* Read all the stats registers. */ XL_SEL_WIN(6); for (i = 0; i < 16; i++) *p++ = CSR_READ_1(sc, XL_W6_CARRIER_LOST + i); ifp->if_ierrors += xl_stats.xl_rx_overrun; ifp->if_collisions += xl_stats.xl_tx_multi_collision + xl_stats.xl_tx_single_collision + xl_stats.xl_tx_late_collision; /* * Boomerang and cyclone chips have an extra stats counter * in window 4 (BadSSD). We have to read this too in order * to clear out all the stats registers and avoid a statsoflow * interrupt. */ XL_SEL_WIN(4); CSR_READ_1(sc, XL_W4_BADSSD); XL_SEL_WIN(7); if (!sc->xl_stats_no_timeout) sc->xl_stat_ch = timeout(xl_stats_update, sc, hz); return; } /* * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data * pointers to the fragment pointers. */ static int xl_encap(sc, c, m_head) struct xl_softc *sc; struct xl_chain *c; struct mbuf *m_head; { int frag = 0; struct xl_frag *f = NULL; int total_len; struct mbuf *m; /* * Start packing the mbufs in this chain into * the fragment pointers. Stop when we run out * of fragments or hit the end of the mbuf chain. */ m = m_head; total_len = 0; for (m = m_head, frag = 0; m != NULL; m = m->m_next) { if (m->m_len != 0) { if (frag == XL_MAXFRAGS) break; total_len+= m->m_len; c->xl_ptr->xl_frag[frag].xl_addr = vtophys(mtod(m, vm_offset_t)); c->xl_ptr->xl_frag[frag].xl_len = m->m_len; frag++; } } /* * Handle special case: we used up all 63 fragments, * but we have more mbufs left in the chain. Copy the * data into an mbuf cluster. Note that we don't * bother clearing the values in the other fragment * pointers/counters; it wouldn't gain us anything, * and would waste cycles. */ if (m != NULL) { struct mbuf *m_new = NULL; MGETHDR(m_new, M_DONTWAIT, MT_DATA); if (m_new == NULL) { printf("xl%d: no memory for tx list", sc->xl_unit); return(1); } if (m_head->m_pkthdr.len > MHLEN) { MCLGET(m_new, M_DONTWAIT); if (!(m_new->m_flags & M_EXT)) { m_freem(m_new); printf("xl%d: no memory for tx list", sc->xl_unit); return(1); } } m_copydata(m_head, 0, m_head->m_pkthdr.len, mtod(m_new, caddr_t)); m_new->m_pkthdr.len = m_new->m_len = m_head->m_pkthdr.len; m_freem(m_head); m_head = m_new; f = &c->xl_ptr->xl_frag[0]; f->xl_addr = vtophys(mtod(m_new, caddr_t)); f->xl_len = total_len = m_new->m_len; frag = 1; } c->xl_mbuf = m_head; c->xl_ptr->xl_frag[frag - 1].xl_len |= XL_LAST_FRAG; c->xl_ptr->xl_status = total_len; c->xl_ptr->xl_next = 0; return(0); } /* * Main transmit routine. To avoid having to do mbuf copies, we put pointers * to the mbuf data regions directly in the transmit lists. We also save a * copy of the pointers since the transmit list fragment pointers are * physical addresses. */ static void xl_start(ifp) struct ifnet *ifp; { struct xl_softc *sc; struct mbuf *m_head = NULL; struct xl_chain *prev = NULL, *cur_tx = NULL, *start_tx; sc = ifp->if_softc; if (sc->xl_autoneg) { sc->xl_tx_pend = 1; return; } /* * Check for an available queue slot. If there are none, * punt. */ if (sc->xl_cdata.xl_tx_free == NULL) { xl_txeoc(sc); xl_txeof(sc); if (sc->xl_cdata.xl_tx_free == NULL) { ifp->if_flags |= IFF_OACTIVE; return; } } start_tx = sc->xl_cdata.xl_tx_free; while(sc->xl_cdata.xl_tx_free != NULL) { IF_DEQUEUE(&ifp->if_snd, m_head); if (m_head == NULL) break; /* Pick a descriptor off the free list. */ cur_tx = sc->xl_cdata.xl_tx_free; sc->xl_cdata.xl_tx_free = cur_tx->xl_next; cur_tx->xl_next = NULL; /* Pack the data into the descriptor. */ xl_encap(sc, cur_tx, m_head); /* Chain it together. */ if (prev != NULL) { prev->xl_next = cur_tx; prev->xl_ptr->xl_next = vtophys(cur_tx->xl_ptr); } prev = cur_tx; #if NBPFILTER > 0 /* * If there's a BPF listener, bounce a copy of this frame * to him. */ if (ifp->if_bpf) bpf_mtap(ifp, cur_tx->xl_mbuf); #endif } /* * If there are no packets queued, bail. */ if (cur_tx == NULL) return; /* * Place the request for the upload interrupt * in the last descriptor in the chain. This way, if * we're chaining several packets at once, we'll only * get an interupt once for the whole chain rather than * once for each packet. */ cur_tx->xl_ptr->xl_status |= XL_TXSTAT_DL_INTR; if (sc->xl_cdata.xl_tx_head == NULL) { sc->xl_cdata.xl_tx_head = start_tx; sc->xl_cdata.xl_tx_tail = cur_tx; CSR_WRITE_4(sc, XL_DOWNLIST_PTR, vtophys(start_tx->xl_ptr)); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_UNSTALL); } else { start_tx->xl_unsent++; sc->xl_cdata.xl_tx_tail->xl_next = start_tx; sc->xl_cdata.xl_tx_tail = cur_tx; } /* * Set a timeout in case the chip goes out to lunch. */ ifp->if_timer = 5; /* * XXX Under certain conditions, usually on slower machines * where interrupts may be dropped, it's possible for the * adapter to chew up all the buffers in the receive ring * and stall, without us being able to do anything about it. * To guard against this, we need to make a pass over the * RX queue to make sure there aren't any packets pending. * Doing it here means we can flush the receive ring at the * same time the chip is DMAing the transmit descriptors we * just gave it. * * 3Com goes to some lengths to emphasize the Parallel Tasking (tm) * nature of their chips in all their marketing literature; * we may as well take advantage of it. :) */ xl_rxeof(sc); return; } static void xl_init(xsc) void *xsc; { struct xl_softc *sc = xsc; struct ifnet *ifp = &sc->arpcom.ac_if; int s, i; u_int16_t rxfilt = 0; u_int16_t phy_bmcr = 0; if (sc->xl_autoneg) return; s = splimp(); /* * XXX Hack for the 3c905B: the built-in autoneg logic's state * gets reset by xl_init() when we don't want it to. Try * to preserve it. (For 3c905 cards with real external PHYs, * the BMCR register doesn't change, but this doesn't hurt.) */ if (sc->xl_pinfo != NULL) phy_bmcr = xl_phy_readreg(sc, PHY_BMCR); /* * Cancel pending I/O and free all RX/TX buffers. */ xl_stop(sc); xl_wait(sc); /* Init our MAC address */ XL_SEL_WIN(2); for (i = 0; i < ETHER_ADDR_LEN; i++) { CSR_WRITE_1(sc, XL_W2_STATION_ADDR_LO + i, sc->arpcom.ac_enaddr[i]); } /* Clear the station mask. */ for (i = 0; i < 3; i++) CSR_WRITE_2(sc, XL_W2_STATION_MASK_LO + (i * 2), 0); #ifdef notdef /* Reset TX and RX. */ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_RESET); xl_wait(sc); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_RESET); xl_wait(sc); #endif /* Init circular RX list. */ if (xl_list_rx_init(sc) == ENOBUFS) { printf("xl%d: initialization failed: no " "memory for rx buffers\n", sc->xl_unit); xl_stop(sc); return; } /* Init TX descriptors. */ xl_list_tx_init(sc); /* * Set the TX freethresh value. * Note that this has no effect on 3c905B "cyclone" * cards but is required for 3c900/3c905 "boomerang" * cards in order to enable the download engine. */ CSR_WRITE_1(sc, XL_TX_FREETHRESH, XL_PACKET_SIZE >> 8); /* * If this is a 3c905B, also set the tx reclaim threshold. * This helps cut down on the number of tx reclaim errors * that could happen on a busy network. The chip multiplies * the register value by 16 to obtain the actual threshold * in bytes, so we divide by 16 when setting the value here. * The existing threshold value can be examined by reading * the register at offset 9 in window 5. */ if (sc->xl_type == XL_TYPE_905B) { CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_SET_TX_RECLAIM|(XL_PACKET_SIZE >> 4)); } /* Set RX filter bits. */ XL_SEL_WIN(5); rxfilt = CSR_READ_1(sc, XL_W5_RX_FILTER); /* Set the individual bit to receive frames for this host only. */ rxfilt |= XL_RXFILTER_INDIVIDUAL; /* If we want promiscuous mode, set the allframes bit. */ if (ifp->if_flags & IFF_PROMISC) { rxfilt |= XL_RXFILTER_ALLFRAMES; CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt); } else { rxfilt &= ~XL_RXFILTER_ALLFRAMES; CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt); } /* * Set capture broadcast bit to capture broadcast frames. */ if (ifp->if_flags & IFF_BROADCAST) { rxfilt |= XL_RXFILTER_BROADCAST; CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt); } else { rxfilt &= ~XL_RXFILTER_BROADCAST; CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt); } /* * Program the multicast filter, if necessary. */ if (sc->xl_type == XL_TYPE_905B) xl_setmulti_hash(sc); else xl_setmulti(sc); /* * Load the address of the RX list. We have to * stall the upload engine before we can manipulate * the uplist pointer register, then unstall it when * we're finished. We also have to wait for the * stall command to complete before proceeding. * Note that we have to do this after any RX resets * have completed since the uplist register is cleared * by a reset. */ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_UP_STALL); xl_wait(sc); CSR_WRITE_4(sc, XL_UPLIST_PTR, vtophys(&sc->xl_ldata->xl_rx_list[0])); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_UP_UNSTALL); /* * If the coax transceiver is on, make sure to enable * the DC-DC converter. */ XL_SEL_WIN(3); if (sc->xl_xcvr == XL_XCVR_COAX) CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_START); else CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_STOP); /* Clear out the stats counters. */ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STATS_DISABLE); sc->xl_stats_no_timeout = 1; xl_stats_update(sc); sc->xl_stats_no_timeout = 0; XL_SEL_WIN(4); CSR_WRITE_2(sc, XL_W4_NET_DIAG, XL_NETDIAG_UPPER_BYTES_ENABLE); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STATS_ENABLE); /* * Enable interrupts. */ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ACK|0xFF); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STAT_ENB|XL_INTRS); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ENB|XL_INTRS); /* Set the RX early threshold */ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_THRESH|(XL_PACKET_SIZE >>2)); CSR_WRITE_2(sc, XL_DMACTL, XL_DMACTL_UP_RX_EARLY); /* Enable receiver and transmitter. */ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_ENABLE); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_ENABLE); /* Restore state of BMCR */ if (sc->xl_pinfo != NULL) xl_phy_writereg(sc, PHY_BMCR, phy_bmcr); /* Select window 7 for normal operations. */ XL_SEL_WIN(7); ifp->if_flags |= IFF_RUNNING; ifp->if_flags &= ~IFF_OACTIVE; (void)splx(s); sc->xl_stat_ch = timeout(xl_stats_update, sc, hz); return; } /* * Set media options. */ static int xl_ifmedia_upd(ifp) struct ifnet *ifp; { struct xl_softc *sc; struct ifmedia *ifm; sc = ifp->if_softc; ifm = &sc->ifmedia; if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER) return(EINVAL); switch(IFM_SUBTYPE(ifm->ifm_media)) { case IFM_100_FX: case IFM_10_2: case IFM_10_5: xl_setmode(sc, ifm->ifm_media); return(0); break; default: break; } if (sc->xl_media & XL_MEDIAOPT_MII || sc->xl_media & XL_MEDIAOPT_BTX || sc->xl_media & XL_MEDIAOPT_BT4) { if (IFM_SUBTYPE(ifm->ifm_media) == IFM_AUTO) xl_autoneg_mii(sc, XL_FLAG_SCHEDDELAY, 1); else xl_setmode_mii(sc, ifm->ifm_media); } else { xl_setmode(sc, ifm->ifm_media); } return(0); } /* * Report current media status. */ static void xl_ifmedia_sts(ifp, ifmr) struct ifnet *ifp; struct ifmediareq *ifmr; { struct xl_softc *sc; u_int16_t advert = 0, ability = 0; u_int32_t icfg; sc = ifp->if_softc; XL_SEL_WIN(3); icfg = CSR_READ_4(sc, XL_W3_INTERNAL_CFG) & XL_ICFG_CONNECTOR_MASK; icfg >>= XL_ICFG_CONNECTOR_BITS; ifmr->ifm_active = IFM_ETHER; switch(icfg) { case XL_XCVR_10BT: ifmr->ifm_active = IFM_ETHER|IFM_10_T; if (CSR_READ_1(sc, XL_W3_MAC_CTRL) & XL_MACCTRL_DUPLEX) ifmr->ifm_active |= IFM_FDX; else ifmr->ifm_active |= IFM_HDX; break; case XL_XCVR_AUI: ifmr->ifm_active = IFM_ETHER|IFM_10_5; break; case XL_XCVR_COAX: ifmr->ifm_active = IFM_ETHER|IFM_10_2; break; /* * XXX MII and BTX/AUTO should be separate cases. */ case XL_XCVR_100BTX: case XL_XCVR_AUTO: case XL_XCVR_MII: if (!(xl_phy_readreg(sc, PHY_BMCR) & PHY_BMCR_AUTONEGENBL)) { if (xl_phy_readreg(sc, PHY_BMCR) & PHY_BMCR_SPEEDSEL) ifmr->ifm_active = IFM_ETHER|IFM_100_TX; else ifmr->ifm_active = IFM_ETHER|IFM_10_T; XL_SEL_WIN(3); if (CSR_READ_2(sc, XL_W3_MAC_CTRL) & XL_MACCTRL_DUPLEX) ifmr->ifm_active |= IFM_FDX; else ifmr->ifm_active |= IFM_HDX; break; } ability = xl_phy_readreg(sc, XL_PHY_LPAR); advert = xl_phy_readreg(sc, XL_PHY_ANAR); if (advert & PHY_ANAR_100BT4 && ability & PHY_ANAR_100BT4) { ifmr->ifm_active = IFM_ETHER|IFM_100_T4; } else if (advert & PHY_ANAR_100BTXFULL && ability & PHY_ANAR_100BTXFULL) { ifmr->ifm_active = IFM_ETHER|IFM_100_TX|IFM_FDX; } else if (advert & PHY_ANAR_100BTXHALF && ability & PHY_ANAR_100BTXHALF) { ifmr->ifm_active = IFM_ETHER|IFM_100_TX|IFM_HDX; } else if (advert & PHY_ANAR_10BTFULL && ability & PHY_ANAR_10BTFULL) { ifmr->ifm_active = IFM_ETHER|IFM_10_T|IFM_FDX; } else if (advert & PHY_ANAR_10BTHALF && ability & PHY_ANAR_10BTHALF) { ifmr->ifm_active = IFM_ETHER|IFM_10_T|IFM_HDX; } break; case XL_XCVR_100BFX: ifmr->ifm_active = IFM_ETHER|IFM_100_FX; break; default: printf("xl%d: unknown XCVR type: %d\n", sc->xl_unit, icfg); break; } return; } static int xl_ioctl(ifp, command, data) struct ifnet *ifp; u_long command; caddr_t data; { struct xl_softc *sc = ifp->if_softc; struct ifreq *ifr = (struct ifreq *) data; int s, error = 0; s = splimp(); switch(command) { case SIOCSIFADDR: case SIOCGIFADDR: case SIOCSIFMTU: error = ether_ioctl(ifp, command, data); break; case SIOCSIFFLAGS: if (ifp->if_flags & IFF_UP) { xl_init(sc); } else { if (ifp->if_flags & IFF_RUNNING) xl_stop(sc); } error = 0; break; case SIOCADDMULTI: case SIOCDELMULTI: if (sc->xl_type == XL_TYPE_905B) xl_setmulti_hash(sc); else xl_setmulti(sc); error = 0; break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: error = ifmedia_ioctl(ifp, ifr, &sc->ifmedia, command); break; default: error = EINVAL; break; } (void)splx(s); return(error); } static void xl_watchdog(ifp) struct ifnet *ifp; { struct xl_softc *sc; u_int16_t status = 0; sc = ifp->if_softc; if (sc->xl_autoneg) { xl_autoneg_mii(sc, XL_FLAG_DELAYTIMEO, 1); return; } ifp->if_oerrors++; XL_SEL_WIN(4); status = CSR_READ_2(sc, XL_W4_MEDIA_STATUS); printf("xl%d: watchdog timeout\n", sc->xl_unit); if (status & XL_MEDIASTAT_CARRIER) printf("xl%d: no carrier - transceiver cable problem?\n", sc->xl_unit); xl_txeoc(sc); xl_txeof(sc); xl_rxeof(sc); xl_init(sc); if (ifp->if_snd.ifq_head != NULL) xl_start(ifp); return; } /* * Stop the adapter and free any mbufs allocated to the * RX and TX lists. */ static void xl_stop(sc) struct xl_softc *sc; { register int i; struct ifnet *ifp; ifp = &sc->arpcom.ac_if; ifp->if_timer = 0; CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_DISABLE); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STATS_DISABLE); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ENB); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_DISCARD); xl_wait(sc); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_DISABLE); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_STOP); DELAY(800); #ifdef notdef CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_RESET); xl_wait(sc); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_RESET); xl_wait(sc); #endif CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ACK|XL_STAT_INTLATCH); /* Stop the stats updater. */ untimeout(xl_stats_update, sc, sc->xl_stat_ch); /* * Free data in the RX lists. */ for (i = 0; i < XL_RX_LIST_CNT; i++) { if (sc->xl_cdata.xl_rx_chain[i].xl_mbuf != NULL) { m_freem(sc->xl_cdata.xl_rx_chain[i].xl_mbuf); sc->xl_cdata.xl_rx_chain[i].xl_mbuf = NULL; } } bzero((char *)&sc->xl_ldata->xl_rx_list, sizeof(sc->xl_ldata->xl_rx_list)); /* * Free the TX list buffers. */ for (i = 0; i < XL_TX_LIST_CNT; i++) { if (sc->xl_cdata.xl_tx_chain[i].xl_mbuf != NULL) { m_freem(sc->xl_cdata.xl_tx_chain[i].xl_mbuf); sc->xl_cdata.xl_tx_chain[i].xl_mbuf = NULL; } } bzero((char *)&sc->xl_ldata->xl_tx_list, sizeof(sc->xl_ldata->xl_tx_list)); ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); return; } /* * Stop all chip I/O so that the kernel's probe routines don't * get confused by errant DMAs when rebooting. */ static void xl_shutdown(howto, arg) int howto; void *arg; { struct xl_softc *sc = (struct xl_softc *)arg; xl_stop(sc); return; } static struct pci_device xl_device = { "xl", xl_probe, xl_attach, &xl_count, NULL }; DATA_SET(pcidevice_set, xl_device); Index: head/sys/pci/ohci_pci.c =================================================================== --- head/sys/pci/ohci_pci.c (revision 45719) +++ head/sys/pci/ohci_pci.c (revision 45720) @@ -1,218 +1,240 @@ /* $FreeBSD$ */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (augustss@carlstedt.se) at * Carlstedt Research & Technology. * * 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 NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */ /* * USB Open Host Controller driver. * * OHCI spec: http://www.intel.com/design/usb/ohci11d.pdf * USB spec: http://www.teleport.com/cgi-bin/mailmerge.cgi/~usb/cgiform.tpl */ +#include "opt_bus.h" + #include #include #include #include #include #include #include #include +#include +#include +#include #include #include -#define PCI_CLASS_SERIALBUS 0x0c000000 -#define PCI_SUBCLASS_COMMUNICATIONS_SERIAL 0x00000000 -#define PCI_SUBCLASS_SERIALBUS_FIREWIRE 0x00000000 -#define PCI_SUBCLASS_SERIALBUS_ACCESS 0x00010000 -#define PCI_SUBCLASS_SERIALBUS_SSA 0x00020000 -#define PCI_SUBCLASS_SERIALBUS_USB 0x00030000 -#define PCI_SUBCLASS_SERIALBUS_FIBER 0x00040000 - -#define PCI_INTERFACE(d) (((d) >> 8) & 0xff) -#define PCI_SUBCLASS(d) ((d) & PCI_SUBCLASS_MASK) -#define PCI_CLASS(d) ((d) & PCI_CLASS_MASK) - - #include #include #include #include #include #include - #define PCI_OHCI_VENDORID_ALI 0x10b9 #define PCI_OHCI_VENDORID_CMDTECH 0x1095 #define PCI_OHCI_VENDORID_COMPAQ 0x0e11 #define PCI_OHCI_VENDORID_NEC 0x1033 #define PCI_OHCI_VENDORID_OPTI 0x1045 #define PCI_OHCI_VENDORID_SIS 0x1039 #define PCI_OHCI_DEVICEID_ALADDIN_V 0x523710b9 static const char *ohci_device_aladdin_v = "AcerLabs M5237 (Aladdin-V) USB Host Controller"; #define PCI_OHCI_DEVICEID_FIRELINK 0xc8611045 static const char *ohci_device_firelink = "OPTi 82C861 (FireLink) USB Host Controller"; #define PCI_OHCI_DEVICEID_NEC 0x00351033 static const char *ohci_device_nec = "NEC uPD 9210 USB Host Controller"; #define PCI_OHCI_DEVICEID_USB0670 0x06701095 static const char *ohci_device_usb0670 = "CMD Tech 670 (USB0670) USB Host Controller"; #define PCI_OHCI_DEVICEID_USB0673 0x06731095 static const char *ohci_device_usb0673 = "CMD Tech 673 (USB0673) USB Host Controller"; static const char *ohci_device_generic = "OHCI (generic) USB Host Controller"; +#define PCI_OHCI_BASE_REG 0x10 -static const char *ohci_pci_probe __P((pcici_t, pcidi_t)); -static void ohci_pci_attach __P((pcici_t, int)); - -static u_long ohci_count = 0; - -static struct pci_device ohci_pci_device = { - "ohci", - ohci_pci_probe, - ohci_pci_attach, - &ohci_count, - NULL -}; - -DATA_SET(pcidevice_set, ohci_pci_device); - - static const char * -ohci_pci_probe(pcici_t config_id, pcidi_t device_id) +ohci_pci_match(device_t dev) { - u_int32_t class; + u_int32_t device_id = pci_get_devid(dev); switch(device_id) { case PCI_OHCI_DEVICEID_ALADDIN_V: return (ohci_device_aladdin_v); case PCI_OHCI_DEVICEID_USB0670: return (ohci_device_usb0670); case PCI_OHCI_DEVICEID_USB0673: return (ohci_device_usb0673); case PCI_OHCI_DEVICEID_FIRELINK: return (ohci_device_firelink); case PCI_OHCI_DEVICEID_NEC: return (ohci_device_nec); default: - class = pci_conf_read(config_id, PCI_CLASS_REG); - if ( (PCI_CLASS(class) == PCI_CLASS_SERIALBUS) - && (PCI_SUBCLASS(class) == PCI_SUBCLASS_SERIALBUS_USB) - && (PCI_INTERFACE(class) == PCI_INTERFACE_OHCI)) { - return(ohci_device_generic); + if ( pci_get_class(dev) == PCIC_SERIALBUS + && pci_get_subclass(dev) == PCIS_SERIALBUS_USB + && pci_get_progif(dev) == PCI_INTERFACE_OHCI) { + return (ohci_device_generic); } } return NULL; /* dunno */ } -static void -ohci_pci_attach(pcici_t config_id, int unit) +static int +ohci_pci_probe(device_t dev) { - vm_offset_t pbase; + const char *desc = ohci_pci_match(dev); + if (desc) { + device_set_desc(dev, desc); + return 0; + } else { + return ENXIO; + } +} + +static int +ohci_pci_attach(device_t dev) +{ + int unit = device_get_unit(dev); + ohci_softc_t *sc = device_get_softc(dev); device_t usbus; - ohci_softc_t *sc; usbd_status err; - int id; + int rid; + struct resource *res; + void *ih; + int error; - sc = malloc(sizeof(ohci_softc_t), M_DEVBUF, M_NOWAIT); - /* Do not free it below, intr might use the sc */ - if ( sc == NULL ) { - printf("ohci%d: could not allocate memory", unit); - return; - } - memset(sc, 0, sizeof(ohci_softc_t)); - - if(!pci_map_mem(config_id, PCI_CBMEM, - (vm_offset_t *)&sc->sc_iobase, &pbase)) { - printf("ohci%d: could not map memory\n", unit); - return; + rid = PCI_CBMEM; + res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, + 0, ~0, 1, RF_ACTIVE); + if (!res) { + device_printf(dev, "could not map memory\n"); + return ENXIO; } - if ( !pci_map_int(config_id, (pci_inthand_t *)ohci_intr, - (void *) sc, &bio_imask)) { - printf("ohci%d: could not map irq\n", unit); - return; + sc->iot = rman_get_bustag(res); + sc->ioh = rman_get_bushandle(res); + + rid = 0; + res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, + RF_SHAREABLE | RF_ACTIVE); + if (res == NULL) { + device_printf(dev, "could not allocate irq\n"); + return ENOMEM; } - usbus = device_add_child(root_bus, "usb", -1, sc); + error = bus_setup_intr(dev, res, (driver_intr_t *) ohci_intr, sc, &ih); + if (error) { + device_printf(dev, "could not setup irq\n"); + return error; + } + + usbus = device_add_child(dev, "usb", -1, sc); if (!usbus) { - printf("ohci%d: could not add USB device to root bus\n", unit); - return; + printf("ohci%d: could not add USB device\n", unit); + return ENOMEM; } - id = pci_conf_read(config_id, PCI_ID_REG); - switch(id) { + switch (pci_get_devid(dev)) { case PCI_OHCI_DEVICEID_ALADDIN_V: device_set_desc(usbus, ohci_device_aladdin_v); sprintf(sc->sc_vendor, "AcerLabs"); break; case PCI_OHCI_DEVICEID_FIRELINK: device_set_desc(usbus, ohci_device_firelink); sprintf(sc->sc_vendor, "OPTi"); break; case PCI_OHCI_DEVICEID_NEC: device_set_desc(usbus, ohci_device_nec); sprintf(sc->sc_vendor, "NEC"); break; case PCI_OHCI_DEVICEID_USB0670: device_set_desc(usbus, ohci_device_usb0670); sprintf(sc->sc_vendor, "CMDTECH"); break; case PCI_OHCI_DEVICEID_USB0673: device_set_desc(usbus, ohci_device_usb0673); sprintf(sc->sc_vendor, "CMDTECH"); break; default: if (bootverbose) - printf("(New OHCI DeviceId=0x%08x)\n", id); + printf("(New OHCI DeviceId=0x%08x)\n", pci_get_devid(dev)); device_set_desc(usbus, ohci_device_generic); sprintf(sc->sc_vendor, "(unknown)"); } sc->sc_bus.bdev = usbus; err = ohci_init(sc); if (err != USBD_NORMAL_COMPLETION) { printf("ohci%d: init failed, error=%d\n", unit, err); - device_delete_child(root_bus, usbus); + device_delete_child(dev, usbus); } - return; + return device_probe_and_attach(sc->sc_bus.bdev); } + +static device_method_t ohci_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ohci_pci_probe), + DEVMETHOD(device_attach, ohci_pci_attach), + + /* Bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), + DEVMETHOD(bus_release_resource, bus_generic_release_resource), + DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), + DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), + DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), + DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), + + { 0, 0 } +}; + +static driver_t ohci_driver = { + "ohci", + ohci_methods, + DRIVER_TYPE_BIO, + sizeof(ohci_softc_t), +}; + +static devclass_t ohci_devclass; + +DRIVER_MODULE(ohci, pci, ohci_driver, ohci_devclass, 0, 0); Index: head/sys/pci/pci.c =================================================================== --- head/sys/pci/pci.c (revision 45719) +++ head/sys/pci/pci.c (revision 45720) @@ -1,948 +1,1377 @@ /* * Copyright (c) 1997, Stefan Esser * 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 unmodified, 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 ``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. * - * $Id: pci.c,v 1.93 1999/01/19 23:29:18 se Exp $ + * $Id: pci.c,v 1.94 1999/04/11 02:47:31 eivind Exp $ * */ +#include "opt_bus.h" + #include "pci.h" #if NPCI > 0 #include "opt_devfs.h" #include "opt_simos.h" #include #include #include #include #include #include #include #include #include #ifdef DEVFS #include #endif /* DEVFS */ #include #include #include +#include +#include +#include +#include + #include #include #include #ifdef APIC_IO #include #endif /* APIC_IO */ static STAILQ_HEAD(devlist, pci_devinfo) pci_devq; u_int32_t pci_numdevs = 0; static u_int32_t pci_generation = 0; /* return highest PCI bus number known to be used, or -1 if none */ static int pci_bushigh(void) { if (pci_cfgopen() == 0) return (-1); return (0); } /* return base address of memory or port map */ static int pci_mapbase(unsigned mapreg) { int mask = 0x03; if ((mapreg & 0x01) == 0) mask = 0x0f; return (mapreg & ~mask); } /* return map type of memory or port map */ static int pci_maptype(unsigned mapreg) { static u_int8_t maptype[0x10] = { PCI_MAPMEM, PCI_MAPPORT, PCI_MAPMEM, 0, PCI_MAPMEM, PCI_MAPPORT, 0, 0, PCI_MAPMEM|PCI_MAPMEMP, PCI_MAPPORT, PCI_MAPMEM|PCI_MAPMEMP, 0, PCI_MAPMEM|PCI_MAPMEMP, PCI_MAPPORT, 0, 0, }; return maptype[mapreg & 0x0f]; } /* return log2 of map size decoded for memory or port map */ static int pci_mapsize(unsigned testval) { int ln2size; testval = pci_mapbase(testval); ln2size = 0; if (testval != 0) { while ((testval & 1) == 0) { ln2size++; testval >>= 1; } } return (ln2size); } /* return log2 of address range supported by map register */ static int pci_maprange(unsigned mapreg) { int ln2range = 0; switch (mapreg & 0x07) { case 0x00: case 0x01: case 0x05: ln2range = 32; break; case 0x02: ln2range = 20; break; case 0x04: ln2range = 64; break; } return (ln2range); } /* extract map parameters into newly allocated array of pcimap structures */ static pcimap * pci_readmaps(pcicfgregs *cfg, int maxmaps) { int i, j = 0; pcimap *map; int map64 = 0; int reg = PCIR_MAPS; for (i = 0; i < maxmaps; i++) { int reg = PCIR_MAPS + i*4; u_int32_t base; u_int32_t ln2range; base = pci_cfgread(cfg, reg, 4); ln2range = pci_maprange(base); if (base == 0 || ln2range == 0 || base == 0xffffffff) continue; /* skip invalid entry */ else { j++; if (ln2range > 32) { i++; j++; } } } map = malloc(j * sizeof (pcimap), M_DEVBUF, M_WAITOK); if (map != NULL) { bzero(map, sizeof(pcimap) * j); cfg->nummaps = j; for (i = 0, j = 0; i < maxmaps; i++, reg += 4) { u_int32_t base; u_int32_t testval; base = pci_cfgread(cfg, reg, 4); if (map64 == 0) { if (base == 0 || base == 0xffffffff) continue; /* skip invalid entry */ pci_cfgwrite(cfg, reg, 0xffffffff, 4); testval = pci_cfgread(cfg, reg, 4); pci_cfgwrite(cfg, reg, base, 4); map[j].reg = reg; map[j].base = pci_mapbase(base); map[j].type = pci_maptype(base); map[j].ln2size = pci_mapsize(testval); map[j].ln2range = pci_maprange(testval); map64 = map[j].ln2range == 64; } else { /* only fill in base, other fields are 0 */ map[j].base = base; map64 = 0; } j++; } } return (map); } /* adjust some values from PCI 1.0 devices to match 2.0 standards ... */ static void pci_fixancient(pcicfgregs *cfg) { if (cfg->hdrtype != 0) return; /* PCI to PCI bridges use header type 1 */ if (cfg->baseclass == PCIC_BRIDGE && cfg->subclass == PCIS_BRIDGE_PCI) cfg->hdrtype = 1; } /* read config data specific to header type 1 device (PCI to PCI bridge) */ static void * pci_readppb(pcicfgregs *cfg) { pcih1cfgregs *p; p = malloc(sizeof (pcih1cfgregs), M_DEVBUF, M_WAITOK); if (p == NULL) return (NULL); bzero(p, sizeof *p); p->secstat = pci_cfgread(cfg, PCIR_SECSTAT_1, 2); p->bridgectl = pci_cfgread(cfg, PCIR_BRIDGECTL_1, 2); p->seclat = pci_cfgread(cfg, PCIR_SECLAT_1, 1); p->iobase = PCI_PPBIOBASE (pci_cfgread(cfg, PCIR_IOBASEH_1, 2), pci_cfgread(cfg, PCIR_IOBASEL_1, 1)); p->iolimit = PCI_PPBIOLIMIT (pci_cfgread(cfg, PCIR_IOLIMITH_1, 2), pci_cfgread(cfg, PCIR_IOLIMITL_1, 1)); p->membase = PCI_PPBMEMBASE (0, pci_cfgread(cfg, PCIR_MEMBASE_1, 2)); p->memlimit = PCI_PPBMEMLIMIT (0, pci_cfgread(cfg, PCIR_MEMLIMIT_1, 2)); p->pmembase = PCI_PPBMEMBASE ( (pci_addr_t)pci_cfgread(cfg, PCIR_PMBASEH_1, 4), pci_cfgread(cfg, PCIR_PMBASEL_1, 2)); p->pmemlimit = PCI_PPBMEMLIMIT ( (pci_addr_t)pci_cfgread(cfg, PCIR_PMLIMITH_1, 4), pci_cfgread(cfg, PCIR_PMLIMITL_1, 2)); return (p); } /* read config data specific to header type 2 device (PCI to CardBus bridge) */ static void * pci_readpcb(pcicfgregs *cfg) { pcih2cfgregs *p; p = malloc(sizeof (pcih2cfgregs), M_DEVBUF, M_WAITOK); if (p == NULL) return (NULL); bzero(p, sizeof *p); p->secstat = pci_cfgread(cfg, PCIR_SECSTAT_2, 2); p->bridgectl = pci_cfgread(cfg, PCIR_BRIDGECTL_2, 2); p->seclat = pci_cfgread(cfg, PCIR_SECLAT_2, 1); p->membase0 = pci_cfgread(cfg, PCIR_MEMBASE0_2, 4); p->memlimit0 = pci_cfgread(cfg, PCIR_MEMLIMIT0_2, 4); p->membase1 = pci_cfgread(cfg, PCIR_MEMBASE1_2, 4); p->memlimit1 = pci_cfgread(cfg, PCIR_MEMLIMIT1_2, 4); p->iobase0 = pci_cfgread(cfg, PCIR_IOBASE0_2, 4); p->iolimit0 = pci_cfgread(cfg, PCIR_IOLIMIT0_2, 4); p->iobase1 = pci_cfgread(cfg, PCIR_IOBASE1_2, 4); p->iolimit1 = pci_cfgread(cfg, PCIR_IOLIMIT1_2, 4); p->pccardif = pci_cfgread(cfg, PCIR_PCCARDIF_2, 4); return p; } /* extract header type specific config data */ static void pci_hdrtypedata(pcicfgregs *cfg) { switch (cfg->hdrtype) { case 0: cfg->subvendor = pci_cfgread(cfg, PCIR_SUBVEND_0, 2); cfg->subdevice = pci_cfgread(cfg, PCIR_SUBDEV_0, 2); cfg->map = pci_readmaps(cfg, PCI_MAXMAPS_0); break; case 1: cfg->subvendor = pci_cfgread(cfg, PCIR_SUBVEND_1, 2); cfg->subdevice = pci_cfgread(cfg, PCIR_SUBDEV_1, 2); cfg->secondarybus = pci_cfgread(cfg, PCIR_SECBUS_1, 1); cfg->subordinatebus = pci_cfgread(cfg, PCIR_SUBBUS_1, 1); cfg->map = pci_readmaps(cfg, PCI_MAXMAPS_1); cfg->hdrspec = pci_readppb(cfg); break; case 2: cfg->subvendor = pci_cfgread(cfg, PCIR_SUBVEND_2, 2); cfg->subdevice = pci_cfgread(cfg, PCIR_SUBDEV_2, 2); cfg->secondarybus = pci_cfgread(cfg, PCIR_SECBUS_2, 1); cfg->subordinatebus = pci_cfgread(cfg, PCIR_SUBBUS_2, 1); cfg->map = pci_readmaps(cfg, PCI_MAXMAPS_2); cfg->hdrspec = pci_readpcb(cfg); break; } } /* read configuration header into pcicfgrect structure */ static struct pci_devinfo * pci_readcfg(pcicfgregs *probe) { pcicfgregs *cfg = NULL; struct pci_devinfo *devlist_entry; struct devlist *devlist_head; devlist_head = &pci_devq; devlist_entry = NULL; if (pci_cfgread(probe, PCIR_DEVVENDOR, 4) != -1) { devlist_entry = malloc(sizeof(struct pci_devinfo), M_DEVBUF, M_WAITOK); if (devlist_entry == NULL) return (NULL); + bzero(devlist_entry, sizeof *devlist_entry); cfg = &devlist_entry->cfg; - bzero(cfg, sizeof *cfg); - cfg->bus = probe->bus; cfg->slot = probe->slot; cfg->func = probe->func; cfg->vendor = pci_cfgread(cfg, PCIR_VENDOR, 2); cfg->device = pci_cfgread(cfg, PCIR_DEVICE, 2); cfg->cmdreg = pci_cfgread(cfg, PCIR_COMMAND, 2); cfg->statreg = pci_cfgread(cfg, PCIR_STATUS, 2); cfg->baseclass = pci_cfgread(cfg, PCIR_CLASS, 1); cfg->subclass = pci_cfgread(cfg, PCIR_SUBCLASS, 1); cfg->progif = pci_cfgread(cfg, PCIR_PROGIF, 1); cfg->revid = pci_cfgread(cfg, PCIR_REVID, 1); cfg->hdrtype = pci_cfgread(cfg, PCIR_HEADERTYPE, 1); cfg->cachelnsz = pci_cfgread(cfg, PCIR_CACHELNSZ, 1); cfg->lattimer = pci_cfgread(cfg, PCIR_LATTIMER, 1); cfg->intpin = pci_cfgread(cfg, PCIR_INTPIN, 1); cfg->intline = pci_cfgread(cfg, PCIR_INTLINE, 1); #ifdef __alpha__ alpha_platform_assign_pciintr(cfg); #endif #ifdef APIC_IO if (cfg->intpin != 0) { int airq; airq = pci_apic_irq(cfg->bus, cfg->slot, cfg->intpin); if (airq >= 0) { /* PCI specific entry found in MP table */ if (airq != cfg->intline) { undirect_pci_irq(cfg->intline); cfg->intline = airq; } } else { /* * PCI interrupts might be redirected to the * ISA bus according to some MP tables. Use the * same methods as used by the ISA devices * devices to find the proper IOAPIC int pin. */ airq = isa_apic_irq(cfg->intline); if ((airq >= 0) && (airq != cfg->intline)) { /* XXX: undirect_pci_irq() ? */ undirect_isa_irq(cfg->intline); cfg->intline = airq; } } } #endif /* APIC_IO */ cfg->mingnt = pci_cfgread(cfg, PCIR_MINGNT, 1); cfg->maxlat = pci_cfgread(cfg, PCIR_MAXLAT, 1); cfg->mfdev = (cfg->hdrtype & PCIM_MFDEV) != 0; cfg->hdrtype &= ~PCIM_MFDEV; pci_fixancient(cfg); pci_hdrtypedata(cfg); STAILQ_INSERT_TAIL(devlist_head, devlist_entry, pci_links); devlist_entry->conf.pc_sel.pc_bus = cfg->bus; devlist_entry->conf.pc_sel.pc_dev = cfg->slot; devlist_entry->conf.pc_sel.pc_func = cfg->func; devlist_entry->conf.pc_hdr = cfg->hdrtype; devlist_entry->conf.pc_subvendor = cfg->subvendor; devlist_entry->conf.pc_subdevice = cfg->subdevice; devlist_entry->conf.pc_vendor = cfg->vendor; devlist_entry->conf.pc_device = cfg->device; devlist_entry->conf.pc_class = cfg->baseclass; devlist_entry->conf.pc_subclass = cfg->subclass; devlist_entry->conf.pc_progif = cfg->progif; devlist_entry->conf.pc_revid = cfg->revid; pci_numdevs++; pci_generation++; } return (devlist_entry); } #if 0 /* free pcicfgregs structure and all depending data structures */ static int pci_freecfg(struct pci_devinfo *dinfo) { struct devlist *devlist_head; devlist_head = &pci_devq; if (dinfo->cfg.hdrspec != NULL) free(dinfo->cfg.hdrspec, M_DEVBUF); if (dinfo->cfg.map != NULL) free(dinfo->cfg.map, M_DEVBUF); /* XXX this hasn't been tested */ STAILQ_REMOVE(devlist_head, dinfo, pci_devinfo, pci_links); free(dinfo, M_DEVBUF); /* increment the generation count */ pci_generation++; /* we're losing one device */ pci_numdevs--; return (0); } #endif -static void -pci_addcfg(struct pci_devinfo *dinfo) -{ - if (bootverbose) { - int i; - pcicfgregs *cfg = &dinfo->cfg; - - printf("found->\tvendor=0x%04x, dev=0x%04x, revid=0x%02x\n", - cfg->vendor, cfg->device, cfg->revid); - printf("\tclass=%02x-%02x-%02x, hdrtype=0x%02x, mfdev=%d\n", - cfg->baseclass, cfg->subclass, cfg->progif, - cfg->hdrtype, cfg->mfdev); - printf("\tsubordinatebus=%x \tsecondarybus=%x\n", - cfg->subordinatebus, cfg->secondarybus); -#ifdef PCI_DEBUG - printf("\tcmdreg=0x%04x, statreg=0x%04x, cachelnsz=%d (dwords)\n", - cfg->cmdreg, cfg->statreg, cfg->cachelnsz); - printf("\tlattimer=0x%02x (%d ns), mingnt=0x%02x (%d ns), maxlat=0x%02x (%d ns)\n", - cfg->lattimer, cfg->lattimer * 30, - cfg->mingnt, cfg->mingnt * 250, cfg->maxlat, cfg->maxlat * 250); -#endif /* PCI_DEBUG */ - if (cfg->intpin > 0) - printf("\tintpin=%c, irq=%d\n", cfg->intpin +'a' -1, cfg->intline); - - for (i = 0; i < cfg->nummaps; i++) { - pcimap *m = &cfg->map[i]; - printf("\tmap[%d]: type %x, range %2d, base %08x, size %2d\n", - i, m->type, m->ln2range, m->base, m->ln2size); - } - } - pci_drvattach(dinfo); /* XXX currently defined in pci_compat.c */ -} - -/* scan one PCI bus for devices */ - -static int -pci_probebus(int bus) -{ - pcicfgregs probe; - int bushigh = bus; - -#ifdef SIMOS -#undef PCI_SLOTMAX -#define PCI_SLOTMAX 0 -#endif - - bzero(&probe, sizeof probe); - /* XXX KDM */ - /* probe.parent = pci_bridgeto(bus); */ - probe.bus = bus; - for (probe.slot = 0; probe.slot <= PCI_SLOTMAX; probe.slot++) { - int pcifunchigh = 0; - for (probe.func = 0; probe.func <= pcifunchigh; probe.func++) { - struct pci_devinfo *dinfo = pci_readcfg(&probe); - if (dinfo != NULL) { - if (dinfo->cfg.mfdev) - pcifunchigh = 7; - /* - * XXX: Temporarily move pci_addcfg() up before - * the use of cfg->subordinatebus. This is - * necessary, since pci_addcfg() calls the - * device's probe(), which may read the bus# - * from some device dependent register of - * some host to PCI bridges. The probe will - * eventually be moved to pci_readcfg(), and - * pci_addcfg() will then be moved back down - * below the conditional statement ... - */ - pci_addcfg(dinfo); - - if (bushigh < dinfo->cfg.subordinatebus) - bushigh = dinfo->cfg.subordinatebus; - if (bushigh < dinfo->cfg.secondarybus) - bushigh = dinfo->cfg.secondarybus; - - /* XXX KDM */ - /* cfg = NULL; we don't own this anymore ... */ - } - } - } - return (bushigh); -} - -/* scan a PCI bus tree reached through one PCI attachment point */ - -int -pci_probe(pciattach *parent) -{ - int bushigh; - int bus = 0; - - STAILQ_INIT(&pci_devq); - - bushigh = pci_bushigh(); - while (bus <= bushigh) { - int newbushigh; - - printf("Probing for devices on PCI bus %d:\n", bus); - newbushigh = pci_probebus(bus); - - if (bushigh < newbushigh) - bushigh = newbushigh; - bus++; - } - return (bushigh); -} - /* * This is the user interface to PCI configuration space. */ static int pci_open(dev_t dev, int oflags, int devtype, struct proc *p) { if ((oflags & FWRITE) && securelevel > 0) { return EPERM; } return 0; } static int pci_close(dev_t dev, int flag, int devtype, struct proc *p) { return 0; } /* * Match a single pci_conf structure against an array of pci_match_conf * structures. The first argument, 'matches', is an array of num_matches * pci_match_conf structures. match_buf is a pointer to the pci_conf * structure that will be compared to every entry in the matches array. * This function returns 1 on failure, 0 on success. */ static int pci_conf_match(struct pci_match_conf *matches, int num_matches, struct pci_conf *match_buf) { int i; if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0)) return(1); for (i = 0; i < num_matches; i++) { /* * I'm not sure why someone would do this...but... */ if (matches[i].flags == PCI_GETCONF_NO_MATCH) continue; /* * Look at each of the match flags. If it's set, do the * comparison. If the comparison fails, we don't have a * match, go on to the next item if there is one. */ if (((matches[i].flags & PCI_GETCONF_MATCH_BUS) != 0) && (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus)) continue; if (((matches[i].flags & PCI_GETCONF_MATCH_DEV) != 0) && (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev)) continue; if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC) != 0) && (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func)) continue; if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR) != 0) && (match_buf->pc_vendor != matches[i].pc_vendor)) continue; if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE) != 0) && (match_buf->pc_device != matches[i].pc_device)) continue; if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS) != 0) && (match_buf->pc_class != matches[i].pc_class)) continue; if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT) != 0) && (match_buf->pd_unit != matches[i].pd_unit)) continue; if (((matches[i].flags & PCI_GETCONF_MATCH_NAME) != 0) && (strncmp(matches[i].pd_name, match_buf->pd_name, sizeof(match_buf->pd_name)) != 0)) continue; return(0); } return(1); } static int pci_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) { struct pci_io *io; int error; if (!(flag & FWRITE)) return EPERM; switch(cmd) { case PCIOCGETCONF: { struct pci_devinfo *dinfo; struct pci_conf_io *cio; struct devlist *devlist_head; struct pci_match_conf *pattern_buf; int num_patterns; size_t iolen; int ionum, i; cio = (struct pci_conf_io *)data; num_patterns = 0; dinfo = NULL; /* * Hopefully the user won't pass in a null pointer, but it * can't hurt to check. */ if (cio == NULL) { error = EINVAL; break; } /* * If the user specified an offset into the device list, * but the list has changed since they last called this * ioctl, tell them that the list has changed. They will * have to get the list from the beginning. */ if ((cio->offset != 0) && (cio->generation != pci_generation)){ cio->num_matches = 0; cio->status = PCI_GETCONF_LIST_CHANGED; error = 0; break; } /* * Check to see whether the user has asked for an offset * past the end of our list. */ if (cio->offset >= pci_numdevs) { cio->num_matches = 0; cio->status = PCI_GETCONF_LAST_DEVICE; error = 0; break; } /* get the head of the device queue */ devlist_head = &pci_devq; /* * Determine how much room we have for pci_conf structures. * Round the user's buffer size down to the nearest * multiple of sizeof(struct pci_conf) in case the user * didn't specify a multiple of that size. */ iolen = min(cio->match_buf_len - (cio->match_buf_len % sizeof(struct pci_conf)), pci_numdevs * sizeof(struct pci_conf)); /* * Since we know that iolen is a multiple of the size of * the pciconf union, it's okay to do this. */ ionum = iolen / sizeof(struct pci_conf); /* * If this test is true, the user wants the pci_conf * structures returned to match the supplied entries. */ if ((cio->num_patterns > 0) && (cio->pat_buf_len > 0)) { /* * pat_buf_len needs to be: * num_patterns * sizeof(struct pci_match_conf) * While it is certainly possible the user just * allocated a large buffer, but set the number of * matches correctly, it is far more likely that * their kernel doesn't match the userland utility * they're using. It's also possible that the user * forgot to initialize some variables. Yes, this * may be overly picky, but I hazard to guess that * it's far more likely to just catch folks that * updated their kernel but not their userland. */ if ((cio->num_patterns * sizeof(struct pci_match_conf)) != cio->pat_buf_len){ /* The user made a mistake, return an error*/ cio->status = PCI_GETCONF_ERROR; printf("pci_ioctl: pat_buf_len %d != " "num_patterns (%d) * sizeof(struct " "pci_match_conf) (%d)\npci_ioctl: " "pat_buf_len should be = %d\n", cio->pat_buf_len, cio->num_patterns, - sizeof(struct pci_match_conf), - sizeof(struct pci_match_conf) * + (int)sizeof(struct pci_match_conf), + (int)sizeof(struct pci_match_conf) * cio->num_patterns); printf("pci_ioctl: do your headers match your " "kernel?\n"); cio->num_matches = 0; error = EINVAL; break; } /* * Check the user's buffer to make sure it's readable. */ if ((error = useracc((caddr_t)cio->patterns, cio->pat_buf_len, B_READ)) != 1){ printf("pci_ioctl: pattern buffer %p, " "length %u isn't user accessible for" " READ\n", cio->patterns, cio->pat_buf_len); error = EACCES; break; } /* * Allocate a buffer to hold the patterns. */ pattern_buf = malloc(cio->pat_buf_len, M_TEMP, M_WAITOK); error = copyin(cio->patterns, pattern_buf, cio->pat_buf_len); if (error != 0) break; num_patterns = cio->num_patterns; } else if ((cio->num_patterns > 0) || (cio->pat_buf_len > 0)) { /* * The user made a mistake, spit out an error. */ cio->status = PCI_GETCONF_ERROR; cio->num_matches = 0; printf("pci_ioctl: invalid GETCONF arguments\n"); error = EINVAL; break; } else pattern_buf = NULL; /* * Make sure we can write to the match buffer. */ if ((error = useracc((caddr_t)cio->matches, cio->match_buf_len, B_WRITE)) != 1) { printf("pci_ioctl: match buffer %p, length %u " "isn't user accessible for WRITE\n", cio->matches, cio->match_buf_len); error = EACCES; break; } /* * Go through the list of devices and copy out the devices * that match the user's criteria. */ for (cio->num_matches = 0, error = 0, i = 0, dinfo = STAILQ_FIRST(devlist_head); (dinfo != NULL) && (cio->num_matches < ionum) && (error == 0) && (i < pci_numdevs); dinfo = STAILQ_NEXT(dinfo, pci_links), i++) { if (i < cio->offset) continue; if ((pattern_buf == NULL) || (pci_conf_match(pattern_buf, num_patterns, &dinfo->conf) == 0)) { /* * If we've filled up the user's buffer, * break out at this point. Since we've * got a match here, we'll pick right back * up at the matching entry. We can also * tell the user that there are more matches * left. */ if (cio->num_matches >= ionum) break; error = copyout(&dinfo->conf, &cio->matches[cio->num_matches], sizeof(struct pci_conf)); cio->num_matches++; } } /* * Set the pointer into the list, so if the user is getting * n records at a time, where n < pci_numdevs, */ cio->offset = i; /* * Set the generation, the user will need this if they make * another ioctl call with offset != 0. */ cio->generation = pci_generation; /* * If this is the last device, inform the user so he won't * bother asking for more devices. If dinfo isn't NULL, we * know that there are more matches in the list because of * the way the traversal is done. */ if (dinfo == NULL) cio->status = PCI_GETCONF_LAST_DEVICE; else cio->status = PCI_GETCONF_MORE_DEVS; if (pattern_buf != NULL) free(pattern_buf, M_TEMP); break; } case PCIOCREAD: io = (struct pci_io *)data; switch(io->pi_width) { pcicfgregs probe; case 4: case 2: case 1: probe.bus = io->pi_sel.pc_bus; probe.slot = io->pi_sel.pc_dev; probe.func = io->pi_sel.pc_func; io->pi_data = pci_cfgread(&probe, io->pi_reg, io->pi_width); error = 0; break; default: error = ENODEV; break; } break; case PCIOCWRITE: io = (struct pci_io *)data; switch(io->pi_width) { pcicfgregs probe; case 4: case 2: case 1: probe.bus = io->pi_sel.pc_bus; probe.slot = io->pi_sel.pc_dev; probe.func = io->pi_sel.pc_func; pci_cfgwrite(&probe, io->pi_reg, io->pi_data, io->pi_width); error = 0; break; default: error = ENODEV; break; } break; default: error = ENOTTY; break; } return (error); } #define PCI_CDEV 78 static struct cdevsw pcicdev = { pci_open, pci_close, noread, nowrite, pci_ioctl, nostop, noreset, nodevtotty, seltrue, nommap, nostrategy, "pci", 0, PCI_CDEV }; #ifdef DEVFS static void *pci_devfs_token; #endif static void pci_cdevinit(void *dummy) { dev_t dev; dev = makedev(PCI_CDEV, 0); cdevsw_add(&dev, &pcicdev, NULL); #ifdef DEVFS pci_devfs_token = devfs_add_devswf(&pcicdev, 0, DV_CHR, UID_ROOT, GID_WHEEL, 0644, "pci"); #endif } SYSINIT(pcidev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE+PCI_CDEV, pci_cdevinit, NULL); + +#include "pci_if.h" + +/* + * A simple driver to wrap the old pci driver mechanism for back-compat. + */ + +static int +pci_compat_probe(device_t dev) +{ + struct pci_device *dvp; + struct pci_devinfo *dinfo; + pcicfgregs *cfg; + const char *name; + int error; + + dinfo = device_get_ivars(dev); + cfg = &dinfo->cfg; + dvp = device_get_driver(dev)->priv; + + /* + * Do the wrapped probe. + */ + error = ENXIO; + if (dvp && dvp->pd_probe) { + name = dvp->pd_probe(cfg, (cfg->device << 16) + cfg->vendor); + if (name) { + device_set_desc_copy(dev, name); + error = 0; + } + } + + return error; +} + +static int +pci_compat_attach(device_t dev) +{ + struct pci_device *dvp; + struct pci_devinfo *dinfo; + pcicfgregs *cfg; + int unit; + + dinfo = device_get_ivars(dev); + cfg = &dinfo->cfg; + dvp = device_get_driver(dev)->priv; + + unit = device_get_unit(dev); + if (unit > *dvp->pd_count) + *dvp->pd_count = unit; + if (dvp->pd_attach) + dvp->pd_attach(cfg, unit); + + /* + * XXX KDM for some devices, dvp->pd_name winds up NULL. + * I haven't investigated enough to figure out why this + * would happen. + */ + if (dvp->pd_name != NULL) + strncpy(dinfo->conf.pd_name, dvp->pd_name, + sizeof(dinfo->conf.pd_name)); + else + strncpy(dinfo->conf.pd_name, "????", + sizeof(dinfo->conf.pd_name)); + dinfo->conf.pd_name[sizeof(dinfo->conf.pd_name) - 1] = 0; + dinfo->conf.pd_unit = unit; + + return 0; +} + +static device_method_t pci_compat_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, pci_compat_probe), + DEVMETHOD(device_attach, pci_compat_attach), + + { 0, 0 } +}; + +static devclass_t pci_devclass; + +/* + * Create a new style driver around each old pci driver. + */ +static void +pci_wrap_old_drivers(void) +{ + struct pci_device **dvpp, *dvp; + + dvpp = (struct pci_device **)pcidevice_set.ls_items; + while ((dvp = *dvpp++) != NULL) { + driver_t *driver; + driver = malloc(sizeof(driver_t), M_DEVBUF, M_NOWAIT); + if (!driver) + continue; + bzero(driver, sizeof(driver_t)); + driver->name = dvp->pd_name; + driver->methods = pci_compat_methods; + driver->type = 0; /* XXX fixup in pci_map_int() */ + driver->softc = sizeof(struct pci_devinfo *); + driver->priv = dvp; + devclass_add_driver(pci_devclass, driver); + } +} + +/* + * New style pci driver. Parent device is either a pci-host-bridge or a + * pci-pci-bridge. Both kinds are represented by instances of pcib. + */ + +static void +pci_print_verbose(struct pci_devinfo *dinfo) +{ + if (bootverbose) { + int i; + pcicfgregs *cfg = &dinfo->cfg; + + printf("found->\tvendor=0x%04x, dev=0x%04x, revid=0x%02x\n", + cfg->vendor, cfg->device, cfg->revid); + printf("\tclass=%02x-%02x-%02x, hdrtype=0x%02x, mfdev=%d\n", + cfg->baseclass, cfg->subclass, cfg->progif, + cfg->hdrtype, cfg->mfdev); + printf("\tsubordinatebus=%x \tsecondarybus=%x\n", + cfg->subordinatebus, cfg->secondarybus); +#ifdef PCI_DEBUG + printf("\tcmdreg=0x%04x, statreg=0x%04x, cachelnsz=%d (dwords)\n", + cfg->cmdreg, cfg->statreg, cfg->cachelnsz); + printf("\tlattimer=0x%02x (%d ns), mingnt=0x%02x (%d ns), maxlat=0x%02x (%d ns)\n", + cfg->lattimer, cfg->lattimer * 30, + cfg->mingnt, cfg->mingnt * 250, cfg->maxlat, cfg->maxlat * 250); +#endif /* PCI_DEBUG */ + if (cfg->intpin > 0) + printf("\tintpin=%c, irq=%d\n", cfg->intpin +'a' -1, cfg->intline); + + for (i = 0; i < cfg->nummaps; i++) { + pcimap *m = &cfg->map[i]; + printf("\tmap[%d]: type %x, range %2d, base %08x, size %2d\n", + i, m->type, m->ln2range, m->base, m->ln2size); + } + } +} + +static int +pci_add_children(device_t dev, int busno) +{ + pcicfgregs probe; + int bushigh = busno; + +#ifdef SIMOS +#undef PCI_SLOTMAX +#define PCI_SLOTMAX 0 +#endif + + bzero(&probe, sizeof probe); + /* XXX KDM */ + /* probe.parent = pci_bridgeto(bus); */ + probe.bus = busno; + for (probe.slot = 0; probe.slot <= PCI_SLOTMAX; probe.slot++) { + int pcifunchigh = 0; + for (probe.func = 0; probe.func <= pcifunchigh; probe.func++) { + struct pci_devinfo *dinfo = pci_readcfg(&probe); + if (dinfo != NULL) { + if (dinfo->cfg.mfdev) + pcifunchigh = 7; + + pci_print_verbose(dinfo); + dinfo->cfg.dev = + device_add_child(dev, NULL, -1, dinfo); + + if (bushigh < dinfo->cfg.subordinatebus) + bushigh = dinfo->cfg.subordinatebus; + if (bushigh < dinfo->cfg.secondarybus) + bushigh = dinfo->cfg.secondarybus; + } + } + } + + return bushigh; +} + +static int +pci_new_probe(device_t dev) +{ + STAILQ_INIT(&pci_devq); + device_set_desc(dev, "PCI bus"); + + pci_add_children(dev, device_get_unit(dev)); + + return 0; +} + +static void +pci_print_child(device_t dev, device_t child) +{ + printf(" at device %d.%d", pci_get_slot(child), pci_get_function(child)); + printf(" on %s%d", device_get_name(dev), device_get_unit(dev)); +} + +static int +pci_read_ivar(device_t dev, device_t child, int which, u_long *result) +{ + struct pci_devinfo *dinfo; + pcicfgregs *cfg; + + dinfo = device_get_ivars(child); + cfg = &dinfo->cfg; + + switch (which) { + case PCI_IVAR_SUBVENDOR: + *result = cfg->subvendor; + break; + case PCI_IVAR_SUBDEVICE: + *result = cfg->subdevice; + break; + case PCI_IVAR_VENDOR: + *result = cfg->vendor; + break; + case PCI_IVAR_DEVICE: + *result = cfg->device; + break; + case PCI_IVAR_DEVID: + *result = (cfg->device << 16) | cfg->vendor; + break; + case PCI_IVAR_CLASS: + *result = cfg->baseclass; + break; + case PCI_IVAR_SUBCLASS: + *result = cfg->subclass; + break; + case PCI_IVAR_PROGIF: + *result = cfg->progif; + break; + case PCI_IVAR_REVID: + *result = cfg->revid; + break; + case PCI_IVAR_INTPIN: + *result = cfg->intpin; + break; + case PCI_IVAR_IRQ: + *result = cfg->intline; + break; + case PCI_IVAR_BUS: + *result = cfg->bus; + break; + case PCI_IVAR_SLOT: + *result = cfg->slot; + break; + case PCI_IVAR_FUNCTION: + *result = cfg->func; + break; + case PCI_IVAR_SECONDARYBUS: + *result = cfg->secondarybus; + break; + case PCI_IVAR_SUBORDINATEBUS: + *result = cfg->subordinatebus; + break; + default: + return ENOENT; + } + return 0; +} + +static int +pci_write_ivar(device_t dev, device_t child, int which, uintptr_t value) +{ + struct pci_devinfo *dinfo; + pcicfgregs *cfg; + + dinfo = device_get_ivars(child); + cfg = &dinfo->cfg; + + switch (which) { + case PCI_IVAR_SUBVENDOR: + case PCI_IVAR_SUBDEVICE: + case PCI_IVAR_VENDOR: + case PCI_IVAR_DEVICE: + case PCI_IVAR_DEVID: + case PCI_IVAR_CLASS: + case PCI_IVAR_SUBCLASS: + case PCI_IVAR_PROGIF: + case PCI_IVAR_REVID: + case PCI_IVAR_INTPIN: + case PCI_IVAR_IRQ: + case PCI_IVAR_BUS: + case PCI_IVAR_SLOT: + case PCI_IVAR_FUNCTION: + return EINVAL; /* disallow for now */ + + case PCI_IVAR_SECONDARYBUS: + cfg->secondarybus = value; + break; + case PCI_IVAR_SUBORDINATEBUS: + cfg->subordinatebus = value; + break; + default: + return ENOENT; + } + return 0; +} + +static int +pci_mapno(pcicfgregs *cfg, int reg) +{ + int i, nummaps; + pcimap *map; + + nummaps = cfg->nummaps; + map = cfg->map; + + for (i = 0; i < nummaps; i++) + if (map[i].reg == reg) + return (i); + return (-1); +} + +static int +pci_porten(pcicfgregs *cfg) +{ + return ((cfg->cmdreg & PCIM_CMD_PORTEN) != 0); +} + +static int +pci_isportmap(pcicfgregs *cfg, int map) + +{ + return ((unsigned)map < cfg->nummaps + && (cfg->map[map].type & PCI_MAPPORT) != 0); +} + +static int +pci_memen(pcicfgregs *cfg) +{ + return ((cfg->cmdreg & PCIM_CMD_MEMEN) != 0); +} + +static int +pci_ismemmap(pcicfgregs *cfg, int map) +{ + return ((unsigned)map < cfg->nummaps + && (cfg->map[map].type & PCI_MAPMEM) != 0); +} + +static struct resource * +pci_alloc_resource(device_t dev, device_t child, int type, int *rid, + u_long start, u_long end, u_long count, u_int flags) +{ + int isdefault; + struct pci_devinfo *dinfo = device_get_ivars(child); + pcicfgregs *cfg = &dinfo->cfg; + struct resource *rv, **rvp = 0; + int map; + + isdefault = (device_get_parent(child) == dev + && start == 0UL && end == ~0UL && count == 1); + + switch (type) { + case SYS_RES_IRQ: + if (*rid != 0) + return 0; + if (isdefault && cfg->intline != 255) { + start = cfg->intline; + end = cfg->intline; + count = 1; + } + break; + + case SYS_RES_DRQ: /* passthru for child isa */ + break; + + case SYS_RES_MEMORY: + if (isdefault) { + map = pci_mapno(cfg, *rid); + if (pci_memen(cfg) && pci_ismemmap(cfg, map)) { + start = cfg->map[map].base; + count = 1 << cfg->map[map].ln2size; + end = start + count; + rvp = &cfg->map[map].res; + } else + return 0; + } + break; + + case SYS_RES_IOPORT: + if (isdefault) { + map = pci_mapno(cfg, *rid); + if (pci_porten(cfg) && pci_isportmap(cfg, map)) { + start = cfg->map[map].base; + count = 1 << cfg->map[map].ln2size; + end = start + count; + rvp = &cfg->map[map].res; + } else + return 0; + } + break; + + default: + return 0; + } + + rv = BUS_ALLOC_RESOURCE(device_get_parent(dev), child, + type, rid, start, end, count, flags); + if (rvp) + *rvp = rv; + + return rv; +} + +static int +pci_release_resource(device_t dev, device_t child, int type, int rid, + struct resource *r) +{ + int rv; + struct pci_devinfo *dinfo = device_get_ivars(child); + pcicfgregs *cfg = &dinfo->cfg; + int map = 0; + + switch (type) { + case SYS_RES_IRQ: + if (rid != 0) + return EINVAL; + break; + + case SYS_RES_DRQ: /* passthru for child isa */ + break; + + case SYS_RES_MEMORY: + case SYS_RES_IOPORT: + /* + * Only check the map registers if this is a direct + * descendant. + */ + if (device_get_parent(child) == dev) + map = pci_mapno(cfg, rid); + else + map = -1; + break; + + default: + return (ENOENT); + } + + rv = BUS_RELEASE_RESOURCE(device_get_parent(dev), child, type, rid, r); + + if (rv == 0) { + switch (type) { + case SYS_RES_IRQ: + cfg->irqres = 0; + break; + + case SYS_RES_DRQ: /* passthru for child isa */ + break; + + case SYS_RES_MEMORY: + case SYS_RES_IOPORT: + if (map != -1) + cfg->map[map].res = 0; + break; + + default: + return ENOENT; + } + } + + return rv; +} + +static u_int32_t +pci_read_config_method(device_t dev, device_t child, int reg, int width) +{ + struct pci_devinfo *dinfo = device_get_ivars(child); + pcicfgregs *cfg = &dinfo->cfg; + return pci_cfgread(cfg, reg, width); +} + +static void +pci_write_config_method(device_t dev, device_t child, int reg, + u_int32_t val, int width) +{ + struct pci_devinfo *dinfo = device_get_ivars(child); + pcicfgregs *cfg = &dinfo->cfg; + pci_cfgwrite(cfg, reg, val, width); +} + +static int +pci_modevent(module_t mod, int what, void *arg) +{ + switch (what) { + case MOD_LOAD: + pci_wrap_old_drivers(); + break; + + case MOD_UNLOAD: + break; + } + + return 0; +} + +static device_method_t pci_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, pci_new_probe), + DEVMETHOD(device_attach, bus_generic_attach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + + /* Bus interface */ + DEVMETHOD(bus_print_child, pci_print_child), + DEVMETHOD(bus_read_ivar, pci_read_ivar), + DEVMETHOD(bus_write_ivar, pci_write_ivar), + DEVMETHOD(bus_driver_added, bus_generic_driver_added), + DEVMETHOD(bus_alloc_resource, pci_alloc_resource), + DEVMETHOD(bus_release_resource, pci_release_resource), + DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), + DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), + DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), + DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), + + /* PCI interface */ + DEVMETHOD(pci_read_config, pci_read_config_method), + DEVMETHOD(pci_write_config, pci_write_config_method), + + { 0, 0 } +}; + +static driver_t pci_driver = { + "pci", + pci_methods, + DRIVER_TYPE_MISC, + 1, /* no softc */ +}; + +DRIVER_MODULE(pci, pcib, pci_driver, pci_devclass, pci_modevent, 0); #endif /* NPCI > 0 */ Index: head/sys/pci/pci_compat.c =================================================================== --- head/sys/pci/pci_compat.c (revision 45719) +++ head/sys/pci/pci_compat.c (revision 45720) @@ -1,484 +1,460 @@ /* * Copyright (c) 1997, Stefan Esser * 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 unmodified, 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 ``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. * - * $Id: pci_compat.c,v 1.20 1999/01/19 23:29:19 se Exp $ + * $Id: pci_compat.c,v 1.21 1999/04/11 02:46:20 eivind Exp $ * */ +#include "opt_bus.h" + #include "pci.h" #if NPCI > 0 /* for compatibility to FreeBSD-2.2 version of PCI code */ #include #include #include #include #include #include #include +#include +#include +#include +#include + #include #include #ifdef RESOURCE_CHECK #include #endif #ifdef APIC_IO #include #endif #ifdef PCI_COMPAT /* ------------------------------------------------------------------------- */ -static int -pci_mapno(pcicfgregs *cfg, int reg) -{ - int i, nummaps; - pcimap *map; - - nummaps = cfg->nummaps; - map = cfg->map; - - for (i = 0; i < nummaps; i++) - if (map[i].reg == reg) - return (i); - return (-1); -} - -static int -pci_porten(pcicfgregs *cfg) -{ - return ((cfg->cmdreg & PCIM_CMD_PORTEN) != 0); -} - -static int -pci_isportmap(pcicfgregs *cfg, int map) - -{ - return ((unsigned)map < cfg->nummaps - && (cfg->map[map].type & PCI_MAPPORT) != 0); -} - -static int -pci_memen(pcicfgregs *cfg) -{ - return ((cfg->cmdreg & PCIM_CMD_MEMEN) != 0); -} - -static int -pci_ismemmap(pcicfgregs *cfg, int map) -{ - return ((unsigned)map < cfg->nummaps - && (cfg->map[map].type & PCI_MAPMEM) != 0); -} - -/* ------------------------------------------------------------------------- */ - u_long -pci_conf_read(pcici_t tag, u_long reg) +pci_conf_read(pcici_t cfg, u_long reg) { - return (pci_cfgread(tag, reg, 4)); + return (pci_read_config(cfg->dev, reg, 4)); } void -pci_conf_write(pcici_t tag, u_long reg, u_long data) +pci_conf_write(pcici_t cfg, u_long reg, u_long data) { - pci_cfgwrite(tag, reg, data, 4); + pci_write_config(cfg->dev, reg, data, 4); } int pci_map_port(pcici_t cfg, u_long reg, pci_port_t* pa) { - int map; + int rid; + struct resource *res; - map = pci_mapno(cfg, reg); - if (pci_porten(cfg) && pci_isportmap(cfg, map)) { - u_int32_t iobase; - u_int32_t iosize; - - iobase = cfg->map[map].base; - iosize = 1 << cfg->map[map].ln2size; -#ifdef RESOURCE_CHECK - if (resource_claim(cfg, REST_PORT, RESF_NONE, - iobase, iobase + iosize -1) == 0) -#endif /* RESOURCE_CHECK */ - { - *pa = iobase; - return (1); - } + rid = reg; + res = bus_alloc_resource(cfg->dev, SYS_RES_IOPORT, &rid, + 0, ~0, 1, RF_ACTIVE); + if (res) { + *pa = rman_get_start(res); + return (1); } return (0); } int pci_map_mem(pcici_t cfg, u_long reg, vm_offset_t* va, vm_offset_t* pa) { - int map; + int rid; + struct resource *res; - map = pci_mapno(cfg, reg); - if (pci_memen(cfg) && pci_ismemmap(cfg, map)) { - u_int32_t paddr; - u_int32_t psize; - - paddr = cfg->map[map].base; - psize = 1 << cfg->map[map].ln2size; -#ifdef RESOURCE_CHECK - if (resource_claim(cfg, REST_MEM, RESF_NONE, - paddr, paddr + psize -1) == 0) -#endif /* RESOURCE_CHECK */ - { - u_int32_t poffs; - vm_offset_t vaddr; - - poffs = paddr - trunc_page(paddr); -#ifdef __i386__ - vaddr = (vm_offset_t)pmap_mapdev(paddr-poffs, psize+poffs); -#endif -#ifdef __alpha__ - vaddr = paddr-poffs; -#endif - if (vaddr != NULL) { - vaddr += poffs; - *va = vaddr; - *pa = paddr; - return (1); - } - } + rid = reg; + res = bus_alloc_resource(cfg->dev, SYS_RES_MEMORY, &rid, + 0, ~0, 1, RF_ACTIVE); + if (res) { + *pa = rman_get_start(res); + *va = (vm_offset_t) rman_get_virtual(res); + return (1); } return (0); } int pci_map_dense(pcici_t cfg, u_long reg, vm_offset_t* va, vm_offset_t* pa) { - if(pci_map_mem(cfg, reg, va, pa)){ + if (pci_map_mem(cfg, reg, va, pa)){ #ifdef __alpha__ vm_offset_t dense; if(dense = pci_cvt_to_dense(*pa)){ *pa = dense; *va = ALPHA_PHYS_TO_K0SEG(*pa); return (1); } #endif #ifdef __i386__ return(1); #endif } return (0); } int pci_map_bwx(pcici_t cfg, u_long reg, vm_offset_t* va, vm_offset_t* pa) { - if(pci_map_mem(cfg, reg, va, pa)){ + if (pci_map_mem(cfg, reg, va, pa)){ #ifdef __alpha__ vm_offset_t bwx; if(bwx = pci_cvt_to_bwx(*pa)){ *pa = bwx; *va = ALPHA_PHYS_TO_K0SEG(*pa); return (1); } #endif #ifdef __i386__ return(1); #endif } return (0); } int pci_map_int(pcici_t cfg, pci_inthand_t *handler, void *arg, intrmask_t *maskptr) { return (pci_map_int_right(cfg, handler, arg, maskptr, 0)); } int pci_map_int_right(pcici_t cfg, pci_inthand_t *handler, void *arg, intrmask_t *maskptr, u_int flags) { int error; #ifdef APIC_IO int nextpin, muxcnt; #endif if (cfg->intpin != 0) { int irq = cfg->intline; - void *dev_instance = (void *)-1; /* XXX use cfg->devdata */ - void *idesc; + driver_t *driver = device_get_driver(cfg->dev); + int rid = 0; + struct resource *res; + void *ih; + res = bus_alloc_resource(cfg->dev, SYS_RES_IRQ, &rid, + irq, irq, 1, RF_SHAREABLE|RF_ACTIVE); + if (!res) { + printf("pci_map_int: can't allocate interrupt\n"); + return 0; + } - idesc = intr_create(dev_instance, irq, handler, arg, maskptr, - flags); - error = intr_connect(idesc); + /* + * This is ugly. Translate the mask into a driver type. + */ + if (maskptr == &tty_imask) + driver->type |= DRIVER_TYPE_TTY; + else if (maskptr == &bio_imask) + driver->type |= DRIVER_TYPE_BIO; + else if (maskptr == &net_imask) + driver->type |= DRIVER_TYPE_NET; + else if (maskptr == &cam_imask) + driver->type |= DRIVER_TYPE_CAM; + + error = BUS_SETUP_INTR(device_get_parent(cfg->dev), cfg->dev, + res, handler, arg, &ih); if (error != 0) return 0; + +#ifdef NEW_BUS_PCI + /* + * XXX this apic stuff looks totally busted. It should + * move to the nexus code which actually registers the + * interrupt. + */ +#endif + #ifdef APIC_IO nextpin = next_apic_irq(irq); if (nextpin < 0) return 1; /* * Attempt handling of some broken mp tables. * * It's OK to yell (since the mp tables are broken). * * Hanging in the boot is not OK */ muxcnt = 2; nextpin = next_apic_irq(nextpin); while (muxcnt < 5 && nextpin >= 0) { muxcnt++; nextpin = next_apic_irq(nextpin); } if (muxcnt >= 5) { printf("bogus MP table, more than 4 IO APIC pins connected to the same PCI device or ISA/EISA interrupt\n"); return 0; } printf("bogus MP table, %d IO APIC pins connected to the same PCI device or ISA/EISA interrupt\n", muxcnt); nextpin = next_apic_irq(irq); while (nextpin >= 0) { - idesc = intr_create(dev_instance, nextpin, handler, - arg, maskptr, flags); - error = intr_connect(idesc); - if (error != 0) + rid = 0; + res = bus_alloc_resource(cfg->dev, SYS_RES_IRQ, &rid, + nextpin, nextpin, 1, + RF_SHAREABLE|RF_ACTIVE); + if (!res) { + printf("pci_map_int: can't allocate extra interrupt\n"); return 0; + } + error = BUS_SETUP_INTR(device_get_parent(cfg->dev), + cfg->dev, res, handler, arg, + &ih); + if (error != 0) { + printf("pci_map_int: BUS_SETUP_INTR failed\n"); + return 0; + } printf("Registered extra interrupt handler for int %d (in addition to int %d)\n", nextpin, irq); nextpin = next_apic_irq(nextpin); } #endif } return (1); } int pci_unmap_int(pcici_t cfg) { return (0); /* not supported, yet, since cfg doesn't know about idesc */ } +#if 0 /* ------------------------------------------------------------------------- */ /* * Preliminary support for "wired" PCI devices. * This code supports currently only devices on PCI bus 0, since the * mapping from PCI BIOS bus numbers to configuration file bus numbers * is not yet maintained, whenever a PCI to PCI bridge is found. * The "bus" field of "pciwirecfg" correlates an PCI bus with the bridge * it is attached to. The "biosbus" field is to be updated for each bus, * whose bridge is probed. An entry with bus != 0 and biosbus == 0 is * invalid and will be skipped in the search for a wired unit, but not * in the test for a free unit number. */ typedef struct { char *name; int unit; u_int8_t bus; u_int8_t slot; u_int8_t func; u_int8_t biosbus; } pciwirecfg; static pciwirecfg pci_wireddevs[] = { /* driver, unit, bus, slot, func, BIOS bus */ #ifdef PCI_DEBUG { "ncr", 2, 1, 4, 0, 0 }, { "ed", 2, 1, 5, 0, 0 }, #endif /* PCI_DEBUG */ /* do not delete the end marker that follows this comment !!! */ { NULL } }; /* return unit number of wired device, or -1 if no match */ static int pci_wiredunit(pcicfgregs *cfg, char *name) { pciwirecfg *p; p = pci_wireddevs; while (p->name != NULL) { if (p->bus == cfg->bus && p->slot == cfg->slot && p->func == cfg->func && strcmp(p->name, name) == 0) return (p->unit); p++; } return (-1); } /* return free unit number equal or greater to the one supplied as parameter */ static int pci_freeunit(pcicfgregs *cfg, char *name, int unit) { pciwirecfg *p; p = pci_wireddevs; while (p->name != NULL) { if (p->unit == unit && strcmp(p->name, name) == 0) { p = pci_wireddevs; unit++; } else { p++; } } return (unit); } static const char *drvname; static const char* pci_probedrv(pcicfgregs *cfg, struct pci_device *dvp) { if (dvp && dvp->pd_probe) { pcidi_t type = (cfg->device << 16) + cfg->vendor; return (dvp->pd_probe(cfg, type)); } return (NULL); } static struct pci_lkm *pci_lkm_head; static struct pci_device* pci_finddrv(pcicfgregs *cfg) { struct pci_device **dvpp; struct pci_device *dvp = NULL; struct pci_lkm *lkm; drvname = NULL; lkm = pci_lkm_head; while (drvname == NULL && lkm != NULL) { dvp = lkm->dvp; drvname = pci_probedrv(cfg, dvp); lkm = lkm->next; } dvpp = (struct pci_device **)pcidevice_set.ls_items; while (drvname == NULL && (dvp = *dvpp++) != NULL) drvname = pci_probedrv(cfg, dvp); return (dvp); } static void pci_drvmessage(pcicfgregs *cfg, char *name, int unit) { if (drvname == NULL || *drvname == '\0') return; printf("%s%d: <%s> rev 0x%02x", name, unit, drvname, cfg->revid); if (cfg->intpin != 0) printf(" int %c irq %d", cfg->intpin + 'a' -1, cfg->intline); printf(" on pci%d.%d.%d\n", cfg->bus, cfg->slot, cfg->func); } void pci_drvattach(struct pci_devinfo *dinfo) { struct pci_device *dvp; pcicfgregs *cfg; cfg = &dinfo->cfg; dvp = pci_finddrv(cfg); if (dvp != NULL) { int unit; unit = pci_wiredunit(cfg, dvp->pd_name); if (unit < 0) { unit = pci_freeunit(cfg, dvp->pd_name, *dvp->pd_count); *dvp->pd_count = unit +1; } pci_drvmessage(cfg, dvp->pd_name, unit); if (dvp->pd_attach) dvp->pd_attach(cfg, unit); dinfo->device = dvp; /* * XXX KDM for some devices, dvp->pd_name winds up NULL. * I haven't investigated enough to figure out why this * would happen. */ if (dvp->pd_name != NULL) strncpy(dinfo->conf.pd_name, dvp->pd_name, sizeof(dinfo->conf.pd_name)); else strncpy(dinfo->conf.pd_name, "????", sizeof(dinfo->conf.pd_name)); dinfo->conf.pd_name[sizeof(dinfo->conf.pd_name) - 1] = 0; dinfo->conf.pd_unit = unit; } } /* ------------------------------------------------------------------------- */ static void pci_rescan(void) { /* XXX do nothing, currently, soon to come ... */ } int pci_register_lkm (struct pci_device *dvp, int if_revision) { struct pci_lkm *lkm; if (if_revision != 0) { return (-1); } if (dvp == NULL || dvp->pd_probe == NULL || dvp->pd_attach == NULL) { return (-1); } lkm = malloc (sizeof (*lkm), M_DEVBUF, M_NOWAIT); if (lkm == NULL) { return (-1); } + bzero(lkm, sizeof (*lkm)); lkm->dvp = dvp; lkm->next = pci_lkm_head; pci_lkm_head = lkm; pci_rescan(); return (0); } void pci_configure(void) { pci_probe(NULL); } /* ------------------------------------------------------------------------- */ + +#endif #endif /* PCI_COMPAT */ #endif /* NPCI > 0 */ Index: head/sys/pci/pci_if.m =================================================================== --- head/sys/pci/pci_if.m (nonexistent) +++ head/sys/pci/pci_if.m (revision 45720) @@ -0,0 +1,44 @@ +# +# Copyright (c) 1998 Doug Rabson +# 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. +# +# $Id$ +# + +INTERFACE pci; + +METHOD u_int32_t read_config { + device_t dev; + device_t child; + int reg; + int width; +}; + +METHOD void write_config { + device_t dev; + device_t child; + int reg; + u_int32_t val; + int width; +}; Property changes on: head/sys/pci/pci_if.m ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: head/sys/pci/pcireg.h =================================================================== --- head/sys/pci/pcireg.h (revision 45719) +++ head/sys/pci/pcireg.h (revision 45720) @@ -1,257 +1,257 @@ #ifndef PCI_COMPAT #define PCI_COMPAT #endif /* * Copyright (c) 1997, Stefan Esser * 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 unmodified, 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 ``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. * - * $Id: pcireg.h,v 1.19 1997/09/20 07:41:58 dyson Exp $ + * $Id: pcireg.h,v 1.20 1998/10/07 03:40:51 gibbs Exp $ * */ /* * PCIM_xxx: mask to locate subfield in register * PCIR_xxx: config register offset * PCIC_xxx: device class * PCIS_xxx: device subclass * PCIP_xxx: device programming interface * PCIV_xxx: PCI vendor ID (only required to fixup ancient devices) * PCID_xxx: device ID */ /* some PCI bus constants */ #define PCI_BUSMAX 255 #define PCI_SLOTMAX 31 #define PCI_FUNCMAX 7 #define PCI_REGMAX 255 /* PCI config header registers for all devices */ #define PCIR_DEVVENDOR 0x00 #define PCIR_VENDOR 0x00 #define PCIR_DEVICE 0x02 #define PCIR_COMMAND 0x04 #define PCIM_CMD_PORTEN 0x0001 #define PCIM_CMD_MEMEN 0x0002 #define PCIM_CMD_BUSMASTEREN 0x0004 #define PCIM_CMD_PERRESPEN 0x0040 #define PCIR_STATUS 0x06 #define PCIR_REVID 0x08 #define PCIR_PROGIF 0x09 #define PCIR_SUBCLASS 0x0a #define PCIR_CLASS 0x0b #define PCIR_CACHELNSZ 0x0c #define PCIR_LATTIMER 0x0d #define PCIR_HEADERTYPE 0x0e #define PCIM_MFDEV 0x80 #define PCIR_BIST 0x0f /* config registers for header type 0 devices */ #define PCIR_MAPS 0x10 #define PCIR_CARDBUSCIS 0x28 #define PCIR_SUBVEND_0 0x2c #define PCIR_SUBDEV_0 0x2e #define PCIR_INTLINE 0x3c #define PCIR_INTPIN 0x3d #define PCIR_MINGNT 0x3e #define PCIR_MAXLAT 0x3f /* config registers for header type 1 devices */ #define PCIR_SECSTAT_1 0 /**/ #define PCIR_PRIBUS_1 0x18 #define PCIR_SECBUS_1 0x19 #define PCIR_SUBBUS_1 0x1a #define PCIR_SECLAT_1 0x1b #define PCIR_IOBASEL_1 0x1c #define PCIR_IOLIMITL_1 0x1d #define PCIR_IOBASEH_1 0 /**/ #define PCIR_IOLIMITH_1 0 /**/ #define PCIR_MEMBASE_1 0x20 #define PCIR_MEMLIMIT_1 0x22 #define PCIR_PMBASEL_1 0x24 #define PCIR_PMLIMITL_1 0x26 #define PCIR_PMBASEH_1 0 /**/ #define PCIR_PMLIMITH_1 0 /**/ #define PCIR_BRIDGECTL_1 0 /**/ #define PCIR_SUBVEND_1 0x34 #define PCIR_SUBDEV_1 0x36 /* config registers for header type 2 devices */ #define PCIR_SECSTAT_2 0x16 #define PCIR_PRIBUS_2 0x18 #define PCIR_SECBUS_2 0x19 #define PCIR_SUBBUS_2 0x1a #define PCIR_SECLAT_2 0x1b #define PCIR_MEMBASE0_2 0x1c #define PCIR_MEMLIMIT0_2 0x20 #define PCIR_MEMBASE1_2 0x24 #define PCIR_MEMLIMIT1_2 0x28 #define PCIR_IOBASE0_2 0x2c #define PCIR_IOLIMIT0_2 0x30 #define PCIR_IOBASE1_2 0x34 #define PCIR_IOLIMIT1_2 0x38 #define PCIR_BRIDGECTL_2 0x3e #define PCIR_SUBVEND_2 0x40 #define PCIR_SUBDEV_2 0x42 #define PCIR_PCCARDIF_2 0x44 /* PCI device class, subclass and programming interface definitions */ #define PCIC_OLD 0x00 #define PCIS_OLD_NONVGA 0x00 #define PCIS_OLD_VGA 0x01 #define PCIC_STORAGE 0x01 #define PCIS_STORAGE_SCSI 0x00 #define PCIS_STORAGE_IDE 0x01 #define PCIP_STORAGE_IDE_MODEPRIM 0x01 #define PCIP_STORAGE_IDE_PROGINDPRIM 0x02 #define PCIP_STORAGE_IDE_MODESEC 0x04 #define PCIP_STORAGE_IDE_PROGINDSEC 0x08 #define PCIP_STORAGE_IDE_MASTERDEV 0x80 #define PCIS_STORAGE_FLOPPY 0x02 #define PCIS_STORAGE_IPI 0x03 #define PCIS_STORAGE_RAID 0x04 #define PCIS_STORAGE_OTHER 0x80 #define PCIC_NETWORK 0x02 #define PCIS_NETWORK_ETHERNET 0x00 #define PCIS_NETWORK_TOKENRING 0x01 #define PCIS_NETWORK_FDDI 0x02 #define PCIS_NETWORK_ATM 0x03 #define PCIS_NETWORK_OTHER 0x80 #define PCIC_DISPLAY 0x03 #define PCIS_DISPLAY_VGA 0x00 #define PCIS_DISPLAY_XGA 0x01 #define PCIS_DISPLAY_OTHER 0x80 #define PCIC_MULTIMEDIA 0x04 #define PCIS_MULTIMEDIA_VIDEO 0x00 #define PCIS_MULTIMEDIA_AUDIO 0x01 #define PCIS_MULTIMEDIA_OTHER 0x80 #define PCIC_MEMORY 0x05 #define PCIS_MEMORY_RAM 0x00 #define PCIS_MEMORY_FLASH 0x01 #define PCIS_MEMORY_OTHER 0x80 #define PCIC_BRIDGE 0x06 -#define PCIS_BRDIGE_HOST 0x00 +#define PCIS_BRIDGE_HOST 0x00 #define PCIS_BRIDGE_ISA 0x01 #define PCIS_BRIDGE_EISA 0x02 #define PCIS_BRIDGE_MCA 0x03 #define PCIS_BRIDGE_PCI 0x04 #define PCIS_BRIDGE_PCMCIA 0x05 #define PCIS_BRIDGE_NUBUS 0x06 #define PCIS_BRIDGE_CARDBUS 0x07 #define PCIS_BRIDGE_OTHER 0x80 #define PCIC_SIMPLECOMM 0x07 #define PCIS_SIMPLECOMM_UART 0x00 #define PCIS_SIMPLECOMM_PAR 0x01 #define PCIS_SIMPLECOMM_OTHER 0x80 #define PCIC_BASEPERIPH 0x08 #define PCIS_BASEPERIPH_PIC 0x00 #define PCIS_BASEPERIPH_DMA 0x01 #define PCIS_BASEPERIPH_TIMER 0x02 #define PCIS_BASEPERIPH_RTC 0x03 #define PCIS_BASEPERIPH_OTHER 0x80 #define PCIC_INPUTDEV 0x09 #define PCIS_INPUTDEV_KEYBOARD 0x00 #define PCIS_INPUTDEV_DIGITIZER 0x01 #define PCIS_INPUTDEV_MOUSE 0x02 #define PCIS_INPUTDEV_OTHER 0x80 #define PCIC_DOCKING 0x0a #define PCIS_DOCKING_GENERIC 0x00 #define PCIS_DOCKING_OTHER 0x80 #define PCIC_PROCESSOR 0x0b #define PCIS_PROCESSOR_386 0x00 #define PCIS_PROCESSOR_486 0x01 #define PCIS_PROCESSOR_PENTIUM 0x02 #define PCIS_PROCESSOR_ALPHA 0x10 #define PCIS_PROCESSOR_POWERPC 0x20 #define PCIS_PROCESSOR_COPROC 0x40 #define PCIC_SERIALBUS 0x0c #define PCIS_SERIALBUS_FW 0x00 #define PCIS_SERIALBUS_ACCESS 0x01 #define PCIS_SERIALBUS_SSA 0x02 #define PCIS_SERIALBUS_USB 0x03 #define PCIS_SERIALBUS_FC 0x04 #define PCIS_SERIALBUS #define PCIS_SERIALBUS #define PCIC_OTHER 0xff /* some PCI vendor definitions (only used to identify ancient devices !!! */ #define PCIV_INTEL 0x8086 #define PCID_INTEL_SATURN 0x0483 #define PCID_INTEL_ORION 0x84c4 /* for compatibility to FreeBSD-2.2 version of PCI code */ #ifdef PCI_COMPAT #define PCI_ID_REG 0x00 #define PCI_COMMAND_STATUS_REG 0x04 #define PCI_COMMAND_IO_ENABLE 0x00000001 #define PCI_COMMAND_MEM_ENABLE 0x00000002 #define PCI_CLASS_REG 0x08 #define PCI_CLASS_MASK 0xff000000 #define PCI_SUBCLASS_MASK 0x00ff0000 #define PCI_REVISION_MASK 0x000000ff #define PCI_CLASS_PREHISTORIC 0x00000000 #define PCI_SUBCLASS_PREHISTORIC_VGA 0x00010000 #define PCI_CLASS_MASS_STORAGE 0x01000000 #define PCI_CLASS_DISPLAY 0x03000000 #define PCI_SUBCLASS_DISPLAY_VGA 0x00000000 #define PCI_CLASS_BRIDGE 0x06000000 #define PCI_MAP_REG_START 0x10 #define PCI_MAP_REG_END 0x28 #define PCI_MAP_IO 0x00000001 #define PCI_INTERRUPT_REG 0x3c #endif /* PCI_COMPAT */ Index: head/sys/pci/pcisupport.c =================================================================== --- head/sys/pci/pcisupport.c (revision 45719) +++ head/sys/pci/pcisupport.c (revision 45720) @@ -1,1348 +1,1550 @@ /************************************************************************** ** -** $Id: pcisupport.c,v 1.94 1999/02/21 11:39:37 dfr Exp $ +** $Id: pcisupport.c,v 1.95 1999/04/07 03:59:13 msmith Exp $ ** ** Device driver for DEC/INTEL PCI chipsets. ** ** FreeBSD ** **------------------------------------------------------------------------- ** ** Written for FreeBSD by ** wolf@cologne.de Wolfgang Stanglmeier ** se@mi.Uni-Koeln.de Stefan Esser ** **------------------------------------------------------------------------- ** ** Copyright (c) 1994,1995 Stefan Esser. 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. 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. ** *************************************************************************** */ +#include "opt_bus.h" #include "opt_pci.h" #include "opt_smp.h" #include "intpm.h" #include "alpm.h" #include #include #include #include +#include #include #include #include #include #include /*--------------------------------------------------------- ** ** Intel chipsets for 486 / Pentium processor ** **--------------------------------------------------------- */ -static const char* chipset_probe (pcici_t tag, pcidi_t type); -static void chipset_attach(pcici_t tag, int unit); -static u_long chipset_count; +static void chipset_attach(device_t dev, int unit); -static struct pci_device chipset_device = { - "chip", - chipset_probe, - chipset_attach, - &chipset_count, - NULL -}; - -DATA_SET (pcidevice_set, chipset_device); - struct condmsg { unsigned char port; unsigned char mask; unsigned char value; char flags; const char *text; }; static char* generic_pci_bridge (pcici_t tag) { char *descr, tmpbuf[120]; unsigned classreg = pci_conf_read (tag, PCI_CLASS_REG); if ((classreg & PCI_CLASS_MASK) == PCI_CLASS_BRIDGE) { unsigned id = pci_conf_read (tag, PCI_ID_REG); switch (classreg >> 16 & 0xff) { case 0: strcpy(tmpbuf, "Host to PCI"); break; case 1: strcpy(tmpbuf, "PCI to ISA"); break; case 2: strcpy(tmpbuf, "PCI to EISA"); break; case 4: strcpy(tmpbuf, "PCI to PCI"); break; case 5: strcpy(tmpbuf, "PCI to PCMCIA"); break; case 7: strcpy(tmpbuf, "PCI to CardBus"); break; default: snprintf(tmpbuf, sizeof(tmpbuf), "PCI to 0x%x", classreg>>16 & 0xff); break; } snprintf(tmpbuf+strlen(tmpbuf), sizeof(tmpbuf)-strlen(tmpbuf), " bridge (vendor=%04x device=%04x)", id & 0xffff, (id >> 16) & 0xffff); descr = malloc (strlen(tmpbuf) +1, M_DEVBUF, M_WAITOK); strcpy(descr, tmpbuf); return descr; } return 0; } /* * XXX Both fixbushigh_orion() and fixbushigh_i1225() are bogus in that way, * that they store the highest bus number to scan in this device's config * data, though it is about PCI buses attached to the CPU independently! * The same goes for fixbushigh_450nx. */ static void -fixbushigh_orion(pcici_t tag) +fixbushigh_orion(device_t dev) { - tag->secondarybus = pci_cfgread(tag, 0x4a, 1); - tag->subordinatebus = pci_cfgread(tag, 0x4b, 1); + pci_set_secondarybus(dev, pci_read_config(dev, 0x4a, 1)); + pci_set_subordinatebus(dev, pci_read_config(dev, 0x4b, 1)); } static void -fixbushigh_i1225(pcici_t tag) +fixbushigh_i1225(device_t dev) { int sublementarybus; - sublementarybus = pci_cfgread(tag, 0x41, 1); - if (sublementarybus != 0xff) - tag->secondarybus = tag->subordinatebus = sublementarybus +1; + sublementarybus = pci_read_config(dev, 0x41, 1); + if (sublementarybus != 0xff) { + pci_set_secondarybus(dev, sublementarybus + 1); + pci_set_subordinatebus(dev, sublementarybus + 1); + } } /* * This reads the PCI config space for the 82451NX MIOC in the 450NX * chipset to determine the PCI bus configuration. * * Assuming the BIOS has set up the MIOC properly, this will correctly * report the number of PCI busses in the system. * * A small problem is that the Host to PCI bridge control is in the MIOC, * while the host-pci bridges are separate PCI devices. So it really * isn't easily possible to set up the subordinatebus mappings as the * 82454NX PCI expander bridges are probed, although that makes the * most sense. */ static void -fixbushigh_450nx(pcici_t tag) +fixbushigh_450nx(device_t dev) { int subordinatebus; unsigned long devmap; /* * Read the DEVMAP field, so we know which fields to check. * If the Host-PCI bridge isn't marked as present by the BIOS, * we have to assume it doesn't exist. * If this doesn't find all the PCI busses, complain to the * BIOS vendor. There is nothing more we can do. */ - devmap = pci_cfgread(tag, 0xd6, 2) & 0x3c; + devmap = pci_read_config(dev, 0xd6, 2) & 0x3c; if (!devmap) panic("450NX MIOC: No host to PCI bridges marked present.\n"); /* * Since the buses are configured in order, we just have to * find the highest bus, and use those numbers. */ if (devmap & 0x20) { /* B1 */ - subordinatebus = pci_cfgread(tag, 0xd5, 1); + subordinatebus = pci_read_config(dev, 0xd5, 1); } else if (devmap & 0x10) { /* A1 */ - subordinatebus = pci_cfgread(tag, 0xd4, 1); + subordinatebus = pci_read_config(dev, 0xd4, 1); } else if (devmap & 0x8) { /* B0 */ - subordinatebus = pci_cfgread(tag, 0xd2, 1); + subordinatebus = pci_read_config(dev, 0xd2, 1); } else /* if (devmap & 0x4) */ { /* A0 */ - subordinatebus = pci_cfgread(tag, 0xd1, 1); + subordinatebus = pci_read_config(dev, 0xd1, 1); } if (subordinatebus == 255) { printf("fixbushigh_450nx: bogus highest PCI bus %d", subordinatebus); #ifdef NBUS subordinatebus = NBUS - 2; #else subordinatebus = 10; #endif printf(", reduced to %d\n", subordinatebus); } if (bootverbose) printf("fixbushigh_450nx: subordinatebus is %d\n", subordinatebus); - tag->secondarybus = tag->subordinatebus = subordinatebus; + pci_set_secondarybus(dev, subordinatebus); + pci_set_subordinatebus(dev, subordinatebus); } static void -fixbushigh_Ross(pcici_t tag) +fixbushigh_Ross(device_t dev) { int secondarybus; /* just guessing the secondary bus register number ... */ - secondarybus = pci_cfgread(tag, 0x45, 1); - if (secondarybus != 0) - tag->secondarybus = tag->subordinatebus = secondarybus + 1; + secondarybus = pci_read_config(dev, 0x45, 1); + if (secondarybus != 0) { + pci_set_secondarybus(dev, secondarybus + 1); + pci_set_subordinatebus(dev, secondarybus + 1); + } } static void -fixwsc_natoma(pcici_t tag) +fixwsc_natoma(device_t dev) { int pmccfg; - pmccfg = pci_cfgread(tag, 0x50, 2); + pmccfg = pci_read_config(dev, 0x50, 2); #if defined(SMP) if (pmccfg & 0x8000) { printf("Correcting Natoma config for SMP\n"); pmccfg &= ~0x8000; - pci_cfgwrite(tag, 0x50, 2, pmccfg); + pci_write_config(dev, 0x50, 2, pmccfg); } #else if ((pmccfg & 0x8000) == 0) { printf("Correcting Natoma config for non-SMP\n"); pmccfg |= 0x8000; - pci_cfgwrite(tag, 0x50, 2, pmccfg); + pci_write_config(dev, 0x50, 2, pmccfg); } #endif } - -static const char* -chipset_probe (pcici_t tag, pcidi_t type) -{ - unsigned rev; - char *descr; - - switch (type) { - case 0x00088086: - /* Silently ignore this one! What is it, anyway ??? */ - return (""); - case 0x04868086: - return ("Intel 82425EX PCI system controller"); - case 0x04848086: - rev = (unsigned) pci_conf_read (tag, PCI_CLASS_REG) & 0xff; - if (rev == 3) - return ("Intel 82378ZB PCI to ISA bridge"); - return ("Intel 82378IB PCI to ISA bridge"); - case 0x04838086: - return ("Intel 82424ZX (Saturn) cache DRAM controller"); - case 0x04828086: - return ("Intel 82375EB PCI-EISA bridge"); - case 0x04a38086: - rev = (unsigned) pci_conf_read (tag, PCI_CLASS_REG) & 0xff; - if (rev == 16 || rev == 17) - return ("Intel 82434NX (Neptune) PCI cache memory controller"); - return ("Intel 82434LX (Mercury) PCI cache memory controller"); - case 0x12258086: - fixbushigh_i1225(tag); - return ("Intel 824?? host to PCI bridge"); - case 0x122d8086: - return ("Intel 82437FX PCI cache memory controller"); - case 0x122e8086: - return ("Intel 82371FB PCI to ISA bridge"); - case 0x12348086: - return ("Intel 82371MX mobile PCI I/O IDE accelerator (MPIIX)"); - case 0x12358086: - return ("Intel 82437MX mobile PCI cache memory controller"); - case 0x12508086: - return ("Intel 82439"); - case 0x70008086: - return ("Intel 82371SB PCI to ISA bridge"); - case 0x70308086: - return ("Intel 82437VX PCI cache memory controller"); - case 0x71008086: - return ("Intel 82439TX System Controller (MTXC)"); - case 0x71108086: - return ("Intel 82371AB PCI to ISA bridge"); - case 0x71138086: -#if NINTPM > 0 - return NULL; /* Need to stop generic_pci_bridge() */ -#else - return ("Intel 82371AB Power management controller"); -#endif - case 0x71908086: - return ("Intel 82443BX host to PCI bridge"); - case 0x71918086: - return ("Intel 82443BX host to AGP bridge"); - case 0x71928086: - return ("Intel 82443BX host to PCI bridge (AGP disabled)"); - case 0x12378086: - fixwsc_natoma(tag); - return ("Intel 82440FX (Natoma) PCI and memory controller"); - case 0x84c48086: - fixbushigh_orion(tag); - return ("Intel 82454KX/GX (Orion) host to PCI bridge"); - case 0x84c58086: - return ("Intel 82453KX/GX (Orion) PCI memory controller"); - case 0x84ca8086: - fixbushigh_450nx(tag); - return ("Intel 82451NX Memory and I/O Controller"); - case 0x84cb8086: - return ("Intel 82454NX PCI Expander Bridge"); - case 0x00221014: - return ("IBM 82351 PCI-PCI bridge"); - case 0x00011011: - return ("DEC 21050 PCI-PCI bridge"); - case 0x124b8086: - return ("Intel 82380FB mobile PCI to PCI bridge"); - /* SiS -- vendor 0x1039 */ - case 0x04961039: - return ("SiS 85c496"); - case 0x04061039: - return ("SiS 85c501"); - case 0x00081039: - return ("SiS 85c503"); - case 0x06011039: - return ("SiS 85c601"); - - /* VLSI -- vendor 0x1004 */ - case 0x00051004: - return ("VLSI 82C592 Host to PCI bridge"); - case 0x00061004: - return ("VLSI 82C593 PCI to ISA bridge"); - case 0x01011004: - return ("VLSI 82C532 Eagle II Peripheral Controller"); - case 0x01021004: - return ("VLSI 82C534 Eagle II PCI Bus bridge"); - case 0x01031004: - return ("VLSI 82C538 Eagle II PCI Docking bridge"); - case 0x01041004: - return ("VLSI 82C535 Eagle II System Controller"); - case 0x01051004: - return ("VLSI 82C147 IrDA Controller"); - - /* VIA Technologies -- vendor 0x1106 - * Note that the old Apollo Master chipset is not in here, as VIA - * does not seem to have any docs on their website for it, and I do - * not have a Master board in my posession. -LC */ - - case 0x05851106: - return("VIA 82C585 (Apollo VP1/VPX) system controller"); - case 0x05861106: /* south bridge section -- IDE is covered in ide_pci.c */ - return("VIA 82C586 PCI-ISA bridge"); - case 0x05951106: - case 0x15951106: - return("VIA 82C595 (Apollo VP2) system controller"); - case 0x05971106: - return("VIA 82C597 (Apollo VP3) system controller"); - /* XXX need info on the MVP3 -- any takers? */ - case 0x30401106: - return("VIA 82C586B ACPI interface"); - /* XXX Here is MVP3, I got the datasheet but NO M/B to test it */ - /* totally. Please let me know if anything wrong. -F */ - case 0x05981106: - return("VIA 82C598MVP (Apollo MVP3) host bridge"); - case 0x85981106: - return("VIA 82C598MVP (Apollo MVP3) PCI-PCI bridge"); - - /* AcerLabs -- vendor 0x10b9 */ - /* Funny : The datasheet told me vendor id is "10b8",sub-vendor */ - /* id is '10b9" but the register always shows "10b9". -Foxfair */ - case 0x154110b9: - return("AcerLabs M1541 (Aladdin-V) PCI host bridge"); - case 0x153310b9: - return("AcerLabs M1533 portable PCI-ISA bridge"); - case 0x154310b9: - return("AcerLabs M1543 desktop PCI-ISA bridge"); - case 0x524710b9: - return("AcerLabs M5247 PCI-PCI(AGP Supported) bridge"); - case 0x524310b9:/* 5243 seems like 5247, need more info to divide*/ - return("AcerLabs M5243 PCI-PCI bridge"); - case 0x710110b9: -#if NALPM > 0 - return NULL; /* Need to stop generic_pci_bridge() */ -#else - return("AcerLabs M15x3 Power Management Unit"); -#endif - - /* NEC -- vendor 0x1033 */ - case 0x00011033: - return ("NEC 0001 PCI to PC-98 C-bus bridge"); - case 0x00021033: - return ("NEC 0002 PCI to PC-98 local bus bridge"); - case 0x00161033: - return ("NEC 0016 PCI to PC-98 local bus bridge"); - case 0x002c1033: - return ("NEC 002C PCI to PC-98 C-bus bridge"); - case 0x003b1033: - return ("NEC 003B PCI to PC-98 C-bus bridge"); - - /* Ross (?) -- vendor 0x1166 */ - case 0x00051166: - fixbushigh_Ross(tag); - return ("Ross (?) host to PCI bridge"); - }; - - if ((descr = generic_pci_bridge(tag)) != NULL) - return descr; - - return NULL; -} - #ifndef PCI_QUIET #define M_XX 0 /* end of list */ #define M_EQ 1 /* mask and return true if equal */ #define M_NE 2 /* mask and return true if not equal */ #define M_TR 3 /* don't read config, always true */ #define M_EN 4 /* mask and print "enabled" if true, "disabled" if false */ #define M_NN 5 /* opposite sense of M_EN */ static const struct condmsg conf82425ex[] = { { 0x00, 0x00, 0x00, M_TR, "\tClock " }, { 0x50, 0x06, 0x00, M_EQ, "25" }, { 0x50, 0x06, 0x02, M_EQ, "33" }, { 0x50, 0x04, 0x04, M_EQ, "??", }, { 0x00, 0x00, 0x00, M_TR, "MHz, L1 Cache " }, { 0x50, 0x01, 0x00, M_EQ, "Disabled\n" }, { 0x50, 0x09, 0x01, M_EQ, "Write-through\n" }, { 0x50, 0x09, 0x09, M_EQ, "Write-back\n" }, { 0x00, 0x00, 0x00, M_TR, "\tL2 Cache " }, { 0x52, 0x07, 0x00, M_EQ, "Disabled" }, { 0x52, 0x0f, 0x01, M_EQ, "64KB Write-through" }, { 0x52, 0x0f, 0x02, M_EQ, "128KB Write-through" }, { 0x52, 0x0f, 0x03, M_EQ, "256KB Write-through" }, { 0x52, 0x0f, 0x04, M_EQ, "512KB Write-through" }, { 0x52, 0x0f, 0x01, M_EQ, "64KB Write-back" }, { 0x52, 0x0f, 0x02, M_EQ, "128KB Write-back" }, { 0x52, 0x0f, 0x03, M_EQ, "256KB Write-back" }, { 0x52, 0x0f, 0x04, M_EQ, "512KB Write-back" }, { 0x53, 0x01, 0x00, M_EQ, ", 3-" }, { 0x53, 0x01, 0x01, M_EQ, ", 2-" }, { 0x53, 0x06, 0x00, M_EQ, "3-3-3" }, { 0x53, 0x06, 0x02, M_EQ, "2-2-2" }, { 0x53, 0x06, 0x04, M_EQ, "1-1-1" }, { 0x53, 0x06, 0x06, M_EQ, "?-?-?" }, { 0x53, 0x18, 0x00, M_EQ, "/4-2-2-2\n" }, { 0x53, 0x18, 0x08, M_EQ, "/3-2-2-2\n" }, { 0x53, 0x18, 0x10, M_EQ, "/?-?-?-?\n" }, { 0x53, 0x18, 0x18, M_EQ, "/2-1-1-1\n" }, { 0x56, 0x00, 0x00, M_TR, "\tDRAM: " }, { 0x56, 0x02, 0x02, M_EQ, "Fast Code Read, " }, { 0x56, 0x04, 0x04, M_EQ, "Fast Data Read, " }, { 0x56, 0x08, 0x08, M_EQ, "Fast Write, " }, { 0x57, 0x20, 0x20, M_EQ, "Pipelined CAS" }, { 0x57, 0x2e, 0x00, M_NE, "\n\t" }, { 0x57, 0x00, 0x00, M_TR, "Timing: RAS: " }, { 0x57, 0x07, 0x00, M_EQ, "4" }, { 0x57, 0x07, 0x01, M_EQ, "3" }, { 0x57, 0x07, 0x02, M_EQ, "2" }, { 0x57, 0x07, 0x04, M_EQ, "1.5" }, { 0x57, 0x07, 0x05, M_EQ, "1" }, { 0x57, 0x00, 0x00, M_TR, " Clocks, CAS Read: " }, { 0x57, 0x18, 0x00, M_EQ, "3/1", }, { 0x57, 0x18, 0x00, M_EQ, "2/1", }, { 0x57, 0x18, 0x00, M_EQ, "1.5/0.5", }, { 0x57, 0x18, 0x00, M_EQ, "1/1", }, { 0x57, 0x00, 0x00, M_TR, ", CAS Write: " }, { 0x57, 0x20, 0x00, M_EQ, "2/1", }, { 0x57, 0x20, 0x20, M_EQ, "1/1", }, { 0x57, 0x00, 0x00, M_TR, "\n" }, { 0x40, 0x01, 0x01, M_EQ, "\tCPU-to-PCI Byte Merging\n" }, { 0x40, 0x02, 0x02, M_EQ, "\tCPU-to-PCI Bursting\n" }, { 0x40, 0x04, 0x04, M_EQ, "\tPCI Posted Writes\n" }, { 0x40, 0x20, 0x00, M_EQ, "\tDRAM Parity Disabled\n" }, { 0x48, 0x03, 0x01, M_EQ, "\tPCI IDE controller: Primary (1F0h-1F7h,3F6h,3F7h)" }, { 0x48, 0x03, 0x02, M_EQ, "\tPCI IDE controller: Secondary (170h-177h,376h,377h)" }, { 0x4d, 0x01, 0x01, M_EQ, "\tRTC (70-77h)\n" }, { 0x4d, 0x02, 0x02, M_EQ, "\tKeyboard (60,62,64,66h)\n" }, { 0x4d, 0x08, 0x08, M_EQ, "\tIRQ12/M Mouse Function\n" }, /* end marker */ { 0 } }; static const struct condmsg conf82424zx[] = { { 0x00, 0x00, 0x00, M_TR, "\tCPU: " }, { 0x50, 0xe0, 0x00, M_EQ, "486DX" }, { 0x50, 0xe0, 0x20, M_EQ, "486SX" }, { 0x50, 0xe0, 0x40, M_EQ, "486DX2 or 486DX4" }, { 0x50, 0xe0, 0x80, M_EQ, "Overdrive (writeback)" }, { 0x00, 0x00, 0x00, M_TR, ", bus=" }, { 0x50, 0x03, 0x00, M_EQ, "25MHz" }, { 0x50, 0x03, 0x01, M_EQ, "33MHz" }, { 0x53, 0x01, 0x01, M_TR, ", CPU->Memory posting "}, { 0x53, 0x01, 0x00, M_EQ, "OFF" }, { 0x53, 0x01, 0x01, M_EQ, "ON" }, { 0x56, 0x30, 0x00, M_NE, "\n\tWarning:" }, { 0x56, 0x20, 0x00, M_NE, " NO cache parity!" }, { 0x56, 0x10, 0x00, M_NE, " NO DRAM parity!" }, { 0x55, 0x04, 0x04, M_EQ, "\n\tWarning: refresh OFF! " }, { 0x00, 0x00, 0x00, M_TR, "\n\tCache: " }, { 0x52, 0x01, 0x00, M_EQ, "None" }, { 0x52, 0xc1, 0x01, M_EQ, "64KB" }, { 0x52, 0xc1, 0x41, M_EQ, "128KB" }, { 0x52, 0xc1, 0x81, M_EQ, "256KB" }, { 0x52, 0xc1, 0xc1, M_EQ, "512KB" }, { 0x52, 0x03, 0x01, M_EQ, " writethrough" }, { 0x52, 0x03, 0x03, M_EQ, " writeback" }, { 0x52, 0x01, 0x01, M_EQ, ", cache clocks=" }, { 0x52, 0x05, 0x01, M_EQ, "3-1-1-1" }, { 0x52, 0x05, 0x05, M_EQ, "2-1-1-1" }, { 0x00, 0x00, 0x00, M_TR, "\n\tDRAM:" }, { 0x55, 0x43, 0x00, M_NE, " page mode" }, { 0x55, 0x02, 0x02, M_EQ, " code fetch" }, { 0x55, 0x43, 0x43, M_EQ, "," }, { 0x55, 0x43, 0x42, M_EQ, " and" }, { 0x55, 0x40, 0x40, M_EQ, " read" }, { 0x55, 0x03, 0x03, M_EQ, " and" }, { 0x55, 0x43, 0x41, M_EQ, " and" }, { 0x55, 0x01, 0x01, M_EQ, " write" }, { 0x55, 0x43, 0x00, M_NE, "," }, { 0x00, 0x00, 0x00, M_TR, " memory clocks=" }, { 0x55, 0x20, 0x00, M_EQ, "X-2-2-2" }, { 0x55, 0x20, 0x20, M_EQ, "X-1-2-1" }, { 0x00, 0x00, 0x00, M_TR, "\n\tCPU->PCI: posting " }, { 0x53, 0x02, 0x00, M_NE, "ON" }, { 0x53, 0x02, 0x00, M_EQ, "OFF" }, { 0x00, 0x00, 0x00, M_TR, ", burst mode " }, { 0x54, 0x02, 0x00, M_NE, "ON" }, { 0x54, 0x02, 0x00, M_EQ, "OFF" }, { 0x00, 0x00, 0x00, M_TR, "\n\tPCI->Memory: posting " }, { 0x54, 0x01, 0x00, M_NE, "ON" }, { 0x54, 0x01, 0x00, M_EQ, "OFF" }, { 0x00, 0x00, 0x00, M_TR, "\n" }, /* end marker */ { 0 } }; static const struct condmsg conf82434lx[] = { { 0x00, 0x00, 0x00, M_TR, "\tCPU: " }, { 0x50, 0xe3, 0x82, M_EQ, "Pentium, 60MHz" }, { 0x50, 0xe3, 0x83, M_EQ, "Pentium, 66MHz" }, { 0x50, 0xe3, 0xa2, M_EQ, "Pentium, 90MHz" }, { 0x50, 0xe3, 0xa3, M_EQ, "Pentium, 100MHz" }, { 0x50, 0xc2, 0x82, M_NE, "(unknown)" }, { 0x50, 0x04, 0x00, M_EQ, " (primary cache OFF)" }, { 0x53, 0x01, 0x01, M_TR, ", CPU->Memory posting "}, { 0x53, 0x01, 0x01, M_NE, "OFF" }, { 0x53, 0x01, 0x01, M_EQ, "ON" }, { 0x53, 0x08, 0x00, M_NE, ", read around write"}, { 0x70, 0x04, 0x00, M_EQ, "\n\tWarning: Cache parity disabled!" }, { 0x57, 0x20, 0x00, M_NE, "\n\tWarning: DRAM parity mask!" }, { 0x57, 0x01, 0x00, M_EQ, "\n\tWarning: refresh OFF! " }, { 0x00, 0x00, 0x00, M_TR, "\n\tCache: " }, { 0x52, 0x01, 0x00, M_EQ, "None" }, { 0x52, 0x81, 0x01, M_EQ, "" }, { 0x52, 0xc1, 0x81, M_EQ, "256KB" }, { 0x52, 0xc1, 0xc1, M_EQ, "512KB" }, { 0x52, 0x03, 0x01, M_EQ, " writethrough" }, { 0x52, 0x03, 0x03, M_EQ, " writeback" }, { 0x52, 0x01, 0x01, M_EQ, ", cache clocks=" }, { 0x52, 0x21, 0x01, M_EQ, "3-2-2-2/4-2-2-2" }, { 0x52, 0x21, 0x21, M_EQ, "3-1-1-1" }, { 0x52, 0x01, 0x01, M_EQ, "\n\tCache flags: " }, { 0x52, 0x11, 0x11, M_EQ, " cache-all" }, { 0x52, 0x09, 0x09, M_EQ, " byte-control" }, { 0x52, 0x05, 0x05, M_EQ, " powersaver" }, { 0x00, 0x00, 0x00, M_TR, "\n\tDRAM:" }, { 0x57, 0x10, 0x00, M_EQ, " page mode" }, { 0x00, 0x00, 0x00, M_TR, " memory clocks=" }, { 0x57, 0xc0, 0x00, M_EQ, "X-4-4-4 (70ns)" }, { 0x57, 0xc0, 0x40, M_EQ, "X-4-4-4/X-3-3-3 (60ns)" }, { 0x57, 0xc0, 0x80, M_EQ, "???" }, { 0x57, 0xc0, 0xc0, M_EQ, "X-3-3-3 (50ns)" }, { 0x58, 0x02, 0x02, M_EQ, ", RAS-wait" }, { 0x58, 0x01, 0x01, M_EQ, ", CAS-wait" }, { 0x00, 0x00, 0x00, M_TR, "\n\tCPU->PCI: posting " }, { 0x53, 0x02, 0x02, M_EQ, "ON" }, { 0x53, 0x02, 0x00, M_EQ, "OFF" }, { 0x00, 0x00, 0x00, M_TR, ", burst mode " }, { 0x54, 0x02, 0x00, M_NE, "ON" }, { 0x54, 0x02, 0x00, M_EQ, "OFF" }, { 0x54, 0x04, 0x00, M_TR, ", PCI clocks=" }, { 0x54, 0x04, 0x00, M_EQ, "2-2-2-2" }, { 0x54, 0x04, 0x00, M_NE, "2-1-1-1" }, { 0x00, 0x00, 0x00, M_TR, "\n\tPCI->Memory: posting " }, { 0x54, 0x01, 0x00, M_NE, "ON" }, { 0x54, 0x01, 0x00, M_EQ, "OFF" }, { 0x57, 0x01, 0x01, M_EQ, "\n\tRefresh:" }, { 0x57, 0x03, 0x03, M_EQ, " CAS#/RAS#(Hidden)" }, { 0x57, 0x03, 0x01, M_EQ, " RAS#Only" }, { 0x57, 0x05, 0x05, M_EQ, " BurstOf4" }, { 0x00, 0x00, 0x00, M_TR, "\n" }, /* end marker */ { 0 } }; static const struct condmsg conf82378[] = { { 0x00, 0x00, 0x00, M_TR, "\tBus Modes:" }, { 0x41, 0x04, 0x04, M_EQ, " Bus Park," }, { 0x41, 0x02, 0x02, M_EQ, " Bus Lock," }, { 0x41, 0x02, 0x00, M_EQ, " Resource Lock," }, { 0x41, 0x01, 0x01, M_EQ, " GAT" }, { 0x4d, 0x20, 0x20, M_EQ, "\n\tCoprocessor errors enabled" }, { 0x4d, 0x10, 0x10, M_EQ, "\n\tMouse function enabled" }, { 0x4e, 0x30, 0x10, M_EQ, "\n\tIDE controller: Primary (1F0h-1F7h,3F6h,3F7h)" }, { 0x4e, 0x30, 0x30, M_EQ, "\n\tIDE controller: Secondary (170h-177h,376h,377h)" }, { 0x4e, 0x28, 0x08, M_EQ, "\n\tFloppy controller: 3F0h,3F1h " }, { 0x4e, 0x24, 0x04, M_EQ, "\n\tFloppy controller: 3F2h-3F7h " }, { 0x4e, 0x28, 0x28, M_EQ, "\n\tFloppy controller: 370h,371h " }, { 0x4e, 0x24, 0x24, M_EQ, "\n\tFloppy controller: 372h-377h " }, { 0x4e, 0x02, 0x02, M_EQ, "\n\tKeyboard controller: 60h,62h,64h,66h" }, { 0x4e, 0x01, 0x01, M_EQ, "\n\tRTC: 70h-77h" }, { 0x4f, 0x80, 0x80, M_EQ, "\n\tConfiguration RAM: 0C00h,0800h-08FFh" }, { 0x4f, 0x40, 0x40, M_EQ, "\n\tPort 92: enabled" }, { 0x4f, 0x03, 0x00, M_EQ, "\n\tSerial Port A: COM1 (3F8h-3FFh)" }, { 0x4f, 0x03, 0x01, M_EQ, "\n\tSerial Port A: COM2 (2F8h-2FFh)" }, { 0x4f, 0x0c, 0x00, M_EQ, "\n\tSerial Port B: COM1 (3F8h-3FFh)" }, { 0x4f, 0x0c, 0x04, M_EQ, "\n\tSerial Port B: COM2 (2F8h-2FFh)" }, { 0x4f, 0x30, 0x00, M_EQ, "\n\tParallel Port: LPT1 (3BCh-3BFh)" }, { 0x4f, 0x30, 0x04, M_EQ, "\n\tParallel Port: LPT2 (378h-37Fh)" }, { 0x4f, 0x30, 0x20, M_EQ, "\n\tParallel Port: LPT3 (278h-27Fh)" }, { 0x00, 0x00, 0x00, M_TR, "\n" }, /* end marker */ { 0 } }; static const struct condmsg conf82437fx[] = { /* PCON -- PCI Control Register */ { 0x00, 0x00, 0x00, M_TR, "\tCPU Inactivity timer: " }, { 0x50, 0xe0, 0xe0, M_EQ, "8" }, { 0x50, 0xe0, 0xd0, M_EQ, "7" }, { 0x50, 0xe0, 0xc0, M_EQ, "6" }, { 0x50, 0xe0, 0xb0, M_EQ, "5" }, { 0x50, 0xe0, 0xa0, M_EQ, "4" }, { 0x50, 0xe0, 0x90, M_EQ, "3" }, { 0x50, 0xe0, 0x80, M_EQ, "2" }, { 0x50, 0xe0, 0x00, M_EQ, "1" }, { 0x00, 0x00, 0x00, M_TR, " clocks\n\tPeer Concurrency: " }, { 0x50, 0x08, 0x08, M_EN, 0 }, { 0x00, 0x00, 0x00, M_TR, "\n\tCPU-to-PCI Write Bursting: " }, { 0x50, 0x04, 0x00, M_NN, 0 }, { 0x00, 0x00, 0x00, M_TR, "\n\tPCI Streaming: " }, { 0x50, 0x02, 0x00, M_NN, 0 }, { 0x00, 0x00, 0x00, M_TR, "\n\tBus Concurrency: " }, { 0x50, 0x01, 0x00, M_NN, 0 }, /* CC -- Cache Control Regsiter */ { 0x00, 0x00, 0x00, M_TR, "\n\tCache:" }, { 0x52, 0xc0, 0x80, M_EQ, " 512K" }, { 0x52, 0xc0, 0x40, M_EQ, " 256K" }, { 0x52, 0xc0, 0x00, M_EQ, " NO" }, { 0x52, 0x30, 0x00, M_EQ, " pipelined-burst" }, { 0x52, 0x30, 0x10, M_EQ, " burst" }, { 0x52, 0x30, 0x20, M_EQ, " asynchronous" }, { 0x52, 0x30, 0x30, M_EQ, " dual-bank pipelined-burst" }, { 0x00, 0x00, 0x00, M_TR, " secondary; L1 " }, { 0x52, 0x01, 0x00, M_EN, 0 }, { 0x00, 0x00, 0x00, M_TR, "\n" }, /* DRAMC -- DRAM Control Register */ { 0x57, 0x07, 0x00, M_EQ, "Warning: refresh OFF!\n" }, { 0x00, 0x00, 0x00, M_TR, "\tDRAM:" }, { 0x57, 0xc0, 0x00, M_EQ, " no memory hole" }, { 0x57, 0xc0, 0x40, M_EQ, " 512K-640K memory hole" }, { 0x57, 0xc0, 0x80, M_EQ, " 15M-16M memory hole" }, { 0x57, 0x07, 0x01, M_EQ, ", 50 MHz refresh" }, { 0x57, 0x07, 0x02, M_EQ, ", 60 MHz refresh" }, { 0x57, 0x07, 0x03, M_EQ, ", 66 MHz refresh" }, /* DRAMT = DRAM Timing Register */ { 0x00, 0x00, 0x00, M_TR, "\n\tRead burst timing: " }, { 0x58, 0x60, 0x00, M_EQ, "x-4-4-4/x-4-4-4" }, { 0x58, 0x60, 0x20, M_EQ, "x-3-3-3/x-4-4-4" }, { 0x58, 0x60, 0x40, M_EQ, "x-2-2-2/x-3-3-3" }, { 0x58, 0x60, 0x60, M_EQ, "???" }, { 0x00, 0x00, 0x00, M_TR, "\n\tWrite burst timing: " }, { 0x58, 0x18, 0x00, M_EQ, "x-4-4-4" }, { 0x58, 0x18, 0x08, M_EQ, "x-3-3-3" }, { 0x58, 0x18, 0x10, M_EQ, "x-2-2-2" }, { 0x58, 0x18, 0x18, M_EQ, "???" }, { 0x00, 0x00, 0x00, M_TR, "\n\tRAS-CAS delay: " }, { 0x58, 0x04, 0x00, M_EQ, "3" }, { 0x58, 0x04, 0x04, M_EQ, "2" }, { 0x00, 0x00, 0x00, M_TR, " clocks\n" }, /* end marker */ { 0 } }; static const struct condmsg conf82437vx[] = { /* PCON -- PCI Control Register */ { 0x00, 0x00, 0x00, M_TR, "\n\tPCI Concurrency: " }, { 0x50, 0x08, 0x08, M_EN, 0 }, /* CC -- Cache Control Regsiter */ { 0x00, 0x00, 0x00, M_TR, "\n\tCache:" }, { 0x52, 0xc0, 0x80, M_EQ, " 512K" }, { 0x52, 0xc0, 0x40, M_EQ, " 256K" }, { 0x52, 0xc0, 0x00, M_EQ, " NO" }, { 0x52, 0x30, 0x00, M_EQ, " pipelined-burst" }, { 0x52, 0x30, 0x10, M_EQ, " burst" }, { 0x52, 0x30, 0x20, M_EQ, " asynchronous" }, { 0x52, 0x30, 0x30, M_EQ, " dual-bank pipelined-burst" }, { 0x00, 0x00, 0x00, M_TR, " secondary; L1 " }, { 0x52, 0x01, 0x00, M_EN, 0 }, { 0x00, 0x00, 0x00, M_TR, "\n" }, /* DRAMC -- DRAM Control Register */ { 0x57, 0x07, 0x00, M_EQ, "Warning: refresh OFF!\n" }, { 0x00, 0x00, 0x00, M_TR, "\tDRAM:" }, { 0x57, 0xc0, 0x00, M_EQ, " no memory hole" }, { 0x57, 0xc0, 0x40, M_EQ, " 512K-640K memory hole" }, { 0x57, 0xc0, 0x80, M_EQ, " 15M-16M memory hole" }, { 0x57, 0x07, 0x01, M_EQ, ", 50 MHz refresh" }, { 0x57, 0x07, 0x02, M_EQ, ", 60 MHz refresh" }, { 0x57, 0x07, 0x03, M_EQ, ", 66 MHz refresh" }, /* DRAMT = DRAM Timing Register */ { 0x00, 0x00, 0x00, M_TR, "\n\tRead burst timing: " }, { 0x58, 0x60, 0x00, M_EQ, "x-4-4-4/x-4-4-4" }, { 0x58, 0x60, 0x20, M_EQ, "x-3-3-3/x-4-4-4" }, { 0x58, 0x60, 0x40, M_EQ, "x-2-2-2/x-3-3-3" }, { 0x58, 0x60, 0x60, M_EQ, "???" }, { 0x00, 0x00, 0x00, M_TR, "\n\tWrite burst timing: " }, { 0x58, 0x18, 0x00, M_EQ, "x-4-4-4" }, { 0x58, 0x18, 0x08, M_EQ, "x-3-3-3" }, { 0x58, 0x18, 0x10, M_EQ, "x-2-2-2" }, { 0x58, 0x18, 0x18, M_EQ, "???" }, { 0x00, 0x00, 0x00, M_TR, "\n\tRAS-CAS delay: " }, { 0x58, 0x04, 0x00, M_EQ, "3" }, { 0x58, 0x04, 0x04, M_EQ, "2" }, { 0x00, 0x00, 0x00, M_TR, " clocks\n" }, /* end marker */ { 0 } }; static const struct condmsg conf82371fb[] = { /* IORT -- ISA I/O Recovery Timer Register */ { 0x00, 0x00, 0x00, M_TR, "\tI/O Recovery Timing: 8-bit " }, { 0x4c, 0x40, 0x00, M_EQ, "3.5" }, { 0x4c, 0x78, 0x48, M_EQ, "1" }, { 0x4c, 0x78, 0x50, M_EQ, "2" }, { 0x4c, 0x78, 0x58, M_EQ, "3" }, { 0x4c, 0x78, 0x60, M_EQ, "4" }, { 0x4c, 0x78, 0x68, M_EQ, "5" }, { 0x4c, 0x78, 0x70, M_EQ, "6" }, { 0x4c, 0x78, 0x78, M_EQ, "7" }, { 0x4c, 0x78, 0x40, M_EQ, "8" }, { 0x00, 0x00, 0x00, M_TR, " clocks, 16-bit " }, { 0x4c, 0x04, 0x00, M_EQ, "3.5" }, { 0x4c, 0x07, 0x05, M_EQ, "1" }, { 0x4c, 0x07, 0x06, M_EQ, "2" }, { 0x4c, 0x07, 0x07, M_EQ, "3" }, { 0x4c, 0x07, 0x04, M_EQ, "4" }, { 0x00, 0x00, 0x00, M_TR, " clocks\n" }, /* XBCS -- X-Bus Chip Select Register */ { 0x00, 0x00, 0x00, M_TR, "\tExtended BIOS: " }, { 0x4e, 0x80, 0x80, M_EN, 0 }, { 0x00, 0x00, 0x00, M_TR, "\n\tLower BIOS: " }, { 0x4e, 0x40, 0x40, M_EN, 0 }, { 0x00, 0x00, 0x00, M_TR, "\n\tCoprocessor IRQ13: " }, { 0x4e, 0x20, 0x20, M_EN, 0 }, { 0x00, 0x00, 0x00, M_TR, "\n\tMouse IRQ12: " }, { 0x4e, 0x10, 0x10, M_EN, 0 }, { 0x00, 0x00, 0x00, M_TR, "\n" }, { 0x00, 0x00, 0x00, M_TR, "\tInterrupt Routing: " }, #define PIRQ(x, n) \ { 0x00, 0x00, 0x00, M_TR, n ": " }, \ { x, 0x80, 0x80, M_EQ, "disabled" }, \ { x, 0xc0, 0x40, M_EQ, "[shared] " }, \ { x, 0x8f, 0x03, M_EQ, "IRQ3" }, \ { x, 0x8f, 0x04, M_EQ, "IRQ4" }, \ { x, 0x8f, 0x05, M_EQ, "IRQ5" }, \ { x, 0x8f, 0x06, M_EQ, "IRQ6" }, \ { x, 0x8f, 0x07, M_EQ, "IRQ7" }, \ { x, 0x8f, 0x09, M_EQ, "IRQ9" }, \ { x, 0x8f, 0x0a, M_EQ, "IRQ10" }, \ { x, 0x8f, 0x0b, M_EQ, "IRQ11" }, \ { x, 0x8f, 0x0c, M_EQ, "IRQ12" }, \ { x, 0x8f, 0x0e, M_EQ, "IRQ14" }, \ { x, 0x8f, 0x0f, M_EQ, "IRQ15" } /* Interrupt routing */ PIRQ(0x60, "A"), PIRQ(0x61, ", B"), PIRQ(0x62, ", C"), PIRQ(0x63, ", D"), PIRQ(0x70, "\n\t\tMB0"), PIRQ(0x71, ", MB1"), { 0x00, 0x00, 0x00, M_TR, "\n" }, #undef PIRQ /* XXX - do DMA routing, too? */ { 0 } }; static const struct condmsg conf82371fb2[] = { /* IDETM -- IDE Timing Register */ { 0x00, 0x00, 0x00, M_TR, "\tPrimary IDE: " }, { 0x41, 0x80, 0x80, M_EN, 0 }, { 0x00, 0x00, 0x00, M_TR, "\n\tSecondary IDE: " }, { 0x43, 0x80, 0x80, M_EN, 0 }, { 0x00, 0x00, 0x00, M_TR, "\n" }, /* end of list */ { 0 } }; -static char confread (pcici_t config_id, int port) -{ - unsigned long portw = port & ~3; - unsigned long ports = (port - portw) << 3; - - unsigned long l = pci_conf_read (config_id, portw); - return (l >> ports); -} - static void -writeconfig (pcici_t config_id, const struct condmsg *tbl) +writeconfig (device_t dev, const struct condmsg *tbl) { while (tbl->flags != M_XX) { const char *text = 0; if (tbl->flags == M_TR) { text = tbl->text; } else { - unsigned char v = (unsigned char) confread(config_id, tbl->port); + unsigned char v = pci_read_config(dev, tbl->port, 1); switch (tbl->flags) { case M_EQ: if ((v & tbl->mask) == tbl->value) text = tbl->text; break; case M_NE: if ((v & tbl->mask) != tbl->value) text = tbl->text; break; case M_EN: text = (v & tbl->mask) ? "enabled" : "disabled"; break; case M_NN: text = (v & tbl->mask) ? "disabled" : "enabled"; } } if (text) printf ("%s", text); tbl++; } } #ifdef DUMPCONFIGSPACE static void -dumpconfigspace (pcici_t tag) +dumpconfigspace (device_t dev) { int reg; printf ("configuration space registers:"); for (reg = 0; reg < 0x100; reg+=4) { if ((reg & 0x0f) == 0) printf ("\n%02x:\t", reg); - printf ("%08x ", pci_conf_read (tag, reg)); + printf ("%08x ", pci_read_config(dev, reg, 4)); } printf ("\n"); } #endif /* DUMPCONFIGSPACE */ #endif /* PCI_QUIET */ static void -chipset_attach (pcici_t config_id, int unit) +chipset_attach (device_t dev, int unit) { #ifndef PCI_QUIET if (!bootverbose) return; - switch (pci_conf_read (config_id, PCI_ID_REG)) { + switch (pci_get_devid(dev)) { case 0x04868086: - writeconfig (config_id, conf82425ex); + writeconfig (dev, conf82425ex); break; case 0x04838086: - writeconfig (config_id, conf82424zx); + writeconfig (dev, conf82424zx); break; case 0x04a38086: - writeconfig (config_id, conf82434lx); + writeconfig (dev, conf82434lx); break; case 0x04848086: - writeconfig (config_id, conf82378); + writeconfig (dev, conf82378); break; case 0x122d8086: - writeconfig (config_id, conf82437fx); + writeconfig (dev, conf82437fx); break; case 0x70308086: - writeconfig (config_id, conf82437vx); + writeconfig (dev, conf82437vx); break; case 0x70008086: case 0x122e8086: - writeconfig (config_id, conf82371fb); + writeconfig (dev, conf82371fb); break; case 0x70108086: case 0x12308086: - writeconfig (config_id, conf82371fb2); + writeconfig (dev, conf82371fb2); break; #if 0 case 0x00011011: /* DEC 21050 */ case 0x00221014: /* IBM xxx */ - writeconfig (config_id, conf_pci2pci); + writeconfig (dev, conf_pci2pci); break; #endif }; #endif /* PCI_QUIET */ } +static const char * +pci_bridge_type(device_t dev) +{ + char *descr, tmpbuf[120]; + + if (pci_get_class(dev) != PCIC_BRIDGE) + return NULL; + + switch (pci_get_subclass(dev)) { + case PCIS_BRIDGE_HOST: strcpy(tmpbuf, "Host to PCI"); break; + case PCIS_BRIDGE_ISA: strcpy(tmpbuf, "PCI to ISA"); break; + case PCIS_BRIDGE_EISA: strcpy(tmpbuf, "PCI to EISA"); break; + case PCIS_BRIDGE_MCA: strcpy(tmpbuf, "PCI to MCA"); break; + case PCIS_BRIDGE_PCI: strcpy(tmpbuf, "PCI to PCI"); break; + case PCIS_BRIDGE_PCMCIA: strcpy(tmpbuf, "PCI to PCMCIA"); break; + case PCIS_BRIDGE_NUBUS: strcpy(tmpbuf, "PCI to NUBUS"); break; + case PCIS_BRIDGE_CARDBUS: strcpy(tmpbuf, "PCI to CardBus"); break; + default: + snprintf(tmpbuf, sizeof(tmpbuf), + "PCI to 0x%x", pci_get_subclass(dev)); + break; + } + snprintf(tmpbuf+strlen(tmpbuf), sizeof(tmpbuf)-strlen(tmpbuf), + " bridge (vendor=%04x device=%04x)", + pci_get_vendor(dev), pci_get_device(dev)); + descr = malloc (strlen(tmpbuf) +1, M_DEVBUF, M_WAITOK); + strcpy(descr, tmpbuf); + return descr; +} + +static const char* +pcib_match(device_t dev) +{ + switch (pci_get_devid(dev)) { + case 0x84cb8086: + return ("Intel 82454NX PCI Expander Bridge"); + case 0x00221014: + return ("IBM 82351 PCI-PCI bridge"); + case 0x00011011: + return ("DEC 21050 PCI-PCI bridge"); + case 0x124b8086: + return ("Intel 82380FB mobile PCI to PCI bridge"); + + /* VLSI -- vendor 0x1004 */ + case 0x01021004: + return ("VLSI 82C534 Eagle II PCI Bus bridge"); + case 0x01031004: + return ("VLSI 82C538 Eagle II PCI Docking bridge"); + + /* XXX Here is MVP3, I got the datasheet but NO M/B to test it */ + /* totally. Please let me know if anything wrong. -F */ + case 0x85981106: + return("VIA 82C598MVP (Apollo MVP3) PCI-PCI bridge"); + + /* AcerLabs -- vendor 0x10b9 */ + /* Funny : The datasheet told me vendor id is "10b8",sub-vendor */ + /* id is '10b9" but the register always shows "10b9". -Foxfair */ + case 0x524710b9: + return("AcerLabs M5247 PCI-PCI(AGP Supported) bridge"); + case 0x524310b9:/* 5243 seems like 5247, need more info to divide*/ + return("AcerLabs M5243 PCI-PCI bridge"); + + }; + + if (pci_get_class(dev) == PCIC_BRIDGE + && pci_get_subclass(dev) == PCIS_BRIDGE_PCI) + return pci_bridge_type(dev); + + return NULL; +} + +static int pcib_probe(device_t dev) +{ + const char *desc; + + desc = pcib_match(dev); + if (desc) { + device_set_desc_copy(dev, desc); + return 0; + } + + return ENXIO; +} + +static int pcib_attach(device_t dev) +{ + struct pci_devinfo *dinfo; + pcicfgregs *cfg; + + dinfo = device_get_ivars(dev); + cfg = &dinfo->cfg; + + chipset_attach(dev, device_get_unit(dev)); + + if (cfg->secondarybus) { + device_add_child(dev, "pci", cfg->secondarybus, 0); + return bus_generic_attach(dev); + } else + return 0; +} + +static device_method_t pcib_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, pcib_probe), + DEVMETHOD(device_attach, pcib_attach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + + /* Bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), + DEVMETHOD(bus_release_resource, bus_generic_release_resource), + DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), + DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), + DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), + DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), + + { 0, 0 } +}; + +static driver_t pcib_driver = { + "pcib", + pcib_methods, + DRIVER_TYPE_MISC, + 1, +}; + +static devclass_t pcib_devclass; + +DRIVER_MODULE(pcib, pci, pcib_driver, pcib_devclass, 0, 0); + +static const char * +isab_match(device_t dev) +{ + unsigned rev; + + switch (pci_get_devid(dev)) { + case 0x04848086: + rev = pci_get_revid(dev); + if (rev == 3) + return ("Intel 82378ZB PCI to ISA bridge"); + return ("Intel 82378IB PCI to ISA bridge"); + case 0x04828086: + return ("Intel 82375EB PCI-EISA bridge"); + case 0x122e8086: + return ("Intel 82371FB PCI to ISA bridge"); + case 0x70008086: + return ("Intel 82371SB PCI to ISA bridge"); + case 0x71108086: + return ("Intel 82371AB PCI to ISA bridge"); + + /* VLSI -- vendor 0x1004 */ + case 0x00061004: + return ("VLSI 82C593 PCI to ISA bridge"); + + /* VIA Technologies -- vendor 0x1106 + * Note that the old Apollo Master chipset is not in here, as VIA + * does not seem to have any docs on their website for it, and I do + * not have a Master board in my posession. -LC */ + + case 0x05861106: /* south bridge section -- IDE is covered in ide_pci.c */ + return("VIA 82C586 PCI-ISA bridge"); + + /* AcerLabs -- vendor 0x10b9 */ + /* Funny : The datasheet told me vendor id is "10b8",sub-vendor */ + /* id is '10b9" but the register always shows "10b9". -Foxfair */ + case 0x153310b9: + return("AcerLabs M1533 portable PCI-ISA bridge"); + case 0x154310b9: + return("AcerLabs M1543 desktop PCI-ISA bridge"); + } + + if (pci_get_class(dev) == PCIC_BRIDGE + && (pci_get_subclass(dev) == PCIS_BRIDGE_ISA + || pci_get_subclass(dev) == PCIS_BRIDGE_EISA)) + return pci_bridge_type(dev); + + return NULL; +} + +static int +isab_probe(device_t dev) +{ + const char *desc; + + desc = isab_match(dev); + if (desc) { + device_set_desc_copy(dev, desc); + /* Don't bother adding more than one ISA bus */ + if (!devclass_get_device(devclass_find("isa"), 0)) + device_add_child(dev, "isa", -1, 0); + return 0; + } + + return ENXIO; +} + +static device_method_t isab_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, isab_probe), + DEVMETHOD(device_attach, bus_generic_attach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + + /* Bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), + DEVMETHOD(bus_release_resource, bus_generic_release_resource), + DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), + DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), + DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), + DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), + + { 0, 0 } +}; + +static driver_t isab_driver = { + "isab", + isab_methods, + DRIVER_TYPE_MISC, + 1, +}; + +static devclass_t isab_devclass; + +DRIVER_MODULE(isab, pci, isab_driver, isab_devclass, 0, 0); + +static const char* +chip_match(device_t dev) +{ + unsigned rev; + + switch (pci_get_devid(dev)) { + case 0x00088086: + /* Silently ignore this one! What is it, anyway ??? */ + return (""); + case 0x71108086: + /* + * On my laptop (Tecra 8000DVD), this device has a + * bogus subclass 0x80 so make sure that it doesn't + * match the generic 'chip' driver by accident. + */ + return NULL; + case 0x12258086: + fixbushigh_i1225(dev); + return ("Intel 824?? host to PCI bridge"); + case 0x71908086: + return ("Intel 82443BX host to PCI bridge"); + case 0x71918086: + return ("Intel 82443BX host to AGP bridge"); + case 0x71928086: + return ("Intel 82443BX host to PCI bridge (AGP disabled)"); + case 0x84c48086: + fixbushigh_orion(dev); + return ("Intel 82454KX/GX (Orion) host to PCI bridge"); + case 0x84ca8086: + fixbushigh_450nx(dev); + return ("Intel 82451NX Memory and I/O Controller"); + case 0x04868086: + return ("Intel 82425EX PCI system controller"); + case 0x04838086: + return ("Intel 82424ZX (Saturn) cache DRAM controller"); + case 0x04a38086: + rev = pci_get_revid(dev); + if (rev == 16 || rev == 17) + return ("Intel 82434NX (Neptune) PCI cache memory controller"); + return ("Intel 82434LX (Mercury) PCI cache memory controller"); + case 0x122d8086: + return ("Intel 82437FX PCI cache memory controller"); + case 0x12348086: + return ("Intel 82371MX mobile PCI I/O IDE accelerator (MPIIX)"); + case 0x12358086: + return ("Intel 82437MX mobile PCI cache memory controller"); + case 0x12508086: + return ("Intel 82439"); + case 0x70308086: + return ("Intel 82437VX PCI cache memory controller"); + case 0x71008086: + return ("Intel 82439TX System Controller (MTXC)"); + + case 0x71138086: + return ("Intel 82371AB Power management controller"); + case 0x12378086: + fixwsc_natoma(dev); + return ("Intel 82440FX (Natoma) PCI and memory controller"); + case 0x84c58086: + return ("Intel 82453KX/GX (Orion) PCI memory controller"); + /* SiS -- vendor 0x1039 */ + case 0x04961039: + return ("SiS 85c496"); + case 0x04061039: + return ("SiS 85c501"); + case 0x00081039: + return ("SiS 85c503"); + case 0x06011039: + return ("SiS 85c601"); + + /* VLSI -- vendor 0x1004 */ + case 0x00051004: + return ("VLSI 82C592 Host to PCI bridge"); + case 0x01011004: + return ("VLSI 82C532 Eagle II Peripheral Controller"); + case 0x01041004: + return ("VLSI 82C535 Eagle II System Controller"); + case 0x01051004: + return ("VLSI 82C147 IrDA Controller"); + + /* VIA Technologies -- vendor 0x1106 + * Note that the old Apollo Master chipset is not in here, as VIA + * does not seem to have any docs on their website for it, and I do + * not have a Master board in my posession. -LC */ + + case 0x05851106: + return("VIA 82C585 (Apollo VP1/VPX) system controller"); + case 0x05951106: + case 0x15951106: + return("VIA 82C595 (Apollo VP2) system controller"); + case 0x05971106: + return("VIA 82C597 (Apollo VP3) system controller"); + /* XXX Here is MVP3, I got the datasheet but NO M/B to test it */ + /* totally. Please let me know if anything wrong. -F */ + /* XXX need info on the MVP3 -- any takers? */ + case 0x05981106: + return("VIA 82C598MVP (Apollo MVP3) host bridge"); + case 0x30401106: + return("VIA 82C586B ACPI interface"); +#if 0 + /* XXX New info added-in */ + case 0x05711106: + return("VIA 82C586B IDE controller"); + case 0x30381106: + return("VIA 82C586B USB controller"); +#endif + + /* NEC -- vendor 0x1033 */ + case 0x00011033: + return ("NEC 0001 PCI to PC-98 C-bus bridge"); + case 0x00021033: + return ("NEC 0002 PCI to PC-98 local bus bridge"); + case 0x00161033: + return ("NEC 0016 PCI to PC-98 local bus bridge"); + case 0x002c1033: + return ("NEC 002C PCI to PC-98 C-bus bridge"); + case 0x003b1033: + return ("NEC 003B PCI to PC-98 C-bus bridge"); + + /* AcerLabs -- vendor 0x10b9 */ + /* Funny : The datasheet told me vendor id is "10b8",sub-vendor */ + /* id is '10b9" but the register always shows "10b9". -Foxfair */ + case 0x154110b9: + return("AcerLabs M1541 (Aladdin-V) PCI host bridge"); + }; + + if (pci_get_class(dev) == PCIC_BRIDGE + && pci_get_subclass(dev) != PCIS_BRIDGE_PCI + && pci_get_subclass(dev) != PCIS_BRIDGE_ISA + && pci_get_subclass(dev) != PCIS_BRIDGE_EISA) + return pci_bridge_type(dev); + + return NULL; +} + +static int chip_probe(device_t dev) +{ + const char *desc; + + desc = chip_match(dev); + if (desc) { + device_set_desc_copy(dev, desc); + return 0; + } + + return ENXIO; +} + +static int chip_attach(device_t dev) +{ + chipset_attach(dev, device_get_unit(dev)); + + return 0; +} + +static device_method_t chip_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, chip_probe), + DEVMETHOD(device_attach, chip_attach), + + { 0, 0 } +}; + +static driver_t chip_driver = { + "chip", + chip_methods, + DRIVER_TYPE_MISC, + 1, +}; + +static devclass_t chip_devclass; + +DRIVER_MODULE(chip, pci, chip_driver, chip_devclass, 0, 0); + /*--------------------------------------------------------- ** ** Catchall driver for VGA devices ** ** By Garrett Wollman ** ** **--------------------------------------------------------- */ -static const char* vga_probe (pcici_t tag, pcidi_t type); -static void vga_attach (pcici_t tag, int unit); -static u_long vga_count; - -static struct pci_device vga_device = { - "vga", - vga_probe, - vga_attach, - &vga_count, - NULL -}; - -DATA_SET (pcidevice_set, vga_device); - -static const char* vga_probe (pcici_t tag, pcidi_t typea) +static const char* vga_match(device_t dev) { - int data = pci_conf_read(tag, PCI_CLASS_REG); - u_int id = pci_conf_read(tag, PCI_ID_REG); + /* int data = pci_conf_read(tag, PCI_CLASS_REG); */ + u_int id = pci_get_devid(dev); const char *vendor, *chip, *type; - vendor = chip = type = 0; + return 0; /* XXX interferes with syscons */ - switch (id & 0xffff) { + vendor = chip = type = 0; + switch (id) { case 0x10c8: vendor = "NeoMagic"; switch (id >> 16) { case 0x0004: chip = "NM2160 laptop"; break; } break; case 0x102b: vendor = "Matrox"; type = "graphics accelerator"; switch (id >> 16) { case 0x0518: chip = "MGA 2085PX"; break; case 0x0519: chip = "MGA 2064W"; break; case 0x051a: chip = "MGA 1024SG/1064SG/1164SG"; break; case 0x051b: chip = "MGA 2164W"; break; } break; case 0x1002: vendor = "ATI"; type = "graphics accelerator"; switch (id >> 16) { case 0x4158: chip = "Mach32"; break; case 0x4758: chip = "Mach64-GX"; break; case 0x4358: chip = "Mach64-CX"; break; case 0x4354: chip = "Mach64-CT"; break; case 0x4554: chip = "Mach64-ET"; break; case 0x5654: chip = "Mach64-VT"; break; case 0x4754: chip = "Mach64-GT"; break; } break; case 0x1005: vendor = "Avance Logic"; switch (id >> 16) { case 0x2301: chip = "ALG2301"; break; } break; case 0x100c: vendor = "Tseng Labs"; type = "graphics accelerator"; switch (id >> 16) { case 0x3202: case 0x3205: case 0x3206: case 0x3207: chip = "ET4000 W32P"; break; case 0x3208: chip = "ET6000"; break; case 0x4702: chip = "ET6300"; break; } break; case 0x100e: vendor = "Weitek"; type = "graphics accelerator"; switch (id >> 16) { case 0x9001: chip = "P9000"; break; case 0x9100: chip = "P9100"; break; } break; case 0x1013: vendor = "Cirrus Logic"; switch (id >> 16) { case 0x0038: chip = "GD7548"; break; case 0x00a0: chip = "GD5430"; break; case 0x00a4: case 0x00a8: chip = "GD5434"; break; case 0x00ac: chip = "GD5436"; break; case 0x00b8: chip = "GD5446"; break; case 0x00d0: chip = "GD5462"; break; case 0x00d4: chip = "GD5464"; break; case 0x1200: chip = "GD7542"; break; case 0x1202: chip = "GD7543"; break; case 0x1204: chip = "GD7541"; break; } break; case 0x1023: vendor = "Trident"; break; /* let default deal with it */ case 0x102c: vendor = "Chips & Technologies"; if ((id >> 16) == 0x00d8) chip = "65545"; break; case 0x1033: vendor = "NEC"; switch (id >> 16) { case 0x0009: type = "PCI to PC-98 Core Graph bridge"; break; } break; case 0x1039: vendor = "SiS"; switch (id >> 16) { case 0x0001: chip = "86c201"; break; case 0x0002: chip = "86c202"; break; case 0x0205: chip = "86c205"; break; } break; case 0x105d: vendor = "Number Nine"; type = "graphics accelerator"; switch (id >> 16) { case 0x2309: case 0x2339: chip = "Imagine 128"; break; } break; case 0x1142: vendor = "Alliance"; switch (id >> 16) { case 0x3210: chip = "PM6410"; break; case 0x6422: chip = "PM6422"; break; case 0x6424: chip = "PMAT24"; break; } break; case 0x1236: vendor = "Sigma Designs"; if ((id >> 16) == 0x6401) chip = "64GX"; break; case 0x5333: vendor = "S3"; type = "graphics accelerator"; switch (id >> 16) { case 0x8811: chip = "Trio"; break; case 0x8812: chip = "Aurora 64"; break; case 0x8814: case 0x8901: chip = "Trio 64"; break; case 0x8902: chip = "Plato"; break; case 0x8880: chip = "868"; break; case 0x88b0: chip = "928"; break; case 0x88c0: case 0x88c1: chip = "864"; break; case 0x88d0: case 0x88d1: chip = "964"; break; case 0x88f0: chip = "968"; break; case 0x5631: chip = "ViRGE"; break; case 0x883d: chip = "ViRGE VX"; break; case 0x8a01: chip = "ViRGE DX/GX"; break; } break; case 0xedd8: vendor = "ARK Logic"; switch (id >> 16) { case 0xa091: chip = "1000PV"; break; case 0xa099: chip = "2000PV"; break; case 0xa0a1: chip = "2000MT"; break; case 0xa0a9: chip = "2000MI"; break; } break; case 0x3d3d: vendor = "3D Labs"; type = "graphics accelerator"; switch (id >> 16) { case 0x0001: chip = "300SX"; break; case 0x0002: chip = "500TX"; break; case 0x0003: chip = "Delta"; break; case 0x0004: chip = "PerMedia"; break; } break; } if (vendor && chip) { char *buf; int len; if (type == 0) { type = "SVGA controller"; } len = strlen(vendor) + strlen(chip) + strlen(type) + 4; MALLOC(buf, char *, len, M_TEMP, M_NOWAIT); if (buf) sprintf(buf, "%s %s %s", vendor, chip, type); return buf; } - switch (data & PCI_CLASS_MASK) { + switch (pci_get_class(dev)) { - case PCI_CLASS_PREHISTORIC: - if ((data & PCI_SUBCLASS_MASK) - != PCI_SUBCLASS_PREHISTORIC_VGA) + case PCIC_OLD: + if (pci_get_subclass(dev) != PCIS_OLD_VGA) return 0; if (type == 0) type = "VGA-compatible display device"; break; - case PCI_CLASS_DISPLAY: + case PCIC_DISPLAY: if (type == 0) { - if ((data & PCI_SUBCLASS_MASK) - == PCI_SUBCLASS_DISPLAY_VGA) + if (pci_get_subclass(dev) != PCIS_DISPLAY_VGA) type = "VGA-compatible display device"; else { /* * If it isn't a vga display device, * don't pretend we found one. */ type = "Display device"; return 0; } } break; default: return 0; }; /* * If we got here, we know for sure it's some sort of display * device, but we weren't able to identify it specifically. * At a minimum we can return the type, but we'd like to * identify the vendor and chip ID if at all possible. * (Some of the checks above intentionally don't bother for * vendors where we know the chip ID is the same as the * model number.) */ if (vendor) { char *buf; int len; len = strlen(vendor) + strlen(type) + 2 + 6 + 4 + 1; MALLOC(buf, char *, len, M_TEMP, M_NOWAIT); if (buf) sprintf(buf, "%s model %04x %s", vendor, id >> 16, type); return buf; } return type; } -static void vga_attach (pcici_t tag, int unit) +static int vga_probe(device_t dev) { + const char *desc; + + desc = vga_match(dev); + if (desc) { + device_set_desc(dev, desc); + return 0; + } + + return ENXIO; +} + +static int vga_attach(device_t dev) +{ /* ** If the assigned addresses are remapped, ** the console driver has to be informed about the new address. */ #if 0 vm_offset_t va; vm_offset_t pa; int reg; for (reg = PCI_MAP_REG_START; reg < PCI_MAP_REG_END; reg += 4) (void) pci_map_mem (tag, reg, &va, &pa); #endif + return 0; } -/*--------------------------------------------------------- -** -** Hook for loadable pci drivers -** -**--------------------------------------------------------- -*/ +static device_method_t vga_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, vga_probe), + DEVMETHOD(device_attach, vga_attach), -static const char* lkm_probe (pcici_t tag, pcidi_t type); -static void lkm_attach (pcici_t tag, int unit); -static u_long lkm_count; + { 0, 0 } +}; -static struct pci_device lkm_device = { - "lkm", - lkm_probe, - lkm_attach, - &lkm_count, - NULL +static driver_t vga_driver = { + "vga", + vga_methods, + DRIVER_TYPE_MISC, + 1, }; -DATA_SET (pcidevice_set, lkm_device); +static devclass_t vga_devclass; -static const char* -lkm_probe (pcici_t tag, pcidi_t type) -{ - /* - ** Not yet! - ** (Should try to load a matching driver) - */ - return ((char*)0); -} +DRIVER_MODULE(vga, pci, vga_driver, vga_devclass, 0, 0); -static void -lkm_attach (pcici_t tag, int unit) -{} - /*--------------------------------------------------------- ** ** Devices to ignore ** **--------------------------------------------------------- */ -static const char* ign_probe (pcici_t tag, pcidi_t type); -static void ign_attach (pcici_t tag, int unit); -static u_long ign_count; - -static struct pci_device ign_device = { - NULL, - ign_probe, - ign_attach, - &ign_count, - NULL -}; - -DATA_SET (pcidevice_set, ign_device); - -static const char* -ign_probe (pcici_t tag, pcidi_t type) +static int +ign_probe (device_t dev) { - switch (type) { + switch (pci_get_devid(dev)) { case 0x10001042ul: /* wd */ - return (""); + return 0; /* return ("SMC FDC 37c665");*/ }; - return ((char*)0); + return ENXIO; } -static void -ign_attach (pcici_t tag, int unit) -{} +static int +ign_attach (device_t dev) +{ + return 0; +} + +static device_method_t ign_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ign_probe), + DEVMETHOD(device_attach, ign_attach), + + { 0, 0 } +}; + +static driver_t ign_driver = { + "ign", + ign_methods, + DRIVER_TYPE_MISC, + 1, +}; + +static devclass_t ign_devclass; + +DRIVER_MODULE(ign, pci, ign_driver, ign_devclass, 0, 0); Index: head/sys/pci/pcivar.h =================================================================== --- head/sys/pci/pcivar.h (revision 45719) +++ head/sys/pci/pcivar.h (revision 45720) @@ -1,234 +1,311 @@ /* * Copyright (c) 1997, Stefan Esser * 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 unmodified, 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 ``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. * - * $Id: pcivar.h,v 1.24 1999/01/13 04:59:19 bde Exp $ + * $Id: pcivar.h,v 1.25 1999/01/19 23:29:20 se Exp $ * */ #ifndef _PCIVAR_H_ #define _PCIVAR_H_ #ifndef PCI_COMPAT #define PCI_COMPAT #endif #include /* XXX KDM */ #include /* some PCI bus constants */ #define PCI_BUSMAX 255 /* highest supported bus number */ #define PCI_SLOTMAX 31 /* highest supported slot number */ #define PCI_FUNCMAX 7 /* highest supported function number */ #define PCI_REGMAX 255 /* highest supported config register addr. */ #define PCI_MAXMAPS_0 6 /* max. no. of memory/port maps */ #define PCI_MAXMAPS_1 2 /* max. no. of maps for PCI to PCI bridge */ #define PCI_MAXMAPS_2 1 /* max. no. of maps for CardBus bridge */ /* pci_addr_t covers this system's PCI bus address space: 32 or 64 bit */ #ifdef PCI_A64 typedef u_int64_t pci_addr_t; /* u_int64_t for system with 64bit addresses */ #else typedef u_int32_t pci_addr_t; /* u_int64_t for system with 64bit addresses */ #endif /* map register information */ typedef struct { u_int32_t base; u_int8_t type; #define PCI_MAPMEM 0x01 /* memory map */ #define PCI_MAPMEMP 0x02 /* prefetchable memory map */ #define PCI_MAPPORT 0x04 /* port map */ u_int8_t ln2size; u_int8_t ln2range; u_int8_t reg; /* offset of map register in config space */ +/* u_int8_t dummy;*/ + struct resource *res; /* handle from resource manager */ } pcimap; /* config header information common to all header types */ typedef struct pcicfg { + struct device *dev; /* device which owns this */ pcimap *map; /* pointer to array of PCI maps */ void *hdrspec; /* pointer to header type specific data */ + struct resource *irqres; /* resource descriptor for interrupt mapping */ u_int16_t subvendor; /* card vendor ID */ u_int16_t subdevice; /* card device ID, assigned by card vendor */ u_int16_t vendor; /* chip vendor ID */ u_int16_t device; /* chip device ID, assigned by chip vendor */ u_int16_t cmdreg; /* disable/enable chip and PCI options */ u_int16_t statreg; /* supported PCI features and error state */ u_int8_t baseclass; /* chip PCI class */ u_int8_t subclass; /* chip PCI subclass */ u_int8_t progif; /* chip PCI programming interface */ u_int8_t revid; /* chip revision ID */ u_int8_t hdrtype; /* chip config header type */ u_int8_t cachelnsz; /* cache line size in 4byte units */ u_int8_t intpin; /* PCI interrupt pin */ u_int8_t intline; /* interrupt line (IRQ for PC arch) */ u_int8_t mingnt; /* min. useful bus grant time in 250ns units */ u_int8_t maxlat; /* max. tolerated bus grant latency in 250ns */ u_int8_t lattimer; /* latency timer in units of 30ns bus cycles */ u_int8_t mfdev; /* multi-function device (from hdrtype reg) */ u_int8_t nummaps; /* actual number of PCI maps used */ u_int8_t bus; /* config space bus address */ u_int8_t slot; /* config space slot address */ u_int8_t func; /* config space function number */ u_int8_t secondarybus; /* bus on secondary side of bridge, if any */ u_int8_t subordinatebus; /* topmost bus number behind bridge, if any */ } pcicfgregs; /* additional type 1 device config header information (PCI to PCI bridge) */ #ifdef PCI_A64 #define PCI_PPBMEMBASE(h,l) ((((pci_addr_t)(h) << 32) + ((l)<<16)) & ~0xfffff) #define PCI_PPBMEMLIMIT(h,l) ((((pci_addr_t)(h) << 32) + ((l)<<16)) | 0xfffff) #else #define PCI_PPBMEMBASE(h,l) (((l)<<16) & ~0xfffff) #define PCI_PPBMEMLIMIT(h,l) (((l)<<16) | 0xfffff) #endif /* PCI_A64 */ #define PCI_PPBIOBASE(h,l) ((((h)<<16) + ((l)<<8)) & ~0xfff) #define PCI_PPBIOLIMIT(h,l) ((((h)<<16) + ((l)<<8)) | 0xfff) typedef struct { pci_addr_t pmembase; /* base address of prefetchable memory */ pci_addr_t pmemlimit; /* topmost address of prefetchable memory */ u_int32_t membase; /* base address of memory window */ u_int32_t memlimit; /* topmost address of memory window */ u_int32_t iobase; /* base address of port window */ u_int32_t iolimit; /* topmost address of port window */ u_int16_t secstat; /* secondary bus status register */ u_int16_t bridgectl; /* bridge control register */ u_int8_t seclat; /* CardBus latency timer */ } pcih1cfgregs; /* additional type 2 device config header information (CardBus bridge) */ typedef struct { u_int32_t membase0; /* base address of memory window */ u_int32_t memlimit0; /* topmost address of memory window */ u_int32_t membase1; /* base address of memory window */ u_int32_t memlimit1; /* topmost address of memory window */ u_int32_t iobase0; /* base address of port window */ u_int32_t iolimit0; /* topmost address of port window */ u_int32_t iobase1; /* base address of port window */ u_int32_t iolimit1; /* topmost address of port window */ u_int32_t pccardif; /* PC Card 16bit IF legacy more base addr. */ u_int16_t secstat; /* secondary bus status register */ u_int16_t bridgectl; /* bridge control register */ u_int8_t seclat; /* CardBus latency timer */ } pcih2cfgregs; /* PCI bus attach definitions (there could be multiple PCI bus *trees* ... */ typedef struct pciattach { int unit; int pcibushigh; struct pciattach *next; } pciattach; struct pci_devinfo { STAILQ_ENTRY(pci_devinfo) pci_links; struct pci_device *device; /* should this be ifdefed? */ pcicfgregs cfg; struct pci_conf conf; }; extern u_int32_t pci_numdevs; /* externally visible functions */ int pci_probe (pciattach *attach); void pci_drvattach(struct pci_devinfo *dinfo); /* low level PCI config register functions provided by pcibus.c */ int pci_cfgopen (void); int pci_cfgread (pcicfgregs *cfg, int reg, int bytes); void pci_cfgwrite (pcicfgregs *cfg, int reg, int data, int bytes); #ifdef __alpha__ vm_offset_t pci_cvt_to_dense (vm_offset_t); vm_offset_t pci_cvt_to_bwx (vm_offset_t); #endif /* __alpha__ */ + +#ifdef _SYS_BUS_H_ + +#include "pci_if.h" + +enum pci_device_ivars { + PCI_IVAR_SUBVENDOR, + PCI_IVAR_SUBDEVICE, + PCI_IVAR_VENDOR, + PCI_IVAR_DEVICE, + PCI_IVAR_DEVID, + PCI_IVAR_CLASS, + PCI_IVAR_SUBCLASS, + PCI_IVAR_PROGIF, + PCI_IVAR_REVID, + PCI_IVAR_INTPIN, + PCI_IVAR_IRQ, + PCI_IVAR_BUS, + PCI_IVAR_SLOT, + PCI_IVAR_FUNCTION, + PCI_IVAR_SECONDARYBUS, + PCI_IVAR_SUBORDINATEBUS, +}; + +/* + * Simplified accessors for pci devices + */ +#define PCI_ACCESSOR(A, B, T) \ + \ +static __inline T pci_get_ ## A(device_t dev) \ +{ \ + uintptr_t v; \ + BUS_READ_IVAR(device_get_parent(dev), dev, PCI_IVAR_ ## B, &v); \ + return (T) v; \ +} \ + \ +static __inline void pci_set_ ## A(device_t dev, T t) \ +{ \ + u_long v = (u_long) t; \ + BUS_WRITE_IVAR(device_get_parent(dev), dev, PCI_IVAR_ ## B, v); \ +} + +PCI_ACCESSOR(subvendor, SUBVENDOR, u_int16_t) +PCI_ACCESSOR(subdevice, SUBDEVICE, u_int16_t) +PCI_ACCESSOR(vendor, VENDOR, u_int16_t) +PCI_ACCESSOR(device, DEVICE, u_int16_t) +PCI_ACCESSOR(devid, DEVID, u_int32_t) +PCI_ACCESSOR(class, CLASS, u_int8_t) +PCI_ACCESSOR(subclass, SUBCLASS, u_int8_t) +PCI_ACCESSOR(progif, PROGIF, u_int8_t) +PCI_ACCESSOR(revid, REVID, u_int8_t) +PCI_ACCESSOR(intpin, INTPIN, u_int8_t) +PCI_ACCESSOR(irq, IRQ, u_int8_t) +PCI_ACCESSOR(bus, BUS, u_int8_t) +PCI_ACCESSOR(slot, SLOT, u_int8_t) +PCI_ACCESSOR(function, FUNCTION, u_int8_t) +PCI_ACCESSOR(secondarybus, SECONDARYBUS, u_int8_t) +PCI_ACCESSOR(subordinatebus, SUBORDINATEBUS, u_int8_t) + +static __inline u_int32_t +pci_read_config(device_t dev, int reg, int width) +{ + return PCI_READ_CONFIG(device_get_parent(dev), dev, reg, width); +} + +static __inline void +pci_write_config(device_t dev, int reg, u_int32_t val, int width) +{ + PCI_WRITE_CONFIG(device_get_parent(dev), dev, reg, val, width); +} + +#endif + /* for compatibility to FreeBSD-2.2 version of PCI code */ #ifdef PCI_COMPAT typedef pcicfgregs *pcici_t; typedef unsigned pcidi_t; typedef void pci_inthand_t(void *arg); #define pci_max_burst_len (3) /* just copied from old PCI code for now ... */ extern struct linker_set pcidevice_set; extern int pci_mechanism; struct pci_device { char* pd_name; const char* (*pd_probe ) (pcici_t tag, pcidi_t type); void (*pd_attach) (pcici_t tag, int unit); u_long *pd_count; int (*pd_shutdown) (int, int); }; struct pci_lkm { struct pci_device *dvp; struct pci_lkm *next; }; #ifdef __i386__ typedef u_short pci_port_t; #else typedef u_int pci_port_t; #endif u_long pci_conf_read (pcici_t tag, u_long reg); void pci_conf_write (pcici_t tag, u_long reg, u_long data); void pci_configure (void); int pci_map_port (pcici_t tag, u_long reg, pci_port_t* pa); int pci_map_mem (pcici_t tag, u_long reg, vm_offset_t* va, vm_offset_t* pa); int pci_map_dense (pcici_t tag, u_long reg, vm_offset_t* va, vm_offset_t* pa); int pci_map_bwx (pcici_t tag, u_long reg, vm_offset_t* va, vm_offset_t* pa); int pci_map_int (pcici_t tag, pci_inthand_t *handler, void *arg, intrmask_t *maskptr); int pci_map_int_right(pcici_t cfg, pci_inthand_t *handler, void *arg, intrmask_t *maskptr, u_int flags); int pci_unmap_int (pcici_t tag); int pci_register_lkm (struct pci_device *dvp, int if_revision); #endif /* PCI_COMPAT */ #endif /* _PCIVAR_H_ */ Index: head/sys/pci/uhci_pci.c =================================================================== --- head/sys/pci/uhci_pci.c (revision 45719) +++ head/sys/pci/uhci_pci.c (revision 45720) @@ -1,215 +1,239 @@ -/* FreeBSD $Id: uhci_pci.c,v 1.4 1999/04/06 23:09:58 n_hibma Exp $ */ +/* FreeBSD $Id: uhci_pci.c,v 1.5 1999/04/11 14:24:20 n_hibma Exp $ */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (augustss@carlstedt.se) at * Carlstedt Research & Technology. * * 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 NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */ +#include "opt_bus.h" + #include #include #include #include #include #include #include #include +#include +#include +#include #include #include -#define PCI_CLASS_SERIALBUS 0x0c000000 -#define PCI_SUBCLASS_COMMUNICATIONS_SERIAL 0x00000000 -#define PCI_SUBCLASS_SERIALBUS_FIREWIRE 0x00000000 -#define PCI_SUBCLASS_SERIALBUS_ACCESS 0x00010000 -#define PCI_SUBCLASS_SERIALBUS_SSA 0x00020000 -#define PCI_SUBCLASS_SERIALBUS_USB 0x00030000 -#define PCI_SUBCLASS_SERIALBUS_FIBER 0x00040000 - -#define PCI_INTERFACE(d) (((d)>>8)&0xff) -#define PCI_SUBCLASS(d) ((d)&PCI_SUBCLASS_MASK) -#define PCI_CLASS(d) ((d)&PCI_CLASS_MASK) - - #include #include #include #include #include #include - #define PCI_UHCI_VENDORID_INTEL 0x8086 #define PCI_UHCI_VENDORID_VIA 0x1106 #define PCI_UHCI_DEVICEID_PIIX3 0x70208086ul static const char *uhci_device_piix3 = "Intel 82371SB (PIIX3) USB Host Controller"; #define PCI_UHCI_DEVICEID_PIIX4 0x71128086ul #define PCI_UHCI_DEVICEID_PIIX4E 0x71128086ul /* no separate step */ static const char *uhci_device_piix4 = "Intel 82371AB/EB (PIIX4) USB Host Controller"; #define PCI_UHCI_DEVICEID_VT83C572 0x30381106ul static const char *uhci_device_vt83c572 = "VIA 83C572 USB Host Controller"; static const char *uhci_device_generic = "UHCI (generic) USB Controller"; #define PCI_UHCI_BASE_REG 0x20 -static const char *uhci_pci_probe __P((pcici_t, pcidi_t)); -static void uhci_pci_attach __P((pcici_t, int)); - -static u_long uhci_count = 0; - -static struct pci_device uhci_pci_device = { - "uhci", - uhci_pci_probe, - uhci_pci_attach, - &uhci_count, - NULL -}; - -DATA_SET(pcidevice_set, uhci_pci_device); - - static const char * -uhci_pci_probe(pcici_t config_id, pcidi_t device_id) +uhci_pci_match(device_t dev) { - u_int32_t class; + u_int32_t device_id = pci_get_devid(dev); if (device_id == PCI_UHCI_DEVICEID_PIIX3) { return (uhci_device_piix3); } else if (device_id == PCI_UHCI_DEVICEID_PIIX4) { return (uhci_device_piix4); } else if (device_id == PCI_UHCI_DEVICEID_VT83C572) { return (uhci_device_vt83c572); } else { - class = pci_conf_read(config_id, PCI_CLASS_REG); - if ( PCI_CLASS(class) == PCI_CLASS_SERIALBUS - && PCI_SUBCLASS(class) == PCI_SUBCLASS_SERIALBUS_USB - && PCI_INTERFACE(class) == PCI_INTERFACE_UHCI) { + if ( pci_get_class(dev) == PCIC_SERIALBUS + && pci_get_subclass(dev) == PCIS_SERIALBUS_USB + && pci_get_progif(dev) == PCI_INTERFACE_UHCI) { return (uhci_device_generic); } } return NULL; /* dunno... */ } -static void -uhci_pci_attach(pcici_t config_id, int unit) +static int +uhci_pci_probe(device_t dev) { - int id, legsup; + const char *desc = uhci_pci_match(dev); + if (desc) { + device_set_desc(dev, desc); + return 0; + } else { + return ENXIO; + } +} + +static int +uhci_pci_attach(device_t dev) +{ + int unit = device_get_unit(dev); + int legsup; char *typestr; usbd_status err; - uhci_softc_t *sc = NULL; device_t usbus; + uhci_softc_t *sc = device_get_softc(dev); + int rid; + struct resource *res; + void *ih; + int error; - sc = malloc(sizeof(uhci_softc_t), M_DEVBUF, M_NOWAIT); - /* Do not free it below, intr might use the sc */ - if ( sc == NULL ) { - printf("uhci%d: could not allocate memory", unit); - return; - } - memset(sc, 0, sizeof(uhci_softc_t)); + rid = PCI_UHCI_BASE_REG; + res = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, + 0, ~0, 1, RF_ACTIVE); + if (!res) { + device_printf(dev, "could not map ports\n"); + return ENXIO; + } - if ( !pci_map_port(config_id, PCI_UHCI_BASE_REG, &sc->sc_iobase) ) { - printf("uhci%d: could not map port\n", unit); - return; - } + sc->iot = rman_get_bustag(res); + sc->ioh = rman_get_bushandle(res); - if ( !pci_map_int(config_id, (pci_inthand_t *)uhci_intr, - (void *) sc, &bio_imask)) { - printf("uhci%d: could not map irq\n", unit); - return; + rid = 0; + res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, + RF_SHAREABLE | RF_ACTIVE); + if (res == NULL) { + device_printf(dev, "could not allocate irq\n"); + return ENOMEM; } + + error = bus_setup_intr(dev, res, (driver_intr_t *) uhci_intr, sc, &ih); + if (error) { + device_printf(dev, "could not setup irq\n"); + return error; + } - usbus = device_add_child(root_bus, "usb", -1, sc); + usbus = device_add_child(dev, "usb", -1, sc); if (!usbus) { - printf("usb%d: could not add USB device to root bus\n", unit); - return; + printf("usb%d: could not add USB device\n", unit); + return ENOMEM; } - id = pci_conf_read(config_id, PCI_ID_REG); - switch (id) { + switch (pci_get_devid(dev)) { case PCI_UHCI_DEVICEID_PIIX3: device_set_desc(usbus, uhci_device_piix3); sprintf(sc->sc_vendor, "Intel"); break; case PCI_UHCI_DEVICEID_PIIX4: device_set_desc(usbus, uhci_device_piix4); sprintf(sc->sc_vendor, "Intel"); break; case PCI_UHCI_DEVICEID_VT83C572: device_set_desc(usbus, uhci_device_vt83c572); sprintf(sc->sc_vendor, "VIA"); break; default: - printf("(New UHCI DeviceId=0x%08x)\n", id); + printf("(New UHCI DeviceId=0x%08x)\n", pci_get_devid(dev)); device_set_desc(usbus, uhci_device_generic); - sprintf(sc->sc_vendor, "(0x%08x)", id); + sprintf(sc->sc_vendor, "(0x%08x)", pci_get_devid(dev)); } if (bootverbose) { - switch(pci_conf_read(config_id, PCI_USBREV) & PCI_USBREV_MASK) { + switch(pci_read_config(dev, PCI_USBREV, 4) & PCI_USBREV_MASK) { case PCI_USBREV_PRE_1_0: typestr = "pre 1.0"; break; case PCI_USBREV_1_0: typestr = "1.0"; break; default: typestr = "unknown"; break; } printf("uhci%d: USB version %s, chip rev. %d\n", unit, typestr, - (int) pci_conf_read(config_id, PCIR_REVID) & 0xff); + pci_get_revid(dev)); } - legsup = pci_conf_read(config_id, PCI_LEGSUP); + legsup = pci_read_config(dev, PCI_LEGSUP, 4); if ( !(legsup & PCI_LEGSUP_USBPIRQDEN) ) { #if ! (defined(USBVERBOSE) || defined(USB_DEBUG)) if (bootverbose) #endif printf("uhci%d: PIRQD enable not set\n", unit); legsup |= PCI_LEGSUP_USBPIRQDEN; - pci_conf_write(config_id, PCI_LEGSUP, legsup); + pci_write_config(dev, PCI_LEGSUP, legsup, 4); } sc->sc_bus.bdev = usbus; err = uhci_init(sc); if (err != USBD_NORMAL_COMPLETION) { printf("uhci%d: init failed, error=%d\n", unit, err); - device_delete_child(root_bus, usbus); + device_delete_child(dev, usbus); } - return; + return device_probe_and_attach(sc->sc_bus.bdev); } + +static device_method_t uhci_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, uhci_pci_probe), + DEVMETHOD(device_attach, uhci_pci_attach), + + /* Bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), + DEVMETHOD(bus_release_resource, bus_generic_release_resource), + DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), + DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), + DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), + DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), + + { 0, 0 } +}; + +static driver_t uhci_driver = { + "uhci", + uhci_methods, + DRIVER_TYPE_BIO, + sizeof(uhci_softc_t), +}; + +static devclass_t uhci_devclass; + +DRIVER_MODULE(uhci, pci, uhci_driver, uhci_devclass, 0, 0); Index: head/sys/sys/bus.h =================================================================== --- head/sys/sys/bus.h (revision 45719) +++ head/sys/sys/bus.h (revision 45720) @@ -1,297 +1,305 @@ /*- * Copyright (c) 1997,1998 Doug Rabson * 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. * - * $Id: bus.h,v 1.9 1999/03/06 16:52:04 bde Exp $ + * $Id: bus.h,v 1.10 1999/03/29 08:54:19 dfr Exp $ */ #ifndef _SYS_BUS_H_ #define _SYS_BUS_H_ #ifdef KERNEL #include /* * Forward declarations */ typedef struct device *device_t; typedef struct driver driver_t; typedef struct device_method device_method_t; typedef struct devclass *devclass_t; typedef struct device_ops *device_ops_t; typedef struct device_op_desc *device_op_desc_t; typedef void driver_intr_t(void*); /* * We define this in terms of bits because some devices may belong * to multiple classes (and therefore need to be included in * multiple interrupt masks, which is what this really serves to * indicate. Buses which do interrupt remapping will want to * change their type to reflect what sort of devices are underneath. */ typedef enum driver_type { DRIVER_TYPE_TTY = 1, DRIVER_TYPE_BIO = 2, DRIVER_TYPE_NET = 4, DRIVER_TYPE_CAM = 8, DRIVER_TYPE_MISC = 16, DRIVER_TYPE_FAST = 128 } driver_type_t; typedef int (*devop_t)(void); struct device_method { device_op_desc_t desc; devop_t func; }; struct driver { const char *name; /* driver name */ device_method_t *methods; /* method table */ driver_type_t type; size_t softc; /* size of device softc struct */ void *priv; /* driver private data */ device_ops_t ops; /* compiled method table */ + int refs; /* # devclasses containing driver */ }; typedef enum device_state { DS_NOTPRESENT, /* not probed or probe failed */ DS_ALIVE, /* probe succeeded */ DS_ATTACHED, /* attach method called */ DS_BUSY /* device is open */ } device_state_t; /* * The root bus, to which all top-level busses are attached. */ extern device_t root_bus; extern devclass_t root_devclass; void root_bus_configure(void); /* * Useful functions for implementing busses. */ struct resource; int bus_generic_activate_resource(device_t dev, device_t child, int type, int rid, struct resource *r); struct resource *bus_generic_alloc_resource(device_t bus, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags); int bus_generic_attach(device_t dev); int bus_generic_deactivate_resource(device_t dev, device_t child, int type, int rid, struct resource *r); int bus_generic_detach(device_t dev); +void bus_generic_driver_added(device_t dev, driver_t *driver); void bus_generic_print_child(device_t dev, device_t child); int bus_generic_read_ivar(device_t dev, device_t child, int which, uintptr_t *result); int bus_generic_release_resource(device_t bus, device_t child, int type, int rid, struct resource *r); int bus_generic_resume(device_t dev); int bus_generic_setup_intr(device_t dev, device_t child, struct resource *irq, driver_intr_t *intr, void *arg, void **cookiep); int bus_generic_shutdown(device_t dev); int bus_generic_suspend(device_t dev); int bus_generic_teardown_intr(device_t dev, device_t child, struct resource *irq, void *cookie); int bus_generic_write_ivar(device_t dev, device_t child, int which, uintptr_t value); /* * Wrapper functions for the BUS_*_RESOURCE methods to make client code * a little simpler. */ struct resource *bus_alloc_resource(device_t dev, int type, int *rid, u_long start, u_long end, u_long count, u_int flags); int bus_activate_resource(device_t dev, int type, int rid, struct resource *r); int bus_deactivate_resource(device_t dev, int type, int rid, struct resource *r); int bus_release_resource(device_t dev, int type, int rid, struct resource *r); int bus_setup_intr(device_t dev, struct resource *r, driver_intr_t handler, void *arg, void **cookiep); int bus_teardown_intr(device_t dev, struct resource *r, void *cookie); /* * Access functions for device. */ device_t device_add_child(device_t dev, const char *name, int unit, void *ivp); device_t device_add_child_after(device_t dev, device_t place, const char *name, int unit, void *ivp); void device_busy(device_t dev); int device_delete_child(device_t dev, device_t child); int device_detach(device_t dev); void device_disable(device_t dev); void device_enable(device_t dev); device_t device_find_child(device_t dev, const char *classname, int unit); const char *device_get_desc(device_t dev); devclass_t device_get_devclass(device_t dev); driver_t *device_get_driver(device_t dev); device_t device_get_parent(device_t dev); int device_get_children(device_t dev, device_t **listp, int *countp); void *device_get_ivars(device_t dev); const char *device_get_name(device_t dev); const char *device_get_nameunit(device_t dev); void *device_get_softc(device_t dev); device_state_t device_get_state(device_t dev); int device_get_unit(device_t dev); int device_is_alive(device_t dev); /* did probe succeed? */ int device_is_enabled(device_t dev); int device_is_quiet(device_t dev); void device_print_prettyname(device_t dev); void device_printf(device_t dev, const char *, ...) __printflike(2, 3); int device_probe_and_attach(device_t dev); void device_quiet(device_t dev); void device_set_desc(device_t dev, const char* desc); void device_set_desc_copy(device_t dev, const char* desc); int device_set_devclass(device_t dev, const char *classname); int device_set_driver(device_t dev, driver_t *driver); int device_shutdown(device_t dev); void device_unbusy(device_t dev); void device_verbose(device_t dev); /* * Access functions for devclass. */ int devclass_add_driver(devclass_t dc, driver_t *driver); int devclass_delete_driver(devclass_t dc, driver_t *driver); devclass_t devclass_find(const char *classname); driver_t *devclass_find_driver(devclass_t dc, const char *classname); const char *devclass_get_name(devclass_t dc); device_t devclass_get_device(devclass_t dc, int unit); void *devclass_get_softc(devclass_t dc, int unit); int devclass_get_devices(devclass_t dc, device_t **listp, int *countp); int devclass_get_maxunit(devclass_t dc); /* * Access functions for device resources. */ + int resource_int_value(const char *name, int unit, char *resname, int *result); int resource_long_value(const char *name, int unit, char *resname, long *result); int resource_string_value(const char *name, int unit, char *resname, char **result); int resource_query_string(int i, char *resname, char *value); char *resource_query_name(int i); int resource_query_unit(int i); +int resource_locate(int i, char *resname); +int resource_set_int(int i, char *resname, int value); +int resource_set_long(int i, char *resname, long value); +int resource_set_string(int i, char *resname, char *value); +int resource_count(void); /* * Shorthand for constructing method tables. */ #define DEVMETHOD(NAME, FUNC) { &NAME##_desc, (devop_t) FUNC } /* * Some common device interfaces. */ #include "device_if.h" #include "bus_if.h" struct module; int driver_module_handler(struct module *, int, void *); /* * Module support for automatically adding drivers to busses. */ struct driver_module_data { int (*dmd_chainevh)(struct module *, int, void *); void *dmd_chainarg; const char *dmd_busname; driver_t **dmd_drivers; int dmd_ndrivers; devclass_t *dmd_devclass; }; #define DRIVER_MODULE(name, busname, driver, devclass, evh, arg) \ \ static driver_t *name##_##busname##_driver_list[] = { &driver }; \ static struct driver_module_data name##_##busname##_driver_mod = { \ evh, arg, \ #busname, \ name##_##busname##_driver_list, \ (sizeof name##_##busname##_driver_list) / \ (sizeof name##_##busname##_driver_list[0]), \ &devclass \ }; \ \ static moduledata_t name##_##busname##_mod = { \ #busname "/" #name, \ driver_module_handler, \ &name##_##busname##_driver_mod \ }; \ DECLARE_MODULE(name##_##busname, name##_##busname##_mod, \ SI_SUB_DRIVERS, SI_ORDER_MIDDLE) #define MULTI_DRIVER_MODULE(name, busname, drivers, devclass, evh, arg) \ \ static driver_t name##_##busname##_driver_list[] = drivers; \ static struct driver_module_data name##_##busname##_driver_mod = { \ evh, arg, \ #busname, \ name##_##busname##_driver_list, \ (sizeof name##_##busname##_driver_list) / \ (sizeof name##_##busname##_driver_list[0]), \ &devclass \ }; \ \ static moduledata_t name##_##busname##_mod = { \ #busname "/" #name, \ driver_module_handler, \ &name##_##busname##_driver_mod \ }; \ DECLARE_MODULE(name##_##busname, name##_##busname##_mod, \ SI_SUB_DRIVERS, SI_ORDER_MIDDLE) #define CDEV_DRIVER_MODULE(name, busname, driver, devclass, \ major, devsw, evh, arg) \ \ static struct cdevsw_module_data name##_##busname##_cdevsw_mod = { \ evh, arg, makedev(major, 0), &devsw \ }; \ \ DRIVER_MODULE(name, busname, driver, devclass, \ cdevsw_module_handler, &name##_##busname##_cdevsw_mod) #define BDEV_DRIVER_MODULE(name, busname, driver, devclass, \ bmajor, cmajor, devsw, evh, arg) \ \ static struct bdevsw_module_data name##_##busname##_bdevsw_mod = { \ evh, arg, makedev(bmajor, 0), makedev(cmajor, 0), &devsw \ }; \ \ DRIVER_MODULE(name, busname, driver, devclass, \ bdevsw_module_handler, &name##_##busname##_bdevsw_mod) #endif /* KERNEL */ #endif /* !_SYS_BUS_H_ */ Index: head/sys/sys/bus_private.h =================================================================== --- head/sys/sys/bus_private.h (revision 45719) +++ head/sys/sys/bus_private.h (revision 45720) @@ -1,144 +1,145 @@ /*- * Copyright (c) 1997,1998 Doug Rabson * 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. * - * $Id: bus_private.h,v 1.4 1998/11/14 21:58:41 wollman Exp $ + * $Id: bus_private.h,v 1.5 1999/03/29 08:54:19 dfr Exp $ */ #ifndef _SYS_BUS_PRIVATE_H_ #define _SYS_BUS_PRIVATE_H_ #include /* * Used to attach drivers to devclasses. */ typedef struct driverlink *driverlink_t; struct driverlink { driver_t *driver; TAILQ_ENTRY(driverlink) link; /* list of drivers in devclass */ }; /* * Forward declarations */ typedef TAILQ_HEAD(devclass_list, devclass) devclass_list_t; typedef TAILQ_HEAD(driver_list, driverlink) driver_list_t; typedef TAILQ_HEAD(device_list, device) device_list_t; struct devclass { TAILQ_ENTRY(devclass) link; driver_list_t drivers; /* bus devclasses store drivers for bus */ char *name; device_t *devices; /* array of devices indexed by unit */ int maxunit; /* size of devices array */ int nextunit; /* next unused unit number */ }; /* * Resources from config(8). */ typedef enum { RES_INT, RES_STRING, RES_LONG } resource_type; struct config_resource { char *name; resource_type type; union { long longval; int intval; char* stringval; } u; }; struct config_device { char *name; /* e.g. "lpt", "wdc" etc */ int unit; int resource_count; struct config_resource *resources; }; /* * Compiled device methods. */ struct device_ops { int maxoffset; devop_t methods[1]; }; /* * Helpers for device method wrappers. */ #define DEVOPDESC(OP) (&OP##_##desc) #define DEVOPOFF(DEV, OP) \ ((DEVOPDESC(OP)->offset >= DEV->ops->maxoffset \ || !DEV->ops->methods[DEVOPDESC(OP)->offset]) \ ? 0 : DEVOPDESC(OP)->offset) #define DEVOPMETH(DEV, OP) (DEV->ops->methods[DEVOPOFF(DEV, OP)]) /* * Implementation of device. */ struct device { /* * Device hierarchy. */ TAILQ_ENTRY(device) link; /* list of devices in parent */ device_t parent; device_list_t children; /* list of subordinate devices */ /* * Details of this device. */ device_ops_t ops; driver_t *driver; devclass_t devclass; /* device class which we are in */ int unit; char* nameunit; /* name+unit e.g. foodev0 */ char* desc; /* driver specific description */ int busy; /* count of calls to device_busy() */ device_state_t state; int flags; #ifdef DEVICE_SYSCTLS struct sysctl_oid oid[4]; struct sysctl_oid_list oidlist[1]; #endif #define DF_ENABLED 1 /* device should be probed/attached */ #define DF_FIXEDCLASS 2 /* devclass specified at create time */ #define DF_WILDCARD 4 /* unit was originally wildcard */ #define DF_DESCMALLOCED 8 /* description was malloced */ #define DF_QUIET 16 /* don't print verbose attach message */ void *ivars; void *softc; }; struct device_op_desc { unsigned int offset; /* offset in driver ops */ + struct method* method; /* internal method implementation */ const char* name; /* unique name (for registration) */ }; #endif /* !_SYS_BUS_PRIVATE_H_ */ Index: head/sys/sys/rman.h =================================================================== --- head/sys/sys/rman.h (revision 45719) +++ head/sys/sys/rman.h (revision 45720) @@ -1,97 +1,106 @@ /* * Copyright 1998 Massachusetts Institute of Technology * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby * granted, provided that both the above copyright notice and this * permission notice appear in all copies, that both the above * copyright notice and this permission notice appear in all * supporting documentation, and that the name of M.I.T. not be used * in advertising or publicity pertaining to distribution of the * software without specific, written prior permission. M.I.T. makes * no representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied * warranty. * * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT * SHALL M.I.T. 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. * - * $Id$ + * $Id: rman.h,v 1.1 1998/10/29 01:48:30 wollman Exp $ */ #ifndef _SYS_RMAN_H_ #define _SYS_RMAN_H_ 1 #ifndef KERNEL #include #endif /* !KERNEL */ /* * We use a linked list rather than a bitmap because we need to be able to * represent potentially huge objects (like all of a processor's physical * address space). That is also why the indices are defined to have type * `unsigned long' -- that being the largest integral type in Standard C. */ CIRCLEQ_HEAD(resource_head, resource); struct resource { CIRCLEQ_ENTRY(resource) r_link; LIST_ENTRY(resource) r_sharelink; LIST_HEAD(, resource) *r_sharehead; u_long r_start; /* index of the first entry in this resource */ u_long r_end; /* index of the last entry (inclusive) */ u_int r_flags; void *r_virtual; /* virtual address of this resource */ + bus_space_tag_t r_bustag; /* bus_space tag */ + bus_space_handle_t r_bushandle; /* bus_space handle */ struct device *r_dev; /* device which has allocated this resource */ struct rman *r_rm; /* resource manager from whence this came */ }; #define RF_ALLOCATED 0x0001 /* resource has been reserved */ #define RF_ACTIVE 0x0002 /* resource allocation has been activated */ #define RF_SHAREABLE 0x0004 /* resource permits contemporaneous sharing */ #define RF_TIMESHARE 0x0008 /* resource permits time-division sharing */ #define RF_WANTED 0x0010 /* somebody is waiting for this resource */ #define RF_FIRSTSHARE 0x0020 /* first in sharing list */ enum rman_type { RMAN_UNINIT = 0, RMAN_GAUGE, RMAN_ARRAY }; struct rman { struct resource_head rm_list; struct simplelock *rm_slock; /* mutex used to protect rm_list */ TAILQ_ENTRY(rman) rm_link; /* link in list of all rmans */ u_long rm_start; /* index of globally first entry */ u_long rm_end; /* index of globally last entry */ enum rman_type rm_type; /* what type of resource this is */ const char *rm_descr; /* text descripion of this resource */ }; TAILQ_HEAD(rman_head, rman); #ifdef KERNEL int rman_activate_resource(struct resource *r); int rman_await_resource(struct resource *r, int pri, int timo); int rman_deactivate_resource(struct resource *r); int rman_fini(struct rman *rm); int rman_init(struct rman *rm); int rman_manage_region(struct rman *rm, u_long start, u_long end); int rman_release_resource(struct resource *r); struct resource *rman_reserve_resource(struct rman *rm, u_long start, u_long end, u_long count, u_int flags, struct device *dev); +#define rman_get_start(r) ((r)->r_start) +#define rman_get_end(r) ((r)->r_end) +#define rman_get_flags(r) ((r)->r_flags) #define rman_set_virtual(r,v) ((r)->r_virtual = (v)) #define rman_get_virtual(r) ((r)->r_virtual) +#define rman_set_bustag(r,t) ((r)->r_bustag = (t)) +#define rman_get_bustag(r) ((r)->r_bustag) +#define rman_set_bushandle(r,h) ((r)->r_bushandle = (h)) +#define rman_get_bushandle(r) ((r)->r_bushandle) extern struct rman_head rman_head; #endif /* KERNEL */ #endif /* !_SYS_RMAN_H_ */