{
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;
+ int first_inquiry_len, try_inquiry_len, next_inquiry_len;
+ int response_len = 0;
+ int pass, count;
*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);
+ /* Perform up to 3 passes. The first pass uses a conservative
+ * transfer length of 36 unless sdev->inquiry_len specifies a
+ * different value. */
+ first_inquiry_len = sdev->inquiry_len ? sdev->inquiry_len : 36;
+ try_inquiry_len = first_inquiry_len;
+ pass = 1;
+
+ next_pass:
+ SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: INQUIRY pass %d "
+ "to host %d channel %d id %d lun %d, length %d\n",
+ pass, sdev->host->host_no, sdev->channel,
+ sdev->id, sdev->lun, try_inquiry_len));
+
+ /* Each pass gets up to three chances to ignore Unit Attention */
+ for (count = 0; count < 3; ++count) {
+ memset(scsi_cmd, 0, 6);
+ scsi_cmd[0] = INQUIRY;
+ scsi_cmd[4] = (unsigned char) try_inquiry_len;
+ sreq->sr_cmd_len = 0;
+ sreq->sr_data_direction = DMA_FROM_DEVICE;
- 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;
+ memset(inq_result, 0, try_inquiry_len);
+ scsi_wait_req(sreq, (void *) scsi_cmd, (void *) inq_result,
+ try_inquiry_len,
+ HZ/2 + HZ*scsi_inq_timeout, 3);
+
+ SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: INQUIRY %s "
+ "with code 0x%x\n",
+ sreq->sr_result ? "failed" : "successful",
+ sreq->sr_result));
+
+ if (sreq->sr_result) {
- 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;
+ /* Supposedly, but many buggy devices do so anyway. */
+ if ((driver_byte(sreq->sr_result) & DRIVER_SENSE) &&
+ (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)
+ continue;
}
- /*
- * assume no peripheral if any other sort of error
- */
- return;
+ break;
}
- /*
- * 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 (sreq->sr_result == 0) {
+ response_len = (unsigned char) inq_result[4] + 5;
+ if (response_len > 255)
+ response_len = first_inquiry_len; /* 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.
+ * 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.
*/
- 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;
+ *bflags = scsi_get_device_flags(sdev, &inq_result[8],
+ &inq_result[16]);
+
+ /* When the first pass succeeds we gain information about
+ * what larger transfer lengths might work. */
+ if (pass == 1) {
+ if (BLIST_INQUIRY_36 & *bflags)
+ next_inquiry_len = 36;
+ else if (BLIST_INQUIRY_58 & *bflags)
+ next_inquiry_len = 58;
+ else if (sdev->inquiry_len)
+ next_inquiry_len = sdev->inquiry_len;
+ else
+ next_inquiry_len = response_len;
+
+ /* If more data is available perform the second pass */
+ if (next_inquiry_len > try_inquiry_len) {
+ try_inquiry_len = next_inquiry_len;
+ pass = 2;
+ goto next_pass;
+ }
}
- /*
- * 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 */
+ } else if (pass == 2) {
+ printk(KERN_INFO "scsi scan: %d byte inquiry failed. "
+ "Consider BLIST_INQUIRY_36 for this device\n",
+ try_inquiry_len);
+
+ /* If this pass failed, the third pass goes back and transfers
+ * the same amount as we successfully got in the first pass. */
+ try_inquiry_len = first_inquiry_len;
+ pass = 3;
+ goto next_pass;
}
- sdev->inquiry_len = possible_inq_resp_len;
+ /* If the last transfer attempt got an error, assume the
+ * peripheral doesn't exist or is dead. */
+ if (sreq->sr_result)
+ return;
+
+ /* Don't report any more data than the device says is valid */
+ sdev->inquiry_len = min(try_inquiry_len, response_len);
/*
* XXX Abort if the response length is less than 36? If less than