📄 aic7xxx_osm.c
字号:
#endif ahc = (struct ahc_softc *)data;#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) ahc_lock(ahc, &flags);#endif while ((dev = ahc_linux_next_device_to_run(ahc)) != NULL) { TAILQ_REMOVE(&ahc->platform_data->device_runq, dev, links); dev->flags &= ~AHC_DEV_ON_RUN_LIST; ahc_linux_check_device_queue(ahc, dev);#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) /* Yeild to our interrupt handler */ ahc_unlock(ahc, &flags); ahc_lock(ahc, &flags);#endif }#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) ahc_unlock(ahc, &flags);#endif}/******************************** Macros **************************************/#define BUILD_SCSIID(ahc, cmd) \ ((((cmd)->device->id << TID_SHIFT) & TID) \ | (((cmd)->device->channel == 0) ? (ahc)->our_id : (ahc)->our_id_b) \ | (((cmd)->device->channel == 0) ? 0 : TWIN_CHNLB))/******************************** Bus DMA *************************************/intahc_dma_tag_create(struct ahc_softc *ahc, bus_dma_tag_t parent, bus_size_t alignment, bus_size_t boundary, bus_addr_t lowaddr, bus_addr_t highaddr, bus_dma_filter_t *filter, void *filterarg, bus_size_t maxsize, int nsegments, bus_size_t maxsegsz, int flags, bus_dma_tag_t *ret_tag){ bus_dma_tag_t dmat; dmat = malloc(sizeof(*dmat), M_DEVBUF, M_NOWAIT); if (dmat == NULL) return (ENOMEM); /* * Linux is very simplistic about DMA memory. For now don't * maintain all specification information. Once Linux supplies * better facilities for doing these operations, or the * needs of this particular driver change, we might need to do * more here. */ dmat->alignment = alignment; dmat->boundary = boundary; dmat->maxsize = maxsize; *ret_tag = dmat; return (0);}voidahc_dma_tag_destroy(struct ahc_softc *ahc, bus_dma_tag_t dmat){ free(dmat, M_DEVBUF);}intahc_dmamem_alloc(struct ahc_softc *ahc, bus_dma_tag_t dmat, void** vaddr, int flags, bus_dmamap_t *mapp){ bus_dmamap_t map;#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) map = malloc(sizeof(*map), M_DEVBUF, M_NOWAIT); if (map == NULL) return (ENOMEM); /* * Although we can dma data above 4GB, our * "consistent" memory is below 4GB for * space efficiency reasons (only need a 4byte * address). For this reason, we have to reset * our dma mask when doing allocations. */ if (ahc->dev_softc != NULL) if (ahc_pci_set_dma_mask(ahc->dev_softc, 0xFFFFFFFF)) { printk(KERN_WARNING "aic7xxx: No suitable DMA available.\n"); return (ENODEV); } *vaddr = pci_alloc_consistent(ahc->dev_softc, dmat->maxsize, &map->bus_addr); if (ahc->dev_softc != NULL) if (ahc_pci_set_dma_mask(ahc->dev_softc, ahc->platform_data->hw_dma_mask)) { printk(KERN_WARNING "aic7xxx: No suitable DMA available.\n"); return (ENODEV); }#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) */ /* * At least in 2.2.14, malloc is a slab allocator so all * allocations are aligned. We assume for these kernel versions * that all allocations will be bellow 4Gig, physically contiguous, * and accessible via DMA by the controller. */ map = NULL; /* No additional information to store */ *vaddr = malloc(dmat->maxsize, M_DEVBUF, M_NOWAIT);#endif if (*vaddr == NULL) return (ENOMEM); *mapp = map; return(0);}voidahc_dmamem_free(struct ahc_softc *ahc, bus_dma_tag_t dmat, void* vaddr, bus_dmamap_t map){#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) pci_free_consistent(ahc->dev_softc, dmat->maxsize, vaddr, map->bus_addr);#else free(vaddr, M_DEVBUF);#endif}intahc_dmamap_load(struct ahc_softc *ahc, bus_dma_tag_t dmat, bus_dmamap_t map, void *buf, bus_size_t buflen, bus_dmamap_callback_t *cb, void *cb_arg, int flags){ /* * Assume for now that this will only be used during * initialization and not for per-transaction buffer mapping. */ bus_dma_segment_t stack_sg;#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) stack_sg.ds_addr = map->bus_addr;#else#define VIRT_TO_BUS(a) (uint32_t)virt_to_bus((void *)(a)) stack_sg.ds_addr = VIRT_TO_BUS(buf);#endif stack_sg.ds_len = dmat->maxsize; cb(cb_arg, &stack_sg, /*nseg*/1, /*error*/0); return (0);}voidahc_dmamap_destroy(struct ahc_softc *ahc, bus_dma_tag_t dmat, bus_dmamap_t map){ /* * The map may is NULL in our < 2.3.X implementation. */ if (map != NULL) free(map, M_DEVBUF);}intahc_dmamap_unload(struct ahc_softc *ahc, bus_dma_tag_t dmat, bus_dmamap_t map){ /* Nothing to do */ return (0);}/********************* Platform Dependent Functions ***************************//* * Compare "left hand" softc with "right hand" softc, returning: * < 0 - lahc has a lower priority than rahc * 0 - Softcs are equal * > 0 - lahc has a higher priority than rahc */intahc_softc_comp(struct ahc_softc *lahc, struct ahc_softc *rahc){ int value; int rvalue; int lvalue; /* * Under Linux, cards are ordered as follows: * 1) VLB/EISA BIOS enabled devices sorted by BIOS address. * 2) PCI devices with BIOS enabled sorted by bus/slot/func. * 3) All remaining VLB/EISA devices sorted by ioport. * 4) All remaining PCI devices sorted by bus/slot/func. */ value = (lahc->flags & AHC_BIOS_ENABLED) - (rahc->flags & AHC_BIOS_ENABLED); if (value != 0) /* Controllers with BIOS enabled have a *higher* priority */ return (value); /* * Same BIOS setting, now sort based on bus type. * EISA and VL controllers sort together. EISA/VL * have higher priority than PCI. */ rvalue = (rahc->chip & AHC_BUS_MASK); if (rvalue == AHC_VL) rvalue = AHC_EISA; lvalue = (lahc->chip & AHC_BUS_MASK); if (lvalue == AHC_VL) lvalue = AHC_EISA; value = rvalue - lvalue; if (value != 0) return (value); /* Still equal. Sort by BIOS address, ioport, or bus/slot/func. */ switch (rvalue) {#ifdef CONFIG_PCI case AHC_PCI: { char primary_channel; if (aic7xxx_reverse_scan != 0) value = ahc_get_pci_bus(lahc->dev_softc) - ahc_get_pci_bus(rahc->dev_softc); else value = ahc_get_pci_bus(rahc->dev_softc) - ahc_get_pci_bus(lahc->dev_softc); if (value != 0) break; if (aic7xxx_reverse_scan != 0) value = ahc_get_pci_slot(lahc->dev_softc) - ahc_get_pci_slot(rahc->dev_softc); else value = ahc_get_pci_slot(rahc->dev_softc) - ahc_get_pci_slot(lahc->dev_softc); if (value != 0) break; /* * On multi-function devices, the user can choose * to have function 1 probed before function 0. * Give whichever channel is the primary channel * the highest priority. */ primary_channel = (lahc->flags & AHC_PRIMARY_CHANNEL) + 'A'; value = -1; if (lahc->channel == primary_channel) value = 1; break; }#endif case AHC_EISA: if ((rahc->flags & AHC_BIOS_ENABLED) != 0) { value = rahc->platform_data->bios_address - lahc->platform_data->bios_address; } else { value = rahc->bsh.ioport - lahc->bsh.ioport; } break; default: panic("ahc_softc_sort: invalid bus type"); } return (value);}static voidahc_linux_setup_tag_info_global(char *p){ int tags, i, j; tags = simple_strtoul(p + 1, NULL, 0) & 0xff; printf("Setting Global Tags= %d\n", tags); for (i = 0; i < NUM_ELEMENTS(aic7xxx_tag_info); i++) { for (j = 0; j < AHC_NUM_TARGETS; j++) { aic7xxx_tag_info[i].tag_commands[j] = tags; } }}static voidahc_linux_setup_tag_info(u_long arg, int instance, int targ, int32_t value){ if ((instance >= 0) && (targ >= 0) && (instance < NUM_ELEMENTS(aic7xxx_tag_info)) && (targ < AHC_NUM_TARGETS)) { aic7xxx_tag_info[instance].tag_commands[targ] = value & 0xff; if (bootverbose) printf("tag_info[%d:%d] = %d\n", instance, targ, value); }}static voidahc_linux_setup_dv(u_long arg, int instance, int targ, int32_t value){ if ((instance >= 0) && (instance < NUM_ELEMENTS(aic7xxx_dv_settings))) { aic7xxx_dv_settings[instance] = value; if (bootverbose) printf("dv[%d] = %d\n", instance, value); }}/* * Handle Linux boot parameters. This routine allows for assigning a value * to a parameter with a ':' between the parameter and the value. * ie. aic7xxx=stpwlev:1,extended */static intaic7xxx_setup(char *s){ int i, n; char *p; char *end; static struct { const char *name; uint32_t *flag; } options[] = { { "extended", &aic7xxx_extended }, { "no_reset", &aic7xxx_no_reset }, { "verbose", &aic7xxx_verbose }, { "allow_memio", &aic7xxx_allow_memio},#ifdef AHC_DEBUG { "debug", &ahc_debug },#endif { "reverse_scan", &aic7xxx_reverse_scan }, { "no_probe", &aic7xxx_probe_eisa_vl }, { "probe_eisa_vl", &aic7xxx_probe_eisa_vl }, { "periodic_otag", &aic7xxx_periodic_otag }, { "pci_parity", &aic7xxx_pci_parity }, { "seltime", &aic7xxx_seltime }, { "tag_info", NULL }, { "global_tag_depth", NULL }, { "dv", NULL } }; end = strchr(s, '\0'); /* * XXX ia64 gcc isn't smart enough to know that NUM_ELEMENTS * will never be 0 in this case. */ n = 0; while ((p = strsep(&s, ",.")) != NULL) { if (*p == '\0') continue; for (i = 0; i < NUM_ELEMENTS(options); i++) { n = strlen(options[i].name); if (strncmp(options[i].name, p, n) == 0) break; } if (i == NUM_ELEMENTS(options)) continue; if (strncmp(p, "global_tag_depth", n) == 0) { ahc_linux_setup_tag_info_global(p + n); } else if (strncmp(p, "tag_info", n) == 0) { s = aic_parse_brace_option("tag_info", p + n, end, 2, ahc_linux_setup_tag_info, 0); } else if (strncmp(p, "dv", n) == 0) { s = aic_parse_brace_option("dv", p + n, end, 1, ahc_linux_setup_dv, 0); } else if (p[n] == ':') { *(options[i].flag) = simple_strtoul(p + n + 1, NULL, 0); } else if (strncmp(p, "verbose", n) == 0) { *(options[i].flag) = 1; } else { *(options[i].flag) ^= 0xFFFFFFFF; } } return 1;}#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,0)__setup("aic7xxx=", aic7xxx_setup);#endifuint32_t aic7xxx_verbose;intahc_linux_register_host(struct ahc_softc *ahc, Scsi_Host_Template *template){ char buf[80]; struct Scsi_Host *host; char *new_name; u_long s; u_int targ_offset; template->name = ahc->description; host = scsi_host_alloc(template, sizeof(struct ahc_softc *)); if (host == NULL) return (ENOMEM); *((struct ahc_softc **)host->hostdata) = ahc; ahc_lock(ahc, &s);#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) scsi_assign_lock(host, &ahc->platform_data->spin_lock);#elif AHC_SCSI_HAS_HOST_LOCK != 0 host->lock = &ahc->platform_data->spin_lock;#endif ahc->platform_data->host = host; host->can_queue = AHC_MAX_QUEUE; host->cmd_per_lun = 2; /* XXX No way to communicate the ID for multiple channels */ host->this_id = ahc->our_id; host->irq = ahc->platform_data->irq; host->max_id = (ahc->features & AHC_WIDE) ? 16 : 8; host->max_lun = AHC_NUM_LUNS; host->max_channel = (ahc->features & AHC_TWIN) ? 1 : 0; host->sg_tablesize = AHC_NSEG; ahc_set_unit(ahc, ahc_linux_next_unit()); sprintf(buf, "scsi%d", host->host_no); new_name = malloc(strlen(buf) + 1, M_DEVBUF, M_NOWAIT); if (new_name != NULL) { strcpy(new_name, buf); ahc_set_name(ahc, new_name); } host->unique_id = ahc->unit;#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,4) && \ LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) scsi_set_pci_device(host, ahc->dev_softc);#endif ahc_linux_initialize_scsi_bus(ahc); ahc_unlock(ahc, &s); ahc->platform_data->dv_pid = kernel_thread(ahc_linux_dv_thread, ahc, 0); ahc_lock(ahc, &s); if (ahc->platform_data->dv_pid < 0) { printf("%s: Failed to create DV thread, error= %d\n", ahc_name(ahc), ahc->platform_data->dv_pid); return (-ahc->platform_data->dv_pid); } /* * Initially allocate *all* of our linux target objects * so that the DV thread will scan them all in parallel * just after driver initialization. Any device that * does not exist will have its target object destroyed * by the selection timeout handler. In the case of a * device that appears after the initial DV scan, async * negotiation will occur for the first command, and DV * will comence should that first command be successful. */ for (targ_offset = 0; targ_offset < host->max_id * (host->max_channel + 1); targ_offset++) { u_int channel; u_int target; channel = 0; target = targ_offset; if (target > 7
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -