📄 scsi.c
字号:
/* * scsi.c Copyright (C) 1992 Drew Eckhardt * Copyright (C) 1993, 1994, 1995 Eric Youngdale * * generic mid-level SCSI driver * Initial versions: Drew Eckhardt * Subsequent revisions: Eric Youngdale * * <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@andante.jic.com or ericy@gnu.ai.mit.edu to * add scatter-gather, multiple outstanding request, and other * enhancements. * * Native multichannel, wide scsi, /proc/scsi and hot plugging * support added by Michael Neuffer <mike@i-connect.net> * * Added request_module("scsi_hostadapter") for kerneld: * (Put an "alias scsi_hostadapter your_hostadapter" in /etc/conf.modules) * Bjorn Ekwall <bj0rn@blox.se> * (changed to kmod) * * Major improvements to the timeout, abort, and reset processing, * as well as performance modifications for large queue depths by * Leonard N. Zubkoff <lnz@dandelion.com> * * Converted cli() code to spinlocks, Ingo Molnar * * Jiffies wrap fixes (host->resetting), 3 Dec 1998 Andrea Arcangeli * * out_of_space + add-single-device work, D. Gilbert (dpg) 990612 */#include <linux/config.h>#include <linux/module.h>#include <linux/sched.h>#include <linux/timer.h>#include <linux/string.h>#include <linux/malloc.h>#include <linux/ioport.h>#include <linux/kernel.h>#include <linux/stat.h>#include <linux/blk.h>#include <linux/interrupt.h>#include <linux/delay.h>#include <linux/init.h>#define __KERNEL_SYSCALLS__#include <linux/unistd.h>#include <asm/system.h>#include <asm/irq.h>#include <asm/dma.h>#include <asm/spinlock.h>#include "scsi.h"#include "hosts.h"#include "constants.h"#ifdef CONFIG_KMOD#include <linux/kmod.h>#endif#ifdef OSKIT#define CONFIG_MODULES#endif#undef USE_STATIC_SCSI_MEMORY/*static const char RCSid[] = "$Header: /vger/u4/cvs/linux/drivers/scsi/scsi.c,v 1.38 1997/01/19 23:07:18 davem Exp $";*//* * Definitions and constants. */#define INTERNAL_ERROR (panic ("Internal error in file %s, line %d.\n", __FILE__, __LINE__))/* * PAGE_SIZE must be a multiple of the sector size (512). True * for all reasonably recent architectures (even the VAX...). */#define SECTOR_SIZE 512#define SECTORS_PER_PAGE (PAGE_SIZE/SECTOR_SIZE)#if SECTORS_PER_PAGE <= 8 typedef unsigned char FreeSectorBitmap;#elif SECTORS_PER_PAGE <= 32 typedef unsigned int FreeSectorBitmap;#else# error You lose.#endif#define MIN_RESET_DELAY (2*HZ)/* Do not call reset on error if we just did a reset within 15 sec. */#define MIN_RESET_PERIOD (15*HZ)/* 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. */#define BLIST_NOLUN 0x01#define BLIST_FORCELUN 0x02#define BLIST_BORKEN 0x04#define BLIST_KEY 0x08#define BLIST_SINGLELUN 0x10#define BLIST_NOTQ 0x20#define BLIST_SPARSELUN 0x40#define BLIST_MAX5LUN 0x80/* * Data declarations. */unsigned long scsi_pid = 0;Scsi_Cmnd * last_cmnd = NULL;/* Command groups 3 and 4 are reserved and should never be used. */const unsigned char scsi_command_size[8] = { 6, 10, 10, 12, 12, 12, 10, 10 };static unsigned long serial_number = 0;static Scsi_Cmnd * scsi_bh_queue_head = NULL;static Scsi_Cmnd * scsi_bh_queue_tail = NULL;static FreeSectorBitmap * dma_malloc_freelist = NULL;static int need_isa_bounce_buffers;static unsigned int dma_sectors = 0;unsigned int scsi_dma_free_sectors = 0;unsigned int scsi_need_isa_buffer = 0;static unsigned char ** dma_malloc_pages = NULL;/* * Note - the initial logging level can be set here to log events at boot time. * After the system is up, you may enable logging via the /proc interface. */unsigned int scsi_logging_level = 0;volatile struct Scsi_Host * host_active = NULL;#if CONFIG_PROC_FS/* * This is the pointer to the /proc/scsi code. * It is only initialized to !=0 if the scsi code is present */struct proc_dir_entry proc_scsi_scsi = { PROC_SCSI_SCSI, 4, "scsi", S_IFREG | S_IRUGO | S_IWUSR, 1, 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL};#endifconst char *const scsi_device_types[MAX_SCSI_DEVICE_CODE] ={ "Direct-Access ", "Sequential-Access", "Printer ", "Processor ", "WORM ", "CD-ROM ", "Scanner ", "Optical Device ", "Medium Changer ", "Communications ", "Unknown ", "Unknown ", "Unknown ", "Enclosure ",};/* * Function prototypes. */static void resize_dma_pool(void);static void print_inquiry(unsigned char *data);extern void scsi_times_out (Scsi_Cmnd * SCpnt);static int scan_scsis_single (int channel,int dev,int lun,int * max_scsi_dev , int * sparse_lun, Scsi_Device ** SDpnt, Scsi_Cmnd * SCpnt, struct Scsi_Host *shpnt, char * scsi_result);void scsi_build_commandblocks(Scsi_Device * SDpnt);static int scsi_unregister_device(struct Scsi_Device_Template * tpnt);/* * These are the interface to the old error handling code. It should go away * someday soon. */extern void scsi_old_done (Scsi_Cmnd *SCpnt);extern void scsi_old_times_out (Scsi_Cmnd * SCpnt);#if CONFIG_PROC_FSextern int (* dispatch_scsi_info_ptr)(int ino, char *buffer, char **start, off_t offset, int length, int inout);extern int dispatch_scsi_info(int ino, char *buffer, char **start, off_t offset, int length, int inout);#endif#define SCSI_BLOCK(DEVICE, HOST) \ ((HOST->block && host_active && HOST != host_active) \ || ((HOST)->can_queue && HOST->host_busy >= HOST->can_queue) \ || ((HOST)->host_blocked) \ || ((DEVICE) != NULL && (DEVICE)->device_blocked) )static void scsi_dump_status(int level);struct dev_info{ const char * vendor; const char * model; const char * revision; /* Latest revision known to be bad. Not used yet */ unsigned flags;};/* * This is what was previously known as the blacklist. The concept * has been expanded so that we can specify other types of things we * need to be aware of. */static struct dev_info device_list[] ={{"Aashima","IMAGERY 2400SP","1.03",BLIST_NOLUN},/* Locks up if polled for lun != 0 */{"CHINON","CD-ROM CDS-431","H42", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */{"CHINON","CD-ROM CDS-535","Q14", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */{"DENON","DRD-25X","V", BLIST_NOLUN}, /* Locks up if probed for lun != 0 */{"HITACHI","DK312C","CM81", BLIST_NOLUN}, /* Responds to all lun - dtg */{"HITACHI","DK314C","CR21" , BLIST_NOLUN}, /* responds to all lun */{"IMS", "CDD521/10","2.06", BLIST_NOLUN}, /* Locks-up when LUN>0 polled. */{"MAXTOR","XT-3280","PR02", BLIST_NOLUN}, /* Locks-up when LUN>0 polled. */{"MAXTOR","XT-4380S","B3C", BLIST_NOLUN}, /* Locks-up when LUN>0 polled. */{"MAXTOR","MXT-1240S","I1.2", BLIST_NOLUN}, /* Locks up when LUN>0 polled */{"MAXTOR","XT-4170S","B5A", BLIST_NOLUN}, /* Locks-up sometimes when LUN>0 polled. */{"MAXTOR","XT-8760S","B7B", BLIST_NOLUN}, /* guess what? */{"MEDIAVIS","RENO CD-ROMX2A","2.03",BLIST_NOLUN},/*Responds to all lun */{"MICROP", "4110", "*", BLIST_NOTQ}, /* Buggy Tagged Queuing */{"NEC","CD-ROM DRIVE:841","1.0", BLIST_NOLUN}, /* Locks-up when LUN>0 polled. */{"PHILIPS", "PCA80SC", "V4-2", BLIST_NOLUN}, /* Responds to all lun */{"RODIME","RO3000S","2.33", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */{"SANYO", "CRD-250S", "1.20", BLIST_NOLUN}, /* causes failed REQUEST SENSE on lun 1 * for aha152x controller, which causes * SCSI code to reset bus.*/{"SEAGATE", "ST157N", "\004|j", BLIST_NOLUN}, /* causes failed REQUEST SENSE on lun 1 * for aha152x controller, which causes * SCSI code to reset bus.*/{"SEAGATE", "ST296","921", BLIST_NOLUN}, /* Responds to all lun */{"SEAGATE","ST1581","6538",BLIST_NOLUN}, /* Responds to all lun */{"SONY","CD-ROM CDU-541","4.3d", BLIST_NOLUN},{"SONY","CD-ROM CDU-55S","1.0i", BLIST_NOLUN},{"SONY","CD-ROM CDU-561","1.7x", BLIST_NOLUN},{"SONY","CD-ROM CDU-8012","*", BLIST_NOLUN},{"TANDBERG","TDC 3600","U07", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */{"TEAC","CD-R55S","1.0H", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */{"TEAC","CD-ROM","1.06", BLIST_NOLUN}, /* causes failed REQUEST SENSE on lun 1 * for seagate controller, which causes * SCSI code to reset bus.*/{"TEXEL","CD-ROM","1.06", BLIST_NOLUN}, /* causes failed REQUEST SENSE on lun 1 * for seagate controller, which causes * SCSI code to reset bus.*/{"QUANTUM","LPS525S","3110", BLIST_NOLUN}, /* Locks sometimes if polled for lun != 0 */{"QUANTUM","PD1225S","3110", BLIST_NOLUN}, /* Locks sometimes if polled for lun != 0 */{"MEDIAVIS","CDR-H93MV","1.31", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */{"SANKYO", "CP525","6.64", BLIST_NOLUN}, /* causes failed REQ SENSE, extra reset */{"HP", "C1750A", "3226", BLIST_NOLUN}, /* scanjet iic */{"HP", "C1790A", "", BLIST_NOLUN}, /* scanjet iip */{"HP", "C2500A", "", BLIST_NOLUN}, /* scanjet iicx */{"YAMAHA", "CDR102", "1.00", BLIST_NOLUN}, /* extra reset */{"RELISYS", "Scorpio", "*", BLIST_NOLUN}, /* responds to all LUN *//* * Other types of devices that have special flags. */{"SONY","CD-ROM CDU-8001","*", BLIST_BORKEN},{"TEXEL","CD-ROM","1.06", BLIST_BORKEN},{"IOMEGA","Io20S *F","*", BLIST_KEY},{"INSITE","Floptical F*8I","*", BLIST_KEY},{"INSITE","I325VM","*", BLIST_KEY},{"NRC","MBR-7","*", BLIST_FORCELUN | BLIST_SINGLELUN},{"NRC","MBR-7.4","*", BLIST_FORCELUN | BLIST_SINGLELUN},{"REGAL","CDC-4X","*", BLIST_MAX5LUN | BLIST_SINGLELUN},{"NAKAMICH","MJ-4.8S","*", BLIST_FORCELUN | BLIST_SINGLELUN},{"NAKAMICH","MJ-5.16S","*", BLIST_FORCELUN | BLIST_SINGLELUN},{"PIONEER","CD-ROM DRM-600","*", BLIST_FORCELUN | BLIST_SINGLELUN},{"PIONEER","CD-ROM DRM-602X","*", BLIST_FORCELUN | BLIST_SINGLELUN},{"PIONEER","CD-ROM DRM-604X","*", BLIST_FORCELUN | BLIST_SINGLELUN},{"EMULEX","MD21/S2 ESDI","*", BLIST_SINGLELUN},{"CANON","IPUBJD","*", BLIST_SPARSELUN},{"nCipher","Fastness Crypto","*", BLIST_FORCELUN},{"NEC","PD-1 ODX654P","*", BLIST_FORCELUN | BLIST_SINGLELUN},{"MATSHITA","PD","*", BLIST_FORCELUN | BLIST_SINGLELUN},{"YAMAHA","CDR100","1.00", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */{"YAMAHA","CDR102","1.00", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */{"iomega","jaz 1GB","J.86", BLIST_NOTQ | BLIST_NOLUN},/* * Must be at end of list... */{NULL, NULL, NULL}};static int get_device_flags(unsigned char * response_data){ int i = 0; unsigned char * pnt; for(i=0; 1; i++){ if(device_list[i].vendor == NULL) return 0; pnt = &response_data[8]; while(*pnt && *pnt == ' ') pnt++; if(memcmp(device_list[i].vendor, pnt, strlen(device_list[i].vendor))) continue; pnt = &response_data[16]; while(*pnt && *pnt == ' ') pnt++; if(memcmp(device_list[i].model, pnt, strlen(device_list[i].model))) continue; return device_list[i].flags; } return 0;}/* * Function: scsi_make_blocked_list * * Purpose: Build linked list of hosts that require blocking. * * Arguments: None. * * Returns: Nothing * * Notes: Blocking is sort of a hack that is used to prevent more than one * host adapter from being active at one time. This is used in cases * where the ISA bus becomes unreliable if you have more than one * host adapter really pumping data through. * * We spent a lot of time examining the problem, and I *believe* that * the problem is bus related as opposed to being a driver bug. * * The blocked list is used as part of the synchronization object * that we use to ensure that only one host is active at one time. * I (ERY) would like to make this go away someday, but this would * require that we have a recursive mutex object. */void scsi_make_blocked_list(void) { int block_count = 0, index; struct Scsi_Host * sh[128], * shpnt; /* * Create a circular linked list from the scsi hosts which have * the "wish_block" field in the Scsi_Host structure set. * The blocked list should include all the scsi hosts using ISA DMA. * In some systems, using two dma channels simultaneously causes * unpredictable results. * Among the scsi hosts in the blocked list, only one host at a time * is allowed to have active commands queued. The transition from * one active host to the next one is allowed only when host_busy == 0 * for the active host (which implies host_busy == 0 for all the hosts * in the list). Moreover for block devices the transition to a new * active host is allowed only when a request is completed, since a * block device request can be divided into multiple scsi commands * (when there are few sg lists or clustering is disabled). * * (DB, 4 Feb 1995) */ host_active = NULL; for(shpnt=scsi_hostlist; shpnt; shpnt = shpnt->next) {#if 0 /* * Is this is a candidate for the blocked list? * Useful to put into the blocked list all the hosts whose driver * does not know about the host->block feature. */ if (shpnt->unchecked_isa_dma) shpnt->wish_block = 1;#endif if (shpnt->wish_block) sh[block_count++] = shpnt; } if (block_count == 1) sh[0]->block = NULL; else if (block_count > 1) { for(index = 0; index < block_count - 1; index++) { sh[index]->block = sh[index + 1]; printk("scsi%d : added to blocked host list.\n", sh[index]->host_no); } sh[block_count - 1]->block = sh[0]; printk("scsi%d : added to blocked host list.\n", sh[index]->host_no); }}static void scan_scsis_done (Scsi_Cmnd * SCpnt){ SCSI_LOG_MLCOMPLETE(1,printk ("scan_scsis_done(%p, %06x)\n", SCpnt->host, SCpnt->result)); SCpnt->request.rq_status = RQ_SCSI_DONE; if (SCpnt->request.sem != NULL) up(SCpnt->request.sem);}__initfunc(void scsi_logging_setup(char *str, int *ints)){ if (ints[0] != 1) { printk("scsi_logging_setup : usage scsi_logging_level=n "
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -