diff options
Diffstat (limited to 'drivers/acorn/scsi/fas216.c')
-rw-r--r-- | drivers/acorn/scsi/fas216.c | 1032 |
1 files changed, 616 insertions, 416 deletions
diff --git a/drivers/acorn/scsi/fas216.c b/drivers/acorn/scsi/fas216.c index 744480f51..1a89a91eb 100644 --- a/drivers/acorn/scsi/fas216.c +++ b/drivers/acorn/scsi/fas216.c @@ -1,11 +1,12 @@ /* * linux/arch/arm/drivers/scsi/fas216.c * - * Copyright (C) 1997 Russell King + * Copyright (C) 1997-2000 Russell King * * Based on information in qlogicfas.c by Tom Zerucha, Michael Griffith, and * other sources, including: * the AMD Am53CF94 data sheet + * the AMD Am53C94 data sheet * * This is a generic driver. To use it, have a look at cumana_2.c. You * should define your own structure that overlays FAS216_Info, eg: @@ -25,6 +26,9 @@ * 24-05-1998 RMK Fixed synchronous transfers with period >= 200ns * 27-06-1998 RMK Changed asm/delay.h to linux/delay.h * 26-08-1998 RMK Improved message support wrt MESSAGE_REJECT + * 02-04-2000 RMK Converted to use the new error handling, and + * automatically request sense data upon check + * condition status from targets. * * Todo: * - allow individual devices to enable sync xfers. @@ -59,8 +63,6 @@ MODULE_DESCRIPTION("Generic FAS216/NCR53C9x driver"); #define VER_MINOR 0 #define VER_PATCH 5 -#define SCSI2_TAG - /* NOTE: SCSI2 Synchronous transfers *require* DMA according to * the data sheet. This restriction is crazy, especially when * you only want to send 16 bytes! What were the guys who @@ -85,8 +87,8 @@ MODULE_DESCRIPTION("Generic FAS216/NCR53C9x driver"); * I was thinking that this was a good chip until I found this restriction ;( */ #define SCSI2_SYNC - -#define SCSI2_WIDE +#undef SCSI2_WIDE +#undef SCSI2_TAG #undef DEBUG_CONNECT #undef DEBUG_BUSSERVICE @@ -143,10 +145,11 @@ static void fas216_dumpinfo(FAS216_Info *info) info->scsi.async_stp, info->scsi.disconnectable, info->scsi.aborting); printk(" stats={ queues=%X removes=%X fins=%X reads=%X writes=%X miscs=%X\n" - " disconnects=%X aborts=%X resets=%X }\n", + " disconnects=%X aborts=%X bus_resets=%X host_resets=%X}\n", info->stats.queues, info->stats.removes, info->stats.fins, info->stats.reads, info->stats.writes, info->stats.miscs, - info->stats.disconnects, info->stats.aborts, info->stats.resets); + info->stats.disconnects, info->stats.aborts, info->stats.bus_resets, + info->stats.host_resets); printk(" ifcfg={ clockrate=%X select_timeout=%X asyncperiod=%X sync_max_depth=%X }\n", info->ifcfg.clockrate, info->ifcfg.select_timeout, info->ifcfg.asyncperiod, info->ifcfg.sync_max_depth); @@ -164,7 +167,7 @@ static void fas216_dumpinfo(FAS216_Info *info) } #ifdef CHECK_STRUCTURE -static void fas216_checkmagic(FAS216_Info *info, const char *func) +static void __fas216_checkmagic(FAS216_Info *info, const char *func) { int corruption = 0; if (info->magic_start != MAGIC) { @@ -180,8 +183,9 @@ static void fas216_checkmagic(FAS216_Info *info, const char *func) panic("scsi memory space corrupted in %s", func); } } +#define fas216_checkmagic(info) __fas216_checkmagic((info), __FUNCTION__) #else -#define fas216_checkmagic(info,func) +#define fas216_checkmagic(info) #endif static const char *fas216_bus_phase(int stat) @@ -312,7 +316,7 @@ fas216_syncperiod(FAS216_Info *info, int ns) { int value = (info->ifcfg.clockrate * ns) / 1000; - fas216_checkmagic(info, "fas216_syncperiod"); + fas216_checkmagic(info); if (value < 4) value = 4; @@ -586,7 +590,7 @@ fas216_updateptrs(FAS216_Info *info, int bytes_transferred) unsigned char *ptr; unsigned int residual; - fas216_checkmagic(info, "fas216_updateptrs"); + fas216_checkmagic(info); ptr = info->scsi.SCp.ptr; residual = info->scsi.SCp.this_residual; @@ -629,7 +633,7 @@ fas216_pio(FAS216_Info *info, fasdmadir_t direction) unsigned int residual; char *ptr; - fas216_checkmagic(info, "fas216_pio"); + fas216_checkmagic(info); residual = info->scsi.SCp.this_residual; ptr = info->scsi.SCp.ptr; @@ -668,7 +672,7 @@ fas216_starttransfer(FAS216_Info *info, fasdmadir_t direction, int flush_fifo) { fasdmatype_t dmatype; - fas216_checkmagic(info, "fas216_starttransfer"); + fas216_checkmagic(info); info->scsi.phase = (direction == DMA_OUT) ? PHASE_DATAOUT : PHASE_DATAIN; @@ -763,7 +767,7 @@ fas216_starttransfer(FAS216_Info *info, fasdmadir_t direction, int flush_fifo) static void fas216_stoptransfer(FAS216_Info *info) { - fas216_checkmagic(info, "fas216_stoptransfer"); + fas216_checkmagic(info); if (info->dma.transfer_type != fasdma_none && info->dma.transfer_type != fasdma_pio) { @@ -797,7 +801,7 @@ fas216_stoptransfer(FAS216_Info *info) static void fas216_disconnect_intr(FAS216_Info *info) { - fas216_checkmagic(info, "fas216_disconnected_intr"); + fas216_checkmagic(info); #ifdef DEBUG_CONNECT printk("scsi%d.%c: disconnect phase=%02X\n", info->host->host_no, @@ -849,7 +853,7 @@ fas216_reselected_intr(FAS216_Info *info) { unsigned char target, identify_msg, ok; - fas216_checkmagic(info, "fas216_reselected_intr"); + fas216_checkmagic(info); if ((info->scsi.phase == PHASE_SELECTION || info->scsi.phase == PHASE_SELSTEPS) && info->SCpnt) { @@ -949,6 +953,7 @@ fas216_reselected_intr(FAS216_Info *info) msgqueue_addmsg(&info->scsi.msgs, 1, ABORT); info->scsi.phase = PHASE_MSGOUT_EXPECT; } + outb(CMD_MSGACCEPTED, REG_CMD(info)); } @@ -959,7 +964,7 @@ fas216_reselected_intr(FAS216_Info *info) static void fas216_finish_reconnect(FAS216_Info *info) { - fas216_checkmagic(info, "fas216_reconnect"); + fas216_checkmagic(info); #ifdef DEBUG_CONNECT printk("Connected: %1X %1X %02X, reconnected: %1X %1X %02X\n", @@ -1087,7 +1092,7 @@ static void fas216_message(FAS216_Info *info) unsigned int msglen = 1, i; int msgbyte = 0; - fas216_checkmagic(info, "fas216_message"); + fas216_checkmagic(info); message[0] = inb(REG_FF(info)); @@ -1270,7 +1275,7 @@ static void fas216_send_command(FAS216_Info *info) { int i; - fas216_checkmagic(info, "fas216_send_command"); + fas216_checkmagic(info); outb(CMD_NOP|CMD_WITHDMA, REG_CMD(info)); outb(CMD_FLUSHFIFO, REG_CMD(info)); @@ -1293,7 +1298,7 @@ static void fas216_send_messageout(FAS216_Info *info, int start) { unsigned int tot_msglen = msgqueue_msglength(&info->scsi.msgs); - fas216_checkmagic(info, "fas216_send_messageout"); + fas216_checkmagic(info); outb(CMD_FLUSHFIFO, REG_CMD(info)); @@ -1326,7 +1331,7 @@ static void fas216_send_messageout(FAS216_Info *info, int start) */ static void fas216_busservice_intr(FAS216_Info *info, unsigned int stat, unsigned int ssr) { - fas216_checkmagic(info, "fas216_busservice_intr"); + fas216_checkmagic(info); #ifdef DEBUG_BUSSERVICE printk("scsi%d.%c: bus service: stat=%02X ssr=%02X phase=%02X\n", @@ -1520,7 +1525,7 @@ static void fas216_funcdone_intr(FAS216_Info *info, unsigned int stat, unsigned { int status, message; - fas216_checkmagic(info, "fas216_funcdone_intr"); + fas216_checkmagic(info); #ifdef DEBUG_FUNCTIONDONE printk("scsi%d.%c: function done: stat=%X ssr=%X phase=%02X\n", @@ -1562,7 +1567,7 @@ void fas216_intr(struct Scsi_Host *instance) FAS216_Info *info = (FAS216_Info *)instance->hostdata; unsigned char isr, ssr, stat; - fas216_checkmagic(info, "fas216_intr"); + fas216_checkmagic(info); stat = inb(REG_STAT(info)); ssr = inb(REG_IS(info)); @@ -1571,9 +1576,10 @@ void fas216_intr(struct Scsi_Host *instance) add_debug_list(stat, ssr, isr, info->scsi.phase); if (stat & STAT_INT) { - if (isr & INST_BUSRESET) + if (isr & INST_BUSRESET) { printk(KERN_DEBUG "scsi%d.H: bus reset detected\n", instance->host_no); - else if (isr & INST_ILLEGALCMD) { + scsi_report_bus_reset(instance, 0); + } else if (isr & INST_ILLEGALCMD) { printk(KERN_CRIT "scsi%d.H: illegal command given\n", instance->host_no); fas216_dumpstate(info); } else if (isr & INST_DISCONNECT) @@ -1599,22 +1605,35 @@ void fas216_intr(struct Scsi_Host *instance) */ static void fas216_kick(FAS216_Info *info) { - Scsi_Cmnd *SCpnt; - int tot_msglen, from_queue = 0; + Scsi_Cmnd *SCpnt = NULL; + int tot_msglen, from_queue = 0, disconnect_ok; - fas216_checkmagic(info, "fas216_kick"); + fas216_checkmagic(info); - if (info->origSCpnt) { - SCpnt = info->origSCpnt; - info->origSCpnt = NULL; - } else - SCpnt = NULL; + /* + * Obtain the next command to process. + */ + do { + if (info->reqSCpnt) { + SCpnt = info->reqSCpnt; + info->reqSCpnt = NULL; + break; + } - /* retrieve next command */ - if (!SCpnt) { - SCpnt = queue_remove_exclude(&info->queues.issue, info->busyluns); - from_queue = 1; - } + if (info->origSCpnt) { + SCpnt = info->origSCpnt; + info->origSCpnt = NULL; + break; + } + + /* retrieve next command */ + if (!SCpnt) { + SCpnt = queue_remove_exclude(&info->queues.issue, + info->busyluns); + from_queue = 1; + break; + } + } while (0); if (!SCpnt) /* no command pending - just exit */ return; @@ -1628,16 +1647,6 @@ static void fas216_kick(FAS216_Info *info) } /* - * tagged queuing - allocate a new tag to this command - */ - if (SCpnt->device->tagged_queue && SCpnt->cmnd[0] != REQUEST_SENSE) { - SCpnt->device->current_tag += 1; - if (SCpnt->device->current_tag == 0) - SCpnt->device->current_tag = 1; - SCpnt->tag = SCpnt->device->current_tag; - } - - /* * claim host busy */ info->scsi.phase = PHASE_SELECTION; @@ -1653,6 +1662,9 @@ static void fas216_kick(FAS216_Info *info) if (from_queue) { #ifdef SCSI2_TAG + /* + * tagged queuing - allocate a new tag to this command + */ if (SCpnt->device->tagged_queue && SCpnt->cmnd[0] != REQUEST_SENSE && SCpnt->cmnd[0] != INQUIRY) { SCpnt->device->current_tag += 1; @@ -1681,41 +1693,48 @@ static void fas216_kick(FAS216_Info *info) } } - /* build outgoing message bytes */ - msgqueue_flush(&info->scsi.msgs); + /* + * Don't allow request sense commands to disconnect. + */ + disconnect_ok = SCpnt->cmnd[0] != REQUEST_SENSE && + info->device[SCpnt->target].disconnect_ok; - if (info->device[SCpnt->target].disconnect_ok) - msgqueue_addmsg(&info->scsi.msgs, 1, IDENTIFY(1, SCpnt->lun)); - else - msgqueue_addmsg(&info->scsi.msgs, 1, IDENTIFY(0, SCpnt->lun)); + /* + * build outgoing message bytes + */ + msgqueue_flush(&info->scsi.msgs); + msgqueue_addmsg(&info->scsi.msgs, 1, IDENTIFY(disconnect_ok, SCpnt->lun)); - /* add tag message if required */ + /* + * add tag message if required + */ if (SCpnt->tag) msgqueue_addmsg(&info->scsi.msgs, 2, SIMPLE_QUEUE_TAG, SCpnt->tag); + do { #ifdef SCSI2_WIDE - if (info->device[SCpnt->target].wide_state == neg_wait) { - info->device[SCpnt->target].wide_state = neg_inprogress; - msgqueue_addmsg(&info->scsi.msgs, 4, - EXTENDED_MESSAGE, 2, EXTENDED_WDTR, - info->ifcfg.wide_max_size); - } -#ifdef SCSI2_SYNC - else -#endif + if (info->device[SCpnt->target].wide_state == neg_wait) { + info->device[SCpnt->target].wide_state = neg_inprogress; + msgqueue_addmsg(&info->scsi.msgs, 4, + EXTENDED_MESSAGE, 2, EXTENDED_WDTR, + info->ifcfg.wide_max_size); + break; + } #endif #ifdef SCSI2_SYNC - if ((info->device[SCpnt->target].sync_state == neg_wait || - info->device[SCpnt->target].sync_state == neg_complete) && - (SCpnt->cmnd[0] == REQUEST_SENSE || - SCpnt->cmnd[0] == INQUIRY)) { - info->device[SCpnt->target].sync_state = neg_inprogress; - msgqueue_addmsg(&info->scsi.msgs, 5, - EXTENDED_MESSAGE, 3, EXTENDED_SDTR, - 1000 / info->ifcfg.clockrate, - info->ifcfg.sync_max_depth); - } + if ((info->device[SCpnt->target].sync_state == neg_wait || + info->device[SCpnt->target].sync_state == neg_complete) && + (SCpnt->cmnd[0] == REQUEST_SENSE || + SCpnt->cmnd[0] == INQUIRY)) { + info->device[SCpnt->target].sync_state = neg_inprogress; + msgqueue_addmsg(&info->scsi.msgs, 5, + EXTENDED_MESSAGE, 3, EXTENDED_SDTR, + 1000 / info->ifcfg.clockrate, + info->ifcfg.sync_max_depth); + break; + } #endif + } while (0); /* following what the ESP driver says */ outb(0, REG_STCL(info)); @@ -1780,18 +1799,156 @@ static void fas216_kick(FAS216_Info *info) /* should now get either DISCONNECT or (FUNCTION DONE with BUS SERVICE) intr */ } +/* Function: void fas216_rq_sns_done(info, SCpnt, result) + * Purpose : Finish processing automatic request sense command + * Params : info - interface that completed + * SCpnt - command that completed + * result - driver byte of result + */ +static void +fas216_rq_sns_done(FAS216_Info *info, Scsi_Cmnd *SCpnt, unsigned int result) +{ +#ifdef DEBUG_CONNECT + printk("scsi%d.%c: request sense complete, result=%04X%02X%02X\n", + info->host->host_no, '0' + SCpnt->target, result, + SCpnt->SCp.Message, SCpnt->SCp.Status); +#endif + + if (result != DID_OK || SCpnt->SCp.Status != GOOD) + /* + * Something went wrong. Make sure that we don't + * have valid data in the sense buffer that could + * confuse the higher levels. + */ + memset(SCpnt->sense_buffer, 0, sizeof(SCpnt->sense_buffer)); + + /* + * Note that we don't set SCpnt->result, since that should + * reflect the status of the command that we were asked by + * the upper layers to process. This would have been set + * correctly by fas216_std_done. + */ + SCpnt->scsi_done(SCpnt); +} + +/* Function: void fas216_std_done(info, SCpnt, result) + * Purpose : Finish processing of standard command + * Params : info - interface that completed + * SCpnt - command that completed + * result - driver byte of result + */ +static void +fas216_std_done(FAS216_Info *info, Scsi_Cmnd *SCpnt, unsigned int result) +{ + info->stats.fins += 1; + + SCpnt->result = result << 16 | info->scsi.SCp.Message << 8 | + info->scsi.SCp.Status; + +#ifdef DEBUG_CONNECT + printk("scsi%d.%c: command complete, result=%08X, command=", + info->host->host_no, '0' + SCpnt->target, SCpnt->result); + print_command(SCpnt->cmnd); +#endif + + /* + * If the driver detected an error, or the command + * was request sense, then we're all done. + */ + if (result != DID_OK || SCpnt->cmnd[0] == REQUEST_SENSE) + goto done; + + /* + * If the command returned CHECK_CONDITION status, + * request the sense information. + */ + if (info->scsi.SCp.Status == CHECK_CONDITION) + goto request_sense; + + /* + * If the command did not complete with GOOD status, + * we are all done here. + */ + if (info->scsi.SCp.Status != GOOD) + goto done; + + /* + * We have successfully completed a command. Make sure that + * we do not have any buffers left to transfer. The world + * is not perfect, and we seem to occasionally hit this. + * It can be indicative of a buggy driver, target or the upper + * levels of the SCSI code. + */ + if (info->scsi.SCp.ptr) { + switch (SCpnt->cmnd[0]) { + case INQUIRY: + case START_STOP: +// case READ_CAPACITY: + case MODE_SENSE: + break; + + default: + printk(KERN_ERR "scsi%d.%c: incomplete data transfer " + "detected: res=%08X ptr=%p len=%X command=", + info->host->host_no, '0' + SCpnt->target, + SCpnt->result, info->scsi.SCp.ptr, + info->scsi.SCp.this_residual); + print_command(SCpnt->cmnd); + } + } + +done: SCpnt->scsi_done(SCpnt); + return; + +request_sense: + memset(SCpnt->cmnd, 0, sizeof (SCpnt->cmnd)); + SCpnt->cmnd[0] = REQUEST_SENSE; + SCpnt->cmnd[1] = SCpnt->lun << 5; + SCpnt->cmnd[4] = sizeof(SCpnt->sense_buffer); + SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]); + SCpnt->SCp.buffer = NULL; + SCpnt->SCp.buffers_residual = 0; + SCpnt->SCp.ptr = (char *)SCpnt->sense_buffer; + SCpnt->SCp.this_residual = sizeof(SCpnt->sense_buffer); + SCpnt->SCp.Message = 0; + SCpnt->SCp.Status = 0; + SCpnt->sc_data_direction = SCSI_DATA_READ; + SCpnt->use_sg = 0; + SCpnt->tag = 0; + SCpnt->host_scribble = (void *)fas216_rq_sns_done; + + /* + * Place this command into the high priority "request + * sense" slot. This will be the very next command + * executed, unless a target connects to us. + */ + if (info->reqSCpnt) + printk(KERN_WARNING "scsi%d.%c: loosing request command\n", + info->host->host_no, '0' + SCpnt->target); + info->reqSCpnt = SCpnt; +} + /* Function: void fas216_done(FAS216_Info *info, unsigned int result) - * Purpose : complete processing for command + * Purpose : complete processing for current command * Params : info - interface that completed * result - driver byte of result */ static void fas216_done(FAS216_Info *info, unsigned int result) { + void (*fn)(FAS216_Info *, Scsi_Cmnd *, unsigned int); Scsi_Cmnd *SCpnt; - fas216_checkmagic(info, "fas216_done"); + fas216_checkmagic(info); + + if (!info->SCpnt) + goto no_command; SCpnt = info->SCpnt; + info->SCpnt = NULL; + info->scsi.phase = PHASE_IDLE; + + if (!SCpnt->scsi_done) + goto no_done; if (info->scsi.aborting) { printk("scsi%d.%c: uncaught abort - returning DID_ABORT\n", @@ -1800,66 +1957,39 @@ static void fas216_done(FAS216_Info *info, unsigned int result) info->scsi.aborting = 0; } - info->stats.fins += 1; - - if (SCpnt) { - info->scsi.phase = PHASE_IDLE; - info->SCpnt = NULL; - - SCpnt->result = result << 16 | info->scsi.SCp.Message << 8 | - info->scsi.SCp.Status; - - /* - * In theory, this should not happen, but just in case it does. - */ - if (info->scsi.SCp.ptr && - info->scsi.SCp.this_residual && - result == DID_OK) { - switch (SCpnt->cmnd[0]) { - case INQUIRY: - case START_STOP: - case READ_CAPACITY: - case TEST_UNIT_READY: - case MODE_SENSE: - case REQUEST_SENSE: - break; - - default: - 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: res=%08X ptr=%p len=%X command=", - info->host->host_no, SCpnt->result, - info->scsi.SCp.ptr, info->scsi.SCp.this_residual); - print_command(SCpnt->cmnd); - } - } - } -#ifdef DEBUG_CONNECT - printk("scsi%d.%c: scsi command (%p) complete, result=%08X\n", - info->host->host_no, fas216_target(info), - SCpnt, SCpnt->result); -#endif - - if (!SCpnt->scsi_done) - panic("scsi%d.H: null scsi_done function in " - "fas216_done", info->host->host_no); + /* + * Sanity check the completion - if we have zero bytes left + * to transfer, we should not have a valid pointer. + */ + if (info->scsi.SCp.ptr && info->scsi.SCp.this_residual == 0) { + printk("scsi%d.%c: zero bytes left to transfer, but " + "buffer pointer still valid: ptr=%p len=%08x command=", + info->host->host_no, '0' + SCpnt->target, + info->scsi.SCp.ptr, info->scsi.SCp.this_residual); + info->scsi.SCp.ptr = NULL; + print_command(SCpnt->cmnd); + } - clear_bit(SCpnt->target * 8 + SCpnt->lun, info->busyluns); + /* + * Clear down this command as completed. If we need to request + * the sense information, fas216_kick will re-assert the busy + * status. + */ + clear_bit(SCpnt->target * 8 + SCpnt->lun, info->busyluns); - SCpnt->scsi_done(SCpnt); - } else - panic("scsi%d.H: null command in fas216_done", info->host->host_no); + fn = (void (*)(FAS216_Info *, Scsi_Cmnd *, unsigned int))SCpnt->host_scribble; + fn(info, SCpnt, result); if (info->scsi.irq != NO_IRQ) fas216_kick(info); + return; + +no_command: + panic("scsi%d.H: null command in fas216_done", + info->host->host_no); +no_done: + panic("scsi%d.H: null scsi_done function in fas216_done", + info->host->host_no); } /* Function: int fas216_queue_command(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) @@ -1867,13 +1997,14 @@ static void fas216_done(FAS216_Info *info, unsigned int result) * Params : SCpnt - Command to queue * done - done function to call once command is complete * Returns : 0 - success, else error + * Notes : io_request_lock is held, interrupts are disabled. */ int fas216_queue_command(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) { FAS216_Info *info = (FAS216_Info *)SCpnt->host->hostdata; - unsigned long flags; + int result; - fas216_checkmagic(info, "fas216_queue_command"); + fas216_checkmagic(info); #ifdef DEBUG_CONNECT printk("scsi%d.%c: received queuable command (%p) %02X\n", @@ -1882,7 +2013,7 @@ int fas216_queue_command(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) #endif SCpnt->scsi_done = done; - SCpnt->host_scribble = NULL; + SCpnt->host_scribble = (void *)fas216_std_done; SCpnt->result = 0; SCpnt->SCp.Message = 0; SCpnt->SCp.Status = 0; @@ -1896,10 +2027,16 @@ int fas216_queue_command(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) SCpnt->SCp.ptr = (char *) SCpnt->SCp.buffer->address; SCpnt->SCp.this_residual = SCpnt->SCp.buffer->length; /* - * Calculate correct buffer length + * Calculate correct buffer length. Some commands + * come in with the wrong request_bufflen. */ for (buf = 0; buf <= SCpnt->SCp.buffers_residual; buf++) len += SCpnt->SCp.buffer[buf].length; + + if (SCpnt->request_bufflen != len) + printk(KERN_WARNING "scsi%d.%c: bad request buffer " + "length %d, should be %ld\n", info->host->host_no, + '0' + SCpnt->target, SCpnt->request_bufflen, len); SCpnt->request_bufflen = len; } else { SCpnt->SCp.buffer = NULL; @@ -1908,22 +2045,36 @@ int fas216_queue_command(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) SCpnt->SCp.this_residual = SCpnt->request_bufflen; } + /* + * If the upper SCSI layers pass a buffer, but zero length, + * we aren't interested in the buffer pointer. + */ + if (SCpnt->SCp.this_residual == 0 && SCpnt->SCp.ptr) { +#if 0 + printk(KERN_WARNING "scsi%d.%c: zero length buffer passed for " + "command ", info->host->host_no, '0' + SCpnt->target); + print_command(SCpnt->cmnd); +#endif + SCpnt->SCp.ptr = NULL; + } + info->stats.queues += 1; SCpnt->tag = 0; - /* add command into execute queue and let it complete under + /* + * Add command into execute queue and let it complete under * whatever scheme we're using. */ - if (!queue_add_cmd_ordered(&info->queues.issue, SCpnt)) { - SCpnt->result = DID_ERROR << 16; - done(SCpnt); - } - save_flags_cli(flags); - if (!info->SCpnt || info->scsi.disconnectable) + result = !queue_add_cmd_ordered(&info->queues.issue, SCpnt); + + /* + * If we successfully added the command, + * kick the interface to get it moving. + */ + if (result == 0 && (!info->SCpnt || info->scsi.disconnectable)) fas216_kick(info); - restore_flags(flags); - return 0; + return result; } /* Function: void fas216_internal_done(Scsi_Cmnd *SCpnt) @@ -1934,7 +2085,7 @@ static void fas216_internal_done(Scsi_Cmnd *SCpnt) { FAS216_Info *info = (FAS216_Info *)SCpnt->host->hostdata; - fas216_checkmagic(info, "fas216_internal_done"); + fas216_checkmagic(info); info->internal_done = 1; } @@ -1943,13 +2094,20 @@ static void fas216_internal_done(Scsi_Cmnd *SCpnt) * Purpose : queue a command for adapter to process. * Params : SCpnt - Command to queue * Returns : scsi result code + * Notes : io_request_lock is held, interrupts are disabled. */ int fas216_command(Scsi_Cmnd *SCpnt) { FAS216_Info *info = (FAS216_Info *)SCpnt->host->hostdata; - unsigned long flags; - fas216_checkmagic(info, "fas216_command"); + fas216_checkmagic(info); + + /* + * We should only be using this if we don't have an interrupt. + * Provide some "incentive" to use the queueing code. + */ + if (info->scsi.irq != NO_IRQ) + BUG(); info->internal_done = 0; fas216_queue_command(SCpnt, fas216_internal_done); @@ -1960,114 +2118,52 @@ int fas216_command(Scsi_Cmnd *SCpnt) * However, we must re-enable interrupts, or else we'll be * waiting forever. */ - save_flags(flags); - sti(); + spin_unlock_irq(&io_request_lock); while (!info->internal_done) { /* - * If we don't have an IRQ, then we must - * poll the card for it's interrupt, and - * use that to call this driver's interrupt - * routine. That way, we keep the command - * progressing. + * If we don't have an IRQ, then we must poll the card for + * it's interrupt, and use that to call this driver's + * interrupt routine. That way, we keep the command + * progressing. Maybe we can add some inteligence here + * and go to sleep if we know that the device is going + * to be some time (eg, disconnected). */ - if (info->scsi.irq == NO_IRQ) { - sti(); - while (!(inb(REG_STAT(info)) & STAT_INT)); - cli(); + if (inb(REG_STAT(info)) & STAT_INT) { + spin_lock_irq(&io_request_lock); fas216_intr(info->host); + spin_unlock_irq(&io_request_lock); } } - restore_flags(flags); + spin_lock_irq(&io_request_lock); return SCpnt->result; } -/* Prototype: void fas216_reportstatus(Scsi_Cmnd **SCpntp1, - * Scsi_Cmnd **SCpntp2, int result, int no_report) - * Purpose : pass a result to *SCpntp1, and check if *SCpntp1 = *SCpntp2 - * Params : SCpntp1 - pointer to command to return - * SCpntp2 - pointer to command to check - * result - result to pass back to mid-level done function - * Returns : *SCpntp2 = NULL if *SCpntp1 is the same command - * structure as *SCpntp2. - */ -static void fas216_reportstatus(Scsi_Cmnd **SCpntp1, Scsi_Cmnd **SCpntp2, - int result, int no_report) -{ - Scsi_Cmnd *SCpnt = *SCpntp1; - - if (SCpnt) { - *SCpntp1 = NULL; - - SCpnt->result = result; - if (!no_report || SCpnt != *SCpntp2) - SCpnt->scsi_done(SCpnt); - } - - if (SCpnt == *SCpntp2) - *SCpntp2 = NULL; -} - -/* Function: int fas216_eh_abort(Scsi_Cmnd *SCpnt) - * Purpose : abort this command - * Params : SCpnt - command to abort - * Returns : FAILED if unable to abort - */ -int fas216_eh_abort(Scsi_Cmnd *SCpnt) -{ - return FAILED; -} - -/* Function: int fas216_eh_device_reset(Scsi_Cmnd *SCpnt) - * Purpose : Reset the device associated with this command - * Params : SCpnt - command specifing device to reset - * Returns : FAILED if unable to reset - */ -int fas216_eh_device_reset(Scsi_Cmnd *SCpnt) -{ - return FAILED; -} - -/* Function: int fas216_eh_bus_reset(Scsi_Cmnd *SCpnt) - * Purpose : Reset the complete bus associated with this command - * Params : SCpnt - command specifing bus to reset - * Returns : FAILED if unable to reset - */ -int fas216_eh_bus_reset(Scsi_Cmnd *SCpnt) -{ - return FAILED; -} - -/* Function: int fas216_eh_host_reset(Scsi_Cmnd *SCpnt) - * Purpose : Reset the host associated with this command - * Params : SCpnt - command specifing host to reset - * Returns : FAILED if unable to reset - */ -int fas216_eh_host_reset(Scsi_Cmnd *SCpnt) -{ - return FAILED; -} - -enum res_abort { res_not_running, res_success, res_success_clear, res_snooze }; +enum res_abort { + res_failed, /* unable to abort */ + res_success, /* command on issue queue */ + res_success_clear, /* command marked tgt/lun busy */ + res_hw_abort /* command on disconnected dev */ +}; /* * Prototype: enum res_abort fas216_do_abort(FAS216_Info *info, Scsi_Cmnd *SCpnt) - * Purpose : abort a command on this host + * Purpose : decide how to abort a command * Params : SCpnt - command to abort * Returns : abort status */ static enum res_abort fas216_do_abort(FAS216_Info *info, Scsi_Cmnd *SCpnt) { - enum res_abort res = res_not_running; + enum res_abort res = res_failed; if (queue_removecmd(&info->queues.issue, SCpnt)) { /* * The command was on the issue queue, and has not been * issued yet. We can remove the command from the queue, - * and acknowledge the abort. Neither the devices nor the + * and acknowledge the abort. Neither the device nor the * interface know about the command. */ printk("on issue queue "); @@ -2075,54 +2171,32 @@ fas216_do_abort(FAS216_Info *info, Scsi_Cmnd *SCpnt) res = res_success; } else if (queue_removecmd(&info->queues.disconnected, SCpnt)) { /* - * The command was on the disconnected queue. Simply - * acknowledge the abort condition, and when the target - * reconnects, we will give it an ABORT message. The - * target should then disconnect, and we will clear - * the busylun bit. + * The command was on the disconnected queue. We must + * reconnect with the device if possible, and send it + * an abort message. */ printk("on disconnected queue "); - res = res_success; + res = res_hw_abort; } else if (info->SCpnt == SCpnt) { - unsigned long flags; - printk("executing "); - save_flags(flags); - cli(); switch (info->scsi.phase) { /* * If the interface is idle, and the command is 'disconnectable', - * then it is the same as on the disconnected queue. We simply - * remove all traces of the command. When the target reconnects, - * we will give it an ABORT message since the command could not - * be found. When the target finally disconnects, we will clear - * the busylun bit. + * then it is the same as on the disconnected queue. */ case PHASE_IDLE: if (info->scsi.disconnectable) { info->scsi.disconnectable = 0; info->SCpnt = NULL; - res = res_success; + res = res_hw_abort; } break; - /* - * If the command has connected and done nothing futher, - * simply force a disconnect. We also need to clear the - * busylun bit. - */ - case PHASE_SELECTION: -// info->SCpnt = NULL; -// res = res_success_clear; -// break; - default: - res = res_snooze; break; } - restore_flags(flags); } else if (info->origSCpnt == SCpnt) { /* * The command will be executed next, but a command @@ -2139,17 +2213,18 @@ fas216_do_abort(FAS216_Info *info, Scsi_Cmnd *SCpnt) return res; } -/* Function: int fas216_abort(Scsi_Cmnd *SCpnt) - * Purpose : abort a command if something horrible happens. - * Params : SCpnt - Command that is believed to be causing a problem. - * Returns : one of SCSI_ABORT_ macros. +/* Function: int fas216_eh_abort(Scsi_Cmnd *SCpnt) + * Purpose : abort this command + * Params : SCpnt - command to abort + * Returns : FAILED if unable to abort + * Notes : io_request_lock is taken, and irqs are disabled */ -int fas216_abort(Scsi_Cmnd *SCpnt) +int fas216_eh_abort(Scsi_Cmnd *SCpnt) { FAS216_Info *info = (FAS216_Info *)SCpnt->host->hostdata; - int result = SCSI_ABORT_SNOOZE; + int result = FAILED; - fas216_checkmagic(info, "fas216_abort"); + fas216_checkmagic(info); info->stats.aborts += 1; @@ -2176,29 +2251,23 @@ int fas216_abort(Scsi_Cmnd *SCpnt) */ case res_success: printk("success\n"); - SCpnt->result = DID_ABORT << 16; - SCpnt->scsi_done(SCpnt); - result = SCSI_ABORT_SUCCESS; + result = SUCCESS; break; /* - * We did find the command, but unfortunately we couldn't - * unhook it from ourselves. Wait some more, and if it - * still doesn't complete, reset the interface. + * We need to reconnect to the target and send it an + * ABORT or ABORT_TAG message. We can only do this + * if the bus is free. */ - case res_snooze: - printk("snooze\n"); - result = SCSI_ABORT_SNOOZE; - break; + case res_hw_abort: + /* - * The command could not be found (either because it completed, - * or it got dropped. + * We are unable to abort the command for some reason. */ default: - case res_not_running: - result = SCSI_ABORT_SNOOZE; - printk("not running\n"); + case res_failed: + printk("failed\n"); break; } @@ -2214,7 +2283,7 @@ static void fas216_reset_state(FAS216_Info *info) neg_t sync_state, wide_state; int i; - fas216_checkmagic(info, "fas216_reset_state"); + fas216_checkmagic(info); /* * Clear out all stale info in our state structure @@ -2257,6 +2326,80 @@ static void fas216_reset_state(FAS216_Info *info) info->device[i].sof = 0; info->device[i].wide_xfer = 0; } + + /* + * Drain all commands on disconnected queue + */ + while (queue_remove(&info->queues.disconnected) != NULL); + + /* + * Remove executing commands. + */ + info->SCpnt = NULL; + info->reqSCpnt = NULL; + info->origSCpnt = NULL; +} + +/* Function: int fas216_eh_device_reset(Scsi_Cmnd *SCpnt) + * Purpose : Reset the device associated with this command + * Params : SCpnt - command specifing device to reset + * Returns : FAILED if unable to reset + */ +int fas216_eh_device_reset(Scsi_Cmnd *SCpnt) +{ + FAS216_Info *info = (FAS216_Info *)SCpnt->host->hostdata; + + printk("scsi%d.%c: "__FUNCTION__": called\n", + info->host->host_no, '0' + SCpnt->target); + return FAILED; +} + +/* Function: int fas216_eh_bus_reset(Scsi_Cmnd *SCpnt) + * Purpose : Reset the bus associated with the command + * Params : SCpnt - command specifing bus to reset + * Returns : FAILED if unable to reset + * Notes : io_request_lock is taken, and irqs are disabled + */ +int fas216_eh_bus_reset(Scsi_Cmnd *SCpnt) +{ + FAS216_Info *info = (FAS216_Info *)SCpnt->host->hostdata; + int result = FAILED; + + fas216_checkmagic(info); + + info->stats.bus_resets += 1; + + printk("scsi%d.%c: "__FUNCTION__": resetting bus\n", + info->host->host_no, '0' + SCpnt->target); + + /* + * Attempt to stop all activity on this interface. + */ + outb(info->scsi.cfg[2], REG_CNTL3(info)); + fas216_stoptransfer(info); + + /* + * Clear any pending interrupts + */ + while (inb(REG_STAT(info)) & STAT_INT) + inb(REG_INST(info)); + + /* + * Reset the SCSI bus + */ + outb(CMD_RESETSCSI, REG_CMD(info)); + udelay(5); + + /* + * Clear reset interrupt + */ + if (inb(REG_STAT(info)) & STAT_INT && + inb(REG_INST(info)) & INST_BUSRESET) + result = SUCCESS; + + fas216_reset_state(info); + + return result; } /* Function: void fas216_init_chip(FAS216_Info *info) @@ -2265,8 +2408,6 @@ static void fas216_reset_state(FAS216_Info *info) */ static void fas216_init_chip(FAS216_Info *info) { - fas216_checkmagic(info, "fas216_init_chip"); - outb(fas216_clockrate(info->ifcfg.clockrate), REG_CLKF(info)); outb(info->scsi.cfg[0], REG_CNTL1(info)); outb(info->scsi.cfg[1], REG_CNTL2(info)); @@ -2277,100 +2418,144 @@ static void fas216_init_chip(FAS216_Info *info) outb(info->scsi.cfg[0], REG_CNTL1(info)); } -/* Function: int fas216_reset(Scsi_Cmnd *SCpnt, unsigned int reset_flags) - * Purpose : resets the adapter if something horrible happens. - * Params : SCpnt - Command that is believed to be causing a problem. - * reset_flags - flags indicating reset type that is believed - * to be required. - * Returns : one of SCSI_RESET_ macros, or'd with the SCSI_RESET_*_RESET - * macros. +/* Function: int fas216_eh_host_reset(Scsi_Cmnd *SCpnt) + * Purpose : Reset the host associated with this command + * Params : SCpnt - command specifing host to reset + * Returns : FAILED if unable to reset + * Notes : io_request_lock is taken, and irqs are disabled */ -int fas216_reset(Scsi_Cmnd *SCpnt, unsigned int reset_flags) +int fas216_eh_host_reset(Scsi_Cmnd *SCpnt) { FAS216_Info *info = (FAS216_Info *)SCpnt->host->hostdata; - Scsi_Cmnd *SCptr; - int result = 0; - int synchronous = reset_flags & SCSI_RESET_SYNCHRONOUS; - fas216_checkmagic(info, "fas216_reset"); + fas216_checkmagic(info); + + printk("scsi%d.%c: "__FUNCTION__": resetting host\n", + info->host->host_no, '0' + SCpnt->target); /* - * Validate that command is actually on one of our queues if we're doing - * an asynchronous reset + * Reset the SCSI chip. */ - if (reset_flags & SCSI_RESET_ASYNCHRONOUS && - SCpnt && - info->SCpnt != SCpnt && - info->origSCpnt != SCpnt && - !queue_cmdonqueue(&info->queues.disconnected, SCpnt) && - !queue_cmdonqueue(&info->queues.issue, SCpnt)) { - printk("scsi%d: fas216_reset: asynchronous reset for unknown command\n", - info->host->host_no); - return SCSI_RESET_NOT_RUNNING; - } + outb(CMD_RESETCHIP, REG_CMD(info)); - info->stats.resets += 1; + /* + * Ugly ugly ugly! + * We need to release the io_request_lock and enable + * IRQs if we sleep, but we must relock and disable + * IRQs after the sleep. + */ + spin_unlock_irq(&io_request_lock); + scsi_sleep(5); + spin_lock_irq(&io_request_lock); - print_debug_list(); - printk(KERN_WARNING "scsi%d: reset ", info->host->host_no); - if (SCpnt) - printk("for target %d ", SCpnt->target); + /* + * Release the SCSI reset. + */ + outb(CMD_NOP, REG_CMD(info)); - printk("\n"); + fas216_init_chip(info); - outb(info->scsi.cfg[3], REG_CNTL3(info)); + return SUCCESS; +} - fas216_stoptransfer(info); +#define TYPE_UNKNOWN 0 +#define TYPE_NCR53C90 1 +#define TYPE_NCR53C90A 2 +#define TYPE_NCR53C9x 3 +#define TYPE_Am53CF94 4 +#define TYPE_EmFAS216 5 +#define TYPE_QLFAS216 6 + +static char *chip_types[] = { + "unknown", + "NS NCR53C90", + "NS NCR53C90A", + "NS NCR53C9x", + "AMD Am53CF94", + "Emulex FAS216", + "QLogic FAS216" +}; + +static int fas216_detect_type(FAS216_Info *info) +{ + int family, rev; - switch (reset_flags & (SCSI_RESET_SUGGEST_BUS_RESET | SCSI_RESET_SUGGEST_HOST_RESET)) { - case SCSI_RESET_SUGGEST_BUS_RESET: - outb(CMD_RESETSCSI, REG_CMD(info)); - outb(CMD_NOP, REG_CMD(info)); - result |= SCSI_RESET_BUS_RESET; - break; + /* + * Reset the chip. + */ + outb(CMD_RESETCHIP, REG_CMD(info)); + udelay(50); + outb(CMD_NOP, REG_CMD(info)); - case SCSI_RESET_SUGGEST_HOST_RESET: - outb(CMD_RESETCHIP, REG_CMD(info)); - outb(CMD_NOP, REG_CMD(info)); - result |= SCSI_RESET_HOST_RESET; - break; + /* + * Check to see if control reg 2 is present. + */ + outb(0, REG_CNTL3(info)); + outb(CNTL2_S2FE, REG_CNTL2(info)); - default: - outb(CMD_RESETCHIP, REG_CMD(info)); - outb(CMD_NOP, REG_CMD(info)); - outb(CMD_RESETSCSI, REG_CMD(info)); - result |= SCSI_RESET_HOST_RESET | SCSI_RESET_BUS_RESET; - break; - } + /* + * If we are unable to read back control reg 2 + * correctly, it is not present, and we have a + * NCR53C90. + */ + if ((inb(REG_CNTL2(info)) & (~0xe0)) != CNTL2_S2FE) + return TYPE_NCR53C90; - udelay(300); - fas216_reset_state(info); - fas216_init_chip(info); + /* + * Now, check control register 3 + */ + outb(0, REG_CNTL2(info)); + outb(0, REG_CNTL3(info)); + outb(5, REG_CNTL3(info)); /* - * Signal all commands in progress have been reset + * If we are unable to read the register back + * correctly, we have a NCR53C90A */ - fas216_reportstatus(&info->SCpnt, &SCpnt, DID_RESET << 16, synchronous); + if (inb(REG_CNTL3(info)) != 5) + return TYPE_NCR53C90A; - while ((SCptr = queue_remove(&info->queues.disconnected)) != NULL) - fas216_reportstatus(&SCptr, &SCpnt, DID_RESET << 16, synchronous); + /* + * Now read the ID from the chip. + */ + outb(0, REG_CNTL3(info)); - if (SCpnt) { - /* - * Command not found on disconnected queue, nor currently - * executing command - check pending commands - */ - if (info->origSCpnt == SCpnt) - info->origSCpnt = NULL; + outb(CNTL3_ADIDCHK, REG_CNTL3(info)); + outb(0, REG_CNTL3(info)); - queue_removecmd(&info->queues.issue, SCpnt); + outb(CMD_RESETCHIP, REG_CMD(info)); + udelay(5); + outb(CMD_WITHDMA | CMD_NOP, REG_CMD(info)); - SCpnt->result = DID_RESET << 16; - if (!synchronous) - SCpnt->scsi_done(SCpnt); - } + outb(CNTL2_ENF, REG_CNTL2(info)); + outb(CMD_RESETCHIP, REG_CMD(info)); + udelay(5); + outb(CMD_NOP, REG_CMD(info)); + + rev = inb(REG1_ID(info)); + family = rev >> 3; + rev &= 7; + + switch (family) { + case 0x01: + if (rev == 4) + return TYPE_Am53CF94; + break; + + case 0x02: + switch (rev) { + case 2: + return TYPE_EmFAS216; + case 3: + return TYPE_QLFAS216; + } + break; - return result | SCSI_RESET_SUCCESS; + default: + break; + } + printk("family %x rev %x\n", family, rev); + return TYPE_NCR53C9x; } /* Function: int fas216_init(struct Scsi_Host *instance) @@ -2381,19 +2566,14 @@ int fas216_reset(Scsi_Cmnd *SCpnt, unsigned int reset_flags) int fas216_init(struct Scsi_Host *instance) { FAS216_Info *info = (FAS216_Info *)instance->hostdata; - unsigned long flags; - int target_jiffies; + int type; info->magic_start = MAGIC; - info->magic_end = MAGIC; - - info->host = instance; + info->magic_end = MAGIC; + info->host = instance; info->scsi.cfg[0] = instance->this_id; info->scsi.cfg[1] = CNTL2_ENF | CNTL2_S2FE; info->scsi.cfg[2] = info->ifcfg.cntl3 | CNTL3_ADIDCHK | CNTL3_G2CB; - info->scsi.type = "unknown"; - info->SCpnt = NULL; - fas216_reset_state(info); memset(&info->stats, 0, sizeof(info->stats)); @@ -2407,64 +2587,36 @@ int fas216_init(struct Scsi_Host *instance) return 1; } - outb(CMD_RESETCHIP, REG_CMD(info)); - - outb(0, REG_CNTL3(info)); - outb(CNTL2_S2FE, REG_CNTL2(info)); - - if ((inb(REG_CNTL2(info)) & (~0xe0)) != CNTL2_S2FE) { - info->scsi.type = "NCR53C90"; - } else { - outb(0, REG_CNTL2(info)); - outb(0, REG_CNTL3(info)); - outb(5, REG_CNTL3(info)); - if (inb(REG_CNTL3(info)) != 5) { - info->scsi.type = "NCR53C90A"; - } else { - outb(0, REG_CNTL3(info)); - info->scsi.type = "NCR53C9x"; - } - } - - - outb(CNTL3_ADIDCHK, REG_CNTL3(info)); - outb(0, REG_CNTL3(info)); - - outb(CMD_RESETCHIP, REG_CMD(info)); - outb(CMD_WITHDMA | CMD_NOP, REG_CMD(info)); - outb(CNTL2_ENF, REG_CNTL2(info)); - outb(CMD_RESETCHIP, REG_CMD(info)); - switch (inb(REG1_ID(info))) { - case 12: - info->scsi.type = "Am53CF94"; - break; - default: - break; - } + fas216_reset_state(info); + type = fas216_detect_type(info); + info->scsi.type = chip_types[type]; udelay(300); - /* now for the real initialisation */ + + /* + * Initialise the chip correctly. + */ fas216_init_chip(info); + /* + * Reset the SCSI bus. We don't want to see + * the resulting reset interrupt, so mask it + * out. + */ outb(info->scsi.cfg[0] | CNTL1_DISR, REG_CNTL1(info)); outb(CMD_RESETSCSI, REG_CMD(info)); - /* scsi standard says 250ms */ - target_jiffies = jiffies + (25 * HZ) / 100; - save_flags(flags); - sti(); - - while (time_before(jiffies, target_jiffies)) barrier(); - - restore_flags(flags); + /* + * scsi standard says wait 250ms + */ + spin_unlock_irq(&io_request_lock); + scsi_sleep(5); + spin_lock_irq(&io_request_lock); outb(info->scsi.cfg[0], REG_CNTL1(info)); inb(REG_INST(info)); - /* now for the real initialisation */ - fas216_init_chip(info); - - fas216_checkmagic(info, "fas216_init"); + fas216_checkmagic(info); return 0; } @@ -2479,7 +2631,7 @@ int fas216_release(struct Scsi_Host *instance) { FAS216_Info *info = (FAS216_Info *)instance->hostdata; - fas216_checkmagic(info, "fas216_release"); + fas216_checkmagic(info); outb(CMD_RESETCHIP, REG_CMD(info)); queue_free(&info->queues.disconnected); @@ -2488,19 +2640,67 @@ int fas216_release(struct Scsi_Host *instance) return 0; } +/* + * Function: int fas216_info(FAS216_Info *info, char *buffer) + * Purpose : generate a string containing information about this + * host. + * Params : info - FAS216 host information + * buffer - string buffer to build string + * Returns : size of built string + */ +int fas216_info(FAS216_Info *info, char *buffer) +{ + char *p = buffer; + + p += sprintf(p, "(%s) at port 0x%08lX ", + info->scsi.type, info->host->io_port); + + if (info->host->irq != NO_IRQ) + p += sprintf(p, "irq %d ", info->host->irq); + else + p += sprintf(p, "no irq "); + + if (info->host->dma_channel != NO_DMA) + p += sprintf(p, "dma %d ", info->host->dma_channel); + else + p += sprintf(p, "no dma "); + + return p - buffer; +} + +int fas216_print_host(FAS216_Info *info, char *buffer) +{ + + return sprintf(buffer, + "\n" + "Chip : %s\n" + " Address: 0x%08lX\n" + " IRQ : %d\n" + " DMA : %d\n", + info->scsi.type, info->host->io_port, + info->host->irq, info->host->dma_channel); +} + int fas216_print_stats(FAS216_Info *info, char *buffer) { return sprintf(buffer, - "Queued commands: %-10u Issued commands: %-10u\n" - "Done commands : %-10u Reads : %-10u\n" - "Writes : %-10u Others : %-10u\n" - "Disconnects : %-10u Aborts : %-10u\n" - "Resets : %-10u\n", + "\n" + "Command Statistics:\n" + " Queued : %u\n" + " Issued : %u\n" + " Completed : %u\n" + " Reads : %u\n" + " Writes : %u\n" + " Others : %u\n" + " Disconnects: %u\n" + " Aborts : %u\n" + " Bus resets : %u\n" + " Host resets: %u\n", info->stats.queues, info->stats.removes, info->stats.fins, info->stats.reads, info->stats.writes, info->stats.miscs, info->stats.disconnects, info->stats.aborts, - info->stats.resets); + info->stats.bus_resets, info->stats.host_resets); } int fas216_print_device(FAS216_Info *info, Scsi_Device *scd, char *buffer) @@ -2531,9 +2731,8 @@ int fas216_print_device(FAS216_Info *info, Scsi_Device *scd, char *buffer) return p - buffer; } +EXPORT_SYMBOL(fas216_info); EXPORT_SYMBOL(fas216_init); -EXPORT_SYMBOL(fas216_abort); -EXPORT_SYMBOL(fas216_reset); EXPORT_SYMBOL(fas216_queue_command); EXPORT_SYMBOL(fas216_command); EXPORT_SYMBOL(fas216_intr); @@ -2542,6 +2741,7 @@ EXPORT_SYMBOL(fas216_eh_abort); EXPORT_SYMBOL(fas216_eh_device_reset); EXPORT_SYMBOL(fas216_eh_bus_reset); EXPORT_SYMBOL(fas216_eh_host_reset); +EXPORT_SYMBOL(fas216_print_host); EXPORT_SYMBOL(fas216_print_stats); EXPORT_SYMBOL(fas216_print_device); |