?? scsi.c
字號:
/* * scsi.c Copyright (C) 1992 Drew Eckhardt * generic mid-level SCSI driver by * Drew Eckhardt * * <drew@colorado.edu> * * Bug correction thanks go to : * Rik Faith <faith@cs.unc.edu> * Tommy Thorn <tthorn> * Thomas Wuensche <tw@fgb1.fgb.mw.tu-muenchen.de> * * Modified by Eric Youngdale eric@tantalus.nrl.navy.mil to * add scatter-gather, multiple outstanding request, and other * enhancements. */#include <asm/system.h>#include <linux/sched.h>#include <linux/timer.h>#include <linux/string.h>#include "../blk.h"#include "scsi.h"#include "hosts.h"/*static const char RCSid[] = "$Header: /usr/src/linux/kernel/blk_drv/scsi/RCS/scsi.c,v 1.1 1992/07/24 06:27:38 root Exp root $";*/#define INTERNAL_ERROR (printk ("Internal error in file %s, line %d.\n", __FILE__, __LINE__), panic(""))static void scsi_done (Scsi_Cmnd *SCpnt);static int update_timeout (Scsi_Cmnd *, int);static void print_inquiry(unsigned char *data);static void scsi_times_out (Scsi_Cmnd * SCpnt);static int time_start;static int time_elapsed;/* global variables : NR_SCSI_DEVICES is the number of SCSI devices we have detected, scsi_devices an array of these specifing the address for each (host, id, LUN)*/ int NR_SCSI_DEVICES=0;Scsi_Device * scsi_devices = NULL;static unsigned char generic_sense[6] = {REQUEST_SENSE, 0,0,0, 255, 0};/* We make this not static so that we can read the array with gdb. *//* static */ Scsi_Cmnd * last_cmnd = NULL;/* * As the scsi do command functions are inteligent, and may need to * redo a command, we need to keep track of the last command * executed on each one. */#define WAS_RESET 0x01#define WAS_TIMEDOUT 0x02#define WAS_SENSE 0x04#define IS_RESETTING 0x08extern int last_reset[];/* * This is the number of clock ticks we should wait before we time out * and abort the command. This is for where the scsi.c module generates * the command, not where it originates from a higher level, in which * case the timeout is specified there. * * ABORT_TIMEOUT and RESET_TIMEOUT are the timeouts for RESET and ABORT * respectively. */#ifdef DEBUG #define SCSI_TIMEOUT 500#else #define SCSI_TIMEOUT 100#endif#ifdef DEBUG #define SENSE_TIMEOUT SCSI_TIMEOUT #define ABORT_TIMEOUT SCSI_TIMEOUT #define RESET_TIMEOUT SCSI_TIMEOUT#else #define SENSE_TIMEOUT 50 #define RESET_TIMEOUT 50 #define ABORT_TIMEOUT 50 #define MIN_RESET_DELAY 25#endif/* The following devices are known not to tolerate a lun != 0 scan for one reason or another. Some will respond to all luns, others will lock up. */ struct blist{ char * vendor; char * model; char * revision; /* Latest revision known to be bad. Not used yet */ };#if 0static struct blist blacklist[] = {{"TANDBERG","TDC 3600","U07"}, /* Locks up if polled for lun != 0 */ {"SEAGATE","ST296","921"}, /* Responds to all lun */ {"NEWBURY","NDR3380S","2.10"}, /* Responds to all lun */ {NULL, NULL, NULL}}; static int blacklisted(char * response_data){ int i = 0; for(i=0; 1; i++){ if(blacklist[i].vendor == NULL) return 0; if(strncmp(blacklist[i].vendor, &response_data[8], strlen(blacklist[i].vendor))) continue; if(strncmp(blacklist[i].model, &response_data[16], strlen(blacklist[i].model))) continue; return 1; }; };#endif/* * As the actual SCSI command runs in the background, we must set up a * flag that tells scan_scsis() when the result it has is valid. * scan_scsis can set the_result to -1, and watch for it to become the * actual return code for that call. the scan_scsis_done function() is * our user specified completion function that is passed on to the * scsi_do_cmd() function. */static volatile int in_scan = 0;static int the_result;static void scan_scsis_done (Scsi_Cmnd * SCpnt) { #ifdef DEBUG printk ("scan_scsis_done(%d, %06x)\n\r", SCpnt->host, SCpnt->result);#endif SCpnt->request.dev = 0xfffe; }/* * Detecting SCSI devices : * We scan all present host adapter's busses, from ID 0 to ID 6. * We use the INQUIRY command, determine device type, and pass the ID / * lun address of all sequential devices to the tape driver, all random * devices to the disk driver. */static void scan_scsis (void){ int host_nr , dev, lun, type; unsigned char scsi_cmd [12]; unsigned char scsi_result [256]; Scsi_Cmnd SCmd; ++in_scan; lun = 0; SCmd.next = NULL; SCmd.prev = NULL; for (host_nr = 0; host_nr < max_scsi_hosts; ++host_nr) if (scsi_hosts[host_nr].present) { host_queue[host_nr] = &SCmd; /* We need this so that commands can time out */ for (dev = 0; dev < 8; ++dev) if (scsi_hosts[host_nr].this_id != dev) for (lun = 0; lun < 8; ++lun) { scsi_devices[NR_SCSI_DEVICES].host_no = host_nr; scsi_devices[NR_SCSI_DEVICES].id = dev; scsi_devices[NR_SCSI_DEVICES].lun = lun; scsi_devices[NR_SCSI_DEVICES].index = NR_SCSI_DEVICES; scsi_devices[NR_SCSI_DEVICES].device_wait = NULL; scsi_cmd[0] = TEST_UNIT_READY; scsi_cmd[1] = lun << 5; scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[5] = 0; scsi_cmd[4] = 0; SCmd.host = host_nr; SCmd.target = dev; SCmd.lun = lun; SCmd.request.dev = 0xffff; /* Mark not busy */ SCmd.use_sg = 0; scsi_do_cmd (&SCmd, (void *) scsi_cmd, (void *) scsi_result, 256, scan_scsis_done, SCSI_TIMEOUT + 400, 3); while (SCmd.request.dev != 0xfffe); if(SCmd.result) { if ((driver_byte(SCmd.result) & DRIVER_SENSE) && ((SCmd.sense_buffer[0] & 0x70) >> 4) == 7) { if (SCmd.sense_buffer[2] &0xe0) continue; /* No devices here... */ if((SCmd.sense_buffer[2] & 0xf != NOT_READY) && (SCmd.sense_buffer[2] & 0xf != UNIT_ATTENTION)) continue; } else break; }; /* * Build an INQUIRY command block. */ scsi_cmd[0] = INQUIRY; scsi_cmd[2] = 0; scsi_cmd[3] = 0; scsi_cmd[4] = 255; scsi_cmd[5] = 0; SCmd.request.dev = 0xffff; /* Mark not busy */ scsi_do_cmd (&SCmd, (void *) scsi_cmd, (void *) scsi_result, 256, scan_scsis_done, SCSI_TIMEOUT, 3); while (SCmd.request.dev != 0xfffe); the_result = SCmd.result; if(the_result) break; /* skip other luns on this device */ if (!the_result) { scsi_devices[NR_SCSI_DEVICES]. removable = (0x80 & scsi_result[1]) >> 7; scsi_devices[NR_SCSI_DEVICES]. changed = 0; scsi_devices[NR_SCSI_DEVICES]. access_count = 0; scsi_devices[NR_SCSI_DEVICES]. busy = 0;/* * Currently, all sequential devices are assumed to be tapes, * all random devices disk, with the appropriate read only * flags set for ROM / WORM treated as RO. */ switch (type = scsi_result[0]) { case TYPE_TAPE : case TYPE_DISK : case TYPE_MOD : scsi_devices[NR_SCSI_DEVICES].writeable = 1; break; case TYPE_WORM : case TYPE_ROM : scsi_devices[NR_SCSI_DEVICES].writeable = 0; break; default : type = -1; } scsi_devices[NR_SCSI_DEVICES].random = (type == TYPE_TAPE) ? 0 : 1; scsi_devices[NR_SCSI_DEVICES].type = type; if (type != -1) { print_inquiry(scsi_result); switch(type){ case TYPE_TAPE: printk("Detected scsi tape st%d at scsi%d, id %d, lun %d\n", MAX_ST, host_nr , dev, lun); if(NR_ST != -1) ++MAX_ST; break; case TYPE_ROM: printk("Detected scsi CD-ROM sr%d at scsi%d, id %d, lun %d\n", MAX_SR, host_nr , dev, lun); if(NR_SR != -1) ++MAX_SR; break; case TYPE_DISK: case TYPE_MOD: printk("Detected scsi disk sd%d at scsi%d, id %d, lun %d\n", MAX_SD, host_nr , dev, lun); if(NR_SD != -1) ++MAX_SD; break; default: break; }; scsi_devices[NR_SCSI_DEVICES].scsi_level = scsi_result[2] & 0x07; if (scsi_devices[NR_SCSI_DEVICES].scsi_level >= 2 || (scsi_devices[NR_SCSI_DEVICES].scsi_level == 1 && (scsi_result[3] & 0x0f) == 1)) scsi_devices[NR_SCSI_DEVICES].scsi_level++; /* These devices need this "key" to unlock the device so we can use it */ if(strncmp("INSITE", &scsi_result[8], 6) == 0 && strncmp("Floptical F*8I", &scsi_result[16], 16) == 0) { scsi_cmd[0] = MODE_SENSE; scsi_cmd[1] = (lun << 5) & 0xe0; scsi_cmd[2] = 0x2e; scsi_cmd[3] = 0; scsi_cmd[4] = 0x2a; scsi_cmd[5] = 0; SCmd.request.dev = 0xffff; /* Mark not busy */ scsi_do_cmd (&SCmd, (void *) scsi_cmd, (void *) scsi_result, 0x2a, scan_scsis_done, SCSI_TIMEOUT, 3); while (SCmd.request.dev != 0xfffe); }; ++NR_SCSI_DEVICES;#if 0 /* Some scsi devices cannot be polled for lun != 0 due to firmware bugs */ if(blacklisted(scsi_result)) break;#endif /* Some scsi-1 peripherals do not handle lun != 0. I am assuming that scsi-2 peripherals do better */ if((scsi_result[2] & 0x07) == 1 && (scsi_result[3] & 0x0f) == 0) break; } } /* if result == DID_OK ends */ } /* for lun ends */ host_queue[host_nr] = NULL; /* No longer needed here */ } /* if present */ for (host_nr = 0; host_nr < max_scsi_hosts; ++host_nr) if (scsi_hosts[host_nr].present) if(host_queue[host_nr]) panic("host_queue not cleared"); printk("scsi : detected "); if(NR_SD != -1) printk("%d SCSI disk%s ", MAX_SD, (MAX_SD != 1) ? "s" : ""); if(NR_ST != -1) printk("%d tape%s ", MAX_ST, (MAX_ST != 1) ? "s" : ""); if(NR_SR != -1) printk("%d CD-ROM drive%s ", MAX_SR, (MAX_SR != 1) ? "s" : ""); printk("total.\n"); in_scan = 0;} /* scan_scsis ends *//* * Flag bits for the internal_timeout array */#define NORMAL_TIMEOUT 0#define IN_ABORT 1#define IN_RESET 2/* This is our time out function, called when the timer expires for a given host adapter. It will attempt to abort the currently executing command, that failing perform a kernel panic.*/ static void scsi_times_out (Scsi_Cmnd * SCpnt) { switch (SCpnt->internal_timeout & (IN_ABORT | IN_RESET)) { case NORMAL_TIMEOUT: if (!in_scan) printk("SCSI host %d timed out - aborting command \r\n", SCpnt->host); if (!scsi_abort (SCpnt, DID_TIME_OUT)) return; case IN_ABORT: printk("SCSI host %d abort() timed out - reseting \r\n", SCpnt->host); if (!scsi_reset (SCpnt)) return; case IN_RESET: case (IN_ABORT | IN_RESET): printk("Unable to reset scsi host %d\r\n",SCpnt->host); panic(""); default: INTERNAL_ERROR; } }/* This function takes a quick look at a request, and decides if itcan be queued now, or if there would be a stall while waiting forsomething else to finish. This routine assumes that interrupts areturned off when entering the routine. It is the responsibilityof the calling code to ensure that this is the case. */Scsi_Cmnd * request_queueable (struct request * req, int index){ int host; Scsi_Cmnd * SCpnt = NULL; if ((index < 0) || (index > NR_SCSI_DEVICES)) panic ("Index number in allocate_device() is out of range.\n"); if (req && req->dev <= 0) panic("Invalid device in allocate_device"); host = scsi_devices[index].host_no; SCpnt = host_queue[host]; while(SCpnt){ if(SCpnt->target == scsi_devices[index].id && SCpnt->lun == scsi_devices[index].lun) if(SCpnt->request.dev < 0) break; SCpnt = SCpnt->next; }; if (!SCpnt) return NULL; if (scsi_hosts[host].can_queue && host_busy[host] >= scsi_hosts[host].can_queue) return NULL; if (req) { memcpy(&SCpnt->request, req, sizeof(struct request)); req->dev = -1; } else SCpnt->request.dev = 0xffff; /* Busy, but no request */ SCpnt->use_sg = 0; /* Reset the scatter-gather flag */ return SCpnt;}/* This function returns a structure pointer that will be valid forthe device. The wait parameter tells us whether we should wait forthe unit to become free or not. We are also able to tell this routinenot to return a descriptor if the host is unable to accept any morecommands for the time being. We need to keep in mind that there is noguarantee that the host remain not busy. Keep in mind therequest_queueable function also knows the internal allocation schemeof the packets for each device */Scsi_Cmnd * allocate_device (struct request ** reqp, int index, int wait){ int host, dev = -1; struct request * req = NULL; Scsi_Cmnd * SCpnt = NULL; Scsi_Cmnd * SCwait = NULL; if ((index < 0) || (index > NR_SCSI_DEVICES)) panic ("Index number in allocate_device() is out of range.\n"); if (reqp) req = *reqp; if (req && (dev = req->dev) <= 0) panic("Invalid device in allocate_device"); host = scsi_devices[index].host_no; while (1==1){ SCpnt = host_queue[host]; while(SCpnt){ if(SCpnt->target == scsi_devices[index].id && SCpnt->lun == scsi_devices[index].lun) { SCwait = SCpnt; if(SCpnt->request.dev < 0) break; }; SCpnt = SCpnt->next; }; cli(); /* See if this request has already been queued by an interrupt routine */ if (req && ((req->dev < 0) || (req->dev != dev))) return NULL; if (!SCpnt || SCpnt->request.dev >= 0) /* Might have changed */ { sti(); if(!wait) return NULL; if (!SCwait) { printk("Attempt to allocate device index %d, target %d, lun %d\n", index, scsi_devices[index].id ,scsi_devices[index].lun); panic("No device found in allocate_device\n"); }; SCSI_SLEEP(&scsi_devices[SCwait->index].device_wait, (SCwait->request.dev > 0));
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -