diff options
author | Mark Lord <liml@rtr.ca> | 2007-08-07 18:08:45 +0200 |
---|---|---|
committer | Jeff Garzik <jeff@garzik.org> | 2007-10-12 20:55:32 +0200 |
commit | baf4fdfaaf5cb9f4fb1c341c8cef60a64e580582 (patch) | |
tree | f26293a36958a6813bf1353cb07afd3d6be6aa0d | |
parent | libata: move ata_altstatus() to pio data xfer functions (diff) | |
download | linux-baf4fdfaaf5cb9f4fb1c341c8cef60a64e580582.tar.xz linux-baf4fdfaaf5cb9f4fb1c341c8cef60a64e580582.zip |
libata: add support for ATA_16 on ATAPI
Add support for issuing ATA_16 passthru commands to ATAPI devices
managed by libata. It requires the previous CDB length fix patch.
A boot/module parameter, "atapi_passthru16=0" can be used to globally
disable this feature, if ever desired.
tj: restructured __ata_scsi_queuecmd() according to Jeff's suggestion.
Signed-off-by: Mark Lord <liml@rtr.ca>
Signed-off-by: Tejun Heo <htejun@gmail.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
-rw-r--r-- | drivers/ata/libata-core.c | 4 | ||||
-rw-r--r-- | drivers/ata/libata-scsi.c | 52 | ||||
-rw-r--r-- | drivers/ata/libata.h | 1 |
3 files changed, 41 insertions, 16 deletions
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index fc0679370638..75ccf46972d8 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -86,6 +86,10 @@ int atapi_dmadir = 0; module_param(atapi_dmadir, int, 0444); MODULE_PARM_DESC(atapi_dmadir, "Enable ATAPI DMADIR bridge support (0=off, 1=on)"); +int atapi_passthru16 = 1; +module_param(atapi_passthru16, int, 0444); +MODULE_PARM_DESC(atapi_passthru16, "Enable ATA_16 passthru for ATAPI devices; on by default (0=off, 1=on)"); + int libata_fua = 0; module_param_named(fua, libata_fua, int, 0444); MODULE_PARM_DESC(fua, "FUA support (0=off, 1=on)"); diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 58051ee40f1a..7d66c986a54c 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -2761,28 +2761,48 @@ static inline int __ata_scsi_queuecmd(struct scsi_cmnd *scmd, void (*done)(struct scsi_cmnd *), struct ata_device *dev) { + u8 scsi_op = scmd->cmnd[0]; + ata_xlat_func_t xlat_func; int rc = 0; - if (unlikely(!scmd->cmd_len || scmd->cmd_len > dev->cdb_len)) { - DPRINTK("bad CDB len=%u, max=%u\n", - scmd->cmd_len, dev->cdb_len); - scmd->result = DID_ERROR << 16; - done(scmd); - return 0; - } - if (dev->class == ATA_DEV_ATA) { - ata_xlat_func_t xlat_func = ata_get_xlat_func(dev, - scmd->cmnd[0]); + if (unlikely(!scmd->cmd_len || scmd->cmd_len > dev->cdb_len)) + goto bad_cdb_len; - if (xlat_func) - rc = ata_scsi_translate(dev, scmd, done, xlat_func); - else - ata_scsi_simulate(dev, scmd, done); - } else - rc = ata_scsi_translate(dev, scmd, done, atapi_xlat); + xlat_func = ata_get_xlat_func(dev, scsi_op); + } else { + if (unlikely(!scmd->cmd_len)) + goto bad_cdb_len; + + xlat_func = NULL; + if (likely((scsi_op != ATA_16) || !atapi_passthru16)) { + /* relay SCSI command to ATAPI device */ + if (unlikely(scmd->cmd_len > dev->cdb_len)) + goto bad_cdb_len; + + xlat_func = atapi_xlat; + } else { + /* ATA_16 passthru, treat as an ATA command */ + if (unlikely(scmd->cmd_len > 16)) + goto bad_cdb_len; + + xlat_func = ata_get_xlat_func(dev, scsi_op); + } + } + + if (xlat_func) + rc = ata_scsi_translate(dev, scmd, done, xlat_func); + else + ata_scsi_simulate(dev, scmd, done); return rc; + + bad_cdb_len: + DPRINTK("bad CDB len=%u, scsi_op=0x%02x, max=%u\n", + scmd->cmd_len, scsi_op, dev->cdb_len); + scmd->result = DID_ERROR << 16; + done(scmd); + return 0; } /** diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index 6d85edefa7c6..11f64a43b38a 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -56,6 +56,7 @@ extern unsigned int ata_print_id; extern struct workqueue_struct *ata_aux_wq; extern int atapi_enabled; extern int atapi_dmadir; +extern int atapi_passthru16; extern int libata_fua; extern int libata_noacpi; extern struct ata_queued_cmd *ata_qc_new_init(struct ata_device *dev); |