📄 aic79xx_osm.c
字号:
* hack. */ if (cmd->result == (CAM_REQUEUE_REQ << 16)) cmd->retries--; completeq = &ahd->platform_data->completeq; list_cmd = TAILQ_FIRST(completeq); acmd = (struct ahd_cmd *)cmd; while (list_cmd != NULL && acmd_scsi_cmd(list_cmd).serial_number < acmd_scsi_cmd(acmd).serial_number) list_cmd = TAILQ_NEXT(list_cmd, acmd_links.tqe); if (list_cmd != NULL) TAILQ_INSERT_BEFORE(list_cmd, acmd, acmd_links.tqe); else TAILQ_INSERT_TAIL(completeq, acmd, acmd_links.tqe);}static __inline voidahd_linux_run_complete_queue(struct ahd_softc *ahd, struct ahd_cmd *acmd){ u_long done_flags; ahd_done_lock(ahd, &done_flags); while (acmd != NULL) { Scsi_Cmnd *cmd; cmd = &acmd_scsi_cmd(acmd); acmd = TAILQ_NEXT(acmd, acmd_links.tqe); cmd->host_scribble = NULL; cmd->scsi_done(cmd); } ahd_done_unlock(ahd, &done_flags);}static __inline voidahd_linux_check_device_queue(struct ahd_softc *ahd, struct ahd_linux_device *dev){ if ((dev->flags & AHD_DEV_FREEZE_TIL_EMPTY) != 0 && dev->active == 0) { dev->flags &= ~AHD_DEV_FREEZE_TIL_EMPTY; dev->qfrozen--; } if (TAILQ_FIRST(&dev->busyq) == NULL || dev->openings == 0 || dev->qfrozen != 0) return; ahd_linux_run_device_queue(ahd, dev);}static __inline struct ahd_linux_device *ahd_linux_next_device_to_run(struct ahd_softc *ahd){ if ((ahd->flags & AHD_RESOURCE_SHORTAGE) != 0 || ahd->platform_data->qfrozen != 0) return (NULL); return (TAILQ_FIRST(&ahd->platform_data->device_runq));}static __inline voidahd_linux_run_device_queues(struct ahd_softc *ahd){ struct ahd_linux_device *dev; while ((dev = ahd_linux_next_device_to_run(ahd)) != NULL) { TAILQ_REMOVE(&ahd->platform_data->device_runq, dev, links); dev->flags &= ~AHD_DEV_ON_RUN_LIST; ahd_linux_check_device_queue(ahd, dev); }}static __inline voidahd_linux_sniff_command(struct ahd_softc *ahd, Scsi_Cmnd *cmd, struct scb *scb){ /* * Determine whether we care to filter * information out of this command. If so, * pass it on to ahd_linux_filter_command() for more * heavy weight processing. */ if (cmd->cmnd[0] == INQUIRY) ahd_linux_filter_command(ahd, cmd, scb);}static __inline voidahd_linux_unmap_scb(struct ahd_softc *ahd, struct scb *scb){ Scsi_Cmnd *cmd; int direction; cmd = scb->io_ctx; direction = scsi_to_pci_dma_dir(cmd->sc_data_direction); ahd_sync_sglist(ahd, scb, BUS_DMASYNC_POSTWRITE); if (cmd->use_sg != 0) { struct scatterlist *sg; sg = (struct scatterlist *)cmd->request_buffer; pci_unmap_sg(ahd->dev_softc, sg, cmd->use_sg, direction); } else if (cmd->request_bufflen != 0) { pci_unmap_single(ahd->dev_softc, scb->platform_data->buf_busaddr, cmd->request_bufflen, direction); }}static __inline intahd_linux_map_seg(struct ahd_softc *ahd, struct scb *scb, struct ahd_dma_seg *sg, bus_addr_t addr, bus_size_t len){ int consumed; if ((scb->sg_count + 1) > AHD_NSEG) panic("Too few segs for dma mapping. " "Increase AHD_NSEG\n"); consumed = 1; sg->addr = ahd_htole32(addr & 0xFFFFFFFF); scb->platform_data->xfer_len += len; if (sizeof(bus_addr_t) > 4 && (ahd->flags & AHD_39BIT_ADDRESSING) != 0) { /* * Due to DAC restrictions, we can't * cross a 4GB boundary. */ if ((addr ^ (addr + len - 1)) & ~0xFFFFFFFF) { struct ahd_dma_seg *next_sg; uint32_t next_len; printf("Crossed Seg\n"); if ((scb->sg_count + 2) > AHD_NSEG) panic("Too few segs for dma mapping. " "Increase AHD_NSEG\n"); consumed++; next_sg = sg + 1; next_sg->addr = 0; next_len = 0x100000000 - (addr & 0xFFFFFFFF); len -= next_len; next_len |= ((addr >> 8) + 0x1000000) & 0x7F000000; next_sg->len = ahd_htole32(next_len); } len |= (addr >> 8) & 0x7F000000; } sg->len = ahd_htole32(len); return (consumed);}/**************************** Tasklet Handler *********************************/static voidahd_runq_tasklet(unsigned long data){ struct ahd_softc* ahd; struct ahd_linux_device *dev; u_long flags; ahd = (struct ahd_softc *)data; ahd_lock(ahd, &flags); while ((dev = ahd_linux_next_device_to_run(ahd)) != NULL) { TAILQ_REMOVE(&ahd->platform_data->device_runq, dev, links); dev->flags &= ~AHD_DEV_ON_RUN_LIST; ahd_linux_check_device_queue(ahd, dev); /* Yeild to our interrupt handler */ ahd_unlock(ahd, &flags); ahd_lock(ahd, &flags); } ahd_unlock(ahd, &flags);}/************************ Shutdown/halt/reboot hook ***************************/#include <linux/notifier.h>#include <linux/reboot.h>static struct notifier_block ahd_linux_notifier = { ahd_linux_halt, NULL, 0};static int ahd_linux_halt(struct notifier_block *nb, u_long event, void *buf){ struct ahd_softc *ahd; if (event == SYS_DOWN || event == SYS_HALT) { TAILQ_FOREACH(ahd, &ahd_tailq, links) { ahd_shutdown(ahd); } } return (NOTIFY_OK);}/******************************** Macros **************************************/#define BUILD_SCSIID(ahd, cmd) \ ((((cmd)->target << TID_SHIFT) & TID) | (ahd)->our_id)/******************************** Bus DMA *************************************/intahd_dma_tag_create(struct ahd_softc *ahd, 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);}voidahd_dma_tag_destroy(struct ahd_softc *ahd, bus_dma_tag_t dmat){ free(dmat, M_DEVBUF);}intahd_dmamem_alloc(struct ahd_softc *ahd, 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 (ahd->dev_softc != NULL) ahd_pci_set_dma_mask(ahd->dev_softc, 0xFFFFFFFF); *vaddr = pci_alloc_consistent(ahd->dev_softc, dmat->maxsize, &map->bus_addr); if (ahd->dev_softc != NULL) ahd_pci_set_dma_mask(ahd->dev_softc, ahd->platform_data->hw_dma_mask);#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 accessable 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);}voidahd_dmamem_free(struct ahd_softc *ahd, bus_dma_tag_t dmat, void* vaddr, bus_dmamap_t map){#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) pci_free_consistent(ahd->dev_softc, dmat->maxsize, vaddr, map->bus_addr);#else free(vaddr, M_DEVBUF);#endif}intahd_dmamap_load(struct ahd_softc *ahd, 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);}voidahd_dmamap_destroy(struct ahd_softc *ahd, 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);}intahd_dmamap_unload(struct ahd_softc *ahd, bus_dma_tag_t dmat, bus_dmamap_t map){ /* Nothing to do */ return (0);}/********************* Platform Dependent Functions ***************************/intahd_softc_comp(struct ahd_softc *lahd, struct ahd_softc *rahd){ int value; char primary_channel; /* * Under Linux, cards are ordered as follows: * 1) PCI devices with BIOS enabled sorted by bus/slot/func. * 2) All remaining PCI devices sorted by bus/slot/func. */ value = (lahd->flags & AHD_BIOS_ENABLED) - (rahd->flags & AHD_BIOS_ENABLED); if (value != 0) /* Controllers with BIOS enabled have a *higher* priority */ return (-value); /* Still equal. Sort by bus/slot/func. */ if (aic79xx_reverse_scan != 0) value = ahd_get_pci_bus(rahd->dev_softc) - ahd_get_pci_bus(lahd->dev_softc); else value = ahd_get_pci_bus(lahd->dev_softc) - ahd_get_pci_bus(rahd->dev_softc); if (value != 0) return (value); if (aic79xx_reverse_scan != 0) value = ahd_get_pci_slot(rahd->dev_softc) - ahd_get_pci_slot(lahd->dev_softc); else value = ahd_get_pci_slot(lahd->dev_softc) - ahd_get_pci_slot(rahd->dev_softc); if (value != 0) return (value); /* * On multi-function devices, the user can choose * to have function 1 probed before function 0. * Give whichever channel is the primary channel * the lowest priority. */ primary_channel = (lahd->flags & AHD_PRIMARY_CHANNEL) + 'A'; value = 1; if (lahd->channel == primary_channel) value = -1; return (value);}static voidahd_linux_setup_tag_info(char *p, char *end){ char *base; char *tok; char *tok_end; char *tok_end2; int i; int instance; int targ; int done; char tok_list[] = {'.', ',', '{', '}', '\0'}; if (*p != ':') return; instance = -1; targ = -1; done = FALSE; base = p; /* Forward us just past the ':' */ tok = base + 1; tok_end = strchr(tok, '\0'); if (tok_end < end) *tok_end = ','; while (!done) { switch (*tok) { case '{': if (instance == -1) instance = 0; else if (targ == -1) targ = 0; tok++; break; case '}': if (targ != -1) targ = -1; else if (instance != -1) instance = -1; tok++; break; case ',': case '.': if (instance == -1) done = TRUE; else if (targ >= 0) targ++; else if (instance >= 0) instance++; if ((targ >= AHD_NUM_TARGETS) || (instance >= NUM_ELEMENTS(aic79xx_tag_info))) done = TRUE; tok++; if (!done) { base = tok; } break; case '\0': done = TRUE; break; default: done = TRUE; tok_end = strchr(tok, '\0'); for (i = 0; tok_list[i]; i++) { tok_end2 = strchr(tok, tok_list[i]); if ((tok_end2) && (tok_end2 < tok_end)) { tok_end = tok_end2; done = FALSE; } } if ((instance >= 0) && (targ >= 0) && (instance < NUM_ELEMENTS(aic79xx_tag_info)) && (targ < AHD_NUM_TARGETS)) { aic79xx_tag_info[instance].tag_commands[targ] = simple_strtoul(tok, NULL, 0) & 0xff; } tok = tok_end; break; } } while ((p != base) && (p != NULL)) p = strtok(NULL, ",.");}static voidahd_linux_setup_rd_strm_info(char *p, char *end){ char *base; char *tok; char *tok_end; char *tok_end2; int i; int instance; int targ; int done; char tok_list[] = {'.', ',', '{', '}', '\0'}; if (*p != ':')
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -