TRIM support
[dragonfly.git] / sys / dev / disk / ahci / ahci_cam.c
index a61b57e..8e61fbf 100644 (file)
@@ -1,4 +1,6 @@
 /*
+ * (MPSAFE)
+ *
  * Copyright (c) 2009 The DragonFly Project.  All rights reserved.
  *
  * This code is derived from software contributed to The DragonFly Project
 
 #include "ahci.h"
 
-const char *ScsiTypeArray[32] = {
-       "DIRECT",
-       "SEQUENTIAL",
-       "PRINTER",
-       "PROCESSOR",
-       "WORM",
-       "CDROM",
-       "SCANNER",
-       "OPTICAL",
-       "CHANGER",
-       "COMM",
-       "ASC0",
-       "ASC1",
-       "STORARRAY",
-       "ENCLOSURE",
-       "RBC",
-       "OCRW",
-       "0x10",
-       "OSD",
-       "ADC",
-       "0x13",
-       "0x14",
-       "0x15",
-       "0x16",
-       "0x17",
-       "0x18",
-       "0x19",
-       "0x1A",
-       "0x1B",
-       "0x1C",
-       "0x1D",
-       "0x1E",
-       "NODEVICE"
-};
-
 static void ahci_xpt_action(struct cam_sim *sim, union ccb *ccb);
 static void ahci_xpt_poll(struct cam_sim *sim);
 static void ahci_xpt_scsi_disk_io(struct ahci_port *ap,
                        struct ata_port *at, union ccb *ccb);
 static void ahci_xpt_scsi_atapi_io(struct ahci_port *ap,
                        struct ata_port *at, union ccb *ccb);
+static void ahci_xpt_page_inquiry(struct ahci_port *ap,
+                       struct ata_port *at, union ccb *ccb);
 
 static void ahci_ata_complete_disk_rw(struct ata_xfer *xa);
 static void ahci_ata_complete_disk_synchronize_cache(struct ata_xfer *xa);
@@ -111,9 +80,11 @@ static void ahci_ata_atapi_sense(struct ata_fis_d2h *rfis,
 
 static int ahci_cam_probe_disk(struct ahci_port *ap, struct ata_port *at);
 static int ahci_cam_probe_atapi(struct ahci_port *ap, struct ata_port *at);
+static int ahci_set_xfer(struct ahci_port *ap, struct ata_port *atx);
 static void ahci_ata_dummy_done(struct ata_xfer *xa);
 static void ata_fix_identify(struct ata_identify *id);
 static void ahci_cam_rescan(struct ahci_port *ap);
+static void ahci_strip_string(const char **basep, int *lenp);
 
 int
 ahci_cam_attach(struct ahci_port *ap)
@@ -135,15 +106,23 @@ ahci_cam_attach(struct ahci_port *ap)
        if (devq == NULL) {
                return (ENOMEM);
        }
+
+       /*
+        * Give the devq enough room to run with 32 max_dev_transactions,
+        * but set the overall max tags to 1 until NCQ is negotiated.
+        */
        sim = cam_sim_alloc(ahci_xpt_action, ahci_xpt_poll, "ahci",
-                          (void *)ap, unit, &sim_mplock, 1, 1, devq);
+                          (void *)ap, unit, &ap->ap_sim_lock,
+                          32, 1, devq);
        cam_simq_release(devq);
        if (sim == NULL) {
                return (ENOMEM);
        }
        ap->ap_sim = sim;
        ahci_os_unlock_port(ap);
+       lockmgr(&ap->ap_sim_lock, LK_EXCLUSIVE);
        error = xpt_bus_register(ap->ap_sim, ap->ap_num);
+       lockmgr(&ap->ap_sim_lock, LK_RELEASE);
        ahci_os_lock_port(ap);
        if (error != CAM_SUCCESS) {
                ahci_cam_detach(ap);
@@ -151,7 +130,10 @@ ahci_cam_attach(struct ahci_port *ap)
        }
        ap->ap_flags |= AP_F_BUS_REGISTERED;
 
-       error = ahci_cam_probe(ap, NULL);
+       if (ap->ap_probe == ATA_PROBE_NEED_IDENT)
+               error = ahci_cam_probe(ap, NULL);
+       else
+               error = 0;
        if (error) {
                ahci_cam_detach(ap);
                return (EIO);
@@ -218,7 +200,7 @@ ahci_cam_detach(struct ahci_port *ap)
 
        if ((ap->ap_flags & AP_F_CAM_ATTACHED) == 0)
                return;
-       get_mplock();
+       lockmgr(&ap->ap_sim_lock, LK_EXCLUSIVE);
        if (ap->ap_sim) {
                xpt_freeze_simq(ap->ap_sim, 1);
        }
@@ -231,7 +213,7 @@ ahci_cam_detach(struct ahci_port *ap)
                cam_sim_free(ap->ap_sim);
                ap->ap_sim = NULL;
        }
-       rel_mplock();
+       lockmgr(&ap->ap_sim_lock, LK_RELEASE);
        ap->ap_flags &= ~AP_F_CAM_ATTACHED;
 }
 
@@ -250,9 +232,14 @@ ahci_cam_probe(struct ahci_port *ap, struct ata_port *atx)
        u_int64_t       capacity;
        u_int64_t       capacity_bytes;
        int             model_len;
