scsi_scan.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,352 行 · 第 1/3 页
C
1,352 行
/* * scsi_scan.c * * Copyright (C) 2000 Eric Youngdale, * Copyright (C) 2002 Patrick Mansfield * * The general scanning/probing algorithm is as follows, exceptions are * made to it depending on device specific flags, compilation options, and * global variable (boot or module load time) settings. * * A specific LUN is scanned via an INQUIRY command; if the LUN has a * device attached, a Scsi_Device is allocated and setup for it. * * For every id of every channel on the given host: * * Scan LUN 0; if the target responds to LUN 0 (even if there is no * device or storage attached to LUN 0): * * If LUN 0 has a device attached, allocate and setup a * Scsi_Device for it. * * If target is SCSI-3 or up, issue a REPORT LUN, and scan * all of the LUNs returned by the REPORT LUN; else, * sequentially scan LUNs up until some maximum is reached, * or a LUN is seen that cannot have a device attached to it. */#include <linux/config.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/init.h>#include <linux/blkdev.h>#include <asm/semaphore.h>#include <scsi/scsi.h>#include <scsi/scsi_device.h>#include <scsi/scsi_driver.h>#include <scsi/scsi_devinfo.h>#include <scsi/scsi_host.h>#include <scsi/scsi_request.h>#include <scsi/scsi_transport.h>#include "scsi_priv.h"#include "scsi_logging.h"#define ALLOC_FAILURE_MSG KERN_ERR "%s: Allocation failure during" \ " SCSI scanning, some SCSI devices might not be configured\n"/* * Default timeout */#define SCSI_TIMEOUT (2*HZ)/* * Prefix values for the SCSI id's (stored in driverfs name field) */#define SCSI_UID_SER_NUM 'S'#define SCSI_UID_UNKNOWN 'Z'/* * Return values of some of the scanning functions. * * SCSI_SCAN_NO_RESPONSE: no valid response received from the target, this * includes allocation or general failures preventing IO from being sent. * * SCSI_SCAN_TARGET_PRESENT: target responded, but no device is available * on the given LUN. * * SCSI_SCAN_LUN_PRESENT: target responded, and a device is available on a * given LUN. */#define SCSI_SCAN_NO_RESPONSE 0#define SCSI_SCAN_TARGET_PRESENT 1#define SCSI_SCAN_LUN_PRESENT 2static char *scsi_null_device_strs = "nullnullnullnull";#define MAX_SCSI_LUNS 512#ifdef CONFIG_SCSI_MULTI_LUNstatic unsigned int max_scsi_luns = MAX_SCSI_LUNS;#elsestatic unsigned int max_scsi_luns = 1;#endifmodule_param_named(max_luns, max_scsi_luns, int, S_IRUGO|S_IWUSR);MODULE_PARM_DESC(max_luns, "last scsi LUN (should be between 1 and 2^32-1)");/* * max_scsi_report_luns: the maximum number of LUNS that will be * returned from the REPORT LUNS command. 8 times this value must * be allocated. In theory this could be up to an 8 byte value, but * in practice, the maximum number of LUNs suppored by any device * is about 16k. */static unsigned int max_scsi_report_luns = 511;module_param_named(max_report_luns, max_scsi_report_luns, int, S_IRUGO|S_IWUSR);MODULE_PARM_DESC(max_report_luns, "REPORT LUNS maximum number of LUNS received (should be" " between 1 and 16384)");static unsigned int scsi_inq_timeout = SCSI_TIMEOUT/HZ+3;module_param_named(inq_timeout, scsi_inq_timeout, int, S_IRUGO|S_IWUSR);MODULE_PARM_DESC(inq_timeout, "Timeout (in seconds) waiting for devices to answer INQUIRY." " Default is 5. Some non-compliant devices need more.");/** * scsi_unlock_floptical - unlock device via a special MODE SENSE command * @sreq: used to send the command * @result: area to store the result of the MODE SENSE * * Description: * Send a vendor specific MODE SENSE (not a MODE SELECT) command using * @sreq to unlock a device, storing the (unused) results into result. * Called for BLIST_KEY devices. **/static void scsi_unlock_floptical(struct scsi_request *sreq, unsigned char *result){ unsigned char scsi_cmd[MAX_COMMAND_SIZE]; printk(KERN_NOTICE "scsi: unlocking floptical drive\n"); scsi_cmd[0] = MODE_SENSE; scsi_cmd[1] = 0; scsi_cmd[2] = 0x2e; scsi_cmd[3] = 0; scsi_cmd[4] = 0x2a; /* size */ scsi_cmd[5] = 0; sreq->sr_cmd_len = 0; sreq->sr_data_direction = DMA_FROM_DEVICE; scsi_wait_req(sreq, scsi_cmd, result, 0x2a /* size */, SCSI_TIMEOUT, 3);}/** * print_inquiry - printk the inquiry information * @inq_result: printk this SCSI INQUIRY * * Description: * printk the vendor, model, and other information found in the * INQUIRY data in @inq_result. * * Notes: * Remove this, and replace with a hotplug event that logs any * relevant information. **/static void print_inquiry(unsigned char *inq_result){ int i; printk(KERN_NOTICE " Vendor: "); for (i = 8; i < 16; i++) if (inq_result[i] >= 0x20 && i < inq_result[4] + 5) printk("%c", inq_result[i]); else printk(" "); printk(" Model: "); for (i = 16; i < 32; i++) if (inq_result[i] >= 0x20 && i < inq_result[4] + 5) printk("%c", inq_result[i]); else printk(" "); printk(" Rev: "); for (i = 32; i < 36; i++) if (inq_result[i] >= 0x20 && i < inq_result[4] + 5) printk("%c", inq_result[i]); else printk(" "); printk("\n"); i = inq_result[0] & 0x1f; printk(KERN_NOTICE " Type: %s ", i < MAX_SCSI_DEVICE_CODE ? scsi_device_types[i] : "Unknown "); printk(" ANSI SCSI revision: %02x", inq_result[2] & 0x07); if ((inq_result[2] & 0x07) == 1 && (inq_result[3] & 0x0f) == 1) printk(" CCS\n"); else printk("\n");}/** * scsi_alloc_sdev - allocate and setup a scsi_Device * * Description: * Allocate, initialize for io, and return a pointer to a scsi_Device. * Stores the @shost, @channel, @id, and @lun in the scsi_Device, and * adds scsi_Device to the appropriate list. * * Return value: * scsi_Device pointer, or NULL on failure. **/static struct scsi_device *scsi_alloc_sdev(struct Scsi_Host *shost, uint channel, uint id, uint lun, void *hostdata){ struct scsi_device *sdev, *device; unsigned long flags; sdev = kmalloc(sizeof(*sdev) + shost->transportt->size, GFP_ATOMIC); if (!sdev) goto out; memset(sdev, 0, sizeof(*sdev)); sdev->vendor = scsi_null_device_strs; sdev->model = scsi_null_device_strs; sdev->rev = scsi_null_device_strs; sdev->host = shost; sdev->id = id; sdev->lun = lun; sdev->channel = channel; sdev->sdev_state = SDEV_CREATED; INIT_LIST_HEAD(&sdev->siblings); INIT_LIST_HEAD(&sdev->same_target_siblings); INIT_LIST_HEAD(&sdev->cmd_list); INIT_LIST_HEAD(&sdev->starved_entry); spin_lock_init(&sdev->list_lock); /* usually NULL and set by ->slave_alloc instead */ sdev->hostdata = hostdata; /* if the device needs this changing, it may do so in the * slave_configure function */ sdev->max_device_blocked = SCSI_DEFAULT_DEVICE_BLOCKED; /* * Some low level driver could use device->type */ sdev->type = -1; /* * Assume that the device will have handshaking problems, * and then fix this field later if it turns out it * doesn't */ sdev->borken = 1; spin_lock_init(&sdev->sdev_lock); sdev->request_queue = scsi_alloc_queue(sdev); if (!sdev->request_queue) goto out_free_dev; sdev->request_queue->queuedata = sdev; scsi_adjust_queue_depth(sdev, 0, sdev->host->cmd_per_lun); if (shost->hostt->slave_alloc) { if (shost->hostt->slave_alloc(sdev)) goto out_free_queue; } if (shost->transportt->setup) { if (shost->transportt->setup(sdev)) goto out_cleanup_slave; } if (get_device(&sdev->host->shost_gendev)) { device_initialize(&sdev->sdev_gendev); sdev->sdev_gendev.parent = &sdev->host->shost_gendev; sdev->sdev_gendev.bus = &scsi_bus_type; sdev->sdev_gendev.release = scsi_device_dev_release; sprintf(sdev->sdev_gendev.bus_id,"%d:%d:%d:%d", sdev->host->host_no, sdev->channel, sdev->id, sdev->lun); class_device_initialize(&sdev->sdev_classdev); sdev->sdev_classdev.dev = &sdev->sdev_gendev; sdev->sdev_classdev.class = &sdev_class; snprintf(sdev->sdev_classdev.class_id, BUS_ID_SIZE, "%d:%d:%d:%d", sdev->host->host_no, sdev->channel, sdev->id, sdev->lun); class_device_initialize(&sdev->transport_classdev); sdev->transport_classdev.dev = &sdev->sdev_gendev; sdev->transport_classdev.class = sdev->host->transportt->class; snprintf(sdev->transport_classdev.class_id, BUS_ID_SIZE, "%d:%d:%d:%d", sdev->host->host_no, sdev->channel, sdev->id, sdev->lun); } else goto out_cleanup_transport; /* * If there are any same target siblings, add this to the * sibling list */ spin_lock_irqsave(shost->host_lock, flags); list_for_each_entry(device, &shost->__devices, siblings) { if (device->id == sdev->id && device->channel == sdev->channel) { list_add_tail(&sdev->same_target_siblings, &device->same_target_siblings); sdev->scsi_level = device->scsi_level; break; } } /* * If there wasn't another lun already configured at this * target, then default this device to SCSI_2 until we * know better */ if (!sdev->scsi_level) sdev->scsi_level = SCSI_2; list_add_tail(&sdev->siblings, &shost->__devices); spin_unlock_irqrestore(shost->host_lock, flags); return sdev;out_cleanup_transport: if (shost->transportt->cleanup) shost->transportt->cleanup(sdev);out_cleanup_slave: if (shost->hostt->slave_destroy) shost->hostt->slave_destroy(sdev);out_free_queue: scsi_free_queue(sdev->request_queue);out_free_dev: kfree(sdev);out: printk(ALLOC_FAILURE_MSG, __FUNCTION__); return NULL;}/** * scsi_probe_lun - probe a single LUN using a SCSI INQUIRY * @sreq: used to send the INQUIRY * @inq_result: area to store the INQUIRY result * @bflags: store any bflags found here * * Description: * Probe the lun associated with @sreq using a standard SCSI INQUIRY; * * If the INQUIRY is successful, sreq->sr_result is zero and: the * INQUIRY data is in @inq_result; the scsi_level and INQUIRY length * are copied to the Scsi_Device at @sreq->sr_device (sdev); * any flags value is stored in *@bflags. **/static void scsi_probe_lun(struct scsi_request *sreq, char *inq_result, int *bflags){ struct scsi_device *sdev = sreq->sr_device; /* a bit ugly */ unsigned char scsi_cmd[MAX_COMMAND_SIZE]; int possible_inq_resp_len; int count = 0; *bflags = 0; repeat_inquiry: SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: INQUIRY to host %d" " channel %d id %d lun %d\n", sdev->host->host_no, sdev->channel, sdev->id, sdev->lun)); memset(scsi_cmd, 0, 6); scsi_cmd[0] = INQUIRY; scsi_cmd[4] = 36; /* issue conservative alloc_length */ sreq->sr_cmd_len = 0; sreq->sr_data_direction = DMA_FROM_DEVICE; memset(inq_result, 0, 36); scsi_wait_req(sreq, (void *) scsi_cmd, (void *) inq_result, 36, HZ/2 + HZ*scsi_inq_timeout, 3); SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: 1st INQUIRY %s with" " code 0x%x\n", sreq->sr_result ? "failed" : "successful", sreq->sr_result)); ++count; if (sreq->sr_result) { if ((driver_byte(sreq->sr_result) & DRIVER_SENSE) != 0 && (sreq->sr_sense_buffer[2] & 0xf) == UNIT_ATTENTION && (sreq->sr_sense_buffer[12] == 0x28 || sreq->sr_sense_buffer[12] == 0x29) && sreq->sr_sense_buffer[13] == 0) { /* not-ready to ready transition or power-on - good */ /* dpg: bogus? INQUIRY never returns UNIT_ATTENTION */ /* Supposedly, but many buggy devices do so anyway */ if (count < 3) goto repeat_inquiry; } /* * assume no peripheral if any other sort of error */ return; } /* * Get any flags for this device. * * XXX add a bflags to Scsi_Device, and replace the corresponding * bit fields in Scsi_Device, so bflags need not be passed as an * argument. */ *bflags |= scsi_get_device_flags(sdev, &inq_result[8], &inq_result[16]); possible_inq_resp_len = (unsigned char) inq_result[4] + 5; if (BLIST_INQUIRY_36 & *bflags) possible_inq_resp_len = 36; else if (BLIST_INQUIRY_58 & *bflags) possible_inq_resp_len = 58; else if (possible_inq_resp_len > 255) possible_inq_resp_len = 36; /* sanity */ if (possible_inq_resp_len > 36) { /* do additional INQUIRY */ memset(scsi_cmd, 0, 6); scsi_cmd[0] = INQUIRY; scsi_cmd[4] = (unsigned char) possible_inq_resp_len; sreq->sr_cmd_len = 0; sreq->sr_data_direction = DMA_FROM_DEVICE; /* * re-zero inq_result just to be safe. */ memset(inq_result, 0, possible_inq_resp_len); scsi_wait_req(sreq, (void *) scsi_cmd, (void *) inq_result, possible_inq_resp_len, (1+scsi_inq_timeout)*(HZ/2), 3); SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: 2nd INQUIRY" " %s with code 0x%x\n", sreq->sr_result ? "failed" : "successful", sreq->sr_result)); if (sreq->sr_result) { /* if the longer inquiry has failed, flag the device * as only accepting 36 byte inquiries and retry the * 36 byte inquiry */ printk(KERN_INFO "scsi scan: %d byte inquiry failed" " with code %d. Consider BLIST_INQUIRY_36 for" " this device\n", possible_inq_resp_len, sreq->sr_result); *bflags = BLIST_INQUIRY_36; goto repeat_inquiry; } /* * The INQUIRY can change, this means the length can change. */ possible_inq_resp_len = (unsigned char) inq_result[4] + 5; if (BLIST_INQUIRY_58 & *bflags) possible_inq_resp_len = 58; else if (possible_inq_resp_len > 255) possible_inq_resp_len = 36; /* sanity */ } sdev->inquiry_len = possible_inq_resp_len; /* * XXX Abort if the response length is less than 36? If less than
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?