diff options
Diffstat (limited to 'drivers/scsi/scsi.c')
-rw-r--r-- | drivers/scsi/scsi.c | 62 |
1 files changed, 62 insertions, 0 deletions
diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index c03814ce23ca..c4bf99a842f3 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -652,6 +652,68 @@ void scsi_cdl_check(struct scsi_device *sdev) } /** + * scsi_cdl_enable - Enable or disable a SCSI device supports for Command + * Duration Limits + * @sdev: The target device + * @enable: the target state + */ +int scsi_cdl_enable(struct scsi_device *sdev, bool enable) +{ + struct scsi_mode_data data; + struct scsi_sense_hdr sshdr; + struct scsi_vpd *vpd; + bool is_ata = false; + char buf[64]; + int ret; + + if (!sdev->cdl_supported) + return -EOPNOTSUPP; + + rcu_read_lock(); + vpd = rcu_dereference(sdev->vpd_pg89); + if (vpd) + is_ata = true; + rcu_read_unlock(); + + /* + * For ATA devices, CDL needs to be enabled with a SET FEATURES command. + */ + if (is_ata) { + char *buf_data; + int len; + + ret = scsi_mode_sense(sdev, 0x08, 0x0a, 0xf2, buf, sizeof(buf), + 5 * HZ, 3, &data, NULL); + if (ret) + return -EINVAL; + + /* Enable CDL using the ATA feature page */ + len = min_t(size_t, sizeof(buf), + data.length - data.header_length - + data.block_descriptor_length); + buf_data = buf + data.header_length + + data.block_descriptor_length; + if (enable) + buf_data[4] = 0x02; + else + buf_data[4] = 0; + + ret = scsi_mode_select(sdev, 1, 0, buf_data, len, 5 * HZ, 3, + &data, &sshdr); + if (ret) { + if (scsi_sense_valid(&sshdr)) + scsi_print_sense_hdr(sdev, + dev_name(&sdev->sdev_gendev), &sshdr); + return ret; + } + } + + sdev->cdl_enable = enable; + + return 0; +} + +/** * scsi_device_get - get an additional reference to a scsi_device * @sdev: device to get a reference to * |