AHCI - Hot Plug support, ATAPI IDENTIFY, CAM bus scan issues, etc
authorMatthew Dillon <dillon@apollo.backplane.com>
Sat, 6 Jun 2009 17:44:31 +0000 (10:44 -0700)
committerMatthew Dillon <dillon@apollo.backplane.com>
Sat, 6 Jun 2009 17:44:31 +0000 (10:44 -0700)
* Split some code out of ahci_port_alloc() into its own procedure,
  ahci_port_init().  This procedure will be used to init/reinit a
  port for Hot-Plug.

* Do not free ports which have device problems, hot-plug can still
  operate on such ports.

* Re-detect ap_type when resetting a port.  Create a helper routine
  called ahci_port_signature_detect() to reduce code duplication.

* Augment ahci_cam_changed() so hot-plug can tell it whether a device
  is being added or removed.

* Scan the CAM bus asynchronously to fix reentrancy issues.

* Consolidate most of the ATAPI and DISK probe code back into one routine.

  Use ATA_C_ATAPI_IDENTIFY when probing an ATAPI device and also probe
  for NCQ on ATAPI devices (though nobody knows if it would actually work
  if an ATAPI device said it could do it).

* Do not try to set the security freeze-lock if the identify info
  indicates that the freeze-lock is already active.  This fixes a
  command timeout that occurs when re-plugging devices whos freeze-lock
  remains intact from a previous use.

* Add defines for ATA_C_ATAPI_IDENTIFY and ATA_SECURE_* identify bits.

sys/dev/disk/ahci/TODO
sys/dev/disk/ahci/ahci.c
sys/dev/disk/ahci/ahci.h
sys/dev/disk/ahci/ahci_cam.c
sys/dev/disk/ahci/atascsi.h

index 210c2f5..b2e5dbb 100644 (file)
@@ -1,16 +1,25 @@
 
-DELAY's will tsleep, so interrupts might run.  fix poll loop to detect
+DELAY's might tsleep, so interrupts might run.  fix poll loop to detect
 completion via other interrupts.
 
 Locking serialize_enter/exit.  Lots of recursion.   Needs help.  Use
-lockmgr()?
-
-Hot plug/unplug SCSI bus rescan sequencing.
+lockmgr()?   Needs to be converted to per-port locking, also.
 
 Port multiplier support.
 
-ATAPI support.
-
 Simulate various mode pages (serial number access and so forth).
 
-ESATA issues with MyBook (port fails after ident even at 1.5 GBits).
+------ Misc probe info --------
+
+<AHCI-PCI-SATA> port
+<S64A,NCQ,SSNTF,SALP,SAL,SCLO,PMD,SSC,PSC,CCCS,EMS>,
+6 ports, 32 tags/port, gen 1 (1.5Gbps) and 2 (3Gbps)
+
+ahci0: AHCI 1.2 capabilities 0xe3229f05
+<S64A,NCQ,SSNTF,SAL,SCLO,SPM,PMD>, 6 ports, 32 tags/port, gen 1 (1.5Gbps) and 2 (3Gbps)
+
+
+Chipsets supporting FBSS (FIS-Based Switching):
+       SB800
+       S5000 (w/ ESB2)
+       (add more)
index 74a1752..7789ddf 100644 (file)
 
 #include "ahci.h"
 
+int    ahci_port_init(struct ahci_port *ap);
 int    ahci_port_start(struct ahci_port *, int);
 int    ahci_port_stop(struct ahci_port *, int);
 int    ahci_port_clo(struct ahci_port *);
-int    ahci_port_softreset(struct ahci_port *);
-int    ahci_port_portreset(struct ahci_port *);
 
+int    ahci_port_signature_detect(struct ahci_port *ap);
 int    ahci_load_prdt(struct ahci_ccb *);
 void   ahci_unload_prdt(struct ahci_ccb *);
 static void ahci_load_prdt_callback(void *info, bus_dma_segment_t *segs,
@@ -93,6 +93,10 @@ void ahci_ata_cmd_done(struct ahci_ccb *ccb);
 /* Wait for all bits in _b to be set */
 #define ahci_pwait_set(_ap, _r, _b) ahci_pwait_eq((_ap), (_r), (_b), (_b))
 
+/*
+ * Initialize the global AHCI hardware.  This code does not set up any of
+ * its ports.
+ */
 int
 ahci_init(struct ahci_softc *sc)
 {
@@ -128,6 +132,9 @@ ahci_init(struct ahci_softc *sc)
        return (0);
 }
 
+/*
+ * Allocate and initialize an AHCI port.
+ */
 int
 ahci_port_alloc(struct ahci_softc *sc, u_int port)
 {
@@ -284,10 +291,43 @@ nomem:
        /* Wait for ICC change to complete */
        ahci_pwait_clr(ap, AHCI_PREG_CMD, AHCI_PREG_CMD_ICC);
 
-       /* Reset port */
+       /*
+        * Do device-related port initialization.  A failure here does not
+        * cause the port to be deallocated as we want to receive future
+        * hot-plug events.
+        */
+       ahci_port_init(ap);
+       return(0);
+freeport:
+       ahci_port_free(sc, port);
+reterr:
+       return (rc);
+}
+
+/*
+ * [re]initialize an idle port.  No CCBs should be active.
+ *
+ * This function is called during the initial port allocation sequence
+ * and is also called on hot-plug insertion.  We take no chances and
+ * use a portreset instead of a softreset.
+ *
+ * Returns 0 if a device is successfully detected.
+ */
+int
+ahci_port_init(struct ahci_port *ap)
+{
+       int rc;
+
+       /*
+        * Hard-reset the port.
+        */
        rc = ahci_port_portreset(ap);
+
        switch (rc) {
        case ENODEV:
+               /*
+                * We had problems talking to the device on the port.
+                */
                switch (ahci_pread(ap, AHCI_PREG_SSTS) & AHCI_PREG_SSTS_DET) {
                case AHCI_PREG_SSTS_DET_DEV_NE:
                        kprintf("%s: Device not communicating\n", PORTNAME(ap));
@@ -302,20 +342,23 @@ nomem:
                break;
 
        case EBUSY:
-               device_printf(sc->sc_dev,
-                             "device on port %d didn't come ready, "
-                             "TFD: 0x%b\n",
-                             port,
-                             ahci_pread(ap, AHCI_PREG_TFD), AHCI_PFMT_TFD_STS);
+               /*
+                * The device on the port is still telling us its busy.
+                *
+                * We try a softreset on the device.
+                */
+               kprintf("%s: Device on port did not come ready, TFD: 0x%b\n",
+                       PORTNAME(ap),
+                     ahci_pread(ap, AHCI_PREG_TFD), AHCI_PFMT_TFD_STS);
 
                /* Try a soft reset to clear busy */
                rc = ahci_port_softreset(ap);
                if (rc) {
-                       device_printf(sc->sc_dev,
-                                     "unable to communicate "
-                                     "with device on port %d\n",
-                                     port);
-                       goto freeport;
+                       kprintf("%s: Unable to clear busy device\n",
+                               PORTNAME(ap));
+               } else {
+                       kprintf("%s: Successfully reset busy device\n",
+                               PORTNAME(ap));
                }
                break;
 
@@ -329,28 +372,16 @@ nomem:
         * intact so we get hot-plug interrupts.
         */
        if (rc == 0) {
-               DPRINTF(AHCI_D_VERBOSE, "%s: detected device on port %d\n",
-                       device_get_name(sc->sc_dev), port);
                if (ahci_port_start(ap, 0)) {
-                       device_printf(sc->sc_dev,
-                                     "failed to start command DMA on port %d, "
-                                     "disabling\n", port);
+                       kprintf("%s: failed to start command DMA on port, "
+                               "disabling\n", PORTNAME(ap));
                        rc = ENXIO;     /* couldn't start port */
                }
        }
 
-       /*
-        * A missing or busy device is not fatal for the purposes of
-        * port allocation.  We still want to detect hot-plug
-        * state changes.
-        */
-       if (rc == ENODEV || rc == EBUSY) {
-               rc = 0;
-       }
-
        /* Flush interrupts for port */
        ahci_pwrite(ap, AHCI_PREG_IS, ahci_pread(ap, AHCI_PREG_IS));
-       ahci_write(sc, AHCI_REG_IS, 1 << port);
+       ahci_write(ap->ap_sc, AHCI_REG_IS, 1 << ap->ap_num);
 
        /* Enable port interrupts */
        ahci_pwrite(ap, AHCI_PREG_IE,
@@ -364,15 +395,13 @@ nomem:
 #else
                        AHCI_PREG_IE_SDBE | AHCI_PREG_IE_DHRE
 #endif
-           );
-
-freeport:
-       if (rc != 0)
-               ahci_port_free(sc, port);
-reterr:
-       return (rc);
+       );
+       return(rc);
 }
 
+/*
+ * De-initialize and detach a port.
+ */
 void
 ahci_port_free(struct ahci_softc *sc, u_int port)
 {
@@ -418,6 +447,9 @@ ahci_port_free(struct ahci_softc *sc, u_int port)
        sc->sc_ports[port] = NULL;
 }
 
+/*
+ * Start high-level command processing on the port
+ */
 int
 ahci_port_start(struct ahci_port *ap, int fre_only)
 {
@@ -452,6 +484,9 @@ ahci_port_start(struct ahci_port *ap, int fre_only)
        return (0);
 }
 
+/*
+ * Stop high-level command processing on a port
+ */
 int
 ahci_port_stop(struct ahci_port *ap, int stop_fis_rx)
 {
@@ -484,7 +519,9 @@ ahci_port_stop(struct ahci_port *ap, int stop_fis_rx)
        return (0);
 }
 
-/* AHCI command list override -> forcibly clear TFD.STS.{BSY,DRQ} */
+/*
+ * AHCI command list override -> forcibly clear TFD.STS.{BSY,DRQ}
+ */
 int
 ahci_port_clo(struct ahci_port *ap)
 {
@@ -514,7 +551,12 @@ ahci_port_clo(struct ahci_port *ap)
        return (0);
 }
 
-/* AHCI soft reset, Section 10.4.1 */
+/*
+ * AHCI soft reset, Section 10.4.1
+ *
+ * This function keeps port communications intact and attempts to generate
+ * a reset to the connected device.
+ */
 int
 ahci_port_softreset(struct ahci_port *ap)
 {
@@ -605,6 +647,24 @@ ahci_port_softreset(struct ahci_port *ap)
                goto err;
        }
 
+       /*
+        * If the softreset is trying to clear a BSY condition after a
+        * normal portreset we assign the port type.
+        *
+        * If the softreset is being run first as part of the ccb error
+        * processing code then report if the device signature changed
+        * unexpectedly.
+        */
+       if (ap->ap_ata.ap_type == ATA_PORT_T_NONE) {
+               ap->ap_ata.ap_type = ahci_port_signature_detect(ap);
+       } else {
+               if (ahci_port_signature_detect(ap) != ap->ap_ata.ap_type) {
+                       kprintf("%s: device signature unexpectedly changed\n",
+                               PORTNAME(ap));
+                       rc = EBUSY;
+               }
+       }
+
        rc = 0;
 err:
        if (ccb != NULL) {
@@ -628,7 +688,12 @@ err:
        return (rc);
 }
 
-/* AHCI port reset, Section 10.4.2 */
+/*
+ * AHCI port reset, Section 10.4.2
+ *
+ * This function does a hard reset of the port.  Note that the device
+ * connected to the port could still end-up hung.
+ */
 int
 ahci_port_portreset(struct ahci_port *ap)
 {
@@ -683,16 +748,7 @@ ahci_port_portreset(struct ahci_port *ap)
                goto err;
        }
 
-       /*
-        * Figure out if we are a ATAPI or DISK device
-        */
-       u_int32_t sig;
-       sig = ahci_pread(ap, AHCI_PREG_SIG);
-       if ((sig & 0xffff0000) == (SATA_SIGNATURE_ATAPI & 0xffff0000)) {
-               ap->ap_ata.ap_type = ATA_PORT_T_ATAPI;
-       } else {
-               ap->ap_ata.ap_type = ATA_PORT_T_DISK;
-       }
+       ap->ap_ata.ap_type = ahci_port_signature_detect(ap);
        rc = 0;
 err:
        /* Restore preserved port state */
@@ -701,6 +757,26 @@ err:
        return (rc);
 }
 
+/*
+ * Figure out what type of device is connected to the port, ATAPI or
+ * DISK.
+ */
+int
+ahci_port_signature_detect(struct ahci_port *ap)
+{
+       u_int32_t sig;
+
+       sig = ahci_pread(ap, AHCI_PREG_SIG);
+       if ((sig & 0xffff0000) == (SATA_SIGNATURE_ATAPI & 0xffff0000)) {
+               return(ATA_PORT_T_ATAPI);
+       } else {
+               return(ATA_PORT_T_DISK);
+       }
+}
+
+/*
+ * Load the DMA descriptor table for a CCB's buffer.
+ */
 int
 ahci_load_prdt(struct ahci_ccb *ccb)
 {
@@ -1148,8 +1224,8 @@ ahci_port_intr(struct ahci_port *ap, u_int32_t ci_mask)
                        if (ap->ap_ata.ap_type == ATA_PORT_T_NONE) {
                                kprintf("%s: HOTPLUG - Device added\n",
                                        PORTNAME(ap));
-                               if (ahci_port_portreset(ap) == 0)
-                                       ahci_cam_changed(ap);
+                               if (ahci_port_init(ap) == 0)
+                                       ahci_cam_changed(ap, 1);
                        }
                        break;
                default:
@@ -1157,7 +1233,7 @@ ahci_port_intr(struct ahci_port *ap, u_int32_t ci_mask)
                                kprintf("%s: HOTPLUG - Device removed\n",
                                        PORTNAME(ap));
                                ahci_port_portreset(ap);
-                               ahci_cam_changed(ap);
+                               ahci_cam_changed(ap, 0);
                        }
                        break;
                }
index df31f76..0e0d088 100644 (file)
@@ -447,6 +447,9 @@ const struct ahci_device *ahci_lookup_device(device_t dev);
 int    ahci_init(struct ahci_softc *);
 int    ahci_port_alloc(struct ahci_softc *, u_int);
 void   ahci_port_free(struct ahci_softc *, u_int);
+int    ahci_port_softreset(struct ahci_port *);
+int    ahci_port_portreset(struct ahci_port *);
+
 u_int32_t ahci_read(struct ahci_softc *, bus_size_t);
 void   ahci_write(struct ahci_softc *, bus_size_t, u_int32_t);
 int    ahci_wait_ne(struct ahci_softc *, bus_size_t, u_int32_t, u_int32_t);
@@ -456,7 +459,7 @@ int ahci_pwait_eq(struct ahci_port *, bus_size_t, u_int32_t, u_int32_t);
 void   ahci_intr(void *);
 
 int    ahci_cam_attach(struct ahci_port *ap);
-void   ahci_cam_changed(struct ahci_port *ap);
+void   ahci_cam_changed(struct ahci_port *ap, int found);
 void   ahci_cam_detach(struct ahci_port *ap);
 
 struct ata_xfer *ahci_ata_get_xfer(struct ahci_port *ap);
index 8625f3f..afbbe21 100644 (file)
@@ -160,12 +160,29 @@ ahci_cam_attach(struct ahci_port *ap)
 }
 
 void
-ahci_cam_changed(struct ahci_port *ap)
+ahci_cam_changed(struct ahci_port *ap, int found)
 {
+       struct cam_path *tmppath;
+
        if (ap->ap_sim == NULL)
                return;
-       ahci_cam_probe(ap);
-       ahci_cam_rescan(ap);
+       if (xpt_create_path(&tmppath, NULL, cam_sim_path(ap->ap_sim),
+                           0, CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
+               return;
+       }
+       if (found) {
+               ahci_cam_probe(ap);
+               /*
+                * XXX calling AC_FOUND_DEVICE with inquiry data is
+                *     basically a NOP.  For now just tell CAM to
+                *     rescan the bus.
+                */
+               xpt_async(AC_FOUND_DEVICE, tmppath, NULL);
+               ahci_cam_rescan(ap);
+       } else {
+               xpt_async(AC_LOST_DEVICE, tmppath, NULL);
+       }
+       xpt_free_path(tmppath);
 }
 
 void
@@ -199,42 +216,25 @@ ahci_cam_detach(struct ahci_port *ap)
 /*
  * Once the AHCI port has been attched we need to probe for a device or
  * devices on the port and setup various options.
- *
- * XXX use this to allow re-probing and disconnect/reconnect
  */
-
 static int
 ahci_cam_probe(struct ahci_port *ap)
 {
-       int error;
-
-       switch(ap->ap_ata.ap_type) {
-       case ATA_PORT_T_ATAPI:
-               error = ahci_cam_probe_atapi(ap);
-               break;
-       case ATA_PORT_T_DISK:
-               error = ahci_cam_probe_disk(ap);
-               break;
-       default:
-               error = EIO;
-               break;
-       }
-       return (error);
-}
-
-static int
-ahci_cam_probe_disk(struct ahci_port *ap)
-{
-       /*struct ahci_softc *sc = ap->ap_sc;*/
        struct ata_xfer *xa;
        u_int64_t       capacity;
        u_int64_t       capacity_bytes;
        int             model_len;
        int             status;
+       int             error;
        int             devncqdepth;
        int             i;
        const char      *wcstr;
        const char      *rastr;
+       const char      *scstr;
+       const char      *type;
+
+       if (ap->ap_ata.ap_type == ATA_PORT_T_NONE)
+               return (EIO);
 
        /*
         * Issue identify, saving the result
@@ -244,7 +244,13 @@ ahci_cam_probe_disk(struct ahci_port *ap)
        xa->data = &ap->ap_ata.ap_identify;
        xa->datalen = sizeof(ap->ap_ata.ap_identify);
        xa->fis->flags = ATA_H2D_FLAGS_CMD;
-       xa->fis->command = ATA_C_IDENTIFY;
+       if (ap->ap_ata.ap_type == ATA_PORT_T_ATAPI) {
+               xa->fis->command = ATA_C_ATAPI_IDENTIFY;
+               type = "ATAPI";
+       } else {
+               xa->fis->command = ATA_C_IDENTIFY;
+               type = "DISK";
+       }
        xa->fis->features = 0;
        xa->fis->device = 0;
        xa->flags = ATA_F_READ | ATA_F_PIO | ATA_F_POLL;
@@ -252,15 +258,15 @@ ahci_cam_probe_disk(struct ahci_port *ap)
 
        status = ahci_ata_cmd(xa);
        if (status != ATA_COMPLETE) {
-               kprintf("%s: Detected DISK device but unable to IDENTIFY\n",
-                       PORTNAME(ap));
+               kprintf("%s: Detected %s device but unable to IDENTIFY\n",
+                       PORTNAME(ap), type);
                ahci_ata_put_xfer(xa);
                return(EIO);
        }
        if (xa->state != ATA_S_COMPLETE) {
-               kprintf("%s: Detected DISK device but unable to IDENTIFY "
+               kprintf("%s: Detected %s device but unable to IDENTIFY "
                        " xa->state=%d\n",
-                       PORTNAME(ap), xa->state);
+                       PORTNAME(ap), type, xa->state);
                ahci_ata_put_xfer(xa);
                return(EIO);
        }
@@ -320,6 +326,9 @@ ahci_cam_probe_disk(struct ahci_port *ap)
                devncqdepth = 0;
        }
 
+       /*
+        * Make the model string a bit more presentable
+        */
        for (model_len = 40; model_len; --model_len) {
                if (ap->ap_ata.ap_identify.model[model_len-1] == ' ')
                        continue;
@@ -328,9 +337,17 @@ ahci_cam_probe_disk(struct ahci_port *ap)
                break;
        }
 
+       /*
+        * Generate informatiive strings.
+        *
+        * NOTE: We do not automatically set write caching, lookahead,
+        *       or the security state for ATAPI devices.
+        */
        if (ap->ap_ata.ap_identify.cmdset82 & ATA_IDENTIFY_WRITECACHE) {
                if (ap->ap_ata.ap_identify.features85 & ATA_IDENTIFY_WRITECACHE)
                        wcstr = "enabled";
+               else if (ap->ap_ata.ap_type == ATA_PORT_T_ATAPI)
+                       wcstr = "disabled";
                else
                        wcstr = "enabling";
        } else {
@@ -340,17 +357,31 @@ ahci_cam_probe_disk(struct ahci_port *ap)
        if (ap->ap_ata.ap_identify.cmdset82 & ATA_IDENTIFY_LOOKAHEAD) {
                if (ap->ap_ata.ap_identify.features85 & ATA_IDENTIFY_LOOKAHEAD)
                        rastr = "enabled";
+               else if (ap->ap_ata.ap_type == ATA_PORT_T_ATAPI)
+                       rastr = "disabled";
                else
                        rastr = "enabling";
        } else {
                    rastr = "notsupp";
        }
 
-       kprintf("%s: Found DISK \"%*.*s %8.8s\" serial=\"%20.20s\"\n"
+       if (ap->ap_ata.ap_identify.cmdset82 & ATA_IDENTIFY_SECURITY) {
+               if (ap->ap_ata.ap_identify.securestatus & ATA_SECURE_FROZEN)
+                       scstr = "frozen";
+               else if (ap->ap_ata.ap_type == ATA_PORT_T_ATAPI)
+                       scstr = "unfrozen";
+               else
+                       scstr = "freezing";
+       } else {
+                   scstr = "notsupp";
+       }
+
+       kprintf("%s: Found %s \"%*.*s %8.8s\" serial=\"%20.20s\"\n"
                "%s: tags=%d/%d satacaps=%04x satafeat=%04x "
                "capacity=%lld.%02dMB\n"
-               "%s: f85=%04x f86=%04x f87=%04x WC=%s RA=%s\n",
+               "%s: f85=%04x f86=%04x f87=%04x WC=%s RA=%s SEC=%s\n",
                PORTNAME(ap),
+               type,
                model_len, model_len,
                ap->ap_ata.ap_identify.model,
                ap->ap_ata.ap_identify.firmware,
@@ -368,11 +399,42 @@ ahci_cam_probe_disk(struct ahci_port *ap)
                ap->ap_ata.ap_identify.features86,
                ap->ap_ata.ap_identify.features87,
                wcstr,
-               rastr
+               rastr,
+               scstr
        );
 
        /*
+        * Additional type-specific probing
+        */
+       switch(ap->ap_ata.ap_type) {
+       case ATA_PORT_T_DISK:
+               error = ahci_cam_probe_disk(ap);
+               break;
+       default:
+               error = ahci_cam_probe_atapi(ap);
+               break;
+       }
+       return (0);
+}
+
+/*
+ * DISK-specific probe after initial ident
+ */
+static int
+ahci_cam_probe_disk(struct ahci_port *ap)
+{
+       struct ata_xfer *xa;
+       int status;
+
+       /*
         * Enable write cache if supported
+        *
+        * NOTE: "WD My Book" external disk devices have a very poor
+        *       daughter board between the the ESATA and the HD.  Sending
+        *       any ATA_C_SET_FEATURES commands will break the hardware port
+        *       with a fatal protocol error.  However, this device also
+        *       indicates that WRITECACHE is already on and READAHEAD is
+        *       not supported so we avoid the issue.
         */
        if ((ap->ap_ata.ap_identify.cmdset82 & ATA_IDENTIFY_WRITECACHE) &&
            (ap->ap_ata.ap_identify.features85 & ATA_IDENTIFY_WRITECACHE) == 0) {
@@ -419,7 +481,8 @@ ahci_cam_probe_disk(struct ahci_port *ap)
         * checking if the device sends a command abort to tell us it doesn't
         * support it
         */
-       if (ap->ap_ata.ap_identify.cmdset82 & ATA_IDENTIFY_SECURITY) {
+       if ((ap->ap_ata.ap_identify.cmdset82 & ATA_IDENTIFY_SECURITY) &&
+           (ap->ap_ata.ap_identify.securestatus & ATA_SECURE_FROZEN) == 0) {
                xa = ahci_ata_get_xfer(ap);
                xa->complete = ahci_ata_dummy_done;
                xa->fis->command = ATA_C_SEC_FREEZE_LOCK;
@@ -436,10 +499,20 @@ ahci_cam_probe_disk(struct ahci_port *ap)
        return (0);
 }
 
+/*
+ * ATAPI-specific probe after initial ident
+ */
 static int
 ahci_cam_probe_atapi(struct ahci_port *ap)
 {
-       /*struct ahci_softc *sc = ap->ap_sc;*/
+       return(0);
+}
+
+#if 0
+       /*
+        * Keep this old code around for a little bit, it is another way
+        * to probe an ATAPI device by using a ATAPI (SCSI) INQUIRY
+        */
        struct ata_xfer *xa;
        int             status;
        int             devncqdepth;
@@ -498,16 +571,6 @@ ahci_cam_probe_atapi(struct ahci_port *ap)
 
        devncqdepth = 0;
 
-#if 0
-       for (model_len = 40; model_len; --model_len) {
-               if (ap->ap_ata.ap_identify.model[model_len-1] == ' ')
-                       continue;
-               if (ap->ap_ata.ap_identify.model[model_len-1] == 0)
-                       continue;
-               break;
-       }
-#endif
-
        kprintf("%s: Found ATAPI %s \"%8.8s %16.16s\" rev=\"%4.4s\"\n"
                "%s: tags=%d/%d\n",
                PORTNAME(ap),
@@ -520,31 +583,8 @@ ahci_cam_probe_atapi(struct ahci_port *ap)
                devncqdepth, ap->ap_sc->sc_ncmds
        );
        kfree(inq_data, M_TEMP);
-
-#if 0
-       /*
-        * FREEZE LOCK the device so malicious users can't lock it on us.
-        * As there is no harm in issuing this to devices that don't
-        * support the security feature set we just send it, and don't bother
-        * checking if the device sends a command abort to tell us it doesn't
-        * support it
-        */
-       xa = ahci_ata_get_xfer(ap);
-       xa->complete = ahci_ata_dummy_done;
-       xa->datalen = 0;
-       xa->fis->command = ATA_C_SEC_FREEZE_LOCK;
-       xa->fis->flags = ATA_H2D_FLAGS_CMD;
-       xa->flags = ATA_F_READ | ATA_F_PIO | ATA_F_POLL;
-       xa->timeout = hz;
-       status = ahci_ata_cmd(xa);
-       if (status == ATA_COMPLETE)
-               ap->ap_ata.ap_features |= ATA_PORT_F_FRZLCK;
-       ahci_ata_put_xfer(xa);
 #endif
 
-       return (0);
-}
-
 /*
  * Fix byte ordering so buffers can be accessed as
  * strings.
@@ -577,7 +617,9 @@ ahci_ata_dummy_done(struct ata_xfer *xa)
 }
 
 /*
- * Initiate bus scan.
+ * Initiate a bus scan.
+ *
+ * An asynchronous bus scan is used to avoid reentrancy issues
  */
 static void
 ahci_cam_rescan_callback(struct cam_periph *periph, union ccb *ccb)
@@ -599,7 +641,7 @@ ahci_cam_rescan(struct ahci_port *ap)
                return;
 
        xpt_setup_ccb(&ccb->ccb_h, path, 5);    /* 5 = low priority */
-       ccb->ccb_h.func_code = XPT_SCAN_BUS;
+       ccb->ccb_h.func_code = XPT_SCAN_BUS | XPT_FC_QUEUED;
        ccb->ccb_h.cbfcnp = ahci_cam_rescan_callback;
        ccb->crcn.flags = CAM_FLAG_NONE;
        xpt_action(ccb);
@@ -700,11 +742,22 @@ ahci_xpt_action(struct cam_sim *sim, union ccb *ccb)
                xpt_done(ccb);
                break;
        case XPT_RESET_DEV:
-               ccbh->status = CAM_REQ_INVALID;
+               lwkt_serialize_enter(&ap->ap_sc->sc_serializer);
+               ahci_port_softreset(ap);
+               lwkt_serialize_exit(&ap->ap_sc->sc_serializer);
+
+               ccbh->status = CAM_REQ_CMP;
                xpt_done(ccb);
                break;
        case XPT_RESET_BUS:
-               ccbh->status = CAM_REQ_INVALID;
+               lwkt_serialize_enter(&ap->ap_sc->sc_serializer);
+               ahci_port_portreset(ap);
+               ahci_port_softreset(ap);
+               lwkt_serialize_exit(&ap->ap_sc->sc_serializer);
+
+               xpt_async(AC_BUS_RESET, ap->ap_path, NULL);
+
+               ccbh->status = CAM_REQ_CMP;
                xpt_done(ccb);
                break;
        case XPT_SET_TRAN_SETTINGS:
index 10555b1..cee8742 100644 (file)
@@ -29,6 +29,7 @@ struct scsi_link;
 #define ATA_C_READ_FPDMA       0x60
 #define ATA_C_WRITE_FPDMA      0x61
 #define ATA_C_PACKET           0xa0
+#define ATA_C_ATAPI_IDENTIFY   0xa1
 #define ATA_C_READDMA          0xc8
 #define ATA_C_WRITEDMA         0xca
 #define ATA_C_FLUSH_CACHE      0xe7
@@ -123,6 +124,8 @@ struct ata_identify {
        u_int16_t       padding2[6];
        u_int16_t       rmsn;           /* 127 */
        u_int16_t       securestatus;   /* 128 */
+#define ATA_SECURE_LOCKED              (1<<2)
+#define ATA_SECURE_FROZEN              (1<<3)
        u_int16_t       vendor[31];     /* 129 */
        u_int16_t       padding3[16];   /* 160 */
        u_int16_t       curmedser[30];  /* 176 */