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