📄 acornscsi.c
字号:
"reset - advanced mode", /* 01 */ /* 2 */ "sel", /* 11 */ "sel+xfer", /* 16 */ "data-out", /* 18 */ "data-in", /* 19 */ "cmd", /* 1A */ "stat", /* 1B */ "??-out", /* 1C */ "??-in", /* 1D */ "msg-out", /* 1E */ "msg-in", /* 1F */ /* 12 */ "/ACK asserted", /* 20 */ "save-data-ptr", /* 21 */ "{re}sel", /* 22 */ /* 15 */ "inv cmd", /* 40 */ "unexpected disconnect", /* 41 */ "sel timeout", /* 42 */ "P err", /* 43 */ "P err+ATN", /* 44 */ "bad status byte", /* 47 */ /* 21 */ "resel, no id", /* 80 */ "resel", /* 81 */ "discon", /* 85 */};staticvoid print_scsi_status(unsigned int ssr){ if (acornscsi_map[ssr] != -1) printk("%s:%s", acornscsi_interrupttype[(ssr >> 4)], acornscsi_interruptcode[acornscsi_map[ssr]]); else printk("%X:%X", ssr >> 4, ssr & 0x0f); } #endifstaticvoid print_sbic_status(int asr, int ssr, int cmdphase){#ifdef CONFIG_ACORNSCSI_CONSTANTS printk("sbic: %c%c%c%c%c%c ", asr & ASR_INT ? 'I' : 'i', asr & ASR_LCI ? 'L' : 'l', asr & ASR_BSY ? 'B' : 'b', asr & ASR_CIP ? 'C' : 'c', asr & ASR_PE ? 'P' : 'p', asr & ASR_DBR ? 'D' : 'd'); printk("scsi: "); print_scsi_status(ssr); printk(" ph %02X\n", cmdphase);#else printk("sbic: %02X scsi: %X:%X ph: %02X\n", asr, (ssr & 0xf0)>>4, ssr & 0x0f, cmdphase);#endif}static voidacornscsi_dumplogline(AS_Host *host, int target, int line){ unsigned long prev; signed int ptr; ptr = host->status_ptr[target] - STATUS_BUFFER_TO_PRINT; if (ptr < 0) ptr += STATUS_BUFFER_SIZE; printk("%c: %3s:", target == 8 ? 'H' : '0' + target, line == 0 ? "ph" : line == 1 ? "ssr" : "int"); prev = host->status[target][ptr].when; for (; ptr != host->status_ptr[target]; ptr = (ptr + 1) & (STATUS_BUFFER_SIZE - 1)) { unsigned long time_diff; if (!host->status[target][ptr].when) continue; switch (line) { case 0: printk("%c%02X", host->status[target][ptr].irq ? '-' : ' ', host->status[target][ptr].ph); break; case 1: printk(" %02X", host->status[target][ptr].ssr); break; case 2: time_diff = host->status[target][ptr].when - prev; prev = host->status[target][ptr].when; if (time_diff == 0) printk("==^"); else if (time_diff >= 100) printk(" "); else printk(" %02ld", time_diff); break; } } printk("\n");}staticvoid acornscsi_dumplog(AS_Host *host, int target){ do { acornscsi_dumplogline(host, target, 0); acornscsi_dumplogline(host, target, 1); acornscsi_dumplogline(host, target, 2); if (target == 8) break; target = 8; } while (1);}staticchar acornscsi_target(AS_Host *host){ if (host->SCpnt) return '0' + host->SCpnt->device->id; return 'H';}/* * Prototype: cmdtype_t acornscsi_cmdtype(int command) * Purpose : differentiate READ from WRITE from other commands * Params : command - command to interpret * Returns : CMD_READ - command reads data, * CMD_WRITE - command writes data, * CMD_MISC - everything else */static inlinecmdtype_t acornscsi_cmdtype(int command){ switch (command) { case WRITE_6: case WRITE_10: case WRITE_12: return CMD_WRITE; case READ_6: case READ_10: case READ_12: return CMD_READ; default: return CMD_MISC; }}/* * Prototype: int acornscsi_datadirection(int command) * Purpose : differentiate between commands that have a DATA IN phase * and a DATA OUT phase * Params : command - command to interpret * Returns : DATADIR_OUT - data out phase expected * DATADIR_IN - data in phase expected */staticdatadir_t acornscsi_datadirection(int command){ switch (command) { case CHANGE_DEFINITION: case COMPARE: case COPY: case COPY_VERIFY: case LOG_SELECT: case MODE_SELECT: case MODE_SELECT_10: case SEND_DIAGNOSTIC: case WRITE_BUFFER: case FORMAT_UNIT: case REASSIGN_BLOCKS: case RESERVE: case SEARCH_EQUAL: case SEARCH_HIGH: case SEARCH_LOW: case WRITE_6: case WRITE_10: case WRITE_VERIFY: case UPDATE_BLOCK: case WRITE_LONG: case WRITE_SAME: case SEARCH_HIGH_12: case SEARCH_EQUAL_12: case SEARCH_LOW_12: case WRITE_12: case WRITE_VERIFY_12: case SET_WINDOW: case MEDIUM_SCAN: case SEND_VOLUME_TAG: case 0xea: return DATADIR_OUT; default: return DATADIR_IN; }}/* * Purpose : provide values for synchronous transfers with 33C93. * Copyright: Copyright (c) 1996 John Shifflett, GeoLog Consulting * Modified by Russell King for 8MHz WD33C93A */static struct sync_xfer_tbl { unsigned int period_ns; unsigned char reg_value;} sync_xfer_table[] = { { 1, 0x20 }, { 249, 0x20 }, { 374, 0x30 }, { 499, 0x40 }, { 624, 0x50 }, { 749, 0x60 }, { 874, 0x70 }, { 999, 0x00 }, { 0, 0 }};/* * Prototype: int acornscsi_getperiod(unsigned char syncxfer) * Purpose : period for the synchronous transfer setting * Params : syncxfer SYNCXFER register value * Returns : period in ns. */staticint acornscsi_getperiod(unsigned char syncxfer){ int i; syncxfer &= 0xf0; if (syncxfer == 0x10) syncxfer = 0; for (i = 1; sync_xfer_table[i].period_ns; i++) if (syncxfer == sync_xfer_table[i].reg_value) return sync_xfer_table[i].period_ns; return 0;}/* * Prototype: int round_period(unsigned int period) * Purpose : return index into above table for a required REQ period * Params : period - time (ns) for REQ * Returns : table index * Copyright: Copyright (c) 1996 John Shifflett, GeoLog Consulting */static inlineint round_period(unsigned int period){ int i; for (i = 1; sync_xfer_table[i].period_ns; i++) { if ((period <= sync_xfer_table[i].period_ns) && (period > sync_xfer_table[i - 1].period_ns)) return i; } return 7;}/* * Prototype: unsigned char calc_sync_xfer(unsigned int period, unsigned int offset) * Purpose : calculate value for 33c93s SYNC register * Params : period - time (ns) for REQ * offset - offset in bytes between REQ/ACK * Returns : value for SYNC register * Copyright: Copyright (c) 1996 John Shifflett, GeoLog Consulting */staticunsigned char calc_sync_xfer(unsigned int period, unsigned int offset){ return sync_xfer_table[round_period(period)].reg_value | ((offset < SDTR_SIZE) ? offset : SDTR_SIZE);}/* ==================================================================================== * Command functions *//* * Function: acornscsi_kick(AS_Host *host) * Purpose : kick next command to interface * Params : host - host to send command to * Returns : INTR_IDLE if idle, otherwise INTR_PROCESSING * Notes : interrupts are always disabled! */staticintr_ret_t acornscsi_kick(AS_Host *host){ int from_queue = 0; Scsi_Cmnd *SCpnt; /* first check to see if a command is waiting to be executed */ SCpnt = host->origSCpnt; host->origSCpnt = NULL; /* retrieve next command */ if (!SCpnt) { SCpnt = queue_remove_exclude(&host->queues.issue, host->busyluns); if (!SCpnt) return INTR_IDLE; from_queue = 1; } if (host->scsi.disconnectable && host->SCpnt) { queue_add_cmd_tail(&host->queues.disconnected, host->SCpnt); host->scsi.disconnectable = 0;#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON)) DBG(host->SCpnt, printk("scsi%d.%c: moved command to disconnected queue\n", host->host->host_no, acornscsi_target(host)));#endif host->SCpnt = NULL; } /* * If we have an interrupt pending, then we may have been reselected. * In this case, we don't want to write to the registers */ if (!(sbic_arm_read(host->scsi.io_port, SBIC_ASR) & (ASR_INT|ASR_BSY|ASR_CIP))) { sbic_arm_write(host->scsi.io_port, SBIC_DESTID, SCpnt->device->id); sbic_arm_write(host->scsi.io_port, SBIC_CMND, CMND_SELWITHATN); } /* * claim host busy - all of these must happen atomically wrt * our interrupt routine. Failure means command loss. */ host->scsi.phase = PHASE_CONNECTING; host->SCpnt = SCpnt; host->scsi.SCp = SCpnt->SCp; host->dma.xfer_setup = 0; host->dma.xfer_required = 0; host->dma.xfer_done = 0;#if (DEBUG & (DEBUG_ABORT|DEBUG_CONNECT)) DBG(SCpnt,printk("scsi%d.%c: starting cmd %02X\n", host->host->host_no, '0' + SCpnt->device->id, SCpnt->cmnd[0]));#endif if (from_queue) {#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE /* * tagged queueing - allocate a new tag to this command */ if (SCpnt->device->simple_tags) { SCpnt->device->current_tag += 1; if (SCpnt->device->current_tag == 0) SCpnt->device->current_tag = 1; SCpnt->tag = SCpnt->device->current_tag; } else#endif set_bit(SCpnt->device->id * 8 + SCpnt->device->lun, host->busyluns); host->stats.removes += 1; switch (acornscsi_cmdtype(SCpnt->cmnd[0])) { case CMD_WRITE: host->stats.writes += 1; break; case CMD_READ: host->stats.reads += 1; break; case CMD_MISC: host->stats.miscs += 1; break; } } return INTR_PROCESSING;} /* * Function: void acornscsi_done(AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result) * Purpose : complete processing for command * Params : host - interface that completed * result - driver byte of result */staticvoid acornscsi_done(AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result){ Scsi_Cmnd *SCpnt = *SCpntp; /* clean up */ sbic_arm_write(host->scsi.io_port, SBIC_SOURCEID, SOURCEID_ER | SOURCEID_DSP); host->stats.fins += 1; if (SCpnt) { *SCpntp = NULL; acornscsi_dma_cleanup(host); SCpnt->result = result << 16 | host->scsi.SCp.Message << 8 | host->scsi.SCp.Status; /* * In theory, this should not happen. In practice, it seems to. * Only trigger an error if the device attempts to report all happy * but with untransferred buffers... If we don't do something, then * data loss will occur. Should we check SCpnt->underflow here? * It doesn't appear to be set to something meaningful by the higher * levels all the time. */ if (result == DID_OK) { int xfer_warn = 0; if (SCpnt->underflow == 0) { if (host->scsi.SCp.ptr && acornscsi_cmdtype(SCpnt->cmnd[0]) != CMD_MISC) xfer_warn = 1; } else { if (host->scsi.SCp.scsi_xferred < SCpnt->underflow || host->scsi.SCp.scsi_xferred != host->dma.transferred) xfer_warn = 1; } /* ANSI standard says: (SCSI-2 Rev 10c Sect 5.6.6) * Targets which break data transfers into multiple * connections shall end each successful connection * (except possibly the last) with a SAVE DATA * POINTER - DISCONNECT message sequence. * * This makes it difficult to ensure that a transfer has * completed. If we reach the end of a transfer during * the command, then we can only have finished the transfer. * therefore, if we seem to have some data remaining, this * is not a problem. */ if (host->dma.xfer_done) xfer_warn = 0; if (xfer_warn) { switch (status_byte(SCpnt->result)) { case CHECK_CONDITION: case COMMAND_TERMINATED: case BUSY: case QUEUE_FULL: case RESERVATION_CONFLICT: break; default: printk(KERN_ERR "scsi%d.H: incomplete data transfer detected: result=%08X command=", host->host->host_no, SCpnt->result); __scsi_print_command(SCpnt->cmnd); acornscsi_dumpdma(host, "done"); acornscsi_dumplog(host, SCpnt->device->id); SCpnt->result &= 0xffff; SCpnt->result |= DID_ERROR << 16; } } } if (!SCpnt->scsi_done) panic("scsi%d.H: null scsi_done function in acornscsi_done", host->host->host_no); clear_bit(SCpnt->device->id * 8 + SCpnt->device->lun, host->busyluns); SCpnt->scsi_done(SCpnt); } else printk("scsi%d: null command in acornscsi_done", host->host->host_no); host->scsi.phase = PHASE_IDLE;}/* ==================================================================================== * DMA routines *//*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -