libata: add support for ATA_16 on ATAPI
authorMark Lord <liml@rtr.ca>
Tue, 7 Aug 2007 16:08:45 +0000 (01:08 +0900)
committerJeff Garzik <jeff@garzik.org>
Fri, 12 Oct 2007 18:55:32 +0000 (14:55 -0400)
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>
drivers/ata/libata-core.c
drivers/ata/libata-scsi.c
drivers/ata/libata.h

index fc0679370638b10fafebe7b0176f81947bcdfc35..75ccf46972d8c6089cee90646d9b3488e1738dad 100644 (file)
@@ -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)");
index 58051ee40f1a41d471f37bf3cb5286627d6ca139..7d66c986a54c99dbe8025288feb0c5a8766b5985 100644 (file)
@@ -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;
 }
 
 /**
index 6d85edefa7c63ffade645b4f6f951b17d3afa023..11f64a43b38a40243d7295580f0a9caa6852d8d5 100644 (file)
@@ -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);