|
@@ -1811,6 +1811,84 @@ void scsi_exit_queue(void)
|
|
|
kmem_cache_destroy(sgp->slab);
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+/**
|
|
|
+ * scsi_mode_select - issue a mode select
|
|
|
+ * @sdev: SCSI device to be queried
|
|
|
+ * @pf: Page format bit (1 == standard, 0 == vendor specific)
|
|
|
+ * @sp: Save page bit (0 == don't save, 1 == save)
|
|
|
+ * @modepage: mode page being requested
|
|
|
+ * @buffer: request buffer (may not be smaller than eight bytes)
|
|
|
+ * @len: length of request buffer.
|
|
|
+ * @timeout: command timeout
|
|
|
+ * @retries: number of retries before failing
|
|
|
+ * @data: returns a structure abstracting the mode header data
|
|
|
+ * @sense: place to put sense data (or NULL if no sense to be collected).
|
|
|
+ * must be SCSI_SENSE_BUFFERSIZE big.
|
|
|
+ *
|
|
|
+ * Returns zero if successful; negative error number or scsi
|
|
|
+ * status on error
|
|
|
+ *
|
|
|
+ */
|
|
|
+int
|
|
|
+scsi_mode_select(struct scsi_device *sdev, int pf, int sp, int modepage,
|
|
|
+ unsigned char *buffer, int len, int timeout, int retries,
|
|
|
+ struct scsi_mode_data *data, struct scsi_sense_hdr *sshdr)
|
|
|
+{
|
|
|
+ unsigned char cmd[10];
|
|
|
+ unsigned char *real_buffer;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ memset(cmd, 0, sizeof(cmd));
|
|
|
+ cmd[1] = (pf ? 0x10 : 0) | (sp ? 0x01 : 0);
|
|
|
+
|
|
|
+ if (sdev->use_10_for_ms) {
|
|
|
+ if (len > 65535)
|
|
|
+ return -EINVAL;
|
|
|
+ real_buffer = kmalloc(8 + len, GFP_KERNEL);
|
|
|
+ if (!real_buffer)
|
|
|
+ return -ENOMEM;
|
|
|
+ memcpy(real_buffer + 8, buffer, len);
|
|
|
+ len += 8;
|
|
|
+ real_buffer[0] = 0;
|
|
|
+ real_buffer[1] = 0;
|
|
|
+ real_buffer[2] = data->medium_type;
|
|
|
+ real_buffer[3] = data->device_specific;
|
|
|
+ real_buffer[4] = data->longlba ? 0x01 : 0;
|
|
|
+ real_buffer[5] = 0;
|
|
|
+ real_buffer[6] = data->block_descriptor_length >> 8;
|
|
|
+ real_buffer[7] = data->block_descriptor_length;
|
|
|
+
|
|
|
+ cmd[0] = MODE_SELECT_10;
|
|
|
+ cmd[7] = len >> 8;
|
|
|
+ cmd[8] = len;
|
|
|
+ } else {
|
|
|
+ if (len > 255 || data->block_descriptor_length > 255 ||
|
|
|
+ data->longlba)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ real_buffer = kmalloc(4 + len, GFP_KERNEL);
|
|
|
+ if (!real_buffer)
|
|
|
+ return -ENOMEM;
|
|
|
+ memcpy(real_buffer + 4, buffer, len);
|
|
|
+ len += 4;
|
|
|
+ real_buffer[0] = 0;
|
|
|
+ real_buffer[1] = data->medium_type;
|
|
|
+ real_buffer[2] = data->device_specific;
|
|
|
+ real_buffer[3] = data->block_descriptor_length;
|
|
|
+
|
|
|
+
|
|
|
+ cmd[0] = MODE_SELECT;
|
|
|
+ cmd[4] = len;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = scsi_execute_req(sdev, cmd, DMA_TO_DEVICE, real_buffer, len,
|
|
|
+ sshdr, timeout, retries);
|
|
|
+ kfree(real_buffer);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(scsi_mode_select);
|
|
|
+
|
|
|
/**
|
|
|
* scsi_mode_sense - issue a mode sense, falling back from 10 to
|
|
|
* six bytes if necessary.
|
|
@@ -1832,7 +1910,8 @@ void scsi_exit_queue(void)
|
|
|
int
|
|
|
scsi_mode_sense(struct scsi_device *sdev, int dbd, int modepage,
|
|
|
unsigned char *buffer, int len, int timeout, int retries,
|
|
|
- struct scsi_mode_data *data, struct scsi_sense_hdr *sshdr) {
|
|
|
+ struct scsi_mode_data *data, struct scsi_sense_hdr *sshdr)
|
|
|
+{
|
|
|
unsigned char cmd[12];
|
|
|
int use_10_for_ms;
|
|
|
int header_length;
|