看板 DFBSD_bugs 關於我們 聯絡資訊
Ok, people whos floppies aren't recognized, please try this patch. I did not adopt the FreeBSD work, it was such a huge hack that there's no point even trying to decipher the mess. Instead I rolled a routine to allocate all available resources of a particular type and return information about what it did, and then check that against reasonable values. -Matt Index: dev/disk/fd/fd.c =================================================================== RCS file: /cvs/src/sys/dev/disk/fd/fd.c,v retrieving revision 1.19 diff -u -r1.19 fd.c --- dev/disk/fd/fd.c 19 Sep 2004 00:36:37 -0000 1.19 +++ dev/disk/fd/fd.c 13 Oct 2004 01:24:50 -0000 @@ -313,7 +313,7 @@ static void fdctl_wr_isa(fdc_p fdc, u_int8_t v) { - bus_space_write_1(fdc->ctlt, fdc->ctlh, 0, v); + bus_space_write_1(fdc->portt, fdc->porth, 7 + fdc->port_off, v); } #if 0 @@ -537,84 +537,107 @@ { device_t dev; int ispnp, ispcmcia; + u_long range_start; + u_long range_size; + u_long actual_size; + int error; dev = fdc->fdc_dev; ispnp = (fdc->flags & FDC_ISPNP) != 0; ispcmcia = (fdc->flags & FDC_ISPCMCIA) != 0; - fdc->rid_ioport = fdc->rid_irq = fdc->rid_drq = 0; - fdc->res_ioport = fdc->res_irq = fdc->res_drq = 0; + fdc->rid_irq = 0; + fdc->rid_drq = 0; + fdc->res_nioports = 0; + fdc->res_irq = NULL; + fdc->res_drq = NULL; + callout_init(&fdc->pseudointr_ch); /* * On standard ISA, we don't just use an 8 port range * (e.g. 0x3f0-0x3f7) since that covers an IDE control - * register at 0x3f6. - * - * Isn't PC hardware wonderful. + * register at 0x3f6. Isn't PC hardware wonderful? * * The Y-E Data PCMCIA FDC doesn't have this problem, it * uses the register with offset 6 for pseudo-DMA, and the * one with offset 7 as control register. + * + * Even worse, many BIOS makers improperly specify the ACPI + * resource lists, sometimes starting at 0x3f2, sometimes only + * listing a contiguous range of 2 bytes for the first (rid 0) + * resource, and so forth. Known combinations: + * + * 1: 0x3f0-0x3f5 # very rare + * 2: 0x3f0 # hints -> 0x3f0-0x3f5,0x3f7 + * 3: 0x3f0-0x3f5,0x3f7 # Most common + * 4: 0x3f2-0x3f5,0x3f7 # Second most common + * 5: 0x3f2-0x3f5 # implies 0x3f7 too. + * 6: 0x3f2-0x3f3,0x3f4-0x3f5,0x3f7 # becoming common + * 7: 0x3f2-0x3f3,0x3f4-0x3f5 # rare + * 8: 0x3f0-0x3f1,0x3f2-0x3f3,0x3f4-0x3f5,0x3f7 + * 9: 0x3f0-0x3f3,0x3f4-0x3f5,0x3f7 + * + * To deal with this mess we use bus_alloc_all_resources() which + * will allocate all necessary resources within the area mask and + * return the overall start and size, and the resource associated with + * the lowest address found. + * + * For floppy contorllers we just assume that */ - fdc->res_ioport = bus_alloc_resource(dev, SYS_RES_IOPORT, - &fdc->rid_ioport, 0ul, ~0ul, - ispcmcia ? 8 : (ispnp ? 1 : 6), - RF_ACTIVE); - if (fdc->res_ioport == 0) { - device_printf(dev, "cannot reserve I/O port range\n"); + error = bus_alloc_all_resources(dev, SYS_RES_IOPORT, RF_ACTIVE, + fdc->res_ioports, FDC_MAX_IORESOURCES, + &fdc->res_nioports, 7, + &range_start, &range_size, + &actual_size, &fdc->res_ioport); + printf("floppy: nioresources %d range %04lx-%04lx (%ld)\n", + fdc->res_nioports, range_start, range_start + range_size, + actual_size); + + if (error) { + device_printf(dev, "Has no conforming I/O port range\n"); return ENXIO; } - fdc->portt = rman_get_bustag(fdc->res_ioport); - fdc->porth = rman_get_bushandle(fdc->res_ioport); - - if (!ispcmcia) { - /* - * Some BIOSen report the device at 0x3f2-0x3f5,0x3f7 - * and some at 0x3f0-0x3f5,0x3f7. We detect the former - * by checking the size and adjust the port address - * accordingly. - */ - if (bus_get_resource_count(dev, SYS_RES_IOPORT, 0) == 4) - fdc->port_off = -2; + /* + * Check the masked start port offset for the 0x3f2 case. + */ + switch(range_start & 7) { + case 0: + fdc->port_off = 0; + break; + case 2: /* - * Register the control port range as rid 1 if it - * isn't there already. Most PnP BIOSen will have - * already done this but non-PnP configurations don't. - * - * And some (!!) report 0x3f2-0x3f5 and completely - * leave out the control register! It seems that some - * non-antique controller chips have a different - * method of programming the transfer speed which - * doesn't require the control register, but it's - * mighty bogus as the chip still responds to the - * address for the control register. + * BIOS resource list bug, started at 0x3f2 instead of 0x3f0. */ - if (bus_get_resource_count(dev, SYS_RES_IOPORT, 1) == 0) { - u_long ctlstart; - - /* Find the control port, usually 0x3f7 */ - ctlstart = rman_get_start(fdc->res_ioport) + - fdc->port_off + 7; - - bus_set_resource(dev, SYS_RES_IOPORT, 1, ctlstart, 1); - } + device_printf(dev, "Warning: corrected illegal port range\n"); + fdc->port_off = -2; + actual_size += 2; /* fake it */ + break; + default: + device_printf(dev, "Illegal I/O port range\n"); + return (ENXIO); + } - /* - * Now (finally!) allocate the control port. - */ - fdc->rid_ctl = 1; - fdc->res_ctl = bus_alloc_resource(dev, SYS_RES_IOPORT, - &fdc->rid_ctl, - 0ul, ~0ul, 1, RF_ACTIVE); - if (fdc->res_ctl == 0) { - device_printf(dev, - "cannot reserve control I/O port range\n"); - return ENXIO; - } - fdc->ctlt = rman_get_bustag(fdc->res_ctl); - fdc->ctlh = rman_get_bushandle(fdc->res_ctl); + /* + * The actual size ought to be 0, 7, or 8. It will be 0 if generated + * from an ISA hint, 7 normally (the range doesn't include 0x3f6 which + * is an IDE control register), or 8, which is usually a PCMCIA + * card that doesn't have an IDE control register in the middle of + * the floppy controller. + * + * Degenerate ISA hints will have a size of 1 + */ + printf("actual size: %d\n", actual_size); + if (actual_size != 0 && actual_size < 7) { + device_printf(dev, "Illegal I/O port range\n"); + return (ENXIO); } + fdc->portt = rman_get_bustag(fdc->res_ioport); + fdc->porth = rman_get_bushandle(fdc->res_ioport); + + /* + * Move on to other resources + */ fdc->res_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &fdc->rid_irq, 0ul, ~0ul, 1, RF_ACTIVE); @@ -642,6 +665,7 @@ { device_t dev; + callout_stop(&fdc->pseudointr_ch); dev = fdc->fdc_dev; if (fdc->res_irq != 0) { bus_deactivate_resource(dev, SYS_RES_IRQ, fdc->rid_irq, @@ -649,17 +673,14 @@ bus_release_resource(dev, SYS_RES_IRQ, fdc->rid_irq, fdc->res_irq); } - if (fdc->res_ctl != 0) { - bus_deactivate_resource(dev, SYS_RES_IOPORT, fdc->rid_ctl, - fdc->res_ctl); - bus_release_resource(dev, SYS_RES_IOPORT, fdc->rid_ctl, - fdc->res_ctl); - } - 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_nioports) { + bus_deactivate_all_resources(dev, SYS_RES_IOPORT, + fdc->res_ioports, fdc->res_nioports); + bus_release_all_resources(dev, SYS_RES_IOPORT, + fdc->res_ioports, &fdc->res_nioports); + fdc->res_nioports = 0; + fdc->res_ioport = NULL; } if (fdc->res_drq != 0) { bus_deactivate_resource(dev, SYS_RES_DRQ, fdc->rid_drq, Index: dev/disk/fd/fdc.h =================================================================== RCS file: /cvs/src/sys/dev/disk/fd/fdc.h,v retrieving revision 1.4 diff -u -r1.4 fdc.h --- dev/disk/fd/fdc.h 19 Sep 2004 00:36:37 -0000 1.4 +++ dev/disk/fd/fdc.h 13 Oct 2004 01:05:51 -0000 @@ -75,8 +75,15 @@ struct resource *res_irq, *res_drq; int rid_ioport, rid_irq, rid_drq; #else - struct resource *res_ioport, *res_ctl, *res_irq, *res_drq; - int rid_ioport, rid_ctl, rid_irq, rid_drq; +#define FDC_MAX_IORESOURCES 8 + struct resource_info res_ioports[FDC_MAX_IORESOURCES]; + struct resource *res_ioport; /* lowest port from ioports[]*/ + struct resource *res_irq; + struct resource *res_drq; + int res_nioports; + int rid_iostart; + int rid_irq; + int rid_drq; #endif int port_off; bus_space_tag_t portt; @@ -86,9 +93,6 @@ bus_space_handle_t sc_fdsioh; bus_space_tag_t sc_fdemsiot; bus_space_handle_t sc_fdemsioh; -#else - bus_space_tag_t ctlt; - bus_space_handle_t ctlh; #endif void *fdc_intr; struct device *fdc_dev; Index: kern/subr_bus.c =================================================================== RCS file: /cvs/src/sys/kern/subr_bus.c,v retrieving revision 1.21 diff -u -r1.21 subr_bus.c --- kern/subr_bus.c 8 Jul 2004 12:43:32 -0000 1.21 +++ kern/subr_bus.c 13 Oct 2004 01:16:22 -0000 @@ -2094,6 +2094,127 @@ count, flags)); } +/* + * This routine allocates all resources which fall within the area mask. + * The first address retrieved determines the I/O area. Resources are + * stuffed in the provided array (note that array entry 0 is left NULL) + * + * This is a real mess because the bus_*() infrastructure is a mess. + */ +int +bus_alloc_all_resources(device_t dev, int type, u_int flags, + struct resource_info *res_ary, int maxcount, + int *count, u_long area_mask, + u_long *range_start, u_long *range_size, + u_long *actual_size, struct resource **res_lo) +{ + struct resource *scan_res; + u_long scan_start; + u_long scan_size; + u_long range_area; + int error; + int rid; + int i; + + *res_lo = NULL; + *range_start = 0; + *range_size = 0; + *actual_size = 0; + *count = 0; + + if (maxcount == 0) + return (EINVAL); + + *range_start = 0; + *range_size = 0; + range_area = 0; + error = 0; + + for (rid = 0; *count < maxcount; ++*count, ++rid) { + scan_res = bus_alloc_resource(dev, type, &rid, 0ul, ~0ul, + 0, flags); + if (scan_res == NULL) { + if (*count == 0) + error = ENXIO; + break; + } + if (rid < 0 || rid >= maxcount) { + device_printf(dev, "bus_alloc_all_resources(): rid %d is out of range, cannot continue\n", rid); + bus_release_resource(dev, type, rid, scan_res); + error = ENXIO; + break; + } + scan_start = rman_get_start(scan_res); + scan_size = rman_get_size(scan_res); + + /* + * Initialize our initial range start in the first loop. + */ + if (*range_size == 0) { + *range_start = scan_start; + range_area = scan_start & ~area_mask; + } + + /* + * If we are beyond the requested area mask, stop. + */ + if (range_area != (scan_start & ~area_mask)) { + bus_release_resource(dev, type, rid, scan_res); + break; + } + + /* + * Update the resource array + */ + res_ary[*count].ri_res = scan_res; + res_ary[*count].ri_rid = rid; + + /* + * Clip the resource to the requested area mask and update + * return parameters. + */ + if (range_area != ((scan_start + scan_size) & ~area_mask)) { + scan_size = ((scan_start + area_mask) & ~area_mask) - + scan_start; + } + if (*range_start > scan_start) { + *range_size += *range_start - scan_start; + *range_start = scan_start; + } + if (*range_start + *range_size < scan_start + scan_size) { + *range_size += (scan_start + scan_size) - + (*range_start + *range_size); + } + if (scan_start <= *range_start) + *res_lo = scan_res; + *actual_size += scan_size; + } + + if (error) { + for (i = *count - 1; i >= 0; --i) + bus_release_resource(dev, type, res_ary[i].ri_rid, + res_ary[i].ri_res); + *count = 0; + *res_lo = NULL; + } + return (error); +} + +void +bus_release_all_resources(device_t dev, int type, + struct resource_info *res_ary, int *count) +{ + int i; + + for (i = *count - 1; i >= 0; --i) { + bus_release_resource(dev, type, + res_ary[i].ri_rid, res_ary[i].ri_res); + res_ary[i].ri_rid = 0; + res_ary[i].ri_res = NULL; + } + *count = 0; +} + int bus_activate_resource(device_t dev, int type, int rid, struct resource *r) { @@ -2111,6 +2232,24 @@ } int +bus_deactivate_all_resources(device_t dev, int type, + struct resource_info *res_ary, int count) +{ + int cumulative_error; + int error; + + cumulative_error = 0; + for (--count; count >= 0; --count) { + error = bus_deactivate_resource(dev, type, + res_ary[count].ri_rid, res_ary[count].ri_res); + if (error) + cumulative_error = error; + } + return(cumulative_error); +} + + +int bus_release_resource(device_t dev, int type, int rid, struct resource *r) { if (dev->parent == 0) Index: sys/bus.h =================================================================== RCS file: /cvs/src/sys/sys/bus.h,v retrieving revision 1.12 diff -u -r1.12 bus.h --- sys/bus.h 5 May 2004 16:57:11 -0000 1.12 +++ sys/bus.h 13 Oct 2004 01:15:34 -0000 @@ -96,6 +96,7 @@ * for their child devices. */ struct resource; +struct resource_info; struct resource_list_entry { SLIST_ENTRY(resource_list_entry) link; @@ -236,10 +237,19 @@ 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_alloc_all_resources(device_t dev, int type, u_int flags, + struct resource_info *res_ary, int maxcount, + int *count, u_long area_mask, + u_long *range_start, u_long *range_size, + u_long *actual_size, struct resource **res_lo); +void bus_release_all_resources(device_t dev, int type, + struct resource_info *res_ary, int *count); 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_deactivate_all_resources(device_t dev, int type, + struct resource_info *res_ary, int count); int bus_release_resource(device_t dev, int type, int rid, struct resource *r); int bus_setup_intr(device_t dev, struct resource *r, int flags, Index: sys/rman.h =================================================================== RCS file: /cvs/src/sys/sys/rman.h,v retrieving revision 1.7 diff -u -r1.7 rman.h --- sys/rman.h 1 Mar 2004 06:33:19 -0000 1.7 +++ sys/rman.h 13 Oct 2004 01:05:14 -0000 @@ -61,6 +61,11 @@ struct rman *r_rm; /* resource manager from whence this came */ }; +struct resource_info { + struct resource *ri_res; + int ri_rid; +}; + #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 */