📄 fas216.c
字号:
* for new implementations. The correct method is to respond to an * SDTR message with a MESSAGE REJECT message if the either the * initiator or target devices does not support synchronous transfers * or does not want to negotiate for synchronous transfers at the time. * Using the correct method assures compatibility with wide data * transfers and future enhancements. * * We will always initiate a synchronous transfer negotiation request on * every INQUIRY or REQUEST SENSE message, unless the target itself has * at some point performed a synchronous transfer negotiation request, or * we have synchronous transfers disabled for this device. *//** * fas216_handlesync - Handle a synchronous transfer message * @info: state structure for interface * @msg: message from target * * Handle a synchronous transfer message from the target */static void fas216_handlesync(FAS216_Info *info, char *msg){ struct fas216_device *dev = &info->device[info->SCpnt->device->id]; enum { sync, async, none, reject } res = none;#ifdef SCSI2_SYNC switch (msg[0]) { case MESSAGE_REJECT: /* Synchronous transfer request failed. * Note: SCSI II r10: * * SCSI devices that are capable of synchronous * data transfers shall not respond to an SDTR * message with a MESSAGE REJECT message. * * Hence, if we get this condition, we disable * negotiation for this device. */ if (dev->sync_state == neg_inprogress) { dev->sync_state = neg_invalid; res = async; } break; case EXTENDED_MESSAGE: switch (dev->sync_state) { /* We don't accept synchronous transfer requests. * Respond with a MESSAGE_REJECT to prevent a * synchronous transfer agreement from being reached. */ case neg_invalid: res = reject; break; /* We were not negotiating a synchronous transfer, * but the device sent us a negotiation request. * Honour the request by sending back a SDTR * message containing our capability, limited by * the targets capability. */ default: fas216_cmd(info, CMD_SETATN); if (msg[4] > info->ifcfg.sync_max_depth) msg[4] = info->ifcfg.sync_max_depth; if (msg[3] < 1000 / info->ifcfg.clockrate) msg[3] = 1000 / info->ifcfg.clockrate; msgqueue_flush(&info->scsi.msgs); msgqueue_addmsg(&info->scsi.msgs, 5, EXTENDED_MESSAGE, 3, EXTENDED_SDTR, msg[3], msg[4]); info->scsi.phase = PHASE_MSGOUT_EXPECT; /* This is wrong. The agreement is not in effect * until this message is accepted by the device */ dev->sync_state = neg_targcomplete; res = sync; break; /* We initiated the synchronous transfer negotiation, * and have successfully received a response from the * target. The synchronous transfer agreement has been * reached. Note: if the values returned are out of our * bounds, we must reject the message. */ case neg_inprogress: res = reject; if (msg[4] <= info->ifcfg.sync_max_depth && msg[3] >= 1000 / info->ifcfg.clockrate) { dev->sync_state = neg_complete; res = sync; } break; } }#else res = reject;#endif switch (res) { case sync: dev->period = msg[3]; dev->sof = msg[4]; dev->stp = fas216_syncperiod(info, msg[3] * 4); fas216_set_sync(info, info->SCpnt->device->id); break; case reject: fas216_cmd(info, CMD_SETATN); msgqueue_flush(&info->scsi.msgs); msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT); info->scsi.phase = PHASE_MSGOUT_EXPECT; case async: dev->period = info->ifcfg.asyncperiod / 4; dev->sof = 0; dev->stp = info->scsi.async_stp; fas216_set_sync(info, info->SCpnt->device->id); break; case none: break; }}/** * fas216_updateptrs - update data pointers after transfer suspended/paused * @info: interface's local pointer to update * @bytes_transferred: number of bytes transferred * * Update data pointers after transfer suspended/paused */static void fas216_updateptrs(FAS216_Info *info, int bytes_transferred){ struct scsi_pointer *SCp = &info->scsi.SCp; fas216_checkmagic(info); BUG_ON(bytes_transferred < 0); info->SCpnt->request_bufflen -= bytes_transferred; while (bytes_transferred != 0) { if (SCp->this_residual > bytes_transferred) break; /* * We have used up this buffer. Move on to the * next buffer. */ bytes_transferred -= SCp->this_residual; if (!next_SCp(SCp) && bytes_transferred) { printk(KERN_WARNING "scsi%d.%c: out of buffers\n", info->host->host_no, '0' + info->SCpnt->device->id); return; } } SCp->this_residual -= bytes_transferred; if (SCp->this_residual) SCp->ptr += bytes_transferred; else SCp->ptr = NULL;}/** * fas216_pio - transfer data off of/on to card using programmed IO * @info: interface to transfer data to/from * @direction: direction to transfer data (DMA_OUT/DMA_IN) * * Transfer data off of/on to card using programmed IO. * Notes: this is incredibly slow. */static void fas216_pio(FAS216_Info *info, fasdmadir_t direction){ struct scsi_pointer *SCp = &info->scsi.SCp; fas216_checkmagic(info); if (direction == DMA_OUT) fas216_writeb(info, REG_FF, get_next_SCp_byte(SCp)); else put_next_SCp_byte(SCp, fas216_readb(info, REG_FF)); if (SCp->this_residual == 0) next_SCp(SCp);}static void fas216_set_stc(FAS216_Info *info, unsigned int length){ fas216_writeb(info, REG_STCL, length); fas216_writeb(info, REG_STCM, length >> 8); fas216_writeb(info, REG_STCH, length >> 16);}static unsigned int fas216_get_ctc(FAS216_Info *info){ return fas216_readb(info, REG_CTCL) + (fas216_readb(info, REG_CTCM) << 8) + (fas216_readb(info, REG_CTCH) << 16);}/** * fas216_cleanuptransfer - clean up after a transfer has completed. * @info: interface to clean up * * Update the data pointers according to the number of bytes transferred * on the SCSI bus. */static void fas216_cleanuptransfer(FAS216_Info *info){ unsigned long total, residual, fifo; fasdmatype_t dmatype = info->dma.transfer_type; info->dma.transfer_type = fasdma_none; /* * PIO transfers do not need to be cleaned up. */ if (dmatype == fasdma_pio || dmatype == fasdma_none) return; if (dmatype == fasdma_real_all) total = info->SCpnt->request_bufflen; else total = info->scsi.SCp.this_residual; residual = fas216_get_ctc(info); fifo = fas216_readb(info, REG_CFIS) & CFIS_CF; fas216_log(info, LOG_BUFFER, "cleaning up from previous " "transfer: length 0x%06x, residual 0x%x, fifo %d", total, residual, fifo); /* * If we were performing Data-Out, the transfer counter * counts down each time a byte is transferred by the * host to the FIFO. This means we must include the * bytes left in the FIFO from the transfer counter. */ if (info->scsi.phase == PHASE_DATAOUT) residual += fifo; fas216_updateptrs(info, total - residual);}/** * fas216_transfer - Perform a DMA/PIO transfer off of/on to card * @info: interface from which device disconnected from * * Start a DMA/PIO transfer off of/on to card */static void fas216_transfer(FAS216_Info *info){ fasdmadir_t direction; fasdmatype_t dmatype; fas216_log(info, LOG_BUFFER, "starttransfer: buffer %p length 0x%06x reqlen 0x%06x", info->scsi.SCp.ptr, info->scsi.SCp.this_residual, info->SCpnt->request_bufflen); if (!info->scsi.SCp.ptr) { fas216_log(info, LOG_ERROR, "null buffer passed to " "fas216_starttransfer"); print_SCp(&info->scsi.SCp, "SCp: ", "\n"); print_SCp(&info->SCpnt->SCp, "Cmnd SCp: ", "\n"); return; } /* * If we have a synchronous transfer agreement in effect, we must * use DMA mode. If we are using asynchronous transfers, we may * use DMA mode or PIO mode. */ if (info->device[info->SCpnt->device->id].sof) dmatype = fasdma_real_all; else dmatype = fasdma_pio; if (info->scsi.phase == PHASE_DATAOUT) direction = DMA_OUT; else direction = DMA_IN; if (info->dma.setup) dmatype = info->dma.setup(info->host, &info->scsi.SCp, direction, dmatype); info->dma.transfer_type = dmatype; if (dmatype == fasdma_real_all) fas216_set_stc(info, info->SCpnt->request_bufflen); else fas216_set_stc(info, info->scsi.SCp.this_residual); switch (dmatype) { case fasdma_pio: fas216_log(info, LOG_BUFFER, "PIO transfer"); fas216_writeb(info, REG_SOF, 0); fas216_writeb(info, REG_STP, info->scsi.async_stp); fas216_cmd(info, CMD_TRANSFERINFO); fas216_pio(info, direction); break; case fasdma_pseudo: fas216_log(info, LOG_BUFFER, "pseudo transfer"); fas216_cmd(info, CMD_TRANSFERINFO | CMD_WITHDMA); info->dma.pseudo(info->host, &info->scsi.SCp, direction, info->SCpnt->transfersize); break; case fasdma_real_block: fas216_log(info, LOG_BUFFER, "block dma transfer"); fas216_cmd(info, CMD_TRANSFERINFO | CMD_WITHDMA); break; case fasdma_real_all: fas216_log(info, LOG_BUFFER, "total dma transfer"); fas216_cmd(info, CMD_TRANSFERINFO | CMD_WITHDMA); break; default: fas216_log(info, LOG_BUFFER | LOG_ERROR, "invalid FAS216 DMA type"); break; }}/** * fas216_stoptransfer - Stop a DMA transfer onto / off of the card * @info: interface from which device disconnected from * * Called when we switch away from DATA IN or DATA OUT phases. */static void fas216_stoptransfer(FAS216_Info *info){ fas216_checkmagic(info); if (info->dma.transfer_type == fasdma_real_all || info->dma.transfer_type == fasdma_real_block) info->dma.stop(info->host, &info->scsi.SCp); fas216_cleanuptransfer(info); if (info->scsi.phase == PHASE_DATAIN) { unsigned int fifo; /* * If we were performing Data-In, then the FIFO counter * contains the number of bytes not transferred via DMA * from the on-board FIFO. Read them manually. */ fifo = fas216_readb(info, REG_CFIS) & CFIS_CF; while (fifo && info->scsi.SCp.ptr) { *info->scsi.SCp.ptr = fas216_readb(info, REG_FF); fas216_updateptrs(info, 1); fifo--; } } else { /* * After a Data-Out phase, there may be unsent * bytes left in the FIFO. Flush them out. */ fas216_cmd(info, CMD_FLUSHFIFO); }}static void fas216_aborttransfer(FAS216_Info *info){ fas216_checkmagic(info); if (info->dma.transfer_type == fasdma_real_all || info->dma.transfer_type == fasdma_real_block) info->dma.stop(info->host, &info->scsi.SCp); info->dma.transfer_type = fasdma_none; fas216_cmd(info, CMD_FLUSHFIFO);}static void fas216_kick(FAS216_Info *info);/** * fas216_disconnected_intr - handle device disconnection * @info: interface from which device disconnected from * * Handle device disconnection */static void fas216_disconnect_intr(FAS216_Info *info){ unsigned long flags; fas216_checkmagic(info); fas216_log(info, LOG_CONNECT, "disconnect phase=%02x", info->scsi.phase); msgqueue_flush(&info->scsi.msgs); switch (info->scsi.phase) { case PHASE_SELECTION: /* while selecting - no target */ case PHASE_SELSTEPS: fas216_done(info, DID_NO_CONNECT); break; case PHASE_MSGIN_DISCONNECT: /* message in - disconnecting */ info->scsi.disconnectable = 1; info->scsi.phase = PHASE_IDLE; info->stats.disconnects += 1; spin_lock_irqsave(&info->host_lock, flags); if (info->scsi.phase == PHASE_IDLE) fas216_kick(info); spin_unlock_irqrestore(&info->host_lock, flags); break; case PHASE_DONE: /* at end of command - complete */ fas216_done(info, DID_OK); break; case PHASE_MSGOUT: /* message out - possible ABORT message */ if (fas216_get_last_msg(info, info->scsi.msgin_fifo) == ABORT) { info->scsi.aborting = 0; fas216_done(info, DID_ABORT); break; } default: /* huh? */ printk(KERN_ERR "scsi%d.%c: unexpected disconnect in phase %s\n", info->host->host_no, fas216_target(info), fas216_drv_phase(info)); print_debug_list(); fas216_stoptransfer(info); fas216_done(info, DID_ERROR); break; }}/** * fas216_reselected_intr - start reconnection of a device * @info: interface which was reselected * * Start reconnection of a device */static voidfas216_reselected_intr(FAS216_Info *info){ unsigned int cfis, i; unsigned char msg[4]; unsigned char target, lun, tag; fas216_checkmagic(info); WARN_ON(info->scsi.phase == PHASE_SELECTION || info->scsi.phase == PHASE_SELSTEPS); cfis = fas216_readb(info, REG_CFIS); fas216_log(info, LOG_CONNECT, "reconnect phase=%02x cfis=%02x", info->scsi.phase, cfis); cfis &= CFIS_CF; if (cfis < 2 || cfis > 4) { printk(KERN_ERR "scsi%d.H: incorrect number of bytes after reselect\n", info->host->host_no); goto bad_message; } for (i = 0; i < cfis; i++) msg[i] = fas216_readb(info, REG_FF); if (!(msg[0] & (1 << info->host->this_id)) || !(msg[1] & 0x80)) goto initiator_error; target = msg[0] & ~(1 << info->host->this_id); target = ffs(target) - 1; lun = msg[1] & 7; tag = 0; if (cfis >= 3) { if (msg[2] != SIMPLE_QUEUE_TAG) goto initiator_error; tag = msg[3]; } /* set up for synchronous transfers */ fas216_writeb(info, REG_SDID, target); fas216_set_sync(info, target); msgqueue_flush(&info->scsi.msgs); fas216_log(info, LOG_CONNECT, "Reconnected: target %1x lun %1x tag %02x", target, lun, tag); if (info->scsi.disconnectable && info->SCpnt) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -