1258223a3SMatthew Dillon /* 2258223a3SMatthew Dillon * Copyright (c) 2009 The DragonFly Project. All rights reserved. 3258223a3SMatthew Dillon * 4258223a3SMatthew Dillon * This code is derived from software contributed to The DragonFly Project 5258223a3SMatthew Dillon * by Matthew Dillon <dillon@backplane.com> 6258223a3SMatthew Dillon * 7258223a3SMatthew Dillon * Redistribution and use in source and binary forms, with or without 8258223a3SMatthew Dillon * modification, are permitted provided that the following conditions 9258223a3SMatthew Dillon * are met: 10258223a3SMatthew Dillon * 11258223a3SMatthew Dillon * 1. Redistributions of source code must retain the above copyright 12258223a3SMatthew Dillon * notice, this list of conditions and the following disclaimer. 13258223a3SMatthew Dillon * 2. Redistributions in binary form must reproduce the above copyright 14258223a3SMatthew Dillon * notice, this list of conditions and the following disclaimer in 15258223a3SMatthew Dillon * the documentation and/or other materials provided with the 16258223a3SMatthew Dillon * distribution. 17258223a3SMatthew Dillon * 3. Neither the name of The DragonFly Project nor the names of its 18258223a3SMatthew Dillon * contributors may be used to endorse or promote products derived 19258223a3SMatthew Dillon * from this software without specific, prior written permission. 20258223a3SMatthew Dillon * 21258223a3SMatthew Dillon * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22258223a3SMatthew Dillon * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23258223a3SMatthew Dillon * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24258223a3SMatthew Dillon * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25258223a3SMatthew Dillon * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26258223a3SMatthew Dillon * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 27258223a3SMatthew Dillon * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28258223a3SMatthew Dillon * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29258223a3SMatthew Dillon * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30258223a3SMatthew Dillon * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31258223a3SMatthew Dillon * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32258223a3SMatthew Dillon * SUCH DAMAGE. 33258223a3SMatthew Dillon * 34258223a3SMatthew Dillon * 35258223a3SMatthew Dillon * Copyright (c) 2007 David Gwynne <dlg@openbsd.org> 36258223a3SMatthew Dillon * 37258223a3SMatthew Dillon * Permission to use, copy, modify, and distribute this software for any 38258223a3SMatthew Dillon * purpose with or without fee is hereby granted, provided that the above 39258223a3SMatthew Dillon * copyright notice and this permission notice appear in all copies. 40258223a3SMatthew Dillon * 41258223a3SMatthew Dillon * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 42258223a3SMatthew Dillon * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 43258223a3SMatthew Dillon * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 44258223a3SMatthew Dillon * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 45258223a3SMatthew Dillon * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 46258223a3SMatthew Dillon * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 47258223a3SMatthew Dillon * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 48258223a3SMatthew Dillon * 49258223a3SMatthew Dillon * $OpenBSD: atascsi.c,v 1.64 2009/02/16 21:19:06 miod Exp $ 50258223a3SMatthew Dillon * $DragonFly$ 51258223a3SMatthew Dillon */ 52258223a3SMatthew Dillon /* 53258223a3SMatthew Dillon * Implement each SATA port as its own SCSI bus on CAM. This way we can 54258223a3SMatthew Dillon * implement future port multiplier features as individual devices on the 55258223a3SMatthew Dillon * bus. 56258223a3SMatthew Dillon * 57258223a3SMatthew Dillon * Much of the cdb<->xa conversion code was taken from OpenBSD, the rest 58258223a3SMatthew Dillon * was written natively for DragonFly. 59258223a3SMatthew Dillon */ 60258223a3SMatthew Dillon 61258223a3SMatthew Dillon #include "ahci.h" 62258223a3SMatthew Dillon 63b4189e5eSMatthew Dillon const char *ScsiTypeArray[32] = { 64b4189e5eSMatthew Dillon "DIRECT", 65b4189e5eSMatthew Dillon "SEQUENTIAL", 66b4189e5eSMatthew Dillon "PRINTER", 67b4189e5eSMatthew Dillon "PROCESSOR", 68b4189e5eSMatthew Dillon "WORM", 69b4189e5eSMatthew Dillon "CDROM", 70b4189e5eSMatthew Dillon "SCANNER", 71b4189e5eSMatthew Dillon "OPTICAL", 72b4189e5eSMatthew Dillon "CHANGER", 73b4189e5eSMatthew Dillon "COMM", 74b4189e5eSMatthew Dillon "ASC0", 75b4189e5eSMatthew Dillon "ASC1", 76b4189e5eSMatthew Dillon "STORARRAY", 77b4189e5eSMatthew Dillon "ENCLOSURE", 78b4189e5eSMatthew Dillon "RBC", 79b4189e5eSMatthew Dillon "OCRW", 80b4189e5eSMatthew Dillon "0x10", 81b4189e5eSMatthew Dillon "OSD", 82b4189e5eSMatthew Dillon "ADC", 83b4189e5eSMatthew Dillon "0x13", 84b4189e5eSMatthew Dillon "0x14", 85b4189e5eSMatthew Dillon "0x15", 86b4189e5eSMatthew Dillon "0x16", 87b4189e5eSMatthew Dillon "0x17", 88b4189e5eSMatthew Dillon "0x18", 89b4189e5eSMatthew Dillon "0x19", 90b4189e5eSMatthew Dillon "0x1A", 91b4189e5eSMatthew Dillon "0x1B", 92b4189e5eSMatthew Dillon "0x1C", 93b4189e5eSMatthew Dillon "0x1D", 94b4189e5eSMatthew Dillon "0x1E", 95b4189e5eSMatthew Dillon "NODEVICE" 96b4189e5eSMatthew Dillon }; 97b4189e5eSMatthew Dillon 98258223a3SMatthew Dillon static void ahci_xpt_action(struct cam_sim *sim, union ccb *ccb); 99258223a3SMatthew Dillon static void ahci_xpt_poll(struct cam_sim *sim); 1001980eff3SMatthew Dillon static void ahci_xpt_scsi_disk_io(struct ahci_port *ap, 1011980eff3SMatthew Dillon struct ata_port *at, union ccb *ccb); 1021980eff3SMatthew Dillon static void ahci_xpt_scsi_atapi_io(struct ahci_port *ap, 1031980eff3SMatthew Dillon struct ata_port *at, union ccb *ccb); 104*6e0003adSMatthew Dillon static void ahci_xpt_page_inquiry(struct ahci_port *ap, 105*6e0003adSMatthew Dillon struct ata_port *at, union ccb *ccb); 106258223a3SMatthew Dillon 107258223a3SMatthew Dillon static void ahci_ata_complete_disk_rw(struct ata_xfer *xa); 108258223a3SMatthew Dillon static void ahci_ata_complete_disk_synchronize_cache(struct ata_xfer *xa); 109b4189e5eSMatthew Dillon static void ahci_atapi_complete_cmd(struct ata_xfer *xa); 110b4189e5eSMatthew Dillon static void ahci_ata_dummy_sense(struct scsi_sense_data *sense_data); 1117d4fcf34SMatthew Dillon static void ahci_ata_atapi_sense(struct ata_fis_d2h *rfis, 1127d4fcf34SMatthew Dillon struct scsi_sense_data *sense_data); 113258223a3SMatthew Dillon 1141980eff3SMatthew Dillon static int ahci_cam_probe_disk(struct ahci_port *ap, struct ata_port *at); 1151980eff3SMatthew Dillon static int ahci_cam_probe_atapi(struct ahci_port *ap, struct ata_port *at); 116b4189e5eSMatthew Dillon static void ahci_ata_dummy_done(struct ata_xfer *xa); 117258223a3SMatthew Dillon static void ata_fix_identify(struct ata_identify *id); 118258223a3SMatthew Dillon static void ahci_cam_rescan(struct ahci_port *ap); 119*6e0003adSMatthew Dillon static void ahci_strip_string(const char **basep, int *lenp); 120258223a3SMatthew Dillon 121258223a3SMatthew Dillon int 122258223a3SMatthew Dillon ahci_cam_attach(struct ahci_port *ap) 123258223a3SMatthew Dillon { 124258223a3SMatthew Dillon struct cam_devq *devq; 125258223a3SMatthew Dillon struct cam_sim *sim; 126258223a3SMatthew Dillon int error; 127258223a3SMatthew Dillon int unit; 128258223a3SMatthew Dillon 129cec85a37SMatthew Dillon /* 130cec85a37SMatthew Dillon * We want at least one ccb to be available for error processing 131cec85a37SMatthew Dillon * so don't let CAM use more then ncmds - 1. 132cec85a37SMatthew Dillon */ 133258223a3SMatthew Dillon unit = device_get_unit(ap->ap_sc->sc_dev); 134cec85a37SMatthew Dillon if (ap->ap_sc->sc_ncmds > 1) 135cec85a37SMatthew Dillon devq = cam_simq_alloc(ap->ap_sc->sc_ncmds - 1); 136cec85a37SMatthew Dillon else 137258223a3SMatthew Dillon devq = cam_simq_alloc(ap->ap_sc->sc_ncmds); 138258223a3SMatthew Dillon if (devq == NULL) { 139258223a3SMatthew Dillon return (ENOMEM); 140258223a3SMatthew Dillon } 141258223a3SMatthew Dillon sim = cam_sim_alloc(ahci_xpt_action, ahci_xpt_poll, "ahci", 142258223a3SMatthew Dillon (void *)ap, unit, &sim_mplock, 1, 1, devq); 143258223a3SMatthew Dillon cam_simq_release(devq); 144258223a3SMatthew Dillon if (sim == NULL) { 145258223a3SMatthew Dillon return (ENOMEM); 146258223a3SMatthew Dillon } 147258223a3SMatthew Dillon ap->ap_sim = sim; 148831bc9e3SMatthew Dillon ahci_os_unlock_port(ap); 149258223a3SMatthew Dillon error = xpt_bus_register(ap->ap_sim, ap->ap_num); 150831bc9e3SMatthew Dillon ahci_os_lock_port(ap); 151258223a3SMatthew Dillon if (error != CAM_SUCCESS) { 152258223a3SMatthew Dillon ahci_cam_detach(ap); 153258223a3SMatthew Dillon return (EINVAL); 154258223a3SMatthew Dillon } 155258223a3SMatthew Dillon ap->ap_flags |= AP_F_BUS_REGISTERED; 156258223a3SMatthew Dillon 157c408a8b3SMatthew Dillon if (ap->ap_probe == ATA_PROBE_NEED_IDENT) 1581980eff3SMatthew Dillon error = ahci_cam_probe(ap, NULL); 159c408a8b3SMatthew Dillon else 160c408a8b3SMatthew Dillon error = 0; 161258223a3SMatthew Dillon if (error) { 162258223a3SMatthew Dillon ahci_cam_detach(ap); 163258223a3SMatthew Dillon return (EIO); 164258223a3SMatthew Dillon } 165258223a3SMatthew Dillon ap->ap_flags |= AP_F_CAM_ATTACHED; 166258223a3SMatthew Dillon 167258223a3SMatthew Dillon return(0); 168258223a3SMatthew Dillon } 169258223a3SMatthew Dillon 1701980eff3SMatthew Dillon /* 1713209f581SMatthew Dillon * The state of the port has changed. 1723209f581SMatthew Dillon * 1733209f581SMatthew Dillon * If at is NULL the physical port has changed state. 1743209f581SMatthew Dillon * If at is non-NULL a particular target behind a PM has changed state. 1753209f581SMatthew Dillon * 1763209f581SMatthew Dillon * If found is -1 the target state must be queued to a non-interrupt context. 1773209f581SMatthew Dillon * (only works with at == NULL). 1783209f581SMatthew Dillon * 1793209f581SMatthew Dillon * If found is 0 the target was removed. 1803209f581SMatthew Dillon * If found is 1 the target was inserted. 1811980eff3SMatthew Dillon */ 182258223a3SMatthew Dillon void 1833209f581SMatthew Dillon ahci_cam_changed(struct ahci_port *ap, struct ata_port *atx, int found) 184258223a3SMatthew Dillon { 185fd8bd957SMatthew Dillon struct cam_path *tmppath; 1863209f581SMatthew Dillon int status; 1873209f581SMatthew Dillon int target; 1883209f581SMatthew Dillon 1893209f581SMatthew Dillon target = atx ? atx->at_target : CAM_TARGET_WILDCARD; 190fd8bd957SMatthew Dillon 191258223a3SMatthew Dillon if (ap->ap_sim == NULL) 192258223a3SMatthew Dillon return; 1933209f581SMatthew Dillon if (found == CAM_TARGET_WILDCARD) { 1943209f581SMatthew Dillon status = xpt_create_path(&tmppath, NULL, 1953209f581SMatthew Dillon cam_sim_path(ap->ap_sim), 1963209f581SMatthew Dillon target, CAM_LUN_WILDCARD); 1973209f581SMatthew Dillon if (status != CAM_REQ_CMP) 198fd8bd957SMatthew Dillon return; 199258223a3SMatthew Dillon ahci_cam_rescan(ap); 200fd8bd957SMatthew Dillon } else { 2013209f581SMatthew Dillon status = xpt_create_path(&tmppath, NULL, 2023209f581SMatthew Dillon cam_sim_path(ap->ap_sim), 2033209f581SMatthew Dillon target, 2043209f581SMatthew Dillon CAM_LUN_WILDCARD); 2053209f581SMatthew Dillon if (status != CAM_REQ_CMP) 2063209f581SMatthew Dillon return; 2073209f581SMatthew Dillon #if 0 2083209f581SMatthew Dillon /* 2093209f581SMatthew Dillon * This confuses CAM 2103209f581SMatthew Dillon */ 2113209f581SMatthew Dillon if (found) 2123209f581SMatthew Dillon xpt_async(AC_FOUND_DEVICE, tmppath, NULL); 2133209f581SMatthew Dillon else 214fd8bd957SMatthew Dillon xpt_async(AC_LOST_DEVICE, tmppath, NULL); 2153209f581SMatthew Dillon #endif 216fd8bd957SMatthew Dillon } 217fd8bd957SMatthew Dillon xpt_free_path(tmppath); 218258223a3SMatthew Dillon } 219258223a3SMatthew Dillon 220258223a3SMatthew Dillon void 221258223a3SMatthew Dillon ahci_cam_detach(struct ahci_port *ap) 222258223a3SMatthew Dillon { 223258223a3SMatthew Dillon int error; 224258223a3SMatthew Dillon 225258223a3SMatthew Dillon if ((ap->ap_flags & AP_F_CAM_ATTACHED) == 0) 226258223a3SMatthew Dillon return; 227258223a3SMatthew Dillon get_mplock(); 228258223a3SMatthew Dillon if (ap->ap_sim) { 229258223a3SMatthew Dillon xpt_freeze_simq(ap->ap_sim, 1); 230258223a3SMatthew Dillon } 231258223a3SMatthew Dillon if (ap->ap_flags & AP_F_BUS_REGISTERED) { 232258223a3SMatthew Dillon error = xpt_bus_deregister(cam_sim_path(ap->ap_sim)); 233258223a3SMatthew Dillon KKASSERT(error == CAM_REQ_CMP); 234258223a3SMatthew Dillon ap->ap_flags &= ~AP_F_BUS_REGISTERED; 235258223a3SMatthew Dillon } 236258223a3SMatthew Dillon if (ap->ap_sim) { 237258223a3SMatthew Dillon cam_sim_free(ap->ap_sim); 238258223a3SMatthew Dillon ap->ap_sim = NULL; 239258223a3SMatthew Dillon } 240258223a3SMatthew Dillon rel_mplock(); 241258223a3SMatthew Dillon ap->ap_flags &= ~AP_F_CAM_ATTACHED; 242258223a3SMatthew Dillon } 243258223a3SMatthew Dillon 244258223a3SMatthew Dillon /* 2451980eff3SMatthew Dillon * Once the AHCI port has been attached we need to probe for a device or 246258223a3SMatthew Dillon * devices on the port and setup various options. 2471980eff3SMatthew Dillon * 2481980eff3SMatthew Dillon * If at is NULL we are probing the direct-attached device on the port, 2491980eff3SMatthew Dillon * which may or may not be a port multiplier. 250258223a3SMatthew Dillon */ 2513209f581SMatthew Dillon int 2521980eff3SMatthew Dillon ahci_cam_probe(struct ahci_port *ap, struct ata_port *atx) 253258223a3SMatthew Dillon { 2541980eff3SMatthew Dillon struct ata_port *at; 255258223a3SMatthew Dillon struct ata_xfer *xa; 256258223a3SMatthew Dillon u_int64_t capacity; 257258223a3SMatthew Dillon u_int64_t capacity_bytes; 258258223a3SMatthew Dillon int model_len; 259*6e0003adSMatthew Dillon int firmware_len; 260*6e0003adSMatthew Dillon int serial_len; 261fd8bd957SMatthew Dillon int error; 262258223a3SMatthew Dillon int devncqdepth; 263258223a3SMatthew Dillon int i; 264*6e0003adSMatthew Dillon const char *model_id; 265*6e0003adSMatthew Dillon const char *firmware_id; 266*6e0003adSMatthew Dillon const char *serial_id; 267669fbbf7SMatthew Dillon const char *wcstr; 268669fbbf7SMatthew Dillon const char *rastr; 269fd8bd957SMatthew Dillon const char *scstr; 270fd8bd957SMatthew Dillon const char *type; 271fd8bd957SMatthew Dillon 2723209f581SMatthew Dillon error = EIO; 2731980eff3SMatthew Dillon 2741980eff3SMatthew Dillon /* 275f4553de1SMatthew Dillon * Delayed CAM attachment for initial probe, sim may be NULL 276f4553de1SMatthew Dillon */ 277f4553de1SMatthew Dillon if (ap->ap_sim == NULL) 278f4553de1SMatthew Dillon return(0); 279f4553de1SMatthew Dillon 280f4553de1SMatthew Dillon /* 2811980eff3SMatthew Dillon * A NULL atx indicates a probe of the directly connected device. 2821980eff3SMatthew Dillon * A non-NULL atx indicates a device connected via a port multiplier. 2831980eff3SMatthew Dillon * We need to preserve atx for calls to ahci_ata_get_xfer(). 2841980eff3SMatthew Dillon * 2851980eff3SMatthew Dillon * at is always non-NULL. For directly connected devices we supply 2861980eff3SMatthew Dillon * an (at) pointing to target 0. 2871980eff3SMatthew Dillon */ 2881980eff3SMatthew Dillon if (atx == NULL) { 2893209f581SMatthew Dillon at = ap->ap_ata; /* direct attached - device 0 */ 2901980eff3SMatthew Dillon if (ap->ap_type == ATA_PORT_T_PM) { 291831bc9e3SMatthew Dillon kprintf("%s: Found Port Multiplier\n", 292831bc9e3SMatthew Dillon ATANAME(ap, atx)); 2931980eff3SMatthew Dillon return (0); 2941980eff3SMatthew Dillon } 2951980eff3SMatthew Dillon at->at_type = ap->ap_type; 2961980eff3SMatthew Dillon } else { 2973209f581SMatthew Dillon at = atx; 2981980eff3SMatthew Dillon if (atx->at_type == ATA_PORT_T_PM) { 2991980eff3SMatthew Dillon kprintf("%s: Bogus device, reducing port count to %d\n", 3001980eff3SMatthew Dillon ATANAME(ap, atx), atx->at_target); 3011980eff3SMatthew Dillon if (ap->ap_pmcount > atx->at_target) 3021980eff3SMatthew Dillon ap->ap_pmcount = atx->at_target; 3033209f581SMatthew Dillon goto err; 3041980eff3SMatthew Dillon } 3051980eff3SMatthew Dillon } 3063209f581SMatthew Dillon if (ap->ap_type == ATA_PORT_T_NONE) 3073209f581SMatthew Dillon goto err; 3081980eff3SMatthew Dillon if (at->at_type == ATA_PORT_T_NONE) 3093209f581SMatthew Dillon goto err; 310258223a3SMatthew Dillon 311258223a3SMatthew Dillon /* 312258223a3SMatthew Dillon * Issue identify, saving the result 313258223a3SMatthew Dillon */ 3141980eff3SMatthew Dillon xa = ahci_ata_get_xfer(ap, atx); 315258223a3SMatthew Dillon xa->complete = ahci_ata_dummy_done; 3161980eff3SMatthew Dillon xa->data = &at->at_identify; 3171980eff3SMatthew Dillon xa->datalen = sizeof(at->at_identify); 31812feb904SMatthew Dillon xa->flags = ATA_F_READ | ATA_F_PIO | ATA_F_POLL; 3191980eff3SMatthew Dillon xa->fis->flags = ATA_H2D_FLAGS_CMD | at->at_target; 3201980eff3SMatthew Dillon 3211980eff3SMatthew Dillon switch(at->at_type) { 3221980eff3SMatthew Dillon case ATA_PORT_T_DISK: 323258223a3SMatthew Dillon xa->fis->command = ATA_C_IDENTIFY; 324fd8bd957SMatthew Dillon type = "DISK"; 3251980eff3SMatthew Dillon break; 3261980eff3SMatthew Dillon case ATA_PORT_T_ATAPI: 3271980eff3SMatthew Dillon xa->fis->command = ATA_C_ATAPI_IDENTIFY; 32812feb904SMatthew Dillon xa->flags |= ATA_F_AUTOSENSE; 3291980eff3SMatthew Dillon type = "ATAPI"; 3301980eff3SMatthew Dillon break; 3311980eff3SMatthew Dillon default: 3321980eff3SMatthew Dillon xa->fis->command = ATA_C_ATAPI_IDENTIFY; 3331980eff3SMatthew Dillon type = "UNKNOWN(ATAPI?)"; 3341980eff3SMatthew Dillon break; 335fd8bd957SMatthew Dillon } 336258223a3SMatthew Dillon xa->fis->features = 0; 337258223a3SMatthew Dillon xa->fis->device = 0; 3383209f581SMatthew Dillon xa->timeout = 1000; 339258223a3SMatthew Dillon 340831bc9e3SMatthew Dillon if (ahci_ata_cmd(xa) != ATA_S_COMPLETE) { 341fd8bd957SMatthew Dillon kprintf("%s: Detected %s device but unable to IDENTIFY\n", 3421980eff3SMatthew Dillon ATANAME(ap, atx), type); 343258223a3SMatthew Dillon ahci_ata_put_xfer(xa); 3443209f581SMatthew Dillon goto err; 345258223a3SMatthew Dillon } 346258223a3SMatthew Dillon ahci_ata_put_xfer(xa); 347258223a3SMatthew Dillon 3481980eff3SMatthew Dillon ata_fix_identify(&at->at_identify); 349258223a3SMatthew Dillon 350258223a3SMatthew Dillon /* 351258223a3SMatthew Dillon * Read capacity using SATA probe info. 352258223a3SMatthew Dillon */ 3531980eff3SMatthew Dillon if (le16toh(at->at_identify.cmdset83) & 0x0400) { 354258223a3SMatthew Dillon /* LBA48 feature set supported */ 355258223a3SMatthew Dillon capacity = 0; 356258223a3SMatthew Dillon for (i = 3; i >= 0; --i) { 357258223a3SMatthew Dillon capacity <<= 16; 358258223a3SMatthew Dillon capacity += 3591980eff3SMatthew Dillon le16toh(at->at_identify.addrsecxt[i]); 360258223a3SMatthew Dillon } 361258223a3SMatthew Dillon } else { 3621980eff3SMatthew Dillon capacity = le16toh(at->at_identify.addrsec[1]); 363258223a3SMatthew Dillon capacity <<= 16; 3641980eff3SMatthew Dillon capacity += le16toh(at->at_identify.addrsec[0]); 365258223a3SMatthew Dillon } 36612feb904SMatthew Dillon if (capacity == 0) 36712feb904SMatthew Dillon capacity = 1024 * 1024 / 512; 3681980eff3SMatthew Dillon at->at_capacity = capacity; 3691980eff3SMatthew Dillon if (atx == NULL) 3701980eff3SMatthew Dillon ap->ap_probe = ATA_PROBE_GOOD; 371258223a3SMatthew Dillon 372258223a3SMatthew Dillon capacity_bytes = capacity * 512; 373258223a3SMatthew Dillon 374258223a3SMatthew Dillon /* 375258223a3SMatthew Dillon * Negotiate NCQ, throw away any ata_xfer's beyond the negotiated 376258223a3SMatthew Dillon * number of slots and limit the number of CAM ccb's to one less 377258223a3SMatthew Dillon * so we always have a slot available for recovery. 378258223a3SMatthew Dillon * 379258223a3SMatthew Dillon * NCQ is not used if ap_ncqdepth is 1 or the host controller does 380258223a3SMatthew Dillon * not support it, and in that case the driver can handle extra 381258223a3SMatthew Dillon * ccb's. 382cec85a37SMatthew Dillon * 3831980eff3SMatthew Dillon * NCQ is currently used only with direct-attached disks. It is 3841980eff3SMatthew Dillon * not used with port multipliers or direct-attached ATAPI devices. 3851980eff3SMatthew Dillon * 386cec85a37SMatthew Dillon * Remember at least one extra CCB needs to be reserved for the 387cec85a37SMatthew Dillon * error ccb. 388258223a3SMatthew Dillon */ 389258223a3SMatthew Dillon if ((ap->ap_sc->sc_cap & AHCI_REG_CAP_SNCQ) && 3901980eff3SMatthew Dillon ap->ap_type == ATA_PORT_T_DISK && 3911980eff3SMatthew Dillon (le16toh(at->at_identify.satacap) & (1 << 8))) { 3921980eff3SMatthew Dillon at->at_ncqdepth = (le16toh(at->at_identify.qdepth) & 0x1F) + 1; 3931980eff3SMatthew Dillon devncqdepth = at->at_ncqdepth; 3941980eff3SMatthew Dillon if (at->at_ncqdepth > ap->ap_sc->sc_ncmds) 3951980eff3SMatthew Dillon at->at_ncqdepth = ap->ap_sc->sc_ncmds; 3961980eff3SMatthew Dillon if (at->at_ncqdepth > 1) { 397258223a3SMatthew Dillon for (i = 0; i < ap->ap_sc->sc_ncmds; ++i) { 3981980eff3SMatthew Dillon xa = ahci_ata_get_xfer(ap, atx); 3991980eff3SMatthew Dillon if (xa->tag < at->at_ncqdepth) { 400258223a3SMatthew Dillon xa->state = ATA_S_COMPLETE; 401258223a3SMatthew Dillon ahci_ata_put_xfer(xa); 402258223a3SMatthew Dillon } 403258223a3SMatthew Dillon } 4041980eff3SMatthew Dillon if (at->at_ncqdepth >= ap->ap_sc->sc_ncmds) { 405258223a3SMatthew Dillon cam_devq_resize(ap->ap_sim->devq, 4061980eff3SMatthew Dillon at->at_ncqdepth - 1); 407258223a3SMatthew Dillon } 408cec85a37SMatthew Dillon } 409258223a3SMatthew Dillon } else { 410258223a3SMatthew Dillon devncqdepth = 0; 411258223a3SMatthew Dillon } 412258223a3SMatthew Dillon 413*6e0003adSMatthew Dillon model_len = sizeof(at->at_identify.model); 414*6e0003adSMatthew Dillon model_id = at->at_identify.model; 415*6e0003adSMatthew Dillon ahci_strip_string(&model_id, &model_len); 416*6e0003adSMatthew Dillon 417*6e0003adSMatthew Dillon firmware_len = sizeof(at->at_identify.firmware); 418*6e0003adSMatthew Dillon firmware_id = at->at_identify.firmware; 419*6e0003adSMatthew Dillon ahci_strip_string(&firmware_id, &firmware_len); 420*6e0003adSMatthew Dillon 421*6e0003adSMatthew Dillon serial_len = sizeof(at->at_identify.serial); 422*6e0003adSMatthew Dillon serial_id = at->at_identify.serial; 423*6e0003adSMatthew Dillon ahci_strip_string(&serial_id, &serial_len); 424669fbbf7SMatthew Dillon 425fd8bd957SMatthew Dillon /* 426fd8bd957SMatthew Dillon * Generate informatiive strings. 427fd8bd957SMatthew Dillon * 428fd8bd957SMatthew Dillon * NOTE: We do not automatically set write caching, lookahead, 429fd8bd957SMatthew Dillon * or the security state for ATAPI devices. 430fd8bd957SMatthew Dillon */ 4311980eff3SMatthew Dillon if (at->at_identify.cmdset82 & ATA_IDENTIFY_WRITECACHE) { 4321980eff3SMatthew Dillon if (at->at_identify.features85 & ATA_IDENTIFY_WRITECACHE) 433669fbbf7SMatthew Dillon wcstr = "enabled"; 4341980eff3SMatthew Dillon else if (at->at_type == ATA_PORT_T_ATAPI) 435fd8bd957SMatthew Dillon wcstr = "disabled"; 436669fbbf7SMatthew Dillon else 437669fbbf7SMatthew Dillon wcstr = "enabling"; 438669fbbf7SMatthew Dillon } else { 439669fbbf7SMatthew Dillon wcstr = "notsupp"; 440669fbbf7SMatthew Dillon } 441669fbbf7SMatthew Dillon 4421980eff3SMatthew Dillon if (at->at_identify.cmdset82 & ATA_IDENTIFY_LOOKAHEAD) { 4431980eff3SMatthew Dillon if (at->at_identify.features85 & ATA_IDENTIFY_LOOKAHEAD) 444669fbbf7SMatthew Dillon rastr = "enabled"; 4451980eff3SMatthew Dillon else if (at->at_type == ATA_PORT_T_ATAPI) 446fd8bd957SMatthew Dillon rastr = "disabled"; 447669fbbf7SMatthew Dillon else 448669fbbf7SMatthew Dillon rastr = "enabling"; 449669fbbf7SMatthew Dillon } else { 450669fbbf7SMatthew Dillon rastr = "notsupp"; 451669fbbf7SMatthew Dillon } 452669fbbf7SMatthew Dillon 4531980eff3SMatthew Dillon if (at->at_identify.cmdset82 & ATA_IDENTIFY_SECURITY) { 4541980eff3SMatthew Dillon if (at->at_identify.securestatus & ATA_SECURE_FROZEN) 455fd8bd957SMatthew Dillon scstr = "frozen"; 4561980eff3SMatthew Dillon else if (at->at_type == ATA_PORT_T_ATAPI) 457fd8bd957SMatthew Dillon scstr = "unfrozen"; 458afa796d2SMatthew Dillon else if (AhciNoFeatures & (1 << ap->ap_num)) 459afa796d2SMatthew Dillon scstr = "<disabled>"; 460fd8bd957SMatthew Dillon else 461fd8bd957SMatthew Dillon scstr = "freezing"; 462fd8bd957SMatthew Dillon } else { 463fd8bd957SMatthew Dillon scstr = "notsupp"; 464fd8bd957SMatthew Dillon } 465fd8bd957SMatthew Dillon 466*6e0003adSMatthew Dillon kprintf("%s: Found %s \"%*.*s %*.*s\" serial=\"%*.*s\"\n" 467074579dfSMatthew Dillon "%s: tags=%d/%d satacap=%04x satafea=%04x NCQ=%s " 468074579dfSMatthew Dillon "capacity=%lld.%02dMB\n", 469074579dfSMatthew Dillon 4701980eff3SMatthew Dillon ATANAME(ap, atx), 471fd8bd957SMatthew Dillon type, 472*6e0003adSMatthew Dillon model_len, model_len, model_id, 473*6e0003adSMatthew Dillon firmware_len, firmware_len, firmware_id, 474*6e0003adSMatthew Dillon serial_len, serial_len, serial_id, 475258223a3SMatthew Dillon 4761980eff3SMatthew Dillon ATANAME(ap, atx), 477258223a3SMatthew Dillon devncqdepth, ap->ap_sc->sc_ncmds, 4781980eff3SMatthew Dillon at->at_identify.satacap, 4791980eff3SMatthew Dillon at->at_identify.satafsup, 480074579dfSMatthew Dillon (at->at_ncqdepth > 1 ? "YES" : "NO"), 481258223a3SMatthew Dillon (long long)capacity_bytes / (1024 * 1024), 482074579dfSMatthew Dillon (int)(capacity_bytes % (1024 * 1024)) * 100 / (1024 * 1024) 483074579dfSMatthew Dillon ); 484074579dfSMatthew Dillon kprintf("%s: f85=%04x f86=%04x f87=%04x WC=%s RA=%s SEC=%s\n", 4851980eff3SMatthew Dillon ATANAME(ap, atx), 4861980eff3SMatthew Dillon at->at_identify.features85, 4871980eff3SMatthew Dillon at->at_identify.features86, 4881980eff3SMatthew Dillon at->at_identify.features87, 489669fbbf7SMatthew Dillon wcstr, 490fd8bd957SMatthew Dillon rastr, 491fd8bd957SMatthew Dillon scstr 492258223a3SMatthew Dillon ); 493258223a3SMatthew Dillon 494258223a3SMatthew Dillon /* 495fd8bd957SMatthew Dillon * Additional type-specific probing 496fd8bd957SMatthew Dillon */ 4971980eff3SMatthew Dillon switch(at->at_type) { 498fd8bd957SMatthew Dillon case ATA_PORT_T_DISK: 4991980eff3SMatthew Dillon error = ahci_cam_probe_disk(ap, atx); 5001980eff3SMatthew Dillon break; 5011980eff3SMatthew Dillon case ATA_PORT_T_ATAPI: 5021980eff3SMatthew Dillon error = ahci_cam_probe_atapi(ap, atx); 503fd8bd957SMatthew Dillon break; 504fd8bd957SMatthew Dillon default: 5051980eff3SMatthew Dillon error = EIO; 506fd8bd957SMatthew Dillon break; 507fd8bd957SMatthew Dillon } 5083209f581SMatthew Dillon err: 5093209f581SMatthew Dillon if (error) { 5103209f581SMatthew Dillon at->at_probe = ATA_PROBE_FAILED; 5113209f581SMatthew Dillon if (atx == NULL) 5123209f581SMatthew Dillon ap->ap_probe = at->at_probe; 5133209f581SMatthew Dillon } else { 5143209f581SMatthew Dillon at->at_probe = ATA_PROBE_GOOD; 5153209f581SMatthew Dillon if (atx == NULL) 5163209f581SMatthew Dillon ap->ap_probe = at->at_probe; 5173209f581SMatthew Dillon } 5183209f581SMatthew Dillon return (error); 519fd8bd957SMatthew Dillon } 520fd8bd957SMatthew Dillon 521fd8bd957SMatthew Dillon /* 522fd8bd957SMatthew Dillon * DISK-specific probe after initial ident 523fd8bd957SMatthew Dillon */ 524fd8bd957SMatthew Dillon static int 5251980eff3SMatthew Dillon ahci_cam_probe_disk(struct ahci_port *ap, struct ata_port *atx) 526fd8bd957SMatthew Dillon { 5271980eff3SMatthew Dillon struct ata_port *at; 528fd8bd957SMatthew Dillon struct ata_xfer *xa; 529fd8bd957SMatthew Dillon 5301980eff3SMatthew Dillon at = atx ? atx : ap->ap_ata; 5311980eff3SMatthew Dillon 532fd8bd957SMatthew Dillon /* 533258223a3SMatthew Dillon * Enable write cache if supported 534fd8bd957SMatthew Dillon * 535fd8bd957SMatthew Dillon * NOTE: "WD My Book" external disk devices have a very poor 536fd8bd957SMatthew Dillon * daughter board between the the ESATA and the HD. Sending 537fd8bd957SMatthew Dillon * any ATA_C_SET_FEATURES commands will break the hardware port 538fd8bd957SMatthew Dillon * with a fatal protocol error. However, this device also 539fd8bd957SMatthew Dillon * indicates that WRITECACHE is already on and READAHEAD is 540fd8bd957SMatthew Dillon * not supported so we avoid the issue. 541258223a3SMatthew Dillon */ 5421980eff3SMatthew Dillon if ((at->at_identify.cmdset82 & ATA_IDENTIFY_WRITECACHE) && 5431980eff3SMatthew Dillon (at->at_identify.features85 & ATA_IDENTIFY_WRITECACHE) == 0) { 5441980eff3SMatthew Dillon xa = ahci_ata_get_xfer(ap, atx); 545258223a3SMatthew Dillon xa->complete = ahci_ata_dummy_done; 546258223a3SMatthew Dillon xa->fis->command = ATA_C_SET_FEATURES; 547669fbbf7SMatthew Dillon /*xa->fis->features = ATA_SF_WRITECACHE_EN;*/ 548669fbbf7SMatthew Dillon xa->fis->features = ATA_SF_LOOKAHEAD_EN; 5491980eff3SMatthew Dillon xa->fis->flags = ATA_H2D_FLAGS_CMD | at->at_target; 550669fbbf7SMatthew Dillon xa->fis->device = 0; 551258223a3SMatthew Dillon xa->flags = ATA_F_READ | ATA_F_PIO | ATA_F_POLL; 5523209f581SMatthew Dillon xa->timeout = 1000; 553669fbbf7SMatthew Dillon xa->datalen = 0; 554831bc9e3SMatthew Dillon if (ahci_ata_cmd(xa) == ATA_S_COMPLETE) 5551980eff3SMatthew Dillon at->at_features |= ATA_PORT_F_WCACHE; 556afa796d2SMatthew Dillon else 557afa796d2SMatthew Dillon kprintf("%s: Unable to enable write-caching\n", 558afa796d2SMatthew Dillon ATANAME(ap, atx)); 559258223a3SMatthew Dillon ahci_ata_put_xfer(xa); 560258223a3SMatthew Dillon } 561258223a3SMatthew Dillon 562258223a3SMatthew Dillon /* 563258223a3SMatthew Dillon * Enable readahead if supported 564258223a3SMatthew Dillon */ 5651980eff3SMatthew Dillon if ((at->at_identify.cmdset82 & ATA_IDENTIFY_LOOKAHEAD) && 5661980eff3SMatthew Dillon (at->at_identify.features85 & ATA_IDENTIFY_LOOKAHEAD) == 0) { 5671980eff3SMatthew Dillon xa = ahci_ata_get_xfer(ap, atx); 568258223a3SMatthew Dillon xa->complete = ahci_ata_dummy_done; 569258223a3SMatthew Dillon xa->fis->command = ATA_C_SET_FEATURES; 570258223a3SMatthew Dillon xa->fis->features = ATA_SF_LOOKAHEAD_EN; 5711980eff3SMatthew Dillon xa->fis->flags = ATA_H2D_FLAGS_CMD | at->at_target; 572669fbbf7SMatthew Dillon xa->fis->device = 0; 573258223a3SMatthew Dillon xa->flags = ATA_F_READ | ATA_F_PIO | ATA_F_POLL; 5743209f581SMatthew Dillon xa->timeout = 1000; 575669fbbf7SMatthew Dillon xa->datalen = 0; 576831bc9e3SMatthew Dillon if (ahci_ata_cmd(xa) == ATA_S_COMPLETE) 5771980eff3SMatthew Dillon at->at_features |= ATA_PORT_F_RAHEAD; 578afa796d2SMatthew Dillon else 579afa796d2SMatthew Dillon kprintf("%s: Unable to enable read-ahead\n", 580afa796d2SMatthew Dillon ATANAME(ap, atx)); 581258223a3SMatthew Dillon ahci_ata_put_xfer(xa); 582258223a3SMatthew Dillon } 583258223a3SMatthew Dillon 584258223a3SMatthew Dillon /* 585258223a3SMatthew Dillon * FREEZE LOCK the device so malicious users can't lock it on us. 586258223a3SMatthew Dillon * As there is no harm in issuing this to devices that don't 587258223a3SMatthew Dillon * support the security feature set we just send it, and don't bother 588258223a3SMatthew Dillon * checking if the device sends a command abort to tell us it doesn't 589258223a3SMatthew Dillon * support it 590258223a3SMatthew Dillon */ 5911980eff3SMatthew Dillon if ((at->at_identify.cmdset82 & ATA_IDENTIFY_SECURITY) && 592afa796d2SMatthew Dillon (at->at_identify.securestatus & ATA_SECURE_FROZEN) == 0 && 593afa796d2SMatthew Dillon (AhciNoFeatures & (1 << ap->ap_num)) == 0) { 5941980eff3SMatthew Dillon xa = ahci_ata_get_xfer(ap, atx); 595258223a3SMatthew Dillon xa->complete = ahci_ata_dummy_done; 596258223a3SMatthew Dillon xa->fis->command = ATA_C_SEC_FREEZE_LOCK; 5971980eff3SMatthew Dillon xa->fis->flags = ATA_H2D_FLAGS_CMD | at->at_target; 598258223a3SMatthew Dillon xa->flags = ATA_F_READ | ATA_F_PIO | ATA_F_POLL; 5993209f581SMatthew Dillon xa->timeout = 1000; 600669fbbf7SMatthew Dillon xa->datalen = 0; 601831bc9e3SMatthew Dillon if (ahci_ata_cmd(xa) == ATA_S_COMPLETE) 6021980eff3SMatthew Dillon at->at_features |= ATA_PORT_F_FRZLCK; 603afa796d2SMatthew Dillon else 604afa796d2SMatthew Dillon kprintf("%s: Unable to set security freeze\n", 605afa796d2SMatthew Dillon ATANAME(ap, atx)); 606258223a3SMatthew Dillon ahci_ata_put_xfer(xa); 607669fbbf7SMatthew Dillon } 608258223a3SMatthew Dillon 609b4189e5eSMatthew Dillon return (0); 610b4189e5eSMatthew Dillon } 611b4189e5eSMatthew Dillon 612fd8bd957SMatthew Dillon /* 613fd8bd957SMatthew Dillon * ATAPI-specific probe after initial ident 614fd8bd957SMatthew Dillon */ 615b4189e5eSMatthew Dillon static int 6161980eff3SMatthew Dillon ahci_cam_probe_atapi(struct ahci_port *ap, struct ata_port *atx) 617b4189e5eSMatthew Dillon { 618fd8bd957SMatthew Dillon return(0); 619fd8bd957SMatthew Dillon } 620fd8bd957SMatthew Dillon 621b4189e5eSMatthew Dillon /* 622b4189e5eSMatthew Dillon * Fix byte ordering so buffers can be accessed as 623b4189e5eSMatthew Dillon * strings. 624b4189e5eSMatthew Dillon */ 625258223a3SMatthew Dillon static void 626258223a3SMatthew Dillon ata_fix_identify(struct ata_identify *id) 627258223a3SMatthew Dillon { 628258223a3SMatthew Dillon u_int16_t *swap; 629258223a3SMatthew Dillon int i; 630258223a3SMatthew Dillon 631258223a3SMatthew Dillon swap = (u_int16_t *)id->serial; 632258223a3SMatthew Dillon for (i = 0; i < sizeof(id->serial) / sizeof(u_int16_t); i++) 633258223a3SMatthew Dillon swap[i] = bswap16(swap[i]); 634258223a3SMatthew Dillon 635258223a3SMatthew Dillon swap = (u_int16_t *)id->firmware; 636258223a3SMatthew Dillon for (i = 0; i < sizeof(id->firmware) / sizeof(u_int16_t); i++) 637258223a3SMatthew Dillon swap[i] = bswap16(swap[i]); 638258223a3SMatthew Dillon 639258223a3SMatthew Dillon swap = (u_int16_t *)id->model; 640258223a3SMatthew Dillon for (i = 0; i < sizeof(id->model) / sizeof(u_int16_t); i++) 641258223a3SMatthew Dillon swap[i] = bswap16(swap[i]); 642258223a3SMatthew Dillon } 643258223a3SMatthew Dillon 644258223a3SMatthew Dillon /* 645b4189e5eSMatthew Dillon * Dummy done callback for xa. 646b4189e5eSMatthew Dillon */ 647b4189e5eSMatthew Dillon static void 648b4189e5eSMatthew Dillon ahci_ata_dummy_done(struct ata_xfer *xa) 649b4189e5eSMatthew Dillon { 650b4189e5eSMatthew Dillon } 651b4189e5eSMatthew Dillon 652b4189e5eSMatthew Dillon /* 6533209f581SMatthew Dillon * Use an engineering request to initiate a target scan for devices 6543209f581SMatthew Dillon * behind a port multiplier. 655fd8bd957SMatthew Dillon * 6563209f581SMatthew Dillon * An asynchronous bus scan is used to avoid reentrancy issues. 657258223a3SMatthew Dillon */ 658258223a3SMatthew Dillon static void 659258223a3SMatthew Dillon ahci_cam_rescan_callback(struct cam_periph *periph, union ccb *ccb) 660258223a3SMatthew Dillon { 6613209f581SMatthew Dillon struct ahci_port *ap = ccb->ccb_h.sim_priv.entries[0].ptr; 6623209f581SMatthew Dillon 663f4553de1SMatthew Dillon if (ccb->ccb_h.func_code == XPT_SCAN_BUS) { 6643209f581SMatthew Dillon ap->ap_flags &= ~AP_F_SCAN_RUNNING; 6653209f581SMatthew Dillon if (ap->ap_flags & AP_F_SCAN_REQUESTED) { 6663209f581SMatthew Dillon ap->ap_flags &= ~AP_F_SCAN_REQUESTED; 6673209f581SMatthew Dillon ahci_cam_rescan(ap); 6683209f581SMatthew Dillon } 669f4553de1SMatthew Dillon ap->ap_flags |= AP_F_SCAN_COMPLETED; 670f4553de1SMatthew Dillon wakeup(&ap->ap_flags); 671f4553de1SMatthew Dillon } 672f4553de1SMatthew Dillon xpt_free_ccb(ccb); 673258223a3SMatthew Dillon } 674258223a3SMatthew Dillon 675258223a3SMatthew Dillon static void 676258223a3SMatthew Dillon ahci_cam_rescan(struct ahci_port *ap) 677258223a3SMatthew Dillon { 678258223a3SMatthew Dillon struct cam_path *path; 679258223a3SMatthew Dillon union ccb *ccb; 680258223a3SMatthew Dillon int status; 6813209f581SMatthew Dillon int i; 6823209f581SMatthew Dillon 6833209f581SMatthew Dillon if (ap->ap_flags & AP_F_SCAN_RUNNING) { 6843209f581SMatthew Dillon ap->ap_flags |= AP_F_SCAN_REQUESTED; 6853209f581SMatthew Dillon return; 6863209f581SMatthew Dillon } 6873209f581SMatthew Dillon ap->ap_flags |= AP_F_SCAN_RUNNING; 6883209f581SMatthew Dillon for (i = 0; i < AHCI_MAX_PMPORTS; ++i) { 6893209f581SMatthew Dillon ap->ap_ata[i].at_features |= ATA_PORT_F_RESCAN; 6903209f581SMatthew Dillon } 691258223a3SMatthew Dillon 692258223a3SMatthew Dillon status = xpt_create_path(&path, xpt_periph, cam_sim_path(ap->ap_sim), 693258223a3SMatthew Dillon CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD); 694258223a3SMatthew Dillon if (status != CAM_REQ_CMP) 695258223a3SMatthew Dillon return; 696258223a3SMatthew Dillon 697f4553de1SMatthew Dillon ccb = xpt_alloc_ccb(); 698258223a3SMatthew Dillon xpt_setup_ccb(&ccb->ccb_h, path, 5); /* 5 = low priority */ 6992de5e9baSMatthew Dillon ccb->ccb_h.func_code = XPT_ENG_EXEC; 700258223a3SMatthew Dillon ccb->ccb_h.cbfcnp = ahci_cam_rescan_callback; 7013209f581SMatthew Dillon ccb->ccb_h.sim_priv.entries[0].ptr = ap; 702258223a3SMatthew Dillon ccb->crcn.flags = CAM_FLAG_NONE; 7032de5e9baSMatthew Dillon xpt_action_async(ccb); 704258223a3SMatthew Dillon } 705258223a3SMatthew Dillon 7063209f581SMatthew Dillon static void 7073209f581SMatthew Dillon ahci_xpt_rescan(struct ahci_port *ap) 7083209f581SMatthew Dillon { 7093209f581SMatthew Dillon struct cam_path *path; 7103209f581SMatthew Dillon union ccb *ccb; 7113209f581SMatthew Dillon int status; 7123209f581SMatthew Dillon 7133209f581SMatthew Dillon status = xpt_create_path(&path, xpt_periph, cam_sim_path(ap->ap_sim), 7143209f581SMatthew Dillon CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD); 7153209f581SMatthew Dillon if (status != CAM_REQ_CMP) 7163209f581SMatthew Dillon return; 717f4553de1SMatthew Dillon 718f4553de1SMatthew Dillon ccb = xpt_alloc_ccb(); 7193209f581SMatthew Dillon xpt_setup_ccb(&ccb->ccb_h, path, 5); /* 5 = low priority */ 7202de5e9baSMatthew Dillon ccb->ccb_h.func_code = XPT_SCAN_BUS; 7213209f581SMatthew Dillon ccb->ccb_h.cbfcnp = ahci_cam_rescan_callback; 7223209f581SMatthew Dillon ccb->ccb_h.sim_priv.entries[0].ptr = ap; 7233209f581SMatthew Dillon ccb->crcn.flags = CAM_FLAG_NONE; 724baef7501SMatthew Dillon xpt_action_async(ccb); 7253209f581SMatthew Dillon } 7263209f581SMatthew Dillon 727258223a3SMatthew Dillon /* 728258223a3SMatthew Dillon * Action function - dispatch command 729258223a3SMatthew Dillon */ 730258223a3SMatthew Dillon static 731258223a3SMatthew Dillon void 732258223a3SMatthew Dillon ahci_xpt_action(struct cam_sim *sim, union ccb *ccb) 733258223a3SMatthew Dillon { 734258223a3SMatthew Dillon struct ahci_port *ap; 7351980eff3SMatthew Dillon struct ata_port *at, *atx; 736258223a3SMatthew Dillon struct ccb_hdr *ccbh; 737258223a3SMatthew Dillon int unit; 738258223a3SMatthew Dillon 739258223a3SMatthew Dillon /* XXX lock */ 740258223a3SMatthew Dillon ap = cam_sim_softc(sim); 7411980eff3SMatthew Dillon at = ap->ap_ata; 7421980eff3SMatthew Dillon atx = NULL; 743258223a3SMatthew Dillon KKASSERT(ap != NULL); 744258223a3SMatthew Dillon ccbh = &ccb->ccb_h; 745258223a3SMatthew Dillon unit = cam_sim_unit(sim); 746258223a3SMatthew Dillon 747258223a3SMatthew Dillon /* 7483209f581SMatthew Dillon * Early failure checks. These checks do not apply to XPT_PATH_INQ, 7493209f581SMatthew Dillon * otherwise the bus rescan will not remove the dead devices when 7503209f581SMatthew Dillon * unplugging a PM. 7513209f581SMatthew Dillon * 7521980eff3SMatthew Dillon * For non-wildcards we have one target (0) and one lun (0), 7531980eff3SMatthew Dillon * unless we have a port multiplier. 7541980eff3SMatthew Dillon * 7551980eff3SMatthew Dillon * A wildcard target indicates only the general bus is being 7561980eff3SMatthew Dillon * probed. 7571980eff3SMatthew Dillon * 7581980eff3SMatthew Dillon * Calculate at and atx. at is always non-NULL. atx is only 7591980eff3SMatthew Dillon * non-NULL for direct-attached devices. It will be NULL for 7601980eff3SMatthew Dillon * devices behind a port multiplier. 761258223a3SMatthew Dillon * 762258223a3SMatthew Dillon * XXX What do we do with a LUN wildcard? 763258223a3SMatthew Dillon */ 7643209f581SMatthew Dillon if (ccbh->target_id != CAM_TARGET_WILDCARD && 7653209f581SMatthew Dillon ccbh->func_code != XPT_PATH_INQ) { 7661980eff3SMatthew Dillon if (ap->ap_type == ATA_PORT_T_NONE) { 7673209f581SMatthew Dillon ccbh->status = CAM_DEV_NOT_THERE; 768258223a3SMatthew Dillon xpt_done(ccb); 769258223a3SMatthew Dillon return; 770258223a3SMatthew Dillon } 7711980eff3SMatthew Dillon if (ccbh->target_id < 0 || ccbh->target_id >= ap->ap_pmcount) { 772258223a3SMatthew Dillon ccbh->status = CAM_DEV_NOT_THERE; 773258223a3SMatthew Dillon xpt_done(ccb); 774258223a3SMatthew Dillon return; 775258223a3SMatthew Dillon } 7761980eff3SMatthew Dillon at += ccbh->target_id; 7771980eff3SMatthew Dillon if (ap->ap_type == ATA_PORT_T_PM) 7781980eff3SMatthew Dillon atx = at; 7791980eff3SMatthew Dillon 780258223a3SMatthew Dillon if (ccbh->target_lun != CAM_LUN_WILDCARD && ccbh->target_lun) { 781258223a3SMatthew Dillon ccbh->status = CAM_DEV_NOT_THERE; 782258223a3SMatthew Dillon xpt_done(ccb); 783258223a3SMatthew Dillon return; 784258223a3SMatthew Dillon } 785258223a3SMatthew Dillon } 786258223a3SMatthew Dillon 787258223a3SMatthew Dillon /* 788258223a3SMatthew Dillon * Switch on the meta XPT command 789258223a3SMatthew Dillon */ 790258223a3SMatthew Dillon switch(ccbh->func_code) { 7913209f581SMatthew Dillon case XPT_ENG_EXEC: 7923209f581SMatthew Dillon /* 7933209f581SMatthew Dillon * This routine is called after a port multiplier has been 7943209f581SMatthew Dillon * probed. 7953209f581SMatthew Dillon */ 7963209f581SMatthew Dillon ccbh->status = CAM_REQ_CMP; 797f4553de1SMatthew Dillon ahci_os_lock_port(ap); 798831bc9e3SMatthew Dillon ahci_port_state_machine(ap, 0); 799f4553de1SMatthew Dillon ahci_os_unlock_port(ap); 8003209f581SMatthew Dillon xpt_done(ccb); 8013209f581SMatthew Dillon ahci_xpt_rescan(ap); 8023209f581SMatthew Dillon break; 803258223a3SMatthew Dillon case XPT_PATH_INQ: 8043209f581SMatthew Dillon /* 8053209f581SMatthew Dillon * This command always succeeds, otherwise the bus scan 8063209f581SMatthew Dillon * will not detach dead devices. 8073209f581SMatthew Dillon */ 808258223a3SMatthew Dillon ccb->cpi.version_num = 1; 809258223a3SMatthew Dillon ccb->cpi.hba_inquiry = 0; 810258223a3SMatthew Dillon ccb->cpi.target_sprt = 0; 8111980eff3SMatthew Dillon ccb->cpi.hba_misc = PIM_SEQSCAN; 812258223a3SMatthew Dillon ccb->cpi.hba_eng_cnt = 0; 813258223a3SMatthew Dillon bzero(ccb->cpi.vuhba_flags, sizeof(ccb->cpi.vuhba_flags)); 81476497a9cSMatthew Dillon ccb->cpi.max_target = AHCI_MAX_PMPORTS - 1; 815258223a3SMatthew Dillon ccb->cpi.max_lun = 0; 816258223a3SMatthew Dillon ccb->cpi.async_flags = 0; 817258223a3SMatthew Dillon ccb->cpi.hpath_id = 0; 8181980eff3SMatthew Dillon ccb->cpi.initiator_id = AHCI_MAX_PMPORTS - 1; 819258223a3SMatthew Dillon ccb->cpi.unit_number = cam_sim_unit(sim); 820258223a3SMatthew Dillon ccb->cpi.bus_id = cam_sim_bus(sim); 821258223a3SMatthew Dillon ccb->cpi.base_transfer_speed = 150000; 8222cc2e845SMatthew Dillon ccb->cpi.transport = XPORT_SATA; 823258223a3SMatthew Dillon ccb->cpi.transport_version = 1; 824258223a3SMatthew Dillon ccb->cpi.protocol = PROTO_SCSI; 825258223a3SMatthew Dillon ccb->cpi.protocol_version = SCSI_REV_2; 826258223a3SMatthew Dillon 8273209f581SMatthew Dillon ccbh->status = CAM_REQ_CMP; 828831bc9e3SMatthew Dillon if (ccbh->target_id == CAM_TARGET_WILDCARD) { 829831bc9e3SMatthew Dillon ahci_os_lock_port(ap); 830831bc9e3SMatthew Dillon ahci_port_state_machine(ap, 0); 831831bc9e3SMatthew Dillon ahci_os_unlock_port(ap); 832831bc9e3SMatthew Dillon } else { 833258223a3SMatthew Dillon switch(ahci_pread(ap, AHCI_PREG_SSTS) & 834258223a3SMatthew Dillon AHCI_PREG_SSTS_SPD) { 835258223a3SMatthew Dillon case AHCI_PREG_SSTS_SPD_GEN1: 836258223a3SMatthew Dillon ccb->cpi.base_transfer_speed = 150000; 837258223a3SMatthew Dillon break; 838258223a3SMatthew Dillon case AHCI_PREG_SSTS_SPD_GEN2: 839258223a3SMatthew Dillon ccb->cpi.base_transfer_speed = 300000; 840258223a3SMatthew Dillon break; 841258223a3SMatthew Dillon default: 842258223a3SMatthew Dillon /* unknown */ 843258223a3SMatthew Dillon ccb->cpi.base_transfer_speed = 1000; 844258223a3SMatthew Dillon break; 845258223a3SMatthew Dillon } 8463209f581SMatthew Dillon #if 0 8471980eff3SMatthew Dillon if (ap->ap_type == ATA_PORT_T_NONE) 8481980eff3SMatthew Dillon ccbh->status = CAM_DEV_NOT_THERE; 8493209f581SMatthew Dillon #endif 8501980eff3SMatthew Dillon } 851258223a3SMatthew Dillon xpt_done(ccb); 852258223a3SMatthew Dillon break; 853258223a3SMatthew Dillon case XPT_RESET_DEV: 854f4553de1SMatthew Dillon ahci_os_lock_port(ap); 8551980eff3SMatthew Dillon if (ap->ap_type == ATA_PORT_T_NONE) { 8563209f581SMatthew Dillon ccbh->status = CAM_DEV_NOT_THERE; 8571980eff3SMatthew Dillon } else { 8581980eff3SMatthew Dillon ahci_port_reset(ap, atx, 0); 859fd8bd957SMatthew Dillon ccbh->status = CAM_REQ_CMP; 8601980eff3SMatthew Dillon } 861f4553de1SMatthew Dillon ahci_os_unlock_port(ap); 862258223a3SMatthew Dillon xpt_done(ccb); 863258223a3SMatthew Dillon break; 864258223a3SMatthew Dillon case XPT_RESET_BUS: 865f4553de1SMatthew Dillon ahci_os_lock_port(ap); 8661980eff3SMatthew Dillon ahci_port_reset(ap, NULL, 1); 867f4553de1SMatthew Dillon ahci_os_unlock_port(ap); 868fd8bd957SMatthew Dillon ccbh->status = CAM_REQ_CMP; 869258223a3SMatthew Dillon xpt_done(ccb); 870258223a3SMatthew Dillon break; 871258223a3SMatthew Dillon case XPT_SET_TRAN_SETTINGS: 872258223a3SMatthew Dillon ccbh->status = CAM_FUNC_NOTAVAIL; 873258223a3SMatthew Dillon xpt_done(ccb); 874258223a3SMatthew Dillon break; 875258223a3SMatthew Dillon case XPT_GET_TRAN_SETTINGS: 876258223a3SMatthew Dillon ccb->cts.protocol = PROTO_SCSI; 877258223a3SMatthew Dillon ccb->cts.protocol_version = SCSI_REV_2; 8782cc2e845SMatthew Dillon ccb->cts.transport = XPORT_SATA; 879258223a3SMatthew Dillon ccb->cts.transport_version = XPORT_VERSION_UNSPECIFIED; 880258223a3SMatthew Dillon ccb->cts.proto_specific.valid = 0; 881258223a3SMatthew Dillon ccb->cts.xport_specific.valid = 0; 882258223a3SMatthew Dillon ccbh->status = CAM_REQ_CMP; 883258223a3SMatthew Dillon xpt_done(ccb); 884258223a3SMatthew Dillon break; 885258223a3SMatthew Dillon case XPT_CALC_GEOMETRY: 886258223a3SMatthew Dillon cam_calc_geometry(&ccb->ccg, 1); 887258223a3SMatthew Dillon xpt_done(ccb); 888258223a3SMatthew Dillon break; 889258223a3SMatthew Dillon case XPT_SCSI_IO: 890f4553de1SMatthew Dillon /* 891f4553de1SMatthew Dillon * Our parallel startup code might have only probed through 892f4553de1SMatthew Dillon * to the IDENT, so do the last step if necessary. 893f4553de1SMatthew Dillon */ 894f4553de1SMatthew Dillon if (at->at_probe == ATA_PROBE_NEED_IDENT) 895f4553de1SMatthew Dillon ahci_cam_probe(ap, atx); 896f4553de1SMatthew Dillon if (at->at_probe != ATA_PROBE_GOOD) { 897f4553de1SMatthew Dillon ccbh->status = CAM_DEV_NOT_THERE; 898f4553de1SMatthew Dillon xpt_done(ccb); 899f4553de1SMatthew Dillon break; 900f4553de1SMatthew Dillon } 9011980eff3SMatthew Dillon switch(at->at_type) { 902258223a3SMatthew Dillon case ATA_PORT_T_DISK: 9031980eff3SMatthew Dillon ahci_xpt_scsi_disk_io(ap, atx, ccb); 904258223a3SMatthew Dillon break; 905258223a3SMatthew Dillon case ATA_PORT_T_ATAPI: 9061980eff3SMatthew Dillon ahci_xpt_scsi_atapi_io(ap, atx, ccb); 907258223a3SMatthew Dillon break; 908258223a3SMatthew Dillon default: 909258223a3SMatthew Dillon ccbh->status = CAM_REQ_INVALID; 910258223a3SMatthew Dillon xpt_done(ccb); 911258223a3SMatthew Dillon break; 912258223a3SMatthew Dillon } 913258223a3SMatthew Dillon break; 914258223a3SMatthew Dillon default: 915258223a3SMatthew Dillon ccbh->status = CAM_REQ_INVALID; 916258223a3SMatthew Dillon xpt_done(ccb); 917258223a3SMatthew Dillon break; 918258223a3SMatthew Dillon } 919258223a3SMatthew Dillon } 920258223a3SMatthew Dillon 921258223a3SMatthew Dillon /* 922de68d532SMatthew Dillon * Poll function. 923de68d532SMatthew Dillon * 924de68d532SMatthew Dillon * Generally this function gets called heavily when interrupts might be 925de68d532SMatthew Dillon * non-operational, during a halt/reboot or panic. 926258223a3SMatthew Dillon */ 927258223a3SMatthew Dillon static 928258223a3SMatthew Dillon void 929258223a3SMatthew Dillon ahci_xpt_poll(struct cam_sim *sim) 930258223a3SMatthew Dillon { 931de68d532SMatthew Dillon struct ahci_port *ap; 932258223a3SMatthew Dillon 933de68d532SMatthew Dillon ap = cam_sim_softc(sim); 934de68d532SMatthew Dillon crit_enter(); 935f4553de1SMatthew Dillon ahci_os_lock_port(ap); 936f4553de1SMatthew Dillon ahci_port_intr(ap, 1); 937f4553de1SMatthew Dillon ahci_os_unlock_port(ap); 938de68d532SMatthew Dillon crit_exit(); 939258223a3SMatthew Dillon } 940258223a3SMatthew Dillon 941258223a3SMatthew Dillon /* 942b4189e5eSMatthew Dillon * Convert the SCSI command in ccb to an ata_xfer command in xa 943b4189e5eSMatthew Dillon * for ATA_PORT_T_DISK operations. Set the completion function 944b4189e5eSMatthew Dillon * to convert the response back, then dispatch to the OpenBSD AHCI 945b4189e5eSMatthew Dillon * layer. 946258223a3SMatthew Dillon * 947b4189e5eSMatthew Dillon * AHCI DISK commands only support a limited command set, and we 948b4189e5eSMatthew Dillon * fake additional commands to make it play nice with the CAM subsystem. 949258223a3SMatthew Dillon */ 950258223a3SMatthew Dillon static 951258223a3SMatthew Dillon void 9521980eff3SMatthew Dillon ahci_xpt_scsi_disk_io(struct ahci_port *ap, struct ata_port *atx, 9531980eff3SMatthew Dillon union ccb *ccb) 954258223a3SMatthew Dillon { 955258223a3SMatthew Dillon struct ccb_hdr *ccbh; 956258223a3SMatthew Dillon struct ccb_scsiio *csio; 957258223a3SMatthew Dillon struct ata_xfer *xa; 9581980eff3SMatthew Dillon struct ata_port *at; 959258223a3SMatthew Dillon struct ata_fis_h2d *fis; 960258223a3SMatthew Dillon scsi_cdb_t cdb; 961258223a3SMatthew Dillon union scsi_data *rdata; 962258223a3SMatthew Dillon int rdata_len; 963258223a3SMatthew Dillon u_int64_t capacity; 964258223a3SMatthew Dillon u_int64_t lba; 965258223a3SMatthew Dillon u_int32_t count; 966258223a3SMatthew Dillon 967258223a3SMatthew Dillon ccbh = &ccb->csio.ccb_h; 968258223a3SMatthew Dillon csio = &ccb->csio; 9691980eff3SMatthew Dillon at = atx ? atx : &ap->ap_ata[0]; 9701980eff3SMatthew Dillon 9711980eff3SMatthew Dillon /* 9721980eff3SMatthew Dillon * XXX not passing NULL at for direct attach! 9731980eff3SMatthew Dillon */ 9741980eff3SMatthew Dillon xa = ahci_ata_get_xfer(ap, atx); 975258223a3SMatthew Dillon rdata = (void *)csio->data_ptr; 976258223a3SMatthew Dillon rdata_len = csio->dxfer_len; 977258223a3SMatthew Dillon 978258223a3SMatthew Dillon /* 979258223a3SMatthew Dillon * Build the FIS or process the csio to completion. 980258223a3SMatthew Dillon */ 981258223a3SMatthew Dillon cdb = (void *)((ccbh->flags & CAM_CDB_POINTER) ? 982258223a3SMatthew Dillon csio->cdb_io.cdb_ptr : csio->cdb_io.cdb_bytes); 983258223a3SMatthew Dillon 984258223a3SMatthew Dillon switch(cdb->generic.opcode) { 985258223a3SMatthew Dillon case REQUEST_SENSE: 986258223a3SMatthew Dillon /* 987258223a3SMatthew Dillon * Auto-sense everything, so explicit sense requests 988258223a3SMatthew Dillon * return no-sense. 989258223a3SMatthew Dillon */ 990258223a3SMatthew Dillon ccbh->status = CAM_SCSI_STATUS_ERROR; 991258223a3SMatthew Dillon break; 992258223a3SMatthew Dillon case INQUIRY: 993258223a3SMatthew Dillon /* 994258223a3SMatthew Dillon * Inquiry supported features 995258223a3SMatthew Dillon * 996258223a3SMatthew Dillon * [opcode, byte2, page_code, length, control] 997258223a3SMatthew Dillon */ 998258223a3SMatthew Dillon if (cdb->inquiry.byte2 & SI_EVPD) { 999*6e0003adSMatthew Dillon ahci_xpt_page_inquiry(ap, at, ccb); 1000258223a3SMatthew Dillon } else { 1001258223a3SMatthew Dillon bzero(rdata, rdata_len); 1002258223a3SMatthew Dillon if (rdata_len < SHORT_INQUIRY_LENGTH) { 1003258223a3SMatthew Dillon ccbh->status = CAM_CCB_LEN_ERR; 1004258223a3SMatthew Dillon break; 1005258223a3SMatthew Dillon } 1006258223a3SMatthew Dillon if (rdata_len > sizeof(rdata->inquiry_data)) 1007258223a3SMatthew Dillon rdata_len = sizeof(rdata->inquiry_data); 1008258223a3SMatthew Dillon rdata->inquiry_data.device = T_DIRECT; 1009258223a3SMatthew Dillon rdata->inquiry_data.version = SCSI_REV_SPC2; 1010258223a3SMatthew Dillon rdata->inquiry_data.response_format = 2; 1011258223a3SMatthew Dillon rdata->inquiry_data.additional_length = 32; 1012258223a3SMatthew Dillon bcopy("SATA ", rdata->inquiry_data.vendor, 8); 10131980eff3SMatthew Dillon bcopy(at->at_identify.model, 1014258223a3SMatthew Dillon rdata->inquiry_data.product, 1015258223a3SMatthew Dillon sizeof(rdata->inquiry_data.product)); 10161980eff3SMatthew Dillon bcopy(at->at_identify.firmware, 1017258223a3SMatthew Dillon rdata->inquiry_data.revision, 1018258223a3SMatthew Dillon sizeof(rdata->inquiry_data.revision)); 1019258223a3SMatthew Dillon ccbh->status = CAM_REQ_CMP; 1020258223a3SMatthew Dillon } 1021258223a3SMatthew Dillon break; 1022258223a3SMatthew Dillon case READ_CAPACITY_16: 1023258223a3SMatthew Dillon if (cdb->read_capacity_16.service_action != SRC16_SERVICE_ACTION) { 1024258223a3SMatthew Dillon ccbh->status = CAM_REQ_INVALID; 1025258223a3SMatthew Dillon break; 1026258223a3SMatthew Dillon } 1027258223a3SMatthew Dillon if (rdata_len < sizeof(rdata->read_capacity_data_16)) { 1028258223a3SMatthew Dillon ccbh->status = CAM_CCB_LEN_ERR; 1029258223a3SMatthew Dillon break; 1030258223a3SMatthew Dillon } 1031258223a3SMatthew Dillon /* fall through */ 1032258223a3SMatthew Dillon case READ_CAPACITY: 1033258223a3SMatthew Dillon if (rdata_len < sizeof(rdata->read_capacity_data)) { 1034258223a3SMatthew Dillon ccbh->status = CAM_CCB_LEN_ERR; 1035258223a3SMatthew Dillon break; 1036258223a3SMatthew Dillon } 1037258223a3SMatthew Dillon 10381980eff3SMatthew Dillon capacity = at->at_capacity; 1039258223a3SMatthew Dillon 1040258223a3SMatthew Dillon bzero(rdata, rdata_len); 1041258223a3SMatthew Dillon if (cdb->generic.opcode == READ_CAPACITY) { 1042258223a3SMatthew Dillon rdata_len = sizeof(rdata->read_capacity_data); 1043258223a3SMatthew Dillon if (capacity > 0xFFFFFFFFU) 1044258223a3SMatthew Dillon capacity = 0xFFFFFFFFU; 1045258223a3SMatthew Dillon bzero(&rdata->read_capacity_data, rdata_len); 1046258223a3SMatthew Dillon scsi_ulto4b((u_int32_t)capacity - 1, 1047258223a3SMatthew Dillon rdata->read_capacity_data.addr); 1048258223a3SMatthew Dillon scsi_ulto4b(512, rdata->read_capacity_data.length); 1049258223a3SMatthew Dillon } else { 1050258223a3SMatthew Dillon rdata_len = sizeof(rdata->read_capacity_data_16); 1051258223a3SMatthew Dillon bzero(&rdata->read_capacity_data_16, rdata_len); 1052258223a3SMatthew Dillon scsi_u64to8b(capacity - 1, 1053258223a3SMatthew Dillon rdata->read_capacity_data_16.addr); 1054258223a3SMatthew Dillon scsi_ulto4b(512, rdata->read_capacity_data_16.length); 1055258223a3SMatthew Dillon } 1056258223a3SMatthew Dillon ccbh->status = CAM_REQ_CMP; 1057258223a3SMatthew Dillon break; 1058258223a3SMatthew Dillon case SYNCHRONIZE_CACHE: 1059258223a3SMatthew Dillon /* 1060258223a3SMatthew Dillon * Synchronize cache. Specification says this can take 1061258223a3SMatthew Dillon * greater then 30 seconds so give it at least 45. 1062258223a3SMatthew Dillon */ 1063258223a3SMatthew Dillon fis = xa->fis; 1064258223a3SMatthew Dillon fis->flags = ATA_H2D_FLAGS_CMD; 1065258223a3SMatthew Dillon fis->command = ATA_C_FLUSH_CACHE; 1066258223a3SMatthew Dillon fis->device = 0; 10673209f581SMatthew Dillon if (xa->timeout < 45000) 10683209f581SMatthew Dillon xa->timeout = 45000; 10691980eff3SMatthew Dillon xa->datalen = 0; 10701980eff3SMatthew Dillon xa->flags = ATA_F_READ; 10711980eff3SMatthew Dillon xa->complete = ahci_ata_complete_disk_synchronize_cache; 1072258223a3SMatthew Dillon break; 1073258223a3SMatthew Dillon case TEST_UNIT_READY: 1074258223a3SMatthew Dillon case START_STOP_UNIT: 1075258223a3SMatthew Dillon case PREVENT_ALLOW: 1076258223a3SMatthew Dillon /* 1077258223a3SMatthew Dillon * Just silently return success 1078258223a3SMatthew Dillon */ 1079258223a3SMatthew Dillon ccbh->status = CAM_REQ_CMP; 1080258223a3SMatthew Dillon rdata_len = 0; 1081258223a3SMatthew Dillon break; 1082258223a3SMatthew Dillon case ATA_PASS_12: 1083258223a3SMatthew Dillon case ATA_PASS_16: 1084258223a3SMatthew Dillon /* 1085258223a3SMatthew Dillon * XXX implement pass-through 1086258223a3SMatthew Dillon */ 1087258223a3SMatthew Dillon ccbh->status = CAM_FUNC_NOTAVAIL; 1088258223a3SMatthew Dillon break; 1089258223a3SMatthew Dillon default: 1090258223a3SMatthew Dillon switch(cdb->generic.opcode) { 1091258223a3SMatthew Dillon case READ_6: 1092258223a3SMatthew Dillon lba = scsi_3btoul(cdb->rw_6.addr) & 0x1FFFFF; 1093258223a3SMatthew Dillon count = cdb->rw_6.length ? cdb->rw_6.length : 0x100; 1094258223a3SMatthew Dillon xa->flags = ATA_F_READ; 1095258223a3SMatthew Dillon break; 1096258223a3SMatthew Dillon case READ_10: 1097258223a3SMatthew Dillon lba = scsi_4btoul(cdb->rw_10.addr); 1098258223a3SMatthew Dillon count = scsi_2btoul(cdb->rw_10.length); 1099258223a3SMatthew Dillon xa->flags = ATA_F_READ; 1100258223a3SMatthew Dillon break; 1101258223a3SMatthew Dillon case READ_12: 1102258223a3SMatthew Dillon lba = scsi_4btoul(cdb->rw_12.addr); 1103258223a3SMatthew Dillon count = scsi_4btoul(cdb->rw_12.length); 1104258223a3SMatthew Dillon xa->flags = ATA_F_READ; 1105258223a3SMatthew Dillon break; 1106258223a3SMatthew Dillon case READ_16: 1107258223a3SMatthew Dillon lba = scsi_8btou64(cdb->rw_16.addr); 1108258223a3SMatthew Dillon count = scsi_4btoul(cdb->rw_16.length); 1109258223a3SMatthew Dillon xa->flags = ATA_F_READ; 1110258223a3SMatthew Dillon break; 1111258223a3SMatthew Dillon case WRITE_6: 1112258223a3SMatthew Dillon lba = scsi_3btoul(cdb->rw_6.addr) & 0x1FFFFF; 1113258223a3SMatthew Dillon count = cdb->rw_6.length ? cdb->rw_6.length : 0x100; 1114258223a3SMatthew Dillon xa->flags = ATA_F_WRITE; 1115258223a3SMatthew Dillon break; 1116258223a3SMatthew Dillon case WRITE_10: 1117258223a3SMatthew Dillon lba = scsi_4btoul(cdb->rw_10.addr); 1118258223a3SMatthew Dillon count = scsi_2btoul(cdb->rw_10.length); 1119258223a3SMatthew Dillon xa->flags = ATA_F_WRITE; 1120258223a3SMatthew Dillon break; 1121258223a3SMatthew Dillon case WRITE_12: 1122258223a3SMatthew Dillon lba = scsi_4btoul(cdb->rw_12.addr); 1123258223a3SMatthew Dillon count = scsi_4btoul(cdb->rw_12.length); 1124258223a3SMatthew Dillon xa->flags = ATA_F_WRITE; 1125258223a3SMatthew Dillon break; 1126258223a3SMatthew Dillon case WRITE_16: 1127258223a3SMatthew Dillon lba = scsi_8btou64(cdb->rw_16.addr); 1128258223a3SMatthew Dillon count = scsi_4btoul(cdb->rw_16.length); 1129258223a3SMatthew Dillon xa->flags = ATA_F_WRITE; 1130258223a3SMatthew Dillon break; 1131258223a3SMatthew Dillon default: 1132258223a3SMatthew Dillon ccbh->status = CAM_REQ_INVALID; 1133258223a3SMatthew Dillon break; 1134258223a3SMatthew Dillon } 1135258223a3SMatthew Dillon if (ccbh->status != CAM_REQ_INPROG) 1136258223a3SMatthew Dillon break; 1137258223a3SMatthew Dillon 1138258223a3SMatthew Dillon fis = xa->fis; 1139258223a3SMatthew Dillon fis->flags = ATA_H2D_FLAGS_CMD; 1140258223a3SMatthew Dillon fis->lba_low = (u_int8_t)lba; 1141258223a3SMatthew Dillon fis->lba_mid = (u_int8_t)(lba >> 8); 1142258223a3SMatthew Dillon fis->lba_high = (u_int8_t)(lba >> 16); 1143258223a3SMatthew Dillon fis->device = ATA_H2D_DEVICE_LBA; 1144258223a3SMatthew Dillon 11451980eff3SMatthew Dillon /* 11461980eff3SMatthew Dillon * NCQ only for direct-attached disks, do not currently 11471980eff3SMatthew Dillon * try to use NCQ with port multipliers. 11481980eff3SMatthew Dillon */ 11491980eff3SMatthew Dillon if (at->at_ncqdepth > 1 && 11501980eff3SMatthew Dillon ap->ap_type == ATA_PORT_T_DISK && 1151258223a3SMatthew Dillon (ap->ap_sc->sc_cap & AHCI_REG_CAP_SNCQ) && 1152258223a3SMatthew Dillon (ccbh->flags & CAM_POLLED) == 0) { 1153258223a3SMatthew Dillon /* 1154258223a3SMatthew Dillon * Use NCQ - always uses 48 bit addressing 1155258223a3SMatthew Dillon */ 1156258223a3SMatthew Dillon xa->flags |= ATA_F_NCQ; 1157258223a3SMatthew Dillon fis->command = (xa->flags & ATA_F_WRITE) ? 1158258223a3SMatthew Dillon ATA_C_WRITE_FPDMA : ATA_C_READ_FPDMA; 1159258223a3SMatthew Dillon fis->lba_low_exp = (u_int8_t)(lba >> 24); 1160258223a3SMatthew Dillon fis->lba_mid_exp = (u_int8_t)(lba >> 32); 1161258223a3SMatthew Dillon fis->lba_high_exp = (u_int8_t)(lba >> 40); 1162258223a3SMatthew Dillon fis->sector_count = xa->tag << 3; 1163258223a3SMatthew Dillon fis->features = (u_int8_t)count; 1164258223a3SMatthew Dillon fis->features_exp = (u_int8_t)(count >> 8); 1165258223a3SMatthew Dillon } else if (count > 0x100 || lba > 0xFFFFFFFFU) { 1166258223a3SMatthew Dillon /* 1167258223a3SMatthew Dillon * Use LBA48 1168258223a3SMatthew Dillon */ 1169258223a3SMatthew Dillon fis->command = (xa->flags & ATA_F_WRITE) ? 1170258223a3SMatthew Dillon ATA_C_WRITEDMA_EXT : ATA_C_READDMA_EXT; 1171258223a3SMatthew Dillon fis->lba_low_exp = (u_int8_t)(lba >> 24); 1172258223a3SMatthew Dillon fis->lba_mid_exp = (u_int8_t)(lba >> 32); 1173258223a3SMatthew Dillon fis->lba_high_exp = (u_int8_t)(lba >> 40); 1174258223a3SMatthew Dillon fis->sector_count = (u_int8_t)count; 1175258223a3SMatthew Dillon fis->sector_count_exp = (u_int8_t)(count >> 8); 1176258223a3SMatthew Dillon } else { 1177258223a3SMatthew Dillon /* 1178258223a3SMatthew Dillon * Use LBA 1179258223a3SMatthew Dillon * 1180258223a3SMatthew Dillon * NOTE: 256 sectors is supported, stored as 0. 1181258223a3SMatthew Dillon */ 1182258223a3SMatthew Dillon fis->command = (xa->flags & ATA_F_WRITE) ? 1183258223a3SMatthew Dillon ATA_C_WRITEDMA : ATA_C_READDMA; 1184258223a3SMatthew Dillon fis->device |= (u_int8_t)(lba >> 24) & 0x0F; 1185258223a3SMatthew Dillon fis->sector_count = (u_int8_t)count; 1186258223a3SMatthew Dillon } 1187258223a3SMatthew Dillon 1188258223a3SMatthew Dillon xa->data = csio->data_ptr; 1189258223a3SMatthew Dillon xa->datalen = csio->dxfer_len; 1190258223a3SMatthew Dillon xa->complete = ahci_ata_complete_disk_rw; 11913209f581SMatthew Dillon xa->timeout = ccbh->timeout; /* milliseconds */ 119212feb904SMatthew Dillon #if 0 119312feb904SMatthew Dillon if (xa->timeout > 10000) /* XXX - debug */ 119412feb904SMatthew Dillon xa->timeout = 10000; 119512feb904SMatthew Dillon #endif 1196258223a3SMatthew Dillon if (ccbh->flags & CAM_POLLED) 1197258223a3SMatthew Dillon xa->flags |= ATA_F_POLL; 1198258223a3SMatthew Dillon break; 1199258223a3SMatthew Dillon } 1200258223a3SMatthew Dillon 1201258223a3SMatthew Dillon /* 1202258223a3SMatthew Dillon * If the request is still in progress the xa and FIS have 1203*6e0003adSMatthew Dillon * been set up (except for the PM target), and must be dispatched. 1204*6e0003adSMatthew Dillon * Otherwise the request was completed. 1205258223a3SMatthew Dillon */ 1206258223a3SMatthew Dillon if (ccbh->status == CAM_REQ_INPROG) { 1207258223a3SMatthew Dillon KKASSERT(xa->complete != NULL); 1208258223a3SMatthew Dillon xa->atascsi_private = ccb; 1209258223a3SMatthew Dillon ccb->ccb_h.sim_priv.entries[0].ptr = ap; 1210f4553de1SMatthew Dillon ahci_os_lock_port(ap); 1211*6e0003adSMatthew Dillon xa->fis->flags |= at->at_target; 1212258223a3SMatthew Dillon ahci_ata_cmd(xa); 1213f4553de1SMatthew Dillon ahci_os_unlock_port(ap); 1214258223a3SMatthew Dillon } else { 1215258223a3SMatthew Dillon ahci_ata_put_xfer(xa); 1216258223a3SMatthew Dillon xpt_done(ccb); 1217258223a3SMatthew Dillon } 1218258223a3SMatthew Dillon } 1219258223a3SMatthew Dillon 1220b4189e5eSMatthew Dillon /* 1221b4189e5eSMatthew Dillon * Convert the SCSI command in ccb to an ata_xfer command in xa 1222b4189e5eSMatthew Dillon * for ATA_PORT_T_ATAPI operations. Set the completion function 1223b4189e5eSMatthew Dillon * to convert the response back, then dispatch to the OpenBSD AHCI 1224b4189e5eSMatthew Dillon * layer. 1225b4189e5eSMatthew Dillon */ 1226258223a3SMatthew Dillon static 1227258223a3SMatthew Dillon void 12281980eff3SMatthew Dillon ahci_xpt_scsi_atapi_io(struct ahci_port *ap, struct ata_port *atx, 12291980eff3SMatthew Dillon union ccb *ccb) 1230258223a3SMatthew Dillon { 1231258223a3SMatthew Dillon struct ccb_hdr *ccbh; 1232258223a3SMatthew Dillon struct ccb_scsiio *csio; 1233258223a3SMatthew Dillon struct ata_xfer *xa; 1234258223a3SMatthew Dillon struct ata_fis_h2d *fis; 1235b4189e5eSMatthew Dillon scsi_cdb_t cdbs; 1236b4189e5eSMatthew Dillon scsi_cdb_t cdbd; 1237b4189e5eSMatthew Dillon int flags; 12381980eff3SMatthew Dillon struct ata_port *at; 1239258223a3SMatthew Dillon 1240258223a3SMatthew Dillon ccbh = &ccb->csio.ccb_h; 1241258223a3SMatthew Dillon csio = &ccb->csio; 12421980eff3SMatthew Dillon at = atx ? atx : &ap->ap_ata[0]; 1243b4189e5eSMatthew Dillon 1244b4189e5eSMatthew Dillon switch (ccbh->flags & CAM_DIR_MASK) { 1245b4189e5eSMatthew Dillon case CAM_DIR_IN: 1246b4189e5eSMatthew Dillon flags = ATA_F_PACKET | ATA_F_READ; 1247b4189e5eSMatthew Dillon break; 1248b4189e5eSMatthew Dillon case CAM_DIR_OUT: 1249b4189e5eSMatthew Dillon flags = ATA_F_PACKET | ATA_F_WRITE; 1250b4189e5eSMatthew Dillon break; 1251b4189e5eSMatthew Dillon case CAM_DIR_NONE: 1252b4189e5eSMatthew Dillon flags = ATA_F_PACKET; 1253b4189e5eSMatthew Dillon break; 1254b4189e5eSMatthew Dillon default: 1255b4189e5eSMatthew Dillon ccbh->status = CAM_REQ_INVALID; 1256b4189e5eSMatthew Dillon xpt_done(ccb); 1257b4189e5eSMatthew Dillon return; 1258b4189e5eSMatthew Dillon /* NOT REACHED */ 1259b4189e5eSMatthew Dillon } 1260b4189e5eSMatthew Dillon 1261b4189e5eSMatthew Dillon /* 126212feb904SMatthew Dillon * Special handling to get the rfis back into host memory while 126312feb904SMatthew Dillon * still allowing the Sili chip to run commands in parallel to 126412feb904SMatthew Dillon * ATAPI devices behind a PM. 126512feb904SMatthew Dillon */ 126612feb904SMatthew Dillon flags |= ATA_F_AUTOSENSE; 126712feb904SMatthew Dillon 126812feb904SMatthew Dillon /* 1269b4189e5eSMatthew Dillon * The command has to fit in the packet command buffer. 1270b4189e5eSMatthew Dillon */ 1271b4189e5eSMatthew Dillon if (csio->cdb_len < 6 || csio->cdb_len > 16) { 1272b4189e5eSMatthew Dillon ccbh->status = CAM_CCB_LEN_ERR; 1273b4189e5eSMatthew Dillon xpt_done(ccb); 1274b4189e5eSMatthew Dillon return; 1275b4189e5eSMatthew Dillon } 1276b4189e5eSMatthew Dillon 1277b4189e5eSMatthew Dillon /* 1278b4189e5eSMatthew Dillon * Initialize the XA and FIS. 12791980eff3SMatthew Dillon * 12801980eff3SMatthew Dillon * XXX not passing NULL at for direct attach! 1281b4189e5eSMatthew Dillon */ 12821980eff3SMatthew Dillon xa = ahci_ata_get_xfer(ap, atx); 1283258223a3SMatthew Dillon fis = xa->fis; 1284258223a3SMatthew Dillon 12851980eff3SMatthew Dillon fis->flags = ATA_H2D_FLAGS_CMD | at->at_target; 12861980eff3SMatthew Dillon fis->command = ATA_C_PACKET; 12871980eff3SMatthew Dillon fis->device = 0; 12881980eff3SMatthew Dillon fis->sector_count = xa->tag << 3; 12891980eff3SMatthew Dillon fis->features = ATA_H2D_FEATURES_DMA | 12901980eff3SMatthew Dillon ((flags & ATA_F_WRITE) ? 12911980eff3SMatthew Dillon ATA_H2D_FEATURES_DIR_WRITE : ATA_H2D_FEATURES_DIR_READ); 12921980eff3SMatthew Dillon fis->lba_mid = 0x00; 12931980eff3SMatthew Dillon fis->lba_high = 0x20; 12941980eff3SMatthew Dillon 1295b4189e5eSMatthew Dillon xa->flags = flags; 1296b4189e5eSMatthew Dillon xa->data = csio->data_ptr; 1297b4189e5eSMatthew Dillon xa->datalen = csio->dxfer_len; 12983209f581SMatthew Dillon xa->timeout = ccbh->timeout; /* milliseconds */ 12991980eff3SMatthew Dillon 1300b4189e5eSMatthew Dillon if (ccbh->flags & CAM_POLLED) 1301b4189e5eSMatthew Dillon xa->flags |= ATA_F_POLL; 1302258223a3SMatthew Dillon 1303258223a3SMatthew Dillon /* 1304b4189e5eSMatthew Dillon * Copy the cdb to the packetcmd buffer in the FIS using a 1305b4189e5eSMatthew Dillon * convenient pointer in the xa. 1306258223a3SMatthew Dillon */ 1307b4189e5eSMatthew Dillon cdbs = (void *)((ccbh->flags & CAM_CDB_POINTER) ? 1308b4189e5eSMatthew Dillon csio->cdb_io.cdb_ptr : csio->cdb_io.cdb_bytes); 1309b4189e5eSMatthew Dillon bcopy(cdbs, xa->packetcmd, csio->cdb_len); 1310b4189e5eSMatthew Dillon 1311669fbbf7SMatthew Dillon #if 0 1312b4189e5eSMatthew Dillon kprintf("opcode %d cdb_len %d dxfer_len %d\n", 1313b4189e5eSMatthew Dillon cdbs->generic.opcode, 1314b4189e5eSMatthew Dillon csio->cdb_len, csio->dxfer_len); 1315669fbbf7SMatthew Dillon #endif 1316b4189e5eSMatthew Dillon 1317b4189e5eSMatthew Dillon /* 1318b4189e5eSMatthew Dillon * Some ATAPI commands do not actually follow the SCSI standard. 1319b4189e5eSMatthew Dillon */ 1320b4189e5eSMatthew Dillon cdbd = (void *)xa->packetcmd; 1321b4189e5eSMatthew Dillon 1322b4189e5eSMatthew Dillon switch(cdbd->generic.opcode) { 1323258223a3SMatthew Dillon case INQUIRY: 1324b4189e5eSMatthew Dillon /* 1325b4189e5eSMatthew Dillon * Some ATAPI devices can't handle long inquiry lengths, 1326b4189e5eSMatthew Dillon * don't ask me why. Truncate the inquiry length. 1327b4189e5eSMatthew Dillon */ 1328b4189e5eSMatthew Dillon if (cdbd->inquiry.page_code == 0 && 1329b4189e5eSMatthew Dillon cdbd->inquiry.length > SHORT_INQUIRY_LENGTH) { 1330b4189e5eSMatthew Dillon cdbd->inquiry.length = SHORT_INQUIRY_LENGTH; 1331b4189e5eSMatthew Dillon } 1332b4189e5eSMatthew Dillon break; 1333258223a3SMatthew Dillon case READ_6: 1334258223a3SMatthew Dillon case WRITE_6: 1335b4189e5eSMatthew Dillon /* 1336b4189e5eSMatthew Dillon * Convert *_6 to *_10 commands. Most ATAPI devices 1337b4189e5eSMatthew Dillon * cannot handle the SCSI READ_6 and WRITE_6 commands. 1338b4189e5eSMatthew Dillon */ 1339b4189e5eSMatthew Dillon cdbd->rw_10.opcode |= 0x20; 1340b4189e5eSMatthew Dillon cdbd->rw_10.byte2 = 0; 1341b4189e5eSMatthew Dillon cdbd->rw_10.addr[0] = cdbs->rw_6.addr[0] & 0x1F; 1342b4189e5eSMatthew Dillon cdbd->rw_10.addr[1] = cdbs->rw_6.addr[1]; 1343b4189e5eSMatthew Dillon cdbd->rw_10.addr[2] = cdbs->rw_6.addr[2]; 1344b4189e5eSMatthew Dillon cdbd->rw_10.addr[3] = 0; 1345b4189e5eSMatthew Dillon cdbd->rw_10.reserved = 0; 1346b4189e5eSMatthew Dillon cdbd->rw_10.length[0] = 0; 1347b4189e5eSMatthew Dillon cdbd->rw_10.length[1] = cdbs->rw_6.length; 1348b4189e5eSMatthew Dillon cdbd->rw_10.control = cdbs->rw_6.control; 1349b4189e5eSMatthew Dillon break; 1350258223a3SMatthew Dillon default: 1351258223a3SMatthew Dillon break; 1352258223a3SMatthew Dillon } 1353258223a3SMatthew Dillon 1354b4189e5eSMatthew Dillon /* 1355b4189e5eSMatthew Dillon * And dispatch 1356b4189e5eSMatthew Dillon */ 1357b4189e5eSMatthew Dillon xa->complete = ahci_atapi_complete_cmd; 1358258223a3SMatthew Dillon xa->atascsi_private = ccb; 1359258223a3SMatthew Dillon ccb->ccb_h.sim_priv.entries[0].ptr = ap; 136076497a9cSMatthew Dillon ahci_os_lock_port(ap); 1361258223a3SMatthew Dillon ahci_ata_cmd(xa); 136276497a9cSMatthew Dillon ahci_os_unlock_port(ap); 1363258223a3SMatthew Dillon } 1364258223a3SMatthew Dillon 1365b4189e5eSMatthew Dillon /* 1366*6e0003adSMatthew Dillon * Simulate page inquiries for disk attachments. 1367*6e0003adSMatthew Dillon */ 1368*6e0003adSMatthew Dillon static 1369*6e0003adSMatthew Dillon void 1370*6e0003adSMatthew Dillon ahci_xpt_page_inquiry(struct ahci_port *ap, struct ata_port *at, union ccb *ccb) 1371*6e0003adSMatthew Dillon { 1372*6e0003adSMatthew Dillon union { 1373*6e0003adSMatthew Dillon struct scsi_vpd_supported_page_list list; 1374*6e0003adSMatthew Dillon struct scsi_vpd_unit_serial_number serno; 1375*6e0003adSMatthew Dillon struct scsi_vpd_unit_devid devid; 1376*6e0003adSMatthew Dillon char buf[256]; 1377*6e0003adSMatthew Dillon } *page; 1378*6e0003adSMatthew Dillon scsi_cdb_t cdb; 1379*6e0003adSMatthew Dillon int i; 1380*6e0003adSMatthew Dillon int j; 1381*6e0003adSMatthew Dillon int len; 1382*6e0003adSMatthew Dillon 1383*6e0003adSMatthew Dillon page = kmalloc(sizeof(*page), M_DEVBUF, M_WAITOK | M_ZERO); 1384*6e0003adSMatthew Dillon 1385*6e0003adSMatthew Dillon cdb = (void *)((ccb->ccb_h.flags & CAM_CDB_POINTER) ? 1386*6e0003adSMatthew Dillon ccb->csio.cdb_io.cdb_ptr : ccb->csio.cdb_io.cdb_bytes); 1387*6e0003adSMatthew Dillon 1388*6e0003adSMatthew Dillon switch(cdb->inquiry.page_code) { 1389*6e0003adSMatthew Dillon case SVPD_SUPPORTED_PAGE_LIST: 1390*6e0003adSMatthew Dillon i = 0; 1391*6e0003adSMatthew Dillon page->list.device = T_DIRECT; 1392*6e0003adSMatthew Dillon page->list.page_code = SVPD_SUPPORTED_PAGE_LIST; 1393*6e0003adSMatthew Dillon page->list.list[i++] = SVPD_SUPPORTED_PAGE_LIST; 1394*6e0003adSMatthew Dillon page->list.list[i++] = SVPD_UNIT_SERIAL_NUMBER; 1395*6e0003adSMatthew Dillon page->list.list[i++] = SVPD_UNIT_DEVID; 1396*6e0003adSMatthew Dillon page->list.length = i; 1397*6e0003adSMatthew Dillon len = offsetof(struct scsi_vpd_supported_page_list, list[3]); 1398*6e0003adSMatthew Dillon break; 1399*6e0003adSMatthew Dillon case SVPD_UNIT_SERIAL_NUMBER: 1400*6e0003adSMatthew Dillon i = 0; 1401*6e0003adSMatthew Dillon j = sizeof(at->at_identify.serial); 1402*6e0003adSMatthew Dillon for (i = 0; i < j && at->at_identify.serial[i] == ' '; ++i) 1403*6e0003adSMatthew Dillon ; 1404*6e0003adSMatthew Dillon while (j > i && at->at_identify.serial[j-1] == ' ') 1405*6e0003adSMatthew Dillon --j; 1406*6e0003adSMatthew Dillon page->serno.device = T_DIRECT; 1407*6e0003adSMatthew Dillon page->serno.page_code = SVPD_UNIT_SERIAL_NUMBER; 1408*6e0003adSMatthew Dillon page->serno.length = j - i; 1409*6e0003adSMatthew Dillon bcopy(at->at_identify.serial + i, 1410*6e0003adSMatthew Dillon page->serno.serial_num, j - i); 1411*6e0003adSMatthew Dillon len = offsetof(struct scsi_vpd_unit_serial_number, 1412*6e0003adSMatthew Dillon serial_num[j-i]); 1413*6e0003adSMatthew Dillon break; 1414*6e0003adSMatthew Dillon case SVPD_UNIT_DEVID: 1415*6e0003adSMatthew Dillon /* fall through for now */ 1416*6e0003adSMatthew Dillon default: 1417*6e0003adSMatthew Dillon ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; 1418*6e0003adSMatthew Dillon len = 0; 1419*6e0003adSMatthew Dillon break; 1420*6e0003adSMatthew Dillon } 1421*6e0003adSMatthew Dillon if (ccb->ccb_h.status == CAM_REQ_INPROG) { 1422*6e0003adSMatthew Dillon if (len <= ccb->csio.dxfer_len) { 1423*6e0003adSMatthew Dillon ccb->ccb_h.status = CAM_REQ_CMP; 1424*6e0003adSMatthew Dillon bzero(ccb->csio.data_ptr, ccb->csio.dxfer_len); 1425*6e0003adSMatthew Dillon bcopy(page, ccb->csio.data_ptr, len); 1426*6e0003adSMatthew Dillon ccb->csio.resid = ccb->csio.dxfer_len - len; 1427*6e0003adSMatthew Dillon } else { 1428*6e0003adSMatthew Dillon ccb->ccb_h.status = CAM_CCB_LEN_ERR; 1429*6e0003adSMatthew Dillon } 1430*6e0003adSMatthew Dillon } 1431*6e0003adSMatthew Dillon kfree(page, M_DEVBUF); 1432*6e0003adSMatthew Dillon } 1433*6e0003adSMatthew Dillon 1434*6e0003adSMatthew Dillon /* 1435b4189e5eSMatthew Dillon * Completion function for ATA_PORT_T_DISK cache synchronization. 1436b4189e5eSMatthew Dillon */ 1437258223a3SMatthew Dillon static 1438258223a3SMatthew Dillon void 1439258223a3SMatthew Dillon ahci_ata_complete_disk_synchronize_cache(struct ata_xfer *xa) 1440258223a3SMatthew Dillon { 1441258223a3SMatthew Dillon union ccb *ccb = xa->atascsi_private; 1442258223a3SMatthew Dillon struct ccb_hdr *ccbh = &ccb->ccb_h; 1443258223a3SMatthew Dillon struct ahci_port *ap = ccb->ccb_h.sim_priv.entries[0].ptr; 1444258223a3SMatthew Dillon 1445258223a3SMatthew Dillon switch(xa->state) { 1446258223a3SMatthew Dillon case ATA_S_COMPLETE: 1447258223a3SMatthew Dillon ccbh->status = CAM_REQ_CMP; 1448b4189e5eSMatthew Dillon ccb->csio.scsi_status = SCSI_STATUS_OK; 1449258223a3SMatthew Dillon break; 1450258223a3SMatthew Dillon case ATA_S_ERROR: 14511980eff3SMatthew Dillon kprintf("%s: synchronize_cache: error\n", 14521980eff3SMatthew Dillon ATANAME(ap, xa->at)); 1453b4189e5eSMatthew Dillon ccbh->status = CAM_SCSI_STATUS_ERROR | CAM_AUTOSNS_VALID; 1454b4189e5eSMatthew Dillon ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND; 1455b4189e5eSMatthew Dillon ahci_ata_dummy_sense(&ccb->csio.sense_data); 1456258223a3SMatthew Dillon break; 1457258223a3SMatthew Dillon case ATA_S_TIMEOUT: 14581980eff3SMatthew Dillon kprintf("%s: synchronize_cache: timeout\n", 14591980eff3SMatthew Dillon ATANAME(ap, xa->at)); 1460258223a3SMatthew Dillon ccbh->status = CAM_CMD_TIMEOUT; 1461258223a3SMatthew Dillon break; 1462258223a3SMatthew Dillon default: 1463258223a3SMatthew Dillon kprintf("%s: synchronize_cache: unknown state %d\n", 14641980eff3SMatthew Dillon ATANAME(ap, xa->at), xa->state); 1465258223a3SMatthew Dillon ccbh->status = CAM_REQ_CMP_ERR; 1466258223a3SMatthew Dillon break; 1467258223a3SMatthew Dillon } 1468258223a3SMatthew Dillon ahci_ata_put_xfer(xa); 1469f4553de1SMatthew Dillon ahci_os_unlock_port(ap); 1470258223a3SMatthew Dillon xpt_done(ccb); 1471f4553de1SMatthew Dillon ahci_os_lock_port(ap); 1472258223a3SMatthew Dillon } 1473258223a3SMatthew Dillon 1474b4189e5eSMatthew Dillon /* 1475b4189e5eSMatthew Dillon * Completion function for ATA_PORT_T_DISK I/O 1476b4189e5eSMatthew Dillon */ 1477258223a3SMatthew Dillon static 1478258223a3SMatthew Dillon void 1479258223a3SMatthew Dillon ahci_ata_complete_disk_rw(struct ata_xfer *xa) 1480258223a3SMatthew Dillon { 1481258223a3SMatthew Dillon union ccb *ccb = xa->atascsi_private; 1482258223a3SMatthew Dillon struct ccb_hdr *ccbh = &ccb->ccb_h; 1483258223a3SMatthew Dillon struct ahci_port *ap = ccb->ccb_h.sim_priv.entries[0].ptr; 1484258223a3SMatthew Dillon 1485258223a3SMatthew Dillon switch(xa->state) { 1486258223a3SMatthew Dillon case ATA_S_COMPLETE: 1487258223a3SMatthew Dillon ccbh->status = CAM_REQ_CMP; 1488b4189e5eSMatthew Dillon ccb->csio.scsi_status = SCSI_STATUS_OK; 1489258223a3SMatthew Dillon break; 1490258223a3SMatthew Dillon case ATA_S_ERROR: 14911980eff3SMatthew Dillon kprintf("%s: disk_rw: error\n", ATANAME(ap, xa->at)); 1492b4189e5eSMatthew Dillon ccbh->status = CAM_SCSI_STATUS_ERROR | CAM_AUTOSNS_VALID; 1493b4189e5eSMatthew Dillon ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND; 1494b4189e5eSMatthew Dillon ahci_ata_dummy_sense(&ccb->csio.sense_data); 1495258223a3SMatthew Dillon break; 1496258223a3SMatthew Dillon case ATA_S_TIMEOUT: 14971980eff3SMatthew Dillon kprintf("%s: disk_rw: timeout\n", ATANAME(ap, xa->at)); 1498258223a3SMatthew Dillon ccbh->status = CAM_CMD_TIMEOUT; 14994c339a5fSMatthew Dillon ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND; 15004c339a5fSMatthew Dillon ahci_ata_dummy_sense(&ccb->csio.sense_data); 1501258223a3SMatthew Dillon break; 1502258223a3SMatthew Dillon default: 1503258223a3SMatthew Dillon kprintf("%s: disk_rw: unknown state %d\n", 15041980eff3SMatthew Dillon ATANAME(ap, xa->at), xa->state); 1505258223a3SMatthew Dillon ccbh->status = CAM_REQ_CMP_ERR; 1506258223a3SMatthew Dillon break; 1507258223a3SMatthew Dillon } 1508258223a3SMatthew Dillon ccb->csio.resid = xa->resid; 1509258223a3SMatthew Dillon ahci_ata_put_xfer(xa); 1510f4553de1SMatthew Dillon ahci_os_unlock_port(ap); 1511258223a3SMatthew Dillon xpt_done(ccb); 1512f4553de1SMatthew Dillon ahci_os_lock_port(ap); 1513258223a3SMatthew Dillon } 1514b4189e5eSMatthew Dillon 15157d4fcf34SMatthew Dillon /* 15167d4fcf34SMatthew Dillon * Completion function for ATA_PORT_T_ATAPI I/O 15177d4fcf34SMatthew Dillon * 15187d4fcf34SMatthew Dillon * Sense data is returned in the rfis. 15197d4fcf34SMatthew Dillon */ 1520b4189e5eSMatthew Dillon static 1521b4189e5eSMatthew Dillon void 1522b4189e5eSMatthew Dillon ahci_atapi_complete_cmd(struct ata_xfer *xa) 1523b4189e5eSMatthew Dillon { 1524b4189e5eSMatthew Dillon union ccb *ccb = xa->atascsi_private; 1525b4189e5eSMatthew Dillon struct ccb_hdr *ccbh = &ccb->ccb_h; 1526b4189e5eSMatthew Dillon struct ahci_port *ap = ccb->ccb_h.sim_priv.entries[0].ptr; 1527b4189e5eSMatthew Dillon scsi_cdb_t cdb; 1528b4189e5eSMatthew Dillon 1529b4189e5eSMatthew Dillon cdb = (void *)((ccb->ccb_h.flags & CAM_CDB_POINTER) ? 1530b4189e5eSMatthew Dillon ccb->csio.cdb_io.cdb_ptr : ccb->csio.cdb_io.cdb_bytes); 1531b4189e5eSMatthew Dillon 1532b4189e5eSMatthew Dillon switch(xa->state) { 1533b4189e5eSMatthew Dillon case ATA_S_COMPLETE: 1534b4189e5eSMatthew Dillon ccbh->status = CAM_REQ_CMP; 1535b4189e5eSMatthew Dillon ccb->csio.scsi_status = SCSI_STATUS_OK; 1536b4189e5eSMatthew Dillon break; 1537b4189e5eSMatthew Dillon case ATA_S_ERROR: 1538b4189e5eSMatthew Dillon ccbh->status = CAM_SCSI_STATUS_ERROR; 1539b4189e5eSMatthew Dillon ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND; 15407d4fcf34SMatthew Dillon ahci_ata_atapi_sense(&xa->rfis, &ccb->csio.sense_data); 1541b4189e5eSMatthew Dillon break; 1542b4189e5eSMatthew Dillon case ATA_S_TIMEOUT: 1543b4189e5eSMatthew Dillon kprintf("%s: cmd %d: timeout\n", 1544b4189e5eSMatthew Dillon PORTNAME(ap), cdb->generic.opcode); 1545b4189e5eSMatthew Dillon ccbh->status = CAM_CMD_TIMEOUT; 15464c339a5fSMatthew Dillon ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND; 15474c339a5fSMatthew Dillon ahci_ata_dummy_sense(&ccb->csio.sense_data); 1548b4189e5eSMatthew Dillon break; 1549b4189e5eSMatthew Dillon default: 1550b4189e5eSMatthew Dillon kprintf("%s: cmd %d: unknown state %d\n", 1551b4189e5eSMatthew Dillon PORTNAME(ap), cdb->generic.opcode, xa->state); 1552b4189e5eSMatthew Dillon ccbh->status = CAM_REQ_CMP_ERR; 1553b4189e5eSMatthew Dillon break; 1554b4189e5eSMatthew Dillon } 1555b4189e5eSMatthew Dillon ccb->csio.resid = xa->resid; 1556b4189e5eSMatthew Dillon ahci_ata_put_xfer(xa); 1557f4553de1SMatthew Dillon ahci_os_unlock_port(ap); 1558b4189e5eSMatthew Dillon xpt_done(ccb); 1559f4553de1SMatthew Dillon ahci_os_lock_port(ap); 1560b4189e5eSMatthew Dillon } 1561b4189e5eSMatthew Dillon 15627d4fcf34SMatthew Dillon /* 15637d4fcf34SMatthew Dillon * Construct dummy sense data for errors on DISKs 15647d4fcf34SMatthew Dillon */ 1565b4189e5eSMatthew Dillon static 1566b4189e5eSMatthew Dillon void 1567b4189e5eSMatthew Dillon ahci_ata_dummy_sense(struct scsi_sense_data *sense_data) 1568b4189e5eSMatthew Dillon { 1569b4189e5eSMatthew Dillon sense_data->error_code = SSD_ERRCODE_VALID | SSD_CURRENT_ERROR; 1570b4189e5eSMatthew Dillon sense_data->segment = 0; 1571b4189e5eSMatthew Dillon sense_data->flags = SSD_KEY_MEDIUM_ERROR; 1572b4189e5eSMatthew Dillon sense_data->info[0] = 0; 1573b4189e5eSMatthew Dillon sense_data->info[1] = 0; 1574b4189e5eSMatthew Dillon sense_data->info[2] = 0; 1575b4189e5eSMatthew Dillon sense_data->info[3] = 0; 1576b4189e5eSMatthew Dillon sense_data->extra_len = 0; 1577b4189e5eSMatthew Dillon } 15787d4fcf34SMatthew Dillon 15797d4fcf34SMatthew Dillon /* 15807d4fcf34SMatthew Dillon * Construct atapi sense data for errors on ATAPI 15817d4fcf34SMatthew Dillon * 15827d4fcf34SMatthew Dillon * The ATAPI sense data is stored in the passed rfis and must be converted 15837d4fcf34SMatthew Dillon * to SCSI sense data. 15847d4fcf34SMatthew Dillon */ 15857d4fcf34SMatthew Dillon static 15867d4fcf34SMatthew Dillon void 15877d4fcf34SMatthew Dillon ahci_ata_atapi_sense(struct ata_fis_d2h *rfis, 15887d4fcf34SMatthew Dillon struct scsi_sense_data *sense_data) 15897d4fcf34SMatthew Dillon { 15907d4fcf34SMatthew Dillon sense_data->error_code = SSD_ERRCODE_VALID | SSD_CURRENT_ERROR; 15917d4fcf34SMatthew Dillon sense_data->segment = 0; 15927d4fcf34SMatthew Dillon sense_data->flags = (rfis->error & 0xF0) >> 4; 15937d4fcf34SMatthew Dillon if (rfis->error & 0x04) 15947d4fcf34SMatthew Dillon sense_data->flags |= SSD_KEY_ILLEGAL_REQUEST; 15957d4fcf34SMatthew Dillon if (rfis->error & 0x02) 15967d4fcf34SMatthew Dillon sense_data->flags |= SSD_EOM; 15977d4fcf34SMatthew Dillon if (rfis->error & 0x01) 15987d4fcf34SMatthew Dillon sense_data->flags |= SSD_ILI; 15997d4fcf34SMatthew Dillon sense_data->info[0] = 0; 16007d4fcf34SMatthew Dillon sense_data->info[1] = 0; 16017d4fcf34SMatthew Dillon sense_data->info[2] = 0; 16027d4fcf34SMatthew Dillon sense_data->info[3] = 0; 16037d4fcf34SMatthew Dillon sense_data->extra_len = 0; 16047d4fcf34SMatthew Dillon } 1605*6e0003adSMatthew Dillon 1606*6e0003adSMatthew Dillon static 1607*6e0003adSMatthew Dillon void 1608*6e0003adSMatthew Dillon ahci_strip_string(const char **basep, int *lenp) 1609*6e0003adSMatthew Dillon { 1610*6e0003adSMatthew Dillon const char *base = *basep; 1611*6e0003adSMatthew Dillon int len = *lenp; 1612*6e0003adSMatthew Dillon 1613*6e0003adSMatthew Dillon while (len && (*base == 0 || *base == ' ')) { 1614*6e0003adSMatthew Dillon --len; 1615*6e0003adSMatthew Dillon ++base; 1616*6e0003adSMatthew Dillon } 1617*6e0003adSMatthew Dillon while (len && (base[len-1] == 0 || base[len-1] == ' ')) 1618*6e0003adSMatthew Dillon --len; 1619*6e0003adSMatthew Dillon *basep = base; 1620*6e0003adSMatthew Dillon *lenp = len; 1621*6e0003adSMatthew Dillon } 1622