ata: libata-scsi: link ata port and scsi device
[linux-2.6-block.git] / drivers / ata / libata-scsi.c
index e4e4175e3e83534e715976929a1ada75f063fe9a..8b43290ca2cd89b87eb0d6797d19057ec483cba1 100644 (file)
@@ -1089,6 +1089,42 @@ int ata_scsi_dev_config(struct scsi_device *sdev, struct ata_device *dev)
        return 0;
 }
 
+/**
+ *     ata_scsi_slave_alloc - Early setup of SCSI device
+ *     @sdev: SCSI device to examine
+ *
+ *     This is called from scsi_alloc_sdev() when the scsi device
+ *     associated with an ATA device is scanned on a port.
+ *
+ *     LOCKING:
+ *     Defined by SCSI layer.  We don't really care.
+ */
+
+int ata_scsi_slave_alloc(struct scsi_device *sdev)
+{
+       struct ata_port *ap = ata_shost_to_port(sdev->host);
+       struct device_link *link;
+
+       ata_scsi_sdev_config(sdev);
+
+       /*
+        * Create a link from the ata_port device to the scsi device to ensure
+        * that PM does suspend/resume in the correct order: the scsi device is
+        * consumer (child) and the ata port the supplier (parent).
+        */
+       link = device_link_add(&sdev->sdev_gendev, &ap->tdev,
+                              DL_FLAG_STATELESS |
+                              DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE);
+       if (!link) {
+               ata_port_err(ap, "Failed to create link to scsi device %s\n",
+                            dev_name(&sdev->sdev_gendev));
+               return -ENODEV;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(ata_scsi_slave_alloc);
+
 /**
  *     ata_scsi_slave_config - Set SCSI device attributes
  *     @sdev: SCSI device to examine
@@ -1105,14 +1141,11 @@ int ata_scsi_slave_config(struct scsi_device *sdev)
 {
        struct ata_port *ap = ata_shost_to_port(sdev->host);
        struct ata_device *dev = __ata_scsi_find_dev(ap, sdev);
-       int rc = 0;
-
-       ata_scsi_sdev_config(sdev);
 
        if (dev)
-               rc = ata_scsi_dev_config(sdev, dev);
+               return ata_scsi_dev_config(sdev, dev);
 
-       return rc;
+       return 0;
 }
 EXPORT_SYMBOL_GPL(ata_scsi_slave_config);
 
@@ -1136,6 +1169,8 @@ void ata_scsi_slave_destroy(struct scsi_device *sdev)
        unsigned long flags;
        struct ata_device *dev;
 
+       device_link_remove(&sdev->sdev_gendev, &ap->tdev);
+
        spin_lock_irqsave(ap->lock, flags);
        dev = __ata_scsi_find_dev(ap, sdev);
        if (dev && dev->sdev) {
@@ -4312,7 +4347,7 @@ void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd)
                break;
 
        case MAINTENANCE_IN:
-               if (scsicmd[1] == MI_REPORT_SUPPORTED_OPERATION_CODES)
+               if ((scsicmd[1] & 0x1f) == MI_REPORT_SUPPORTED_OPERATION_CODES)
                        ata_scsi_rbuf_fill(&args, ata_scsiop_maint_in);
                else
                        ata_scsi_set_invalid_field(dev, cmd, 1, 0xff);
@@ -4752,7 +4787,7 @@ void ata_scsi_dev_rescan(struct work_struct *work)
                        }
 
                        spin_unlock_irqrestore(ap->lock, flags);
-                       scsi_rescan_device(&(sdev->sdev_gendev));
+                       scsi_rescan_device(sdev);
                        scsi_device_put(sdev);
                        spin_lock_irqsave(ap->lock, flags);
                }