📄 acornscsi.c
字号:
host->host->host_no, acornscsi_target(host)); return 0; } residual = host->SCpnt->request_bufflen - host->scsi.SCp.scsi_xferred; sbic_arm_write(host->scsi.io_port, SBIC_SYNCHTRANSFER, host->device[host->SCpnt->device->id].sync_xfer); sbic_arm_writenext(host->scsi.io_port, residual >> 16); sbic_arm_writenext(host->scsi.io_port, residual >> 8); sbic_arm_writenext(host->scsi.io_port, residual); acornscsi_sbic_issuecmd(host, CMND_XFERINFO); return 1;}/* ========================================================================================= * Connection & Disconnection *//* * Function : acornscsi_reconnect(AS_Host *host) * Purpose : reconnect a previously disconnected command * Params : host - host specific data * Remarks : SCSI spec says: * 'The set of active pointers is restored from the set * of saved pointers upon reconnection of the I/O process' */staticint acornscsi_reconnect(AS_Host *host){ unsigned int target, lun, ok = 0; target = sbic_arm_read(host->scsi.io_port, SBIC_SOURCEID); if (!(target & 8)) printk(KERN_ERR "scsi%d: invalid source id after reselection " "- device fault?\n", host->host->host_no); target &= 7; if (host->SCpnt && !host->scsi.disconnectable) { printk(KERN_ERR "scsi%d.%d: reconnected while command in " "progress to target %d?\n", host->host->host_no, target, host->SCpnt->device->id); host->SCpnt = NULL; } lun = sbic_arm_read(host->scsi.io_port, SBIC_DATA) & 7; host->scsi.reconnected.target = target; host->scsi.reconnected.lun = lun; host->scsi.reconnected.tag = 0; if (host->scsi.disconnectable && host->SCpnt && host->SCpnt->device->id == target && host->SCpnt->device->lun == lun) ok = 1; if (!ok && queue_probetgtlun(&host->queues.disconnected, target, lun)) ok = 1; ADD_STATUS(target, 0x81, host->scsi.phase, 0); if (ok) { host->scsi.phase = PHASE_RECONNECTED; } else { /* this doesn't seem to work */ printk(KERN_ERR "scsi%d.%c: reselected with no command " "to reconnect with\n", host->host->host_no, '0' + target); acornscsi_dumplog(host, target); acornscsi_abortcmd(host, 0); if (host->SCpnt) { queue_add_cmd_tail(&host->queues.disconnected, host->SCpnt); host->SCpnt = NULL; } } acornscsi_sbic_issuecmd(host, CMND_NEGATEACK); return !ok;}/* * Function: int acornscsi_reconect_finish(AS_Host *host) * Purpose : finish reconnecting a command * Params : host - host to complete * Returns : 0 if failed */staticint acornscsi_reconnect_finish(AS_Host *host){ if (host->scsi.disconnectable && host->SCpnt) { host->scsi.disconnectable = 0; if (host->SCpnt->device->id == host->scsi.reconnected.target && host->SCpnt->device->lun == host->scsi.reconnected.lun && host->SCpnt->tag == host->scsi.reconnected.tag) {#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON)) DBG(host->SCpnt, printk("scsi%d.%c: reconnected", host->host->host_no, acornscsi_target(host)));#endif } else { queue_add_cmd_tail(&host->queues.disconnected, host->SCpnt);#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON)) DBG(host->SCpnt, printk("scsi%d.%c: had to move command " "to disconnected queue\n", host->host->host_no, acornscsi_target(host)));#endif host->SCpnt = NULL; } } if (!host->SCpnt) { host->SCpnt = queue_remove_tgtluntag(&host->queues.disconnected, host->scsi.reconnected.target, host->scsi.reconnected.lun, host->scsi.reconnected.tag);#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON)) DBG(host->SCpnt, printk("scsi%d.%c: had to get command", host->host->host_no, acornscsi_target(host)));#endif } if (!host->SCpnt) acornscsi_abortcmd(host, host->scsi.reconnected.tag); else { /* * Restore data pointer from SAVED pointers. */ host->scsi.SCp = host->SCpnt->SCp;#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON)) printk(", data pointers: [%p, %X]", host->scsi.SCp.ptr, host->scsi.SCp.this_residual);#endif }#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON)) printk("\n");#endif host->dma.transferred = host->scsi.SCp.scsi_xferred; return host->SCpnt != NULL;}/* * Function: void acornscsi_disconnect_unexpected(AS_Host *host) * Purpose : handle an unexpected disconnect * Params : host - host on which disconnect occurred */staticvoid acornscsi_disconnect_unexpected(AS_Host *host){ printk(KERN_ERR "scsi%d.%c: unexpected disconnect\n", host->host->host_no, acornscsi_target(host));#if (DEBUG & DEBUG_ABORT) acornscsi_dumplog(host, 8);#endif acornscsi_done(host, &host->SCpnt, DID_ERROR);}/* * Function: void acornscsi_abortcmd(AS_host *host, unsigned char tag) * Purpose : abort a currently executing command * Params : host - host with connected command to abort * tag - tag to abort */staticvoid acornscsi_abortcmd(AS_Host *host, unsigned char tag){ host->scsi.phase = PHASE_ABORTED; sbic_arm_write(host->scsi.io_port, SBIC_CMND, CMND_ASSERTATN); msgqueue_flush(&host->scsi.msgs);#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE if (tag) msgqueue_addmsg(&host->scsi.msgs, 2, ABORT_TAG, tag); else#endif msgqueue_addmsg(&host->scsi.msgs, 1, ABORT);}/* ========================================================================================== * Interrupt routines. *//* * Function: int acornscsi_sbicintr(AS_Host *host) * Purpose : handle interrupts from SCSI device * Params : host - host to process * Returns : INTR_PROCESS if expecting another SBIC interrupt * INTR_IDLE if no interrupt * INTR_NEXT_COMMAND if we have finished processing the command */staticintr_ret_t acornscsi_sbicintr(AS_Host *host, int in_irq){ unsigned int asr, ssr; asr = sbic_arm_read(host->scsi.io_port, SBIC_ASR); if (!(asr & ASR_INT)) return INTR_IDLE; ssr = sbic_arm_read(host->scsi.io_port, SBIC_SSR);#if (DEBUG & DEBUG_PHASES) print_sbic_status(asr, ssr, host->scsi.phase);#endif ADD_STATUS(8, ssr, host->scsi.phase, in_irq); if (host->SCpnt && !host->scsi.disconnectable) ADD_STATUS(host->SCpnt->device->id, ssr, host->scsi.phase, in_irq); switch (ssr) { case 0x00: /* reset state - not advanced */ printk(KERN_ERR "scsi%d: reset in standard mode but wanted advanced mode.\n", host->host->host_no); /* setup sbic - WD33C93A */ sbic_arm_write(host->scsi.io_port, SBIC_OWNID, OWNID_EAF | host->host->this_id); sbic_arm_write(host->scsi.io_port, SBIC_CMND, CMND_RESET); return INTR_IDLE; case 0x01: /* reset state - advanced */ sbic_arm_write(host->scsi.io_port, SBIC_CTRL, INIT_SBICDMA | CTRL_IDI); sbic_arm_write(host->scsi.io_port, SBIC_TIMEOUT, TIMEOUT_TIME); sbic_arm_write(host->scsi.io_port, SBIC_SYNCHTRANSFER, SYNCHTRANSFER_2DBA); sbic_arm_write(host->scsi.io_port, SBIC_SOURCEID, SOURCEID_ER | SOURCEID_DSP); msgqueue_flush(&host->scsi.msgs); return INTR_IDLE; case 0x41: /* unexpected disconnect aborted command */ acornscsi_disconnect_unexpected(host); return INTR_NEXT_COMMAND; } switch (host->scsi.phase) { case PHASE_CONNECTING: /* STATE: command removed from issue queue */ switch (ssr) { case 0x11: /* -> PHASE_CONNECTED */ /* BUS FREE -> SELECTION */ host->scsi.phase = PHASE_CONNECTED; msgqueue_flush(&host->scsi.msgs); host->dma.transferred = host->scsi.SCp.scsi_xferred; /* 33C93 gives next interrupt indicating bus phase */ asr = sbic_arm_read(host->scsi.io_port, SBIC_ASR); if (!(asr & ASR_INT)) break; ssr = sbic_arm_read(host->scsi.io_port, SBIC_SSR); ADD_STATUS(8, ssr, host->scsi.phase, 1); ADD_STATUS(host->SCpnt->device->id, ssr, host->scsi.phase, 1); goto connected; case 0x42: /* select timed out */ /* -> PHASE_IDLE */ acornscsi_done(host, &host->SCpnt, DID_NO_CONNECT); return INTR_NEXT_COMMAND; case 0x81: /* -> PHASE_RECONNECTED or PHASE_ABORTED */ /* BUS FREE -> RESELECTION */ host->origSCpnt = host->SCpnt; host->SCpnt = NULL; msgqueue_flush(&host->scsi.msgs); acornscsi_reconnect(host); break; default: printk(KERN_ERR "scsi%d.%c: PHASE_CONNECTING, SSR %02X?\n", host->host->host_no, acornscsi_target(host), ssr); acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); acornscsi_abortcmd(host, host->SCpnt->tag); } return INTR_PROCESSING; connected: case PHASE_CONNECTED: /* STATE: device selected ok */ switch (ssr) {#ifdef NONSTANDARD case 0x8a: /* -> PHASE_COMMAND, PHASE_COMMANDPAUSED */ /* SELECTION -> COMMAND */ acornscsi_sendcommand(host); break; case 0x8b: /* -> PHASE_STATUS */ /* SELECTION -> STATUS */ acornscsi_readstatusbyte(host); host->scsi.phase = PHASE_STATUSIN; break;#endif case 0x8e: /* -> PHASE_MSGOUT */ /* SELECTION ->MESSAGE OUT */ host->scsi.phase = PHASE_MSGOUT; acornscsi_buildmessages(host); acornscsi_sendmessage(host); break; /* these should not happen */ case 0x85: /* target disconnected */ acornscsi_done(host, &host->SCpnt, DID_ERROR); break; default: printk(KERN_ERR "scsi%d.%c: PHASE_CONNECTED, SSR %02X?\n", host->host->host_no, acornscsi_target(host), ssr); acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); acornscsi_abortcmd(host, host->SCpnt->tag); } return INTR_PROCESSING; case PHASE_MSGOUT: /* STATE: connected & sent IDENTIFY message */ /* * SCSI standard says that MESSAGE OUT phases can be followed by a * DATA phase, STATUS phase, MESSAGE IN phase or COMMAND phase */ switch (ssr) { case 0x8a: /* -> PHASE_COMMAND, PHASE_COMMANDPAUSED */ case 0x1a: /* -> PHASE_COMMAND, PHASE_COMMANDPAUSED */ /* MESSAGE OUT -> COMMAND */ acornscsi_sendcommand(host); break; case 0x8b: /* -> PHASE_STATUS */ case 0x1b: /* -> PHASE_STATUS */ /* MESSAGE OUT -> STATUS */ acornscsi_readstatusbyte(host); host->scsi.phase = PHASE_STATUSIN; break; case 0x8e: /* -> PHASE_MSGOUT */ /* MESSAGE_OUT(MESSAGE_IN) ->MESSAGE OUT */ acornscsi_sendmessage(host); break; case 0x4f: /* -> PHASE_MSGIN, PHASE_DISCONNECT */ case 0x1f: /* -> PHASE_MSGIN, PHASE_DISCONNECT */ /* MESSAGE OUT -> MESSAGE IN */ acornscsi_message(host); break; default: printk(KERN_ERR "scsi%d.%c: PHASE_MSGOUT, SSR %02X?\n", host->host->host_no, acornscsi_target(host), ssr); acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); } return INTR_PROCESSING; case PHASE_COMMAND: /* STATE: connected & command sent */ switch (ssr) { case 0x18: /* -> PHASE_DATAOUT */ /* COMMAND -> DATA OUT */ if (host->scsi.SCp.sent_command != host->SCpnt->cmd_len) acornscsi_abortcmd(host, host->SCpnt->tag); acornscsi_dma_setup(host, DMA_OUT); if (!acornscsi_starttransfer(host)) acornscsi_abortcmd(host, host->SCpnt->tag); host->scsi.phase = PHASE_DATAOUT; return INTR_IDLE; case 0x19: /* -> PHASE_DATAIN */ /* COMMAND -> DATA IN */ if (host->scsi.SCp.sent_command != host->SCpnt->cmd_len) acornscsi_abortcmd(host, host->SCpnt->tag); acornscsi_dma_setup(host, DMA_IN); if (!acornscsi_starttransfer(host)) acornscsi_abortcmd(host, host->SCpnt->tag); host->scsi.phase = PHASE_DATAIN; return INTR_IDLE; case 0x1b: /* -> PHASE_STATUS */ /* COMMAND -> STATUS */ acornscsi_readstatusbyte(host); host->scsi.phase = PHASE_STATUSIN; break; case 0x1e: /* -> PHASE_MSGOUT */ /* COMMAND -> MESSAGE OUT */ acornscsi_sendmessage(host); break; case 0x1f: /* -> PHASE_MSGIN, PHASE_DISCONNECT */ /* COMMAND -> MESSAGE IN */ acornscsi_message(host); break; default: printk(KERN_ERR "scsi%d.%c: PHASE_COMMAND, SSR %02X?\n", host->host->host_no, acornscsi_target(host), ssr); acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); } return INTR_PROCESSING; case PHASE_DISCONNECT: /* STATE: connected, received DISCONNECT msg */ if (ssr == 0x85) { /* -> PHASE_IDLE */ host->scsi.disconnectable = 1; host->scsi.reconnected.tag = 0; host->scsi.phase = PHASE_IDLE; host->stats.disconnects += 1; } else { printk(KERN_ERR "scsi%d.%c: PHASE_DISCONNECT, SSR %02X instead of disconnect?\n", host->host->host_no, acornscsi_target(host), ssr); acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); } return INTR_NEXT_COMMAND; case PHASE_IDLE: /* STATE: disconnected */ if (ssr == 0x81) /* -> PHASE_RECONNECTED or PHASE_ABORTED */ acornscsi_reconnect(host); else { printk(KERN_ERR "scsi%d.%c: PHASE_IDLE, SSR %02X while idle?\n", host->host->host_no, acornscsi_target(host), ssr); acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); } return INTR_PROCESSING; case PHASE_RECONNECTED: /* STATE: device reconnected to initiator */ /* * Command reconnected - if MESGIN, get message - it may be * the tag. If not, get command out of disconnected queue */ /* * If we reconnected and we're not in MESSAGE IN phase after IDENTIFY, * reconnect I_T_L command */ if (ssr != 0x8f && !acornscsi_reconnect_finish(host)) return INTR_IDLE; ADD_STATUS(host->SCpnt->device->id, ssr, host->scsi.phase, in_irq); switch (ssr) { case 0x88: /* data out phase */ /* -> PHASE_DATAOUT */ /* MESSAGE IN -> DATA OUT */ acornscsi_dma_setup(host, DMA_OUT); if (!acornscsi_starttransfer(host)) acornscsi_abortcmd(host, host->SCpnt->tag); host->scsi.phase = PHASE_DATAOUT; return INTR_IDLE; case 0x89: /* data in phase */ /* -> PHASE_DATAIN */ /* MESSAGE IN -> DATA IN */ acornscsi_dma_setup(host, DMA_IN); if (!acornscsi_starttransfer(host)) acornscsi_abortcmd(host, host->SCpnt->tag); host->scsi.phase = PHASE_DATAIN; return INTR_IDLE; case 0x8a: /* command out */ /* MESSAGE IN -> COMMAND */ acornscsi_sendcommand(host);/* -
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -