xref: /dflybsd-src/sys/dev/disk/ahci/ahci_cam.c (revision 46528d338822ba0b7e4becd8f66c04e2d9698b24)
1258223a3SMatthew Dillon /*
2fb00c6edSMatthew Dillon  * (MPSAFE)
3fb00c6edSMatthew Dillon  *
4258223a3SMatthew Dillon  * Copyright (c) 2009 The DragonFly Project.  All rights reserved.
5258223a3SMatthew Dillon  *
6258223a3SMatthew Dillon  * This code is derived from software contributed to The DragonFly Project
7258223a3SMatthew Dillon  * by Matthew Dillon <dillon@backplane.com>
8258223a3SMatthew Dillon  *
9258223a3SMatthew Dillon  * Redistribution and use in source and binary forms, with or without
10258223a3SMatthew Dillon  * modification, are permitted provided that the following conditions
11258223a3SMatthew Dillon  * are met:
12258223a3SMatthew Dillon  *
13258223a3SMatthew Dillon  * 1. Redistributions of source code must retain the above copyright
14258223a3SMatthew Dillon  *    notice, this list of conditions and the following disclaimer.
15258223a3SMatthew Dillon  * 2. Redistributions in binary form must reproduce the above copyright
16258223a3SMatthew Dillon  *    notice, this list of conditions and the following disclaimer in
17258223a3SMatthew Dillon  *    the documentation and/or other materials provided with the
18258223a3SMatthew Dillon  *    distribution.
19258223a3SMatthew Dillon  * 3. Neither the name of The DragonFly Project nor the names of its
20258223a3SMatthew Dillon  *    contributors may be used to endorse or promote products derived
21258223a3SMatthew Dillon  *    from this software without specific, prior written permission.
22258223a3SMatthew Dillon  *
23258223a3SMatthew Dillon  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24258223a3SMatthew Dillon  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25258223a3SMatthew Dillon  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26258223a3SMatthew Dillon  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
27258223a3SMatthew Dillon  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28258223a3SMatthew Dillon  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
29258223a3SMatthew Dillon  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30258223a3SMatthew Dillon  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
31258223a3SMatthew Dillon  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32258223a3SMatthew Dillon  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
33258223a3SMatthew Dillon  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34258223a3SMatthew Dillon  * SUCH DAMAGE.
35258223a3SMatthew Dillon  *
36258223a3SMatthew Dillon  *
37258223a3SMatthew Dillon  * Copyright (c) 2007 David Gwynne <dlg@openbsd.org>
38258223a3SMatthew Dillon  *
39258223a3SMatthew Dillon  * Permission to use, copy, modify, and distribute this software for any
40258223a3SMatthew Dillon  * purpose with or without fee is hereby granted, provided that the above
41258223a3SMatthew Dillon  * copyright notice and this permission notice appear in all copies.
42258223a3SMatthew Dillon  *
43258223a3SMatthew Dillon  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
44258223a3SMatthew Dillon  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
45258223a3SMatthew Dillon  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
46258223a3SMatthew Dillon  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
47258223a3SMatthew Dillon  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
48258223a3SMatthew Dillon  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
49258223a3SMatthew Dillon  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
50258223a3SMatthew Dillon  *
51258223a3SMatthew Dillon  * $OpenBSD: atascsi.c,v 1.64 2009/02/16 21:19:06 miod Exp $
52258223a3SMatthew Dillon  * $DragonFly$
53258223a3SMatthew Dillon  */
54258223a3SMatthew Dillon /*
55258223a3SMatthew Dillon  * Implement each SATA port as its own SCSI bus on CAM.  This way we can
56258223a3SMatthew Dillon  * implement future port multiplier features as individual devices on the
57258223a3SMatthew Dillon  * bus.
58258223a3SMatthew Dillon  *
59258223a3SMatthew Dillon  * Much of the cdb<->xa conversion code was taken from OpenBSD, the rest
60258223a3SMatthew Dillon  * was written natively for DragonFly.
61258223a3SMatthew Dillon  */
62258223a3SMatthew Dillon 
63258223a3SMatthew Dillon #include "ahci.h"
64258223a3SMatthew Dillon 
65258223a3SMatthew Dillon static void ahci_xpt_action(struct cam_sim *sim, union ccb *ccb);
66258223a3SMatthew Dillon static void ahci_xpt_poll(struct cam_sim *sim);
671980eff3SMatthew Dillon static void ahci_xpt_scsi_disk_io(struct ahci_port *ap,
681980eff3SMatthew Dillon 			struct ata_port *at, union ccb *ccb);
691980eff3SMatthew Dillon static void ahci_xpt_scsi_atapi_io(struct ahci_port *ap,
701980eff3SMatthew Dillon 			struct ata_port *at, union ccb *ccb);
716e0003adSMatthew Dillon static void ahci_xpt_page_inquiry(struct ahci_port *ap,
726e0003adSMatthew Dillon 			struct ata_port *at, union ccb *ccb);
73258223a3SMatthew Dillon 
74258223a3SMatthew Dillon static void ahci_ata_complete_disk_rw(struct ata_xfer *xa);
75258223a3SMatthew Dillon static void ahci_ata_complete_disk_synchronize_cache(struct ata_xfer *xa);
76b4189e5eSMatthew Dillon static void ahci_atapi_complete_cmd(struct ata_xfer *xa);
77b4189e5eSMatthew Dillon static void ahci_ata_dummy_sense(struct scsi_sense_data *sense_data);
787d4fcf34SMatthew Dillon static void ahci_ata_atapi_sense(struct ata_fis_d2h *rfis,
797d4fcf34SMatthew Dillon 		     struct scsi_sense_data *sense_data);
80258223a3SMatthew Dillon 
811980eff3SMatthew Dillon static int ahci_cam_probe_disk(struct ahci_port *ap, struct ata_port *at);
821980eff3SMatthew Dillon static int ahci_cam_probe_atapi(struct ahci_port *ap, struct ata_port *at);
8369b3741eSMatthew Dillon static int ahci_set_xfer(struct ahci_port *ap, struct ata_port *atx);
84b4189e5eSMatthew Dillon static void ahci_ata_dummy_done(struct ata_xfer *xa);
85258223a3SMatthew Dillon static void ata_fix_identify(struct ata_identify *id);
86258223a3SMatthew Dillon static void ahci_cam_rescan(struct ahci_port *ap);
876e0003adSMatthew Dillon static void ahci_strip_string(const char **basep, int *lenp);
88258223a3SMatthew Dillon 
89258223a3SMatthew Dillon int
90258223a3SMatthew Dillon ahci_cam_attach(struct ahci_port *ap)
91258223a3SMatthew Dillon {
92258223a3SMatthew Dillon 	struct cam_devq *devq;
93258223a3SMatthew Dillon 	struct cam_sim *sim;
94258223a3SMatthew Dillon 	int error;
95258223a3SMatthew Dillon 	int unit;
96258223a3SMatthew Dillon 
97cec85a37SMatthew Dillon 	/*
98cec85a37SMatthew Dillon 	 * We want at least one ccb to be available for error processing
99cec85a37SMatthew Dillon 	 * so don't let CAM use more then ncmds - 1.
100cec85a37SMatthew Dillon 	 */
101258223a3SMatthew Dillon 	unit = device_get_unit(ap->ap_sc->sc_dev);
102cec85a37SMatthew Dillon 	if (ap->ap_sc->sc_ncmds > 1)
103cec85a37SMatthew Dillon 		devq = cam_simq_alloc(ap->ap_sc->sc_ncmds - 1);
104cec85a37SMatthew Dillon 	else
105258223a3SMatthew Dillon 		devq = cam_simq_alloc(ap->ap_sc->sc_ncmds);
106258223a3SMatthew Dillon 	if (devq == NULL) {
107258223a3SMatthew Dillon 		return (ENOMEM);
108258223a3SMatthew Dillon 	}
109949597c2SMatthew Dillon 
110949597c2SMatthew Dillon 	/*
111949597c2SMatthew Dillon 	 * Give the devq enough room to run with 32 max_dev_transactions,
112949597c2SMatthew Dillon 	 * but set the overall max tags to 1 until NCQ is negotiated.
113949597c2SMatthew Dillon 	 */
114258223a3SMatthew Dillon 	sim = cam_sim_alloc(ahci_xpt_action, ahci_xpt_poll, "ahci",
115949597c2SMatthew Dillon 			   (void *)ap, unit, &ap->ap_sim_lock,
116949597c2SMatthew Dillon 			   32, 1, devq);
117258223a3SMatthew Dillon 	cam_simq_release(devq);
118258223a3SMatthew Dillon 	if (sim == NULL) {
119258223a3SMatthew Dillon 		return (ENOMEM);
120258223a3SMatthew Dillon 	}
121258223a3SMatthew Dillon 	ap->ap_sim = sim;
122831bc9e3SMatthew Dillon 	ahci_os_unlock_port(ap);
123fb00c6edSMatthew Dillon 	lockmgr(&ap->ap_sim_lock, LK_EXCLUSIVE);
124258223a3SMatthew Dillon 	error = xpt_bus_register(ap->ap_sim, ap->ap_num);
125fb00c6edSMatthew Dillon 	lockmgr(&ap->ap_sim_lock, LK_RELEASE);
126831bc9e3SMatthew Dillon 	ahci_os_lock_port(ap);
127258223a3SMatthew Dillon 	if (error != CAM_SUCCESS) {
128258223a3SMatthew Dillon 		ahci_cam_detach(ap);
129258223a3SMatthew Dillon 		return (EINVAL);
130258223a3SMatthew Dillon 	}
131258223a3SMatthew Dillon 	ap->ap_flags |= AP_F_BUS_REGISTERED;
132258223a3SMatthew Dillon 
133c408a8b3SMatthew Dillon 	if (ap->ap_probe == ATA_PROBE_NEED_IDENT)
1341980eff3SMatthew Dillon 		error = ahci_cam_probe(ap, NULL);
135c408a8b3SMatthew Dillon 	else
136c408a8b3SMatthew Dillon 		error = 0;
137258223a3SMatthew Dillon 	if (error) {
138258223a3SMatthew Dillon 		ahci_cam_detach(ap);
139258223a3SMatthew Dillon 		return (EIO);
140258223a3SMatthew Dillon 	}
141258223a3SMatthew Dillon 	ap->ap_flags |= AP_F_CAM_ATTACHED;
142258223a3SMatthew Dillon 
143258223a3SMatthew Dillon 	return(0);
144258223a3SMatthew Dillon }
145258223a3SMatthew Dillon 
1461980eff3SMatthew Dillon /*
1473209f581SMatthew Dillon  * The state of the port has changed.
1483209f581SMatthew Dillon  *
1493209f581SMatthew Dillon  * If at is NULL the physical port has changed state.
1503209f581SMatthew Dillon  * If at is non-NULL a particular target behind a PM has changed state.
1513209f581SMatthew Dillon  *
1523209f581SMatthew Dillon  * If found is -1 the target state must be queued to a non-interrupt context.
1533209f581SMatthew Dillon  * (only works with at == NULL).
1543209f581SMatthew Dillon  *
1553209f581SMatthew Dillon  * If found is 0 the target was removed.
1563209f581SMatthew Dillon  * If found is 1 the target was inserted.
1571980eff3SMatthew Dillon  */
158258223a3SMatthew Dillon void
1593209f581SMatthew Dillon ahci_cam_changed(struct ahci_port *ap, struct ata_port *atx, int found)
160258223a3SMatthew Dillon {
161fd8bd957SMatthew Dillon 	struct cam_path *tmppath;
1623209f581SMatthew Dillon 	int status;
1633209f581SMatthew Dillon 	int target;
1643209f581SMatthew Dillon 
1653209f581SMatthew Dillon 	target = atx ? atx->at_target : CAM_TARGET_WILDCARD;
166fd8bd957SMatthew Dillon 
167258223a3SMatthew Dillon 	if (ap->ap_sim == NULL)
168258223a3SMatthew Dillon 		return;
1693209f581SMatthew Dillon 	if (found == CAM_TARGET_WILDCARD) {
1703209f581SMatthew Dillon 		status = xpt_create_path(&tmppath, NULL,
1713209f581SMatthew Dillon 					 cam_sim_path(ap->ap_sim),
1723209f581SMatthew Dillon 					 target, CAM_LUN_WILDCARD);
1733209f581SMatthew Dillon 		if (status != CAM_REQ_CMP)
174fd8bd957SMatthew Dillon 			return;
175258223a3SMatthew Dillon 		ahci_cam_rescan(ap);
176fd8bd957SMatthew Dillon 	} else {
1773209f581SMatthew Dillon 		status = xpt_create_path(&tmppath, NULL,
1783209f581SMatthew Dillon 					 cam_sim_path(ap->ap_sim),
1793209f581SMatthew Dillon 					 target,
1803209f581SMatthew Dillon 					 CAM_LUN_WILDCARD);
1813209f581SMatthew Dillon 		if (status != CAM_REQ_CMP)
1823209f581SMatthew Dillon 			return;
1833209f581SMatthew Dillon #if 0
1843209f581SMatthew Dillon 		/*
1853209f581SMatthew Dillon 		 * This confuses CAM
1863209f581SMatthew Dillon 		 */
1873209f581SMatthew Dillon 		if (found)
1883209f581SMatthew Dillon 			xpt_async(AC_FOUND_DEVICE, tmppath, NULL);
1893209f581SMatthew Dillon 		else
190fd8bd957SMatthew Dillon 			xpt_async(AC_LOST_DEVICE, tmppath, NULL);
1913209f581SMatthew Dillon #endif
192fd8bd957SMatthew Dillon 	}
193fd8bd957SMatthew Dillon 	xpt_free_path(tmppath);
194258223a3SMatthew Dillon }
195258223a3SMatthew Dillon 
196258223a3SMatthew Dillon void
197258223a3SMatthew Dillon ahci_cam_detach(struct ahci_port *ap)
198258223a3SMatthew Dillon {
199258223a3SMatthew Dillon 	int error;
200258223a3SMatthew Dillon 
201258223a3SMatthew Dillon 	if ((ap->ap_flags & AP_F_CAM_ATTACHED) == 0)
202258223a3SMatthew Dillon 		return;
203fb00c6edSMatthew Dillon 	lockmgr(&ap->ap_sim_lock, LK_EXCLUSIVE);
204258223a3SMatthew Dillon 	if (ap->ap_sim) {
205258223a3SMatthew Dillon 		xpt_freeze_simq(ap->ap_sim, 1);
206258223a3SMatthew Dillon 	}
207258223a3SMatthew Dillon 	if (ap->ap_flags & AP_F_BUS_REGISTERED) {
208258223a3SMatthew Dillon 		error = xpt_bus_deregister(cam_sim_path(ap->ap_sim));
209258223a3SMatthew Dillon 		KKASSERT(error == CAM_REQ_CMP);
210258223a3SMatthew Dillon 		ap->ap_flags &= ~AP_F_BUS_REGISTERED;
211258223a3SMatthew Dillon 	}
212258223a3SMatthew Dillon 	if (ap->ap_sim) {
213258223a3SMatthew Dillon 		cam_sim_free(ap->ap_sim);
214258223a3SMatthew Dillon 		ap->ap_sim = NULL;
215258223a3SMatthew Dillon 	}
216fb00c6edSMatthew Dillon 	lockmgr(&ap->ap_sim_lock, LK_RELEASE);
217258223a3SMatthew Dillon 	ap->ap_flags &= ~AP_F_CAM_ATTACHED;
218258223a3SMatthew Dillon }
219258223a3SMatthew Dillon 
220258223a3SMatthew Dillon /*
2211980eff3SMatthew Dillon  * Once the AHCI port has been attached we need to probe for a device or
222258223a3SMatthew Dillon  * devices on the port and setup various options.
2231980eff3SMatthew Dillon  *
2241980eff3SMatthew Dillon  * If at is NULL we are probing the direct-attached device on the port,
2251980eff3SMatthew Dillon  * which may or may not be a port multiplier.
226258223a3SMatthew Dillon  */
2273209f581SMatthew Dillon int
2281980eff3SMatthew Dillon ahci_cam_probe(struct ahci_port *ap, struct ata_port *atx)
229258223a3SMatthew Dillon {
2301980eff3SMatthew Dillon 	struct ata_port	*at;
231258223a3SMatthew Dillon 	struct ata_xfer	*xa;
232258223a3SMatthew Dillon 	u_int64_t	capacity;
233258223a3SMatthew Dillon 	u_int64_t	capacity_bytes;
234258223a3SMatthew Dillon 	int		model_len;
2356e0003adSMatthew Dillon 	int		firmware_len;
2366e0003adSMatthew Dillon 	int		serial_len;
237fd8bd957SMatthew Dillon 	int		error;
238258223a3SMatthew Dillon 	int		devncqdepth;
239258223a3SMatthew Dillon 	int		i;
2406e0003adSMatthew Dillon 	const char	*model_id;
2416e0003adSMatthew Dillon 	const char	*firmware_id;
2426e0003adSMatthew Dillon 	const char	*serial_id;
243669fbbf7SMatthew Dillon 	const char	*wcstr;
244669fbbf7SMatthew Dillon 	const char	*rastr;
245fd8bd957SMatthew Dillon 	const char	*scstr;
246fd8bd957SMatthew Dillon 	const char	*type;
247fd8bd957SMatthew Dillon 
2483209f581SMatthew Dillon 	error = EIO;
2491980eff3SMatthew Dillon 
2501980eff3SMatthew Dillon 	/*
251f4553de1SMatthew Dillon 	 * Delayed CAM attachment for initial probe, sim may be NULL
252f4553de1SMatthew Dillon 	 */
253f4553de1SMatthew Dillon 	if (ap->ap_sim == NULL)
254f4553de1SMatthew Dillon 		return(0);
255f4553de1SMatthew Dillon 
256f4553de1SMatthew Dillon 	/*
2571980eff3SMatthew Dillon 	 * A NULL atx indicates a probe of the directly connected device.
2581980eff3SMatthew Dillon 	 * A non-NULL atx indicates a device connected via a port multiplier.
2591980eff3SMatthew Dillon 	 * We need to preserve atx for calls to ahci_ata_get_xfer().
2601980eff3SMatthew Dillon 	 *
2611980eff3SMatthew Dillon 	 * at is always non-NULL.  For directly connected devices we supply
2621980eff3SMatthew Dillon 	 * an (at) pointing to target 0.
2631980eff3SMatthew Dillon 	 */
2641980eff3SMatthew Dillon 	if (atx == NULL) {
265b012a2caSMatthew Dillon 		at = ap->ap_ata[0];	/* direct attached - device 0 */
2661980eff3SMatthew Dillon 		if (ap->ap_type == ATA_PORT_T_PM) {
267831bc9e3SMatthew Dillon 			kprintf("%s: Found Port Multiplier\n",
268831bc9e3SMatthew Dillon 				ATANAME(ap, atx));
2691980eff3SMatthew Dillon 			return (0);
2701980eff3SMatthew Dillon 		}
2711980eff3SMatthew Dillon 		at->at_type = ap->ap_type;
2721980eff3SMatthew Dillon 	} else {
2733209f581SMatthew Dillon 		at = atx;
2741980eff3SMatthew Dillon 		if (atx->at_type == ATA_PORT_T_PM) {
2751980eff3SMatthew Dillon 			kprintf("%s: Bogus device, reducing port count to %d\n",
2761980eff3SMatthew Dillon 				ATANAME(ap, atx), atx->at_target);
2771980eff3SMatthew Dillon 			if (ap->ap_pmcount > atx->at_target)
2781980eff3SMatthew Dillon 				ap->ap_pmcount = atx->at_target;
2793209f581SMatthew Dillon 			goto err;
2801980eff3SMatthew Dillon 		}
2811980eff3SMatthew Dillon 	}
2823209f581SMatthew Dillon 	if (ap->ap_type == ATA_PORT_T_NONE)
2833209f581SMatthew Dillon 		goto err;
2841980eff3SMatthew Dillon 	if (at->at_type == ATA_PORT_T_NONE)
2853209f581SMatthew Dillon 		goto err;
286258223a3SMatthew Dillon 
287258223a3SMatthew Dillon 	/*
288258223a3SMatthew Dillon 	 * Issue identify, saving the result
289258223a3SMatthew Dillon 	 */
2901980eff3SMatthew Dillon 	xa = ahci_ata_get_xfer(ap, atx);
291258223a3SMatthew Dillon 	xa->complete = ahci_ata_dummy_done;
2921980eff3SMatthew Dillon 	xa->data = &at->at_identify;
2931980eff3SMatthew Dillon 	xa->datalen = sizeof(at->at_identify);
29412feb904SMatthew Dillon 	xa->flags = ATA_F_READ | ATA_F_PIO | ATA_F_POLL;
2951980eff3SMatthew Dillon 	xa->fis->flags = ATA_H2D_FLAGS_CMD | at->at_target;
2961980eff3SMatthew Dillon 
2971980eff3SMatthew Dillon 	switch(at->at_type) {
2981980eff3SMatthew Dillon 	case ATA_PORT_T_DISK:
299258223a3SMatthew Dillon 		xa->fis->command = ATA_C_IDENTIFY;
300fd8bd957SMatthew Dillon 		type = "DISK";
3011980eff3SMatthew Dillon 		break;
3021980eff3SMatthew Dillon 	case ATA_PORT_T_ATAPI:
3031980eff3SMatthew Dillon 		xa->fis->command = ATA_C_ATAPI_IDENTIFY;
30412feb904SMatthew Dillon 		xa->flags |= ATA_F_AUTOSENSE;
3051980eff3SMatthew Dillon 		type = "ATAPI";
3061980eff3SMatthew Dillon 		break;
3071980eff3SMatthew Dillon 	default:
3081980eff3SMatthew Dillon 		xa->fis->command = ATA_C_ATAPI_IDENTIFY;
3091980eff3SMatthew Dillon 		type = "UNKNOWN(ATAPI?)";
3101980eff3SMatthew Dillon 		break;
311fd8bd957SMatthew Dillon 	}
312258223a3SMatthew Dillon 	xa->fis->features = 0;
313258223a3SMatthew Dillon 	xa->fis->device = 0;
3143209f581SMatthew Dillon 	xa->timeout = 1000;
315258223a3SMatthew Dillon 
316831bc9e3SMatthew Dillon 	if (ahci_ata_cmd(xa) != ATA_S_COMPLETE) {
317fd8bd957SMatthew Dillon 		kprintf("%s: Detected %s device but unable to IDENTIFY\n",
3181980eff3SMatthew Dillon 			ATANAME(ap, atx), type);
319258223a3SMatthew Dillon 		ahci_ata_put_xfer(xa);
3203209f581SMatthew Dillon 		goto err;
321258223a3SMatthew Dillon 	}
322258223a3SMatthew Dillon 	ahci_ata_put_xfer(xa);
323258223a3SMatthew Dillon 
3241980eff3SMatthew Dillon 	ata_fix_identify(&at->at_identify);
325258223a3SMatthew Dillon 
326258223a3SMatthew Dillon 	/*
327258223a3SMatthew Dillon 	 * Read capacity using SATA probe info.
328258223a3SMatthew Dillon 	 */
3291980eff3SMatthew Dillon 	if (le16toh(at->at_identify.cmdset83) & 0x0400) {
330258223a3SMatthew Dillon 		/* LBA48 feature set supported */
331258223a3SMatthew Dillon 		capacity = 0;
332258223a3SMatthew Dillon 		for (i = 3; i >= 0; --i) {
333258223a3SMatthew Dillon 			capacity <<= 16;
334258223a3SMatthew Dillon 			capacity +=
3351980eff3SMatthew Dillon 			    le16toh(at->at_identify.addrsecxt[i]);
336258223a3SMatthew Dillon 		}
337258223a3SMatthew Dillon 	} else {
3381980eff3SMatthew Dillon 		capacity = le16toh(at->at_identify.addrsec[1]);
339258223a3SMatthew Dillon 		capacity <<= 16;
3401980eff3SMatthew Dillon 		capacity += le16toh(at->at_identify.addrsec[0]);
341258223a3SMatthew Dillon 	}
34212feb904SMatthew Dillon 	if (capacity == 0)
34312feb904SMatthew Dillon 		capacity = 1024 * 1024 / 512;
3441980eff3SMatthew Dillon 	at->at_capacity = capacity;
3451980eff3SMatthew Dillon 	if (atx == NULL)
3461980eff3SMatthew Dillon 		ap->ap_probe = ATA_PROBE_GOOD;
347258223a3SMatthew Dillon 
348258223a3SMatthew Dillon 	capacity_bytes = capacity * 512;
349258223a3SMatthew Dillon 
350258223a3SMatthew Dillon 	/*
351258223a3SMatthew Dillon 	 * Negotiate NCQ, throw away any ata_xfer's beyond the negotiated
352258223a3SMatthew Dillon 	 * number of slots and limit the number of CAM ccb's to one less
353258223a3SMatthew Dillon 	 * so we always have a slot available for recovery.
354258223a3SMatthew Dillon 	 *
355258223a3SMatthew Dillon 	 * NCQ is not used if ap_ncqdepth is 1 or the host controller does
356258223a3SMatthew Dillon 	 * not support it, and in that case the driver can handle extra
357258223a3SMatthew Dillon 	 * ccb's.
358cec85a37SMatthew Dillon 	 *
3591980eff3SMatthew Dillon 	 * NCQ is currently used only with direct-attached disks.  It is
3601980eff3SMatthew Dillon 	 * not used with port multipliers or direct-attached ATAPI devices.
3611980eff3SMatthew Dillon 	 *
362cec85a37SMatthew Dillon 	 * Remember at least one extra CCB needs to be reserved for the
363cec85a37SMatthew Dillon 	 * error ccb.
364258223a3SMatthew Dillon 	 */
365258223a3SMatthew Dillon 	if ((ap->ap_sc->sc_cap & AHCI_REG_CAP_SNCQ) &&
3661980eff3SMatthew Dillon 	    ap->ap_type == ATA_PORT_T_DISK &&
3671980eff3SMatthew Dillon 	    (le16toh(at->at_identify.satacap) & (1 << 8))) {
3681980eff3SMatthew Dillon 		at->at_ncqdepth = (le16toh(at->at_identify.qdepth) & 0x1F) + 1;
3691980eff3SMatthew Dillon 		devncqdepth = at->at_ncqdepth;
3701980eff3SMatthew Dillon 		if (at->at_ncqdepth > ap->ap_sc->sc_ncmds)
3711980eff3SMatthew Dillon 			at->at_ncqdepth = ap->ap_sc->sc_ncmds;
3721980eff3SMatthew Dillon 		if (at->at_ncqdepth > 1) {
373258223a3SMatthew Dillon 			for (i = 0; i < ap->ap_sc->sc_ncmds; ++i) {
3741980eff3SMatthew Dillon 				xa = ahci_ata_get_xfer(ap, atx);
3751980eff3SMatthew Dillon 				if (xa->tag < at->at_ncqdepth) {
376258223a3SMatthew Dillon 					xa->state = ATA_S_COMPLETE;
377258223a3SMatthew Dillon 					ahci_ata_put_xfer(xa);
378258223a3SMatthew Dillon 				}
379258223a3SMatthew Dillon 			}
3801980eff3SMatthew Dillon 			if (at->at_ncqdepth >= ap->ap_sc->sc_ncmds) {
381949597c2SMatthew Dillon 				cam_sim_set_max_tags(ap->ap_sim,
3821980eff3SMatthew Dillon 						     at->at_ncqdepth - 1);
383258223a3SMatthew Dillon 			}
384cec85a37SMatthew Dillon 		}
385258223a3SMatthew Dillon 	} else {
386258223a3SMatthew Dillon 		devncqdepth = 0;
387258223a3SMatthew Dillon 	}
388258223a3SMatthew Dillon 
3896e0003adSMatthew Dillon 	model_len = sizeof(at->at_identify.model);
3906e0003adSMatthew Dillon 	model_id = at->at_identify.model;
3916e0003adSMatthew Dillon 	ahci_strip_string(&model_id, &model_len);
3926e0003adSMatthew Dillon 
3936e0003adSMatthew Dillon 	firmware_len = sizeof(at->at_identify.firmware);
3946e0003adSMatthew Dillon 	firmware_id = at->at_identify.firmware;
3956e0003adSMatthew Dillon 	ahci_strip_string(&firmware_id, &firmware_len);
3966e0003adSMatthew Dillon 
3976e0003adSMatthew Dillon 	serial_len = sizeof(at->at_identify.serial);
3986e0003adSMatthew Dillon 	serial_id = at->at_identify.serial;
3996e0003adSMatthew Dillon 	ahci_strip_string(&serial_id, &serial_len);
400669fbbf7SMatthew Dillon 
401fd8bd957SMatthew Dillon 	/*
402fd8bd957SMatthew Dillon 	 * Generate informatiive strings.
403fd8bd957SMatthew Dillon 	 *
404fd8bd957SMatthew Dillon 	 * NOTE: We do not automatically set write caching, lookahead,
405fd8bd957SMatthew Dillon 	 *	 or the security state for ATAPI devices.
406fd8bd957SMatthew Dillon 	 */
4071980eff3SMatthew Dillon 	if (at->at_identify.cmdset82 & ATA_IDENTIFY_WRITECACHE) {
4081980eff3SMatthew Dillon 		if (at->at_identify.features85 & ATA_IDENTIFY_WRITECACHE)
409669fbbf7SMatthew Dillon 			wcstr = "enabled";
4101980eff3SMatthew Dillon 		else if (at->at_type == ATA_PORT_T_ATAPI)
411fd8bd957SMatthew Dillon 			wcstr = "disabled";
412669fbbf7SMatthew Dillon 		else
413669fbbf7SMatthew Dillon 			wcstr = "enabling";
414669fbbf7SMatthew Dillon 	} else {
415669fbbf7SMatthew Dillon 		    wcstr = "notsupp";
416669fbbf7SMatthew Dillon 	}
417669fbbf7SMatthew Dillon 
4181980eff3SMatthew Dillon 	if (at->at_identify.cmdset82 & ATA_IDENTIFY_LOOKAHEAD) {
4191980eff3SMatthew Dillon 		if (at->at_identify.features85 & ATA_IDENTIFY_LOOKAHEAD)
420669fbbf7SMatthew Dillon 			rastr = "enabled";
4211980eff3SMatthew Dillon 		else if (at->at_type == ATA_PORT_T_ATAPI)
422fd8bd957SMatthew Dillon 			rastr = "disabled";
423669fbbf7SMatthew Dillon 		else
424669fbbf7SMatthew Dillon 			rastr = "enabling";
425669fbbf7SMatthew Dillon 	} else {
426669fbbf7SMatthew Dillon 		    rastr = "notsupp";
427669fbbf7SMatthew Dillon 	}
428669fbbf7SMatthew Dillon 
4291980eff3SMatthew Dillon 	if (at->at_identify.cmdset82 & ATA_IDENTIFY_SECURITY) {
4301980eff3SMatthew Dillon 		if (at->at_identify.securestatus & ATA_SECURE_FROZEN)
431fd8bd957SMatthew Dillon 			scstr = "frozen";
4321980eff3SMatthew Dillon 		else if (at->at_type == ATA_PORT_T_ATAPI)
433fd8bd957SMatthew Dillon 			scstr = "unfrozen";
434afa796d2SMatthew Dillon 		else if (AhciNoFeatures & (1 << ap->ap_num))
435afa796d2SMatthew Dillon 			scstr = "<disabled>";
436fd8bd957SMatthew Dillon 		else
437fd8bd957SMatthew Dillon 			scstr = "freezing";
438fd8bd957SMatthew Dillon 	} else {
439fd8bd957SMatthew Dillon 		    scstr = "notsupp";
440fd8bd957SMatthew Dillon 	}
441fd8bd957SMatthew Dillon 
4426e0003adSMatthew Dillon 	kprintf("%s: Found %s \"%*.*s %*.*s\" serial=\"%*.*s\"\n"
443074579dfSMatthew Dillon 		"%s: tags=%d/%d satacap=%04x satafea=%04x NCQ=%s "
444074579dfSMatthew Dillon 		"capacity=%lld.%02dMB\n",
445074579dfSMatthew Dillon 
4461980eff3SMatthew Dillon 		ATANAME(ap, atx),
447fd8bd957SMatthew Dillon 		type,
4486e0003adSMatthew Dillon 		model_len, model_len, model_id,
4496e0003adSMatthew Dillon 		firmware_len, firmware_len, firmware_id,
4506e0003adSMatthew Dillon 		serial_len, serial_len, serial_id,
451258223a3SMatthew Dillon 
4521980eff3SMatthew Dillon 		ATANAME(ap, atx),
453258223a3SMatthew Dillon 		devncqdepth, ap->ap_sc->sc_ncmds,
4541980eff3SMatthew Dillon 		at->at_identify.satacap,
4551980eff3SMatthew Dillon 		at->at_identify.satafsup,
456074579dfSMatthew Dillon 		(at->at_ncqdepth > 1 ? "YES" : "NO"),
457258223a3SMatthew Dillon 		(long long)capacity_bytes / (1024 * 1024),
458074579dfSMatthew Dillon 		(int)(capacity_bytes % (1024 * 1024)) * 100 / (1024 * 1024)
459074579dfSMatthew Dillon 	);
460074579dfSMatthew Dillon 	kprintf("%s: f85=%04x f86=%04x f87=%04x WC=%s RA=%s SEC=%s\n",
4611980eff3SMatthew Dillon 		ATANAME(ap, atx),
4621980eff3SMatthew Dillon 		at->at_identify.features85,
4631980eff3SMatthew Dillon 		at->at_identify.features86,
4641980eff3SMatthew Dillon 		at->at_identify.features87,
465669fbbf7SMatthew Dillon 		wcstr,
466fd8bd957SMatthew Dillon 		rastr,
467fd8bd957SMatthew Dillon 		scstr
468258223a3SMatthew Dillon 	);
469258223a3SMatthew Dillon 
470258223a3SMatthew Dillon 	/*
471fd8bd957SMatthew Dillon 	 * Additional type-specific probing
472fd8bd957SMatthew Dillon 	 */
4731980eff3SMatthew Dillon 	switch(at->at_type) {
474fd8bd957SMatthew Dillon 	case ATA_PORT_T_DISK:
4751980eff3SMatthew Dillon 		error = ahci_cam_probe_disk(ap, atx);
4761980eff3SMatthew Dillon 		break;
4771980eff3SMatthew Dillon 	case ATA_PORT_T_ATAPI:
4781980eff3SMatthew Dillon 		error = ahci_cam_probe_atapi(ap, atx);
479fd8bd957SMatthew Dillon 		break;
480fd8bd957SMatthew Dillon 	default:
4811980eff3SMatthew Dillon 		error = EIO;
482fd8bd957SMatthew Dillon 		break;
483fd8bd957SMatthew Dillon 	}
4843209f581SMatthew Dillon err:
4853209f581SMatthew Dillon 	if (error) {
4863209f581SMatthew Dillon 		at->at_probe = ATA_PROBE_FAILED;
4873209f581SMatthew Dillon 		if (atx == NULL)
4883209f581SMatthew Dillon 			ap->ap_probe = at->at_probe;
4893209f581SMatthew Dillon 	} else {
4903209f581SMatthew Dillon 		at->at_probe = ATA_PROBE_GOOD;
4913209f581SMatthew Dillon 		if (atx == NULL)
4923209f581SMatthew Dillon 			ap->ap_probe = at->at_probe;
4933209f581SMatthew Dillon 	}
4943209f581SMatthew Dillon 	return (error);
495fd8bd957SMatthew Dillon }
496fd8bd957SMatthew Dillon 
497fd8bd957SMatthew Dillon /*
498fd8bd957SMatthew Dillon  * DISK-specific probe after initial ident
499fd8bd957SMatthew Dillon  */
500fd8bd957SMatthew Dillon static int
5011980eff3SMatthew Dillon ahci_cam_probe_disk(struct ahci_port *ap, struct ata_port *atx)
502fd8bd957SMatthew Dillon {
5031980eff3SMatthew Dillon 	struct ata_port *at;
504fd8bd957SMatthew Dillon 	struct ata_xfer	*xa;
505fd8bd957SMatthew Dillon 
506b012a2caSMatthew Dillon 	at = atx ? atx : ap->ap_ata[0];
5071980eff3SMatthew Dillon 
508fd8bd957SMatthew Dillon 	/*
50969b3741eSMatthew Dillon 	 * Set dummy xfer mode
51069b3741eSMatthew Dillon 	 */
51169b3741eSMatthew Dillon 	ahci_set_xfer(ap, atx);
51269b3741eSMatthew Dillon 
51369b3741eSMatthew Dillon 	/*
514258223a3SMatthew Dillon 	 * Enable write cache if supported
515fd8bd957SMatthew Dillon 	 *
516fd8bd957SMatthew Dillon 	 * NOTE: "WD My Book" external disk devices have a very poor
517fd8bd957SMatthew Dillon 	 *	 daughter board between the the ESATA and the HD.  Sending
518fd8bd957SMatthew Dillon 	 *	 any ATA_C_SET_FEATURES commands will break the hardware port
519fd8bd957SMatthew Dillon 	 *	 with a fatal protocol error.  However, this device also
520fd8bd957SMatthew Dillon 	 *	 indicates that WRITECACHE is already on and READAHEAD is
521fd8bd957SMatthew Dillon 	 *	 not supported so we avoid the issue.
522258223a3SMatthew Dillon 	 */
5231980eff3SMatthew Dillon 	if ((at->at_identify.cmdset82 & ATA_IDENTIFY_WRITECACHE) &&
5241980eff3SMatthew Dillon 	    (at->at_identify.features85 & ATA_IDENTIFY_WRITECACHE) == 0) {
5251980eff3SMatthew Dillon 		xa = ahci_ata_get_xfer(ap, atx);
526258223a3SMatthew Dillon 		xa->complete = ahci_ata_dummy_done;
527258223a3SMatthew Dillon 		xa->fis->command = ATA_C_SET_FEATURES;
528b012a2caSMatthew Dillon 		xa->fis->features = ATA_SF_WRITECACHE_EN;
529b012a2caSMatthew Dillon 		/* xa->fis->features = ATA_SF_LOOKAHEAD_EN; */
5301980eff3SMatthew Dillon 		xa->fis->flags = ATA_H2D_FLAGS_CMD | at->at_target;
531669fbbf7SMatthew Dillon 		xa->fis->device = 0;
532b012a2caSMatthew Dillon 		xa->flags = ATA_F_PIO | ATA_F_POLL;
5333209f581SMatthew Dillon 		xa->timeout = 1000;
534669fbbf7SMatthew Dillon 		xa->datalen = 0;
535831bc9e3SMatthew Dillon 		if (ahci_ata_cmd(xa) == ATA_S_COMPLETE)
5361980eff3SMatthew Dillon 			at->at_features |= ATA_PORT_F_WCACHE;
537afa796d2SMatthew Dillon 		else
538afa796d2SMatthew Dillon 			kprintf("%s: Unable to enable write-caching\n",
539afa796d2SMatthew Dillon 				ATANAME(ap, atx));
540258223a3SMatthew Dillon 		ahci_ata_put_xfer(xa);
541258223a3SMatthew Dillon 	}
542258223a3SMatthew Dillon 
543258223a3SMatthew Dillon 	/*
544258223a3SMatthew Dillon 	 * Enable readahead if supported
545258223a3SMatthew Dillon 	 */
5461980eff3SMatthew Dillon 	if ((at->at_identify.cmdset82 & ATA_IDENTIFY_LOOKAHEAD) &&
5471980eff3SMatthew Dillon 	    (at->at_identify.features85 & ATA_IDENTIFY_LOOKAHEAD) == 0) {
5481980eff3SMatthew Dillon 		xa = ahci_ata_get_xfer(ap, atx);
549258223a3SMatthew Dillon 		xa->complete = ahci_ata_dummy_done;
550258223a3SMatthew Dillon 		xa->fis->command = ATA_C_SET_FEATURES;
551258223a3SMatthew Dillon 		xa->fis->features = ATA_SF_LOOKAHEAD_EN;
5521980eff3SMatthew Dillon 		xa->fis->flags = ATA_H2D_FLAGS_CMD | at->at_target;
553669fbbf7SMatthew Dillon 		xa->fis->device = 0;
554b012a2caSMatthew Dillon 		xa->flags = ATA_F_PIO | ATA_F_POLL;
5553209f581SMatthew Dillon 		xa->timeout = 1000;
556669fbbf7SMatthew Dillon 		xa->datalen = 0;
557831bc9e3SMatthew Dillon 		if (ahci_ata_cmd(xa) == ATA_S_COMPLETE)
5581980eff3SMatthew Dillon 			at->at_features |= ATA_PORT_F_RAHEAD;
559afa796d2SMatthew Dillon 		else
560afa796d2SMatthew Dillon 			kprintf("%s: Unable to enable read-ahead\n",
561afa796d2SMatthew Dillon 				ATANAME(ap, atx));
562258223a3SMatthew Dillon 		ahci_ata_put_xfer(xa);
563258223a3SMatthew Dillon 	}
564258223a3SMatthew Dillon 
565258223a3SMatthew Dillon 	/*
566258223a3SMatthew Dillon 	 * FREEZE LOCK the device so malicious users can't lock it on us.
567258223a3SMatthew Dillon 	 * As there is no harm in issuing this to devices that don't
568258223a3SMatthew Dillon 	 * support the security feature set we just send it, and don't bother
569258223a3SMatthew Dillon 	 * checking if the device sends a command abort to tell us it doesn't
570258223a3SMatthew Dillon 	 * support it
571258223a3SMatthew Dillon 	 */
5721980eff3SMatthew Dillon 	if ((at->at_identify.cmdset82 & ATA_IDENTIFY_SECURITY) &&
573afa796d2SMatthew Dillon 	    (at->at_identify.securestatus & ATA_SECURE_FROZEN) == 0 &&
574afa796d2SMatthew Dillon 	    (AhciNoFeatures & (1 << ap->ap_num)) == 0) {
5751980eff3SMatthew Dillon 		xa = ahci_ata_get_xfer(ap, atx);
576258223a3SMatthew Dillon 		xa->complete = ahci_ata_dummy_done;
577258223a3SMatthew Dillon 		xa->fis->command = ATA_C_SEC_FREEZE_LOCK;
5781980eff3SMatthew Dillon 		xa->fis->flags = ATA_H2D_FLAGS_CMD | at->at_target;
579b012a2caSMatthew Dillon 		xa->flags = ATA_F_PIO | ATA_F_POLL;
5803209f581SMatthew Dillon 		xa->timeout = 1000;
581669fbbf7SMatthew Dillon 		xa->datalen = 0;
582831bc9e3SMatthew Dillon 		if (ahci_ata_cmd(xa) == ATA_S_COMPLETE)
5831980eff3SMatthew Dillon 			at->at_features |= ATA_PORT_F_FRZLCK;
584afa796d2SMatthew Dillon 		else
585afa796d2SMatthew Dillon 			kprintf("%s: Unable to set security freeze\n",
586afa796d2SMatthew Dillon 				ATANAME(ap, atx));
587258223a3SMatthew Dillon 		ahci_ata_put_xfer(xa);
588669fbbf7SMatthew Dillon 	}
589258223a3SMatthew Dillon 
590b4189e5eSMatthew Dillon 	return (0);
591b4189e5eSMatthew Dillon }
592b4189e5eSMatthew Dillon 
593fd8bd957SMatthew Dillon /*
594fd8bd957SMatthew Dillon  * ATAPI-specific probe after initial ident
595fd8bd957SMatthew Dillon  */
596b4189e5eSMatthew Dillon static int
5971980eff3SMatthew Dillon ahci_cam_probe_atapi(struct ahci_port *ap, struct ata_port *atx)
598b4189e5eSMatthew Dillon {
59969b3741eSMatthew Dillon 	ahci_set_xfer(ap, atx);
60069b3741eSMatthew Dillon 	return(0);
60169b3741eSMatthew Dillon }
60269b3741eSMatthew Dillon 
60369b3741eSMatthew Dillon /*
60469b3741eSMatthew Dillon  * Setting the transfer mode is irrelevant for the SATA transport
60569b3741eSMatthew Dillon  * but some (atapi) devices seem to need it anyway.  In addition
60669b3741eSMatthew Dillon  * if we are running through a SATA->PATA converter for some reason
60769b3741eSMatthew Dillon  * beyond my comprehension we might have to set the mode.
608d15a49f2SMatthew Dillon  *
609d15a49f2SMatthew Dillon  * We only support DMA modes for SATA attached devices, so don't bother
610d15a49f2SMatthew Dillon  * with legacy modes.
61169b3741eSMatthew Dillon  */
61269b3741eSMatthew Dillon static int
61369b3741eSMatthew Dillon ahci_set_xfer(struct ahci_port *ap, struct ata_port *atx)
61469b3741eSMatthew Dillon {
61569b3741eSMatthew Dillon 	struct ata_port *at;
61669b3741eSMatthew Dillon 	struct ata_xfer	*xa;
61769b3741eSMatthew Dillon 	u_int16_t mode;
618d15a49f2SMatthew Dillon 	u_int16_t mask;
61969b3741eSMatthew Dillon 
62069b3741eSMatthew Dillon 	at = atx ? atx : ap->ap_ata[0];
62169b3741eSMatthew Dillon 
62269b3741eSMatthew Dillon 	/*
623d15a49f2SMatthew Dillon 	 * Figure out the supported UDMA mode.  Ignore other legacy modes.
62469b3741eSMatthew Dillon 	 */
625d15a49f2SMatthew Dillon 	mask = le16toh(at->at_identify.ultradma);
626d15a49f2SMatthew Dillon 	if ((mask & 0xFF) == 0 || mask == 0xFFFF)
62769b3741eSMatthew Dillon 		return(0);
628d15a49f2SMatthew Dillon 	mask &= 0xFF;
629d15a49f2SMatthew Dillon 	mode = 0x4F;
630d15a49f2SMatthew Dillon 	while ((mask & 0x8000) == 0) {
631d15a49f2SMatthew Dillon 		mask <<= 1;
632d15a49f2SMatthew Dillon 		--mode;
633d15a49f2SMatthew Dillon 	}
63469b3741eSMatthew Dillon 
63569b3741eSMatthew Dillon 	/*
63669b3741eSMatthew Dillon 	 * SATA atapi devices often still report a dma mode, even though
63769b3741eSMatthew Dillon 	 * it is irrelevant for SATA transport.  It is also possible that
63869b3741eSMatthew Dillon 	 * we are running through a SATA->PATA converter and seeing the
63969b3741eSMatthew Dillon 	 * PATA dma mode.
64069b3741eSMatthew Dillon 	 *
64169b3741eSMatthew Dillon 	 * In this case the device may require a (dummy) SETXFER to be
64269b3741eSMatthew Dillon 	 * sent before it will work properly.
64369b3741eSMatthew Dillon 	 */
64469b3741eSMatthew Dillon 	xa = ahci_ata_get_xfer(ap, atx);
64569b3741eSMatthew Dillon 	xa->complete = ahci_ata_dummy_done;
64669b3741eSMatthew Dillon 	xa->fis->command = ATA_C_SET_FEATURES;
64769b3741eSMatthew Dillon 	xa->fis->features = ATA_SF_SETXFER;
64869b3741eSMatthew Dillon 	xa->fis->flags = ATA_H2D_FLAGS_CMD | at->at_target;
649d15a49f2SMatthew Dillon 	xa->fis->sector_count = mode;
65069b3741eSMatthew Dillon 	xa->flags = ATA_F_PIO | ATA_F_POLL;
65169b3741eSMatthew Dillon 	xa->timeout = 1000;
65269b3741eSMatthew Dillon 	xa->datalen = 0;
65369b3741eSMatthew Dillon 	if (ahci_ata_cmd(xa) != ATA_S_COMPLETE) {
65469b3741eSMatthew Dillon 		kprintf("%s: Unable to set dummy xfer mode \n",
65569b3741eSMatthew Dillon 			ATANAME(ap, atx));
65669b3741eSMatthew Dillon 	} else if (bootverbose) {
65769b3741eSMatthew Dillon 		kprintf("%s: Set dummy xfer mode to %02x\n",
658d15a49f2SMatthew Dillon 			ATANAME(ap, atx), mode);
65969b3741eSMatthew Dillon 	}
66069b3741eSMatthew Dillon 	ahci_ata_put_xfer(xa);
661fd8bd957SMatthew Dillon 	return(0);
662fd8bd957SMatthew Dillon }
663fd8bd957SMatthew Dillon 
664b4189e5eSMatthew Dillon /*
665b4189e5eSMatthew Dillon  * Fix byte ordering so buffers can be accessed as
666b4189e5eSMatthew Dillon  * strings.
667b4189e5eSMatthew Dillon  */
668258223a3SMatthew Dillon static void
669258223a3SMatthew Dillon ata_fix_identify(struct ata_identify *id)
670258223a3SMatthew Dillon {
671258223a3SMatthew Dillon 	u_int16_t	*swap;
672258223a3SMatthew Dillon 	int		i;
673258223a3SMatthew Dillon 
674258223a3SMatthew Dillon 	swap = (u_int16_t *)id->serial;
675258223a3SMatthew Dillon 	for (i = 0; i < sizeof(id->serial) / sizeof(u_int16_t); i++)
676258223a3SMatthew Dillon 		swap[i] = bswap16(swap[i]);
677258223a3SMatthew Dillon 
678258223a3SMatthew Dillon 	swap = (u_int16_t *)id->firmware;
679258223a3SMatthew Dillon 	for (i = 0; i < sizeof(id->firmware) / sizeof(u_int16_t); i++)
680258223a3SMatthew Dillon 		swap[i] = bswap16(swap[i]);
681258223a3SMatthew Dillon 
682258223a3SMatthew Dillon 	swap = (u_int16_t *)id->model;
683258223a3SMatthew Dillon 	for (i = 0; i < sizeof(id->model) / sizeof(u_int16_t); i++)
684258223a3SMatthew Dillon 		swap[i] = bswap16(swap[i]);
685258223a3SMatthew Dillon }
686258223a3SMatthew Dillon 
687258223a3SMatthew Dillon /*
688b4189e5eSMatthew Dillon  * Dummy done callback for xa.
689b4189e5eSMatthew Dillon  */
690b4189e5eSMatthew Dillon static void
691b4189e5eSMatthew Dillon ahci_ata_dummy_done(struct ata_xfer *xa)
692b4189e5eSMatthew Dillon {
693b4189e5eSMatthew Dillon }
694b4189e5eSMatthew Dillon 
695b4189e5eSMatthew Dillon /*
6963209f581SMatthew Dillon  * Use an engineering request to initiate a target scan for devices
6973209f581SMatthew Dillon  * behind a port multiplier.
698fd8bd957SMatthew Dillon  *
6993209f581SMatthew Dillon  * An asynchronous bus scan is used to avoid reentrancy issues.
700258223a3SMatthew Dillon  */
701258223a3SMatthew Dillon static void
702258223a3SMatthew Dillon ahci_cam_rescan_callback(struct cam_periph *periph, union ccb *ccb)
703258223a3SMatthew Dillon {
7043209f581SMatthew Dillon 	struct ahci_port *ap = ccb->ccb_h.sim_priv.entries[0].ptr;
7053209f581SMatthew Dillon 
706f4553de1SMatthew Dillon 	if (ccb->ccb_h.func_code == XPT_SCAN_BUS) {
7073209f581SMatthew Dillon 		ap->ap_flags &= ~AP_F_SCAN_RUNNING;
7083209f581SMatthew Dillon 		if (ap->ap_flags & AP_F_SCAN_REQUESTED) {
7093209f581SMatthew Dillon 			ap->ap_flags &= ~AP_F_SCAN_REQUESTED;
7103209f581SMatthew Dillon 			ahci_cam_rescan(ap);
7113209f581SMatthew Dillon 		}
712f4553de1SMatthew Dillon 		ap->ap_flags |= AP_F_SCAN_COMPLETED;
713f4553de1SMatthew Dillon 		wakeup(&ap->ap_flags);
714f4553de1SMatthew Dillon 	}
715f4553de1SMatthew Dillon 	xpt_free_ccb(ccb);
716258223a3SMatthew Dillon }
717258223a3SMatthew Dillon 
718258223a3SMatthew Dillon static void
719258223a3SMatthew Dillon ahci_cam_rescan(struct ahci_port *ap)
720258223a3SMatthew Dillon {
721258223a3SMatthew Dillon 	struct cam_path *path;
722258223a3SMatthew Dillon 	union ccb *ccb;
723258223a3SMatthew Dillon 	int status;
7243209f581SMatthew Dillon 	int i;
7253209f581SMatthew Dillon 
7263209f581SMatthew Dillon 	if (ap->ap_flags & AP_F_SCAN_RUNNING) {
7273209f581SMatthew Dillon 		ap->ap_flags |= AP_F_SCAN_REQUESTED;
7283209f581SMatthew Dillon 		return;
7293209f581SMatthew Dillon 	}
7303209f581SMatthew Dillon 	ap->ap_flags |= AP_F_SCAN_RUNNING;
7313209f581SMatthew Dillon 	for (i = 0; i < AHCI_MAX_PMPORTS; ++i) {
732b012a2caSMatthew Dillon 		ap->ap_ata[i]->at_features |= ATA_PORT_F_RESCAN;
7333209f581SMatthew Dillon 	}
734258223a3SMatthew Dillon 
735258223a3SMatthew Dillon 	status = xpt_create_path(&path, xpt_periph, cam_sim_path(ap->ap_sim),
736258223a3SMatthew Dillon 				 CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD);
737258223a3SMatthew Dillon 	if (status != CAM_REQ_CMP)
738258223a3SMatthew Dillon 		return;
739258223a3SMatthew Dillon 
740f4553de1SMatthew Dillon 	ccb = xpt_alloc_ccb();
741258223a3SMatthew Dillon 	xpt_setup_ccb(&ccb->ccb_h, path, 5);	/* 5 = low priority */
7422de5e9baSMatthew Dillon 	ccb->ccb_h.func_code = XPT_ENG_EXEC;
743258223a3SMatthew Dillon 	ccb->ccb_h.cbfcnp = ahci_cam_rescan_callback;
7443209f581SMatthew Dillon 	ccb->ccb_h.sim_priv.entries[0].ptr = ap;
745258223a3SMatthew Dillon 	ccb->crcn.flags = CAM_FLAG_NONE;
7462de5e9baSMatthew Dillon 	xpt_action_async(ccb);
747258223a3SMatthew Dillon }
748258223a3SMatthew Dillon 
7493209f581SMatthew Dillon static void
7503209f581SMatthew Dillon ahci_xpt_rescan(struct ahci_port *ap)
7513209f581SMatthew Dillon {
7523209f581SMatthew Dillon 	struct cam_path *path;
7533209f581SMatthew Dillon 	union ccb *ccb;
7543209f581SMatthew Dillon 	int status;
7553209f581SMatthew Dillon 
7563209f581SMatthew Dillon 	status = xpt_create_path(&path, xpt_periph, cam_sim_path(ap->ap_sim),
7573209f581SMatthew Dillon 				 CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD);
7583209f581SMatthew Dillon 	if (status != CAM_REQ_CMP)
7593209f581SMatthew Dillon 		return;
760f4553de1SMatthew Dillon 
761f4553de1SMatthew Dillon 	ccb = xpt_alloc_ccb();
7623209f581SMatthew Dillon 	xpt_setup_ccb(&ccb->ccb_h, path, 5);	/* 5 = low priority */
7632de5e9baSMatthew Dillon 	ccb->ccb_h.func_code = XPT_SCAN_BUS;
7643209f581SMatthew Dillon 	ccb->ccb_h.cbfcnp = ahci_cam_rescan_callback;
7653209f581SMatthew Dillon 	ccb->ccb_h.sim_priv.entries[0].ptr = ap;
7663209f581SMatthew Dillon 	ccb->crcn.flags = CAM_FLAG_NONE;
767baef7501SMatthew Dillon 	xpt_action_async(ccb);
7683209f581SMatthew Dillon }
7693209f581SMatthew Dillon 
770258223a3SMatthew Dillon /*
771258223a3SMatthew Dillon  * Action function - dispatch command
772258223a3SMatthew Dillon  */
773258223a3SMatthew Dillon static
774258223a3SMatthew Dillon void
775258223a3SMatthew Dillon ahci_xpt_action(struct cam_sim *sim, union ccb *ccb)
776258223a3SMatthew Dillon {
777258223a3SMatthew Dillon 	struct ahci_port *ap;
7781980eff3SMatthew Dillon 	struct ata_port	 *at, *atx;
779258223a3SMatthew Dillon 	struct ccb_hdr *ccbh;
780258223a3SMatthew Dillon 	int unit;
781258223a3SMatthew Dillon 
782258223a3SMatthew Dillon 	/* XXX lock */
783258223a3SMatthew Dillon 	ap = cam_sim_softc(sim);
7841980eff3SMatthew Dillon 	atx = NULL;
785258223a3SMatthew Dillon 	KKASSERT(ap != NULL);
786258223a3SMatthew Dillon 	ccbh = &ccb->ccb_h;
787258223a3SMatthew Dillon 	unit = cam_sim_unit(sim);
788258223a3SMatthew Dillon 
789258223a3SMatthew Dillon 	/*
7903209f581SMatthew Dillon 	 * Early failure checks.  These checks do not apply to XPT_PATH_INQ,
7913209f581SMatthew Dillon 	 * otherwise the bus rescan will not remove the dead devices when
7923209f581SMatthew Dillon 	 * unplugging a PM.
7933209f581SMatthew Dillon 	 *
7941980eff3SMatthew Dillon 	 * For non-wildcards we have one target (0) and one lun (0),
7951980eff3SMatthew Dillon 	 * unless we have a port multiplier.
7961980eff3SMatthew Dillon 	 *
7971980eff3SMatthew Dillon 	 * A wildcard target indicates only the general bus is being
7981980eff3SMatthew Dillon 	 * probed.
7991980eff3SMatthew Dillon 	 *
8001980eff3SMatthew Dillon 	 * Calculate at and atx.  at is always non-NULL.  atx is only
8011980eff3SMatthew Dillon 	 * non-NULL for direct-attached devices.  It will be NULL for
8021980eff3SMatthew Dillon 	 * devices behind a port multiplier.
803258223a3SMatthew Dillon 	 *
804258223a3SMatthew Dillon 	 * XXX What do we do with a LUN wildcard?
805258223a3SMatthew Dillon 	 */
8063209f581SMatthew Dillon 	if (ccbh->target_id != CAM_TARGET_WILDCARD &&
8073209f581SMatthew Dillon 	    ccbh->func_code != XPT_PATH_INQ) {
8081980eff3SMatthew Dillon 		if (ap->ap_type == ATA_PORT_T_NONE) {
8093209f581SMatthew Dillon 			ccbh->status = CAM_DEV_NOT_THERE;
810258223a3SMatthew Dillon 			xpt_done(ccb);
811258223a3SMatthew Dillon 			return;
812258223a3SMatthew Dillon 		}
8131980eff3SMatthew Dillon 		if (ccbh->target_id < 0 || ccbh->target_id >= ap->ap_pmcount) {
814258223a3SMatthew Dillon 			ccbh->status = CAM_DEV_NOT_THERE;
815258223a3SMatthew Dillon 			xpt_done(ccb);
816258223a3SMatthew Dillon 			return;
817258223a3SMatthew Dillon 		}
818b012a2caSMatthew Dillon 		at = ap->ap_ata[ccbh->target_id];
8191980eff3SMatthew Dillon 		if (ap->ap_type == ATA_PORT_T_PM)
8201980eff3SMatthew Dillon 			atx = at;
8211980eff3SMatthew Dillon 
822258223a3SMatthew Dillon 		if (ccbh->target_lun != CAM_LUN_WILDCARD && ccbh->target_lun) {
823258223a3SMatthew Dillon 			ccbh->status = CAM_DEV_NOT_THERE;
824258223a3SMatthew Dillon 			xpt_done(ccb);
825258223a3SMatthew Dillon 			return;
826258223a3SMatthew Dillon 		}
827b012a2caSMatthew Dillon 	} else {
828b012a2caSMatthew Dillon 		at = ap->ap_ata[0];
829258223a3SMatthew Dillon 	}
830258223a3SMatthew Dillon 
831258223a3SMatthew Dillon 	/*
832258223a3SMatthew Dillon 	 * Switch on the meta XPT command
833258223a3SMatthew Dillon 	 */
834258223a3SMatthew Dillon 	switch(ccbh->func_code) {
8353209f581SMatthew Dillon 	case XPT_ENG_EXEC:
8363209f581SMatthew Dillon 		/*
8373209f581SMatthew Dillon 		 * This routine is called after a port multiplier has been
8383209f581SMatthew Dillon 		 * probed.
8393209f581SMatthew Dillon 		 */
8403209f581SMatthew Dillon 		ccbh->status = CAM_REQ_CMP;
841f4553de1SMatthew Dillon 		ahci_os_lock_port(ap);
842831bc9e3SMatthew Dillon 		ahci_port_state_machine(ap, 0);
843f4553de1SMatthew Dillon 		ahci_os_unlock_port(ap);
8443209f581SMatthew Dillon 		xpt_done(ccb);
8453209f581SMatthew Dillon 		ahci_xpt_rescan(ap);
8463209f581SMatthew Dillon 		break;
847258223a3SMatthew Dillon 	case XPT_PATH_INQ:
8483209f581SMatthew Dillon 		/*
8493209f581SMatthew Dillon 		 * This command always succeeds, otherwise the bus scan
8503209f581SMatthew Dillon 		 * will not detach dead devices.
8513209f581SMatthew Dillon 		 */
852258223a3SMatthew Dillon 		ccb->cpi.version_num = 1;
853258223a3SMatthew Dillon 		ccb->cpi.hba_inquiry = 0;
854258223a3SMatthew Dillon 		ccb->cpi.target_sprt = 0;
8551980eff3SMatthew Dillon 		ccb->cpi.hba_misc = PIM_SEQSCAN;
856258223a3SMatthew Dillon 		ccb->cpi.hba_eng_cnt = 0;
857258223a3SMatthew Dillon 		bzero(ccb->cpi.vuhba_flags, sizeof(ccb->cpi.vuhba_flags));
85876497a9cSMatthew Dillon 		ccb->cpi.max_target = AHCI_MAX_PMPORTS - 1;
859258223a3SMatthew Dillon 		ccb->cpi.max_lun = 0;
860258223a3SMatthew Dillon 		ccb->cpi.async_flags = 0;
861258223a3SMatthew Dillon 		ccb->cpi.hpath_id = 0;
8621980eff3SMatthew Dillon 		ccb->cpi.initiator_id = AHCI_MAX_PMPORTS - 1;
863258223a3SMatthew Dillon 		ccb->cpi.unit_number = cam_sim_unit(sim);
864258223a3SMatthew Dillon 		ccb->cpi.bus_id = cam_sim_bus(sim);
865258223a3SMatthew Dillon 		ccb->cpi.base_transfer_speed = 150000;
8662cc2e845SMatthew Dillon 		ccb->cpi.transport = XPORT_SATA;
867258223a3SMatthew Dillon 		ccb->cpi.transport_version = 1;
868258223a3SMatthew Dillon 		ccb->cpi.protocol = PROTO_SCSI;
869258223a3SMatthew Dillon 		ccb->cpi.protocol_version = SCSI_REV_2;
870258223a3SMatthew Dillon 
8713209f581SMatthew Dillon 		ccbh->status = CAM_REQ_CMP;
872831bc9e3SMatthew Dillon 		if (ccbh->target_id == CAM_TARGET_WILDCARD) {
873831bc9e3SMatthew Dillon 			ahci_os_lock_port(ap);
874831bc9e3SMatthew Dillon 			ahci_port_state_machine(ap, 0);
875831bc9e3SMatthew Dillon 			ahci_os_unlock_port(ap);
876831bc9e3SMatthew Dillon 		} else {
877258223a3SMatthew Dillon 			switch(ahci_pread(ap, AHCI_PREG_SSTS) &
878258223a3SMatthew Dillon 			       AHCI_PREG_SSTS_SPD) {
879258223a3SMatthew Dillon 			case AHCI_PREG_SSTS_SPD_GEN1:
880258223a3SMatthew Dillon 				ccb->cpi.base_transfer_speed = 150000;
881258223a3SMatthew Dillon 				break;
882258223a3SMatthew Dillon 			case AHCI_PREG_SSTS_SPD_GEN2:
883258223a3SMatthew Dillon 				ccb->cpi.base_transfer_speed = 300000;
884258223a3SMatthew Dillon 				break;
8858986d351SMatthew Dillon 			case AHCI_PREG_SSTS_SPD_GEN3:
8868986d351SMatthew Dillon 				ccb->cpi.base_transfer_speed = 600000;
8878986d351SMatthew Dillon 				break;
888258223a3SMatthew Dillon 			default:
889258223a3SMatthew Dillon 				/* unknown */
890258223a3SMatthew Dillon 				ccb->cpi.base_transfer_speed = 1000;
891258223a3SMatthew Dillon 				break;
892258223a3SMatthew Dillon 			}
8933209f581SMatthew Dillon #if 0
8941980eff3SMatthew Dillon 			if (ap->ap_type == ATA_PORT_T_NONE)
8951980eff3SMatthew Dillon 				ccbh->status = CAM_DEV_NOT_THERE;
8963209f581SMatthew Dillon #endif
8971980eff3SMatthew Dillon 		}
898258223a3SMatthew Dillon 		xpt_done(ccb);
899258223a3SMatthew Dillon 		break;
900258223a3SMatthew Dillon 	case XPT_RESET_DEV:
901f4553de1SMatthew Dillon 		ahci_os_lock_port(ap);
9021980eff3SMatthew Dillon 		if (ap->ap_type == ATA_PORT_T_NONE) {
9033209f581SMatthew Dillon 			ccbh->status = CAM_DEV_NOT_THERE;
9041980eff3SMatthew Dillon 		} else {
9051980eff3SMatthew Dillon 			ahci_port_reset(ap, atx, 0);
906fd8bd957SMatthew Dillon 			ccbh->status = CAM_REQ_CMP;
9071980eff3SMatthew Dillon 		}
908f4553de1SMatthew Dillon 		ahci_os_unlock_port(ap);
909258223a3SMatthew Dillon 		xpt_done(ccb);
910258223a3SMatthew Dillon 		break;
911258223a3SMatthew Dillon 	case XPT_RESET_BUS:
912f4553de1SMatthew Dillon 		ahci_os_lock_port(ap);
9131980eff3SMatthew Dillon 		ahci_port_reset(ap, NULL, 1);
914f4553de1SMatthew Dillon 		ahci_os_unlock_port(ap);
915fd8bd957SMatthew Dillon 		ccbh->status = CAM_REQ_CMP;
916258223a3SMatthew Dillon 		xpt_done(ccb);
917258223a3SMatthew Dillon 		break;
918258223a3SMatthew Dillon 	case XPT_SET_TRAN_SETTINGS:
919258223a3SMatthew Dillon 		ccbh->status = CAM_FUNC_NOTAVAIL;
920258223a3SMatthew Dillon 		xpt_done(ccb);
921258223a3SMatthew Dillon 		break;
922258223a3SMatthew Dillon 	case XPT_GET_TRAN_SETTINGS:
923258223a3SMatthew Dillon 		ccb->cts.protocol = PROTO_SCSI;
924258223a3SMatthew Dillon 		ccb->cts.protocol_version = SCSI_REV_2;
9252cc2e845SMatthew Dillon 		ccb->cts.transport = XPORT_SATA;
926258223a3SMatthew Dillon 		ccb->cts.transport_version = XPORT_VERSION_UNSPECIFIED;
927258223a3SMatthew Dillon 		ccb->cts.proto_specific.valid = 0;
928258223a3SMatthew Dillon 		ccb->cts.xport_specific.valid = 0;
929258223a3SMatthew Dillon 		ccbh->status = CAM_REQ_CMP;
930258223a3SMatthew Dillon 		xpt_done(ccb);
931258223a3SMatthew Dillon 		break;
932258223a3SMatthew Dillon 	case XPT_CALC_GEOMETRY:
933258223a3SMatthew Dillon 		cam_calc_geometry(&ccb->ccg, 1);
934258223a3SMatthew Dillon 		xpt_done(ccb);
935258223a3SMatthew Dillon 		break;
936258223a3SMatthew Dillon 	case XPT_SCSI_IO:
937f4553de1SMatthew Dillon 		/*
938f4553de1SMatthew Dillon 		 * Our parallel startup code might have only probed through
939f4553de1SMatthew Dillon 		 * to the IDENT, so do the last step if necessary.
940f4553de1SMatthew Dillon 		 */
941f4553de1SMatthew Dillon 		if (at->at_probe == ATA_PROBE_NEED_IDENT)
942f4553de1SMatthew Dillon 			ahci_cam_probe(ap, atx);
943f4553de1SMatthew Dillon 		if (at->at_probe != ATA_PROBE_GOOD) {
944f4553de1SMatthew Dillon 			ccbh->status = CAM_DEV_NOT_THERE;
945f4553de1SMatthew Dillon 			xpt_done(ccb);
946f4553de1SMatthew Dillon 			break;
947f4553de1SMatthew Dillon 		}
9481980eff3SMatthew Dillon 		switch(at->at_type) {
949258223a3SMatthew Dillon 		case ATA_PORT_T_DISK:
9501980eff3SMatthew Dillon 			ahci_xpt_scsi_disk_io(ap, atx, ccb);
951258223a3SMatthew Dillon 			break;
952258223a3SMatthew Dillon 		case ATA_PORT_T_ATAPI:
9531980eff3SMatthew Dillon 			ahci_xpt_scsi_atapi_io(ap, atx, ccb);
954258223a3SMatthew Dillon 			break;
955258223a3SMatthew Dillon 		default:
956258223a3SMatthew Dillon 			ccbh->status = CAM_REQ_INVALID;
957258223a3SMatthew Dillon 			xpt_done(ccb);
958258223a3SMatthew Dillon 			break;
959258223a3SMatthew Dillon 		}
960258223a3SMatthew Dillon 		break;
961e0fb398bSTim 	case XPT_TRIM:
962e0fb398bSTim 	{
963e0fb398bSTim 		scsi_cdb_t cdb;
964e0fb398bSTim 		struct ccb_scsiio *csio;
965e0fb398bSTim 		csio = &ccb->csio;
966e0fb398bSTim 		cdb = (void *)((ccbh->flags & CAM_CDB_POINTER) ?
967e0fb398bSTim 		    csio->cdb_io.cdb_ptr : csio->cdb_io.cdb_bytes);
968e0fb398bSTim 		cdb->generic.opcode = TRIM;
969e0fb398bSTim 		ahci_xpt_scsi_disk_io(ap, atx, ccb);
970e0fb398bSTim 		break;
971e0fb398bSTim 	}
972258223a3SMatthew Dillon 	default:
973258223a3SMatthew Dillon 		ccbh->status = CAM_REQ_INVALID;
974258223a3SMatthew Dillon 		xpt_done(ccb);
975258223a3SMatthew Dillon 		break;
976258223a3SMatthew Dillon 	}
977258223a3SMatthew Dillon }
978258223a3SMatthew Dillon 
979258223a3SMatthew Dillon /*
980de68d532SMatthew Dillon  * Poll function.
981de68d532SMatthew Dillon  *
982de68d532SMatthew Dillon  * Generally this function gets called heavily when interrupts might be
983de68d532SMatthew Dillon  * non-operational, during a halt/reboot or panic.
984258223a3SMatthew Dillon  */
985258223a3SMatthew Dillon static
986258223a3SMatthew Dillon void
987258223a3SMatthew Dillon ahci_xpt_poll(struct cam_sim *sim)
988258223a3SMatthew Dillon {
989de68d532SMatthew Dillon 	struct ahci_port *ap;
990258223a3SMatthew Dillon 
991de68d532SMatthew Dillon 	ap = cam_sim_softc(sim);
992de68d532SMatthew Dillon 	crit_enter();
993f4553de1SMatthew Dillon 	ahci_os_lock_port(ap);
994f4553de1SMatthew Dillon 	ahci_port_intr(ap, 1);
995f4553de1SMatthew Dillon 	ahci_os_unlock_port(ap);
996de68d532SMatthew Dillon 	crit_exit();
997258223a3SMatthew Dillon }
998258223a3SMatthew Dillon 
999258223a3SMatthew Dillon /*
1000b4189e5eSMatthew Dillon  * Convert the SCSI command in ccb to an ata_xfer command in xa
1001b4189e5eSMatthew Dillon  * for ATA_PORT_T_DISK operations.  Set the completion function
1002b4189e5eSMatthew Dillon  * to convert the response back, then dispatch to the OpenBSD AHCI
1003b4189e5eSMatthew Dillon  * layer.
1004258223a3SMatthew Dillon  *
1005b4189e5eSMatthew Dillon  * AHCI DISK commands only support a limited command set, and we
1006b4189e5eSMatthew Dillon  * fake additional commands to make it play nice with the CAM subsystem.
1007258223a3SMatthew Dillon  */
1008258223a3SMatthew Dillon static
1009258223a3SMatthew Dillon void
10101980eff3SMatthew Dillon ahci_xpt_scsi_disk_io(struct ahci_port *ap, struct ata_port *atx,
10111980eff3SMatthew Dillon 		      union ccb *ccb)
1012258223a3SMatthew Dillon {
1013258223a3SMatthew Dillon 	struct ccb_hdr *ccbh;
1014258223a3SMatthew Dillon 	struct ccb_scsiio *csio;
1015258223a3SMatthew Dillon 	struct ata_xfer *xa;
10161980eff3SMatthew Dillon 	struct ata_port	*at;
1017258223a3SMatthew Dillon 	struct ata_fis_h2d *fis;
1018bedbe7f8SAlex Hornung 	struct ata_pass_12 *atp12;
1019bedbe7f8SAlex Hornung 	struct ata_pass_16 *atp16;
1020258223a3SMatthew Dillon 	scsi_cdb_t cdb;
1021258223a3SMatthew Dillon 	union scsi_data *rdata;
1022258223a3SMatthew Dillon 	int rdata_len;
1023258223a3SMatthew Dillon 	u_int64_t capacity;
1024258223a3SMatthew Dillon 	u_int64_t lba;
1025258223a3SMatthew Dillon 	u_int32_t count;
1026258223a3SMatthew Dillon 
1027258223a3SMatthew Dillon 	ccbh = &ccb->csio.ccb_h;
1028258223a3SMatthew Dillon 	csio = &ccb->csio;
1029b012a2caSMatthew Dillon 	at = atx ? atx : ap->ap_ata[0];
10301980eff3SMatthew Dillon 
10311980eff3SMatthew Dillon 	/*
10321980eff3SMatthew Dillon 	 * XXX not passing NULL at for direct attach!
10331980eff3SMatthew Dillon 	 */
10341980eff3SMatthew Dillon 	xa = ahci_ata_get_xfer(ap, atx);
1035258223a3SMatthew Dillon 	rdata = (void *)csio->data_ptr;
1036258223a3SMatthew Dillon 	rdata_len = csio->dxfer_len;
1037258223a3SMatthew Dillon 
1038258223a3SMatthew Dillon 	/*
1039258223a3SMatthew Dillon 	 * Build the FIS or process the csio to completion.
1040258223a3SMatthew Dillon 	 */
1041258223a3SMatthew Dillon 	cdb = (void *)((ccbh->flags & CAM_CDB_POINTER) ?
1042258223a3SMatthew Dillon 			csio->cdb_io.cdb_ptr : csio->cdb_io.cdb_bytes);
1043258223a3SMatthew Dillon 
1044258223a3SMatthew Dillon 	switch(cdb->generic.opcode) {
1045258223a3SMatthew Dillon 	case REQUEST_SENSE:
1046258223a3SMatthew Dillon 		/*
1047258223a3SMatthew Dillon 		 * Auto-sense everything, so explicit sense requests
1048258223a3SMatthew Dillon 		 * return no-sense.
1049258223a3SMatthew Dillon 		 */
1050258223a3SMatthew Dillon 		ccbh->status = CAM_SCSI_STATUS_ERROR;
1051258223a3SMatthew Dillon 		break;
1052258223a3SMatthew Dillon 	case INQUIRY:
1053258223a3SMatthew Dillon 		/*
1054258223a3SMatthew Dillon 		 * Inquiry supported features
1055258223a3SMatthew Dillon 		 *
1056258223a3SMatthew Dillon 		 * [opcode, byte2, page_code, length, control]
1057258223a3SMatthew Dillon 		 */
1058258223a3SMatthew Dillon 		if (cdb->inquiry.byte2 & SI_EVPD) {
10596e0003adSMatthew Dillon 			ahci_xpt_page_inquiry(ap, at, ccb);
1060258223a3SMatthew Dillon 		} else {
1061258223a3SMatthew Dillon 			bzero(rdata, rdata_len);
1062258223a3SMatthew Dillon 			if (rdata_len < SHORT_INQUIRY_LENGTH) {
1063258223a3SMatthew Dillon 				ccbh->status = CAM_CCB_LEN_ERR;
1064258223a3SMatthew Dillon 				break;
1065258223a3SMatthew Dillon 			}
1066258223a3SMatthew Dillon 			if (rdata_len > sizeof(rdata->inquiry_data))
1067258223a3SMatthew Dillon 				rdata_len = sizeof(rdata->inquiry_data);
1068258223a3SMatthew Dillon 			rdata->inquiry_data.device = T_DIRECT;
1069258223a3SMatthew Dillon 			rdata->inquiry_data.version = SCSI_REV_SPC2;
1070258223a3SMatthew Dillon 			rdata->inquiry_data.response_format = 2;
1071258223a3SMatthew Dillon 			rdata->inquiry_data.additional_length = 32;
1072258223a3SMatthew Dillon 			bcopy("SATA    ", rdata->inquiry_data.vendor, 8);
10731980eff3SMatthew Dillon 			bcopy(at->at_identify.model,
1074258223a3SMatthew Dillon 			      rdata->inquiry_data.product,
1075258223a3SMatthew Dillon 			      sizeof(rdata->inquiry_data.product));
10761980eff3SMatthew Dillon 			bcopy(at->at_identify.firmware,
1077258223a3SMatthew Dillon 			      rdata->inquiry_data.revision,
1078258223a3SMatthew Dillon 			      sizeof(rdata->inquiry_data.revision));
1079258223a3SMatthew Dillon 			ccbh->status = CAM_REQ_CMP;
1080258223a3SMatthew Dillon 		}
1081e0fb398bSTim 
1082e0fb398bSTim 		/*
1083e0fb398bSTim 		 * Use the vendor specific area to set the TRIM status
1084e0fb398bSTim 		 * for scsi_da
1085e0fb398bSTim 		 */
1086e0fb398bSTim 		if (at->at_identify.support_dsm) {
1087e0fb398bSTim 			rdata->inquiry_data.vendor_specific1[0] =
1088e0fb398bSTim 			    at->at_identify.support_dsm &ATA_SUPPORT_DSM_TRIM;
1089e0fb398bSTim 			rdata->inquiry_data.vendor_specific1[1] =
1090e0fb398bSTim 			    at->at_identify.max_dsm_blocks;
1091e0fb398bSTim 		}
1092258223a3SMatthew Dillon 		break;
1093258223a3SMatthew Dillon 	case READ_CAPACITY_16:
1094258223a3SMatthew Dillon 		if (cdb->read_capacity_16.service_action != SRC16_SERVICE_ACTION) {
1095258223a3SMatthew Dillon 			ccbh->status = CAM_REQ_INVALID;
1096258223a3SMatthew Dillon 			break;
1097258223a3SMatthew Dillon 		}
1098258223a3SMatthew Dillon 		if (rdata_len < sizeof(rdata->read_capacity_data_16)) {
1099258223a3SMatthew Dillon 			ccbh->status = CAM_CCB_LEN_ERR;
1100258223a3SMatthew Dillon 			break;
1101258223a3SMatthew Dillon 		}
1102258223a3SMatthew Dillon 		/* fall through */
1103258223a3SMatthew Dillon 	case READ_CAPACITY:
1104258223a3SMatthew Dillon 		if (rdata_len < sizeof(rdata->read_capacity_data)) {
1105258223a3SMatthew Dillon 			ccbh->status = CAM_CCB_LEN_ERR;
1106258223a3SMatthew Dillon 			break;
1107258223a3SMatthew Dillon 		}
1108258223a3SMatthew Dillon 
11091980eff3SMatthew Dillon 		capacity = at->at_capacity;
1110258223a3SMatthew Dillon 
1111258223a3SMatthew Dillon 		bzero(rdata, rdata_len);
1112258223a3SMatthew Dillon 		if (cdb->generic.opcode == READ_CAPACITY) {
1113258223a3SMatthew Dillon 			rdata_len = sizeof(rdata->read_capacity_data);
1114258223a3SMatthew Dillon 			if (capacity > 0xFFFFFFFFU)
1115258223a3SMatthew Dillon 				capacity = 0xFFFFFFFFU;
1116258223a3SMatthew Dillon 			bzero(&rdata->read_capacity_data, rdata_len);
1117258223a3SMatthew Dillon 			scsi_ulto4b((u_int32_t)capacity - 1,
1118258223a3SMatthew Dillon 				    rdata->read_capacity_data.addr);
1119258223a3SMatthew Dillon 			scsi_ulto4b(512, rdata->read_capacity_data.length);
1120258223a3SMatthew Dillon 		} else {
1121258223a3SMatthew Dillon 			rdata_len = sizeof(rdata->read_capacity_data_16);
1122258223a3SMatthew Dillon 			bzero(&rdata->read_capacity_data_16, rdata_len);
1123258223a3SMatthew Dillon 			scsi_u64to8b(capacity - 1,
1124258223a3SMatthew Dillon 				     rdata->read_capacity_data_16.addr);
1125258223a3SMatthew Dillon 			scsi_ulto4b(512, rdata->read_capacity_data_16.length);
1126258223a3SMatthew Dillon 		}
1127258223a3SMatthew Dillon 		ccbh->status = CAM_REQ_CMP;
1128258223a3SMatthew Dillon 		break;
1129258223a3SMatthew Dillon 	case SYNCHRONIZE_CACHE:
1130258223a3SMatthew Dillon 		/*
1131258223a3SMatthew Dillon 		 * Synchronize cache.  Specification says this can take
1132258223a3SMatthew Dillon 		 * greater then 30 seconds so give it at least 45.
1133258223a3SMatthew Dillon 		 */
1134258223a3SMatthew Dillon 		fis = xa->fis;
1135258223a3SMatthew Dillon 		fis->flags = ATA_H2D_FLAGS_CMD;
1136258223a3SMatthew Dillon 		fis->command = ATA_C_FLUSH_CACHE;
1137258223a3SMatthew Dillon 		fis->device = 0;
11383209f581SMatthew Dillon 		if (xa->timeout < 45000)
11393209f581SMatthew Dillon 			xa->timeout = 45000;
11401980eff3SMatthew Dillon 		xa->datalen = 0;
1141b012a2caSMatthew Dillon 		xa->flags = 0;
11421980eff3SMatthew Dillon 		xa->complete = ahci_ata_complete_disk_synchronize_cache;
1143258223a3SMatthew Dillon 		break;
1144e0fb398bSTim 	case TRIM:
1145e0fb398bSTim 		fis = xa->fis;
1146e0fb398bSTim 		fis->command = ATA_C_DATA_SET_MANAGEMENT;
1147e0fb398bSTim 		fis->features = (u_int8_t)ATA_SF_DSM_TRIM;
1148e0fb398bSTim 		fis->features_exp = (u_int8_t)(ATA_SF_DSM_TRIM>> 8);
1149e0fb398bSTim 
1150e0fb398bSTim 		xa->flags = ATA_F_WRITE;
1151e0fb398bSTim 		fis->flags = ATA_H2D_FLAGS_CMD;
1152e0fb398bSTim 
1153e0fb398bSTim 		xa->data = csio->data_ptr;
1154e0fb398bSTim 		xa->datalen = csio->dxfer_len;
1155e0fb398bSTim 		xa->timeout = ccbh->timeout*50;	/* milliseconds */
1156e0fb398bSTim 
1157e0fb398bSTim 		fis->sector_count =(u_int8_t)(xa->datalen/512);
1158e0fb398bSTim 		fis->sector_count_exp =(u_int8_t)((xa->datalen/512)>>8);
1159e0fb398bSTim 
1160e0fb398bSTim 		lba = 0;
1161e0fb398bSTim 		fis->lba_low = (u_int8_t)lba;
1162e0fb398bSTim 		fis->lba_mid = (u_int8_t)(lba >> 8);
1163e0fb398bSTim 		fis->lba_high = (u_int8_t)(lba >> 16);
1164e0fb398bSTim 		fis->lba_low_exp = (u_int8_t)(lba >> 24);
1165e0fb398bSTim 		fis->lba_mid_exp = (u_int8_t)(lba >> 32);
1166e0fb398bSTim 		fis->lba_high_exp = (u_int8_t)(lba >> 40);
1167e0fb398bSTim 
1168e0fb398bSTim 		fis->device = ATA_H2D_DEVICE_LBA;
1169e0fb398bSTim 		xa->data = csio->data_ptr;
1170e0fb398bSTim 
1171e0fb398bSTim 		xa->complete = ahci_ata_complete_disk_rw;
1172e0fb398bSTim 		ccbh->status = CAM_REQ_INPROG;
1173e0fb398bSTim 		break;
1174258223a3SMatthew Dillon 	case TEST_UNIT_READY:
1175258223a3SMatthew Dillon 	case START_STOP_UNIT:
1176258223a3SMatthew Dillon 	case PREVENT_ALLOW:
1177258223a3SMatthew Dillon 		/*
1178258223a3SMatthew Dillon 		 * Just silently return success
1179258223a3SMatthew Dillon 		 */
1180258223a3SMatthew Dillon 		ccbh->status = CAM_REQ_CMP;
1181258223a3SMatthew Dillon 		rdata_len = 0;
1182258223a3SMatthew Dillon 		break;
1183258223a3SMatthew Dillon 	case ATA_PASS_12:
1184bedbe7f8SAlex Hornung 		atp12 = &cdb->ata_pass_12;
1185bedbe7f8SAlex Hornung 		fis = xa->fis;
1186258223a3SMatthew Dillon 		/*
1187bedbe7f8SAlex Hornung 		 * Figure out the flags to be used, depending on the direction of the
1188bedbe7f8SAlex Hornung 		 * CAM request.
1189258223a3SMatthew Dillon 		 */
1190bedbe7f8SAlex Hornung 		switch (ccbh->flags & CAM_DIR_MASK) {
1191bedbe7f8SAlex Hornung 		case CAM_DIR_IN:
1192bedbe7f8SAlex Hornung 			xa->flags = ATA_F_READ;
1193bedbe7f8SAlex Hornung 			break;
1194bedbe7f8SAlex Hornung 		case CAM_DIR_OUT:
1195bedbe7f8SAlex Hornung 			xa->flags = ATA_F_WRITE;
1196bedbe7f8SAlex Hornung 			break;
1197bedbe7f8SAlex Hornung 		default:
1198bedbe7f8SAlex Hornung 			xa->flags = 0;
1199bedbe7f8SAlex Hornung 		}
120022726f69SMatthew Dillon 		xa->flags |= ATA_F_POLL | ATA_F_EXCLUSIVE;
1201bedbe7f8SAlex Hornung 		xa->data = csio->data_ptr;
1202bedbe7f8SAlex Hornung 		xa->datalen = csio->dxfer_len;
1203bedbe7f8SAlex Hornung 		xa->complete = ahci_ata_complete_disk_rw;
1204bedbe7f8SAlex Hornung 		xa->timeout = ccbh->timeout;
1205bedbe7f8SAlex Hornung 
1206bedbe7f8SAlex Hornung 		/*
1207bedbe7f8SAlex Hornung 		 * Populate the fis from the information we received through CAM
1208bedbe7f8SAlex Hornung 		 * ATA passthrough.
1209bedbe7f8SAlex Hornung 		 */
1210bedbe7f8SAlex Hornung 		fis->flags = ATA_H2D_FLAGS_CMD;	/* maybe also atp12->flags ? */
1211bedbe7f8SAlex Hornung 		fis->features = atp12->features;
1212bedbe7f8SAlex Hornung 		fis->sector_count = atp12->sector_count;
1213bedbe7f8SAlex Hornung 		fis->lba_low = atp12->lba_low;
1214bedbe7f8SAlex Hornung 		fis->lba_mid = atp12->lba_mid;
1215bedbe7f8SAlex Hornung 		fis->lba_high = atp12->lba_high;
1216bedbe7f8SAlex Hornung 		fis->device = atp12->device;	/* maybe always 0? */
1217bedbe7f8SAlex Hornung 		fis->command = atp12->command;
1218bedbe7f8SAlex Hornung 		fis->control = atp12->control;
1219bedbe7f8SAlex Hornung 
1220bedbe7f8SAlex Hornung 		/*
1221bedbe7f8SAlex Hornung 		 * Mark as in progress so it is sent to the device.
1222bedbe7f8SAlex Hornung 		 */
1223bedbe7f8SAlex Hornung 		ccbh->status = CAM_REQ_INPROG;
1224bedbe7f8SAlex Hornung 		break;
1225bedbe7f8SAlex Hornung 	case ATA_PASS_16:
1226bedbe7f8SAlex Hornung 		atp16 = &cdb->ata_pass_16;
1227bedbe7f8SAlex Hornung 		fis = xa->fis;
1228bedbe7f8SAlex Hornung 		/*
1229bedbe7f8SAlex Hornung 		 * Figure out the flags to be used, depending on the direction of the
1230bedbe7f8SAlex Hornung 		 * CAM request.
1231bedbe7f8SAlex Hornung 		 */
1232bedbe7f8SAlex Hornung 		switch (ccbh->flags & CAM_DIR_MASK) {
1233bedbe7f8SAlex Hornung 		case CAM_DIR_IN:
1234bedbe7f8SAlex Hornung 			xa->flags = ATA_F_READ;
1235bedbe7f8SAlex Hornung 			break;
1236bedbe7f8SAlex Hornung 		case CAM_DIR_OUT:
1237bedbe7f8SAlex Hornung 			xa->flags = ATA_F_WRITE;
1238bedbe7f8SAlex Hornung 			break;
1239bedbe7f8SAlex Hornung 		default:
1240bedbe7f8SAlex Hornung 			xa->flags = 0;
1241bedbe7f8SAlex Hornung 		}
124222726f69SMatthew Dillon 		xa->flags |= ATA_F_POLL | ATA_F_EXCLUSIVE;
1243bedbe7f8SAlex Hornung 		xa->data = csio->data_ptr;
1244bedbe7f8SAlex Hornung 		xa->datalen = csio->dxfer_len;
1245bedbe7f8SAlex Hornung 		xa->complete = ahci_ata_complete_disk_rw;
1246bedbe7f8SAlex Hornung 		xa->timeout = ccbh->timeout;
1247bedbe7f8SAlex Hornung 
1248bedbe7f8SAlex Hornung 		/*
1249bedbe7f8SAlex Hornung 		 * Populate the fis from the information we received through CAM
1250bedbe7f8SAlex Hornung 		 * ATA passthrough.
1251bedbe7f8SAlex Hornung 		 */
1252bedbe7f8SAlex Hornung 		fis->flags = ATA_H2D_FLAGS_CMD;	/* maybe also atp16->flags ? */
1253bedbe7f8SAlex Hornung 		fis->features = atp16->features;
1254bedbe7f8SAlex Hornung 		fis->features_exp = atp16->features_ext;
1255bedbe7f8SAlex Hornung 		fis->sector_count = atp16->sector_count;
1256bedbe7f8SAlex Hornung 		fis->sector_count_exp = atp16->sector_count_ext;
1257bedbe7f8SAlex Hornung 		fis->lba_low = atp16->lba_low;
1258bedbe7f8SAlex Hornung 		fis->lba_low_exp = atp16->lba_low_ext;
1259bedbe7f8SAlex Hornung 		fis->lba_mid = atp16->lba_mid;
1260bedbe7f8SAlex Hornung 		fis->lba_mid_exp = atp16->lba_mid_ext;
1261bedbe7f8SAlex Hornung 		fis->lba_high = atp16->lba_high;
1262bedbe7f8SAlex Hornung 		fis->lba_mid_exp = atp16->lba_mid_ext;
1263bedbe7f8SAlex Hornung 		fis->device = atp16->device;	/* maybe always 0? */
1264bedbe7f8SAlex Hornung 		fis->command = atp16->command;
1265bedbe7f8SAlex Hornung 
1266bedbe7f8SAlex Hornung 		/*
1267bedbe7f8SAlex Hornung 		 * Mark as in progress so it is sent to the device.
1268bedbe7f8SAlex Hornung 		 */
1269bedbe7f8SAlex Hornung 		ccbh->status = CAM_REQ_INPROG;
1270258223a3SMatthew Dillon 		break;
1271258223a3SMatthew Dillon 	default:
1272258223a3SMatthew Dillon 		switch(cdb->generic.opcode) {
1273258223a3SMatthew Dillon 		case READ_6:
1274258223a3SMatthew Dillon 			lba = scsi_3btoul(cdb->rw_6.addr) & 0x1FFFFF;
1275258223a3SMatthew Dillon 			count = cdb->rw_6.length ? cdb->rw_6.length : 0x100;
1276258223a3SMatthew Dillon 			xa->flags = ATA_F_READ;
1277258223a3SMatthew Dillon 			break;
1278258223a3SMatthew Dillon 		case READ_10:
1279258223a3SMatthew Dillon 			lba = scsi_4btoul(cdb->rw_10.addr);
1280258223a3SMatthew Dillon 			count = scsi_2btoul(cdb->rw_10.length);
1281258223a3SMatthew Dillon 			xa->flags = ATA_F_READ;
1282258223a3SMatthew Dillon 			break;
1283258223a3SMatthew Dillon 		case READ_12:
1284258223a3SMatthew Dillon 			lba = scsi_4btoul(cdb->rw_12.addr);
1285258223a3SMatthew Dillon 			count = scsi_4btoul(cdb->rw_12.length);
1286258223a3SMatthew Dillon 			xa->flags = ATA_F_READ;
1287258223a3SMatthew Dillon 			break;
1288258223a3SMatthew Dillon 		case READ_16:
1289258223a3SMatthew Dillon 			lba = scsi_8btou64(cdb->rw_16.addr);
1290258223a3SMatthew Dillon 			count = scsi_4btoul(cdb->rw_16.length);
1291258223a3SMatthew Dillon 			xa->flags = ATA_F_READ;
1292258223a3SMatthew Dillon 			break;
1293258223a3SMatthew Dillon 		case WRITE_6:
1294258223a3SMatthew Dillon 			lba = scsi_3btoul(cdb->rw_6.addr) & 0x1FFFFF;
1295258223a3SMatthew Dillon 			count = cdb->rw_6.length ? cdb->rw_6.length : 0x100;
1296258223a3SMatthew Dillon 			xa->flags = ATA_F_WRITE;
1297258223a3SMatthew Dillon 			break;
1298258223a3SMatthew Dillon 		case WRITE_10:
1299258223a3SMatthew Dillon 			lba = scsi_4btoul(cdb->rw_10.addr);
1300258223a3SMatthew Dillon 			count = scsi_2btoul(cdb->rw_10.length);
1301258223a3SMatthew Dillon 			xa->flags = ATA_F_WRITE;
1302258223a3SMatthew Dillon 			break;
1303258223a3SMatthew Dillon 		case WRITE_12:
1304258223a3SMatthew Dillon 			lba = scsi_4btoul(cdb->rw_12.addr);
1305258223a3SMatthew Dillon 			count = scsi_4btoul(cdb->rw_12.length);
1306258223a3SMatthew Dillon 			xa->flags = ATA_F_WRITE;
1307258223a3SMatthew Dillon 			break;
1308258223a3SMatthew Dillon 		case WRITE_16:
1309258223a3SMatthew Dillon 			lba = scsi_8btou64(cdb->rw_16.addr);
1310258223a3SMatthew Dillon 			count = scsi_4btoul(cdb->rw_16.length);
1311258223a3SMatthew Dillon 			xa->flags = ATA_F_WRITE;
1312258223a3SMatthew Dillon 			break;
1313258223a3SMatthew Dillon 		default:
1314258223a3SMatthew Dillon 			ccbh->status = CAM_REQ_INVALID;
1315258223a3SMatthew Dillon 			break;
1316258223a3SMatthew Dillon 		}
1317258223a3SMatthew Dillon 		if (ccbh->status != CAM_REQ_INPROG)
1318258223a3SMatthew Dillon 			break;
1319258223a3SMatthew Dillon 
1320258223a3SMatthew Dillon 		fis = xa->fis;
1321258223a3SMatthew Dillon 		fis->flags = ATA_H2D_FLAGS_CMD;
1322258223a3SMatthew Dillon 		fis->lba_low = (u_int8_t)lba;
1323258223a3SMatthew Dillon 		fis->lba_mid = (u_int8_t)(lba >> 8);
1324258223a3SMatthew Dillon 		fis->lba_high = (u_int8_t)(lba >> 16);
1325258223a3SMatthew Dillon 		fis->device = ATA_H2D_DEVICE_LBA;
1326258223a3SMatthew Dillon 
13271980eff3SMatthew Dillon 		/*
13281980eff3SMatthew Dillon 		 * NCQ only for direct-attached disks, do not currently
13291980eff3SMatthew Dillon 		 * try to use NCQ with port multipliers.
13301980eff3SMatthew Dillon 		 */
13311980eff3SMatthew Dillon 		if (at->at_ncqdepth > 1 &&
13321980eff3SMatthew Dillon 		    ap->ap_type == ATA_PORT_T_DISK &&
1333258223a3SMatthew Dillon 		    (ap->ap_sc->sc_cap & AHCI_REG_CAP_SNCQ) &&
1334258223a3SMatthew Dillon 		    (ccbh->flags & CAM_POLLED) == 0) {
1335258223a3SMatthew Dillon 			/*
1336258223a3SMatthew Dillon 			 * Use NCQ - always uses 48 bit addressing
1337258223a3SMatthew Dillon 			 */
1338258223a3SMatthew Dillon 			xa->flags |= ATA_F_NCQ;
1339258223a3SMatthew Dillon 			fis->command = (xa->flags & ATA_F_WRITE) ?
1340258223a3SMatthew Dillon 					ATA_C_WRITE_FPDMA : ATA_C_READ_FPDMA;
1341258223a3SMatthew Dillon 			fis->lba_low_exp = (u_int8_t)(lba >> 24);
1342258223a3SMatthew Dillon 			fis->lba_mid_exp = (u_int8_t)(lba >> 32);
1343258223a3SMatthew Dillon 			fis->lba_high_exp = (u_int8_t)(lba >> 40);
1344258223a3SMatthew Dillon 			fis->sector_count = xa->tag << 3;
1345258223a3SMatthew Dillon 			fis->features = (u_int8_t)count;
1346258223a3SMatthew Dillon 			fis->features_exp = (u_int8_t)(count >> 8);
1347b2772aeeSMatthew Dillon 		} else if (count > 0x100 || lba > 0x0FFFFFFFU) {
1348258223a3SMatthew Dillon 			/*
1349258223a3SMatthew Dillon 			 * Use LBA48
1350258223a3SMatthew Dillon 			 */
1351258223a3SMatthew Dillon 			fis->command = (xa->flags & ATA_F_WRITE) ?
1352258223a3SMatthew Dillon 					ATA_C_WRITEDMA_EXT : ATA_C_READDMA_EXT;
1353258223a3SMatthew Dillon 			fis->lba_low_exp = (u_int8_t)(lba >> 24);
1354258223a3SMatthew Dillon 			fis->lba_mid_exp = (u_int8_t)(lba >> 32);
1355258223a3SMatthew Dillon 			fis->lba_high_exp = (u_int8_t)(lba >> 40);
1356258223a3SMatthew Dillon 			fis->sector_count = (u_int8_t)count;
1357258223a3SMatthew Dillon 			fis->sector_count_exp = (u_int8_t)(count >> 8);
1358258223a3SMatthew Dillon 		} else {
1359258223a3SMatthew Dillon 			/*
1360258223a3SMatthew Dillon 			 * Use LBA
1361258223a3SMatthew Dillon 			 *
1362258223a3SMatthew Dillon 			 * NOTE: 256 sectors is supported, stored as 0.
1363258223a3SMatthew Dillon 			 */
1364258223a3SMatthew Dillon 			fis->command = (xa->flags & ATA_F_WRITE) ?
1365258223a3SMatthew Dillon 					ATA_C_WRITEDMA : ATA_C_READDMA;
1366258223a3SMatthew Dillon 			fis->device |= (u_int8_t)(lba >> 24) & 0x0F;
1367258223a3SMatthew Dillon 			fis->sector_count = (u_int8_t)count;
1368258223a3SMatthew Dillon 		}
1369258223a3SMatthew Dillon 
1370258223a3SMatthew Dillon 		xa->data = csio->data_ptr;
1371258223a3SMatthew Dillon 		xa->datalen = csio->dxfer_len;
1372258223a3SMatthew Dillon 		xa->complete = ahci_ata_complete_disk_rw;
13733209f581SMatthew Dillon 		xa->timeout = ccbh->timeout;	/* milliseconds */
137412feb904SMatthew Dillon #if 0
137512feb904SMatthew Dillon 		if (xa->timeout > 10000)	/* XXX - debug */
137612feb904SMatthew Dillon 			xa->timeout = 10000;
137712feb904SMatthew Dillon #endif
1378258223a3SMatthew Dillon 		if (ccbh->flags & CAM_POLLED)
1379258223a3SMatthew Dillon 			xa->flags |= ATA_F_POLL;
1380258223a3SMatthew Dillon 		break;
1381258223a3SMatthew Dillon 	}
1382258223a3SMatthew Dillon 
1383258223a3SMatthew Dillon 	/*
1384258223a3SMatthew Dillon 	 * If the request is still in progress the xa and FIS have
13856e0003adSMatthew Dillon 	 * been set up (except for the PM target), and must be dispatched.
13866e0003adSMatthew Dillon 	 * Otherwise the request was completed.
1387258223a3SMatthew Dillon 	 */
1388258223a3SMatthew Dillon 	if (ccbh->status == CAM_REQ_INPROG) {
1389258223a3SMatthew Dillon 		KKASSERT(xa->complete != NULL);
1390258223a3SMatthew Dillon 		xa->atascsi_private = ccb;
1391258223a3SMatthew Dillon 		ccb->ccb_h.sim_priv.entries[0].ptr = ap;
1392f4553de1SMatthew Dillon 		ahci_os_lock_port(ap);
13936e0003adSMatthew Dillon 		xa->fis->flags |= at->at_target;
1394258223a3SMatthew Dillon 		ahci_ata_cmd(xa);
1395f4553de1SMatthew Dillon 		ahci_os_unlock_port(ap);
1396258223a3SMatthew Dillon 	} else {
1397258223a3SMatthew Dillon 		ahci_ata_put_xfer(xa);
1398258223a3SMatthew Dillon 		xpt_done(ccb);
1399258223a3SMatthew Dillon 	}
1400258223a3SMatthew Dillon }
1401258223a3SMatthew Dillon 
1402b4189e5eSMatthew Dillon /*
1403b4189e5eSMatthew Dillon  * Convert the SCSI command in ccb to an ata_xfer command in xa
1404b4189e5eSMatthew Dillon  * for ATA_PORT_T_ATAPI operations.  Set the completion function
1405b4189e5eSMatthew Dillon  * to convert the response back, then dispatch to the OpenBSD AHCI
1406b4189e5eSMatthew Dillon  * layer.
1407b4189e5eSMatthew Dillon  */
1408258223a3SMatthew Dillon static
1409258223a3SMatthew Dillon void
14101980eff3SMatthew Dillon ahci_xpt_scsi_atapi_io(struct ahci_port *ap, struct ata_port *atx,
14111980eff3SMatthew Dillon 			union ccb *ccb)
1412258223a3SMatthew Dillon {
1413258223a3SMatthew Dillon 	struct ccb_hdr *ccbh;
1414258223a3SMatthew Dillon 	struct ccb_scsiio *csio;
1415258223a3SMatthew Dillon 	struct ata_xfer *xa;
1416258223a3SMatthew Dillon 	struct ata_fis_h2d *fis;
1417b4189e5eSMatthew Dillon 	scsi_cdb_t cdbs;
1418b4189e5eSMatthew Dillon 	scsi_cdb_t cdbd;
1419b4189e5eSMatthew Dillon 	int flags;
14201980eff3SMatthew Dillon 	struct ata_port	*at;
1421258223a3SMatthew Dillon 
1422258223a3SMatthew Dillon 	ccbh = &ccb->csio.ccb_h;
1423258223a3SMatthew Dillon 	csio = &ccb->csio;
1424b012a2caSMatthew Dillon 	at = atx ? atx : ap->ap_ata[0];
1425b4189e5eSMatthew Dillon 
1426b4189e5eSMatthew Dillon 	switch (ccbh->flags & CAM_DIR_MASK) {
1427b4189e5eSMatthew Dillon 	case CAM_DIR_IN:
1428b4189e5eSMatthew Dillon 		flags = ATA_F_PACKET | ATA_F_READ;
1429b4189e5eSMatthew Dillon 		break;
1430b4189e5eSMatthew Dillon 	case CAM_DIR_OUT:
1431b4189e5eSMatthew Dillon 		flags = ATA_F_PACKET | ATA_F_WRITE;
1432b4189e5eSMatthew Dillon 		break;
1433b4189e5eSMatthew Dillon 	case CAM_DIR_NONE:
1434b4189e5eSMatthew Dillon 		flags = ATA_F_PACKET;
1435b4189e5eSMatthew Dillon 		break;
1436b4189e5eSMatthew Dillon 	default:
1437b4189e5eSMatthew Dillon 		ccbh->status = CAM_REQ_INVALID;
1438b4189e5eSMatthew Dillon 		xpt_done(ccb);
1439b4189e5eSMatthew Dillon 		return;
1440b4189e5eSMatthew Dillon 		/* NOT REACHED */
1441b4189e5eSMatthew Dillon 	}
1442b4189e5eSMatthew Dillon 
1443b4189e5eSMatthew Dillon 	/*
144412feb904SMatthew Dillon 	 * Special handling to get the rfis back into host memory while
1445192ee1d0SMatthew Dillon 	 * still allowing the chip to run commands in parallel to
144612feb904SMatthew Dillon 	 * ATAPI devices behind a PM.
144712feb904SMatthew Dillon 	 */
144812feb904SMatthew Dillon 	flags |= ATA_F_AUTOSENSE;
144912feb904SMatthew Dillon 
145012feb904SMatthew Dillon 	/*
1451b4189e5eSMatthew Dillon 	 * The command has to fit in the packet command buffer.
1452b4189e5eSMatthew Dillon 	 */
1453b4189e5eSMatthew Dillon 	if (csio->cdb_len < 6 || csio->cdb_len > 16) {
1454b4189e5eSMatthew Dillon 		ccbh->status = CAM_CCB_LEN_ERR;
1455b4189e5eSMatthew Dillon 		xpt_done(ccb);
1456b4189e5eSMatthew Dillon 		return;
1457b4189e5eSMatthew Dillon 	}
1458b4189e5eSMatthew Dillon 
1459b4189e5eSMatthew Dillon 	/*
1460192ee1d0SMatthew Dillon 	 * Initialize the XA and FIS.  It is unclear how much of
1461192ee1d0SMatthew Dillon 	 * this has to mimic the equivalent ATA command.
14621980eff3SMatthew Dillon 	 *
14631980eff3SMatthew Dillon 	 * XXX not passing NULL at for direct attach!
1464b4189e5eSMatthew Dillon 	 */
14651980eff3SMatthew Dillon 	xa = ahci_ata_get_xfer(ap, atx);
1466258223a3SMatthew Dillon 	fis = xa->fis;
1467258223a3SMatthew Dillon 
14681980eff3SMatthew Dillon 	fis->flags = ATA_H2D_FLAGS_CMD | at->at_target;
14691980eff3SMatthew Dillon 	fis->command = ATA_C_PACKET;
1470192ee1d0SMatthew Dillon 	fis->device = ATA_H2D_DEVICE_LBA;
14711980eff3SMatthew Dillon 	fis->sector_count = xa->tag << 3;
1472192ee1d0SMatthew Dillon 	if (flags & (ATA_F_READ | ATA_F_WRITE)) {
1473192ee1d0SMatthew Dillon 		if (flags & ATA_F_WRITE) {
14741980eff3SMatthew Dillon 			fis->features = ATA_H2D_FEATURES_DMA |
1475192ee1d0SMatthew Dillon 				       ATA_H2D_FEATURES_DIR_WRITE;
1476192ee1d0SMatthew Dillon 		} else {
1477192ee1d0SMatthew Dillon 			fis->features = ATA_H2D_FEATURES_DMA |
1478192ee1d0SMatthew Dillon 				       ATA_H2D_FEATURES_DIR_READ;
1479192ee1d0SMatthew Dillon 		}
1480192ee1d0SMatthew Dillon 	} else {
1481192ee1d0SMatthew Dillon 		fis->lba_mid = 0;
1482192ee1d0SMatthew Dillon 		fis->lba_high = 0;
1483192ee1d0SMatthew Dillon 	}
1484192ee1d0SMatthew Dillon 	fis->control = ATA_FIS_CONTROL_4BIT;
14851980eff3SMatthew Dillon 
1486b4189e5eSMatthew Dillon 	xa->flags = flags;
1487b4189e5eSMatthew Dillon 	xa->data = csio->data_ptr;
1488b4189e5eSMatthew Dillon 	xa->datalen = csio->dxfer_len;
14893209f581SMatthew Dillon 	xa->timeout = ccbh->timeout;	/* milliseconds */
14901980eff3SMatthew Dillon 
1491b4189e5eSMatthew Dillon 	if (ccbh->flags & CAM_POLLED)
1492b4189e5eSMatthew Dillon 		xa->flags |= ATA_F_POLL;
1493258223a3SMatthew Dillon 
1494258223a3SMatthew Dillon 	/*
1495b4189e5eSMatthew Dillon 	 * Copy the cdb to the packetcmd buffer in the FIS using a
1496b4189e5eSMatthew Dillon 	 * convenient pointer in the xa.
1497a7d90c87SMatthew Dillon 	 *
1498a7d90c87SMatthew Dillon 	 * Zero-out any trailing bytes in case the ATAPI device cares.
1499258223a3SMatthew Dillon 	 */
1500b4189e5eSMatthew Dillon 	cdbs = (void *)((ccbh->flags & CAM_CDB_POINTER) ?
1501b4189e5eSMatthew Dillon 			csio->cdb_io.cdb_ptr : csio->cdb_io.cdb_bytes);
1502b4189e5eSMatthew Dillon 	bcopy(cdbs, xa->packetcmd, csio->cdb_len);
1503a7d90c87SMatthew Dillon 	if (csio->cdb_len < 16)
1504a7d90c87SMatthew Dillon 		bzero(xa->packetcmd + csio->cdb_len, 16 - csio->cdb_len);
1505b4189e5eSMatthew Dillon 
1506669fbbf7SMatthew Dillon #if 0
1507b4189e5eSMatthew Dillon 	kprintf("opcode %d cdb_len %d dxfer_len %d\n",
1508b4189e5eSMatthew Dillon 		cdbs->generic.opcode,
1509b4189e5eSMatthew Dillon 		csio->cdb_len, csio->dxfer_len);
1510669fbbf7SMatthew Dillon #endif
1511b4189e5eSMatthew Dillon 
1512b4189e5eSMatthew Dillon 	/*
1513b4189e5eSMatthew Dillon 	 * Some ATAPI commands do not actually follow the SCSI standard.
1514b4189e5eSMatthew Dillon 	 */
1515b4189e5eSMatthew Dillon 	cdbd = (void *)xa->packetcmd;
1516b4189e5eSMatthew Dillon 
1517b4189e5eSMatthew Dillon 	switch(cdbd->generic.opcode) {
1518192ee1d0SMatthew Dillon 	case REQUEST_SENSE:
1519192ee1d0SMatthew Dillon 		/*
1520192ee1d0SMatthew Dillon 		 * Force SENSE requests to the ATAPI sense length.
1521192ee1d0SMatthew Dillon 		 *
1522192ee1d0SMatthew Dillon 		 * It is unclear if this is needed or not.
1523192ee1d0SMatthew Dillon 		 */
1524192ee1d0SMatthew Dillon 		if (cdbd->sense.length == SSD_FULL_SIZE) {
1525b012a2caSMatthew Dillon 			if (bootverbose) {
1526192ee1d0SMatthew Dillon 				kprintf("%s: Shortening sense request\n",
1527192ee1d0SMatthew Dillon 					PORTNAME(ap));
1528b012a2caSMatthew Dillon 			}
1529192ee1d0SMatthew Dillon 			cdbd->sense.length = offsetof(struct scsi_sense_data,
1530192ee1d0SMatthew Dillon 						      extra_bytes[0]);
1531192ee1d0SMatthew Dillon 		}
1532192ee1d0SMatthew Dillon 		break;
1533258223a3SMatthew Dillon 	case INQUIRY:
1534b4189e5eSMatthew Dillon 		/*
1535b4189e5eSMatthew Dillon 		 * Some ATAPI devices can't handle long inquiry lengths,
1536b4189e5eSMatthew Dillon 		 * don't ask me why.  Truncate the inquiry length.
1537b4189e5eSMatthew Dillon 		 */
1538b4189e5eSMatthew Dillon 		if (cdbd->inquiry.page_code == 0 &&
1539b4189e5eSMatthew Dillon 		    cdbd->inquiry.length > SHORT_INQUIRY_LENGTH) {
1540b4189e5eSMatthew Dillon 			cdbd->inquiry.length = SHORT_INQUIRY_LENGTH;
1541b4189e5eSMatthew Dillon 		}
1542b4189e5eSMatthew Dillon 		break;
1543258223a3SMatthew Dillon 	case READ_6:
1544258223a3SMatthew Dillon 	case WRITE_6:
1545b4189e5eSMatthew Dillon 		/*
1546b4189e5eSMatthew Dillon 		 * Convert *_6 to *_10 commands.  Most ATAPI devices
1547b4189e5eSMatthew Dillon 		 * cannot handle the SCSI READ_6 and WRITE_6 commands.
1548b4189e5eSMatthew Dillon 		 */
1549b4189e5eSMatthew Dillon 		cdbd->rw_10.opcode |= 0x20;
1550b4189e5eSMatthew Dillon 		cdbd->rw_10.byte2 = 0;
1551b4189e5eSMatthew Dillon 		cdbd->rw_10.addr[0] = cdbs->rw_6.addr[0] & 0x1F;
1552b4189e5eSMatthew Dillon 		cdbd->rw_10.addr[1] = cdbs->rw_6.addr[1];
1553b4189e5eSMatthew Dillon 		cdbd->rw_10.addr[2] = cdbs->rw_6.addr[2];
1554b4189e5eSMatthew Dillon 		cdbd->rw_10.addr[3] = 0;
1555b4189e5eSMatthew Dillon 		cdbd->rw_10.reserved = 0;
1556b4189e5eSMatthew Dillon 		cdbd->rw_10.length[0] = 0;
1557b4189e5eSMatthew Dillon 		cdbd->rw_10.length[1] = cdbs->rw_6.length;
1558b4189e5eSMatthew Dillon 		cdbd->rw_10.control = cdbs->rw_6.control;
1559b4189e5eSMatthew Dillon 		break;
1560258223a3SMatthew Dillon 	default:
1561258223a3SMatthew Dillon 		break;
1562258223a3SMatthew Dillon 	}
1563258223a3SMatthew Dillon 
1564b4189e5eSMatthew Dillon 	/*
1565b4189e5eSMatthew Dillon 	 * And dispatch
1566b4189e5eSMatthew Dillon 	 */
1567b4189e5eSMatthew Dillon 	xa->complete = ahci_atapi_complete_cmd;
1568258223a3SMatthew Dillon 	xa->atascsi_private = ccb;
1569258223a3SMatthew Dillon 	ccb->ccb_h.sim_priv.entries[0].ptr = ap;
157076497a9cSMatthew Dillon 	ahci_os_lock_port(ap);
1571258223a3SMatthew Dillon 	ahci_ata_cmd(xa);
157276497a9cSMatthew Dillon 	ahci_os_unlock_port(ap);
1573258223a3SMatthew Dillon }
1574258223a3SMatthew Dillon 
1575b4189e5eSMatthew Dillon /*
15766e0003adSMatthew Dillon  * Simulate page inquiries for disk attachments.
15776e0003adSMatthew Dillon  */
15786e0003adSMatthew Dillon static
15796e0003adSMatthew Dillon void
15806e0003adSMatthew Dillon ahci_xpt_page_inquiry(struct ahci_port *ap, struct ata_port *at, union ccb *ccb)
15816e0003adSMatthew Dillon {
15826e0003adSMatthew Dillon 	union {
15836e0003adSMatthew Dillon 		struct scsi_vpd_supported_page_list	list;
15846e0003adSMatthew Dillon 		struct scsi_vpd_unit_serial_number	serno;
15856e0003adSMatthew Dillon 		struct scsi_vpd_unit_devid		devid;
15866e0003adSMatthew Dillon 		char					buf[256];
15876e0003adSMatthew Dillon 	} *page;
15886e0003adSMatthew Dillon 	scsi_cdb_t cdb;
15896e0003adSMatthew Dillon 	int i;
15906e0003adSMatthew Dillon 	int j;
15916e0003adSMatthew Dillon 	int len;
15926e0003adSMatthew Dillon 
15936e0003adSMatthew Dillon 	page = kmalloc(sizeof(*page), M_DEVBUF, M_WAITOK | M_ZERO);
15946e0003adSMatthew Dillon 
15956e0003adSMatthew Dillon 	cdb = (void *)((ccb->ccb_h.flags & CAM_CDB_POINTER) ?
15966e0003adSMatthew Dillon 			ccb->csio.cdb_io.cdb_ptr : ccb->csio.cdb_io.cdb_bytes);
15976e0003adSMatthew Dillon 
15986e0003adSMatthew Dillon 	switch(cdb->inquiry.page_code) {
15996e0003adSMatthew Dillon 	case SVPD_SUPPORTED_PAGE_LIST:
16006e0003adSMatthew Dillon 		i = 0;
16016e0003adSMatthew Dillon 		page->list.device = T_DIRECT;
16026e0003adSMatthew Dillon 		page->list.page_code = SVPD_SUPPORTED_PAGE_LIST;
16036e0003adSMatthew Dillon 		page->list.list[i++] = SVPD_SUPPORTED_PAGE_LIST;
16046e0003adSMatthew Dillon 		page->list.list[i++] = SVPD_UNIT_SERIAL_NUMBER;
16056e0003adSMatthew Dillon 		page->list.list[i++] = SVPD_UNIT_DEVID;
16066e0003adSMatthew Dillon 		page->list.length = i;
16076e0003adSMatthew Dillon 		len = offsetof(struct scsi_vpd_supported_page_list, list[3]);
16086e0003adSMatthew Dillon 		break;
16096e0003adSMatthew Dillon 	case SVPD_UNIT_SERIAL_NUMBER:
16106e0003adSMatthew Dillon 		i = 0;
16116e0003adSMatthew Dillon 		j = sizeof(at->at_identify.serial);
16126e0003adSMatthew Dillon 		for (i = 0; i < j && at->at_identify.serial[i] == ' '; ++i)
16136e0003adSMatthew Dillon 			;
16146e0003adSMatthew Dillon 		while (j > i && at->at_identify.serial[j-1] == ' ')
16156e0003adSMatthew Dillon 			--j;
16166e0003adSMatthew Dillon 		page->serno.device = T_DIRECT;
16176e0003adSMatthew Dillon 		page->serno.page_code = SVPD_UNIT_SERIAL_NUMBER;
16186e0003adSMatthew Dillon 		page->serno.length = j - i;
16196e0003adSMatthew Dillon 		bcopy(at->at_identify.serial + i,
16206e0003adSMatthew Dillon 		      page->serno.serial_num, j - i);
16216e0003adSMatthew Dillon 		len = offsetof(struct scsi_vpd_unit_serial_number,
16226e0003adSMatthew Dillon 			       serial_num[j-i]);
16236e0003adSMatthew Dillon 		break;
16246e0003adSMatthew Dillon 	case SVPD_UNIT_DEVID:
16256e0003adSMatthew Dillon 		/* fall through for now */
16266e0003adSMatthew Dillon 	default:
16276e0003adSMatthew Dillon 		ccb->ccb_h.status = CAM_FUNC_NOTAVAIL;
16286e0003adSMatthew Dillon 		len = 0;
16296e0003adSMatthew Dillon 		break;
16306e0003adSMatthew Dillon 	}
16316e0003adSMatthew Dillon 	if (ccb->ccb_h.status == CAM_REQ_INPROG) {
16326e0003adSMatthew Dillon 		if (len <= ccb->csio.dxfer_len) {
16336e0003adSMatthew Dillon 			ccb->ccb_h.status = CAM_REQ_CMP;
16346e0003adSMatthew Dillon 			bzero(ccb->csio.data_ptr, ccb->csio.dxfer_len);
16356e0003adSMatthew Dillon 			bcopy(page, ccb->csio.data_ptr, len);
16366e0003adSMatthew Dillon 			ccb->csio.resid = ccb->csio.dxfer_len - len;
16376e0003adSMatthew Dillon 		} else {
16386e0003adSMatthew Dillon 			ccb->ccb_h.status = CAM_CCB_LEN_ERR;
16396e0003adSMatthew Dillon 		}
16406e0003adSMatthew Dillon 	}
16416e0003adSMatthew Dillon 	kfree(page, M_DEVBUF);
16426e0003adSMatthew Dillon }
16436e0003adSMatthew Dillon 
16446e0003adSMatthew Dillon /*
1645b4189e5eSMatthew Dillon  * Completion function for ATA_PORT_T_DISK cache synchronization.
1646b4189e5eSMatthew Dillon  */
1647258223a3SMatthew Dillon static
1648258223a3SMatthew Dillon void
1649258223a3SMatthew Dillon ahci_ata_complete_disk_synchronize_cache(struct ata_xfer *xa)
1650258223a3SMatthew Dillon {
1651258223a3SMatthew Dillon 	union ccb *ccb = xa->atascsi_private;
1652258223a3SMatthew Dillon 	struct ccb_hdr *ccbh = &ccb->ccb_h;
1653258223a3SMatthew Dillon 	struct ahci_port *ap = ccb->ccb_h.sim_priv.entries[0].ptr;
1654258223a3SMatthew Dillon 
1655258223a3SMatthew Dillon 	switch(xa->state) {
1656258223a3SMatthew Dillon 	case ATA_S_COMPLETE:
1657258223a3SMatthew Dillon 		ccbh->status = CAM_REQ_CMP;
1658b4189e5eSMatthew Dillon 		ccb->csio.scsi_status = SCSI_STATUS_OK;
1659258223a3SMatthew Dillon 		break;
1660258223a3SMatthew Dillon 	case ATA_S_ERROR:
16611980eff3SMatthew Dillon 		kprintf("%s: synchronize_cache: error\n",
16621980eff3SMatthew Dillon 			ATANAME(ap, xa->at));
1663b4189e5eSMatthew Dillon 		ccbh->status = CAM_SCSI_STATUS_ERROR | CAM_AUTOSNS_VALID;
1664b4189e5eSMatthew Dillon 		ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND;
1665b4189e5eSMatthew Dillon 		ahci_ata_dummy_sense(&ccb->csio.sense_data);
1666258223a3SMatthew Dillon 		break;
1667258223a3SMatthew Dillon 	case ATA_S_TIMEOUT:
16681980eff3SMatthew Dillon 		kprintf("%s: synchronize_cache: timeout\n",
16691980eff3SMatthew Dillon 			ATANAME(ap, xa->at));
1670258223a3SMatthew Dillon 		ccbh->status = CAM_CMD_TIMEOUT;
1671258223a3SMatthew Dillon 		break;
1672258223a3SMatthew Dillon 	default:
1673258223a3SMatthew Dillon 		kprintf("%s: synchronize_cache: unknown state %d\n",
16741980eff3SMatthew Dillon 			ATANAME(ap, xa->at), xa->state);
1675*46528d33SMatthew Dillon 		panic("%s: Unknown state", ATANAME(ap, xa->at));
1676258223a3SMatthew Dillon 		ccbh->status = CAM_REQ_CMP_ERR;
1677258223a3SMatthew Dillon 		break;
1678258223a3SMatthew Dillon 	}
1679258223a3SMatthew Dillon 	ahci_ata_put_xfer(xa);
1680f4553de1SMatthew Dillon 	ahci_os_unlock_port(ap);
1681258223a3SMatthew Dillon 	xpt_done(ccb);
1682f4553de1SMatthew Dillon 	ahci_os_lock_port(ap);
1683258223a3SMatthew Dillon }
1684258223a3SMatthew Dillon 
1685b4189e5eSMatthew Dillon /*
1686b4189e5eSMatthew Dillon  * Completion function for ATA_PORT_T_DISK I/O
1687b4189e5eSMatthew Dillon  */
1688258223a3SMatthew Dillon static
1689258223a3SMatthew Dillon void
1690258223a3SMatthew Dillon ahci_ata_complete_disk_rw(struct ata_xfer *xa)
1691258223a3SMatthew Dillon {
1692258223a3SMatthew Dillon 	union ccb *ccb = xa->atascsi_private;
1693258223a3SMatthew Dillon 	struct ccb_hdr *ccbh = &ccb->ccb_h;
1694258223a3SMatthew Dillon 	struct ahci_port *ap = ccb->ccb_h.sim_priv.entries[0].ptr;
1695258223a3SMatthew Dillon 
1696258223a3SMatthew Dillon 	switch(xa->state) {
1697258223a3SMatthew Dillon 	case ATA_S_COMPLETE:
1698258223a3SMatthew Dillon 		ccbh->status = CAM_REQ_CMP;
1699b4189e5eSMatthew Dillon 		ccb->csio.scsi_status = SCSI_STATUS_OK;
1700258223a3SMatthew Dillon 		break;
1701258223a3SMatthew Dillon 	case ATA_S_ERROR:
17021980eff3SMatthew Dillon 		kprintf("%s: disk_rw: error\n", ATANAME(ap, xa->at));
1703b4189e5eSMatthew Dillon 		ccbh->status = CAM_SCSI_STATUS_ERROR | CAM_AUTOSNS_VALID;
1704b4189e5eSMatthew Dillon 		ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND;
1705b4189e5eSMatthew Dillon 		ahci_ata_dummy_sense(&ccb->csio.sense_data);
1706258223a3SMatthew Dillon 		break;
1707258223a3SMatthew Dillon 	case ATA_S_TIMEOUT:
17081980eff3SMatthew Dillon 		kprintf("%s: disk_rw: timeout\n", ATANAME(ap, xa->at));
1709258223a3SMatthew Dillon 		ccbh->status = CAM_CMD_TIMEOUT;
17104c339a5fSMatthew Dillon 		ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND;
17114c339a5fSMatthew Dillon 		ahci_ata_dummy_sense(&ccb->csio.sense_data);
1712258223a3SMatthew Dillon 		break;
1713258223a3SMatthew Dillon 	default:
1714258223a3SMatthew Dillon 		kprintf("%s: disk_rw: unknown state %d\n",
17151980eff3SMatthew Dillon 			ATANAME(ap, xa->at), xa->state);
1716*46528d33SMatthew Dillon 		panic("%s: Unknown state", ATANAME(ap, xa->at));
1717258223a3SMatthew Dillon 		ccbh->status = CAM_REQ_CMP_ERR;
1718258223a3SMatthew Dillon 		break;
1719258223a3SMatthew Dillon 	}
1720258223a3SMatthew Dillon 	ccb->csio.resid = xa->resid;
1721258223a3SMatthew Dillon 	ahci_ata_put_xfer(xa);
1722f4553de1SMatthew Dillon 	ahci_os_unlock_port(ap);
1723258223a3SMatthew Dillon 	xpt_done(ccb);
1724f4553de1SMatthew Dillon 	ahci_os_lock_port(ap);
1725258223a3SMatthew Dillon }
1726b4189e5eSMatthew Dillon 
17277d4fcf34SMatthew Dillon /*
17287d4fcf34SMatthew Dillon  * Completion function for ATA_PORT_T_ATAPI I/O
17297d4fcf34SMatthew Dillon  *
17307d4fcf34SMatthew Dillon  * Sense data is returned in the rfis.
17317d4fcf34SMatthew Dillon  */
1732b4189e5eSMatthew Dillon static
1733b4189e5eSMatthew Dillon void
1734b4189e5eSMatthew Dillon ahci_atapi_complete_cmd(struct ata_xfer *xa)
1735b4189e5eSMatthew Dillon {
1736b4189e5eSMatthew Dillon 	union ccb *ccb = xa->atascsi_private;
1737b4189e5eSMatthew Dillon 	struct ccb_hdr *ccbh = &ccb->ccb_h;
1738b4189e5eSMatthew Dillon 	struct ahci_port *ap = ccb->ccb_h.sim_priv.entries[0].ptr;
1739b4189e5eSMatthew Dillon 	scsi_cdb_t cdb;
1740b4189e5eSMatthew Dillon 
1741b4189e5eSMatthew Dillon 	cdb = (void *)((ccb->ccb_h.flags & CAM_CDB_POINTER) ?
1742b4189e5eSMatthew Dillon 			ccb->csio.cdb_io.cdb_ptr : ccb->csio.cdb_io.cdb_bytes);
1743b4189e5eSMatthew Dillon 
1744b4189e5eSMatthew Dillon 	switch(xa->state) {
1745b4189e5eSMatthew Dillon 	case ATA_S_COMPLETE:
1746b4189e5eSMatthew Dillon 		ccbh->status = CAM_REQ_CMP;
1747b4189e5eSMatthew Dillon 		ccb->csio.scsi_status = SCSI_STATUS_OK;
1748b4189e5eSMatthew Dillon 		break;
1749b4189e5eSMatthew Dillon 	case ATA_S_ERROR:
1750b4189e5eSMatthew Dillon 		ccbh->status = CAM_SCSI_STATUS_ERROR;
1751b4189e5eSMatthew Dillon 		ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND;
17527d4fcf34SMatthew Dillon 		ahci_ata_atapi_sense(&xa->rfis, &ccb->csio.sense_data);
1753b4189e5eSMatthew Dillon 		break;
1754b4189e5eSMatthew Dillon 	case ATA_S_TIMEOUT:
1755b4189e5eSMatthew Dillon 		kprintf("%s: cmd %d: timeout\n",
1756b4189e5eSMatthew Dillon 			PORTNAME(ap), cdb->generic.opcode);
1757b4189e5eSMatthew Dillon 		ccbh->status = CAM_CMD_TIMEOUT;
17584c339a5fSMatthew Dillon 		ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND;
17594c339a5fSMatthew Dillon 		ahci_ata_dummy_sense(&ccb->csio.sense_data);
1760b4189e5eSMatthew Dillon 		break;
1761b4189e5eSMatthew Dillon 	default:
1762b4189e5eSMatthew Dillon 		kprintf("%s: cmd %d: unknown state %d\n",
1763b4189e5eSMatthew Dillon 			PORTNAME(ap), cdb->generic.opcode, xa->state);
1764*46528d33SMatthew Dillon 		panic("%s: Unknown state", PORTNAME(ap));
1765b4189e5eSMatthew Dillon 		ccbh->status = CAM_REQ_CMP_ERR;
1766b4189e5eSMatthew Dillon 		break;
1767b4189e5eSMatthew Dillon 	}
1768b4189e5eSMatthew Dillon 	ccb->csio.resid = xa->resid;
1769b4189e5eSMatthew Dillon 	ahci_ata_put_xfer(xa);
1770f4553de1SMatthew Dillon 	ahci_os_unlock_port(ap);
1771b4189e5eSMatthew Dillon 	xpt_done(ccb);
1772f4553de1SMatthew Dillon 	ahci_os_lock_port(ap);
1773b4189e5eSMatthew Dillon }
1774b4189e5eSMatthew Dillon 
17757d4fcf34SMatthew Dillon /*
17767d4fcf34SMatthew Dillon  * Construct dummy sense data for errors on DISKs
17777d4fcf34SMatthew Dillon  */
1778b4189e5eSMatthew Dillon static
1779b4189e5eSMatthew Dillon void
1780b4189e5eSMatthew Dillon ahci_ata_dummy_sense(struct scsi_sense_data *sense_data)
1781b4189e5eSMatthew Dillon {
1782b4189e5eSMatthew Dillon 	sense_data->error_code = SSD_ERRCODE_VALID | SSD_CURRENT_ERROR;
1783b4189e5eSMatthew Dillon 	sense_data->segment = 0;
1784b4189e5eSMatthew Dillon 	sense_data->flags = SSD_KEY_MEDIUM_ERROR;
1785b4189e5eSMatthew Dillon 	sense_data->info[0] = 0;
1786b4189e5eSMatthew Dillon 	sense_data->info[1] = 0;
1787b4189e5eSMatthew Dillon 	sense_data->info[2] = 0;
1788b4189e5eSMatthew Dillon 	sense_data->info[3] = 0;
1789b4189e5eSMatthew Dillon 	sense_data->extra_len = 0;
1790b4189e5eSMatthew Dillon }
17917d4fcf34SMatthew Dillon 
17927d4fcf34SMatthew Dillon /*
17937d4fcf34SMatthew Dillon  * Construct atapi sense data for errors on ATAPI
17947d4fcf34SMatthew Dillon  *
17957d4fcf34SMatthew Dillon  * The ATAPI sense data is stored in the passed rfis and must be converted
17967d4fcf34SMatthew Dillon  * to SCSI sense data.
17977d4fcf34SMatthew Dillon  */
17987d4fcf34SMatthew Dillon static
17997d4fcf34SMatthew Dillon void
18007d4fcf34SMatthew Dillon ahci_ata_atapi_sense(struct ata_fis_d2h *rfis,
18017d4fcf34SMatthew Dillon 		     struct scsi_sense_data *sense_data)
18027d4fcf34SMatthew Dillon {
18037d4fcf34SMatthew Dillon 	sense_data->error_code = SSD_ERRCODE_VALID | SSD_CURRENT_ERROR;
18047d4fcf34SMatthew Dillon 	sense_data->segment = 0;
18057d4fcf34SMatthew Dillon 	sense_data->flags = (rfis->error & 0xF0) >> 4;
18067d4fcf34SMatthew Dillon 	if (rfis->error & 0x04)
18077d4fcf34SMatthew Dillon 		sense_data->flags |= SSD_KEY_ILLEGAL_REQUEST;
18087d4fcf34SMatthew Dillon 	if (rfis->error & 0x02)
18097d4fcf34SMatthew Dillon 		sense_data->flags |= SSD_EOM;
18107d4fcf34SMatthew Dillon 	if (rfis->error & 0x01)
18117d4fcf34SMatthew Dillon 		sense_data->flags |= SSD_ILI;
18127d4fcf34SMatthew Dillon 	sense_data->info[0] = 0;
18137d4fcf34SMatthew Dillon 	sense_data->info[1] = 0;
18147d4fcf34SMatthew Dillon 	sense_data->info[2] = 0;
18157d4fcf34SMatthew Dillon 	sense_data->info[3] = 0;
18167d4fcf34SMatthew Dillon 	sense_data->extra_len = 0;
18177d4fcf34SMatthew Dillon }
18186e0003adSMatthew Dillon 
18196e0003adSMatthew Dillon static
18206e0003adSMatthew Dillon void
18216e0003adSMatthew Dillon ahci_strip_string(const char **basep, int *lenp)
18226e0003adSMatthew Dillon {
18236e0003adSMatthew Dillon 	const char *base = *basep;
18246e0003adSMatthew Dillon 	int len = *lenp;
18256e0003adSMatthew Dillon 
18266e0003adSMatthew Dillon 	while (len && (*base == 0 || *base == ' ')) {
18276e0003adSMatthew Dillon 		--len;
18286e0003adSMatthew Dillon 		++base;
18296e0003adSMatthew Dillon 	}
18306e0003adSMatthew Dillon 	while (len && (base[len-1] == 0 || base[len-1] == ' '))
18316e0003adSMatthew Dillon 		--len;
18326e0003adSMatthew Dillon 	*basep = base;
18336e0003adSMatthew Dillon 	*lenp = len;
18346e0003adSMatthew Dillon }
1835