📄 aic7xxx_linux.c
字号:
{ u_int target_offset; target_offset = targ->target; if (targ->channel != 0) target_offset += 8; ahc->platform_data->targets[target_offset] = NULL; free(targ, M_DEVBUF);}static struct ahc_linux_device*ahc_linux_alloc_device(struct ahc_softc *ahc, struct ahc_linux_target *targ, u_int lun){ struct ahc_linux_device *dev; dev = malloc(sizeof(*dev), M_DEVBUG, M_NOWAIT); if (dev == NULL) return (NULL); memset(dev, 0, sizeof(*dev)); TAILQ_INIT(&dev->busyq); dev->flags = AHC_DEV_UNCONFIGURED; dev->lun = lun; dev->target = targ; /* * We start out life using untagged * transactions of which we allow one. */ dev->openings = 1; /* * Set maxtags to 0. This will be changed if we * later determine that we are dealing with * a tagged queuing capable device. */ dev->maxtags = 0; targ->refcount++; targ->devices[lun] = dev; return (dev);}static voidahc_linux_free_device(struct ahc_softc *ahc, struct ahc_linux_device *dev){ struct ahc_linux_target *targ; targ = dev->target; targ->devices[dev->lun] = NULL; free(dev, M_DEVBUF); targ->refcount--; if (targ->refcount == 0) ahc_linux_free_target(ahc, targ);}/* * Return a string describing the driver. */const char *ahc_linux_info(struct Scsi_Host *host){ static char buffer[512]; char ahc_info[256]; char *bp; struct ahc_softc *ahc; bp = &buffer[0]; ahc = *(struct ahc_softc **)host->hostdata; memset(bp, 0, sizeof(buffer)); strcpy(bp, "Adaptec AIC7XXX EISA/VLB/PCI SCSI HBA DRIVER, Rev "); strcat(bp, AIC7XXX_DRIVER_VERSION); strcat(bp, "\n"); strcat(bp, " <"); strcat(bp, ahc->description); strcat(bp, ">\n"); strcat(bp, " "); ahc_controller_info(ahc, ahc_info); strcat(bp, ahc_info); strcat(bp, "\n"); return (bp);}voidahc_send_async(struct ahc_softc *ahc, char channel, u_int target, u_int lun, ac_code code, void *arg){ switch (code) { case AC_TRANSFER_NEG: { char buf[80]; struct ahc_linux_target *targ; struct info_str info; struct ahc_initiator_tinfo *tinfo; struct ahc_tmode_tstate *tstate; int target_offset; info.buffer = buf; info.length = sizeof(buf); info.offset = 0; info.pos = 0; tinfo = ahc_fetch_transinfo(ahc, channel, channel == 'A' ? ahc->our_id : ahc->our_id_b, target, &tstate); /* * Don't bother reporting results while * negotiations are still pending. */ if (tinfo->curr.period != tinfo->goal.period || tinfo->curr.width != tinfo->goal.width || tinfo->curr.offset != tinfo->goal.offset || tinfo->curr.ppr_options != tinfo->goal.ppr_options) if (bootverbose == 0) break; /* * Don't bother reporting results that * are identical to those last reported. */ target_offset = target; if (channel == 'B') target_offset += 8; targ = ahc->platform_data->targets[target_offset]; if (targ != NULL && tinfo->curr.period == targ->last_tinfo.period && tinfo->curr.width == targ->last_tinfo.width && tinfo->curr.offset == targ->last_tinfo.offset && tinfo->curr.ppr_options == targ->last_tinfo.ppr_options) if (bootverbose == 0) break; targ->last_tinfo.period = tinfo->curr.period; targ->last_tinfo.width = tinfo->curr.width; targ->last_tinfo.offset = tinfo->curr.offset; targ->last_tinfo.ppr_options = tinfo->curr.ppr_options; printf("(%s:%c:", ahc_name(ahc), channel); if (target == CAM_TARGET_WILDCARD) printf("*): "); else printf("%d): ", target); ahc_format_transinfo(&info, &tinfo->curr); if (info.pos < info.length) *info.buffer = '\0'; else buf[info.length - 1] = '\0'; printf("%s", buf); break; } case AC_SENT_BDR: break; case AC_BUS_RESET:#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) if (ahc->platform_data->host != NULL) { scsi_report_bus_reset(ahc->platform_data->host, channel - 'A'); }#endif break; default: panic("ahc_send_async: Unexpected async event"); }}/* * Calls the higher level scsi done function and frees the scb. */voidahc_done(struct ahc_softc *ahc, struct scb * scb){ Scsi_Cmnd *cmd; struct ahc_linux_device *dev; LIST_REMOVE(scb, pending_links); if ((scb->flags & SCB_UNTAGGEDQ) != 0) { struct scb_tailq *untagged_q; int target_offset; target_offset = SCB_GET_TARGET_OFFSET(ahc, scb); untagged_q = &(ahc->untagged_queues[target_offset]); TAILQ_REMOVE(untagged_q, scb, links.tqe); ahc_run_untagged_queue(ahc, untagged_q); } if ((scb->flags & SCB_ACTIVE) == 0) { printf("SCB %d done'd twice\n", scb->hscb->tag); ahc_dump_card_state(ahc); panic("Stopping for safety"); } cmd = scb->io_ctx; dev = scb->platform_data->dev; dev->active--; dev->openings++; ahc_linux_unmap_scb(ahc, scb); if (scb->flags & SCB_SENSE) { memcpy(cmd->sense_buffer, ahc_get_sense_buf(ahc, scb), MIN(sizeof(struct scsi_sense_data), sizeof(cmd->sense_buffer))); cmd->result |= (DRIVER_SENSE << 24); } else { /* * Guard against stale sense data. * The Linux mid-layer assumes that sense * was retrieved anytime the first byte of * the sense buffer looks "sane". */ cmd->sense_buffer[0] = 0; } if (ahc_get_transaction_status(scb) == CAM_REQ_INPROG) { uint32_t amount_xferred; amount_xferred = ahc_get_transfer_length(scb) - ahc_get_residual(scb); if (amount_xferred < scb->io_ctx->underflow) { printf("Saw underflow (%ld of %ld bytes). " "Treated as error\n", ahc_get_residual(scb), ahc_get_transfer_length(scb)); ahc_set_transaction_status(scb, CAM_DATA_RUN_ERR); } else { ahc_set_transaction_status(scb, CAM_REQ_CMP); ahc_linux_sniff_command(ahc, cmd, scb); } } else if (ahc_get_transaction_status(scb) == DID_OK) { ahc_linux_handle_scsi_status(ahc, dev, scb); } else if (ahc_get_transaction_status(scb) == DID_NO_CONNECT) { /* * Should a selection timeout kill the device? * That depends on whether the selection timeout * is persistent. Since we have no guarantee that * the mid-layer will issue an inquiry for this device * again, we can't just kill it off. dev->flags |= AHC_DEV_UNCONFIGURED; */ } if (dev->openings == 1 && ahc_get_transaction_status(scb) == CAM_REQ_CMP && ahc_get_scsi_status(scb) != SCSI_STATUS_QUEUE_FULL) dev->tag_success_count++; /* * Some devices deal with temporary internal resource * shortages by returning queue full. When the queue * full occurrs, we throttle back. Slowly try to get * back to our previous queue depth. */ if ((dev->openings + dev->active) < dev->maxtags && dev->tag_success_count > AHC_TAG_SUCCESS_INTERVAL) { dev->tag_success_count = 0; dev->openings++; } if (dev->active == 0) dev->commands_since_idle_or_otag = 0; if (TAILQ_EMPTY(&dev->busyq)) { if ((dev->flags & AHC_DEV_UNCONFIGURED) != 0 && dev->active == 0) ahc_linux_free_device(ahc, dev); } else if ((dev->flags & AHC_DEV_ON_RUN_LIST) == 0) { TAILQ_INSERT_TAIL(&ahc->platform_data->device_runq, dev, links); dev->flags |= AHC_DEV_ON_RUN_LIST; } if ((scb->flags & SCB_RECOVERY_SCB) != 0) { printf("Recovery SCB completes\n"); up(&ahc->platform_data->eh_sem); } ahc_free_scb(ahc, scb); ahc_linux_queue_cmd_complete(ahc, cmd);}static voidahc_linux_handle_scsi_status(struct ahc_softc *ahc, struct ahc_linux_device *dev, struct scb *scb){ /* * We don't currently trust the mid-layer to * properly deal with queue full or busy. So, * when one occurs, we tell the mid-layer to * unconditionally requeue the command to us * so that we can retry it ourselves. We also * implement our own throttling mechanism so * we don't clobber the device with too many * commands. */ switch (ahc_get_scsi_status(scb)) { default: break; case SCSI_STATUS_QUEUE_FULL: { /* * By the time the core driver has returned this * command, all other commands that were queued * to us but not the device have been returned. * This ensures that dev->active is equal to * the number of commands actually queued to * the device. */ dev->tag_success_count = 0; if (dev->active != 0) { /* * Drop our opening count to the number * of commands currently outstanding. */ dev->openings = 0;/* ahc_print_path(ahc, scb); printf("Dropping tag count to %d\n", dev->active); */ if (dev->active == dev->tags_on_last_queuefull) { dev->last_queuefull_same_count++; /* * If we repeatedly see a queue full * at the same queue depth, this * device has a fixed number of tag * slots. Lock in this tag depth * so we stop seeing queue fulls from * this device. */ if (dev->last_queuefull_same_count == AHC_LOCK_TAGS_COUNT) { dev->maxtags = dev->active; ahc_print_path(ahc, scb); printf("Locking max tag count at %d\n", dev->active); } } else { dev->tags_on_last_queuefull = dev->active; dev->last_queuefull_same_count = 0; } ahc_set_transaction_status(scb, CAM_REQUEUE_REQ); ahc_set_scsi_status(scb, SCSI_STATUS_OK); break; } /* * Drop down to a single opening, and treat this * as if the target return BUSY SCSI status. */ dev->openings = 1; /* FALLTHROUGH */ } case SCSI_STATUS_BUSY: /* * XXX Set a timer and handle ourselves???? * For now we pray that the mid-layer does something * sane for devices that are busy. */ ahc_set_scsi_status(scb, SCSI_STATUS_BUSY); break; }}static voidahc_linux_filter_command(struct ahc_softc *ahc, Scsi_Cmnd *cmd, struct scb *scb){ switch (cmd->cmnd[0]) { case INQUIRY: { struct ahc_devinfo devinfo; struct scsi_inquiry *inq; struct scsi_inquiry_data *sid; struct ahc_initiator_tinfo *targ_info; struct ahc_tmode_tstate *tstate; struct ahc_syncrate *syncrate; struct ahc_linux_device *dev; u_int scsiid; u_int maxsync; int transferred_len; int minlen; u_int width; u_int period; u_int offset; u_int ppr_options; /* * Validate the command. We only want to filter * standard inquiry commands, not those querying * Vital Product Data. */ inq = (struct scsi_inquiry *)cmd->cmnd; if ((inq->byte2 & SI_EVPD) != 0 || inq->page_code != 0) break; if (cmd->use_sg != 0) { printf("%s: SG Inquiry response ignored\n", ahc_name(ahc)); break; } transferred_len = ahc_get_transfer_length(scb) - ahc_get_residual(scb); sid = (struct scsi_inquiry_data *)cmd->request_buffer; /* * Determine if this lun actually exists. If so, * hold on to its corresponding device structure. * If not, make sure we release the device and * don't bother processing the rest of this inquiry * command. */ dev = ahc_linux_get_device(ahc, cmd->channel, cmd->target, cmd->lun, /*alloc*/FALSE); if (transferred_len >= 1 && SID_QUAL(sid) == SID_QUAL_LU_CONNECTED) { dev->flags &= ~AHC_DEV_UNCONFIGURED; } else { dev->flags |= AHC_DEV_UNCONFIGURED; break; } /* * Update our notion of this device's transfer * negotiation capabilities. */ scsiid = BUILD_SCSIID(ahc, cmd); ahc_compile_devinfo(&devinfo, SCSIID_OUR_ID(scsiid), cmd->target, cmd->lun, SCSIID_CHANNEL(ahc, scsiid), ROLE_INITIATOR); targ_info = ahc_fetch_transinfo(ahc, devinfo.channel, devinfo.our_scsiid, devinfo.target, &tstate); width = targ_info->user.width; period = targ_info->user.period; offset = targ_info->user.offset; ppr_options = targ_info->user.ppr_options; minlen = offsetof(struct scsi_inquiry_data, version) + 1; if (transferred_len >= minlen) { targ_info->curr.protocol_version = SID_ANSI_REV(sid); /* * Only attempt SPI3 once we've verified that * the device claims to support SPI3 features. */ if (targ_info->curr.protocol_version < SCSI_REV_2) targ_info->curr.transport_version = SID_ANSI_REV(sid); else targ_info->curr.transport_version = SCSI_REV_2; } minlen = offsetof(struct scsi_inquiry
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -