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