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