+       int             firmware_len;
+       int             serial_len;
        int             error;
        int             devncqdepth;
        int             i;
+       const char      *model_id;
+       const char      *firmware_id;
+       const char      *serial_id;
        const char      *wcstr;
        const char      *rastr;
        const char      *scstr;
@@ -275,7 +262,7 @@ ahci_cam_probe(struct ahci_port *ap, struct ata_port *atx)
         * an (at) pointing to target 0.
         */
        if (atx == NULL) {
-               at = ap->ap_ata;        /* direct attached - device 0 */
+               at = ap->ap_ata[0];     /* direct attached - device 0 */
                if (ap->ap_type == ATA_PORT_T_PM) {
                        kprintf("%s: Found Port Multiplier\n",
                                ATANAME(ap, atx));
@@ -304,6 +291,7 @@ ahci_cam_probe(struct ahci_port *ap, struct ata_port *atx)
        xa->complete = ahci_ata_dummy_done;
        xa->data = &at->at_identify;
        xa->datalen = sizeof(at->at_identify);
+       xa->flags = ATA_F_READ | ATA_F_PIO | ATA_F_POLL;
        xa->fis->flags = ATA_H2D_FLAGS_CMD | at->at_target;
 
        switch(at->at_type) {
@@ -313,6 +301,7 @@ ahci_cam_probe(struct ahci_port *ap, struct ata_port *atx)
                break;
        case ATA_PORT_T_ATAPI:
                xa->fis->command = ATA_C_ATAPI_IDENTIFY;
+               xa->flags |= ATA_F_AUTOSENSE;
                type = "ATAPI";
                break;
        default:
@@ -322,7 +311,6 @@ ahci_cam_probe(struct ahci_port *ap, struct ata_port *atx)
        }
        xa->fis->features = 0;
        xa->fis->device = 0;
-       xa->flags = ATA_F_READ | ATA_F_PIO | ATA_F_POLL;
        xa->timeout = 1000;
 
        if (ahci_ata_cmd(xa) != ATA_S_COMPLETE) {
@@ -351,6 +339,8 @@ ahci_cam_probe(struct ahci_port *ap, struct ata_port *atx)
                capacity <<= 16;
                capacity += le16toh(at->at_identify.addrsec[0]);
        }
+       if (capacity == 0)
+               capacity = 1024 * 1024 / 512;
        at->at_capacity = capacity;
        if (atx == NULL)
                ap->ap_probe = ATA_PROBE_GOOD;
@@ -388,24 +378,25 @@ ahci_cam_probe(struct ahci_port *ap, struct ata_port *atx)
                                }
                        }
                        if (at->at_ncqdepth >= ap->ap_sc->sc_ncmds) {
-                               cam_devq_resize(ap->ap_sim->devq,
-                                               at->at_ncqdepth - 1);
+                               cam_sim_set_max_tags(ap->ap_sim,
+                                                    at->at_ncqdepth - 1);
                        }
                }
        } else {
                devncqdepth = 0;
        }
 
-       /*
-        * Make the model string a bit more presentable
-        */
-       for (model_len = 40; model_len; --model_len) {
-               if (at->at_identify.model[model_len-1] == ' ')
-                       continue;
-               if (at->at_identify.model[model_len-1] == 0)
-                       continue;
-               break;
-       }
+       model_len = sizeof(at->at_identify.model);
+       model_id = at->at_identify.model;
+       ahci_strip_string(&model_id, &model_len);
+
+       firmware_len = sizeof(at->at_identify.firmware);
+       firmware_id = at->at_identify.firmware;
+       ahci_strip_string(&firmware_id, &firmware_len);
+
+       serial_len = sizeof(at->at_identify.serial);
+       serial_id = at->at_identify.serial;
+       ahci_strip_string(&serial_id, &serial_len);
 
        /*
         * Generate informatiive strings.
@@ -440,30 +431,33 @@ ahci_cam_probe(struct ahci_port *ap, struct ata_port *atx)
                        scstr = "frozen";
                else if (at->at_type == ATA_PORT_T_ATAPI)
                        scstr = "unfrozen";
+               else if (AhciNoFeatures & (1 << ap->ap_num))
+                       scstr = "<disabled>";
                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 SEC=%s\n",
+       kprintf("%s: Found %s \"%*.*s %*.*s\" serial=\"%*.*s\"\n"
+               "%s: tags=%d/%d satacap=%04x satafea=%04x NCQ=%s "
+               "capacity=%lld.%02dMB\n",
+
                ATANAME(ap, atx),
                type,
-               model_len, model_len,
-               at->at_identify.model,
-               at->at_identify.firmware,
-               at->at_identify.serial,
+               model_len, model_len, model_id,
+               firmware_len, firmware_len, firmware_id,
+               serial_len, serial_len, serial_id,
 
                ATANAME(ap, atx),
                devncqdepth, ap->ap_sc->sc_ncmds,
                at->at_identify.satacap,
                at->at_identify.satafsup,
+               (at->at_ncqdepth > 1 ? "YES" : "NO"),
                (long long)capacity_bytes / (1024 * 1024),
-               (int)(capacity_bytes % (1024 * 1024)) * 100 / (1024 * 1024),
-
+               (int)(capacity_bytes % (1024 * 1024)) * 100 / (1024 * 1024)
+       );
+       kprintf("%s: f85=%04x f86=%04x f87=%04x WC=%s RA=%s SEC=%s\n",
                ATANAME(ap, atx),
                at->at_identify.features85,
                at->at_identify.features86,
@@ -509,7 +503,12 @@ ahci_cam_probe_disk(struct ahci_port *ap, struct ata_port *atx)
        struct ata_port *at;
        struct ata_xfer *xa;
 
-       at = atx ? atx : ap->ap_ata;
+       at = atx ? atx : ap->ap_ata[0];
+
+       /*
+        * Set dummy xfer mode
+        */
+       ahci_set_xfer(ap, atx);
 
        /*
         * Enable write cache if supported
@@ -526,15 +525,18 @@ ahci_cam_probe_disk(struct ahci_port *ap, struct ata_port *atx)
                xa = ahci_ata_get_xfer(ap, atx);
                xa->complete = ahci_ata_dummy_done;
                xa->fis->command = ATA_C_SET_FEATURES;
-               /*xa->fis->features = ATA_SF_WRITECACHE_EN;*/
-               xa->fis->features = ATA_SF_LOOKAHEAD_EN;
+               xa->fis->features = ATA_SF_WRITECACHE_EN;
+               /* xa->fis->features = ATA_SF_LOOKAHEAD_EN; */
                xa->fis->flags = ATA_H2D_FLAGS_CMD | at->at_target;
                xa->fis->device = 0;
-               xa->flags = ATA_F_READ | ATA_F_PIO | ATA_F_POLL;
+               xa->flags = ATA_F_PIO | ATA_F_POLL;
                xa->timeout = 1000;
                xa->datalen = 0;
                if (ahci_ata_cmd(xa) == ATA_S_COMPLETE)
                        at->at_features |= ATA_PORT_F_WCACHE;
+               else
+                       kprintf("%s: Unable to enable write-caching\n",
+                               ATANAME(ap, atx));
                ahci_ata_put_xfer(xa);
        }
 
@@ -549,11 +551,14 @@ ahci_cam_probe_disk(struct ahci_port *ap, struct ata_port *atx)
                xa->fis->features = ATA_SF_LOOKAHEAD_EN;
                xa->fis->flags = ATA_H2D_FLAGS_CMD | at->at_target;
                xa->fis->device = 0;
-               xa->flags = ATA_F_READ | ATA_F_PIO | ATA_F_POLL;
+               xa->flags = ATA_F_PIO | ATA_F_POLL;
                xa->timeout = 1000;
                xa->datalen = 0;
                if (ahci_ata_cmd(xa) == ATA_S_COMPLETE)
                        at->at_features |= ATA_PORT_F_RAHEAD;
+               else
+                       kprintf("%s: Unable to enable read-ahead\n",
+                               ATANAME(ap, atx));
                ahci_ata_put_xfer(xa);
        }
 
@@ -565,16 +570,20 @@ ahci_cam_probe_disk(struct ahci_port *ap, struct ata_port *atx)
         * support it
         */
        if ((at->at_identify.cmdset82 & ATA_IDENTIFY_SECURITY) &&
-           (at->at_identify.securestatus & ATA_SECURE_FROZEN) == 0) {
+           (at->at_identify.securestatus & ATA_SECURE_FROZEN) == 0 &&
+           (AhciNoFeatures & (1 << ap->ap_num)) == 0) {
                xa = ahci_ata_get_xfer(ap, atx);
                xa->complete = ahci_ata_dummy_done;
                xa->fis->command = ATA_C_SEC_FREEZE_LOCK;
                xa->fis->flags = ATA_H2D_FLAGS_CMD | at->at_target;
-               xa->flags = ATA_F_READ | ATA_F_PIO | ATA_F_POLL;
+               xa->flags = ATA_F_PIO | ATA_F_POLL;
                xa->timeout = 1000;
                xa->datalen = 0;
                if (ahci_ata_cmd(xa) == ATA_S_COMPLETE)
                        at->at_features |= ATA_PORT_F_FRZLCK;
+               else
+                       kprintf("%s: Unable to set security freeze\n",
+                               ATANAME(ap, atx));
                ahci_ata_put_xfer(xa);
        }
 
@@ -587,6 +596,68 @@ ahci_cam_probe_disk(struct ahci_port *ap, struct ata_port *atx)
 static int
 ahci_cam_probe_atapi(struct ahci_port *ap, struct ata_port *atx)
 {
+       ahci_set_xfer(ap, atx);
+       return(0);
+}
+
+/*
+ * Setting the transfer mode is irrelevant for the SATA transport
+ * but some (atapi) devices seem to need it anyway.  In addition
+ * if we are running through a SATA->PATA converter for some reason
+ * beyond my comprehension we might have to set the mode.
+ *
+ * We only support DMA modes for SATA attached devices, so don't bother
+ * with legacy modes.
+ */
+static int
+ahci_set_xfer(struct ahci_port *ap, struct ata_port *atx)
+{
+       struct ata_port *at;
+       struct ata_xfer *xa;
+       u_int16_t mode;
+       u_int16_t mask;
+
+       at = atx ? atx : ap->ap_ata[0];
+
+       /*
+        * Figure out the supported UDMA mode.  Ignore other legacy modes.
+        */
+       mask = le16toh(at->at_identify.ultradma);
+       if ((mask & 0xFF) == 0 || mask == 0xFFFF)
+               return(0);
+       mask &= 0xFF;
+       mode = 0x4F;
+       while ((mask & 0x8000) == 0) {
+               mask <<= 1;
+               --mode;
+       }
+
+       /*
+        * SATA atapi devices often still report a dma mode, even though
+        * it is irrelevant for SATA transport.  It is also possible that
+        * we are running through a SATA->PATA converter and seeing the
+        * PATA dma mode.
+        *
+        * In this case the device may require a (dummy) SETXFER to be
+        * sent before it will work properly.
+        */
+       xa = ahci_ata_get_xfer(ap, atx);
+       xa->complete = ahci_ata_dummy_done;
+       xa->fis->command = ATA_C_SET_FEATURES;
+       xa->fis->features = ATA_SF_SETXFER;
+       xa->fis->flags = ATA_H2D_FLAGS_CMD | at->at_target;
+       xa->fis->sector_count = mode;
+       xa->flags = ATA_F_PIO | ATA_F_POLL;
+       xa->timeout = 1000;
+       xa->datalen = 0;
+       if (ahci_ata_cmd(xa) != ATA_S_COMPLETE) {
+               kprintf("%s: Unable to set dummy xfer mode \n",
+                       ATANAME(ap, atx));
+       } else if (bootverbose) {
+               kprintf("%s: Set dummy xfer mode to %02x\n",
+                       ATANAME(ap, atx), mode);
+       }
+       ahci_ata_put_xfer(xa);
        return(0);
 }
 
@@ -658,7 +729,7 @@ ahci_cam_rescan(struct ahci_port *ap)
        }
        ap->ap_flags |= AP_F_SCAN_RUNNING;
        for (i = 0; i < AHCI_MAX_PMPORTS; ++i) {
-               ap->ap_ata[i].at_features |= ATA_PORT_F_RESCAN;
+               ap->ap_ata[i]->at_features |= ATA_PORT_F_RESCAN;
        }
 
        status = xpt_create_path(&path, xpt_periph, cam_sim_path(ap->ap_sim),
@@ -710,7 +781,6 @@ ahci_xpt_action(struct cam_sim *sim, union ccb *ccb)
 
        /* XXX lock */
        ap = cam_sim_softc(sim);
-       at = ap->ap_ata;
        atx = NULL;
        KKASSERT(ap != NULL);
        ccbh = &ccb->ccb_h;
@@ -745,7 +815,7 @@ ahci_xpt_action(struct cam_sim *sim, union ccb *ccb)
                        xpt_done(ccb);
                        return;
                }
-               at += ccbh->target_id;
+               at = ap->ap_ata[ccbh->target_id];
                if (ap->ap_type == ATA_PORT_T_PM)
                        atx = at;
 
@@ -754,6 +824,8 @@ ahci_xpt_action(struct cam_sim *sim, union ccb *ccb)
                        xpt_done(ccb);
                        return;
                }
+       } else {
+               at = ap->ap_ata[0];
        }
 
        /*
@@ -783,7 +855,7 @@ ahci_xpt_action(struct cam_sim *sim, union ccb *ccb)
                ccb->cpi.hba_misc = PIM_SEQSCAN;
                ccb->cpi.hba_eng_cnt = 0;
                bzero(ccb->cpi.vuhba_flags, sizeof(ccb->cpi.vuhba_flags));
-               ccb->cpi.max_target = AHCI_MAX_PMPORTS;
+               ccb->cpi.max_target = AHCI_MAX_PMPORTS - 1;
                ccb->cpi.max_lun = 0;
                ccb->cpi.async_flags = 0;
                ccb->cpi.hpath_id = 0;
@@ -791,7 +863,7 @@ ahci_xpt_action(struct cam_sim *sim, union ccb *ccb)
                ccb->cpi.unit_number = cam_sim_unit(sim);
                ccb->cpi.bus_id = cam_sim_bus(sim);
                ccb->cpi.base_transfer_speed = 150000;
-               ccb->cpi.transport = XPORT_AHCI;
+               ccb->cpi.transport = XPORT_SATA;
                ccb->cpi.transport_version = 1;
                ccb->cpi.protocol = PROTO_SCSI;
                ccb->cpi.protocol_version = SCSI_REV_2;
@@ -810,6 +882,9 @@ ahci_xpt_action(struct cam_sim *sim, union ccb *ccb)
                        case AHCI_PREG_SSTS_SPD_GEN2:
                                ccb->cpi.base_transfer_speed = 300000;
                                break;
+                       case AHCI_PREG_SSTS_SPD_GEN3:
+                               ccb->cpi.base_transfer_speed = 600000;
+                               break;
                        default:
                                /* unknown */
                                ccb->cpi.base_transfer_speed = 1000;
@@ -847,7 +922,7 @@ ahci_xpt_action(struct cam_sim *sim, union ccb *ccb)
        case XPT_GET_TRAN_SETTINGS:
                ccb->cts.protocol = PROTO_SCSI;
                ccb->cts.protocol_version = SCSI_REV_2;
-               ccb->cts.transport = XPORT_AHCI;
+               ccb->cts.transport = XPORT_SATA;
                ccb->cts.transport_version = XPORT_VERSION_UNSPECIFIED;
                ccb->cts.proto_specific.valid = 0;
                ccb->cts.xport_specific.valid = 0;
@@ -883,6 +958,17 @@ ahci_xpt_action(struct cam_sim *sim, union ccb *ccb)
                        break;
                }
                break;
+       case XPT_TRIM:
+       {
+               scsi_cdb_t cdb;
+               struct ccb_scsiio *csio;
+               csio = &ccb->csio;
+               cdb = (void *)((ccbh->flags & CAM_CDB_POINTER) ?
+                   csio->cdb_io.cdb_ptr : csio->cdb_io.cdb_bytes);
+               cdb->generic.opcode = TRIM;
+               ahci_xpt_scsi_disk_io(ap, atx, ccb);
+               break;
+       }
        default:
                ccbh->status = CAM_REQ_INVALID;
                xpt_done(ccb);
@@ -929,6 +1015,8 @@ ahci_xpt_scsi_disk_io(struct ahci_port *ap, struct ata_port *atx,
        struct ata_xfer *xa;
        struct ata_port *at;
        struct ata_fis_h2d *fis;
+       struct ata_pass_12 *atp12;
+       struct ata_pass_16 *atp16;
        scsi_cdb_t cdb;
        union scsi_data *rdata;
        int rdata_len;
@@ -938,7 +1026,7 @@ ahci_xpt_scsi_disk_io(struct ahci_port *ap, struct ata_port *atx,
 
        ccbh = &ccb->csio.ccb_h;
        csio = &ccb->csio;
-       at = atx ? atx : &ap->ap_ata[0];
+       at = atx ? atx : ap->ap_ata[0];
 
        /*
         * XXX not passing NULL at for direct attach!
@@ -968,17 +1056,7 @@ ahci_xpt_scsi_disk_io(struct ahci_port *ap, struct ata_port *atx,
                 * [opcode, byte2, page_code, length, control]
                 */
                if (cdb->inquiry.byte2 & SI_EVPD) {
-                       switch(cdb->inquiry.page_code) {
-                       case SVPD_SUPPORTED_PAGE_LIST:
-                               /* XXX atascsi_disk_vpd_supported */
-                       case SVPD_UNIT_SERIAL_NUMBER:
-                               /* XXX atascsi_disk_vpd_serial */
-                       case SVPD_UNIT_DEVID:
-                               /* XXX atascsi_disk_vpd_ident */
-                       default:
-                               ccbh->status = CAM_FUNC_NOTAVAIL;
-                               break;
-                       }
+                       ahci_xpt_page_inquiry(ap, at, ccb);
                } else {
                        bzero(rdata, rdata_len);
                        if (rdata_len < SHORT_INQUIRY_LENGTH) {
@@ -1000,6 +1078,17 @@ ahci_xpt_scsi_disk_io(struct ahci_port *ap, struct ata_port *atx,
                              sizeof(rdata->inquiry_data.revision));
                        ccbh->status = CAM_REQ_CMP;
                }
+               
+               /*
+                * Use the vendor specific area to set the TRIM status
+                * for scsi_da
+                */
+               if (at->at_identify.support_dsm) {
+                       rdata->inquiry_data.vendor_specific1[0] =
+                           at->at_identify.support_dsm &ATA_SUPPORT_DSM_TRIM;
+                       rdata->inquiry_data.vendor_specific1[1] = 
+                           at->at_identify.max_dsm_blocks;
+               }
                break;
        case READ_CAPACITY_16:
                if (cdb->read_capacity_16.service_action != SRC16_SERVICE_ACTION) {
@@ -1049,9 +1138,39 @@ ahci_xpt_scsi_disk_io(struct ahci_port *ap, struct ata_port *atx,
                if (xa->timeout < 45000)
                        xa->timeout = 45000;
                xa->datalen = 0;
-               xa->flags = ATA_F_READ;
+               xa->flags = 0;
                xa->complete = ahci_ata_complete_disk_synchronize_cache;
                break;
+       case TRIM:
+               fis = xa->fis;
+               fis->command = ATA_C_DATA_SET_MANAGEMENT;
+               fis->features = (u_int8_t)ATA_SF_DSM_TRIM;
+               fis->features_exp = (u_int8_t)(ATA_SF_DSM_TRIM>> 8);
+
+               xa->flags = ATA_F_WRITE;
+               fis->flags = ATA_H2D_FLAGS_CMD;
+
+               xa->data = csio->data_ptr;
+               xa->datalen = csio->dxfer_len;
+               xa->timeout = ccbh->timeout*50; /* milliseconds */
+
+               fis->sector_count =(u_int8_t)(xa->datalen/512);
+               fis->sector_count_exp =(u_int8_t)((xa->datalen/512)>>8);
+
+               lba = 0;
+               fis->lba_low = (u_int8_t)lba;
+               fis->lba_mid = (u_int8_t)(lba >> 8);
+               fis->lba_high = (u_int8_t)(lba >> 16);
+               fis->lba_low_exp = (u_int8_t)(lba >> 24);
+               fis->lba_mid_exp = (u_int8_t)(lba >> 32);
+               fis->lba_high_exp = (u_int8_t)(lba >> 40);
+                  
+               fis->device = ATA_H2D_DEVICE_LBA;
+               xa->data = csio->data_ptr;
+
+               xa->complete = ahci_ata_complete_disk_rw;
+               ccbh->status = CAM_REQ_INPROG;
+               break;
        case TEST_UNIT_READY:
        case START_STOP_UNIT:
        case PREVENT_ALLOW:
@@ -1062,11 +1181,92 @@ ahci_xpt_scsi_disk_io(struct ahci_port *ap, struct ata_port *atx,
                rdata_len = 0;
                break;
        case ATA_PASS_12:
+               atp12 = &cdb->ata_pass_12;
+               fis = xa->fis;
+               /*
+                * Figure out the flags to be used, depending on the direction of the
+                * CAM request.
+                */
+               switch (ccbh->flags & CAM_DIR_MASK) {
+               case CAM_DIR_IN:
+                       xa->flags = ATA_F_READ;
+                       break;
+               case CAM_DIR_OUT:
+                       xa->flags = ATA_F_WRITE;
+                       break;
+               default:
+                       xa->flags = 0;
+               }
+               xa->flags |= ATA_F_POLL | ATA_F_EXCLUSIVE;
+               xa->data = csio->data_ptr;
+               xa->datalen = csio->dxfer_len;
+               xa->complete = ahci_ata_complete_disk_rw;
+               xa->timeout = ccbh->timeout;
+
+               /*
+                * Populate the fis from the information we received through CAM
+                * ATA passthrough.
+                */
+               fis->flags = ATA_H2D_FLAGS_CMD; /* maybe also atp12->flags ? */
+               fis->features = atp12->features;
+               fis->sector_count = atp12->sector_count;
+               fis->lba_low = atp12->lba_low;
+               fis->lba_mid = atp12->lba_mid;
+               fis->lba_high = atp12->lba_high;
+               fis->device = atp12->device;    /* maybe always 0? */
+               fis->command = atp12->command;
+               fis->control = atp12->control;
+
+               /*
+                * Mark as in progress so it is sent to the device.
+                */
+               ccbh->status = CAM_REQ_INPROG;
+               break;
        case ATA_PASS_16:
+               atp16 = &cdb->ata_pass_16;
+               fis = xa->fis;
                /*
-                * XXX implement pass-through
+                * Figure out the flags to be used, depending on the direction of the
+                * CAM request.
                 */
-               ccbh->status = CAM_FUNC_NOTAVAIL;
+               switch (ccbh->flags & CAM_DIR_MASK) {
+               case CAM_DIR_IN:
+                       xa->flags = ATA_F_READ;
+                       break;
+               case CAM_DIR_OUT:
+                       xa->flags = ATA_F_WRITE;
+                       break;
+               default:
+                       xa->flags = 0;
+               }
+               xa->flags |= ATA_F_POLL | ATA_F_EXCLUSIVE;
+               xa->data = csio->data_ptr;
+               xa->datalen = csio->dxfer_len;
+               xa->complete = ahci_ata_complete_disk_rw;
+               xa->timeout = ccbh->timeout;
+
+               /*
+                * Populate the fis from the information we received through CAM
+                * ATA passthrough.
+                */
+               fis->flags = ATA_H2D_FLAGS_CMD; /* maybe also atp16->flags ? */
+               fis->features = atp16->features;
+               fis->features_exp = atp16->features_ext;
+               fis->sector_count = atp16->sector_count;
+               fis->sector_count_exp = atp16->sector_count_ext;
+               fis->lba_low = atp16->lba_low;
+               fis->lba_low_exp = atp16->lba_low_ext;
+               fis->lba_mid = atp16->lba_mid;
+               fis->lba_mid_exp = atp16->lba_mid_ext;
+               fis->lba_high = atp16->lba_high;
+               fis->lba_mid_exp = atp16->lba_mid_ext;
+               fis->device = atp16->device;    /* maybe always 0? */
+               fis->command = atp16->command;
+
+               /*
+                * Mark as in progress so it is sent to the device.
+                */
+               ccbh->status = CAM_REQ_INPROG;
                break;
        default:
                switch(cdb->generic.opcode) {
@@ -1144,7 +1344,7 @@ ahci_xpt_scsi_disk_io(struct ahci_port *ap, struct ata_port *atx,
                        fis->sector_count = xa->tag << 3;
                        fis->features = (u_int8_t)count;
                        fis->features_exp = (u_int8_t)(count >> 8);
-               } else if (count > 0x100 || lba > 0xFFFFFFFFU) {
+               } else if (count > 0x100 || lba > 0x0FFFFFFFU) {
                        /*
                         * Use LBA48
                         */
@@ -1171,6 +1371,10 @@ ahci_xpt_scsi_disk_io(struct ahci_port *ap, struct ata_port *atx,
                xa->datalen = csio->dxfer_len;
                xa->complete = ahci_ata_complete_disk_rw;
                xa->timeout = ccbh->timeout;    /* milliseconds */
+#if 0
+               if (xa->timeout > 10000)        /* XXX - debug */
+                       xa->timeout = 10000;
+#endif
                if (ccbh->flags & CAM_POLLED)
                        xa->flags |= ATA_F_POLL;
                break;
@@ -1178,15 +1382,15 @@ ahci_xpt_scsi_disk_io(struct ahci_port *ap, struct ata_port *atx,
 
        /*
         * If the request is still in progress the xa and FIS have
-        * been set up and must be dispatched.  Otherwise the request
-        * is complete.
+        * been set up (except for the PM target), and must be dispatched.
+        * Otherwise the request was completed.
         */
        if (ccbh->status == CAM_REQ_INPROG) {
                KKASSERT(xa->complete != NULL);
                xa->atascsi_private = ccb;
                ccb->ccb_h.sim_priv.entries[0].ptr = ap;
                ahci_os_lock_port(ap);
-               fis->flags |= at->at_target;
+               xa->fis->flags |= at->at_target;
                ahci_ata_cmd(xa);
                ahci_os_unlock_port(ap);
        } else {
@@ -1217,7 +1421,7 @@ ahci_xpt_scsi_atapi_io(struct ahci_port *ap, struct ata_port *atx,
 
        ccbh = &ccb->csio.ccb_h;
        csio = &ccb->csio;
-       at = atx ? atx : &ap->ap_ata[0];
+       at = atx ? atx : ap->ap_ata[0];
 
        switch (ccbh->flags & CAM_DIR_MASK) {
        case CAM_DIR_IN:
@@ -1236,6 +1440,13 @@ ahci_xpt_scsi_atapi_io(struct ahci_port *ap, struct ata_port *atx,
                /* NOT REACHED */
        }
 
+       /*
+        * Special handling to get the rfis back into host memory while
+        * still allowing the chip to run commands in parallel to
+        * ATAPI devices behind a PM.
+        */
+       flags |= ATA_F_AUTOSENSE;
+
        /*
         * The command has to fit in the packet command buffer.
         */
@@ -1246,7 +1457,8 @@ ahci_xpt_scsi_atapi_io(struct ahci_port *ap, struct ata_port *atx,
        }
 
        /*
-        * Initialize the XA and FIS.
+        * Initialize the XA and FIS.  It is unclear how much of
+        * this has to mimic the equivalent ATA command.
         *
         * XXX not passing NULL at for direct attach!
         */
@@ -1255,13 +1467,21 @@ ahci_xpt_scsi_atapi_io(struct ahci_port *ap, struct ata_port *atx,
 
        fis->flags = ATA_H2D_FLAGS_CMD | at->at_target;
        fis->command = ATA_C_PACKET;
-       fis->device = 0;
+       fis->device = ATA_H2D_DEVICE_LBA;
        fis->sector_count = xa->tag << 3;
-       fis->features = ATA_H2D_FEATURES_DMA |
-                   ((flags & ATA_F_WRITE) ?
-                   ATA_H2D_FEATURES_DIR_WRITE : ATA_H2D_FEATURES_DIR_READ);
-       fis->lba_mid = 0x00;
-       fis->lba_high = 0x20;
+       if (flags & (ATA_F_READ | ATA_F_WRITE)) {
+               if (flags & ATA_F_WRITE) {
+                       fis->features = ATA_H2D_FEATURES_DMA |
+                                      ATA_H2D_FEATURES_DIR_WRITE;
+               } else {
+                       fis->features = ATA_H2D_FEATURES_DMA |
+                                      ATA_H2D_FEATURES_DIR_READ;
+               }
+       } else {
+               fis->lba_mid = 0;
+               fis->lba_high = 0;
+       }
+       fis->control = ATA_FIS_CONTROL_4BIT;
 
        xa->flags = flags;
        xa->data = csio->data_ptr;
@@ -1274,10 +1494,14 @@ ahci_xpt_scsi_atapi_io(struct ahci_port *ap, struct ata_port *atx,
        /*
         * Copy the cdb to the packetcmd buffer in the FIS using a
         * convenient pointer in the xa.
+        *
+        * Zero-out any trailing bytes in case the ATAPI device cares.
         */
        cdbs = (void *)((ccbh->flags & CAM_CDB_POINTER) ?
                        csio->cdb_io.cdb_ptr : csio->cdb_io.cdb_bytes);
        bcopy(cdbs, xa->packetcmd, csio->cdb_len);
+       if (csio->cdb_len < 16)
+               bzero(xa->packetcmd + csio->cdb_len, 16 - csio->cdb_len);
 
 #if 0
        kprintf("opcode %d cdb_len %d dxfer_len %d\n",
@@ -1291,18 +1515,26 @@ ahci_xpt_scsi_atapi_io(struct ahci_port *ap, struct ata_port *atx,
        cdbd = (void *)xa->packetcmd;
 
        switch(cdbd->generic.opcode) {
-       case INQUIRY:
+       case REQUEST_SENSE:
                /*
-                * Some ATAPI devices can't handle SI_EVPD being set
-                * for a basic inquiry (page_code == 0).
+                * Force SENSE requests to the ATAPI sense length.
                 *
+                * It is unclear if this is needed or not.
+                */
+               if (cdbd->sense.length == SSD_FULL_SIZE) {
+                       if (bootverbose) {
+                               kprintf("%s: Shortening sense request\n",
+                                       PORTNAME(ap));
+                       }
+                       cdbd->sense.length = offsetof(struct scsi_sense_data,
+                                                     extra_bytes[0]);
+               }
+               break;
+       case INQUIRY:
+               /*
                 * Some ATAPI devices can't handle long inquiry lengths,
                 * don't ask me why.  Truncate the inquiry length.
                 */
-               if ((cdbd->inquiry.byte2 & SI_EVPD) &&
-                   cdbd->inquiry.page_code == 0) {
-                       cdbd->inquiry.byte2 &= ~SI_EVPD;
-               }
                if (cdbd->inquiry.page_code == 0 &&
                    cdbd->inquiry.length > SHORT_INQUIRY_LENGTH) {
                        cdbd->inquiry.length = SHORT_INQUIRY_LENGTH;
@@ -1335,7 +1567,78 @@ ahci_xpt_scsi_atapi_io(struct ahci_port *ap, struct ata_port *atx,
        xa->complete = ahci_atapi_complete_cmd;
        xa->atascsi_private = ccb;
        ccb->ccb_h.sim_priv.entries[0].ptr = ap;
+       ahci_os_lock_port(ap);
        ahci_ata_cmd(xa);
+       ahci_os_unlock_port(ap);
+}
+
+/*
+ * Simulate page inquiries for disk attachments.
+ */
+static
+void
+ahci_xpt_page_inquiry(struct ahci_port *ap, struct ata_port *at, union ccb *ccb)
+{
+       union {
+               struct scsi_vpd_supported_page_list     list;
+               struct scsi_vpd_unit_serial_number      serno;
+               struct scsi_vpd_unit_devid              devid;
+               char                                    buf[256];
+       } *page;
+       scsi_cdb_t cdb;
+       int i;
+       int j;
+       int len;
+
+       page = kmalloc(sizeof(*page), M_DEVBUF, M_WAITOK | M_ZERO);
+
+       cdb = (void *)((ccb->ccb_h.flags & CAM_CDB_POINTER) ?
+                       ccb->csio.cdb_io.cdb_ptr : ccb->csio.cdb_io.cdb_bytes);
+
+       switch(cdb->inquiry.page_code) {
+       case SVPD_SUPPORTED_PAGE_LIST:
+               i = 0;
+               page->list.device = T_DIRECT;
+               page->list.page_code = SVPD_SUPPORTED_PAGE_LIST;
+               page->list.list[i++] = SVPD_SUPPORTED_PAGE_LIST;
+               page->list.list[i++] = SVPD_UNIT_SERIAL_NUMBER;
+               page->list.list[i++] = SVPD_UNIT_DEVID;
+               page->list.length = i;
+               len = offsetof(struct scsi_vpd_supported_page_list, list[3]);
+               break;
+       case SVPD_UNIT_SERIAL_NUMBER:
+               i = 0;
+               j = sizeof(at->at_identify.serial);
+               for (i = 0; i < j && at->at_identify.serial[i] == ' '; ++i)
+                       ;
+               while (j > i && at->at_identify.serial[j-1] == ' ')
+                       --j;
+               page->serno.device = T_DIRECT;
+               page->serno.page_code = SVPD_UNIT_SERIAL_NUMBER;
+               page->serno.length = j - i;
+               bcopy(at->at_identify.serial + i,
+                     page->serno.serial_num, j - i);
+               len = offsetof(struct scsi_vpd_unit_serial_number,
+                              serial_num[j-i]);
+               break;
+       case SVPD_UNIT_DEVID:
+               /* fall through for now */
+       default:
+               ccb->ccb_h.status = CAM_FUNC_NOTAVAIL;
+               len = 0;
+               break;
+       }
+       if (ccb->ccb_h.status == CAM_REQ_INPROG) {
+               if (len <= ccb->csio.dxfer_len) {
+                       ccb->ccb_h.status = CAM_REQ_CMP;
+                       bzero(ccb->csio.data_ptr, ccb->csio.dxfer_len);
+                       bcopy(page, ccb->csio.data_ptr, len);
+                       ccb->csio.resid = ccb->csio.dxfer_len - len;
+               } else {
+                       ccb->ccb_h.status = CAM_CCB_LEN_ERR;
+               }
+       }
+       kfree(page, M_DEVBUF);
 }
 
 /*
@@ -1442,8 +1745,6 @@ ahci_atapi_complete_cmd(struct ata_xfer *xa)
                ccb->csio.scsi_status = SCSI_STATUS_OK;
                break;
        case ATA_S_ERROR:
-               kprintf("%s: cmd %d: error\n",
-                       PORTNAME(ap), cdb->generic.opcode);
                ccbh->status = CAM_SCSI_STATUS_ERROR;
                ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND;
                ahci_ata_atapi_sense(&xa->rfis, &ccb->csio.sense_data);
@@ -1511,3 +1812,20 @@ ahci_ata_atapi_sense(struct ata_fis_d2h *rfis,
        sense_data->info[3] = 0;
        sense_data->extra_len = 0;
 }
+
+static
+void
+ahci_strip_string(const char **basep, int *lenp)
+{
+       const char *base = *basep;
+       int len = *lenp;
+
+       while (len && (*base == 0 || *base == ' ')) {
+               --len;
+               ++base;
+       }
+       while (len && (base[len-1] == 0 || base[len-1] == ' '))
+               --len;
+       *basep = base;
+       *lenp = len;
+}