📄 sym_glue.c
字号:
drv_status = 0; cam_status = DID_OK; scsi_status = cp->ssss_status; if (cp->host_flags & HF_SENSE) { scsi_status = cp->sv_scsi_status; resid = cp->sv_resid; if (sym_verbose && cp->sv_xerr_status) sym_print_xerr(cp, cp->sv_xerr_status); if (cp->host_status == HS_COMPLETE && cp->ssss_status == S_GOOD && cp->xerr_status == 0) { cam_status = sym_xerr_cam_status(DID_OK, cp->sv_xerr_status); drv_status = DRIVER_SENSE; /* * Bounce back the sense data to user. */ bzero(&csio->sense_buffer, sizeof(csio->sense_buffer)); bcopy(cp->sns_bbuf, csio->sense_buffer, MIN(sizeof(csio->sense_buffer),SYM_SNS_BBUF_LEN));#if 0 /* * If the device reports a UNIT ATTENTION condition * due to a RESET condition, we should consider all * disconnect CCBs for this unit as aborted. */ if (1) { u_char *p; p = (u_char *) csio->sense_data; if (p[0]==0x70 && p[2]==0x6 && p[12]==0x29) sym_clear_tasks(np, DID_ABORT, cp->target,cp->lun, -1); }#endif } else cam_status = DID_ERROR; } else if (cp->host_status == HS_COMPLETE) /* Bad SCSI status */ cam_status = DID_OK; else if (cp->host_status == HS_SEL_TIMEOUT) /* Selection timeout */ cam_status = DID_NO_CONNECT; else if (cp->host_status == HS_UNEXPECTED) /* Unexpected BUS FREE*/ cam_status = DID_ERROR; else { /* Extended error */ if (sym_verbose) { PRINT_ADDR(cp); printf ("COMMAND FAILED (%x %x %x).\n", cp->host_status, cp->ssss_status, cp->xerr_status); } /* * Set the most appropriate value for CAM status. */ cam_status = sym_xerr_cam_status(DID_ERROR, cp->xerr_status); }#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,99) csio->resid = resid;#endif csio->result = (drv_status << 24) + (cam_status << 16) + scsi_status;}/* * Called on successfull INQUIRY response. */void sym_sniff_inquiry(hcb_p np, Scsi_Cmnd *cmd, int resid){ int retv; if (!cmd || cmd->use_sg) return; sync_scsi_data(np, cmd); retv = __sym_sniff_inquiry(np, cmd->target, cmd->lun, (u_char *) cmd->request_buffer, cmd->request_bufflen - resid); if (retv < 0) return; else if (retv) sym_update_trans_settings(np, &np->target[cmd->target]);}/* * Build the scatter/gather array for an I/O. */static int sym_scatter_no_sglist(hcb_p np, ccb_p cp, Scsi_Cmnd *cmd){ struct sym_tblmove *data = &cp->phys.data[SYM_CONF_MAX_SG-1]; int segment; cp->data_len = cmd->request_bufflen; if (cmd->request_bufflen) { bus_addr_t baddr = map_scsi_single_data(np, cmd); if (baddr) { sym_build_sge(np, data, baddr, cmd->request_bufflen); segment = 1; } else segment = -2; } else segment = 0; return segment;}static int sym_scatter(hcb_p np, ccb_p cp, Scsi_Cmnd *cmd){ int segment; int use_sg = (int) cmd->use_sg; cp->data_len = 0; if (!use_sg) segment = sym_scatter_no_sglist(np, cp, cmd); else if (use_sg > SYM_CONF_MAX_SG) segment = -1; else if ((use_sg = map_scsi_sg_data(np, cmd)) > 0) { struct scatterlist *scatter = (struct scatterlist *)cmd->buffer; struct sym_tblmove *data; data = &cp->phys.data[SYM_CONF_MAX_SG - use_sg]; for (segment = 0; segment < use_sg; segment++) { bus_addr_t baddr = bus_sg_dma_address(&scatter[segment]); unsigned int len = bus_sg_dma_len(&scatter[segment]); sym_build_sge(np, &data[segment], baddr, len); cp->data_len += len; } } else segment = -2; return segment;}/* * Queue a SCSI command. */static int sym_queue_command(hcb_p np, Scsi_Cmnd *ccb){/* Scsi_Device *device = ccb->device; */ tcb_p tp; lcb_p lp; ccb_p cp; int order; /* * Minimal checkings, so that we will not * go outside our tables. */ if (ccb->target == np->myaddr || ccb->target >= SYM_CONF_MAX_TARGET || ccb->lun >= SYM_CONF_MAX_LUN) { sym_xpt_done2(np, ccb, CAM_DEV_NOT_THERE); return 0; } /* * Retreive the target descriptor. */ tp = &np->target[ccb->target]; /* * Complete the 1st INQUIRY command with error * condition if the device is flagged NOSCAN * at BOOT in the NVRAM. This may speed up * the boot and maintain coherency with BIOS * device numbering. Clearing the flag allows * user to rescan skipped devices later. * We also return error for devices not flagged * for SCAN LUNS in the NVRAM since some mono-lun * devices behave badly when asked for some non * zero LUN. Btw, this is an absolute hack.:-) */ if (ccb->cmnd[0] == 0x12 || ccb->cmnd[0] == 0x0) { if ((tp->usrflags & SYM_SCAN_BOOT_DISABLED) || ((tp->usrflags & SYM_SCAN_LUNS_DISABLED) && ccb->lun != 0)) { tp->usrflags &= ~SYM_SCAN_BOOT_DISABLED; sym_xpt_done2(np, ccb, CAM_DEV_NOT_THERE); return 0; } } /* * Select tagged/untagged. */ lp = sym_lp(np, tp, ccb->lun); order = (lp && lp->s.reqtags) ? M_SIMPLE_TAG : 0; /* * Queue the SCSI IO. */ cp = sym_get_ccb(np, ccb->target, ccb->lun, order); if (!cp) return 1; /* Means resource shortage */ (void) sym_queue_scsiio(np, ccb, cp); return 0;}/* * Setup buffers and pointers that address the CDB. */static int __inline sym_setup_cdb(hcb_p np, Scsi_Cmnd *ccb, ccb_p cp){ u32 cmd_ba; int cmd_len; /* * CDB is 16 bytes max. */ if (ccb->cmd_len > sizeof(cp->cdb_buf)) { sym_set_cam_status(cp->cam_ccb, CAM_REQ_INVALID); return -1; } bcopy(ccb->cmnd, cp->cdb_buf, ccb->cmd_len); cmd_ba = CCB_BA (cp, cdb_buf[0]); cmd_len = ccb->cmd_len; cp->phys.cmd.addr = cpu_to_scr(cmd_ba); cp->phys.cmd.size = cpu_to_scr(cmd_len); return 0;}/* * Setup pointers that address the data and start the I/O. */int sym_setup_data_and_start(hcb_p np, Scsi_Cmnd *csio, ccb_p cp){ int dir; tcb_p tp = &np->target[cp->target]; lcb_p lp = sym_lp(np, tp, cp->lun); /* * Build the CDB. */ if (sym_setup_cdb(np, csio, cp)) goto out_abort; /* * No direction means no data. */ dir = scsi_data_direction(csio); if (dir != SCSI_DATA_NONE) { cp->segments = sym_scatter (np, cp, csio); if (cp->segments < 0) { if (cp->segments == -2) sym_set_cam_status(csio, CAM_RESRC_UNAVAIL); else sym_set_cam_status(csio, CAM_REQ_TOO_BIG); goto out_abort; } } else { cp->data_len = 0; cp->segments = 0; } /* * Set data pointers. */ sym_setup_data_pointers(np, cp, dir); /* * When `#ifed 1', the code below makes the driver * panic on the first attempt to write to a SCSI device. * It is the first test we want to do after a driver * change that does not seem obviously safe. :) */#if 0 switch (cp->cdb_buf[0]) { case 0x0A: case 0x2A: case 0xAA: panic("XXXXXXXXXXXXX WRITE NOT YET ALLOWED XXXXXXXXXXXXXX\n"); MDELAY(10000); break; default: break; }#endif /* * activate this job. */ if (lp) sym_start_next_ccbs(np, lp, 2); else sym_put_start_queue(np, cp); return 0;out_abort: sym_free_ccb(np, cp); sym_xpt_done(np, csio); return 0;}/* * timer daemon. * * Misused to keep the driver running when * interrupts are not configured correctly. */static void sym_timer (hcb_p np){ u_long thistime = ktime_get(0);#if LINUX_VERSION_CODE < LinuxVersionCode(2, 4, 0) /* * If release process in progress, let's go * Set the release stage from 1 to 2 to synchronize * with the release process. */ if (np->s.release_stage) { if (np->s.release_stage == 1) np->s.release_stage = 2; return; }#endif /* * Restart the timer. */#ifdef SYM_CONF_PCIQ_BROKEN_INTR np->s.timer.expires = ktime_get((HZ+99)/100);#else np->s.timer.expires = ktime_get(SYM_CONF_TIMER_INTERVAL);#endif add_timer(&np->s.timer); /* * If we are resetting the ncr, wait for settle_time before * clearing it. Then command processing will be resumed. */ if (np->s.settle_time_valid) { if (ktime_dif(np->s.settle_time, thistime) <= 0){ if (sym_verbose >= 2 ) printk("%s: command processing resumed\n", sym_name(np)); np->s.settle_time_valid = 0; } return; } /* * Nothing to do for now, but that may come. */ if (np->s.lasttime + 4*HZ < thistime) { np->s.lasttime = thistime; }#ifdef SYM_CONF_PCIQ_MAY_MISS_COMPLETIONS /* * Some way-broken PCI bridges may lead to * completions being lost when the clearing * of the INTFLY flag by the CPU occurs * concurrently with the chip raising this flag. * If this ever happen, lost completions will * be reaped here. */ sym_wakeup_done(np);#endif#ifdef SYM_CONF_PCIQ_BROKEN_INTR if (INB(nc_istat) & (INTF|SIP|DIP)) { /* ** Process pending interrupts. */ if (DEBUG_FLAGS & DEBUG_TINY) printk ("{"); sym_interrupt(np); if (DEBUG_FLAGS & DEBUG_TINY) printk ("}"); }#endif /* SYM_CONF_PCIQ_BROKEN_INTR */}/* * PCI BUS error handler. */void sym_log_bus_error(hcb_p np){ u_short pci_sts; pci_read_config_word(np->s.device, PCI_STATUS, &pci_sts); if (pci_sts & 0xf900) { pci_write_config_word(np->s.device, PCI_STATUS, pci_sts); printf("%s: PCI STATUS = 0x%04x\n", sym_name(np), pci_sts & 0xf900); }}/* * Requeue awaiting commands. */static void sym_requeue_awaiting_cmds(hcb_p np){ Scsi_Cmnd *cmd; ucmd_p ucp = SYM_UCMD_PTR(cmd); SYM_QUEHEAD tmp_cmdq; int sts; sym_que_move(&np->s.wait_cmdq, &tmp_cmdq); while ((ucp = (ucmd_p) sym_remque_head(&tmp_cmdq)) != 0) { sym_insque_tail(&ucp->link_cmdq, &np->s.busy_cmdq); cmd = SYM_SCMD_PTR(ucp); sts = sym_queue_command(np, cmd); if (sts) { sym_remque(&ucp->link_cmdq); sym_insque_head(&ucp->link_cmdq, &np->s.wait_cmdq); } }}/* * Linux entry point of the queuecommand() function */int sym53c8xx_queue_command (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *)){ hcb_p np = SYM_SOFTC_PTR(cmd); ucmd_p ucp = SYM_UCMD_PTR(cmd); u_long flags; int sts = 0; cmd->scsi_done = done; cmd->host_scribble = NULL; memset(ucp, 0, sizeof(*ucp)); SYM_LOCK_HCB(np, flags); /* * Shorten our settle_time if needed for * this command not to time out. */ if (np->s.settle_time_valid && cmd->timeout_per_command) { u_long tlimit = ktime_get(cmd->timeout_per_command); tlimit = ktime_sub(tlimit, SYM_CONF_TIMER_INTERVAL*2); if (ktime_dif(np->s.settle_time, tlimit) > 0) { np->s.settle_time = tlimit; } } if (np->s.settle_time_valid || !sym_que_empty(&np->s.wait_cmdq)) { sym_insque_tail(&ucp->link_cmdq, &np->s.wait_cmdq); goto out; } sym_insque_tail(&ucp->link_cmdq, &np->s.busy_cmdq); sts = sym_queue_command(np, cmd); if (sts) { sym_remque(&ucp->link_cmdq); sym_insque_tail(&ucp->link_cmdq, &np->s.wait_cmdq); }out: SYM_UNLOCK_HCB(np, flags); return 0;}/* * Linux entry point of the interrupt handler. */static void sym53c8xx_intr(int irq, void *dev_id, struct pt_regs * regs){ unsigned long flags; unsigned long flags1; hcb_p np = (hcb_p) dev_id; if (DEBUG_FLAGS & DEBUG_TINY) printf_debug ("["); SYM_LOCK_SCSI(np, flags1); SYM_LOCK_HCB(np, flags); sym_interrupt(np); if (!sym_que_empty(&np->s.wait_cmdq) && !np->s.settle_time_valid) sym_requeue_awaiting_cmds(np); SYM_UNLOCK_HCB(np, flags); SYM_UNLOCK_SCSI(np, flags1); if (DEBUG_FLAGS & DEBUG_TINY) printf_debug ("]\n");}/* * Linux entry point of the timer handler */static void sym53c8xx_timer(unsigned long npref){ hcb_p np = (hcb_p) npref; unsigned long flags; unsigned long flags1; SYM_LOCK_SCSI(np, flags1); SYM_LOCK_HCB(np, flags); sym_timer(np); if (!sym_que_empty(&np->s.wait_cmdq) && !np->s.settle_time_valid) sym_requeue_awaiting_cmds(np); SYM_UNLOCK_HCB(np, flags); SYM_UNLOCK_SCSI(np, flags1);}/* * What the eh thread wants us to perform. */#define SYM_EH_ABORT 0#define SYM_EH_DEVICE_RESET 1#define SYM_EH_BUS_RESET 2#define SYM_EH_HOST_RESET 3/* * What we will do regarding the involved SCSI command. */#define SYM_EH_DO_IGNORE 0#define SYM_EH_DO_COMPLETE 1#define SYM_EH_DO_WAIT 2/* * Our general completion handler. */static void __sym_eh_done(Scsi_Cmnd *cmd, int timed_out){ struct sym_eh_wait *ep = SYM_UCMD_PTR(cmd)->eh_wait;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -