📄 aic79xx_osm.c
字号:
if ((pending_scb->flags & SCB_PACKETIZED) != 0) { /* * Mark the SCB has having an outstanding * task management function. Should the command * complete normally before the task management * function can be sent, the host will be notified * to abort our requeued SCB. */ ahd_outb(ahd, SCB_TASK_MANAGEMENT, pending_scb->hscb->task_management); } else { /* * If non-packetized, set the MK_MESSAGE control * bit indicating that we desire to send a message. * We also set the disconnected flag since there is * no guarantee that our SCB control byte matches * the version on the card. We don't want the * sequencer to abort the command thinking an * unsolicited reselection occurred. */ pending_scb->hscb->control |= MK_MESSAGE|DISCONNECTED; /* * The sequencer will never re-reference the * in-core SCB. To make sure we are notified * during reslection, set the MK_MESSAGE flag in * the card's copy of the SCB. */ ahd_outb(ahd, SCB_CONTROL, ahd_inb(ahd, SCB_CONTROL)|MK_MESSAGE); } /* * Clear out any entries in the QINFIFO first * so we are the next SCB for this target * to run. */ ahd_search_qinfifo(ahd, cmd->device->id, cmd->device->channel + 'A', cmd->device->lun, SCB_LIST_NULL, ROLE_INITIATOR, CAM_REQUEUE_REQ, SEARCH_COMPLETE); ahd_qinfifo_requeue_tail(ahd, pending_scb); ahd_set_scbptr(ahd, saved_scbptr); ahd_print_path(ahd, pending_scb); printf("Device is disconnected, re-queuing SCB\n"); wait = TRUE; } else { printf("%s:%d:%d:%d: Unable to deliver message\n", ahd_name(ahd), cmd->device->channel, cmd->device->id, cmd->device->lun); retval = FAILED; goto done; }no_cmd: /* * Our assumption is that if we don't have the command, no * recovery action was required, so we return success. Again, * the semantics of the mid-layer recovery engine are not * well defined, so this may change in time. */ retval = SUCCESS;done: if (paused) ahd_unpause(ahd); if (wait) { struct timer_list timer; int ret; ahd->platform_data->flags |= AHD_UP_EH_SEMAPHORE;#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) ahd_unlock(ahd, &s);#else spin_unlock_irq(ahd->platform_data->host->host_lock);#endif init_timer(&timer); timer.data = (u_long)ahd; timer.expires = jiffies + (5 * HZ); timer.function = ahd_linux_sem_timeout; add_timer(&timer); printf("Recovery code sleeping\n"); down(&ahd->platform_data->eh_sem); printf("Recovery code awake\n"); ret = del_timer(&timer); if (ret == 0) { printf("Timer Expired\n"); retval = FAILED; }#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) ahd_lock(ahd, &s);#else spin_lock_irq(ahd->platform_data->host->host_lock);#endif } acmd = TAILQ_FIRST(&ahd->platform_data->completeq); TAILQ_INIT(&ahd->platform_data->completeq); ahd_midlayer_entrypoint_unlock(ahd, &s); if (acmd != NULL) { acmd = ahd_linux_run_complete_queue(ahd, acmd); if (acmd != NULL) { ahd_midlayer_entrypoint_lock(ahd, &s); ahd_schedule_completeq(ahd, acmd); ahd_midlayer_entrypoint_unlock(ahd, &s); } } ahd_schedule_runq(ahd);#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) spin_lock_irq(&io_request_lock);#endif return (retval);}static voidahd_linux_dev_reset_complete(Scsi_Cmnd *cmd){ free(cmd, M_DEVBUF);}/* * Attempt to send a target reset message to the device that timed out. */static intahd_linux_dev_reset(Scsi_Cmnd *cmd){ struct ahd_softc *ahd; struct scsi_cmnd *recovery_cmd; struct ahd_linux_device *dev; struct ahd_initiator_tinfo *tinfo; struct ahd_tmode_tstate *tstate; struct scb *scb; struct hardware_scb *hscb; struct ahd_cmd *acmd; u_long s; struct timer_list timer; int retval;#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) spin_unlock_irq(&io_request_lock);#endif ahd = *(struct ahd_softc **)cmd->device->host->hostdata; recovery_cmd = malloc(sizeof(struct scsi_cmnd), M_DEVBUF, M_WAITOK); memset(recovery_cmd, 0, sizeof(struct scsi_cmnd)); recovery_cmd->device = cmd->device; recovery_cmd->scsi_done = ahd_linux_dev_reset_complete;#if AHD_DEBUG if ((ahd_debug & AHD_SHOW_RECOVERY) != 0) printf("%s:%d:%d:%d: Device reset called for cmd %p\n", ahd_name(ahd), cmd->device->channel, cmd->device->id, cmd->device->lun, cmd);#endif ahd_midlayer_entrypoint_lock(ahd, &s); dev = ahd_linux_get_device(ahd, cmd->device->channel, cmd->device->id, cmd->device->lun, /*alloc*/FALSE); if (dev == NULL) { ahd_midlayer_entrypoint_unlock(ahd, &s); return (FAILED); } if ((scb = ahd_get_scb(ahd, AHD_NEVER_COL_IDX)) == NULL) { ahd_midlayer_entrypoint_unlock(ahd, &s); return (FAILED); } tinfo = ahd_fetch_transinfo(ahd, 'A', ahd->our_id, cmd->device->id, &tstate); recovery_cmd->result = CAM_REQ_INPROG << 16; recovery_cmd->host_scribble = (char *)scb; scb->io_ctx = recovery_cmd; scb->platform_data->dev = dev; scb->sg_count = 0; ahd_set_residual(scb, 0); ahd_set_sense_residual(scb, 0); hscb = scb->hscb; hscb->control = 0; hscb->scsiid = BUILD_SCSIID(ahd, cmd); hscb->lun = cmd->lun; hscb->cdb_len = 0; hscb->task_management = SIU_TASKMGMT_LUN_RESET; scb->flags |= SCB_DEVICE_RESET|SCB_RECOVERY_SCB|SCB_ACTIVE; if ((tinfo->curr.ppr_options & MSG_EXT_PPR_IU_REQ) != 0) { scb->flags |= SCB_PACKETIZED; } else { hscb->control |= MK_MESSAGE; } dev->openings--; dev->active++; dev->commands_issued++; LIST_INSERT_HEAD(&ahd->pending_scbs, scb, pending_links); ahd_queue_scb(ahd, scb); ahd->platform_data->flags |= AHD_UP_EH_SEMAPHORE;#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) ahd_unlock(ahd, &s);#else spin_unlock_irq(ahd->platform_data->host->host_lock);#endif init_timer(&timer); timer.data = (u_long)ahd; timer.expires = jiffies + (5 * HZ); timer.function = ahd_linux_sem_timeout; add_timer(&timer); printf("Recovery code sleeping\n"); down(&ahd->platform_data->eh_sem); printf("Recovery code awake\n"); retval = SUCCESS; if (del_timer(&timer) == 0) { printf("Timer Expired\n"); retval = FAILED; }#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) ahd_lock(ahd, &s);#else spin_lock_irq(ahd->platform_data->host->host_lock);#endif acmd = TAILQ_FIRST(&ahd->platform_data->completeq); TAILQ_INIT(&ahd->platform_data->completeq); ahd_midlayer_entrypoint_unlock(ahd, &s); if (acmd != NULL) { acmd = ahd_linux_run_complete_queue(ahd, acmd); if (acmd != NULL) { ahd_midlayer_entrypoint_lock(ahd, &s); ahd_schedule_completeq(ahd, acmd); ahd_midlayer_entrypoint_unlock(ahd, &s); } } ahd_schedule_runq(ahd);#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) spin_lock_irq(&io_request_lock);#endif printf("%s: Device reset returning 0x%x\n", ahd_name(ahd), retval); return (retval);}/* * Reset the SCSI bus. */static intahd_linux_bus_reset(Scsi_Cmnd *cmd){ struct ahd_softc *ahd; struct ahd_cmd *acmd; u_long s; int found;#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) spin_unlock_irq(&io_request_lock);#endif ahd = *(struct ahd_softc **)cmd->device->host->hostdata;#ifdef AHD_DEBUG if ((ahd_debug & AHD_SHOW_RECOVERY) != 0) printf("%s: Bus reset called for cmd %p\n", ahd_name(ahd), cmd);#endif ahd_midlayer_entrypoint_lock(ahd, &s); found = ahd_reset_channel(ahd, cmd->channel + 'A', /*initiate reset*/TRUE); acmd = TAILQ_FIRST(&ahd->platform_data->completeq); TAILQ_INIT(&ahd->platform_data->completeq); ahd_midlayer_entrypoint_unlock(ahd, &s); if (bootverbose) printf("%s: SCSI bus reset delivered. " "%d SCBs aborted.\n", ahd_name(ahd), found); if (acmd != NULL) { acmd = ahd_linux_run_complete_queue(ahd, acmd); if (acmd != NULL) { ahd_midlayer_entrypoint_lock(ahd, &s); ahd_schedule_completeq(ahd, acmd); ahd_midlayer_entrypoint_unlock(ahd, &s); } }#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) spin_lock_irq(&io_request_lock);#endif return (SUCCESS);}Scsi_Host_Template aic79xx_driver_template = { .proc_info = ahd_linux_proc_info, .detect = ahd_linux_detect, .release = ahd_linux_release, .info = ahd_linux_info, .queuecommand = ahd_linux_queue, .eh_abort_handler = ahd_linux_abort, .eh_device_reset_handler = ahd_linux_dev_reset, .eh_bus_reset_handler = ahd_linux_bus_reset,#if defined(__i386__) .bios_param = ahd_linux_biosparam,#endif .can_queue = AHD_MAX_QUEUE, .this_id = -1, .sg_tablesize = AHD_NSEG, .cmd_per_lun = 2, .use_clustering = ENABLE_CLUSTERING,#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,7) /* * We can only map 16MB per-SG * so create a sector limit of * "16MB" in 2K sectors. */ .max_sectors = 8192,#endif#if defined CONFIG_HIGHIO || LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,18)/* Assume RedHat Distribution with its different HIGHIO conventions. */ .can_dma_32 = 1, .single_sg_okay = 1,#else .highmem_io = 1,#endif#endif#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) .name = "aic79xx", .slave_alloc = ahd_linux_slave_alloc, .slave_configure = ahd_linux_slave_configure, .slave_destroy = ahd_linux_slave_destroy,#else .select_queue_depths = ahd_linux_select_queue_depth, .use_new_eh_code = 1,#endif};#define driver_template aic79xx_driver_template#include "scsi_module.c"/**************************** 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){#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) struct ahd_softc *ahd; /* * In 2.5.X, this is called prior to the filesystems * being synced and the SCSI layer being properly * shutdown. A different API is required there, * but the device hooks for this don't quite look * right. */ if (event == SYS_DOWN || event == SYS_HALT) { TAILQ_FOREACH(ahd, &ahd_tailq, links) { ahd_shutdown(ahd); } }#endif return (NOTIFY_OK);}/******************************** 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}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -