📄 acornscsi.c.dag2
字号:
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}staticvoid acornscsi_dumplog (AS_Host *host, int target){ unsigned int prev; do { signed int statptr; printk ("%c:", target == 8 ? 'H' : ('0' + target)); statptr = status_ptr[target] - 10; if (statptr < 0) statptr += 16; prev = status[target][statptr].when; for (; statptr != status_ptr[target]; statptr = (statptr + 1) & 15) { if (status[target][statptr].when) {#ifdef CONFIG_ACORNSCSI_CONSTANTS printk ("%c%02X:S=", status[target][statptr].irq ? '-' : ' ', status[target][statptr].ph); print_scsi_status (status[target][statptr].ssr);#else printk ("%c%02X:%02X", status[target][statptr].irq ? '-' : ' ', status[target][statptr].ph, status[target][statptr].ssr);#endif printk ("+%02ld", (status[target][statptr].when - prev) < 100 ? (status[target][statptr].when - prev) : 99); prev = status[target][statptr].when; } } printk ("\n"); if (target == 8) break; target = 8; } while (1);}staticchar acornscsi_target (AS_Host *host){ if (host->SCpnt) return '0' + host->SCpnt->target; return 'H';}staticvoid acornscsi_dumpdma (AS_Host *host, char *where){ unsigned int mode, addr, len; mode = dmac_read (host->dma.io_port, MODECON); addr = dmac_address (host->dma.io_port); len = dmac_read (host->dma.io_port, TXCNTHI) << 8 | dmac_read (host->dma.io_port, TXCNTLO); printk ("scsi%d: %s: DMAC %02x @%06x+%04x msk %02x, ", host->host->host_no, where, mode, addr, (len + 1) & 0xffff, dmac_read (host->dma.io_port, MASKREG)); printk ("DMA @%06x, ", host->dma.start_addr); printk ("BH @%p +%04x, ", host->scsi.SCp.ptr, host->scsi.SCp.this_residual); printk ("DT @+%04x ST @+%04x", host->dma.transferred, host->scsi.SCp.have_data_in); printk ("\n");}/* * 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, ASR) & (ASR_INT|ASR_BSY|ASR_CIP))) { sbic_arm_write (host->scsi.io_port, DESTID, SCpnt->target); sbic_arm_write (host->scsi.io_port, 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;#if (DEBUG & (DEBUG_ABORT|DEBUG_CONNECT)) DBG(SCpnt,printk ("scsi%d.%c: starting cmd %02X\n", host->host->host_no, '0' + SCpnt->target, SCpnt->cmnd[0]));#endif if (from_queue) {#ifdef SCSI2_TAG /* * tagged queueing - allocate a new tag to this command */ if (SCpnt->device->tagged_queue) { 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->target * 8 + SCpnt->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, 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 (host->scsi.SCp.ptr && result == DID_OK && acornscsi_cmdtype (SCpnt->cmnd[0]) != CMD_MISC) { 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); print_command (SCpnt->cmnd); acornscsi_dumpdma (host, "done"); acornscsi_dumplog (host, SCpnt->target); 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->target * 8 + SCpnt->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 *//* * Purpose : update SCSI Data Pointer * Notes : this will only be one SG entry or less */staticvoid acornscsi_data_updateptr (AS_Host *host, Scsi_Pointer *SCp, unsigned int length){ SCp->ptr += length; SCp->this_residual -= length; if (!SCp->this_residual) { if (SCp->buffers_residual) { SCp->buffer++; SCp->buffers_residual--; SCp->ptr = (char *)SCp->buffer->address; SCp->this_residual = SCp->buffer->length; } else SCp->ptr = NULL; }}/* * Prototype: void acornscsi_data_read (AS_Host *host, char *ptr, * unsigned int start_addr, unsigned int length) * Purpose : read data from DMA RAM * Params : host - host to transfer from * ptr - DRAM address * start_addr - host mem address * length - number of bytes to transfer * Notes : this will only be one SG entry or less */staticvoid acornscsi_data_read (AS_Host *host, char *ptr, unsigned int start_addr, unsigned int length){ extern void __acornscsi_in (int port, char *buf, int len); unsigned int page, offset, len = length; page = (start_addr >> 12); offset = start_addr & ((1 << 12) - 1); outb ((page & 0x3f) | host->card.page_reg, host->card.io_page); while (len > 0) { unsigned int this_len; if (len + offset > (1 << 12)) this_len = (1 << 12) - offset; else this_len = len; __acornscsi_in (host->card.io_ram + (offset << 1), ptr, this_len); offset += this_len; ptr += this_len; len -= this_len; if (offset == (1 << 12)) { offset = 0; page ++; outb ((page & 0x3f) | host->card.page_reg, host->card.io_page); } } outb (host->card.page_reg, host->card.io_page);}/* * Prototype: void acornscsi_data_write (AS_Host *host, char *ptr, * unsigned int start_addr, unsigned int length) * Purpose : write data to DMA RAM * Params : host - host to transfer from * ptr - DRAM address * start_addr - host mem address * length - number of bytes to transfer * Notes : this will only be one SG entry or less */static
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -