11ac8d5baSMatthew Dillon /* 21ac8d5baSMatthew Dillon * Copyright (c) 2009 The DragonFly Project. All rights reserved. 31ac8d5baSMatthew Dillon * 41ac8d5baSMatthew Dillon * This code is derived from software contributed to The DragonFly Project 51ac8d5baSMatthew Dillon * by Matthew Dillon <dillon@backplane.com> 61ac8d5baSMatthew Dillon * 71ac8d5baSMatthew Dillon * Redistribution and use in source and binary forms, with or without 81ac8d5baSMatthew Dillon * modification, are permitted provided that the following conditions 91ac8d5baSMatthew Dillon * are met: 101ac8d5baSMatthew Dillon * 111ac8d5baSMatthew Dillon * 1. Redistributions of source code must retain the above copyright 121ac8d5baSMatthew Dillon * notice, this list of conditions and the following disclaimer. 131ac8d5baSMatthew Dillon * 2. Redistributions in binary form must reproduce the above copyright 141ac8d5baSMatthew Dillon * notice, this list of conditions and the following disclaimer in 151ac8d5baSMatthew Dillon * the documentation and/or other materials provided with the 161ac8d5baSMatthew Dillon * distribution. 171ac8d5baSMatthew Dillon * 3. Neither the name of The DragonFly Project nor the names of its 181ac8d5baSMatthew Dillon * contributors may be used to endorse or promote products derived 191ac8d5baSMatthew Dillon * from this software without specific, prior written permission. 201ac8d5baSMatthew Dillon * 211ac8d5baSMatthew Dillon * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 221ac8d5baSMatthew Dillon * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 231ac8d5baSMatthew Dillon * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 241ac8d5baSMatthew Dillon * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 251ac8d5baSMatthew Dillon * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 261ac8d5baSMatthew Dillon * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 271ac8d5baSMatthew Dillon * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 281ac8d5baSMatthew Dillon * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 291ac8d5baSMatthew Dillon * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 301ac8d5baSMatthew Dillon * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 311ac8d5baSMatthew Dillon * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 321ac8d5baSMatthew Dillon * SUCH DAMAGE. 331ac8d5baSMatthew Dillon * 341ac8d5baSMatthew Dillon * 351ac8d5baSMatthew Dillon * Copyright (c) 2007 David Gwynne <dlg@openbsd.org> 361ac8d5baSMatthew Dillon * 371ac8d5baSMatthew Dillon * Permission to use, copy, modify, and distribute this software for any 381ac8d5baSMatthew Dillon * purpose with or without fee is hereby granted, provided that the above 391ac8d5baSMatthew Dillon * copyright notice and this permission notice appear in all copies. 401ac8d5baSMatthew Dillon * 411ac8d5baSMatthew Dillon * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 421ac8d5baSMatthew Dillon * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 431ac8d5baSMatthew Dillon * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 441ac8d5baSMatthew Dillon * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 451ac8d5baSMatthew Dillon * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 461ac8d5baSMatthew Dillon * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 471ac8d5baSMatthew Dillon * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 481ac8d5baSMatthew Dillon * 491ac8d5baSMatthew Dillon * $OpenBSD: atascsi.c,v 1.64 2009/02/16 21:19:06 miod Exp $ 501ac8d5baSMatthew Dillon * $DragonFly$ 511ac8d5baSMatthew Dillon */ 521ac8d5baSMatthew Dillon /* 531ac8d5baSMatthew Dillon * Implement each SATA port as its own SCSI bus on CAM. This way we can 541ac8d5baSMatthew Dillon * implement future port multiplier features as individual devices on the 551ac8d5baSMatthew Dillon * bus. 561ac8d5baSMatthew Dillon * 571ac8d5baSMatthew Dillon * Much of the cdb<->xa conversion code was taken from OpenBSD, the rest 581ac8d5baSMatthew Dillon * was written natively for DragonFly. 591ac8d5baSMatthew Dillon */ 601ac8d5baSMatthew Dillon 611ac8d5baSMatthew Dillon #include "sili.h" 621ac8d5baSMatthew Dillon 631ac8d5baSMatthew Dillon static void sili_xpt_action(struct cam_sim *sim, union ccb *ccb); 641ac8d5baSMatthew Dillon static void sili_xpt_poll(struct cam_sim *sim); 651ac8d5baSMatthew Dillon static void sili_xpt_scsi_disk_io(struct sili_port *ap, 661ac8d5baSMatthew Dillon struct ata_port *at, union ccb *ccb); 671ac8d5baSMatthew Dillon static void sili_xpt_scsi_atapi_io(struct sili_port *ap, 681ac8d5baSMatthew Dillon struct ata_port *at, union ccb *ccb); 693ac61d78SMatthew Dillon static void sili_xpt_page_inquiry(struct sili_port *ap, 703ac61d78SMatthew Dillon struct ata_port *at, union ccb *ccb); 711ac8d5baSMatthew Dillon 721ac8d5baSMatthew Dillon static void sili_ata_complete_disk_rw(struct ata_xfer *xa); 731ac8d5baSMatthew Dillon static void sili_ata_complete_disk_synchronize_cache(struct ata_xfer *xa); 741ac8d5baSMatthew Dillon static void sili_atapi_complete_cmd(struct ata_xfer *xa); 751ac8d5baSMatthew Dillon static void sili_ata_dummy_sense(struct scsi_sense_data *sense_data); 761ac8d5baSMatthew Dillon static void sili_ata_atapi_sense(struct ata_fis_d2h *rfis, 771ac8d5baSMatthew Dillon struct scsi_sense_data *sense_data); 781ac8d5baSMatthew Dillon 791ac8d5baSMatthew Dillon static int sili_cam_probe_disk(struct sili_port *ap, struct ata_port *at); 801ac8d5baSMatthew Dillon static int sili_cam_probe_atapi(struct sili_port *ap, struct ata_port *at); 811ac8d5baSMatthew Dillon static void sili_ata_dummy_done(struct ata_xfer *xa); 821ac8d5baSMatthew Dillon static void ata_fix_identify(struct ata_identify *id); 8369b3741eSMatthew Dillon static int sili_set_xfer(struct sili_port *ap, struct ata_port *atx); 841ac8d5baSMatthew Dillon static void sili_cam_rescan(struct sili_port *ap); 853ac61d78SMatthew Dillon static void sili_strip_string(const char **basep, int *lenp); 861ac8d5baSMatthew Dillon 871ac8d5baSMatthew Dillon int 881ac8d5baSMatthew Dillon sili_cam_attach(struct sili_port *ap) 891ac8d5baSMatthew Dillon { 901ac8d5baSMatthew Dillon struct cam_devq *devq; 911ac8d5baSMatthew Dillon struct cam_sim *sim; 921ac8d5baSMatthew Dillon int error; 931ac8d5baSMatthew Dillon int unit; 941ac8d5baSMatthew Dillon 951ac8d5baSMatthew Dillon /* 961ac8d5baSMatthew Dillon * We want at least one ccb to be available for error processing 971ac8d5baSMatthew Dillon * so don't let CAM use more then ncmds - 1. 981ac8d5baSMatthew Dillon */ 991ac8d5baSMatthew Dillon unit = device_get_unit(ap->ap_sc->sc_dev); 1001ac8d5baSMatthew Dillon if (ap->ap_sc->sc_ncmds > 1) 1011ac8d5baSMatthew Dillon devq = cam_simq_alloc(ap->ap_sc->sc_ncmds - 1); 1021ac8d5baSMatthew Dillon else 1031ac8d5baSMatthew Dillon devq = cam_simq_alloc(ap->ap_sc->sc_ncmds); 1041ac8d5baSMatthew Dillon if (devq == NULL) { 1051ac8d5baSMatthew Dillon return (ENOMEM); 1061ac8d5baSMatthew Dillon } 1071ac8d5baSMatthew Dillon sim = cam_sim_alloc(sili_xpt_action, sili_xpt_poll, "sili", 1081ac8d5baSMatthew Dillon (void *)ap, unit, &sim_mplock, 1, 1, devq); 1091ac8d5baSMatthew Dillon cam_simq_release(devq); 1101ac8d5baSMatthew Dillon if (sim == NULL) { 1111ac8d5baSMatthew Dillon return (ENOMEM); 1121ac8d5baSMatthew Dillon } 1131ac8d5baSMatthew Dillon ap->ap_sim = sim; 1141ac8d5baSMatthew Dillon sili_os_unlock_port(ap); 1151ac8d5baSMatthew Dillon error = xpt_bus_register(ap->ap_sim, ap->ap_num); 1161ac8d5baSMatthew Dillon sili_os_lock_port(ap); 1171ac8d5baSMatthew Dillon if (error != CAM_SUCCESS) { 1181ac8d5baSMatthew Dillon sili_cam_detach(ap); 1191ac8d5baSMatthew Dillon return (EINVAL); 1201ac8d5baSMatthew Dillon } 1211ac8d5baSMatthew Dillon ap->ap_flags |= AP_F_BUS_REGISTERED; 1221ac8d5baSMatthew Dillon 1231ac8d5baSMatthew Dillon if (ap->ap_probe == ATA_PROBE_NEED_IDENT) 1241ac8d5baSMatthew Dillon error = sili_cam_probe(ap, NULL); 1251ac8d5baSMatthew Dillon else 1261ac8d5baSMatthew Dillon error = 0; 1271ac8d5baSMatthew Dillon if (error) { 1281ac8d5baSMatthew Dillon sili_cam_detach(ap); 1291ac8d5baSMatthew Dillon return (EIO); 1301ac8d5baSMatthew Dillon } 1311ac8d5baSMatthew Dillon ap->ap_flags |= AP_F_CAM_ATTACHED; 1321ac8d5baSMatthew Dillon 1331ac8d5baSMatthew Dillon return(0); 1341ac8d5baSMatthew Dillon } 1351ac8d5baSMatthew Dillon 1361ac8d5baSMatthew Dillon /* 1371ac8d5baSMatthew Dillon * The state of the port has changed. 1381ac8d5baSMatthew Dillon * 1391ac8d5baSMatthew Dillon * If at is NULL the physical port has changed state. 1401ac8d5baSMatthew Dillon * If at is non-NULL a particular target behind a PM has changed state. 1411ac8d5baSMatthew Dillon * 1421ac8d5baSMatthew Dillon * If found is -1 the target state must be queued to a non-interrupt context. 1431ac8d5baSMatthew Dillon * (only works with at == NULL). 1441ac8d5baSMatthew Dillon * 1451ac8d5baSMatthew Dillon * If found is 0 the target was removed. 1461ac8d5baSMatthew Dillon * If found is 1 the target was inserted. 1471ac8d5baSMatthew Dillon */ 1481ac8d5baSMatthew Dillon void 1491ac8d5baSMatthew Dillon sili_cam_changed(struct sili_port *ap, struct ata_port *atx, int found) 1501ac8d5baSMatthew Dillon { 1511ac8d5baSMatthew Dillon struct cam_path *tmppath; 1521ac8d5baSMatthew Dillon int status; 1531ac8d5baSMatthew Dillon int target; 1541ac8d5baSMatthew Dillon 1551ac8d5baSMatthew Dillon target = atx ? atx->at_target : CAM_TARGET_WILDCARD; 1561ac8d5baSMatthew Dillon 1571ac8d5baSMatthew Dillon if (ap->ap_sim == NULL) 1581ac8d5baSMatthew Dillon return; 1591ac8d5baSMatthew Dillon if (found == CAM_TARGET_WILDCARD) { 1601ac8d5baSMatthew Dillon status = xpt_create_path(&tmppath, NULL, 1611ac8d5baSMatthew Dillon cam_sim_path(ap->ap_sim), 1621ac8d5baSMatthew Dillon target, CAM_LUN_WILDCARD); 1631ac8d5baSMatthew Dillon if (status != CAM_REQ_CMP) 1641ac8d5baSMatthew Dillon return; 1651ac8d5baSMatthew Dillon sili_cam_rescan(ap); 1661ac8d5baSMatthew Dillon } else { 1671ac8d5baSMatthew Dillon status = xpt_create_path(&tmppath, NULL, 1681ac8d5baSMatthew Dillon cam_sim_path(ap->ap_sim), 1691ac8d5baSMatthew Dillon target, 1701ac8d5baSMatthew Dillon CAM_LUN_WILDCARD); 1711ac8d5baSMatthew Dillon if (status != CAM_REQ_CMP) 1721ac8d5baSMatthew Dillon return; 1731ac8d5baSMatthew Dillon #if 0 1741ac8d5baSMatthew Dillon /* 1751ac8d5baSMatthew Dillon * This confuses CAM 1761ac8d5baSMatthew Dillon */ 1771ac8d5baSMatthew Dillon if (found) 1781ac8d5baSMatthew Dillon xpt_async(AC_FOUND_DEVICE, tmppath, NULL); 1791ac8d5baSMatthew Dillon else 1801ac8d5baSMatthew Dillon xpt_async(AC_LOST_DEVICE, tmppath, NULL); 1811ac8d5baSMatthew Dillon #endif 1821ac8d5baSMatthew Dillon } 1831ac8d5baSMatthew Dillon xpt_free_path(tmppath); 1841ac8d5baSMatthew Dillon } 1851ac8d5baSMatthew Dillon 1861ac8d5baSMatthew Dillon void 1871ac8d5baSMatthew Dillon sili_cam_detach(struct sili_port *ap) 1881ac8d5baSMatthew Dillon { 1891ac8d5baSMatthew Dillon int error; 1901ac8d5baSMatthew Dillon 1911ac8d5baSMatthew Dillon if ((ap->ap_flags & AP_F_CAM_ATTACHED) == 0) 1921ac8d5baSMatthew Dillon return; 1931ac8d5baSMatthew Dillon get_mplock(); 1941ac8d5baSMatthew Dillon if (ap->ap_sim) { 1951ac8d5baSMatthew Dillon xpt_freeze_simq(ap->ap_sim, 1); 1961ac8d5baSMatthew Dillon } 1971ac8d5baSMatthew Dillon if (ap->ap_flags & AP_F_BUS_REGISTERED) { 1981ac8d5baSMatthew Dillon error = xpt_bus_deregister(cam_sim_path(ap->ap_sim)); 1991ac8d5baSMatthew Dillon KKASSERT(error == CAM_REQ_CMP); 2001ac8d5baSMatthew Dillon ap->ap_flags &= ~AP_F_BUS_REGISTERED; 2011ac8d5baSMatthew Dillon } 2021ac8d5baSMatthew Dillon if (ap->ap_sim) { 2031ac8d5baSMatthew Dillon cam_sim_free(ap->ap_sim); 2041ac8d5baSMatthew Dillon ap->ap_sim = NULL; 2051ac8d5baSMatthew Dillon } 2061ac8d5baSMatthew Dillon rel_mplock(); 2071ac8d5baSMatthew Dillon ap->ap_flags &= ~AP_F_CAM_ATTACHED; 2081ac8d5baSMatthew Dillon } 2091ac8d5baSMatthew Dillon 2101ac8d5baSMatthew Dillon /* 2111ac8d5baSMatthew Dillon * Once the SILI port has been attached we need to probe for a device or 2121ac8d5baSMatthew Dillon * devices on the port and setup various options. 2131ac8d5baSMatthew Dillon * 2141ac8d5baSMatthew Dillon * If at is NULL we are probing the direct-attached device on the port, 2151ac8d5baSMatthew Dillon * which may or may not be a port multiplier. 2161ac8d5baSMatthew Dillon */ 2171ac8d5baSMatthew Dillon int 2181ac8d5baSMatthew Dillon sili_cam_probe(struct sili_port *ap, struct ata_port *atx) 2191ac8d5baSMatthew Dillon { 2201ac8d5baSMatthew Dillon struct ata_port *at; 2211ac8d5baSMatthew Dillon struct ata_xfer *xa; 2221ac8d5baSMatthew Dillon u_int64_t capacity; 2231ac8d5baSMatthew Dillon u_int64_t capacity_bytes; 2241ac8d5baSMatthew Dillon int model_len; 2253ac61d78SMatthew Dillon int firmware_len; 2263ac61d78SMatthew Dillon int serial_len; 2271ac8d5baSMatthew Dillon int error; 2281ac8d5baSMatthew Dillon int devncqdepth; 2291ac8d5baSMatthew Dillon int i; 2303ac61d78SMatthew Dillon const char *model_id; 2313ac61d78SMatthew Dillon const char *firmware_id; 2323ac61d78SMatthew Dillon const char *serial_id; 2331ac8d5baSMatthew Dillon const char *wcstr; 2341ac8d5baSMatthew Dillon const char *rastr; 2351ac8d5baSMatthew Dillon const char *scstr; 2361ac8d5baSMatthew Dillon const char *type; 2371ac8d5baSMatthew Dillon 2381ac8d5baSMatthew Dillon error = EIO; 2391ac8d5baSMatthew Dillon 2401ac8d5baSMatthew Dillon /* 2411ac8d5baSMatthew Dillon * Delayed CAM attachment for initial probe, sim may be NULL 2421ac8d5baSMatthew Dillon */ 2431ac8d5baSMatthew Dillon if (ap->ap_sim == NULL) 2441ac8d5baSMatthew Dillon return(0); 2451ac8d5baSMatthew Dillon 2461ac8d5baSMatthew Dillon /* 2471ac8d5baSMatthew Dillon * A NULL atx indicates a probe of the directly connected device. 2481ac8d5baSMatthew Dillon * A non-NULL atx indicates a device connected via a port multiplier. 2491ac8d5baSMatthew Dillon * We need to preserve atx for calls to sili_ata_get_xfer(). 2501ac8d5baSMatthew Dillon * 2511ac8d5baSMatthew Dillon * at is always non-NULL. For directly connected devices we supply 2521ac8d5baSMatthew Dillon * an (at) pointing to target 0. 2531ac8d5baSMatthew Dillon */ 2541ac8d5baSMatthew Dillon if (atx == NULL) { 2551ac8d5baSMatthew Dillon at = ap->ap_ata; /* direct attached - device 0 */ 2561ac8d5baSMatthew Dillon if (ap->ap_type == ATA_PORT_T_PM) { 2571ac8d5baSMatthew Dillon kprintf("%s: Found Port Multiplier\n", 2581ac8d5baSMatthew Dillon ATANAME(ap, atx)); 2591ac8d5baSMatthew Dillon return (0); 2601ac8d5baSMatthew Dillon } 2611ac8d5baSMatthew Dillon at->at_type = ap->ap_type; 2621ac8d5baSMatthew Dillon } else { 2631ac8d5baSMatthew Dillon at = atx; 2641ac8d5baSMatthew Dillon if (atx->at_type == ATA_PORT_T_PM) { 2651ac8d5baSMatthew Dillon kprintf("%s: Bogus device, reducing port count to %d\n", 2661ac8d5baSMatthew Dillon ATANAME(ap, atx), atx->at_target); 2671ac8d5baSMatthew Dillon if (ap->ap_pmcount > atx->at_target) 2681ac8d5baSMatthew Dillon ap->ap_pmcount = atx->at_target; 2691ac8d5baSMatthew Dillon goto err; 2701ac8d5baSMatthew Dillon } 2711ac8d5baSMatthew Dillon } 2721ac8d5baSMatthew Dillon if (ap->ap_type == ATA_PORT_T_NONE) 2731ac8d5baSMatthew Dillon goto err; 2741ac8d5baSMatthew Dillon if (at->at_type == ATA_PORT_T_NONE) 2751ac8d5baSMatthew Dillon goto err; 2761ac8d5baSMatthew Dillon 2771ac8d5baSMatthew Dillon /* 2781ac8d5baSMatthew Dillon * Issue identify, saving the result 2791ac8d5baSMatthew Dillon */ 2801ac8d5baSMatthew Dillon xa = sili_ata_get_xfer(ap, atx); 2811ac8d5baSMatthew Dillon xa->complete = sili_ata_dummy_done; 2821ac8d5baSMatthew Dillon xa->data = &at->at_identify; 2831ac8d5baSMatthew Dillon xa->datalen = sizeof(at->at_identify); 2844383d440SMatthew Dillon xa->flags = ATA_F_READ | ATA_F_PIO | ATA_F_POLL; 2851ac8d5baSMatthew Dillon xa->fis->flags = ATA_H2D_FLAGS_CMD | at->at_target; 2861ac8d5baSMatthew Dillon 2871ac8d5baSMatthew Dillon switch(at->at_type) { 2881ac8d5baSMatthew Dillon case ATA_PORT_T_DISK: 2891ac8d5baSMatthew Dillon xa->fis->command = ATA_C_IDENTIFY; 2901ac8d5baSMatthew Dillon type = "DISK"; 2911ac8d5baSMatthew Dillon break; 2921ac8d5baSMatthew Dillon case ATA_PORT_T_ATAPI: 2931ac8d5baSMatthew Dillon xa->fis->command = ATA_C_ATAPI_IDENTIFY; 2944383d440SMatthew Dillon xa->flags |= ATA_F_AUTOSENSE; 2951ac8d5baSMatthew Dillon type = "ATAPI"; 2961ac8d5baSMatthew Dillon break; 2971ac8d5baSMatthew Dillon default: 2981ac8d5baSMatthew Dillon xa->fis->command = ATA_C_ATAPI_IDENTIFY; 2991ac8d5baSMatthew Dillon type = "UNKNOWN(ATAPI?)"; 3001ac8d5baSMatthew Dillon break; 3011ac8d5baSMatthew Dillon } 3021ac8d5baSMatthew Dillon xa->fis->features = 0; 3031ac8d5baSMatthew Dillon xa->fis->device = 0; 3041ac8d5baSMatthew Dillon xa->timeout = 1000; 3051ac8d5baSMatthew Dillon 3061ac8d5baSMatthew Dillon if (sili_ata_cmd(xa) != ATA_S_COMPLETE) { 3071ac8d5baSMatthew Dillon kprintf("%s: Detected %s device but unable to IDENTIFY\n", 3081ac8d5baSMatthew Dillon ATANAME(ap, atx), type); 3091ac8d5baSMatthew Dillon sili_ata_put_xfer(xa); 3101ac8d5baSMatthew Dillon goto err; 3111ac8d5baSMatthew Dillon } 3121ac8d5baSMatthew Dillon sili_ata_put_xfer(xa); 3131ac8d5baSMatthew Dillon 3141ac8d5baSMatthew Dillon ata_fix_identify(&at->at_identify); 3151ac8d5baSMatthew Dillon 3161ac8d5baSMatthew Dillon /* 3171ac8d5baSMatthew Dillon * Read capacity using SATA probe info. 3181ac8d5baSMatthew Dillon */ 3191ac8d5baSMatthew Dillon if (le16toh(at->at_identify.cmdset83) & 0x0400) { 3201ac8d5baSMatthew Dillon /* LBA48 feature set supported */ 3211ac8d5baSMatthew Dillon capacity = 0; 3221ac8d5baSMatthew Dillon for (i = 3; i >= 0; --i) { 3231ac8d5baSMatthew Dillon capacity <<= 16; 3241ac8d5baSMatthew Dillon capacity += 3251ac8d5baSMatthew Dillon le16toh(at->at_identify.addrsecxt[i]); 3261ac8d5baSMatthew Dillon } 3271ac8d5baSMatthew Dillon } else { 3281ac8d5baSMatthew Dillon capacity = le16toh(at->at_identify.addrsec[1]); 3291ac8d5baSMatthew Dillon capacity <<= 16; 3301ac8d5baSMatthew Dillon capacity += le16toh(at->at_identify.addrsec[0]); 3311ac8d5baSMatthew Dillon } 3321ac8d5baSMatthew Dillon at->at_capacity = capacity; 3331ac8d5baSMatthew Dillon if (atx == NULL) 3341ac8d5baSMatthew Dillon ap->ap_probe = ATA_PROBE_GOOD; 3351ac8d5baSMatthew Dillon 3361ac8d5baSMatthew Dillon capacity_bytes = capacity * 512; 3371ac8d5baSMatthew Dillon 3381ac8d5baSMatthew Dillon /* 3391ac8d5baSMatthew Dillon * Negotiate NCQ, throw away any ata_xfer's beyond the negotiated 3401ac8d5baSMatthew Dillon * number of slots and limit the number of CAM ccb's to one less 3411ac8d5baSMatthew Dillon * so we always have a slot available for recovery. 3421ac8d5baSMatthew Dillon * 3431ac8d5baSMatthew Dillon * NCQ is not used if ap_ncqdepth is 1 or the host controller does 3441ac8d5baSMatthew Dillon * not support it, and in that case the driver can handle extra 3451ac8d5baSMatthew Dillon * ccb's. 3461ac8d5baSMatthew Dillon * 3471ac8d5baSMatthew Dillon * NCQ is currently used only with direct-attached disks. It is 3481ac8d5baSMatthew Dillon * not used with port multipliers or direct-attached ATAPI devices. 3491ac8d5baSMatthew Dillon * 3501ac8d5baSMatthew Dillon * Remember at least one extra CCB needs to be reserved for the 3511ac8d5baSMatthew Dillon * error ccb. 3521ac8d5baSMatthew Dillon */ 3531ac8d5baSMatthew Dillon if ((ap->ap_sc->sc_flags & SILI_F_NCQ) && 3541ac8d5baSMatthew Dillon at->at_type == ATA_PORT_T_DISK && 3551ac8d5baSMatthew Dillon (le16toh(at->at_identify.satacap) & (1 << 8))) { 3561ac8d5baSMatthew Dillon at->at_ncqdepth = (le16toh(at->at_identify.qdepth) & 0x1F) + 1; 3571ac8d5baSMatthew Dillon devncqdepth = at->at_ncqdepth; 3581ac8d5baSMatthew Dillon if (at->at_ncqdepth > ap->ap_sc->sc_ncmds) 3591ac8d5baSMatthew Dillon at->at_ncqdepth = ap->ap_sc->sc_ncmds; 3601ac8d5baSMatthew Dillon if (at->at_ncqdepth > 1) { 3611ac8d5baSMatthew Dillon for (i = 0; i < ap->ap_sc->sc_ncmds; ++i) { 3621ac8d5baSMatthew Dillon xa = sili_ata_get_xfer(ap, atx); 3631ac8d5baSMatthew Dillon if (xa->tag < at->at_ncqdepth) { 3641ac8d5baSMatthew Dillon xa->state = ATA_S_COMPLETE; 3651ac8d5baSMatthew Dillon sili_ata_put_xfer(xa); 3661ac8d5baSMatthew Dillon } 3671ac8d5baSMatthew Dillon } 3681ac8d5baSMatthew Dillon if (at->at_ncqdepth >= ap->ap_sc->sc_ncmds) { 3691ac8d5baSMatthew Dillon cam_devq_resize(ap->ap_sim->devq, 3701ac8d5baSMatthew Dillon at->at_ncqdepth - 1); 3711ac8d5baSMatthew Dillon } 3721ac8d5baSMatthew Dillon } 3731ac8d5baSMatthew Dillon } else { 3741ac8d5baSMatthew Dillon devncqdepth = 0; 3751ac8d5baSMatthew Dillon } 3761ac8d5baSMatthew Dillon 3771ac8d5baSMatthew Dillon /* 3781ac8d5baSMatthew Dillon * Make the model string a bit more presentable 3791ac8d5baSMatthew Dillon */ 3801ac8d5baSMatthew Dillon for (model_len = 40; model_len; --model_len) { 3811ac8d5baSMatthew Dillon if (at->at_identify.model[model_len-1] == ' ') 3821ac8d5baSMatthew Dillon continue; 3831ac8d5baSMatthew Dillon if (at->at_identify.model[model_len-1] == 0) 3841ac8d5baSMatthew Dillon continue; 3851ac8d5baSMatthew Dillon break; 3861ac8d5baSMatthew Dillon } 3871ac8d5baSMatthew Dillon 3883ac61d78SMatthew Dillon model_len = sizeof(at->at_identify.model); 3893ac61d78SMatthew Dillon model_id = at->at_identify.model; 3903ac61d78SMatthew Dillon sili_strip_string(&model_id, &model_len); 3913ac61d78SMatthew Dillon 3923ac61d78SMatthew Dillon firmware_len = sizeof(at->at_identify.firmware); 3933ac61d78SMatthew Dillon firmware_id = at->at_identify.firmware; 3943ac61d78SMatthew Dillon sili_strip_string(&firmware_id, &firmware_len); 3953ac61d78SMatthew Dillon 3963ac61d78SMatthew Dillon serial_len = sizeof(at->at_identify.serial); 3973ac61d78SMatthew Dillon serial_id = at->at_identify.serial; 3983ac61d78SMatthew Dillon sili_strip_string(&serial_id, &serial_len); 3993ac61d78SMatthew Dillon 4001ac8d5baSMatthew Dillon /* 4011ac8d5baSMatthew Dillon * Generate informatiive strings. 4021ac8d5baSMatthew Dillon * 4031ac8d5baSMatthew Dillon * NOTE: We do not automatically set write caching, lookahead, 4041ac8d5baSMatthew Dillon * or the security state for ATAPI devices. 4051ac8d5baSMatthew Dillon */ 4061ac8d5baSMatthew Dillon if (at->at_identify.cmdset82 & ATA_IDENTIFY_WRITECACHE) { 4071ac8d5baSMatthew Dillon if (at->at_identify.features85 & ATA_IDENTIFY_WRITECACHE) 4081ac8d5baSMatthew Dillon wcstr = "enabled"; 4091ac8d5baSMatthew Dillon else if (at->at_type == ATA_PORT_T_ATAPI) 4101ac8d5baSMatthew Dillon wcstr = "disabled"; 4111ac8d5baSMatthew Dillon else 4121ac8d5baSMatthew Dillon wcstr = "enabling"; 4131ac8d5baSMatthew Dillon } else { 4141ac8d5baSMatthew Dillon wcstr = "notsupp"; 4151ac8d5baSMatthew Dillon } 4161ac8d5baSMatthew Dillon 4171ac8d5baSMatthew Dillon if (at->at_identify.cmdset82 & ATA_IDENTIFY_LOOKAHEAD) { 4181ac8d5baSMatthew Dillon if (at->at_identify.features85 & ATA_IDENTIFY_LOOKAHEAD) 4191ac8d5baSMatthew Dillon rastr = "enabled"; 4201ac8d5baSMatthew Dillon else if (at->at_type == ATA_PORT_T_ATAPI) 4211ac8d5baSMatthew Dillon rastr = "disabled"; 4221ac8d5baSMatthew Dillon else 4231ac8d5baSMatthew Dillon rastr = "enabling"; 4241ac8d5baSMatthew Dillon } else { 4251ac8d5baSMatthew Dillon rastr = "notsupp"; 4261ac8d5baSMatthew Dillon } 4271ac8d5baSMatthew Dillon 4281ac8d5baSMatthew Dillon if (at->at_identify.cmdset82 & ATA_IDENTIFY_SECURITY) { 4291ac8d5baSMatthew Dillon if (at->at_identify.securestatus & ATA_SECURE_FROZEN) 4301ac8d5baSMatthew Dillon scstr = "frozen"; 4311ac8d5baSMatthew Dillon else if (at->at_type == ATA_PORT_T_ATAPI) 4321ac8d5baSMatthew Dillon scstr = "unfrozen"; 4331ac8d5baSMatthew Dillon else if (SiliNoFeatures & (1 << ap->ap_num)) 4341ac8d5baSMatthew Dillon scstr = "<disabled>"; 4351ac8d5baSMatthew Dillon else 4361ac8d5baSMatthew Dillon scstr = "freezing"; 4371ac8d5baSMatthew Dillon } else { 4381ac8d5baSMatthew Dillon scstr = "notsupp"; 4391ac8d5baSMatthew Dillon } 4401ac8d5baSMatthew Dillon 4413ac61d78SMatthew Dillon kprintf("%s: Found %s \"%*.*s %*.*s\" serial=\"%*.*s\"\n" 4421ac8d5baSMatthew Dillon "%s: tags=%d/%d satacap=%04x satafea=%04x NCQ=%s " 4431ac8d5baSMatthew Dillon "capacity=%lld.%02dMB\n", 4441ac8d5baSMatthew Dillon 4451ac8d5baSMatthew Dillon ATANAME(ap, atx), 4461ac8d5baSMatthew Dillon type, 4473ac61d78SMatthew Dillon model_len, model_len, model_id, 4483ac61d78SMatthew Dillon firmware_len, firmware_len, firmware_id, 4493ac61d78SMatthew Dillon serial_len, serial_len, serial_id, 4501ac8d5baSMatthew Dillon 4511ac8d5baSMatthew Dillon ATANAME(ap, atx), 4521ac8d5baSMatthew Dillon devncqdepth, ap->ap_sc->sc_ncmds, 4531ac8d5baSMatthew Dillon at->at_identify.satacap, 4541ac8d5baSMatthew Dillon at->at_identify.satafsup, 4551ac8d5baSMatthew Dillon (at->at_ncqdepth > 1 ? "YES" : "NO"), 4561ac8d5baSMatthew Dillon (long long)capacity_bytes / (1024 * 1024), 4571ac8d5baSMatthew Dillon (int)(capacity_bytes % (1024 * 1024)) * 100 / (1024 * 1024) 4581ac8d5baSMatthew Dillon ); 4591ac8d5baSMatthew Dillon kprintf("%s: f85=%04x f86=%04x f87=%04x WC=%s RA=%s SEC=%s\n", 4601ac8d5baSMatthew Dillon ATANAME(ap, atx), 4611ac8d5baSMatthew Dillon at->at_identify.features85, 4621ac8d5baSMatthew Dillon at->at_identify.features86, 4631ac8d5baSMatthew Dillon at->at_identify.features87, 4641ac8d5baSMatthew Dillon wcstr, 4651ac8d5baSMatthew Dillon rastr, 4661ac8d5baSMatthew Dillon scstr 4671ac8d5baSMatthew Dillon ); 4681ac8d5baSMatthew Dillon 4691ac8d5baSMatthew Dillon /* 4701ac8d5baSMatthew Dillon * Additional type-specific probing 4711ac8d5baSMatthew Dillon */ 4721ac8d5baSMatthew Dillon switch(at->at_type) { 4731ac8d5baSMatthew Dillon case ATA_PORT_T_DISK: 4741ac8d5baSMatthew Dillon error = sili_cam_probe_disk(ap, atx); 4751ac8d5baSMatthew Dillon break; 4761ac8d5baSMatthew Dillon case ATA_PORT_T_ATAPI: 4771ac8d5baSMatthew Dillon error = sili_cam_probe_atapi(ap, atx); 4781ac8d5baSMatthew Dillon break; 4791ac8d5baSMatthew Dillon default: 4801ac8d5baSMatthew Dillon error = EIO; 4811ac8d5baSMatthew Dillon break; 4821ac8d5baSMatthew Dillon } 4831ac8d5baSMatthew Dillon err: 4841ac8d5baSMatthew Dillon if (error) { 4851ac8d5baSMatthew Dillon at->at_probe = ATA_PROBE_FAILED; 4861ac8d5baSMatthew Dillon if (atx == NULL) 4871ac8d5baSMatthew Dillon ap->ap_probe = at->at_probe; 4881ac8d5baSMatthew Dillon } else { 4891ac8d5baSMatthew Dillon at->at_probe = ATA_PROBE_GOOD; 4901ac8d5baSMatthew Dillon if (atx == NULL) 4911ac8d5baSMatthew Dillon ap->ap_probe = at->at_probe; 4921ac8d5baSMatthew Dillon } 4931ac8d5baSMatthew Dillon return (error); 4941ac8d5baSMatthew Dillon } 4951ac8d5baSMatthew Dillon 4961ac8d5baSMatthew Dillon /* 4971ac8d5baSMatthew Dillon * DISK-specific probe after initial ident 4981ac8d5baSMatthew Dillon */ 4991ac8d5baSMatthew Dillon static int 5001ac8d5baSMatthew Dillon sili_cam_probe_disk(struct sili_port *ap, struct ata_port *atx) 5011ac8d5baSMatthew Dillon { 5021ac8d5baSMatthew Dillon struct ata_port *at; 5031ac8d5baSMatthew Dillon struct ata_xfer *xa; 5041ac8d5baSMatthew Dillon 5051ac8d5baSMatthew Dillon at = atx ? atx : ap->ap_ata; 5061ac8d5baSMatthew Dillon 5071ac8d5baSMatthew Dillon /* 50869b3741eSMatthew Dillon * Set dummy xfer mode 50969b3741eSMatthew Dillon */ 51069b3741eSMatthew Dillon sili_set_xfer(ap, atx); 51169b3741eSMatthew Dillon 51269b3741eSMatthew Dillon /* 5131ac8d5baSMatthew Dillon * Enable write cache if supported 5141ac8d5baSMatthew Dillon * 5151ac8d5baSMatthew Dillon * NOTE: "WD My Book" external disk devices have a very poor 5161ac8d5baSMatthew Dillon * daughter board between the the ESATA and the HD. Sending 5171ac8d5baSMatthew Dillon * any ATA_C_SET_FEATURES commands will break the hardware port 5181ac8d5baSMatthew Dillon * with a fatal protocol error. However, this device also 5191ac8d5baSMatthew Dillon * indicates that WRITECACHE is already on and READAHEAD is 5201ac8d5baSMatthew Dillon * not supported so we avoid the issue. 5211ac8d5baSMatthew Dillon */ 5221ac8d5baSMatthew Dillon if ((at->at_identify.cmdset82 & ATA_IDENTIFY_WRITECACHE) && 5231ac8d5baSMatthew Dillon (at->at_identify.features85 & ATA_IDENTIFY_WRITECACHE) == 0) { 5241ac8d5baSMatthew Dillon xa = sili_ata_get_xfer(ap, atx); 5251ac8d5baSMatthew Dillon xa->complete = sili_ata_dummy_done; 5261ac8d5baSMatthew Dillon xa->fis->command = ATA_C_SET_FEATURES; 5271ac8d5baSMatthew Dillon /*xa->fis->features = ATA_SF_WRITECACHE_EN;*/ 5281ac8d5baSMatthew Dillon xa->fis->features = ATA_SF_LOOKAHEAD_EN; 5291ac8d5baSMatthew Dillon xa->fis->flags = ATA_H2D_FLAGS_CMD | at->at_target; 5301ac8d5baSMatthew Dillon xa->fis->device = 0; 5311ac8d5baSMatthew Dillon xa->flags = ATA_F_READ | ATA_F_PIO | ATA_F_POLL; 5321ac8d5baSMatthew Dillon xa->timeout = 1000; 5331ac8d5baSMatthew Dillon xa->datalen = 0; 5341ac8d5baSMatthew Dillon if (sili_ata_cmd(xa) == ATA_S_COMPLETE) 5351ac8d5baSMatthew Dillon at->at_features |= ATA_PORT_F_WCACHE; 5361ac8d5baSMatthew Dillon else 5371ac8d5baSMatthew Dillon kprintf("%s: Unable to enable write-caching\n", 5381ac8d5baSMatthew Dillon ATANAME(ap, atx)); 5391ac8d5baSMatthew Dillon sili_ata_put_xfer(xa); 5401ac8d5baSMatthew Dillon } 5411ac8d5baSMatthew Dillon 5421ac8d5baSMatthew Dillon /* 5431ac8d5baSMatthew Dillon * Enable readahead if supported 5441ac8d5baSMatthew Dillon */ 5451ac8d5baSMatthew Dillon if ((at->at_identify.cmdset82 & ATA_IDENTIFY_LOOKAHEAD) && 5461ac8d5baSMatthew Dillon (at->at_identify.features85 & ATA_IDENTIFY_LOOKAHEAD) == 0) { 5471ac8d5baSMatthew Dillon xa = sili_ata_get_xfer(ap, atx); 5481ac8d5baSMatthew Dillon xa->complete = sili_ata_dummy_done; 5491ac8d5baSMatthew Dillon xa->fis->command = ATA_C_SET_FEATURES; 5501ac8d5baSMatthew Dillon xa->fis->features = ATA_SF_LOOKAHEAD_EN; 5511ac8d5baSMatthew Dillon xa->fis->flags = ATA_H2D_FLAGS_CMD | at->at_target; 5521ac8d5baSMatthew Dillon xa->fis->device = 0; 5531ac8d5baSMatthew Dillon xa->flags = ATA_F_READ | ATA_F_PIO | ATA_F_POLL; 5541ac8d5baSMatthew Dillon xa->timeout = 1000; 5551ac8d5baSMatthew Dillon xa->datalen = 0; 5561ac8d5baSMatthew Dillon if (sili_ata_cmd(xa) == ATA_S_COMPLETE) 5571ac8d5baSMatthew Dillon at->at_features |= ATA_PORT_F_RAHEAD; 5581ac8d5baSMatthew Dillon else 5591ac8d5baSMatthew Dillon kprintf("%s: Unable to enable read-ahead\n", 5601ac8d5baSMatthew Dillon ATANAME(ap, atx)); 5611ac8d5baSMatthew Dillon sili_ata_put_xfer(xa); 5621ac8d5baSMatthew Dillon } 5631ac8d5baSMatthew Dillon 5641ac8d5baSMatthew Dillon /* 5651ac8d5baSMatthew Dillon * FREEZE LOCK the device so malicious users can't lock it on us. 5661ac8d5baSMatthew Dillon * As there is no harm in issuing this to devices that don't 5671ac8d5baSMatthew Dillon * support the security feature set we just send it, and don't bother 5681ac8d5baSMatthew Dillon * checking if the device sends a command abort to tell us it doesn't 5691ac8d5baSMatthew Dillon * support it 5701ac8d5baSMatthew Dillon */ 5711ac8d5baSMatthew Dillon if ((at->at_identify.cmdset82 & ATA_IDENTIFY_SECURITY) && 5721ac8d5baSMatthew Dillon (at->at_identify.securestatus & ATA_SECURE_FROZEN) == 0 && 5731ac8d5baSMatthew Dillon (SiliNoFeatures & (1 << ap->ap_num)) == 0) { 5741ac8d5baSMatthew Dillon xa = sili_ata_get_xfer(ap, atx); 5751ac8d5baSMatthew Dillon xa->complete = sili_ata_dummy_done; 5761ac8d5baSMatthew Dillon xa->fis->command = ATA_C_SEC_FREEZE_LOCK; 5771ac8d5baSMatthew Dillon xa->fis->flags = ATA_H2D_FLAGS_CMD | at->at_target; 5781ac8d5baSMatthew Dillon xa->flags = ATA_F_READ | ATA_F_PIO | ATA_F_POLL; 5791ac8d5baSMatthew Dillon xa->timeout = 1000; 5801ac8d5baSMatthew Dillon xa->datalen = 0; 5811ac8d5baSMatthew Dillon if (sili_ata_cmd(xa) == ATA_S_COMPLETE) 5821ac8d5baSMatthew Dillon at->at_features |= ATA_PORT_F_FRZLCK; 5831ac8d5baSMatthew Dillon else 5841ac8d5baSMatthew Dillon kprintf("%s: Unable to set security freeze\n", 5851ac8d5baSMatthew Dillon ATANAME(ap, atx)); 5861ac8d5baSMatthew Dillon sili_ata_put_xfer(xa); 5871ac8d5baSMatthew Dillon } 5881ac8d5baSMatthew Dillon 5891ac8d5baSMatthew Dillon return (0); 5901ac8d5baSMatthew Dillon } 5911ac8d5baSMatthew Dillon 5921ac8d5baSMatthew Dillon /* 5931ac8d5baSMatthew Dillon * ATAPI-specific probe after initial ident 5941ac8d5baSMatthew Dillon */ 5951ac8d5baSMatthew Dillon static int 5961ac8d5baSMatthew Dillon sili_cam_probe_atapi(struct sili_port *ap, struct ata_port *atx) 5971ac8d5baSMatthew Dillon { 59869b3741eSMatthew Dillon sili_set_xfer(ap, atx); 59969b3741eSMatthew Dillon return(0); 60069b3741eSMatthew Dillon } 60169b3741eSMatthew Dillon 60269b3741eSMatthew Dillon /* 60369b3741eSMatthew Dillon * Setting the transfer mode is irrelevant for the SATA transport 60469b3741eSMatthew Dillon * but some (atapi) devices seem to need it anyway. In addition 60569b3741eSMatthew Dillon * if we are running through a SATA->PATA converter for some reason 60669b3741eSMatthew Dillon * beyond my comprehension we might have to set the mode. 607d15a49f2SMatthew Dillon * 608d15a49f2SMatthew Dillon * We only support DMA modes for SATA attached devices, so don't bother 609d15a49f2SMatthew Dillon * with legacy modes. 61069b3741eSMatthew Dillon */ 61169b3741eSMatthew Dillon static int 61269b3741eSMatthew Dillon sili_set_xfer(struct sili_port *ap, struct ata_port *atx) 61369b3741eSMatthew Dillon { 61469b3741eSMatthew Dillon struct ata_port *at; 61569b3741eSMatthew Dillon struct ata_xfer *xa; 61669b3741eSMatthew Dillon u_int16_t mode; 617d15a49f2SMatthew Dillon u_int16_t mask; 61869b3741eSMatthew Dillon 61969b3741eSMatthew Dillon at = atx ? atx : ap->ap_ata; 62069b3741eSMatthew Dillon 62169b3741eSMatthew Dillon /* 622d15a49f2SMatthew Dillon * Figure out the supported UDMA mode. Ignore other legacy modes. 62369b3741eSMatthew Dillon */ 624d15a49f2SMatthew Dillon mask = le16toh(at->at_identify.ultradma); 625d15a49f2SMatthew Dillon if ((mask & 0xFF) == 0 || mask == 0xFFFF) 62669b3741eSMatthew Dillon return(0); 627d15a49f2SMatthew Dillon mask &= 0xFF; 628d15a49f2SMatthew Dillon mode = 0x4F; 629d15a49f2SMatthew Dillon while ((mask & 0x8000) == 0) { 630d15a49f2SMatthew Dillon mask <<= 1; 631d15a49f2SMatthew Dillon --mode; 632d15a49f2SMatthew Dillon } 63369b3741eSMatthew Dillon 63469b3741eSMatthew Dillon /* 63569b3741eSMatthew Dillon * SATA atapi devices often still report a dma mode, even though 63669b3741eSMatthew Dillon * it is irrelevant for SATA transport. It is also possible that 63769b3741eSMatthew Dillon * we are running through a SATA->PATA converter and seeing the 63869b3741eSMatthew Dillon * PATA dma mode. 63969b3741eSMatthew Dillon * 64069b3741eSMatthew Dillon * In this case the device may require a (dummy) SETXFER to be 64169b3741eSMatthew Dillon * sent before it will work properly. 64269b3741eSMatthew Dillon */ 64369b3741eSMatthew Dillon xa = sili_ata_get_xfer(ap, atx); 64469b3741eSMatthew Dillon xa->complete = sili_ata_dummy_done; 64569b3741eSMatthew Dillon xa->fis->command = ATA_C_SET_FEATURES; 64669b3741eSMatthew Dillon xa->fis->features = ATA_SF_SETXFER; 64769b3741eSMatthew Dillon xa->fis->flags = ATA_H2D_FLAGS_CMD | at->at_target; 648d15a49f2SMatthew Dillon xa->fis->sector_count = mode; 64969b3741eSMatthew Dillon xa->flags = ATA_F_PIO | ATA_F_POLL; 65069b3741eSMatthew Dillon xa->timeout = 1000; 65169b3741eSMatthew Dillon xa->datalen = 0; 65269b3741eSMatthew Dillon if (sili_ata_cmd(xa) != ATA_S_COMPLETE) { 65369b3741eSMatthew Dillon kprintf("%s: Unable to set dummy xfer mode \n", 65469b3741eSMatthew Dillon ATANAME(ap, atx)); 65569b3741eSMatthew Dillon } else if (bootverbose) { 65669b3741eSMatthew Dillon kprintf("%s: Set dummy xfer mode to %02x\n", 657d15a49f2SMatthew Dillon ATANAME(ap, atx), mode); 65869b3741eSMatthew Dillon } 65969b3741eSMatthew Dillon sili_ata_put_xfer(xa); 6601ac8d5baSMatthew Dillon return(0); 6611ac8d5baSMatthew Dillon } 6621ac8d5baSMatthew Dillon 6631ac8d5baSMatthew Dillon /* 6641ac8d5baSMatthew Dillon * Fix byte ordering so buffers can be accessed as 6651ac8d5baSMatthew Dillon * strings. 6661ac8d5baSMatthew Dillon */ 6671ac8d5baSMatthew Dillon static void 6681ac8d5baSMatthew Dillon ata_fix_identify(struct ata_identify *id) 6691ac8d5baSMatthew Dillon { 6701ac8d5baSMatthew Dillon u_int16_t *swap; 6711ac8d5baSMatthew Dillon int i; 6721ac8d5baSMatthew Dillon 6731ac8d5baSMatthew Dillon swap = (u_int16_t *)id->serial; 6741ac8d5baSMatthew Dillon for (i = 0; i < sizeof(id->serial) / sizeof(u_int16_t); i++) 6751ac8d5baSMatthew Dillon swap[i] = bswap16(swap[i]); 6761ac8d5baSMatthew Dillon 6771ac8d5baSMatthew Dillon swap = (u_int16_t *)id->firmware; 6781ac8d5baSMatthew Dillon for (i = 0; i < sizeof(id->firmware) / sizeof(u_int16_t); i++) 6791ac8d5baSMatthew Dillon swap[i] = bswap16(swap[i]); 6801ac8d5baSMatthew Dillon 6811ac8d5baSMatthew Dillon swap = (u_int16_t *)id->model; 6821ac8d5baSMatthew Dillon for (i = 0; i < sizeof(id->model) / sizeof(u_int16_t); i++) 6831ac8d5baSMatthew Dillon swap[i] = bswap16(swap[i]); 6841ac8d5baSMatthew Dillon } 6851ac8d5baSMatthew Dillon 6861ac8d5baSMatthew Dillon /* 6871ac8d5baSMatthew Dillon * Dummy done callback for xa. 6881ac8d5baSMatthew Dillon */ 6891ac8d5baSMatthew Dillon static void 6901ac8d5baSMatthew Dillon sili_ata_dummy_done(struct ata_xfer *xa) 6911ac8d5baSMatthew Dillon { 6921ac8d5baSMatthew Dillon } 6931ac8d5baSMatthew Dillon 6941ac8d5baSMatthew Dillon /* 6951ac8d5baSMatthew Dillon * Use an engineering request to initiate a target scan for devices 6961ac8d5baSMatthew Dillon * behind a port multiplier. 6971ac8d5baSMatthew Dillon * 6981ac8d5baSMatthew Dillon * An asynchronous bus scan is used to avoid reentrancy issues. 6991ac8d5baSMatthew Dillon */ 7001ac8d5baSMatthew Dillon static void 7011ac8d5baSMatthew Dillon sili_cam_rescan_callback(struct cam_periph *periph, union ccb *ccb) 7021ac8d5baSMatthew Dillon { 7031ac8d5baSMatthew Dillon struct sili_port *ap = ccb->ccb_h.sim_priv.entries[0].ptr; 7041ac8d5baSMatthew Dillon 7051ac8d5baSMatthew Dillon if (ccb->ccb_h.func_code == XPT_SCAN_BUS) { 7061ac8d5baSMatthew Dillon ap->ap_flags &= ~AP_F_SCAN_RUNNING; 7071ac8d5baSMatthew Dillon if (ap->ap_flags & AP_F_SCAN_REQUESTED) { 7081ac8d5baSMatthew Dillon ap->ap_flags &= ~AP_F_SCAN_REQUESTED; 7091ac8d5baSMatthew Dillon sili_cam_rescan(ap); 7101ac8d5baSMatthew Dillon } 7111ac8d5baSMatthew Dillon ap->ap_flags |= AP_F_SCAN_COMPLETED; 7121ac8d5baSMatthew Dillon wakeup(&ap->ap_flags); 7131ac8d5baSMatthew Dillon } 7141ac8d5baSMatthew Dillon xpt_free_ccb(ccb); 7151ac8d5baSMatthew Dillon } 7161ac8d5baSMatthew Dillon 7171ac8d5baSMatthew Dillon static void 7181ac8d5baSMatthew Dillon sili_cam_rescan(struct sili_port *ap) 7191ac8d5baSMatthew Dillon { 7201ac8d5baSMatthew Dillon struct cam_path *path; 7211ac8d5baSMatthew Dillon union ccb *ccb; 7221ac8d5baSMatthew Dillon int status; 7231ac8d5baSMatthew Dillon int i; 7241ac8d5baSMatthew Dillon 7251ac8d5baSMatthew Dillon if (ap->ap_flags & AP_F_SCAN_RUNNING) { 7261ac8d5baSMatthew Dillon ap->ap_flags |= AP_F_SCAN_REQUESTED; 7271ac8d5baSMatthew Dillon return; 7281ac8d5baSMatthew Dillon } 7291ac8d5baSMatthew Dillon ap->ap_flags |= AP_F_SCAN_RUNNING; 7301ac8d5baSMatthew Dillon for (i = 0; i < SILI_MAX_PMPORTS; ++i) { 7311ac8d5baSMatthew Dillon ap->ap_ata[i].at_features |= ATA_PORT_F_RESCAN; 7321ac8d5baSMatthew Dillon } 7331ac8d5baSMatthew Dillon 7341ac8d5baSMatthew Dillon status = xpt_create_path(&path, xpt_periph, cam_sim_path(ap->ap_sim), 7351ac8d5baSMatthew Dillon CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD); 7361ac8d5baSMatthew Dillon if (status != CAM_REQ_CMP) 7371ac8d5baSMatthew Dillon return; 7381ac8d5baSMatthew Dillon 7391ac8d5baSMatthew Dillon ccb = xpt_alloc_ccb(); 7401ac8d5baSMatthew Dillon xpt_setup_ccb(&ccb->ccb_h, path, 5); /* 5 = low priority */ 7411ac8d5baSMatthew Dillon ccb->ccb_h.func_code = XPT_ENG_EXEC; 7421ac8d5baSMatthew Dillon ccb->ccb_h.cbfcnp = sili_cam_rescan_callback; 7431ac8d5baSMatthew Dillon ccb->ccb_h.sim_priv.entries[0].ptr = ap; 7441ac8d5baSMatthew Dillon ccb->crcn.flags = CAM_FLAG_NONE; 7451ac8d5baSMatthew Dillon xpt_action_async(ccb); 7461ac8d5baSMatthew Dillon } 7471ac8d5baSMatthew Dillon 7481ac8d5baSMatthew Dillon static void 7491ac8d5baSMatthew Dillon sili_xpt_rescan(struct sili_port *ap) 7501ac8d5baSMatthew Dillon { 7511ac8d5baSMatthew Dillon struct cam_path *path; 7521ac8d5baSMatthew Dillon union ccb *ccb; 7531ac8d5baSMatthew Dillon int status; 7541ac8d5baSMatthew Dillon 7551ac8d5baSMatthew Dillon status = xpt_create_path(&path, xpt_periph, cam_sim_path(ap->ap_sim), 7561ac8d5baSMatthew Dillon CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD); 7571ac8d5baSMatthew Dillon if (status != CAM_REQ_CMP) 7581ac8d5baSMatthew Dillon return; 7591ac8d5baSMatthew Dillon 7601ac8d5baSMatthew Dillon ccb = xpt_alloc_ccb(); 7611ac8d5baSMatthew Dillon xpt_setup_ccb(&ccb->ccb_h, path, 5); /* 5 = low priority */ 7621ac8d5baSMatthew Dillon ccb->ccb_h.func_code = XPT_SCAN_BUS; 7631ac8d5baSMatthew Dillon ccb->ccb_h.cbfcnp = sili_cam_rescan_callback; 7641ac8d5baSMatthew Dillon ccb->ccb_h.sim_priv.entries[0].ptr = ap; 7651ac8d5baSMatthew Dillon ccb->crcn.flags = CAM_FLAG_NONE; 7661ac8d5baSMatthew Dillon xpt_action_async(ccb); 7671ac8d5baSMatthew Dillon } 7681ac8d5baSMatthew Dillon 7691ac8d5baSMatthew Dillon /* 7701ac8d5baSMatthew Dillon * Action function - dispatch command 7711ac8d5baSMatthew Dillon */ 7721ac8d5baSMatthew Dillon static 7731ac8d5baSMatthew Dillon void 7741ac8d5baSMatthew Dillon sili_xpt_action(struct cam_sim *sim, union ccb *ccb) 7751ac8d5baSMatthew Dillon { 7761ac8d5baSMatthew Dillon struct sili_port *ap; 7771ac8d5baSMatthew Dillon struct ata_port *at, *atx; 7781ac8d5baSMatthew Dillon struct ccb_hdr *ccbh; 7791ac8d5baSMatthew Dillon int unit; 7801ac8d5baSMatthew Dillon 7811ac8d5baSMatthew Dillon /* XXX lock */ 7821ac8d5baSMatthew Dillon ap = cam_sim_softc(sim); 7831ac8d5baSMatthew Dillon at = ap->ap_ata; 7841ac8d5baSMatthew Dillon atx = NULL; 7851ac8d5baSMatthew Dillon KKASSERT(ap != NULL); 7861ac8d5baSMatthew Dillon ccbh = &ccb->ccb_h; 7871ac8d5baSMatthew Dillon unit = cam_sim_unit(sim); 7881ac8d5baSMatthew Dillon 7891ac8d5baSMatthew Dillon /* 7901ac8d5baSMatthew Dillon * Early failure checks. These checks do not apply to XPT_PATH_INQ, 7911ac8d5baSMatthew Dillon * otherwise the bus rescan will not remove the dead devices when 7921ac8d5baSMatthew Dillon * unplugging a PM. 7931ac8d5baSMatthew Dillon * 7941ac8d5baSMatthew Dillon * For non-wildcards we have one target (0) and one lun (0), 7951ac8d5baSMatthew Dillon * unless we have a port multiplier. 7961ac8d5baSMatthew Dillon * 7971ac8d5baSMatthew Dillon * A wildcard target indicates only the general bus is being 7981ac8d5baSMatthew Dillon * probed. 7991ac8d5baSMatthew Dillon * 8001ac8d5baSMatthew Dillon * Calculate at and atx. at is always non-NULL. atx is only 8011ac8d5baSMatthew Dillon * non-NULL for direct-attached devices. It will be NULL for 8021ac8d5baSMatthew Dillon * devices behind a port multiplier. 8031ac8d5baSMatthew Dillon * 8041ac8d5baSMatthew Dillon * XXX What do we do with a LUN wildcard? 8051ac8d5baSMatthew Dillon */ 8061ac8d5baSMatthew Dillon if (ccbh->target_id != CAM_TARGET_WILDCARD && 8071ac8d5baSMatthew Dillon ccbh->func_code != XPT_PATH_INQ) { 8081ac8d5baSMatthew Dillon if (ap->ap_type == ATA_PORT_T_NONE) { 8091ac8d5baSMatthew Dillon ccbh->status = CAM_DEV_NOT_THERE; 8101ac8d5baSMatthew Dillon xpt_done(ccb); 8111ac8d5baSMatthew Dillon return; 8121ac8d5baSMatthew Dillon } 8131ac8d5baSMatthew Dillon if (ccbh->target_id < 0 || ccbh->target_id >= ap->ap_pmcount) { 8141ac8d5baSMatthew Dillon ccbh->status = CAM_DEV_NOT_THERE; 8151ac8d5baSMatthew Dillon xpt_done(ccb); 8161ac8d5baSMatthew Dillon return; 8171ac8d5baSMatthew Dillon } 8181ac8d5baSMatthew Dillon at += ccbh->target_id; 8191ac8d5baSMatthew Dillon if (ap->ap_type == ATA_PORT_T_PM) 8201ac8d5baSMatthew Dillon atx = at; 8211ac8d5baSMatthew Dillon 8221ac8d5baSMatthew Dillon if (ccbh->target_lun != CAM_LUN_WILDCARD && ccbh->target_lun) { 8231ac8d5baSMatthew Dillon ccbh->status = CAM_DEV_NOT_THERE; 8241ac8d5baSMatthew Dillon xpt_done(ccb); 8251ac8d5baSMatthew Dillon return; 8261ac8d5baSMatthew Dillon } 8271ac8d5baSMatthew Dillon } 8281ac8d5baSMatthew Dillon 8291ac8d5baSMatthew Dillon /* 8301ac8d5baSMatthew Dillon * Switch on the meta XPT command 8311ac8d5baSMatthew Dillon */ 8321ac8d5baSMatthew Dillon switch(ccbh->func_code) { 8331ac8d5baSMatthew Dillon case XPT_ENG_EXEC: 8341ac8d5baSMatthew Dillon /* 8351ac8d5baSMatthew Dillon * This routine is called after a port multiplier has been 8361ac8d5baSMatthew Dillon * probed. 8371ac8d5baSMatthew Dillon */ 8381ac8d5baSMatthew Dillon ccbh->status = CAM_REQ_CMP; 8391ac8d5baSMatthew Dillon sili_os_lock_port(ap); 8401ac8d5baSMatthew Dillon sili_port_state_machine(ap, 0); 8411ac8d5baSMatthew Dillon sili_os_unlock_port(ap); 8421ac8d5baSMatthew Dillon xpt_done(ccb); 8431ac8d5baSMatthew Dillon sili_xpt_rescan(ap); 8441ac8d5baSMatthew Dillon break; 8451ac8d5baSMatthew Dillon case XPT_PATH_INQ: 8461ac8d5baSMatthew Dillon /* 8471ac8d5baSMatthew Dillon * This command always succeeds, otherwise the bus scan 8481ac8d5baSMatthew Dillon * will not detach dead devices. 8491ac8d5baSMatthew Dillon */ 8501ac8d5baSMatthew Dillon ccb->cpi.version_num = 1; 8511ac8d5baSMatthew Dillon ccb->cpi.hba_inquiry = 0; 8521ac8d5baSMatthew Dillon ccb->cpi.target_sprt = 0; 8531ac8d5baSMatthew Dillon ccb->cpi.hba_misc = PIM_SEQSCAN; 8541ac8d5baSMatthew Dillon ccb->cpi.hba_eng_cnt = 0; 8551ac8d5baSMatthew Dillon bzero(ccb->cpi.vuhba_flags, sizeof(ccb->cpi.vuhba_flags)); 8561ac8d5baSMatthew Dillon ccb->cpi.max_target = SILI_MAX_PMPORTS - 1; 8571ac8d5baSMatthew Dillon ccb->cpi.max_lun = 0; 8581ac8d5baSMatthew Dillon ccb->cpi.async_flags = 0; 8591ac8d5baSMatthew Dillon ccb->cpi.hpath_id = 0; 8601ac8d5baSMatthew Dillon ccb->cpi.initiator_id = SILI_MAX_PMPORTS - 1; 8611ac8d5baSMatthew Dillon ccb->cpi.unit_number = cam_sim_unit(sim); 8621ac8d5baSMatthew Dillon ccb->cpi.bus_id = cam_sim_bus(sim); 8631ac8d5baSMatthew Dillon ccb->cpi.base_transfer_speed = 150000; 8641ac8d5baSMatthew Dillon ccb->cpi.transport = XPORT_SATA; 8651ac8d5baSMatthew Dillon ccb->cpi.transport_version = 1; 8661ac8d5baSMatthew Dillon ccb->cpi.protocol = PROTO_SCSI; 8671ac8d5baSMatthew Dillon ccb->cpi.protocol_version = SCSI_REV_2; 8681ac8d5baSMatthew Dillon 8691ac8d5baSMatthew Dillon ccbh->status = CAM_REQ_CMP; 8701ac8d5baSMatthew Dillon if (ccbh->target_id == CAM_TARGET_WILDCARD) { 8711ac8d5baSMatthew Dillon sili_os_lock_port(ap); 8721ac8d5baSMatthew Dillon sili_port_state_machine(ap, 0); 8731ac8d5baSMatthew Dillon sili_os_unlock_port(ap); 8741ac8d5baSMatthew Dillon } else { 8751ac8d5baSMatthew Dillon switch(sili_pread(ap, SILI_PREG_SSTS) & 8761ac8d5baSMatthew Dillon SILI_PREG_SSTS_SPD) { 8771ac8d5baSMatthew Dillon case SILI_PREG_SSTS_SPD_GEN1: 8781ac8d5baSMatthew Dillon ccb->cpi.base_transfer_speed = 150000; 8791ac8d5baSMatthew Dillon break; 8801ac8d5baSMatthew Dillon case SILI_PREG_SSTS_SPD_GEN2: 8811ac8d5baSMatthew Dillon ccb->cpi.base_transfer_speed = 300000; 8821ac8d5baSMatthew Dillon break; 8831ac8d5baSMatthew Dillon default: 8841ac8d5baSMatthew Dillon /* unknown */ 8851ac8d5baSMatthew Dillon ccb->cpi.base_transfer_speed = 1000; 8861ac8d5baSMatthew Dillon break; 8871ac8d5baSMatthew Dillon } 8881ac8d5baSMatthew Dillon #if 0 8891ac8d5baSMatthew Dillon if (ap->ap_type == ATA_PORT_T_NONE) 8901ac8d5baSMatthew Dillon ccbh->status = CAM_DEV_NOT_THERE; 8911ac8d5baSMatthew Dillon #endif 8921ac8d5baSMatthew Dillon } 8931ac8d5baSMatthew Dillon xpt_done(ccb); 8941ac8d5baSMatthew Dillon break; 8951ac8d5baSMatthew Dillon case XPT_RESET_DEV: 8961ac8d5baSMatthew Dillon sili_os_lock_port(ap); 8971ac8d5baSMatthew Dillon if (ap->ap_type == ATA_PORT_T_NONE) { 8981ac8d5baSMatthew Dillon ccbh->status = CAM_DEV_NOT_THERE; 8991ac8d5baSMatthew Dillon } else { 9001ac8d5baSMatthew Dillon sili_port_reset(ap, atx, 0); 9011ac8d5baSMatthew Dillon ccbh->status = CAM_REQ_CMP; 9021ac8d5baSMatthew Dillon } 9031ac8d5baSMatthew Dillon sili_os_unlock_port(ap); 9041ac8d5baSMatthew Dillon xpt_done(ccb); 9051ac8d5baSMatthew Dillon break; 9061ac8d5baSMatthew Dillon case XPT_RESET_BUS: 9071ac8d5baSMatthew Dillon sili_os_lock_port(ap); 9081ac8d5baSMatthew Dillon sili_port_reset(ap, NULL, 1); 9091ac8d5baSMatthew Dillon sili_os_unlock_port(ap); 9101ac8d5baSMatthew Dillon ccbh->status = CAM_REQ_CMP; 9111ac8d5baSMatthew Dillon xpt_done(ccb); 9121ac8d5baSMatthew Dillon break; 9131ac8d5baSMatthew Dillon case XPT_SET_TRAN_SETTINGS: 9141ac8d5baSMatthew Dillon ccbh->status = CAM_FUNC_NOTAVAIL; 9151ac8d5baSMatthew Dillon xpt_done(ccb); 9161ac8d5baSMatthew Dillon break; 9171ac8d5baSMatthew Dillon case XPT_GET_TRAN_SETTINGS: 9181ac8d5baSMatthew Dillon ccb->cts.protocol = PROTO_SCSI; 9191ac8d5baSMatthew Dillon ccb->cts.protocol_version = SCSI_REV_2; 9201ac8d5baSMatthew Dillon ccb->cts.transport = XPORT_SATA; 9211ac8d5baSMatthew Dillon ccb->cts.transport_version = XPORT_VERSION_UNSPECIFIED; 9221ac8d5baSMatthew Dillon ccb->cts.proto_specific.valid = 0; 9231ac8d5baSMatthew Dillon ccb->cts.xport_specific.valid = 0; 9241ac8d5baSMatthew Dillon ccbh->status = CAM_REQ_CMP; 9251ac8d5baSMatthew Dillon xpt_done(ccb); 9261ac8d5baSMatthew Dillon break; 9271ac8d5baSMatthew Dillon case XPT_CALC_GEOMETRY: 9281ac8d5baSMatthew Dillon cam_calc_geometry(&ccb->ccg, 1); 9291ac8d5baSMatthew Dillon xpt_done(ccb); 9301ac8d5baSMatthew Dillon break; 9311ac8d5baSMatthew Dillon case XPT_SCSI_IO: 9321ac8d5baSMatthew Dillon /* 9331ac8d5baSMatthew Dillon * Our parallel startup code might have only probed through 9341ac8d5baSMatthew Dillon * to the IDENT, so do the last step if necessary. 9351ac8d5baSMatthew Dillon */ 9361ac8d5baSMatthew Dillon if (at->at_probe == ATA_PROBE_NEED_IDENT) 9371ac8d5baSMatthew Dillon sili_cam_probe(ap, atx); 9381ac8d5baSMatthew Dillon if (at->at_probe != ATA_PROBE_GOOD) { 9391ac8d5baSMatthew Dillon ccbh->status = CAM_DEV_NOT_THERE; 9401ac8d5baSMatthew Dillon xpt_done(ccb); 9411ac8d5baSMatthew Dillon break; 9421ac8d5baSMatthew Dillon } 9431ac8d5baSMatthew Dillon switch(at->at_type) { 9441ac8d5baSMatthew Dillon case ATA_PORT_T_DISK: 9451ac8d5baSMatthew Dillon sili_xpt_scsi_disk_io(ap, atx, ccb); 9461ac8d5baSMatthew Dillon break; 9471ac8d5baSMatthew Dillon case ATA_PORT_T_ATAPI: 9481ac8d5baSMatthew Dillon sili_xpt_scsi_atapi_io(ap, atx, ccb); 9491ac8d5baSMatthew Dillon break; 9501ac8d5baSMatthew Dillon default: 9511ac8d5baSMatthew Dillon ccbh->status = CAM_REQ_INVALID; 9521ac8d5baSMatthew Dillon xpt_done(ccb); 9531ac8d5baSMatthew Dillon break; 9541ac8d5baSMatthew Dillon } 9551ac8d5baSMatthew Dillon break; 9561ac8d5baSMatthew Dillon default: 9571ac8d5baSMatthew Dillon ccbh->status = CAM_REQ_INVALID; 9581ac8d5baSMatthew Dillon xpt_done(ccb); 9591ac8d5baSMatthew Dillon break; 9601ac8d5baSMatthew Dillon } 9611ac8d5baSMatthew Dillon } 9621ac8d5baSMatthew Dillon 9631ac8d5baSMatthew Dillon /* 9641ac8d5baSMatthew Dillon * Poll function. 9651ac8d5baSMatthew Dillon * 9661ac8d5baSMatthew Dillon * Generally this function gets called heavily when interrupts might be 9671ac8d5baSMatthew Dillon * non-operational, during a halt/reboot or panic. 9681ac8d5baSMatthew Dillon */ 9691ac8d5baSMatthew Dillon static 9701ac8d5baSMatthew Dillon void 9711ac8d5baSMatthew Dillon sili_xpt_poll(struct cam_sim *sim) 9721ac8d5baSMatthew Dillon { 9731ac8d5baSMatthew Dillon struct sili_port *ap; 9741ac8d5baSMatthew Dillon 9751ac8d5baSMatthew Dillon ap = cam_sim_softc(sim); 9761ac8d5baSMatthew Dillon crit_enter(); 9771ac8d5baSMatthew Dillon sili_os_lock_port(ap); 9781ac8d5baSMatthew Dillon sili_port_intr(ap, 1); 9791ac8d5baSMatthew Dillon sili_os_unlock_port(ap); 9801ac8d5baSMatthew Dillon crit_exit(); 9811ac8d5baSMatthew Dillon } 9821ac8d5baSMatthew Dillon 9831ac8d5baSMatthew Dillon /* 9841ac8d5baSMatthew Dillon * Convert the SCSI command in ccb to an ata_xfer command in xa 9851ac8d5baSMatthew Dillon * for ATA_PORT_T_DISK operations. Set the completion function 9861ac8d5baSMatthew Dillon * to convert the response back, then dispatch to the OpenBSD SILI 9871ac8d5baSMatthew Dillon * layer. 9881ac8d5baSMatthew Dillon * 9891ac8d5baSMatthew Dillon * SILI DISK commands only support a limited command set, and we 9901ac8d5baSMatthew Dillon * fake additional commands to make it play nice with the CAM subsystem. 9911ac8d5baSMatthew Dillon */ 9921ac8d5baSMatthew Dillon static 9931ac8d5baSMatthew Dillon void 9941ac8d5baSMatthew Dillon sili_xpt_scsi_disk_io(struct sili_port *ap, struct ata_port *atx, 9951ac8d5baSMatthew Dillon union ccb *ccb) 9961ac8d5baSMatthew Dillon { 9971ac8d5baSMatthew Dillon struct ccb_hdr *ccbh; 9981ac8d5baSMatthew Dillon struct ccb_scsiio *csio; 9991ac8d5baSMatthew Dillon struct ata_xfer *xa; 10001ac8d5baSMatthew Dillon struct ata_port *at; 10011ac8d5baSMatthew Dillon struct ata_fis_h2d *fis; 100275046a24SMatthew Dillon struct ata_pass_12 *atp12; 100375046a24SMatthew Dillon struct ata_pass_16 *atp16; 10041ac8d5baSMatthew Dillon scsi_cdb_t cdb; 10051ac8d5baSMatthew Dillon union scsi_data *rdata; 10061ac8d5baSMatthew Dillon int rdata_len; 10071ac8d5baSMatthew Dillon u_int64_t capacity; 10081ac8d5baSMatthew Dillon u_int64_t lba; 10091ac8d5baSMatthew Dillon u_int32_t count; 10101ac8d5baSMatthew Dillon 10111ac8d5baSMatthew Dillon ccbh = &ccb->csio.ccb_h; 10121ac8d5baSMatthew Dillon csio = &ccb->csio; 10131ac8d5baSMatthew Dillon at = atx ? atx : &ap->ap_ata[0]; 10141ac8d5baSMatthew Dillon 10151ac8d5baSMatthew Dillon /* 10161ac8d5baSMatthew Dillon * XXX not passing NULL at for direct attach! 10171ac8d5baSMatthew Dillon */ 10181ac8d5baSMatthew Dillon xa = sili_ata_get_xfer(ap, atx); 10191ac8d5baSMatthew Dillon rdata = (void *)csio->data_ptr; 10201ac8d5baSMatthew Dillon rdata_len = csio->dxfer_len; 10211ac8d5baSMatthew Dillon 10221ac8d5baSMatthew Dillon /* 10231ac8d5baSMatthew Dillon * Build the FIS or process the csio to completion. 10241ac8d5baSMatthew Dillon */ 10251ac8d5baSMatthew Dillon cdb = (void *)((ccbh->flags & CAM_CDB_POINTER) ? 10261ac8d5baSMatthew Dillon csio->cdb_io.cdb_ptr : csio->cdb_io.cdb_bytes); 10271ac8d5baSMatthew Dillon 10281ac8d5baSMatthew Dillon switch(cdb->generic.opcode) { 10291ac8d5baSMatthew Dillon case REQUEST_SENSE: 10301ac8d5baSMatthew Dillon /* 10311ac8d5baSMatthew Dillon * Auto-sense everything, so explicit sense requests 10321ac8d5baSMatthew Dillon * return no-sense. 10331ac8d5baSMatthew Dillon */ 10341ac8d5baSMatthew Dillon ccbh->status = CAM_SCSI_STATUS_ERROR; 10351ac8d5baSMatthew Dillon break; 10361ac8d5baSMatthew Dillon case INQUIRY: 10371ac8d5baSMatthew Dillon /* 10381ac8d5baSMatthew Dillon * Inquiry supported features 10391ac8d5baSMatthew Dillon * 10401ac8d5baSMatthew Dillon * [opcode, byte2, page_code, length, control] 10411ac8d5baSMatthew Dillon */ 10421ac8d5baSMatthew Dillon if (cdb->inquiry.byte2 & SI_EVPD) { 10433ac61d78SMatthew Dillon sili_xpt_page_inquiry(ap, at, ccb); 10441ac8d5baSMatthew Dillon } else { 10451ac8d5baSMatthew Dillon bzero(rdata, rdata_len); 10461ac8d5baSMatthew Dillon if (rdata_len < SHORT_INQUIRY_LENGTH) { 10471ac8d5baSMatthew Dillon ccbh->status = CAM_CCB_LEN_ERR; 10481ac8d5baSMatthew Dillon break; 10491ac8d5baSMatthew Dillon } 10501ac8d5baSMatthew Dillon if (rdata_len > sizeof(rdata->inquiry_data)) 10511ac8d5baSMatthew Dillon rdata_len = sizeof(rdata->inquiry_data); 10521ac8d5baSMatthew Dillon rdata->inquiry_data.device = T_DIRECT; 10531ac8d5baSMatthew Dillon rdata->inquiry_data.version = SCSI_REV_SPC2; 10541ac8d5baSMatthew Dillon rdata->inquiry_data.response_format = 2; 10551ac8d5baSMatthew Dillon rdata->inquiry_data.additional_length = 32; 10561ac8d5baSMatthew Dillon bcopy("SATA ", rdata->inquiry_data.vendor, 8); 10571ac8d5baSMatthew Dillon bcopy(at->at_identify.model, 10581ac8d5baSMatthew Dillon rdata->inquiry_data.product, 10591ac8d5baSMatthew Dillon sizeof(rdata->inquiry_data.product)); 10601ac8d5baSMatthew Dillon bcopy(at->at_identify.firmware, 10611ac8d5baSMatthew Dillon rdata->inquiry_data.revision, 10621ac8d5baSMatthew Dillon sizeof(rdata->inquiry_data.revision)); 10631ac8d5baSMatthew Dillon ccbh->status = CAM_REQ_CMP; 10641ac8d5baSMatthew Dillon } 10651ac8d5baSMatthew Dillon break; 10661ac8d5baSMatthew Dillon case READ_CAPACITY_16: 10671ac8d5baSMatthew Dillon if (cdb->read_capacity_16.service_action != SRC16_SERVICE_ACTION) { 10681ac8d5baSMatthew Dillon ccbh->status = CAM_REQ_INVALID; 10691ac8d5baSMatthew Dillon break; 10701ac8d5baSMatthew Dillon } 10711ac8d5baSMatthew Dillon if (rdata_len < sizeof(rdata->read_capacity_data_16)) { 10721ac8d5baSMatthew Dillon ccbh->status = CAM_CCB_LEN_ERR; 10731ac8d5baSMatthew Dillon break; 10741ac8d5baSMatthew Dillon } 10751ac8d5baSMatthew Dillon /* fall through */ 10761ac8d5baSMatthew Dillon case READ_CAPACITY: 10771ac8d5baSMatthew Dillon if (rdata_len < sizeof(rdata->read_capacity_data)) { 10781ac8d5baSMatthew Dillon ccbh->status = CAM_CCB_LEN_ERR; 10791ac8d5baSMatthew Dillon break; 10801ac8d5baSMatthew Dillon } 10811ac8d5baSMatthew Dillon 10821ac8d5baSMatthew Dillon capacity = at->at_capacity; 10831ac8d5baSMatthew Dillon 10841ac8d5baSMatthew Dillon bzero(rdata, rdata_len); 10851ac8d5baSMatthew Dillon if (cdb->generic.opcode == READ_CAPACITY) { 10861ac8d5baSMatthew Dillon rdata_len = sizeof(rdata->read_capacity_data); 10871ac8d5baSMatthew Dillon if (capacity > 0xFFFFFFFFU) 10881ac8d5baSMatthew Dillon capacity = 0xFFFFFFFFU; 10891ac8d5baSMatthew Dillon bzero(&rdata->read_capacity_data, rdata_len); 10901ac8d5baSMatthew Dillon scsi_ulto4b((u_int32_t)capacity - 1, 10911ac8d5baSMatthew Dillon rdata->read_capacity_data.addr); 10921ac8d5baSMatthew Dillon scsi_ulto4b(512, rdata->read_capacity_data.length); 10931ac8d5baSMatthew Dillon } else { 10941ac8d5baSMatthew Dillon rdata_len = sizeof(rdata->read_capacity_data_16); 10951ac8d5baSMatthew Dillon bzero(&rdata->read_capacity_data_16, rdata_len); 10961ac8d5baSMatthew Dillon scsi_u64to8b(capacity - 1, 10971ac8d5baSMatthew Dillon rdata->read_capacity_data_16.addr); 10981ac8d5baSMatthew Dillon scsi_ulto4b(512, rdata->read_capacity_data_16.length); 10991ac8d5baSMatthew Dillon } 11001ac8d5baSMatthew Dillon ccbh->status = CAM_REQ_CMP; 11011ac8d5baSMatthew Dillon break; 11021ac8d5baSMatthew Dillon case SYNCHRONIZE_CACHE: 11031ac8d5baSMatthew Dillon /* 11041ac8d5baSMatthew Dillon * Synchronize cache. Specification says this can take 11051ac8d5baSMatthew Dillon * greater then 30 seconds so give it at least 45. 11061ac8d5baSMatthew Dillon */ 11071ac8d5baSMatthew Dillon fis = xa->fis; 11081ac8d5baSMatthew Dillon fis->flags = ATA_H2D_FLAGS_CMD; 11091ac8d5baSMatthew Dillon fis->command = ATA_C_FLUSH_CACHE; 11101ac8d5baSMatthew Dillon fis->device = 0; 11111ac8d5baSMatthew Dillon if (xa->timeout < 45000) 11121ac8d5baSMatthew Dillon xa->timeout = 45000; 11131ac8d5baSMatthew Dillon xa->datalen = 0; 11141ac8d5baSMatthew Dillon xa->flags = ATA_F_READ; 11151ac8d5baSMatthew Dillon xa->complete = sili_ata_complete_disk_synchronize_cache; 11161ac8d5baSMatthew Dillon break; 11171ac8d5baSMatthew Dillon case TEST_UNIT_READY: 11181ac8d5baSMatthew Dillon case START_STOP_UNIT: 11191ac8d5baSMatthew Dillon case PREVENT_ALLOW: 11201ac8d5baSMatthew Dillon /* 11211ac8d5baSMatthew Dillon * Just silently return success 11221ac8d5baSMatthew Dillon */ 11231ac8d5baSMatthew Dillon ccbh->status = CAM_REQ_CMP; 11241ac8d5baSMatthew Dillon rdata_len = 0; 11251ac8d5baSMatthew Dillon break; 11261ac8d5baSMatthew Dillon case ATA_PASS_12: 112775046a24SMatthew Dillon atp12 = &cdb->ata_pass_12; 112875046a24SMatthew Dillon fis = xa->fis; 11291ac8d5baSMatthew Dillon /* 113075046a24SMatthew Dillon * Figure out the flags to be used, depending on the 113175046a24SMatthew Dillon * direction of the CAM request. 11321ac8d5baSMatthew Dillon */ 113375046a24SMatthew Dillon switch (ccbh->flags & CAM_DIR_MASK) { 113475046a24SMatthew Dillon case CAM_DIR_IN: 113575046a24SMatthew Dillon xa->flags = ATA_F_READ; 113675046a24SMatthew Dillon break; 113775046a24SMatthew Dillon case CAM_DIR_OUT: 113875046a24SMatthew Dillon xa->flags = ATA_F_WRITE; 113975046a24SMatthew Dillon break; 114075046a24SMatthew Dillon default: 114175046a24SMatthew Dillon xa->flags = 0; 114275046a24SMatthew Dillon } 1143*22726f69SMatthew Dillon xa->flags |= ATA_F_POLL | ATA_F_EXCLUSIVE; 114475046a24SMatthew Dillon xa->data = csio->data_ptr; 114575046a24SMatthew Dillon xa->datalen = csio->dxfer_len; 114675046a24SMatthew Dillon xa->complete = sili_ata_complete_disk_rw; 114775046a24SMatthew Dillon xa->timeout = ccbh->timeout; 114875046a24SMatthew Dillon 114975046a24SMatthew Dillon /* 115075046a24SMatthew Dillon * Populate the fis from the information we received through CAM 115175046a24SMatthew Dillon * ATA passthrough. 115275046a24SMatthew Dillon */ 115375046a24SMatthew Dillon fis->flags = ATA_H2D_FLAGS_CMD; /* maybe also atp12->flags ? */ 115475046a24SMatthew Dillon fis->features = atp12->features; 115575046a24SMatthew Dillon fis->sector_count = atp12->sector_count; 115675046a24SMatthew Dillon fis->lba_low = atp12->lba_low; 115775046a24SMatthew Dillon fis->lba_mid = atp12->lba_mid; 115875046a24SMatthew Dillon fis->lba_high = atp12->lba_high; 115975046a24SMatthew Dillon fis->device = atp12->device; /* maybe always 0? */ 116075046a24SMatthew Dillon fis->command = atp12->command; 116175046a24SMatthew Dillon fis->control = atp12->control; 116275046a24SMatthew Dillon 116375046a24SMatthew Dillon /* 116475046a24SMatthew Dillon * Mark as in progress so it is sent to the device. 116575046a24SMatthew Dillon */ 116675046a24SMatthew Dillon ccbh->status = CAM_REQ_INPROG; 116775046a24SMatthew Dillon break; 116875046a24SMatthew Dillon case ATA_PASS_16: 116975046a24SMatthew Dillon atp16 = &cdb->ata_pass_16; 117075046a24SMatthew Dillon fis = xa->fis; 117175046a24SMatthew Dillon /* 117275046a24SMatthew Dillon * Figure out the flags to be used, depending on the direction of the 117375046a24SMatthew Dillon * CAM request. 117475046a24SMatthew Dillon */ 117575046a24SMatthew Dillon switch (ccbh->flags & CAM_DIR_MASK) { 117675046a24SMatthew Dillon case CAM_DIR_IN: 117775046a24SMatthew Dillon xa->flags = ATA_F_READ; 117875046a24SMatthew Dillon break; 117975046a24SMatthew Dillon case CAM_DIR_OUT: 118075046a24SMatthew Dillon xa->flags = ATA_F_WRITE; 118175046a24SMatthew Dillon break; 118275046a24SMatthew Dillon default: 118375046a24SMatthew Dillon xa->flags = 0; 118475046a24SMatthew Dillon } 1185*22726f69SMatthew Dillon xa->flags |= ATA_F_POLL | ATA_F_EXCLUSIVE; 118675046a24SMatthew Dillon xa->data = csio->data_ptr; 118775046a24SMatthew Dillon xa->datalen = csio->dxfer_len; 118875046a24SMatthew Dillon xa->complete = sili_ata_complete_disk_rw; 118975046a24SMatthew Dillon xa->timeout = ccbh->timeout; 119075046a24SMatthew Dillon 119175046a24SMatthew Dillon /* 119275046a24SMatthew Dillon * Populate the fis from the information we received through CAM 119375046a24SMatthew Dillon * ATA passthrough. 119475046a24SMatthew Dillon */ 119575046a24SMatthew Dillon fis->flags = ATA_H2D_FLAGS_CMD; /* maybe also atp16->flags ? */ 119675046a24SMatthew Dillon fis->features = atp16->features; 119775046a24SMatthew Dillon fis->features_exp = atp16->features_ext; 119875046a24SMatthew Dillon fis->sector_count = atp16->sector_count; 119975046a24SMatthew Dillon fis->sector_count_exp = atp16->sector_count_ext; 120075046a24SMatthew Dillon fis->lba_low = atp16->lba_low; 120175046a24SMatthew Dillon fis->lba_low_exp = atp16->lba_low_ext; 120275046a24SMatthew Dillon fis->lba_mid = atp16->lba_mid; 120375046a24SMatthew Dillon fis->lba_mid_exp = atp16->lba_mid_ext; 120475046a24SMatthew Dillon fis->lba_high = atp16->lba_high; 120575046a24SMatthew Dillon fis->lba_mid_exp = atp16->lba_mid_ext; 120675046a24SMatthew Dillon fis->device = atp16->device; /* maybe always 0? */ 120775046a24SMatthew Dillon fis->command = atp16->command; 120875046a24SMatthew Dillon 120975046a24SMatthew Dillon /* 121075046a24SMatthew Dillon * Mark as in progress so it is sent to the device. 121175046a24SMatthew Dillon */ 121275046a24SMatthew Dillon ccbh->status = CAM_REQ_INPROG; 12131ac8d5baSMatthew Dillon break; 12141ac8d5baSMatthew Dillon default: 12151ac8d5baSMatthew Dillon switch(cdb->generic.opcode) { 12161ac8d5baSMatthew Dillon case READ_6: 12171ac8d5baSMatthew Dillon lba = scsi_3btoul(cdb->rw_6.addr) & 0x1FFFFF; 12181ac8d5baSMatthew Dillon count = cdb->rw_6.length ? cdb->rw_6.length : 0x100; 12191ac8d5baSMatthew Dillon xa->flags = ATA_F_READ; 12201ac8d5baSMatthew Dillon break; 12211ac8d5baSMatthew Dillon case READ_10: 12221ac8d5baSMatthew Dillon lba = scsi_4btoul(cdb->rw_10.addr); 12231ac8d5baSMatthew Dillon count = scsi_2btoul(cdb->rw_10.length); 12241ac8d5baSMatthew Dillon xa->flags = ATA_F_READ; 12251ac8d5baSMatthew Dillon break; 12261ac8d5baSMatthew Dillon case READ_12: 12271ac8d5baSMatthew Dillon lba = scsi_4btoul(cdb->rw_12.addr); 12281ac8d5baSMatthew Dillon count = scsi_4btoul(cdb->rw_12.length); 12291ac8d5baSMatthew Dillon xa->flags = ATA_F_READ; 12301ac8d5baSMatthew Dillon break; 12311ac8d5baSMatthew Dillon case READ_16: 12321ac8d5baSMatthew Dillon lba = scsi_8btou64(cdb->rw_16.addr); 12331ac8d5baSMatthew Dillon count = scsi_4btoul(cdb->rw_16.length); 12341ac8d5baSMatthew Dillon xa->flags = ATA_F_READ; 12351ac8d5baSMatthew Dillon break; 12361ac8d5baSMatthew Dillon case WRITE_6: 12371ac8d5baSMatthew Dillon lba = scsi_3btoul(cdb->rw_6.addr) & 0x1FFFFF; 12381ac8d5baSMatthew Dillon count = cdb->rw_6.length ? cdb->rw_6.length : 0x100; 12391ac8d5baSMatthew Dillon xa->flags = ATA_F_WRITE; 12401ac8d5baSMatthew Dillon break; 12411ac8d5baSMatthew Dillon case WRITE_10: 12421ac8d5baSMatthew Dillon lba = scsi_4btoul(cdb->rw_10.addr); 12431ac8d5baSMatthew Dillon count = scsi_2btoul(cdb->rw_10.length); 12441ac8d5baSMatthew Dillon xa->flags = ATA_F_WRITE; 12451ac8d5baSMatthew Dillon break; 12461ac8d5baSMatthew Dillon case WRITE_12: 12471ac8d5baSMatthew Dillon lba = scsi_4btoul(cdb->rw_12.addr); 12481ac8d5baSMatthew Dillon count = scsi_4btoul(cdb->rw_12.length); 12491ac8d5baSMatthew Dillon xa->flags = ATA_F_WRITE; 12501ac8d5baSMatthew Dillon break; 12511ac8d5baSMatthew Dillon case WRITE_16: 12521ac8d5baSMatthew Dillon lba = scsi_8btou64(cdb->rw_16.addr); 12531ac8d5baSMatthew Dillon count = scsi_4btoul(cdb->rw_16.length); 12541ac8d5baSMatthew Dillon xa->flags = ATA_F_WRITE; 12551ac8d5baSMatthew Dillon break; 12561ac8d5baSMatthew Dillon default: 12571ac8d5baSMatthew Dillon ccbh->status = CAM_REQ_INVALID; 12581ac8d5baSMatthew Dillon break; 12591ac8d5baSMatthew Dillon } 12601ac8d5baSMatthew Dillon if (ccbh->status != CAM_REQ_INPROG) 12611ac8d5baSMatthew Dillon break; 12621ac8d5baSMatthew Dillon 12631ac8d5baSMatthew Dillon fis = xa->fis; 12641ac8d5baSMatthew Dillon fis->flags = ATA_H2D_FLAGS_CMD; 12651ac8d5baSMatthew Dillon fis->lba_low = (u_int8_t)lba; 12661ac8d5baSMatthew Dillon fis->lba_mid = (u_int8_t)(lba >> 8); 12671ac8d5baSMatthew Dillon fis->lba_high = (u_int8_t)(lba >> 16); 12681ac8d5baSMatthew Dillon fis->device = ATA_H2D_DEVICE_LBA; 12691ac8d5baSMatthew Dillon 12701ac8d5baSMatthew Dillon /* 12711ac8d5baSMatthew Dillon * NCQ only for direct-attached disks, do not currently 12721ac8d5baSMatthew Dillon * try to use NCQ with port multipliers. 12731ac8d5baSMatthew Dillon * 12741ac8d5baSMatthew Dillon * XXX fixme SII chip can do NCQ w/ port multipliers. 12751ac8d5baSMatthew Dillon */ 12761ac8d5baSMatthew Dillon if (at->at_ncqdepth > 1 && 12771ac8d5baSMatthew Dillon at->at_type == ATA_PORT_T_DISK && 12781ac8d5baSMatthew Dillon (ap->ap_sc->sc_flags & SILI_F_NCQ) && 12791ac8d5baSMatthew Dillon (ccbh->flags & CAM_POLLED) == 0) { 12801ac8d5baSMatthew Dillon /* 12811ac8d5baSMatthew Dillon * Use NCQ - always uses 48 bit addressing 12821ac8d5baSMatthew Dillon */ 12831ac8d5baSMatthew Dillon xa->flags |= ATA_F_NCQ; 12841ac8d5baSMatthew Dillon fis->command = (xa->flags & ATA_F_WRITE) ? 12851ac8d5baSMatthew Dillon ATA_C_WRITE_FPDMA : ATA_C_READ_FPDMA; 12861ac8d5baSMatthew Dillon fis->lba_low_exp = (u_int8_t)(lba >> 24); 12871ac8d5baSMatthew Dillon fis->lba_mid_exp = (u_int8_t)(lba >> 32); 12881ac8d5baSMatthew Dillon fis->lba_high_exp = (u_int8_t)(lba >> 40); 12891ac8d5baSMatthew Dillon fis->sector_count = xa->tag << 3; 12901ac8d5baSMatthew Dillon fis->features = (u_int8_t)count; 12911ac8d5baSMatthew Dillon fis->features_exp = (u_int8_t)(count >> 8); 1292b96fc23dSMatthew Dillon } else if (count > 0x100 || lba > 0x0FFFFFFFU) { 12931ac8d5baSMatthew Dillon /* 12941ac8d5baSMatthew Dillon * Use LBA48 12951ac8d5baSMatthew Dillon */ 12961ac8d5baSMatthew Dillon fis->command = (xa->flags & ATA_F_WRITE) ? 12971ac8d5baSMatthew Dillon ATA_C_WRITEDMA_EXT : ATA_C_READDMA_EXT; 12981ac8d5baSMatthew Dillon fis->lba_low_exp = (u_int8_t)(lba >> 24); 12991ac8d5baSMatthew Dillon fis->lba_mid_exp = (u_int8_t)(lba >> 32); 13001ac8d5baSMatthew Dillon fis->lba_high_exp = (u_int8_t)(lba >> 40); 13011ac8d5baSMatthew Dillon fis->sector_count = (u_int8_t)count; 13021ac8d5baSMatthew Dillon fis->sector_count_exp = (u_int8_t)(count >> 8); 13031ac8d5baSMatthew Dillon } else { 13041ac8d5baSMatthew Dillon /* 13051ac8d5baSMatthew Dillon * Use LBA 13061ac8d5baSMatthew Dillon * 13071ac8d5baSMatthew Dillon * NOTE: 256 sectors is supported, stored as 0. 13081ac8d5baSMatthew Dillon */ 13091ac8d5baSMatthew Dillon fis->command = (xa->flags & ATA_F_WRITE) ? 13101ac8d5baSMatthew Dillon ATA_C_WRITEDMA : ATA_C_READDMA; 13111ac8d5baSMatthew Dillon fis->device |= (u_int8_t)(lba >> 24) & 0x0F; 13121ac8d5baSMatthew Dillon fis->sector_count = (u_int8_t)count; 13131ac8d5baSMatthew Dillon } 13141ac8d5baSMatthew Dillon 13151ac8d5baSMatthew Dillon xa->data = csio->data_ptr; 13161ac8d5baSMatthew Dillon xa->datalen = csio->dxfer_len; 13171ac8d5baSMatthew Dillon xa->complete = sili_ata_complete_disk_rw; 13181ac8d5baSMatthew Dillon xa->timeout = ccbh->timeout; /* milliseconds */ 13191ac8d5baSMatthew Dillon if (ccbh->flags & CAM_POLLED) 13201ac8d5baSMatthew Dillon xa->flags |= ATA_F_POLL; 13211ac8d5baSMatthew Dillon break; 13221ac8d5baSMatthew Dillon } 13231ac8d5baSMatthew Dillon 13241ac8d5baSMatthew Dillon /* 13251ac8d5baSMatthew Dillon * If the request is still in progress the xa and FIS have 13263ac61d78SMatthew Dillon * been set up (except for the PM target), and must be dispatched. 13273ac61d78SMatthew Dillon * Otherwise the request was completed. 13281ac8d5baSMatthew Dillon */ 13291ac8d5baSMatthew Dillon if (ccbh->status == CAM_REQ_INPROG) { 13301ac8d5baSMatthew Dillon KKASSERT(xa->complete != NULL); 13311ac8d5baSMatthew Dillon xa->atascsi_private = ccb; 13321ac8d5baSMatthew Dillon ccb->ccb_h.sim_priv.entries[0].ptr = ap; 13331ac8d5baSMatthew Dillon sili_os_lock_port(ap); 13343ac61d78SMatthew Dillon xa->fis->flags |= at->at_target; 13351ac8d5baSMatthew Dillon sili_ata_cmd(xa); 13361ac8d5baSMatthew Dillon sili_os_unlock_port(ap); 13371ac8d5baSMatthew Dillon } else { 13381ac8d5baSMatthew Dillon sili_ata_put_xfer(xa); 13391ac8d5baSMatthew Dillon xpt_done(ccb); 13401ac8d5baSMatthew Dillon } 13411ac8d5baSMatthew Dillon } 13421ac8d5baSMatthew Dillon 13431ac8d5baSMatthew Dillon /* 13441ac8d5baSMatthew Dillon * Convert the SCSI command in ccb to an ata_xfer command in xa 13451ac8d5baSMatthew Dillon * for ATA_PORT_T_ATAPI operations. Set the completion function 13461ac8d5baSMatthew Dillon * to convert the response back, then dispatch to the OpenBSD SILI 13471ac8d5baSMatthew Dillon * layer. 13481ac8d5baSMatthew Dillon */ 13491ac8d5baSMatthew Dillon static 13501ac8d5baSMatthew Dillon void 13511ac8d5baSMatthew Dillon sili_xpt_scsi_atapi_io(struct sili_port *ap, struct ata_port *atx, 13521ac8d5baSMatthew Dillon union ccb *ccb) 13531ac8d5baSMatthew Dillon { 13541ac8d5baSMatthew Dillon struct ccb_hdr *ccbh; 13551ac8d5baSMatthew Dillon struct ccb_scsiio *csio; 13561ac8d5baSMatthew Dillon struct ata_xfer *xa; 13571ac8d5baSMatthew Dillon struct ata_fis_h2d *fis; 13581ac8d5baSMatthew Dillon scsi_cdb_t cdbs; 13591ac8d5baSMatthew Dillon scsi_cdb_t cdbd; 13601ac8d5baSMatthew Dillon int flags; 13611ac8d5baSMatthew Dillon struct ata_port *at; 13621ac8d5baSMatthew Dillon 13631ac8d5baSMatthew Dillon ccbh = &ccb->csio.ccb_h; 13641ac8d5baSMatthew Dillon csio = &ccb->csio; 13651ac8d5baSMatthew Dillon at = atx ? atx : &ap->ap_ata[0]; 13661ac8d5baSMatthew Dillon 13671ac8d5baSMatthew Dillon switch (ccbh->flags & CAM_DIR_MASK) { 13681ac8d5baSMatthew Dillon case CAM_DIR_IN: 13691ac8d5baSMatthew Dillon flags = ATA_F_PACKET | ATA_F_READ; 13701ac8d5baSMatthew Dillon break; 13711ac8d5baSMatthew Dillon case CAM_DIR_OUT: 13721ac8d5baSMatthew Dillon flags = ATA_F_PACKET | ATA_F_WRITE; 13731ac8d5baSMatthew Dillon break; 13741ac8d5baSMatthew Dillon case CAM_DIR_NONE: 13751ac8d5baSMatthew Dillon flags = ATA_F_PACKET; 13761ac8d5baSMatthew Dillon break; 13771ac8d5baSMatthew Dillon default: 13781ac8d5baSMatthew Dillon ccbh->status = CAM_REQ_INVALID; 13791ac8d5baSMatthew Dillon xpt_done(ccb); 13801ac8d5baSMatthew Dillon return; 13811ac8d5baSMatthew Dillon /* NOT REACHED */ 13821ac8d5baSMatthew Dillon } 13831ac8d5baSMatthew Dillon 13841ac8d5baSMatthew Dillon /* 13854383d440SMatthew Dillon * Special handling to get the rfis back into host memory while 1386200c399aSMatthew Dillon * still allowing the chip to run commands in parallel to 13874383d440SMatthew Dillon * ATAPI devices behind a PM. 13884383d440SMatthew Dillon */ 13894383d440SMatthew Dillon flags |= ATA_F_AUTOSENSE; 13904383d440SMatthew Dillon 13914383d440SMatthew Dillon /* 13921ac8d5baSMatthew Dillon * The command has to fit in the packet command buffer. 13931ac8d5baSMatthew Dillon */ 13941ac8d5baSMatthew Dillon if (csio->cdb_len < 6 || csio->cdb_len > 16) { 13951ac8d5baSMatthew Dillon ccbh->status = CAM_CCB_LEN_ERR; 13961ac8d5baSMatthew Dillon xpt_done(ccb); 13971ac8d5baSMatthew Dillon return; 13981ac8d5baSMatthew Dillon } 13991ac8d5baSMatthew Dillon 14001ac8d5baSMatthew Dillon /* 1401200c399aSMatthew Dillon * Initialize the XA and FIS. It is unclear how much of 1402200c399aSMatthew Dillon * this has to mimic the equivalent ATA command. 14031ac8d5baSMatthew Dillon * 14041ac8d5baSMatthew Dillon * XXX not passing NULL at for direct attach! 14051ac8d5baSMatthew Dillon */ 14061ac8d5baSMatthew Dillon xa = sili_ata_get_xfer(ap, atx); 14071ac8d5baSMatthew Dillon fis = xa->fis; 14081ac8d5baSMatthew Dillon 14091ac8d5baSMatthew Dillon fis->flags = ATA_H2D_FLAGS_CMD | at->at_target; 14101ac8d5baSMatthew Dillon fis->command = ATA_C_PACKET; 1411200c399aSMatthew Dillon fis->device = ATA_H2D_DEVICE_LBA; 14121ac8d5baSMatthew Dillon fis->sector_count = xa->tag << 3; 1413200c399aSMatthew Dillon if (flags & (ATA_F_READ | ATA_F_WRITE)) { 1414200c399aSMatthew Dillon if (flags & ATA_F_WRITE) { 14151ac8d5baSMatthew Dillon fis->features = ATA_H2D_FEATURES_DMA | 1416200c399aSMatthew Dillon ATA_H2D_FEATURES_DIR_WRITE; 1417200c399aSMatthew Dillon } else { 1418200c399aSMatthew Dillon fis->features = ATA_H2D_FEATURES_DMA | 1419200c399aSMatthew Dillon ATA_H2D_FEATURES_DIR_READ; 1420200c399aSMatthew Dillon } 1421200c399aSMatthew Dillon } else { 1422200c399aSMatthew Dillon fis->lba_mid = 0; 1423200c399aSMatthew Dillon fis->lba_high = 0; 1424200c399aSMatthew Dillon } 1425200c399aSMatthew Dillon fis->control = ATA_FIS_CONTROL_4BIT; 14261ac8d5baSMatthew Dillon 14271ac8d5baSMatthew Dillon xa->flags = flags; 14281ac8d5baSMatthew Dillon xa->data = csio->data_ptr; 14291ac8d5baSMatthew Dillon xa->datalen = csio->dxfer_len; 14301ac8d5baSMatthew Dillon xa->timeout = ccbh->timeout; /* milliseconds */ 14311ac8d5baSMatthew Dillon 14321ac8d5baSMatthew Dillon if (ccbh->flags & CAM_POLLED) 14331ac8d5baSMatthew Dillon xa->flags |= ATA_F_POLL; 14341ac8d5baSMatthew Dillon 14351ac8d5baSMatthew Dillon /* 14361ac8d5baSMatthew Dillon * Copy the cdb to the packetcmd buffer in the FIS using a 14371ac8d5baSMatthew Dillon * convenient pointer in the xa. 14381ac8d5baSMatthew Dillon */ 14391ac8d5baSMatthew Dillon cdbs = (void *)((ccbh->flags & CAM_CDB_POINTER) ? 14401ac8d5baSMatthew Dillon csio->cdb_io.cdb_ptr : csio->cdb_io.cdb_bytes); 14411ac8d5baSMatthew Dillon bcopy(cdbs, xa->packetcmd, csio->cdb_len); 14421ac8d5baSMatthew Dillon 14431ac8d5baSMatthew Dillon #if 0 14441ac8d5baSMatthew Dillon kprintf("opcode %d cdb_len %d dxfer_len %d\n", 14451ac8d5baSMatthew Dillon cdbs->generic.opcode, 14461ac8d5baSMatthew Dillon csio->cdb_len, csio->dxfer_len); 14471ac8d5baSMatthew Dillon #endif 14481ac8d5baSMatthew Dillon 14491ac8d5baSMatthew Dillon /* 14501ac8d5baSMatthew Dillon * Some ATAPI commands do not actually follow the SCSI standard. 14511ac8d5baSMatthew Dillon */ 14521ac8d5baSMatthew Dillon cdbd = (void *)xa->packetcmd; 14531ac8d5baSMatthew Dillon 14541ac8d5baSMatthew Dillon switch(cdbd->generic.opcode) { 1455200c399aSMatthew Dillon case REQUEST_SENSE: 1456200c399aSMatthew Dillon /* 1457200c399aSMatthew Dillon * Force SENSE requests to the ATAPI sense length. 1458200c399aSMatthew Dillon * 1459200c399aSMatthew Dillon * It is unclear if this is needed or not. 1460200c399aSMatthew Dillon */ 1461200c399aSMatthew Dillon if (cdbd->sense.length == SSD_FULL_SIZE) { 1462200c399aSMatthew Dillon kprintf("%s: Shortening sense request\n", 1463200c399aSMatthew Dillon PORTNAME(ap)); 1464200c399aSMatthew Dillon cdbd->sense.length = offsetof(struct scsi_sense_data, 1465200c399aSMatthew Dillon extra_bytes[0]); 1466200c399aSMatthew Dillon } 1467200c399aSMatthew Dillon break; 14681ac8d5baSMatthew Dillon case INQUIRY: 14691ac8d5baSMatthew Dillon /* 14701ac8d5baSMatthew Dillon * Some ATAPI devices can't handle long inquiry lengths, 14711ac8d5baSMatthew Dillon * don't ask me why. Truncate the inquiry length. 14721ac8d5baSMatthew Dillon */ 14731ac8d5baSMatthew Dillon if (cdbd->inquiry.page_code == 0 && 14741ac8d5baSMatthew Dillon cdbd->inquiry.length > SHORT_INQUIRY_LENGTH) { 14751ac8d5baSMatthew Dillon cdbd->inquiry.length = SHORT_INQUIRY_LENGTH; 14761ac8d5baSMatthew Dillon } 14771ac8d5baSMatthew Dillon break; 14781ac8d5baSMatthew Dillon case READ_6: 14791ac8d5baSMatthew Dillon case WRITE_6: 14801ac8d5baSMatthew Dillon /* 14811ac8d5baSMatthew Dillon * Convert *_6 to *_10 commands. Most ATAPI devices 14821ac8d5baSMatthew Dillon * cannot handle the SCSI READ_6 and WRITE_6 commands. 14831ac8d5baSMatthew Dillon */ 14841ac8d5baSMatthew Dillon cdbd->rw_10.opcode |= 0x20; 14851ac8d5baSMatthew Dillon cdbd->rw_10.byte2 = 0; 14861ac8d5baSMatthew Dillon cdbd->rw_10.addr[0] = cdbs->rw_6.addr[0] & 0x1F; 14871ac8d5baSMatthew Dillon cdbd->rw_10.addr[1] = cdbs->rw_6.addr[1]; 14881ac8d5baSMatthew Dillon cdbd->rw_10.addr[2] = cdbs->rw_6.addr[2]; 14891ac8d5baSMatthew Dillon cdbd->rw_10.addr[3] = 0; 14901ac8d5baSMatthew Dillon cdbd->rw_10.reserved = 0; 14911ac8d5baSMatthew Dillon cdbd->rw_10.length[0] = 0; 14921ac8d5baSMatthew Dillon cdbd->rw_10.length[1] = cdbs->rw_6.length; 14931ac8d5baSMatthew Dillon cdbd->rw_10.control = cdbs->rw_6.control; 14941ac8d5baSMatthew Dillon break; 14951ac8d5baSMatthew Dillon default: 14961ac8d5baSMatthew Dillon break; 14971ac8d5baSMatthew Dillon } 14981ac8d5baSMatthew Dillon 14991ac8d5baSMatthew Dillon /* 15001ac8d5baSMatthew Dillon * And dispatch 15011ac8d5baSMatthew Dillon */ 15021ac8d5baSMatthew Dillon xa->complete = sili_atapi_complete_cmd; 15031ac8d5baSMatthew Dillon xa->atascsi_private = ccb; 15041ac8d5baSMatthew Dillon ccb->ccb_h.sim_priv.entries[0].ptr = ap; 15051ac8d5baSMatthew Dillon sili_os_lock_port(ap); 15061ac8d5baSMatthew Dillon sili_ata_cmd(xa); 15071ac8d5baSMatthew Dillon sili_os_unlock_port(ap); 15081ac8d5baSMatthew Dillon } 15091ac8d5baSMatthew Dillon 15101ac8d5baSMatthew Dillon /* 15113ac61d78SMatthew Dillon * Simulate page inquiries for disk attachments. 15123ac61d78SMatthew Dillon */ 15133ac61d78SMatthew Dillon static 15143ac61d78SMatthew Dillon void 15153ac61d78SMatthew Dillon sili_xpt_page_inquiry(struct sili_port *ap, struct ata_port *at, union ccb *ccb) 15163ac61d78SMatthew Dillon { 15173ac61d78SMatthew Dillon union { 15183ac61d78SMatthew Dillon struct scsi_vpd_supported_page_list list; 15193ac61d78SMatthew Dillon struct scsi_vpd_unit_serial_number serno; 15203ac61d78SMatthew Dillon struct scsi_vpd_unit_devid devid; 15213ac61d78SMatthew Dillon char buf[256]; 15223ac61d78SMatthew Dillon } *page; 15233ac61d78SMatthew Dillon scsi_cdb_t cdb; 15243ac61d78SMatthew Dillon int i; 15253ac61d78SMatthew Dillon int j; 15263ac61d78SMatthew Dillon int len; 15273ac61d78SMatthew Dillon 15283ac61d78SMatthew Dillon page = kmalloc(sizeof(*page), M_DEVBUF, M_WAITOK | M_ZERO); 15293ac61d78SMatthew Dillon 15303ac61d78SMatthew Dillon cdb = (void *)((ccb->ccb_h.flags & CAM_CDB_POINTER) ? 15313ac61d78SMatthew Dillon ccb->csio.cdb_io.cdb_ptr : ccb->csio.cdb_io.cdb_bytes); 15323ac61d78SMatthew Dillon 15333ac61d78SMatthew Dillon switch(cdb->inquiry.page_code) { 15343ac61d78SMatthew Dillon case SVPD_SUPPORTED_PAGE_LIST: 15353ac61d78SMatthew Dillon i = 0; 15363ac61d78SMatthew Dillon page->list.device = T_DIRECT; 15373ac61d78SMatthew Dillon page->list.page_code = SVPD_SUPPORTED_PAGE_LIST; 15383ac61d78SMatthew Dillon page->list.list[i++] = SVPD_SUPPORTED_PAGE_LIST; 15393ac61d78SMatthew Dillon page->list.list[i++] = SVPD_UNIT_SERIAL_NUMBER; 15403ac61d78SMatthew Dillon page->list.list[i++] = SVPD_UNIT_DEVID; 15413ac61d78SMatthew Dillon page->list.length = i; 15423ac61d78SMatthew Dillon len = offsetof(struct scsi_vpd_supported_page_list, list[3]); 15433ac61d78SMatthew Dillon break; 15443ac61d78SMatthew Dillon case SVPD_UNIT_SERIAL_NUMBER: 15453ac61d78SMatthew Dillon i = 0; 15463ac61d78SMatthew Dillon j = sizeof(at->at_identify.serial); 15473ac61d78SMatthew Dillon for (i = 0; i < j && at->at_identify.serial[i] == ' '; ++i) 15483ac61d78SMatthew Dillon ; 15493ac61d78SMatthew Dillon while (j > i && at->at_identify.serial[j-1] == ' ') 15503ac61d78SMatthew Dillon --j; 15513ac61d78SMatthew Dillon page->serno.device = T_DIRECT; 15523ac61d78SMatthew Dillon page->serno.page_code = SVPD_UNIT_SERIAL_NUMBER; 15533ac61d78SMatthew Dillon page->serno.length = j - i; 15543ac61d78SMatthew Dillon bcopy(at->at_identify.serial + i, 15553ac61d78SMatthew Dillon page->serno.serial_num, j - i); 15563ac61d78SMatthew Dillon len = offsetof(struct scsi_vpd_unit_serial_number, 15573ac61d78SMatthew Dillon serial_num[j-i]); 15583ac61d78SMatthew Dillon break; 15593ac61d78SMatthew Dillon case SVPD_UNIT_DEVID: 15603ac61d78SMatthew Dillon /* fall through for now */ 15613ac61d78SMatthew Dillon default: 15623ac61d78SMatthew Dillon ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; 15633ac61d78SMatthew Dillon len = 0; 15643ac61d78SMatthew Dillon break; 15653ac61d78SMatthew Dillon } 15663ac61d78SMatthew Dillon if (ccb->ccb_h.status == CAM_REQ_INPROG) { 15673ac61d78SMatthew Dillon if (len <= ccb->csio.dxfer_len) { 15683ac61d78SMatthew Dillon ccb->ccb_h.status = CAM_REQ_CMP; 15693ac61d78SMatthew Dillon bzero(ccb->csio.data_ptr, ccb->csio.dxfer_len); 15703ac61d78SMatthew Dillon bcopy(page, ccb->csio.data_ptr, len); 15713ac61d78SMatthew Dillon ccb->csio.resid = ccb->csio.dxfer_len - len; 15723ac61d78SMatthew Dillon } else { 15733ac61d78SMatthew Dillon ccb->ccb_h.status = CAM_CCB_LEN_ERR; 15743ac61d78SMatthew Dillon } 15753ac61d78SMatthew Dillon } 15763ac61d78SMatthew Dillon kfree(page, M_DEVBUF); 15773ac61d78SMatthew Dillon } 15783ac61d78SMatthew Dillon 15793ac61d78SMatthew Dillon /* 15801ac8d5baSMatthew Dillon * Completion function for ATA_PORT_T_DISK cache synchronization. 15811ac8d5baSMatthew Dillon */ 15821ac8d5baSMatthew Dillon static 15831ac8d5baSMatthew Dillon void 15841ac8d5baSMatthew Dillon sili_ata_complete_disk_synchronize_cache(struct ata_xfer *xa) 15851ac8d5baSMatthew Dillon { 15861ac8d5baSMatthew Dillon union ccb *ccb = xa->atascsi_private; 15871ac8d5baSMatthew Dillon struct ccb_hdr *ccbh = &ccb->ccb_h; 15881ac8d5baSMatthew Dillon struct sili_port *ap = ccb->ccb_h.sim_priv.entries[0].ptr; 15891ac8d5baSMatthew Dillon 15901ac8d5baSMatthew Dillon switch(xa->state) { 15911ac8d5baSMatthew Dillon case ATA_S_COMPLETE: 15921ac8d5baSMatthew Dillon ccbh->status = CAM_REQ_CMP; 15931ac8d5baSMatthew Dillon ccb->csio.scsi_status = SCSI_STATUS_OK; 15941ac8d5baSMatthew Dillon break; 15951ac8d5baSMatthew Dillon case ATA_S_ERROR: 15961ac8d5baSMatthew Dillon kprintf("%s: synchronize_cache: error\n", 15971ac8d5baSMatthew Dillon ATANAME(ap, xa->at)); 15981ac8d5baSMatthew Dillon ccbh->status = CAM_SCSI_STATUS_ERROR | CAM_AUTOSNS_VALID; 15991ac8d5baSMatthew Dillon ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND; 16001ac8d5baSMatthew Dillon sili_ata_dummy_sense(&ccb->csio.sense_data); 16011ac8d5baSMatthew Dillon break; 16021ac8d5baSMatthew Dillon case ATA_S_TIMEOUT: 16031ac8d5baSMatthew Dillon kprintf("%s: synchronize_cache: timeout\n", 16041ac8d5baSMatthew Dillon ATANAME(ap, xa->at)); 16051ac8d5baSMatthew Dillon ccbh->status = CAM_CMD_TIMEOUT; 16061ac8d5baSMatthew Dillon break; 16071ac8d5baSMatthew Dillon default: 16081ac8d5baSMatthew Dillon kprintf("%s: synchronize_cache: unknown state %d\n", 16091ac8d5baSMatthew Dillon ATANAME(ap, xa->at), xa->state); 16101ac8d5baSMatthew Dillon ccbh->status = CAM_REQ_CMP_ERR; 16111ac8d5baSMatthew Dillon break; 16121ac8d5baSMatthew Dillon } 16131ac8d5baSMatthew Dillon sili_ata_put_xfer(xa); 16141ac8d5baSMatthew Dillon sili_os_unlock_port(ap); 16151ac8d5baSMatthew Dillon xpt_done(ccb); 16161ac8d5baSMatthew Dillon sili_os_lock_port(ap); 16171ac8d5baSMatthew Dillon } 16181ac8d5baSMatthew Dillon 16191ac8d5baSMatthew Dillon /* 16201ac8d5baSMatthew Dillon * Completion function for ATA_PORT_T_DISK I/O 16211ac8d5baSMatthew Dillon */ 16221ac8d5baSMatthew Dillon static 16231ac8d5baSMatthew Dillon void 16241ac8d5baSMatthew Dillon sili_ata_complete_disk_rw(struct ata_xfer *xa) 16251ac8d5baSMatthew Dillon { 16261ac8d5baSMatthew Dillon union ccb *ccb = xa->atascsi_private; 16271ac8d5baSMatthew Dillon struct ccb_hdr *ccbh = &ccb->ccb_h; 16281ac8d5baSMatthew Dillon struct sili_port *ap = ccb->ccb_h.sim_priv.entries[0].ptr; 16291ac8d5baSMatthew Dillon 16301ac8d5baSMatthew Dillon switch(xa->state) { 16311ac8d5baSMatthew Dillon case ATA_S_COMPLETE: 16321ac8d5baSMatthew Dillon ccbh->status = CAM_REQ_CMP; 16331ac8d5baSMatthew Dillon ccb->csio.scsi_status = SCSI_STATUS_OK; 16341ac8d5baSMatthew Dillon break; 16351ac8d5baSMatthew Dillon case ATA_S_ERROR: 16361ac8d5baSMatthew Dillon kprintf("%s: disk_rw: error\n", ATANAME(ap, xa->at)); 16371ac8d5baSMatthew Dillon ccbh->status = CAM_SCSI_STATUS_ERROR | CAM_AUTOSNS_VALID; 16381ac8d5baSMatthew Dillon ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND; 16391ac8d5baSMatthew Dillon sili_ata_dummy_sense(&ccb->csio.sense_data); 16401ac8d5baSMatthew Dillon break; 16411ac8d5baSMatthew Dillon case ATA_S_TIMEOUT: 16421ac8d5baSMatthew Dillon kprintf("%s: disk_rw: timeout\n", ATANAME(ap, xa->at)); 16431ac8d5baSMatthew Dillon ccbh->status = CAM_CMD_TIMEOUT; 16441ac8d5baSMatthew Dillon ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND; 16451ac8d5baSMatthew Dillon sili_ata_dummy_sense(&ccb->csio.sense_data); 16461ac8d5baSMatthew Dillon break; 16471ac8d5baSMatthew Dillon default: 16481ac8d5baSMatthew Dillon kprintf("%s: disk_rw: unknown state %d\n", 16491ac8d5baSMatthew Dillon ATANAME(ap, xa->at), xa->state); 16501ac8d5baSMatthew Dillon ccbh->status = CAM_REQ_CMP_ERR; 16511ac8d5baSMatthew Dillon break; 16521ac8d5baSMatthew Dillon } 16531ac8d5baSMatthew Dillon ccb->csio.resid = xa->resid; 16541ac8d5baSMatthew Dillon sili_ata_put_xfer(xa); 16551ac8d5baSMatthew Dillon sili_os_unlock_port(ap); 16561ac8d5baSMatthew Dillon xpt_done(ccb); 16571ac8d5baSMatthew Dillon sili_os_lock_port(ap); 16581ac8d5baSMatthew Dillon } 16591ac8d5baSMatthew Dillon 16601ac8d5baSMatthew Dillon /* 16611ac8d5baSMatthew Dillon * Completion function for ATA_PORT_T_ATAPI I/O 16621ac8d5baSMatthew Dillon * 16631ac8d5baSMatthew Dillon * Sense data is returned in the rfis. 16641ac8d5baSMatthew Dillon */ 16651ac8d5baSMatthew Dillon static 16661ac8d5baSMatthew Dillon void 16671ac8d5baSMatthew Dillon sili_atapi_complete_cmd(struct ata_xfer *xa) 16681ac8d5baSMatthew Dillon { 16691ac8d5baSMatthew Dillon union ccb *ccb = xa->atascsi_private; 16701ac8d5baSMatthew Dillon struct ccb_hdr *ccbh = &ccb->ccb_h; 16711ac8d5baSMatthew Dillon struct sili_port *ap = ccb->ccb_h.sim_priv.entries[0].ptr; 16721ac8d5baSMatthew Dillon scsi_cdb_t cdb; 16731ac8d5baSMatthew Dillon 16741ac8d5baSMatthew Dillon cdb = (void *)((ccb->ccb_h.flags & CAM_CDB_POINTER) ? 16751ac8d5baSMatthew Dillon ccb->csio.cdb_io.cdb_ptr : ccb->csio.cdb_io.cdb_bytes); 16761ac8d5baSMatthew Dillon 16771ac8d5baSMatthew Dillon switch(xa->state) { 16781ac8d5baSMatthew Dillon case ATA_S_COMPLETE: 16791ac8d5baSMatthew Dillon ccbh->status = CAM_REQ_CMP; 16801ac8d5baSMatthew Dillon ccb->csio.scsi_status = SCSI_STATUS_OK; 16811ac8d5baSMatthew Dillon break; 16821ac8d5baSMatthew Dillon case ATA_S_ERROR: 16831ac8d5baSMatthew Dillon ccbh->status = CAM_SCSI_STATUS_ERROR; 16841ac8d5baSMatthew Dillon ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND; 16851ac8d5baSMatthew Dillon sili_ata_atapi_sense(xa->rfis, &ccb->csio.sense_data); 16861ac8d5baSMatthew Dillon break; 16871ac8d5baSMatthew Dillon case ATA_S_TIMEOUT: 16881ac8d5baSMatthew Dillon kprintf("%s: cmd %d: timeout\n", 16891ac8d5baSMatthew Dillon PORTNAME(ap), cdb->generic.opcode); 16901ac8d5baSMatthew Dillon ccbh->status = CAM_CMD_TIMEOUT; 16911ac8d5baSMatthew Dillon ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND; 16921ac8d5baSMatthew Dillon sili_ata_dummy_sense(&ccb->csio.sense_data); 16931ac8d5baSMatthew Dillon break; 16941ac8d5baSMatthew Dillon default: 16951ac8d5baSMatthew Dillon kprintf("%s: cmd %d: unknown state %d\n", 16961ac8d5baSMatthew Dillon PORTNAME(ap), cdb->generic.opcode, xa->state); 16971ac8d5baSMatthew Dillon ccbh->status = CAM_REQ_CMP_ERR; 16981ac8d5baSMatthew Dillon break; 16991ac8d5baSMatthew Dillon } 17001ac8d5baSMatthew Dillon ccb->csio.resid = xa->resid; 17011ac8d5baSMatthew Dillon sili_ata_put_xfer(xa); 17021ac8d5baSMatthew Dillon sili_os_unlock_port(ap); 17031ac8d5baSMatthew Dillon xpt_done(ccb); 17041ac8d5baSMatthew Dillon sili_os_lock_port(ap); 17051ac8d5baSMatthew Dillon } 17061ac8d5baSMatthew Dillon 17071ac8d5baSMatthew Dillon /* 17081ac8d5baSMatthew Dillon * Construct dummy sense data for errors on DISKs 17091ac8d5baSMatthew Dillon */ 17101ac8d5baSMatthew Dillon static 17111ac8d5baSMatthew Dillon void 17121ac8d5baSMatthew Dillon sili_ata_dummy_sense(struct scsi_sense_data *sense_data) 17131ac8d5baSMatthew Dillon { 17141ac8d5baSMatthew Dillon sense_data->error_code = SSD_ERRCODE_VALID | SSD_CURRENT_ERROR; 17151ac8d5baSMatthew Dillon sense_data->segment = 0; 17161ac8d5baSMatthew Dillon sense_data->flags = SSD_KEY_MEDIUM_ERROR; 17171ac8d5baSMatthew Dillon sense_data->info[0] = 0; 17181ac8d5baSMatthew Dillon sense_data->info[1] = 0; 17191ac8d5baSMatthew Dillon sense_data->info[2] = 0; 17201ac8d5baSMatthew Dillon sense_data->info[3] = 0; 17211ac8d5baSMatthew Dillon sense_data->extra_len = 0; 17221ac8d5baSMatthew Dillon } 17231ac8d5baSMatthew Dillon 17241ac8d5baSMatthew Dillon /* 17251ac8d5baSMatthew Dillon * Construct atapi sense data for errors on ATAPI 17261ac8d5baSMatthew Dillon * 17271ac8d5baSMatthew Dillon * The ATAPI sense data is stored in the passed rfis and must be converted 17281ac8d5baSMatthew Dillon * to SCSI sense data. 17291ac8d5baSMatthew Dillon */ 17301ac8d5baSMatthew Dillon static 17311ac8d5baSMatthew Dillon void 17321ac8d5baSMatthew Dillon sili_ata_atapi_sense(struct ata_fis_d2h *rfis, 17331ac8d5baSMatthew Dillon struct scsi_sense_data *sense_data) 17341ac8d5baSMatthew Dillon { 17351ac8d5baSMatthew Dillon sense_data->error_code = SSD_ERRCODE_VALID | SSD_CURRENT_ERROR; 17361ac8d5baSMatthew Dillon sense_data->segment = 0; 17371ac8d5baSMatthew Dillon sense_data->flags = (rfis->error & 0xF0) >> 4; 17381ac8d5baSMatthew Dillon if (rfis->error & 0x04) 17391ac8d5baSMatthew Dillon sense_data->flags |= SSD_KEY_ILLEGAL_REQUEST; 17401ac8d5baSMatthew Dillon if (rfis->error & 0x02) 17411ac8d5baSMatthew Dillon sense_data->flags |= SSD_EOM; 17421ac8d5baSMatthew Dillon if (rfis->error & 0x01) 17431ac8d5baSMatthew Dillon sense_data->flags |= SSD_ILI; 17441ac8d5baSMatthew Dillon sense_data->info[0] = 0; 17451ac8d5baSMatthew Dillon sense_data->info[1] = 0; 17461ac8d5baSMatthew Dillon sense_data->info[2] = 0; 17471ac8d5baSMatthew Dillon sense_data->info[3] = 0; 17481ac8d5baSMatthew Dillon sense_data->extra_len = 0; 17491ac8d5baSMatthew Dillon } 17503ac61d78SMatthew Dillon 17513ac61d78SMatthew Dillon static 17523ac61d78SMatthew Dillon void 17533ac61d78SMatthew Dillon sili_strip_string(const char **basep, int *lenp) 17543ac61d78SMatthew Dillon { 17553ac61d78SMatthew Dillon const char *base = *basep; 17563ac61d78SMatthew Dillon int len = *lenp; 17573ac61d78SMatthew Dillon 17583ac61d78SMatthew Dillon while (len && (*base == 0 || *base == ' ')) { 17593ac61d78SMatthew Dillon --len; 17603ac61d78SMatthew Dillon ++base; 17613ac61d78SMatthew Dillon } 17623ac61d78SMatthew Dillon while (len && (base[len-1] == 0 || base[len-1] == ' ')) 17633ac61d78SMatthew Dillon --len; 17643ac61d78SMatthew Dillon *basep = base; 17653ac61d78SMatthew Dillon *lenp = len; 17663ac61d78SMatthew Dillon } 1767