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