xref: /dflybsd-src/sys/dev/disk/ahci/ahci_cam.c (revision 074579dffc8ecd16b19974dbc7c7c19f92160f39)
1258223a3SMatthew Dillon /*
2258223a3SMatthew Dillon  * Copyright (c) 2009 The DragonFly Project.  All rights reserved.
3258223a3SMatthew Dillon  *
4258223a3SMatthew Dillon  * This code is derived from software contributed to The DragonFly Project
5258223a3SMatthew Dillon  * by Matthew Dillon <dillon@backplane.com>
6258223a3SMatthew Dillon  *
7258223a3SMatthew Dillon  * Redistribution and use in source and binary forms, with or without
8258223a3SMatthew Dillon  * modification, are permitted provided that the following conditions
9258223a3SMatthew Dillon  * are met:
10258223a3SMatthew Dillon  *
11258223a3SMatthew Dillon  * 1. Redistributions of source code must retain the above copyright
12258223a3SMatthew Dillon  *    notice, this list of conditions and the following disclaimer.
13258223a3SMatthew Dillon  * 2. Redistributions in binary form must reproduce the above copyright
14258223a3SMatthew Dillon  *    notice, this list of conditions and the following disclaimer in
15258223a3SMatthew Dillon  *    the documentation and/or other materials provided with the
16258223a3SMatthew Dillon  *    distribution.
17258223a3SMatthew Dillon  * 3. Neither the name of The DragonFly Project nor the names of its
18258223a3SMatthew Dillon  *    contributors may be used to endorse or promote products derived
19258223a3SMatthew Dillon  *    from this software without specific, prior written permission.
20258223a3SMatthew Dillon  *
21258223a3SMatthew Dillon  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22258223a3SMatthew Dillon  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23258223a3SMatthew Dillon  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24258223a3SMatthew Dillon  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25258223a3SMatthew Dillon  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26258223a3SMatthew Dillon  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27258223a3SMatthew Dillon  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28258223a3SMatthew Dillon  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29258223a3SMatthew Dillon  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30258223a3SMatthew Dillon  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31258223a3SMatthew Dillon  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32258223a3SMatthew Dillon  * SUCH DAMAGE.
33258223a3SMatthew Dillon  *
34258223a3SMatthew Dillon  *
35258223a3SMatthew Dillon  * Copyright (c) 2007 David Gwynne <dlg@openbsd.org>
36258223a3SMatthew Dillon  *
37258223a3SMatthew Dillon  * Permission to use, copy, modify, and distribute this software for any
38258223a3SMatthew Dillon  * purpose with or without fee is hereby granted, provided that the above
39258223a3SMatthew Dillon  * copyright notice and this permission notice appear in all copies.
40258223a3SMatthew Dillon  *
41258223a3SMatthew Dillon  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
42258223a3SMatthew Dillon  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
43258223a3SMatthew Dillon  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
44258223a3SMatthew Dillon  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
45258223a3SMatthew Dillon  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
46258223a3SMatthew Dillon  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
47258223a3SMatthew Dillon  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
48258223a3SMatthew Dillon  *
49258223a3SMatthew Dillon  * $OpenBSD: atascsi.c,v 1.64 2009/02/16 21:19:06 miod Exp $
50258223a3SMatthew Dillon  * $DragonFly$
51258223a3SMatthew Dillon  */
52258223a3SMatthew Dillon /*
53258223a3SMatthew Dillon  * Implement each SATA port as its own SCSI bus on CAM.  This way we can
54258223a3SMatthew Dillon  * implement future port multiplier features as individual devices on the
55258223a3SMatthew Dillon  * bus.
56258223a3SMatthew Dillon  *
57258223a3SMatthew Dillon  * Much of the cdb<->xa conversion code was taken from OpenBSD, the rest
58258223a3SMatthew Dillon  * was written natively for DragonFly.
59258223a3SMatthew Dillon  */
60258223a3SMatthew Dillon 
61258223a3SMatthew Dillon #include "ahci.h"
62258223a3SMatthew Dillon 
63b4189e5eSMatthew Dillon const char *ScsiTypeArray[32] = {
64b4189e5eSMatthew Dillon 	"DIRECT",
65b4189e5eSMatthew Dillon 	"SEQUENTIAL",
66b4189e5eSMatthew Dillon 	"PRINTER",
67b4189e5eSMatthew Dillon 	"PROCESSOR",
68b4189e5eSMatthew Dillon 	"WORM",
69b4189e5eSMatthew Dillon 	"CDROM",
70b4189e5eSMatthew Dillon 	"SCANNER",
71b4189e5eSMatthew Dillon 	"OPTICAL",
72b4189e5eSMatthew Dillon 	"CHANGER",
73b4189e5eSMatthew Dillon 	"COMM",
74b4189e5eSMatthew Dillon 	"ASC0",
75b4189e5eSMatthew Dillon 	"ASC1",
76b4189e5eSMatthew Dillon 	"STORARRAY",
77b4189e5eSMatthew Dillon 	"ENCLOSURE",
78b4189e5eSMatthew Dillon 	"RBC",
79b4189e5eSMatthew Dillon 	"OCRW",
80b4189e5eSMatthew Dillon 	"0x10",
81b4189e5eSMatthew Dillon 	"OSD",
82b4189e5eSMatthew Dillon 	"ADC",
83b4189e5eSMatthew Dillon 	"0x13",
84b4189e5eSMatthew Dillon 	"0x14",
85b4189e5eSMatthew Dillon 	"0x15",
86b4189e5eSMatthew Dillon 	"0x16",
87b4189e5eSMatthew Dillon 	"0x17",
88b4189e5eSMatthew Dillon 	"0x18",
89b4189e5eSMatthew Dillon 	"0x19",
90b4189e5eSMatthew Dillon 	"0x1A",
91b4189e5eSMatthew Dillon 	"0x1B",
92b4189e5eSMatthew Dillon 	"0x1C",
93b4189e5eSMatthew Dillon 	"0x1D",
94b4189e5eSMatthew Dillon 	"0x1E",
95b4189e5eSMatthew Dillon 	"NODEVICE"
96b4189e5eSMatthew Dillon };
97b4189e5eSMatthew Dillon 
98258223a3SMatthew Dillon static void ahci_xpt_action(struct cam_sim *sim, union ccb *ccb);
99258223a3SMatthew Dillon static void ahci_xpt_poll(struct cam_sim *sim);
1001980eff3SMatthew Dillon static void ahci_xpt_scsi_disk_io(struct ahci_port *ap,
1011980eff3SMatthew Dillon 			struct ata_port *at, union ccb *ccb);
1021980eff3SMatthew Dillon static void ahci_xpt_scsi_atapi_io(struct ahci_port *ap,
1031980eff3SMatthew Dillon 			struct ata_port *at, union ccb *ccb);
104258223a3SMatthew Dillon 
105258223a3SMatthew Dillon static void ahci_ata_complete_disk_rw(struct ata_xfer *xa);
106258223a3SMatthew Dillon static void ahci_ata_complete_disk_synchronize_cache(struct ata_xfer *xa);
107b4189e5eSMatthew Dillon static void ahci_atapi_complete_cmd(struct ata_xfer *xa);
108b4189e5eSMatthew Dillon static void ahci_ata_dummy_sense(struct scsi_sense_data *sense_data);
1097d4fcf34SMatthew Dillon static void ahci_ata_atapi_sense(struct ata_fis_d2h *rfis,
1107d4fcf34SMatthew Dillon 		     struct scsi_sense_data *sense_data);
111258223a3SMatthew Dillon 
1121980eff3SMatthew Dillon static int ahci_cam_probe_disk(struct ahci_port *ap, struct ata_port *at);
1131980eff3SMatthew Dillon static int ahci_cam_probe_atapi(struct ahci_port *ap, struct ata_port *at);
114b4189e5eSMatthew Dillon static void ahci_ata_dummy_done(struct ata_xfer *xa);
115258223a3SMatthew Dillon static void ata_fix_identify(struct ata_identify *id);
116258223a3SMatthew Dillon static void ahci_cam_rescan(struct ahci_port *ap);
117258223a3SMatthew Dillon 
118258223a3SMatthew Dillon int
119258223a3SMatthew Dillon ahci_cam_attach(struct ahci_port *ap)
120258223a3SMatthew Dillon {
121258223a3SMatthew Dillon 	struct cam_devq *devq;
122258223a3SMatthew Dillon 	struct cam_sim *sim;
123258223a3SMatthew Dillon 	int error;
124258223a3SMatthew Dillon 	int unit;
125258223a3SMatthew Dillon 
126cec85a37SMatthew Dillon 	/*
127cec85a37SMatthew Dillon 	 * We want at least one ccb to be available for error processing
128cec85a37SMatthew Dillon 	 * so don't let CAM use more then ncmds - 1.
129cec85a37SMatthew Dillon 	 */
130258223a3SMatthew Dillon 	unit = device_get_unit(ap->ap_sc->sc_dev);
131cec85a37SMatthew Dillon 	if (ap->ap_sc->sc_ncmds > 1)
132cec85a37SMatthew Dillon 		devq = cam_simq_alloc(ap->ap_sc->sc_ncmds - 1);
133cec85a37SMatthew Dillon 	else
134258223a3SMatthew Dillon 		devq = cam_simq_alloc(ap->ap_sc->sc_ncmds);
135258223a3SMatthew Dillon 	if (devq == NULL) {
136258223a3SMatthew Dillon 		return (ENOMEM);
137258223a3SMatthew Dillon 	}
138258223a3SMatthew Dillon 	sim = cam_sim_alloc(ahci_xpt_action, ahci_xpt_poll, "ahci",
139258223a3SMatthew Dillon 			   (void *)ap, unit, &sim_mplock, 1, 1, devq);
140258223a3SMatthew Dillon 	cam_simq_release(devq);
141258223a3SMatthew Dillon 	if (sim == NULL) {
142258223a3SMatthew Dillon 		return (ENOMEM);
143258223a3SMatthew Dillon 	}
144258223a3SMatthew Dillon 	ap->ap_sim = sim;
145831bc9e3SMatthew Dillon 	ahci_os_unlock_port(ap);
146258223a3SMatthew Dillon 	error = xpt_bus_register(ap->ap_sim, ap->ap_num);
147831bc9e3SMatthew Dillon 	ahci_os_lock_port(ap);
148258223a3SMatthew Dillon 	if (error != CAM_SUCCESS) {
149258223a3SMatthew Dillon 		ahci_cam_detach(ap);
150258223a3SMatthew Dillon 		return (EINVAL);
151258223a3SMatthew Dillon 	}
152258223a3SMatthew Dillon 	ap->ap_flags |= AP_F_BUS_REGISTERED;
153258223a3SMatthew Dillon 
154c408a8b3SMatthew Dillon 	if (ap->ap_probe == ATA_PROBE_NEED_IDENT)
1551980eff3SMatthew Dillon 		error = ahci_cam_probe(ap, NULL);
156c408a8b3SMatthew Dillon 	else
157c408a8b3SMatthew Dillon 		error = 0;
158258223a3SMatthew Dillon 	if (error) {
159258223a3SMatthew Dillon 		ahci_cam_detach(ap);
160258223a3SMatthew Dillon 		return (EIO);
161258223a3SMatthew Dillon 	}
162258223a3SMatthew Dillon 	ap->ap_flags |= AP_F_CAM_ATTACHED;
163258223a3SMatthew Dillon 
164258223a3SMatthew Dillon 	return(0);
165258223a3SMatthew Dillon }
166258223a3SMatthew Dillon 
1671980eff3SMatthew Dillon /*
1683209f581SMatthew Dillon  * The state of the port has changed.
1693209f581SMatthew Dillon  *
1703209f581SMatthew Dillon  * If at is NULL the physical port has changed state.
1713209f581SMatthew Dillon  * If at is non-NULL a particular target behind a PM has changed state.
1723209f581SMatthew Dillon  *
1733209f581SMatthew Dillon  * If found is -1 the target state must be queued to a non-interrupt context.
1743209f581SMatthew Dillon  * (only works with at == NULL).
1753209f581SMatthew Dillon  *
1763209f581SMatthew Dillon  * If found is 0 the target was removed.
1773209f581SMatthew Dillon  * If found is 1 the target was inserted.
1781980eff3SMatthew Dillon  */
179258223a3SMatthew Dillon void
1803209f581SMatthew Dillon ahci_cam_changed(struct ahci_port *ap, struct ata_port *atx, int found)
181258223a3SMatthew Dillon {
182fd8bd957SMatthew Dillon 	struct cam_path *tmppath;
1833209f581SMatthew Dillon 	int status;
1843209f581SMatthew Dillon 	int target;
1853209f581SMatthew Dillon 
1863209f581SMatthew Dillon 	target = atx ? atx->at_target : CAM_TARGET_WILDCARD;
187fd8bd957SMatthew Dillon 
188258223a3SMatthew Dillon 	if (ap->ap_sim == NULL)
189258223a3SMatthew Dillon 		return;
1903209f581SMatthew Dillon 	if (found == CAM_TARGET_WILDCARD) {
1913209f581SMatthew Dillon 		status = xpt_create_path(&tmppath, NULL,
1923209f581SMatthew Dillon 					 cam_sim_path(ap->ap_sim),
1933209f581SMatthew Dillon 					 target, CAM_LUN_WILDCARD);
1943209f581SMatthew Dillon 		if (status != CAM_REQ_CMP)
195fd8bd957SMatthew Dillon 			return;
196258223a3SMatthew Dillon 		ahci_cam_rescan(ap);
197fd8bd957SMatthew Dillon 	} else {
1983209f581SMatthew Dillon 		status = xpt_create_path(&tmppath, NULL,
1993209f581SMatthew Dillon 					 cam_sim_path(ap->ap_sim),
2003209f581SMatthew Dillon 					 target,
2013209f581SMatthew Dillon 					 CAM_LUN_WILDCARD);
2023209f581SMatthew Dillon 		if (status != CAM_REQ_CMP)
2033209f581SMatthew Dillon 			return;
2043209f581SMatthew Dillon #if 0
2053209f581SMatthew Dillon 		/*
2063209f581SMatthew Dillon 		 * This confuses CAM
2073209f581SMatthew Dillon 		 */
2083209f581SMatthew Dillon 		if (found)
2093209f581SMatthew Dillon 			xpt_async(AC_FOUND_DEVICE, tmppath, NULL);
2103209f581SMatthew Dillon 		else
211fd8bd957SMatthew Dillon 			xpt_async(AC_LOST_DEVICE, tmppath, NULL);
2123209f581SMatthew Dillon #endif
213fd8bd957SMatthew Dillon 	}
214fd8bd957SMatthew Dillon 	xpt_free_path(tmppath);
215258223a3SMatthew Dillon }
216258223a3SMatthew Dillon 
217258223a3SMatthew Dillon void
218258223a3SMatthew Dillon ahci_cam_detach(struct ahci_port *ap)
219258223a3SMatthew Dillon {
220258223a3SMatthew Dillon 	int error;
221258223a3SMatthew Dillon 
222258223a3SMatthew Dillon 	if ((ap->ap_flags & AP_F_CAM_ATTACHED) == 0)
223258223a3SMatthew Dillon 		return;
224258223a3SMatthew Dillon 	get_mplock();
225258223a3SMatthew Dillon 	if (ap->ap_sim) {
226258223a3SMatthew Dillon 		xpt_freeze_simq(ap->ap_sim, 1);
227258223a3SMatthew Dillon 	}
228258223a3SMatthew Dillon 	if (ap->ap_flags & AP_F_BUS_REGISTERED) {
229258223a3SMatthew Dillon 		error = xpt_bus_deregister(cam_sim_path(ap->ap_sim));
230258223a3SMatthew Dillon 		KKASSERT(error == CAM_REQ_CMP);
231258223a3SMatthew Dillon 		ap->ap_flags &= ~AP_F_BUS_REGISTERED;
232258223a3SMatthew Dillon 	}
233258223a3SMatthew Dillon 	if (ap->ap_sim) {
234258223a3SMatthew Dillon 		cam_sim_free(ap->ap_sim);
235258223a3SMatthew Dillon 		ap->ap_sim = NULL;
236258223a3SMatthew Dillon 	}
237258223a3SMatthew Dillon 	rel_mplock();
238258223a3SMatthew Dillon 	ap->ap_flags &= ~AP_F_CAM_ATTACHED;
239258223a3SMatthew Dillon }
240258223a3SMatthew Dillon 
241258223a3SMatthew Dillon /*
2421980eff3SMatthew Dillon  * Once the AHCI port has been attached we need to probe for a device or
243258223a3SMatthew Dillon  * devices on the port and setup various options.
2441980eff3SMatthew Dillon  *
2451980eff3SMatthew Dillon  * If at is NULL we are probing the direct-attached device on the port,
2461980eff3SMatthew Dillon  * which may or may not be a port multiplier.
247258223a3SMatthew Dillon  */
2483209f581SMatthew Dillon int
2491980eff3SMatthew Dillon ahci_cam_probe(struct ahci_port *ap, struct ata_port *atx)
250258223a3SMatthew Dillon {
2511980eff3SMatthew Dillon 	struct ata_port	*at;
252258223a3SMatthew Dillon 	struct ata_xfer	*xa;
253258223a3SMatthew Dillon 	u_int64_t	capacity;
254258223a3SMatthew Dillon 	u_int64_t	capacity_bytes;
255258223a3SMatthew Dillon 	int		model_len;
256fd8bd957SMatthew Dillon 	int		error;
257258223a3SMatthew Dillon 	int		devncqdepth;
258258223a3SMatthew Dillon 	int		i;
259669fbbf7SMatthew Dillon 	const char	*wcstr;
260669fbbf7SMatthew Dillon 	const char	*rastr;
261fd8bd957SMatthew Dillon 	const char	*scstr;
262fd8bd957SMatthew Dillon 	const char	*type;
263fd8bd957SMatthew Dillon 
2643209f581SMatthew Dillon 	error = EIO;
2651980eff3SMatthew Dillon 
2661980eff3SMatthew Dillon 	/*
267f4553de1SMatthew Dillon 	 * Delayed CAM attachment for initial probe, sim may be NULL
268f4553de1SMatthew Dillon 	 */
269f4553de1SMatthew Dillon 	if (ap->ap_sim == NULL)
270f4553de1SMatthew Dillon 		return(0);
271f4553de1SMatthew Dillon 
272f4553de1SMatthew Dillon 	/*
2731980eff3SMatthew Dillon 	 * A NULL atx indicates a probe of the directly connected device.
2741980eff3SMatthew Dillon 	 * A non-NULL atx indicates a device connected via a port multiplier.
2751980eff3SMatthew Dillon 	 * We need to preserve atx for calls to ahci_ata_get_xfer().
2761980eff3SMatthew Dillon 	 *
2771980eff3SMatthew Dillon 	 * at is always non-NULL.  For directly connected devices we supply
2781980eff3SMatthew Dillon 	 * an (at) pointing to target 0.
2791980eff3SMatthew Dillon 	 */
2801980eff3SMatthew Dillon 	if (atx == NULL) {
2813209f581SMatthew Dillon 		at = ap->ap_ata;	/* direct attached - device 0 */
2821980eff3SMatthew Dillon 		if (ap->ap_type == ATA_PORT_T_PM) {
283831bc9e3SMatthew Dillon 			kprintf("%s: Found Port Multiplier\n",
284831bc9e3SMatthew Dillon 				ATANAME(ap, atx));
2851980eff3SMatthew Dillon 			return (0);
2861980eff3SMatthew Dillon 		}
2871980eff3SMatthew Dillon 		at->at_type = ap->ap_type;
2881980eff3SMatthew Dillon 	} else {
2893209f581SMatthew Dillon 		at = atx;
2901980eff3SMatthew Dillon 		if (atx->at_type == ATA_PORT_T_PM) {
2911980eff3SMatthew Dillon 			kprintf("%s: Bogus device, reducing port count to %d\n",
2921980eff3SMatthew Dillon 				ATANAME(ap, atx), atx->at_target);
2931980eff3SMatthew Dillon 			if (ap->ap_pmcount > atx->at_target)
2941980eff3SMatthew Dillon 				ap->ap_pmcount = atx->at_target;
2953209f581SMatthew Dillon 			goto err;
2961980eff3SMatthew Dillon 		}
2971980eff3SMatthew Dillon 	}
2983209f581SMatthew Dillon 	if (ap->ap_type == ATA_PORT_T_NONE)
2993209f581SMatthew Dillon 		goto err;
3001980eff3SMatthew Dillon 	if (at->at_type == ATA_PORT_T_NONE)
3013209f581SMatthew Dillon 		goto err;
302258223a3SMatthew Dillon 
303258223a3SMatthew Dillon 	/*
304258223a3SMatthew Dillon 	 * Issue identify, saving the result
305258223a3SMatthew Dillon 	 */
3061980eff3SMatthew Dillon 	xa = ahci_ata_get_xfer(ap, atx);
307258223a3SMatthew Dillon 	xa->complete = ahci_ata_dummy_done;
3081980eff3SMatthew Dillon 	xa->data = &at->at_identify;
3091980eff3SMatthew Dillon 	xa->datalen = sizeof(at->at_identify);
3101980eff3SMatthew Dillon 	xa->fis->flags = ATA_H2D_FLAGS_CMD | at->at_target;
3111980eff3SMatthew Dillon 
3121980eff3SMatthew Dillon 	switch(at->at_type) {
3131980eff3SMatthew Dillon 	case ATA_PORT_T_DISK:
314258223a3SMatthew Dillon 		xa->fis->command = ATA_C_IDENTIFY;
315fd8bd957SMatthew Dillon 		type = "DISK";
3161980eff3SMatthew Dillon 		break;
3171980eff3SMatthew Dillon 	case ATA_PORT_T_ATAPI:
3181980eff3SMatthew Dillon 		xa->fis->command = ATA_C_ATAPI_IDENTIFY;
3191980eff3SMatthew Dillon 		type = "ATAPI";
3201980eff3SMatthew Dillon 		break;
3211980eff3SMatthew Dillon 	default:
3221980eff3SMatthew Dillon 		xa->fis->command = ATA_C_ATAPI_IDENTIFY;
3231980eff3SMatthew Dillon 		type = "UNKNOWN(ATAPI?)";
3241980eff3SMatthew Dillon 		break;
325fd8bd957SMatthew Dillon 	}
326258223a3SMatthew Dillon 	xa->fis->features = 0;
327258223a3SMatthew Dillon 	xa->fis->device = 0;
328258223a3SMatthew Dillon 	xa->flags = ATA_F_READ | ATA_F_PIO | ATA_F_POLL;
3293209f581SMatthew Dillon 	xa->timeout = 1000;
330258223a3SMatthew Dillon 
331831bc9e3SMatthew Dillon 	if (ahci_ata_cmd(xa) != ATA_S_COMPLETE) {
332fd8bd957SMatthew Dillon 		kprintf("%s: Detected %s device but unable to IDENTIFY\n",
3331980eff3SMatthew Dillon 			ATANAME(ap, atx), type);
334258223a3SMatthew Dillon 		ahci_ata_put_xfer(xa);
3353209f581SMatthew Dillon 		goto err;
336258223a3SMatthew Dillon 	}
337258223a3SMatthew Dillon 	ahci_ata_put_xfer(xa);
338258223a3SMatthew Dillon 
3391980eff3SMatthew Dillon 	ata_fix_identify(&at->at_identify);
340258223a3SMatthew Dillon 
341258223a3SMatthew Dillon 	/*
342258223a3SMatthew Dillon 	 * Read capacity using SATA probe info.
343258223a3SMatthew Dillon 	 */
3441980eff3SMatthew Dillon 	if (le16toh(at->at_identify.cmdset83) & 0x0400) {
345258223a3SMatthew Dillon 		/* LBA48 feature set supported */
346258223a3SMatthew Dillon 		capacity = 0;
347258223a3SMatthew Dillon 		for (i = 3; i >= 0; --i) {
348258223a3SMatthew Dillon 			capacity <<= 16;
349258223a3SMatthew Dillon 			capacity +=
3501980eff3SMatthew Dillon 			    le16toh(at->at_identify.addrsecxt[i]);
351258223a3SMatthew Dillon 		}
352258223a3SMatthew Dillon 	} else {
3531980eff3SMatthew Dillon 		capacity = le16toh(at->at_identify.addrsec[1]);
354258223a3SMatthew Dillon 		capacity <<= 16;
3551980eff3SMatthew Dillon 		capacity += le16toh(at->at_identify.addrsec[0]);
356258223a3SMatthew Dillon 	}
3571980eff3SMatthew Dillon 	at->at_capacity = capacity;
3581980eff3SMatthew Dillon 	if (atx == NULL)
3591980eff3SMatthew Dillon 		ap->ap_probe = ATA_PROBE_GOOD;
360258223a3SMatthew Dillon 
361258223a3SMatthew Dillon 	capacity_bytes = capacity * 512;
362258223a3SMatthew Dillon 
363258223a3SMatthew Dillon 	/*
364258223a3SMatthew Dillon 	 * Negotiate NCQ, throw away any ata_xfer's beyond the negotiated
365258223a3SMatthew Dillon 	 * number of slots and limit the number of CAM ccb's to one less
366258223a3SMatthew Dillon 	 * so we always have a slot available for recovery.
367258223a3SMatthew Dillon 	 *
368258223a3SMatthew Dillon 	 * NCQ is not used if ap_ncqdepth is 1 or the host controller does
369258223a3SMatthew Dillon 	 * not support it, and in that case the driver can handle extra
370258223a3SMatthew Dillon 	 * ccb's.
371cec85a37SMatthew Dillon 	 *
3721980eff3SMatthew Dillon 	 * NCQ is currently used only with direct-attached disks.  It is
3731980eff3SMatthew Dillon 	 * not used with port multipliers or direct-attached ATAPI devices.
3741980eff3SMatthew Dillon 	 *
375cec85a37SMatthew Dillon 	 * Remember at least one extra CCB needs to be reserved for the
376cec85a37SMatthew Dillon 	 * error ccb.
377258223a3SMatthew Dillon 	 */
378258223a3SMatthew Dillon 	if ((ap->ap_sc->sc_cap & AHCI_REG_CAP_SNCQ) &&
3791980eff3SMatthew Dillon 	    ap->ap_type == ATA_PORT_T_DISK &&
3801980eff3SMatthew Dillon 	    (le16toh(at->at_identify.satacap) & (1 << 8))) {
3811980eff3SMatthew Dillon 		at->at_ncqdepth = (le16toh(at->at_identify.qdepth) & 0x1F) + 1;
3821980eff3SMatthew Dillon 		devncqdepth = at->at_ncqdepth;
3831980eff3SMatthew Dillon 		if (at->at_ncqdepth > ap->ap_sc->sc_ncmds)
3841980eff3SMatthew Dillon 			at->at_ncqdepth = ap->ap_sc->sc_ncmds;
3851980eff3SMatthew Dillon 		if (at->at_ncqdepth > 1) {
386258223a3SMatthew Dillon 			for (i = 0; i < ap->ap_sc->sc_ncmds; ++i) {
3871980eff3SMatthew Dillon 				xa = ahci_ata_get_xfer(ap, atx);
3881980eff3SMatthew Dillon 				if (xa->tag < at->at_ncqdepth) {
389258223a3SMatthew Dillon 					xa->state = ATA_S_COMPLETE;
390258223a3SMatthew Dillon 					ahci_ata_put_xfer(xa);
391258223a3SMatthew Dillon 				}
392258223a3SMatthew Dillon 			}
3931980eff3SMatthew Dillon 			if (at->at_ncqdepth >= ap->ap_sc->sc_ncmds) {
394258223a3SMatthew Dillon 				cam_devq_resize(ap->ap_sim->devq,
3951980eff3SMatthew Dillon 						at->at_ncqdepth - 1);
396258223a3SMatthew Dillon 			}
397cec85a37SMatthew Dillon 		}
398258223a3SMatthew Dillon 	} else {
399258223a3SMatthew Dillon 		devncqdepth = 0;
400258223a3SMatthew Dillon 	}
401258223a3SMatthew Dillon 
402fd8bd957SMatthew Dillon 	/*
403fd8bd957SMatthew Dillon 	 * Make the model string a bit more presentable
404fd8bd957SMatthew Dillon 	 */
405258223a3SMatthew Dillon 	for (model_len = 40; model_len; --model_len) {
4061980eff3SMatthew Dillon 		if (at->at_identify.model[model_len-1] == ' ')
407258223a3SMatthew Dillon 			continue;
4081980eff3SMatthew Dillon 		if (at->at_identify.model[model_len-1] == 0)
409258223a3SMatthew Dillon 			continue;
410258223a3SMatthew Dillon 		break;
411258223a3SMatthew Dillon 	}
412669fbbf7SMatthew Dillon 
413fd8bd957SMatthew Dillon 	/*
414fd8bd957SMatthew Dillon 	 * Generate informatiive strings.
415fd8bd957SMatthew Dillon 	 *
416fd8bd957SMatthew Dillon 	 * NOTE: We do not automatically set write caching, lookahead,
417fd8bd957SMatthew Dillon 	 *	 or the security state for ATAPI devices.
418fd8bd957SMatthew Dillon 	 */
4191980eff3SMatthew Dillon 	if (at->at_identify.cmdset82 & ATA_IDENTIFY_WRITECACHE) {
4201980eff3SMatthew Dillon 		if (at->at_identify.features85 & ATA_IDENTIFY_WRITECACHE)
421669fbbf7SMatthew Dillon 			wcstr = "enabled";
4221980eff3SMatthew Dillon 		else if (at->at_type == ATA_PORT_T_ATAPI)
423fd8bd957SMatthew Dillon 			wcstr = "disabled";
424669fbbf7SMatthew Dillon 		else
425669fbbf7SMatthew Dillon 			wcstr = "enabling";
426669fbbf7SMatthew Dillon 	} else {
427669fbbf7SMatthew Dillon 		    wcstr = "notsupp";
428669fbbf7SMatthew Dillon 	}
429669fbbf7SMatthew Dillon 
4301980eff3SMatthew Dillon 	if (at->at_identify.cmdset82 & ATA_IDENTIFY_LOOKAHEAD) {
4311980eff3SMatthew Dillon 		if (at->at_identify.features85 & ATA_IDENTIFY_LOOKAHEAD)
432669fbbf7SMatthew Dillon 			rastr = "enabled";
4331980eff3SMatthew Dillon 		else if (at->at_type == ATA_PORT_T_ATAPI)
434fd8bd957SMatthew Dillon 			rastr = "disabled";
435669fbbf7SMatthew Dillon 		else
436669fbbf7SMatthew Dillon 			rastr = "enabling";
437669fbbf7SMatthew Dillon 	} else {
438669fbbf7SMatthew Dillon 		    rastr = "notsupp";
439669fbbf7SMatthew Dillon 	}
440669fbbf7SMatthew Dillon 
4411980eff3SMatthew Dillon 	if (at->at_identify.cmdset82 & ATA_IDENTIFY_SECURITY) {
4421980eff3SMatthew Dillon 		if (at->at_identify.securestatus & ATA_SECURE_FROZEN)
443fd8bd957SMatthew Dillon 			scstr = "frozen";
4441980eff3SMatthew Dillon 		else if (at->at_type == ATA_PORT_T_ATAPI)
445fd8bd957SMatthew Dillon 			scstr = "unfrozen";
446fd8bd957SMatthew Dillon 		else
447fd8bd957SMatthew Dillon 			scstr = "freezing";
448fd8bd957SMatthew Dillon 	} else {
449fd8bd957SMatthew Dillon 		    scstr = "notsupp";
450fd8bd957SMatthew Dillon 	}
451fd8bd957SMatthew Dillon 
452fd8bd957SMatthew Dillon 	kprintf("%s: Found %s \"%*.*s %8.8s\" serial=\"%20.20s\"\n"
453*074579dfSMatthew Dillon 		"%s: tags=%d/%d satacap=%04x satafea=%04x NCQ=%s "
454*074579dfSMatthew Dillon 		"capacity=%lld.%02dMB\n",
455*074579dfSMatthew Dillon 
4561980eff3SMatthew Dillon 		ATANAME(ap, atx),
457fd8bd957SMatthew Dillon 		type,
458258223a3SMatthew Dillon 		model_len, model_len,
4591980eff3SMatthew Dillon 		at->at_identify.model,
4601980eff3SMatthew Dillon 		at->at_identify.firmware,
4611980eff3SMatthew Dillon 		at->at_identify.serial,
462258223a3SMatthew Dillon 
4631980eff3SMatthew Dillon 		ATANAME(ap, atx),
464258223a3SMatthew Dillon 		devncqdepth, ap->ap_sc->sc_ncmds,
4651980eff3SMatthew Dillon 		at->at_identify.satacap,
4661980eff3SMatthew Dillon 		at->at_identify.satafsup,
467*074579dfSMatthew Dillon 		(at->at_ncqdepth > 1 ? "YES" : "NO"),
468258223a3SMatthew Dillon 		(long long)capacity_bytes / (1024 * 1024),
469*074579dfSMatthew Dillon 		(int)(capacity_bytes % (1024 * 1024)) * 100 / (1024 * 1024)
470*074579dfSMatthew Dillon 	);
471*074579dfSMatthew Dillon 	if (bootverbose)
472*074579dfSMatthew Dillon 	kprintf("%s: f85=%04x f86=%04x f87=%04x WC=%s RA=%s SEC=%s\n",
4731980eff3SMatthew Dillon 		ATANAME(ap, atx),
4741980eff3SMatthew Dillon 		at->at_identify.features85,
4751980eff3SMatthew Dillon 		at->at_identify.features86,
4761980eff3SMatthew Dillon 		at->at_identify.features87,
477669fbbf7SMatthew Dillon 		wcstr,
478fd8bd957SMatthew Dillon 		rastr,
479fd8bd957SMatthew Dillon 		scstr
480258223a3SMatthew Dillon 	);
481258223a3SMatthew Dillon 
482258223a3SMatthew Dillon 	/*
483fd8bd957SMatthew Dillon 	 * Additional type-specific probing
484fd8bd957SMatthew Dillon 	 */
4851980eff3SMatthew Dillon 	switch(at->at_type) {
486fd8bd957SMatthew Dillon 	case ATA_PORT_T_DISK:
4871980eff3SMatthew Dillon 		error = ahci_cam_probe_disk(ap, atx);
4881980eff3SMatthew Dillon 		break;
4891980eff3SMatthew Dillon 	case ATA_PORT_T_ATAPI:
4901980eff3SMatthew Dillon 		error = ahci_cam_probe_atapi(ap, atx);
491fd8bd957SMatthew Dillon 		break;
492fd8bd957SMatthew Dillon 	default:
4931980eff3SMatthew Dillon 		error = EIO;
494fd8bd957SMatthew Dillon 		break;
495fd8bd957SMatthew Dillon 	}
4963209f581SMatthew Dillon err:
4973209f581SMatthew Dillon 	if (error) {
4983209f581SMatthew Dillon 		at->at_probe = ATA_PROBE_FAILED;
4993209f581SMatthew Dillon 		if (atx == NULL)
5003209f581SMatthew Dillon 			ap->ap_probe = at->at_probe;
5013209f581SMatthew Dillon 	} else {
5023209f581SMatthew Dillon 		at->at_probe = ATA_PROBE_GOOD;
5033209f581SMatthew Dillon 		if (atx == NULL)
5043209f581SMatthew Dillon 			ap->ap_probe = at->at_probe;
5053209f581SMatthew Dillon 	}
5063209f581SMatthew Dillon 	return (error);
507fd8bd957SMatthew Dillon }
508fd8bd957SMatthew Dillon 
509fd8bd957SMatthew Dillon /*
510fd8bd957SMatthew Dillon  * DISK-specific probe after initial ident
511fd8bd957SMatthew Dillon  */
512fd8bd957SMatthew Dillon static int
5131980eff3SMatthew Dillon ahci_cam_probe_disk(struct ahci_port *ap, struct ata_port *atx)
514fd8bd957SMatthew Dillon {
5151980eff3SMatthew Dillon 	struct ata_port *at;
516fd8bd957SMatthew Dillon 	struct ata_xfer	*xa;
517fd8bd957SMatthew Dillon 
5181980eff3SMatthew Dillon 	at = atx ? atx : ap->ap_ata;
5191980eff3SMatthew Dillon 
520fd8bd957SMatthew Dillon 	/*
521258223a3SMatthew Dillon 	 * Enable write cache if supported
522fd8bd957SMatthew Dillon 	 *
523fd8bd957SMatthew Dillon 	 * NOTE: "WD My Book" external disk devices have a very poor
524fd8bd957SMatthew Dillon 	 *	 daughter board between the the ESATA and the HD.  Sending
525fd8bd957SMatthew Dillon 	 *	 any ATA_C_SET_FEATURES commands will break the hardware port
526fd8bd957SMatthew Dillon 	 *	 with a fatal protocol error.  However, this device also
527fd8bd957SMatthew Dillon 	 *	 indicates that WRITECACHE is already on and READAHEAD is
528fd8bd957SMatthew Dillon 	 *	 not supported so we avoid the issue.
529258223a3SMatthew Dillon 	 */
5301980eff3SMatthew Dillon 	if ((at->at_identify.cmdset82 & ATA_IDENTIFY_WRITECACHE) &&
5311980eff3SMatthew Dillon 	    (at->at_identify.features85 & ATA_IDENTIFY_WRITECACHE) == 0) {
5321980eff3SMatthew Dillon 		xa = ahci_ata_get_xfer(ap, atx);
533258223a3SMatthew Dillon 		xa->complete = ahci_ata_dummy_done;
534258223a3SMatthew Dillon 		xa->fis->command = ATA_C_SET_FEATURES;
535669fbbf7SMatthew Dillon 		/*xa->fis->features = ATA_SF_WRITECACHE_EN;*/
536669fbbf7SMatthew Dillon 		xa->fis->features = ATA_SF_LOOKAHEAD_EN;
5371980eff3SMatthew Dillon 		xa->fis->flags = ATA_H2D_FLAGS_CMD | at->at_target;
538669fbbf7SMatthew Dillon 		xa->fis->device = 0;
539258223a3SMatthew Dillon 		xa->flags = ATA_F_READ | ATA_F_PIO | ATA_F_POLL;
5403209f581SMatthew Dillon 		xa->timeout = 1000;
541669fbbf7SMatthew Dillon 		xa->datalen = 0;
542831bc9e3SMatthew Dillon 		if (ahci_ata_cmd(xa) == ATA_S_COMPLETE)
5431980eff3SMatthew Dillon 			at->at_features |= ATA_PORT_F_WCACHE;
544258223a3SMatthew Dillon 		ahci_ata_put_xfer(xa);
545258223a3SMatthew Dillon 	}
546258223a3SMatthew Dillon 
547258223a3SMatthew Dillon 	/*
548258223a3SMatthew Dillon 	 * Enable readahead if supported
549258223a3SMatthew Dillon 	 */
5501980eff3SMatthew Dillon 	if ((at->at_identify.cmdset82 & ATA_IDENTIFY_LOOKAHEAD) &&
5511980eff3SMatthew Dillon 	    (at->at_identify.features85 & ATA_IDENTIFY_LOOKAHEAD) == 0) {
5521980eff3SMatthew Dillon 		xa = ahci_ata_get_xfer(ap, atx);
553258223a3SMatthew Dillon 		xa->complete = ahci_ata_dummy_done;
554258223a3SMatthew Dillon 		xa->fis->command = ATA_C_SET_FEATURES;
555258223a3SMatthew Dillon 		xa->fis->features = ATA_SF_LOOKAHEAD_EN;
5561980eff3SMatthew Dillon 		xa->fis->flags = ATA_H2D_FLAGS_CMD | at->at_target;
557669fbbf7SMatthew Dillon 		xa->fis->device = 0;
558258223a3SMatthew Dillon 		xa->flags = ATA_F_READ | ATA_F_PIO | ATA_F_POLL;
5593209f581SMatthew Dillon 		xa->timeout = 1000;
560669fbbf7SMatthew Dillon 		xa->datalen = 0;
561831bc9e3SMatthew Dillon 		if (ahci_ata_cmd(xa) == ATA_S_COMPLETE)
5621980eff3SMatthew Dillon 			at->at_features |= ATA_PORT_F_RAHEAD;
563258223a3SMatthew Dillon 		ahci_ata_put_xfer(xa);
564258223a3SMatthew Dillon 	}
565258223a3SMatthew Dillon 
566258223a3SMatthew Dillon 	/*
567258223a3SMatthew Dillon 	 * FREEZE LOCK the device so malicious users can't lock it on us.
568258223a3SMatthew Dillon 	 * As there is no harm in issuing this to devices that don't
569258223a3SMatthew Dillon 	 * support the security feature set we just send it, and don't bother
570258223a3SMatthew Dillon 	 * checking if the device sends a command abort to tell us it doesn't
571258223a3SMatthew Dillon 	 * support it
572258223a3SMatthew Dillon 	 */
5731980eff3SMatthew Dillon 	if ((at->at_identify.cmdset82 & ATA_IDENTIFY_SECURITY) &&
5741980eff3SMatthew Dillon 	    (at->at_identify.securestatus & ATA_SECURE_FROZEN) == 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;
579258223a3SMatthew Dillon 		xa->flags = ATA_F_READ | 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;
584258223a3SMatthew Dillon 		ahci_ata_put_xfer(xa);
585669fbbf7SMatthew Dillon 	}
586258223a3SMatthew Dillon 
587b4189e5eSMatthew Dillon 	return (0);
588b4189e5eSMatthew Dillon }
589b4189e5eSMatthew Dillon 
590fd8bd957SMatthew Dillon /*
591fd8bd957SMatthew Dillon  * ATAPI-specific probe after initial ident
592fd8bd957SMatthew Dillon  */
593b4189e5eSMatthew Dillon static int
5941980eff3SMatthew Dillon ahci_cam_probe_atapi(struct ahci_port *ap, struct ata_port *atx)
595b4189e5eSMatthew Dillon {
596fd8bd957SMatthew Dillon 	return(0);
597fd8bd957SMatthew Dillon }
598fd8bd957SMatthew Dillon 
599b4189e5eSMatthew Dillon /*
600b4189e5eSMatthew Dillon  * Fix byte ordering so buffers can be accessed as
601b4189e5eSMatthew Dillon  * strings.
602b4189e5eSMatthew Dillon  */
603258223a3SMatthew Dillon static void
604258223a3SMatthew Dillon ata_fix_identify(struct ata_identify *id)
605258223a3SMatthew Dillon {
606258223a3SMatthew Dillon 	u_int16_t	*swap;
607258223a3SMatthew Dillon 	int		i;
608258223a3SMatthew Dillon 
609258223a3SMatthew Dillon 	swap = (u_int16_t *)id->serial;
610258223a3SMatthew Dillon 	for (i = 0; i < sizeof(id->serial) / sizeof(u_int16_t); i++)
611258223a3SMatthew Dillon 		swap[i] = bswap16(swap[i]);
612258223a3SMatthew Dillon 
613258223a3SMatthew Dillon 	swap = (u_int16_t *)id->firmware;
614258223a3SMatthew Dillon 	for (i = 0; i < sizeof(id->firmware) / sizeof(u_int16_t); i++)
615258223a3SMatthew Dillon 		swap[i] = bswap16(swap[i]);
616258223a3SMatthew Dillon 
617258223a3SMatthew Dillon 	swap = (u_int16_t *)id->model;
618258223a3SMatthew Dillon 	for (i = 0; i < sizeof(id->model) / sizeof(u_int16_t); i++)
619258223a3SMatthew Dillon 		swap[i] = bswap16(swap[i]);
620258223a3SMatthew Dillon }
621258223a3SMatthew Dillon 
622258223a3SMatthew Dillon /*
623b4189e5eSMatthew Dillon  * Dummy done callback for xa.
624b4189e5eSMatthew Dillon  */
625b4189e5eSMatthew Dillon static void
626b4189e5eSMatthew Dillon ahci_ata_dummy_done(struct ata_xfer *xa)
627b4189e5eSMatthew Dillon {
628b4189e5eSMatthew Dillon }
629b4189e5eSMatthew Dillon 
630b4189e5eSMatthew Dillon /*
6313209f581SMatthew Dillon  * Use an engineering request to initiate a target scan for devices
6323209f581SMatthew Dillon  * behind a port multiplier.
633fd8bd957SMatthew Dillon  *
6343209f581SMatthew Dillon  * An asynchronous bus scan is used to avoid reentrancy issues.
635258223a3SMatthew Dillon  */
636258223a3SMatthew Dillon static void
637258223a3SMatthew Dillon ahci_cam_rescan_callback(struct cam_periph *periph, union ccb *ccb)
638258223a3SMatthew Dillon {
6393209f581SMatthew Dillon 	struct ahci_port *ap = ccb->ccb_h.sim_priv.entries[0].ptr;
6403209f581SMatthew Dillon 
641f4553de1SMatthew Dillon 	if (ccb->ccb_h.func_code == XPT_SCAN_BUS) {
6423209f581SMatthew Dillon 		ap->ap_flags &= ~AP_F_SCAN_RUNNING;
6433209f581SMatthew Dillon 		if (ap->ap_flags & AP_F_SCAN_REQUESTED) {
6443209f581SMatthew Dillon 			ap->ap_flags &= ~AP_F_SCAN_REQUESTED;
6453209f581SMatthew Dillon 			ahci_cam_rescan(ap);
6463209f581SMatthew Dillon 		}
647f4553de1SMatthew Dillon 		ap->ap_flags |= AP_F_SCAN_COMPLETED;
648f4553de1SMatthew Dillon 		wakeup(&ap->ap_flags);
649f4553de1SMatthew Dillon 	}
650f4553de1SMatthew Dillon 	xpt_free_ccb(ccb);
651258223a3SMatthew Dillon }
652258223a3SMatthew Dillon 
653258223a3SMatthew Dillon static void
654258223a3SMatthew Dillon ahci_cam_rescan(struct ahci_port *ap)
655258223a3SMatthew Dillon {
656258223a3SMatthew Dillon 	struct cam_path *path;
657258223a3SMatthew Dillon 	union ccb *ccb;
658258223a3SMatthew Dillon 	int status;
6593209f581SMatthew Dillon 	int i;
6603209f581SMatthew Dillon 
6613209f581SMatthew Dillon 	if (ap->ap_flags & AP_F_SCAN_RUNNING) {
6623209f581SMatthew Dillon 		ap->ap_flags |= AP_F_SCAN_REQUESTED;
6633209f581SMatthew Dillon 		return;
6643209f581SMatthew Dillon 	}
6653209f581SMatthew Dillon 	ap->ap_flags |= AP_F_SCAN_RUNNING;
6663209f581SMatthew Dillon 	for (i = 0; i < AHCI_MAX_PMPORTS; ++i) {
6673209f581SMatthew Dillon 		ap->ap_ata[i].at_features |= ATA_PORT_F_RESCAN;
6683209f581SMatthew Dillon 	}
669258223a3SMatthew Dillon 
670258223a3SMatthew Dillon 	status = xpt_create_path(&path, xpt_periph, cam_sim_path(ap->ap_sim),
671258223a3SMatthew Dillon 				 CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD);
672258223a3SMatthew Dillon 	if (status != CAM_REQ_CMP)
673258223a3SMatthew Dillon 		return;
674258223a3SMatthew Dillon 
675f4553de1SMatthew Dillon 	ccb = xpt_alloc_ccb();
676258223a3SMatthew Dillon 	xpt_setup_ccb(&ccb->ccb_h, path, 5);	/* 5 = low priority */
6772de5e9baSMatthew Dillon 	ccb->ccb_h.func_code = XPT_ENG_EXEC;
678258223a3SMatthew Dillon 	ccb->ccb_h.cbfcnp = ahci_cam_rescan_callback;
6793209f581SMatthew Dillon 	ccb->ccb_h.sim_priv.entries[0].ptr = ap;
680258223a3SMatthew Dillon 	ccb->crcn.flags = CAM_FLAG_NONE;
6812de5e9baSMatthew Dillon 	xpt_action_async(ccb);
682258223a3SMatthew Dillon }
683258223a3SMatthew Dillon 
6843209f581SMatthew Dillon static void
6853209f581SMatthew Dillon ahci_xpt_rescan(struct ahci_port *ap)
6863209f581SMatthew Dillon {
6873209f581SMatthew Dillon 	struct cam_path *path;
6883209f581SMatthew Dillon 	union ccb *ccb;
6893209f581SMatthew Dillon 	int status;
6903209f581SMatthew Dillon 
6913209f581SMatthew Dillon 	status = xpt_create_path(&path, xpt_periph, cam_sim_path(ap->ap_sim),
6923209f581SMatthew Dillon 				 CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD);
6933209f581SMatthew Dillon 	if (status != CAM_REQ_CMP)
6943209f581SMatthew Dillon 		return;
695f4553de1SMatthew Dillon 
696f4553de1SMatthew Dillon 	ccb = xpt_alloc_ccb();
6973209f581SMatthew Dillon 	xpt_setup_ccb(&ccb->ccb_h, path, 5);	/* 5 = low priority */
6982de5e9baSMatthew Dillon 	ccb->ccb_h.func_code = XPT_SCAN_BUS;
6993209f581SMatthew Dillon 	ccb->ccb_h.cbfcnp = ahci_cam_rescan_callback;
7003209f581SMatthew Dillon 	ccb->ccb_h.sim_priv.entries[0].ptr = ap;
7013209f581SMatthew Dillon 	ccb->crcn.flags = CAM_FLAG_NONE;
702baef7501SMatthew Dillon 	xpt_action_async(ccb);
7033209f581SMatthew Dillon }
7043209f581SMatthew Dillon 
705258223a3SMatthew Dillon /*
706258223a3SMatthew Dillon  * Action function - dispatch command
707258223a3SMatthew Dillon  */
708258223a3SMatthew Dillon static
709258223a3SMatthew Dillon void
710258223a3SMatthew Dillon ahci_xpt_action(struct cam_sim *sim, union ccb *ccb)
711258223a3SMatthew Dillon {
712258223a3SMatthew Dillon 	struct ahci_port *ap;
7131980eff3SMatthew Dillon 	struct ata_port	 *at, *atx;
714258223a3SMatthew Dillon 	struct ccb_hdr *ccbh;
715258223a3SMatthew Dillon 	int unit;
716258223a3SMatthew Dillon 
717258223a3SMatthew Dillon 	/* XXX lock */
718258223a3SMatthew Dillon 	ap = cam_sim_softc(sim);
7191980eff3SMatthew Dillon 	at = ap->ap_ata;
7201980eff3SMatthew Dillon 	atx = NULL;
721258223a3SMatthew Dillon 	KKASSERT(ap != NULL);
722258223a3SMatthew Dillon 	ccbh = &ccb->ccb_h;
723258223a3SMatthew Dillon 	unit = cam_sim_unit(sim);
724258223a3SMatthew Dillon 
725258223a3SMatthew Dillon 	/*
7263209f581SMatthew Dillon 	 * Early failure checks.  These checks do not apply to XPT_PATH_INQ,
7273209f581SMatthew Dillon 	 * otherwise the bus rescan will not remove the dead devices when
7283209f581SMatthew Dillon 	 * unplugging a PM.
7293209f581SMatthew Dillon 	 *
7301980eff3SMatthew Dillon 	 * For non-wildcards we have one target (0) and one lun (0),
7311980eff3SMatthew Dillon 	 * unless we have a port multiplier.
7321980eff3SMatthew Dillon 	 *
7331980eff3SMatthew Dillon 	 * A wildcard target indicates only the general bus is being
7341980eff3SMatthew Dillon 	 * probed.
7351980eff3SMatthew Dillon 	 *
7361980eff3SMatthew Dillon 	 * Calculate at and atx.  at is always non-NULL.  atx is only
7371980eff3SMatthew Dillon 	 * non-NULL for direct-attached devices.  It will be NULL for
7381980eff3SMatthew Dillon 	 * devices behind a port multiplier.
739258223a3SMatthew Dillon 	 *
740258223a3SMatthew Dillon 	 * XXX What do we do with a LUN wildcard?
741258223a3SMatthew Dillon 	 */
7423209f581SMatthew Dillon 	if (ccbh->target_id != CAM_TARGET_WILDCARD &&
7433209f581SMatthew Dillon 	    ccbh->func_code != XPT_PATH_INQ) {
7441980eff3SMatthew Dillon 		if (ap->ap_type == ATA_PORT_T_NONE) {
7453209f581SMatthew Dillon 			ccbh->status = CAM_DEV_NOT_THERE;
746258223a3SMatthew Dillon 			xpt_done(ccb);
747258223a3SMatthew Dillon 			return;
748258223a3SMatthew Dillon 		}
7491980eff3SMatthew Dillon 		if (ccbh->target_id < 0 || ccbh->target_id >= ap->ap_pmcount) {
750258223a3SMatthew Dillon 			ccbh->status = CAM_DEV_NOT_THERE;
751258223a3SMatthew Dillon 			xpt_done(ccb);
752258223a3SMatthew Dillon 			return;
753258223a3SMatthew Dillon 		}
7541980eff3SMatthew Dillon 		at += ccbh->target_id;
7551980eff3SMatthew Dillon 		if (ap->ap_type == ATA_PORT_T_PM)
7561980eff3SMatthew Dillon 			atx = at;
7571980eff3SMatthew Dillon 
758258223a3SMatthew Dillon 		if (ccbh->target_lun != CAM_LUN_WILDCARD && ccbh->target_lun) {
759258223a3SMatthew Dillon 			ccbh->status = CAM_DEV_NOT_THERE;
760258223a3SMatthew Dillon 			xpt_done(ccb);
761258223a3SMatthew Dillon 			return;
762258223a3SMatthew Dillon 		}
763258223a3SMatthew Dillon 	}
764258223a3SMatthew Dillon 
765258223a3SMatthew Dillon 	/*
766258223a3SMatthew Dillon 	 * Switch on the meta XPT command
767258223a3SMatthew Dillon 	 */
768258223a3SMatthew Dillon 	switch(ccbh->func_code) {
7693209f581SMatthew Dillon 	case XPT_ENG_EXEC:
7703209f581SMatthew Dillon 		/*
7713209f581SMatthew Dillon 		 * This routine is called after a port multiplier has been
7723209f581SMatthew Dillon 		 * probed.
7733209f581SMatthew Dillon 		 */
7743209f581SMatthew Dillon 		ccbh->status = CAM_REQ_CMP;
775f4553de1SMatthew Dillon 		ahci_os_lock_port(ap);
776831bc9e3SMatthew Dillon 		ahci_port_state_machine(ap, 0);
777f4553de1SMatthew Dillon 		ahci_os_unlock_port(ap);
7783209f581SMatthew Dillon 		xpt_done(ccb);
7793209f581SMatthew Dillon 		ahci_xpt_rescan(ap);
7803209f581SMatthew Dillon 		break;
781258223a3SMatthew Dillon 	case XPT_PATH_INQ:
7823209f581SMatthew Dillon 		/*
7833209f581SMatthew Dillon 		 * This command always succeeds, otherwise the bus scan
7843209f581SMatthew Dillon 		 * will not detach dead devices.
7853209f581SMatthew Dillon 		 */
786258223a3SMatthew Dillon 		ccb->cpi.version_num = 1;
787258223a3SMatthew Dillon 		ccb->cpi.hba_inquiry = 0;
788258223a3SMatthew Dillon 		ccb->cpi.target_sprt = 0;
7891980eff3SMatthew Dillon 		ccb->cpi.hba_misc = PIM_SEQSCAN;
790258223a3SMatthew Dillon 		ccb->cpi.hba_eng_cnt = 0;
791258223a3SMatthew Dillon 		bzero(ccb->cpi.vuhba_flags, sizeof(ccb->cpi.vuhba_flags));
7921980eff3SMatthew Dillon 		ccb->cpi.max_target = AHCI_MAX_PMPORTS;
793258223a3SMatthew Dillon 		ccb->cpi.max_lun = 0;
794258223a3SMatthew Dillon 		ccb->cpi.async_flags = 0;
795258223a3SMatthew Dillon 		ccb->cpi.hpath_id = 0;
7961980eff3SMatthew Dillon 		ccb->cpi.initiator_id = AHCI_MAX_PMPORTS - 1;
797258223a3SMatthew Dillon 		ccb->cpi.unit_number = cam_sim_unit(sim);
798258223a3SMatthew Dillon 		ccb->cpi.bus_id = cam_sim_bus(sim);
799258223a3SMatthew Dillon 		ccb->cpi.base_transfer_speed = 150000;
800258223a3SMatthew Dillon 		ccb->cpi.transport = XPORT_AHCI;
801258223a3SMatthew Dillon 		ccb->cpi.transport_version = 1;
802258223a3SMatthew Dillon 		ccb->cpi.protocol = PROTO_SCSI;
803258223a3SMatthew Dillon 		ccb->cpi.protocol_version = SCSI_REV_2;
804258223a3SMatthew Dillon 
8053209f581SMatthew Dillon 		ccbh->status = CAM_REQ_CMP;
806831bc9e3SMatthew Dillon 		if (ccbh->target_id == CAM_TARGET_WILDCARD) {
807831bc9e3SMatthew Dillon 			ahci_os_lock_port(ap);
808831bc9e3SMatthew Dillon 			ahci_port_state_machine(ap, 0);
809831bc9e3SMatthew Dillon 			ahci_os_unlock_port(ap);
810831bc9e3SMatthew Dillon 		} else {
811258223a3SMatthew Dillon 			switch(ahci_pread(ap, AHCI_PREG_SSTS) &
812258223a3SMatthew Dillon 			       AHCI_PREG_SSTS_SPD) {
813258223a3SMatthew Dillon 			case AHCI_PREG_SSTS_SPD_GEN1:
814258223a3SMatthew Dillon 				ccb->cpi.base_transfer_speed = 150000;
815258223a3SMatthew Dillon 				break;
816258223a3SMatthew Dillon 			case AHCI_PREG_SSTS_SPD_GEN2:
817258223a3SMatthew Dillon 				ccb->cpi.base_transfer_speed = 300000;
818258223a3SMatthew Dillon 				break;
819258223a3SMatthew Dillon 			default:
820258223a3SMatthew Dillon 				/* unknown */
821258223a3SMatthew Dillon 				ccb->cpi.base_transfer_speed = 1000;
822258223a3SMatthew Dillon 				break;
823258223a3SMatthew Dillon 			}
8243209f581SMatthew Dillon #if 0
8251980eff3SMatthew Dillon 			if (ap->ap_type == ATA_PORT_T_NONE)
8261980eff3SMatthew Dillon 				ccbh->status = CAM_DEV_NOT_THERE;
8273209f581SMatthew Dillon #endif
8281980eff3SMatthew Dillon 		}
829258223a3SMatthew Dillon 		xpt_done(ccb);
830258223a3SMatthew Dillon 		break;
831258223a3SMatthew Dillon 	case XPT_RESET_DEV:
832f4553de1SMatthew Dillon 		ahci_os_lock_port(ap);
8331980eff3SMatthew Dillon 		if (ap->ap_type == ATA_PORT_T_NONE) {
8343209f581SMatthew Dillon 			ccbh->status = CAM_DEV_NOT_THERE;
8351980eff3SMatthew Dillon 		} else {
8361980eff3SMatthew Dillon 			ahci_port_reset(ap, atx, 0);
837fd8bd957SMatthew Dillon 			ccbh->status = CAM_REQ_CMP;
8381980eff3SMatthew Dillon 		}
839f4553de1SMatthew Dillon 		ahci_os_unlock_port(ap);
840258223a3SMatthew Dillon 		xpt_done(ccb);
841258223a3SMatthew Dillon 		break;
842258223a3SMatthew Dillon 	case XPT_RESET_BUS:
843f4553de1SMatthew Dillon 		ahci_os_lock_port(ap);
8441980eff3SMatthew Dillon 		ahci_port_reset(ap, NULL, 1);
845f4553de1SMatthew Dillon 		ahci_os_unlock_port(ap);
846fd8bd957SMatthew Dillon 		ccbh->status = CAM_REQ_CMP;
847258223a3SMatthew Dillon 		xpt_done(ccb);
848258223a3SMatthew Dillon 		break;
849258223a3SMatthew Dillon 	case XPT_SET_TRAN_SETTINGS:
850258223a3SMatthew Dillon 		ccbh->status = CAM_FUNC_NOTAVAIL;
851258223a3SMatthew Dillon 		xpt_done(ccb);
852258223a3SMatthew Dillon 		break;
853258223a3SMatthew Dillon 	case XPT_GET_TRAN_SETTINGS:
854258223a3SMatthew Dillon 		ccb->cts.protocol = PROTO_SCSI;
855258223a3SMatthew Dillon 		ccb->cts.protocol_version = SCSI_REV_2;
856258223a3SMatthew Dillon 		ccb->cts.transport = XPORT_AHCI;
857258223a3SMatthew Dillon 		ccb->cts.transport_version = XPORT_VERSION_UNSPECIFIED;
858258223a3SMatthew Dillon 		ccb->cts.proto_specific.valid = 0;
859258223a3SMatthew Dillon 		ccb->cts.xport_specific.valid = 0;
860258223a3SMatthew Dillon 		ccbh->status = CAM_REQ_CMP;
861258223a3SMatthew Dillon 		xpt_done(ccb);
862258223a3SMatthew Dillon 		break;
863258223a3SMatthew Dillon 	case XPT_CALC_GEOMETRY:
864258223a3SMatthew Dillon 		cam_calc_geometry(&ccb->ccg, 1);
865258223a3SMatthew Dillon 		xpt_done(ccb);
866258223a3SMatthew Dillon 		break;
867258223a3SMatthew Dillon 	case XPT_SCSI_IO:
868f4553de1SMatthew Dillon 		/*
869f4553de1SMatthew Dillon 		 * Our parallel startup code might have only probed through
870f4553de1SMatthew Dillon 		 * to the IDENT, so do the last step if necessary.
871f4553de1SMatthew Dillon 		 */
872f4553de1SMatthew Dillon 		if (at->at_probe == ATA_PROBE_NEED_IDENT)
873f4553de1SMatthew Dillon 			ahci_cam_probe(ap, atx);
874f4553de1SMatthew Dillon 		if (at->at_probe != ATA_PROBE_GOOD) {
875f4553de1SMatthew Dillon 			ccbh->status = CAM_DEV_NOT_THERE;
876f4553de1SMatthew Dillon 			xpt_done(ccb);
877f4553de1SMatthew Dillon 			break;
878f4553de1SMatthew Dillon 		}
8791980eff3SMatthew Dillon 		switch(at->at_type) {
880258223a3SMatthew Dillon 		case ATA_PORT_T_DISK:
8811980eff3SMatthew Dillon 			ahci_xpt_scsi_disk_io(ap, atx, ccb);
882258223a3SMatthew Dillon 			break;
883258223a3SMatthew Dillon 		case ATA_PORT_T_ATAPI:
8841980eff3SMatthew Dillon 			ahci_xpt_scsi_atapi_io(ap, atx, ccb);
885258223a3SMatthew Dillon 			break;
886258223a3SMatthew Dillon 		default:
887258223a3SMatthew Dillon 			ccbh->status = CAM_REQ_INVALID;
888258223a3SMatthew Dillon 			xpt_done(ccb);
889258223a3SMatthew Dillon 			break;
890258223a3SMatthew Dillon 		}
891258223a3SMatthew Dillon 		break;
892258223a3SMatthew Dillon 	default:
893258223a3SMatthew Dillon 		ccbh->status = CAM_REQ_INVALID;
894258223a3SMatthew Dillon 		xpt_done(ccb);
895258223a3SMatthew Dillon 		break;
896258223a3SMatthew Dillon 	}
897258223a3SMatthew Dillon }
898258223a3SMatthew Dillon 
899258223a3SMatthew Dillon /*
900de68d532SMatthew Dillon  * Poll function.
901de68d532SMatthew Dillon  *
902de68d532SMatthew Dillon  * Generally this function gets called heavily when interrupts might be
903de68d532SMatthew Dillon  * non-operational, during a halt/reboot or panic.
904258223a3SMatthew Dillon  */
905258223a3SMatthew Dillon static
906258223a3SMatthew Dillon void
907258223a3SMatthew Dillon ahci_xpt_poll(struct cam_sim *sim)
908258223a3SMatthew Dillon {
909de68d532SMatthew Dillon 	struct ahci_port *ap;
910258223a3SMatthew Dillon 
911de68d532SMatthew Dillon 	ap = cam_sim_softc(sim);
912de68d532SMatthew Dillon 	crit_enter();
913f4553de1SMatthew Dillon 	ahci_os_lock_port(ap);
914f4553de1SMatthew Dillon 	ahci_port_intr(ap, 1);
915f4553de1SMatthew Dillon 	ahci_os_unlock_port(ap);
916de68d532SMatthew Dillon 	crit_exit();
917258223a3SMatthew Dillon }
918258223a3SMatthew Dillon 
919258223a3SMatthew Dillon /*
920b4189e5eSMatthew Dillon  * Convert the SCSI command in ccb to an ata_xfer command in xa
921b4189e5eSMatthew Dillon  * for ATA_PORT_T_DISK operations.  Set the completion function
922b4189e5eSMatthew Dillon  * to convert the response back, then dispatch to the OpenBSD AHCI
923b4189e5eSMatthew Dillon  * layer.
924258223a3SMatthew Dillon  *
925b4189e5eSMatthew Dillon  * AHCI DISK commands only support a limited command set, and we
926b4189e5eSMatthew Dillon  * fake additional commands to make it play nice with the CAM subsystem.
927258223a3SMatthew Dillon  */
928258223a3SMatthew Dillon static
929258223a3SMatthew Dillon void
9301980eff3SMatthew Dillon ahci_xpt_scsi_disk_io(struct ahci_port *ap, struct ata_port *atx,
9311980eff3SMatthew Dillon 		      union ccb *ccb)
932258223a3SMatthew Dillon {
933258223a3SMatthew Dillon 	struct ccb_hdr *ccbh;
934258223a3SMatthew Dillon 	struct ccb_scsiio *csio;
935258223a3SMatthew Dillon 	struct ata_xfer *xa;
9361980eff3SMatthew Dillon 	struct ata_port	*at;
937258223a3SMatthew Dillon 	struct ata_fis_h2d *fis;
938258223a3SMatthew Dillon 	scsi_cdb_t cdb;
939258223a3SMatthew Dillon 	union scsi_data *rdata;
940258223a3SMatthew Dillon 	int rdata_len;
941258223a3SMatthew Dillon 	u_int64_t capacity;
942258223a3SMatthew Dillon 	u_int64_t lba;
943258223a3SMatthew Dillon 	u_int32_t count;
944258223a3SMatthew Dillon 
945258223a3SMatthew Dillon 	ccbh = &ccb->csio.ccb_h;
946258223a3SMatthew Dillon 	csio = &ccb->csio;
9471980eff3SMatthew Dillon 	at = atx ? atx : &ap->ap_ata[0];
9481980eff3SMatthew Dillon 
9491980eff3SMatthew Dillon 	/*
9501980eff3SMatthew Dillon 	 * XXX not passing NULL at for direct attach!
9511980eff3SMatthew Dillon 	 */
9521980eff3SMatthew Dillon 	xa = ahci_ata_get_xfer(ap, atx);
953258223a3SMatthew Dillon 	rdata = (void *)csio->data_ptr;
954258223a3SMatthew Dillon 	rdata_len = csio->dxfer_len;
955258223a3SMatthew Dillon 
956258223a3SMatthew Dillon 	/*
957258223a3SMatthew Dillon 	 * Build the FIS or process the csio to completion.
958258223a3SMatthew Dillon 	 */
959258223a3SMatthew Dillon 	cdb = (void *)((ccbh->flags & CAM_CDB_POINTER) ?
960258223a3SMatthew Dillon 			csio->cdb_io.cdb_ptr : csio->cdb_io.cdb_bytes);
961258223a3SMatthew Dillon 
962258223a3SMatthew Dillon 	switch(cdb->generic.opcode) {
963258223a3SMatthew Dillon 	case REQUEST_SENSE:
964258223a3SMatthew Dillon 		/*
965258223a3SMatthew Dillon 		 * Auto-sense everything, so explicit sense requests
966258223a3SMatthew Dillon 		 * return no-sense.
967258223a3SMatthew Dillon 		 */
968258223a3SMatthew Dillon 		ccbh->status = CAM_SCSI_STATUS_ERROR;
969258223a3SMatthew Dillon 		break;
970258223a3SMatthew Dillon 	case INQUIRY:
971258223a3SMatthew Dillon 		/*
972258223a3SMatthew Dillon 		 * Inquiry supported features
973258223a3SMatthew Dillon 		 *
974258223a3SMatthew Dillon 		 * [opcode, byte2, page_code, length, control]
975258223a3SMatthew Dillon 		 */
976258223a3SMatthew Dillon 		if (cdb->inquiry.byte2 & SI_EVPD) {
977258223a3SMatthew Dillon 			switch(cdb->inquiry.page_code) {
978258223a3SMatthew Dillon 			case SVPD_SUPPORTED_PAGE_LIST:
979258223a3SMatthew Dillon 				/* XXX atascsi_disk_vpd_supported */
980258223a3SMatthew Dillon 			case SVPD_UNIT_SERIAL_NUMBER:
981258223a3SMatthew Dillon 				/* XXX atascsi_disk_vpd_serial */
982258223a3SMatthew Dillon 			case SVPD_UNIT_DEVID:
983258223a3SMatthew Dillon 				/* XXX atascsi_disk_vpd_ident */
984258223a3SMatthew Dillon 			default:
985258223a3SMatthew Dillon 				ccbh->status = CAM_FUNC_NOTAVAIL;
986258223a3SMatthew Dillon 				break;
987258223a3SMatthew Dillon 			}
988258223a3SMatthew Dillon 		} else {
989258223a3SMatthew Dillon 			bzero(rdata, rdata_len);
990258223a3SMatthew Dillon 			if (rdata_len < SHORT_INQUIRY_LENGTH) {
991258223a3SMatthew Dillon 				ccbh->status = CAM_CCB_LEN_ERR;
992258223a3SMatthew Dillon 				break;
993258223a3SMatthew Dillon 			}
994258223a3SMatthew Dillon 			if (rdata_len > sizeof(rdata->inquiry_data))
995258223a3SMatthew Dillon 				rdata_len = sizeof(rdata->inquiry_data);
996258223a3SMatthew Dillon 			rdata->inquiry_data.device = T_DIRECT;
997258223a3SMatthew Dillon 			rdata->inquiry_data.version = SCSI_REV_SPC2;
998258223a3SMatthew Dillon 			rdata->inquiry_data.response_format = 2;
999258223a3SMatthew Dillon 			rdata->inquiry_data.additional_length = 32;
1000258223a3SMatthew Dillon 			bcopy("SATA    ", rdata->inquiry_data.vendor, 8);
10011980eff3SMatthew Dillon 			bcopy(at->at_identify.model,
1002258223a3SMatthew Dillon 			      rdata->inquiry_data.product,
1003258223a3SMatthew Dillon 			      sizeof(rdata->inquiry_data.product));
10041980eff3SMatthew Dillon 			bcopy(at->at_identify.firmware,
1005258223a3SMatthew Dillon 			      rdata->inquiry_data.revision,
1006258223a3SMatthew Dillon 			      sizeof(rdata->inquiry_data.revision));
1007258223a3SMatthew Dillon 			ccbh->status = CAM_REQ_CMP;
1008258223a3SMatthew Dillon 		}
1009258223a3SMatthew Dillon 		break;
1010258223a3SMatthew Dillon 	case READ_CAPACITY_16:
1011258223a3SMatthew Dillon 		if (cdb->read_capacity_16.service_action != SRC16_SERVICE_ACTION) {
1012258223a3SMatthew Dillon 			ccbh->status = CAM_REQ_INVALID;
1013258223a3SMatthew Dillon 			break;
1014258223a3SMatthew Dillon 		}
1015258223a3SMatthew Dillon 		if (rdata_len < sizeof(rdata->read_capacity_data_16)) {
1016258223a3SMatthew Dillon 			ccbh->status = CAM_CCB_LEN_ERR;
1017258223a3SMatthew Dillon 			break;
1018258223a3SMatthew Dillon 		}
1019258223a3SMatthew Dillon 		/* fall through */
1020258223a3SMatthew Dillon 	case READ_CAPACITY:
1021258223a3SMatthew Dillon 		if (rdata_len < sizeof(rdata->read_capacity_data)) {
1022258223a3SMatthew Dillon 			ccbh->status = CAM_CCB_LEN_ERR;
1023258223a3SMatthew Dillon 			break;
1024258223a3SMatthew Dillon 		}
1025258223a3SMatthew Dillon 
10261980eff3SMatthew Dillon 		capacity = at->at_capacity;
1027258223a3SMatthew Dillon 
1028258223a3SMatthew Dillon 		bzero(rdata, rdata_len);
1029258223a3SMatthew Dillon 		if (cdb->generic.opcode == READ_CAPACITY) {
1030258223a3SMatthew Dillon 			rdata_len = sizeof(rdata->read_capacity_data);
1031258223a3SMatthew Dillon 			if (capacity > 0xFFFFFFFFU)
1032258223a3SMatthew Dillon 				capacity = 0xFFFFFFFFU;
1033258223a3SMatthew Dillon 			bzero(&rdata->read_capacity_data, rdata_len);
1034258223a3SMatthew Dillon 			scsi_ulto4b((u_int32_t)capacity - 1,
1035258223a3SMatthew Dillon 				    rdata->read_capacity_data.addr);
1036258223a3SMatthew Dillon 			scsi_ulto4b(512, rdata->read_capacity_data.length);
1037258223a3SMatthew Dillon 		} else {
1038258223a3SMatthew Dillon 			rdata_len = sizeof(rdata->read_capacity_data_16);
1039258223a3SMatthew Dillon 			bzero(&rdata->read_capacity_data_16, rdata_len);
1040258223a3SMatthew Dillon 			scsi_u64to8b(capacity - 1,
1041258223a3SMatthew Dillon 				     rdata->read_capacity_data_16.addr);
1042258223a3SMatthew Dillon 			scsi_ulto4b(512, rdata->read_capacity_data_16.length);
1043258223a3SMatthew Dillon 		}
1044258223a3SMatthew Dillon 		ccbh->status = CAM_REQ_CMP;
1045258223a3SMatthew Dillon 		break;
1046258223a3SMatthew Dillon 	case SYNCHRONIZE_CACHE:
1047258223a3SMatthew Dillon 		/*
1048258223a3SMatthew Dillon 		 * Synchronize cache.  Specification says this can take
1049258223a3SMatthew Dillon 		 * greater then 30 seconds so give it at least 45.
1050258223a3SMatthew Dillon 		 */
1051258223a3SMatthew Dillon 		fis = xa->fis;
1052258223a3SMatthew Dillon 		fis->flags = ATA_H2D_FLAGS_CMD;
1053258223a3SMatthew Dillon 		fis->command = ATA_C_FLUSH_CACHE;
1054258223a3SMatthew Dillon 		fis->device = 0;
10553209f581SMatthew Dillon 		if (xa->timeout < 45000)
10563209f581SMatthew Dillon 			xa->timeout = 45000;
10571980eff3SMatthew Dillon 		xa->datalen = 0;
10581980eff3SMatthew Dillon 		xa->flags = ATA_F_READ;
10591980eff3SMatthew Dillon 		xa->complete = ahci_ata_complete_disk_synchronize_cache;
1060258223a3SMatthew Dillon 		break;
1061258223a3SMatthew Dillon 	case TEST_UNIT_READY:
1062258223a3SMatthew Dillon 	case START_STOP_UNIT:
1063258223a3SMatthew Dillon 	case PREVENT_ALLOW:
1064258223a3SMatthew Dillon 		/*
1065258223a3SMatthew Dillon 		 * Just silently return success
1066258223a3SMatthew Dillon 		 */
1067258223a3SMatthew Dillon 		ccbh->status = CAM_REQ_CMP;
1068258223a3SMatthew Dillon 		rdata_len = 0;
1069258223a3SMatthew Dillon 		break;
1070258223a3SMatthew Dillon 	case ATA_PASS_12:
1071258223a3SMatthew Dillon 	case ATA_PASS_16:
1072258223a3SMatthew Dillon 		/*
1073258223a3SMatthew Dillon 		 * XXX implement pass-through
1074258223a3SMatthew Dillon 		 */
1075258223a3SMatthew Dillon 		ccbh->status = CAM_FUNC_NOTAVAIL;
1076258223a3SMatthew Dillon 		break;
1077258223a3SMatthew Dillon 	default:
1078258223a3SMatthew Dillon 		switch(cdb->generic.opcode) {
1079258223a3SMatthew Dillon 		case READ_6:
1080258223a3SMatthew Dillon 			lba = scsi_3btoul(cdb->rw_6.addr) & 0x1FFFFF;
1081258223a3SMatthew Dillon 			count = cdb->rw_6.length ? cdb->rw_6.length : 0x100;
1082258223a3SMatthew Dillon 			xa->flags = ATA_F_READ;
1083258223a3SMatthew Dillon 			break;
1084258223a3SMatthew Dillon 		case READ_10:
1085258223a3SMatthew Dillon 			lba = scsi_4btoul(cdb->rw_10.addr);
1086258223a3SMatthew Dillon 			count = scsi_2btoul(cdb->rw_10.length);
1087258223a3SMatthew Dillon 			xa->flags = ATA_F_READ;
1088258223a3SMatthew Dillon 			break;
1089258223a3SMatthew Dillon 		case READ_12:
1090258223a3SMatthew Dillon 			lba = scsi_4btoul(cdb->rw_12.addr);
1091258223a3SMatthew Dillon 			count = scsi_4btoul(cdb->rw_12.length);
1092258223a3SMatthew Dillon 			xa->flags = ATA_F_READ;
1093258223a3SMatthew Dillon 			break;
1094258223a3SMatthew Dillon 		case READ_16:
1095258223a3SMatthew Dillon 			lba = scsi_8btou64(cdb->rw_16.addr);
1096258223a3SMatthew Dillon 			count = scsi_4btoul(cdb->rw_16.length);
1097258223a3SMatthew Dillon 			xa->flags = ATA_F_READ;
1098258223a3SMatthew Dillon 			break;
1099258223a3SMatthew Dillon 		case WRITE_6:
1100258223a3SMatthew Dillon 			lba = scsi_3btoul(cdb->rw_6.addr) & 0x1FFFFF;
1101258223a3SMatthew Dillon 			count = cdb->rw_6.length ? cdb->rw_6.length : 0x100;
1102258223a3SMatthew Dillon 			xa->flags = ATA_F_WRITE;
1103258223a3SMatthew Dillon 			break;
1104258223a3SMatthew Dillon 		case WRITE_10:
1105258223a3SMatthew Dillon 			lba = scsi_4btoul(cdb->rw_10.addr);
1106258223a3SMatthew Dillon 			count = scsi_2btoul(cdb->rw_10.length);
1107258223a3SMatthew Dillon 			xa->flags = ATA_F_WRITE;
1108258223a3SMatthew Dillon 			break;
1109258223a3SMatthew Dillon 		case WRITE_12:
1110258223a3SMatthew Dillon 			lba = scsi_4btoul(cdb->rw_12.addr);
1111258223a3SMatthew Dillon 			count = scsi_4btoul(cdb->rw_12.length);
1112258223a3SMatthew Dillon 			xa->flags = ATA_F_WRITE;
1113258223a3SMatthew Dillon 			break;
1114258223a3SMatthew Dillon 		case WRITE_16:
1115258223a3SMatthew Dillon 			lba = scsi_8btou64(cdb->rw_16.addr);
1116258223a3SMatthew Dillon 			count = scsi_4btoul(cdb->rw_16.length);
1117258223a3SMatthew Dillon 			xa->flags = ATA_F_WRITE;
1118258223a3SMatthew Dillon 			break;
1119258223a3SMatthew Dillon 		default:
1120258223a3SMatthew Dillon 			ccbh->status = CAM_REQ_INVALID;
1121258223a3SMatthew Dillon 			break;
1122258223a3SMatthew Dillon 		}
1123258223a3SMatthew Dillon 		if (ccbh->status != CAM_REQ_INPROG)
1124258223a3SMatthew Dillon 			break;
1125258223a3SMatthew Dillon 
1126258223a3SMatthew Dillon 		fis = xa->fis;
1127258223a3SMatthew Dillon 		fis->flags = ATA_H2D_FLAGS_CMD;
1128258223a3SMatthew Dillon 		fis->lba_low = (u_int8_t)lba;
1129258223a3SMatthew Dillon 		fis->lba_mid = (u_int8_t)(lba >> 8);
1130258223a3SMatthew Dillon 		fis->lba_high = (u_int8_t)(lba >> 16);
1131258223a3SMatthew Dillon 		fis->device = ATA_H2D_DEVICE_LBA;
1132258223a3SMatthew Dillon 
11331980eff3SMatthew Dillon 		/*
11341980eff3SMatthew Dillon 		 * NCQ only for direct-attached disks, do not currently
11351980eff3SMatthew Dillon 		 * try to use NCQ with port multipliers.
11361980eff3SMatthew Dillon 		 */
11371980eff3SMatthew Dillon 		if (at->at_ncqdepth > 1 &&
11381980eff3SMatthew Dillon 		    ap->ap_type == ATA_PORT_T_DISK &&
1139258223a3SMatthew Dillon 		    (ap->ap_sc->sc_cap & AHCI_REG_CAP_SNCQ) &&
1140258223a3SMatthew Dillon 		    (ccbh->flags & CAM_POLLED) == 0) {
1141258223a3SMatthew Dillon 			/*
1142258223a3SMatthew Dillon 			 * Use NCQ - always uses 48 bit addressing
1143258223a3SMatthew Dillon 			 */
1144258223a3SMatthew Dillon 			xa->flags |= ATA_F_NCQ;
1145258223a3SMatthew Dillon 			fis->command = (xa->flags & ATA_F_WRITE) ?
1146258223a3SMatthew Dillon 					ATA_C_WRITE_FPDMA : ATA_C_READ_FPDMA;
1147258223a3SMatthew Dillon 			fis->lba_low_exp = (u_int8_t)(lba >> 24);
1148258223a3SMatthew Dillon 			fis->lba_mid_exp = (u_int8_t)(lba >> 32);
1149258223a3SMatthew Dillon 			fis->lba_high_exp = (u_int8_t)(lba >> 40);
1150258223a3SMatthew Dillon 			fis->sector_count = xa->tag << 3;
1151258223a3SMatthew Dillon 			fis->features = (u_int8_t)count;
1152258223a3SMatthew Dillon 			fis->features_exp = (u_int8_t)(count >> 8);
1153258223a3SMatthew Dillon 		} else if (count > 0x100 || lba > 0xFFFFFFFFU) {
1154258223a3SMatthew Dillon 			/*
1155258223a3SMatthew Dillon 			 * Use LBA48
1156258223a3SMatthew Dillon 			 */
1157258223a3SMatthew Dillon 			fis->command = (xa->flags & ATA_F_WRITE) ?
1158258223a3SMatthew Dillon 					ATA_C_WRITEDMA_EXT : ATA_C_READDMA_EXT;
1159258223a3SMatthew Dillon 			fis->lba_low_exp = (u_int8_t)(lba >> 24);
1160258223a3SMatthew Dillon 			fis->lba_mid_exp = (u_int8_t)(lba >> 32);
1161258223a3SMatthew Dillon 			fis->lba_high_exp = (u_int8_t)(lba >> 40);
1162258223a3SMatthew Dillon 			fis->sector_count = (u_int8_t)count;
1163258223a3SMatthew Dillon 			fis->sector_count_exp = (u_int8_t)(count >> 8);
1164258223a3SMatthew Dillon 		} else {
1165258223a3SMatthew Dillon 			/*
1166258223a3SMatthew Dillon 			 * Use LBA
1167258223a3SMatthew Dillon 			 *
1168258223a3SMatthew Dillon 			 * NOTE: 256 sectors is supported, stored as 0.
1169258223a3SMatthew Dillon 			 */
1170258223a3SMatthew Dillon 			fis->command = (xa->flags & ATA_F_WRITE) ?
1171258223a3SMatthew Dillon 					ATA_C_WRITEDMA : ATA_C_READDMA;
1172258223a3SMatthew Dillon 			fis->device |= (u_int8_t)(lba >> 24) & 0x0F;
1173258223a3SMatthew Dillon 			fis->sector_count = (u_int8_t)count;
1174258223a3SMatthew Dillon 		}
1175258223a3SMatthew Dillon 
1176258223a3SMatthew Dillon 		xa->data = csio->data_ptr;
1177258223a3SMatthew Dillon 		xa->datalen = csio->dxfer_len;
1178258223a3SMatthew Dillon 		xa->complete = ahci_ata_complete_disk_rw;
11793209f581SMatthew Dillon 		xa->timeout = ccbh->timeout;	/* milliseconds */
1180258223a3SMatthew Dillon 		if (ccbh->flags & CAM_POLLED)
1181258223a3SMatthew Dillon 			xa->flags |= ATA_F_POLL;
1182258223a3SMatthew Dillon 		break;
1183258223a3SMatthew Dillon 	}
1184258223a3SMatthew Dillon 
1185258223a3SMatthew Dillon 	/*
1186258223a3SMatthew Dillon 	 * If the request is still in progress the xa and FIS have
1187258223a3SMatthew Dillon 	 * been set up and must be dispatched.  Otherwise the request
1188258223a3SMatthew Dillon 	 * is complete.
1189258223a3SMatthew Dillon 	 */
1190258223a3SMatthew Dillon 	if (ccbh->status == CAM_REQ_INPROG) {
1191258223a3SMatthew Dillon 		KKASSERT(xa->complete != NULL);
1192258223a3SMatthew Dillon 		xa->atascsi_private = ccb;
1193258223a3SMatthew Dillon 		ccb->ccb_h.sim_priv.entries[0].ptr = ap;
1194f4553de1SMatthew Dillon 		ahci_os_lock_port(ap);
11951980eff3SMatthew Dillon 		fis->flags |= at->at_target;
1196258223a3SMatthew Dillon 		ahci_ata_cmd(xa);
1197f4553de1SMatthew Dillon 		ahci_os_unlock_port(ap);
1198258223a3SMatthew Dillon 	} else {
1199258223a3SMatthew Dillon 		ahci_ata_put_xfer(xa);
1200258223a3SMatthew Dillon 		xpt_done(ccb);
1201258223a3SMatthew Dillon 	}
1202258223a3SMatthew Dillon }
1203258223a3SMatthew Dillon 
1204b4189e5eSMatthew Dillon /*
1205b4189e5eSMatthew Dillon  * Convert the SCSI command in ccb to an ata_xfer command in xa
1206b4189e5eSMatthew Dillon  * for ATA_PORT_T_ATAPI operations.  Set the completion function
1207b4189e5eSMatthew Dillon  * to convert the response back, then dispatch to the OpenBSD AHCI
1208b4189e5eSMatthew Dillon  * layer.
1209b4189e5eSMatthew Dillon  */
1210258223a3SMatthew Dillon static
1211258223a3SMatthew Dillon void
12121980eff3SMatthew Dillon ahci_xpt_scsi_atapi_io(struct ahci_port *ap, struct ata_port *atx,
12131980eff3SMatthew Dillon 			union ccb *ccb)
1214258223a3SMatthew Dillon {
1215258223a3SMatthew Dillon 	struct ccb_hdr *ccbh;
1216258223a3SMatthew Dillon 	struct ccb_scsiio *csio;
1217258223a3SMatthew Dillon 	struct ata_xfer *xa;
1218258223a3SMatthew Dillon 	struct ata_fis_h2d *fis;
1219b4189e5eSMatthew Dillon 	scsi_cdb_t cdbs;
1220b4189e5eSMatthew Dillon 	scsi_cdb_t cdbd;
1221b4189e5eSMatthew Dillon 	int flags;
12221980eff3SMatthew Dillon 	struct ata_port	*at;
1223258223a3SMatthew Dillon 
1224258223a3SMatthew Dillon 	ccbh = &ccb->csio.ccb_h;
1225258223a3SMatthew Dillon 	csio = &ccb->csio;
12261980eff3SMatthew Dillon 	at = atx ? atx : &ap->ap_ata[0];
1227b4189e5eSMatthew Dillon 
1228b4189e5eSMatthew Dillon 	switch (ccbh->flags & CAM_DIR_MASK) {
1229b4189e5eSMatthew Dillon 	case CAM_DIR_IN:
1230b4189e5eSMatthew Dillon 		flags = ATA_F_PACKET | ATA_F_READ;
1231b4189e5eSMatthew Dillon 		break;
1232b4189e5eSMatthew Dillon 	case CAM_DIR_OUT:
1233b4189e5eSMatthew Dillon 		flags = ATA_F_PACKET | ATA_F_WRITE;
1234b4189e5eSMatthew Dillon 		break;
1235b4189e5eSMatthew Dillon 	case CAM_DIR_NONE:
1236b4189e5eSMatthew Dillon 		flags = ATA_F_PACKET;
1237b4189e5eSMatthew Dillon 		break;
1238b4189e5eSMatthew Dillon 	default:
1239b4189e5eSMatthew Dillon 		ccbh->status = CAM_REQ_INVALID;
1240b4189e5eSMatthew Dillon 		xpt_done(ccb);
1241b4189e5eSMatthew Dillon 		return;
1242b4189e5eSMatthew Dillon 		/* NOT REACHED */
1243b4189e5eSMatthew Dillon 	}
1244b4189e5eSMatthew Dillon 
1245b4189e5eSMatthew Dillon 	/*
1246b4189e5eSMatthew Dillon 	 * The command has to fit in the packet command buffer.
1247b4189e5eSMatthew Dillon 	 */
1248b4189e5eSMatthew Dillon 	if (csio->cdb_len < 6 || csio->cdb_len > 16) {
1249b4189e5eSMatthew Dillon 		ccbh->status = CAM_CCB_LEN_ERR;
1250b4189e5eSMatthew Dillon 		xpt_done(ccb);
1251b4189e5eSMatthew Dillon 		return;
1252b4189e5eSMatthew Dillon 	}
1253b4189e5eSMatthew Dillon 
1254b4189e5eSMatthew Dillon 	/*
1255b4189e5eSMatthew Dillon 	 * Initialize the XA and FIS.
12561980eff3SMatthew Dillon 	 *
12571980eff3SMatthew Dillon 	 * XXX not passing NULL at for direct attach!
1258b4189e5eSMatthew Dillon 	 */
12591980eff3SMatthew Dillon 	xa = ahci_ata_get_xfer(ap, atx);
1260258223a3SMatthew Dillon 	fis = xa->fis;
1261258223a3SMatthew Dillon 
12621980eff3SMatthew Dillon 	fis->flags = ATA_H2D_FLAGS_CMD | at->at_target;
12631980eff3SMatthew Dillon 	fis->command = ATA_C_PACKET;
12641980eff3SMatthew Dillon 	fis->device = 0;
12651980eff3SMatthew Dillon 	fis->sector_count = xa->tag << 3;
12661980eff3SMatthew Dillon 	fis->features = ATA_H2D_FEATURES_DMA |
12671980eff3SMatthew Dillon 		    ((flags & ATA_F_WRITE) ?
12681980eff3SMatthew Dillon 		    ATA_H2D_FEATURES_DIR_WRITE : ATA_H2D_FEATURES_DIR_READ);
12691980eff3SMatthew Dillon 	fis->lba_mid = 0x00;
12701980eff3SMatthew Dillon 	fis->lba_high = 0x20;
12711980eff3SMatthew Dillon 
1272b4189e5eSMatthew Dillon 	xa->flags = flags;
1273b4189e5eSMatthew Dillon 	xa->data = csio->data_ptr;
1274b4189e5eSMatthew Dillon 	xa->datalen = csio->dxfer_len;
12753209f581SMatthew Dillon 	xa->timeout = ccbh->timeout;	/* milliseconds */
12761980eff3SMatthew Dillon 
1277b4189e5eSMatthew Dillon 	if (ccbh->flags & CAM_POLLED)
1278b4189e5eSMatthew Dillon 		xa->flags |= ATA_F_POLL;
1279258223a3SMatthew Dillon 
1280258223a3SMatthew Dillon 	/*
1281b4189e5eSMatthew Dillon 	 * Copy the cdb to the packetcmd buffer in the FIS using a
1282b4189e5eSMatthew Dillon 	 * convenient pointer in the xa.
1283258223a3SMatthew Dillon 	 */
1284b4189e5eSMatthew Dillon 	cdbs = (void *)((ccbh->flags & CAM_CDB_POINTER) ?
1285b4189e5eSMatthew Dillon 			csio->cdb_io.cdb_ptr : csio->cdb_io.cdb_bytes);
1286b4189e5eSMatthew Dillon 	bcopy(cdbs, xa->packetcmd, csio->cdb_len);
1287b4189e5eSMatthew Dillon 
1288669fbbf7SMatthew Dillon #if 0
1289b4189e5eSMatthew Dillon 	kprintf("opcode %d cdb_len %d dxfer_len %d\n",
1290b4189e5eSMatthew Dillon 		cdbs->generic.opcode,
1291b4189e5eSMatthew Dillon 		csio->cdb_len, csio->dxfer_len);
1292669fbbf7SMatthew Dillon #endif
1293b4189e5eSMatthew Dillon 
1294b4189e5eSMatthew Dillon 	/*
1295b4189e5eSMatthew Dillon 	 * Some ATAPI commands do not actually follow the SCSI standard.
1296b4189e5eSMatthew Dillon 	 */
1297b4189e5eSMatthew Dillon 	cdbd = (void *)xa->packetcmd;
1298b4189e5eSMatthew Dillon 
1299b4189e5eSMatthew Dillon 	switch(cdbd->generic.opcode) {
1300258223a3SMatthew Dillon 	case INQUIRY:
1301b4189e5eSMatthew Dillon 		/*
1302b4189e5eSMatthew Dillon 		 * Some ATAPI devices can't handle SI_EVPD being set
1303b4189e5eSMatthew Dillon 		 * for a basic inquiry (page_code == 0).
1304b4189e5eSMatthew Dillon 		 *
1305b4189e5eSMatthew Dillon 		 * Some ATAPI devices can't handle long inquiry lengths,
1306b4189e5eSMatthew Dillon 		 * don't ask me why.  Truncate the inquiry length.
1307b4189e5eSMatthew Dillon 		 */
1308b4189e5eSMatthew Dillon 		if ((cdbd->inquiry.byte2 & SI_EVPD) &&
1309b4189e5eSMatthew Dillon 		    cdbd->inquiry.page_code == 0) {
1310b4189e5eSMatthew Dillon 			cdbd->inquiry.byte2 &= ~SI_EVPD;
1311b4189e5eSMatthew Dillon 		}
1312b4189e5eSMatthew Dillon 		if (cdbd->inquiry.page_code == 0 &&
1313b4189e5eSMatthew Dillon 		    cdbd->inquiry.length > SHORT_INQUIRY_LENGTH) {
1314b4189e5eSMatthew Dillon 			cdbd->inquiry.length = SHORT_INQUIRY_LENGTH;
1315b4189e5eSMatthew Dillon 		}
1316b4189e5eSMatthew Dillon 		break;
1317258223a3SMatthew Dillon 	case READ_6:
1318258223a3SMatthew Dillon 	case WRITE_6:
1319b4189e5eSMatthew Dillon 		/*
1320b4189e5eSMatthew Dillon 		 * Convert *_6 to *_10 commands.  Most ATAPI devices
1321b4189e5eSMatthew Dillon 		 * cannot handle the SCSI READ_6 and WRITE_6 commands.
1322b4189e5eSMatthew Dillon 		 */
1323b4189e5eSMatthew Dillon 		cdbd->rw_10.opcode |= 0x20;
1324b4189e5eSMatthew Dillon 		cdbd->rw_10.byte2 = 0;
1325b4189e5eSMatthew Dillon 		cdbd->rw_10.addr[0] = cdbs->rw_6.addr[0] & 0x1F;
1326b4189e5eSMatthew Dillon 		cdbd->rw_10.addr[1] = cdbs->rw_6.addr[1];
1327b4189e5eSMatthew Dillon 		cdbd->rw_10.addr[2] = cdbs->rw_6.addr[2];
1328b4189e5eSMatthew Dillon 		cdbd->rw_10.addr[3] = 0;
1329b4189e5eSMatthew Dillon 		cdbd->rw_10.reserved = 0;
1330b4189e5eSMatthew Dillon 		cdbd->rw_10.length[0] = 0;
1331b4189e5eSMatthew Dillon 		cdbd->rw_10.length[1] = cdbs->rw_6.length;
1332b4189e5eSMatthew Dillon 		cdbd->rw_10.control = cdbs->rw_6.control;
1333b4189e5eSMatthew Dillon 		break;
1334258223a3SMatthew Dillon 	default:
1335258223a3SMatthew Dillon 		break;
1336258223a3SMatthew Dillon 	}
1337258223a3SMatthew Dillon 
1338b4189e5eSMatthew Dillon 	/*
1339b4189e5eSMatthew Dillon 	 * And dispatch
1340b4189e5eSMatthew Dillon 	 */
1341b4189e5eSMatthew Dillon 	xa->complete = ahci_atapi_complete_cmd;
1342258223a3SMatthew Dillon 	xa->atascsi_private = ccb;
1343258223a3SMatthew Dillon 	ccb->ccb_h.sim_priv.entries[0].ptr = ap;
1344258223a3SMatthew Dillon 	ahci_ata_cmd(xa);
1345258223a3SMatthew Dillon }
1346258223a3SMatthew Dillon 
1347b4189e5eSMatthew Dillon /*
1348b4189e5eSMatthew Dillon  * Completion function for ATA_PORT_T_DISK cache synchronization.
1349b4189e5eSMatthew Dillon  */
1350258223a3SMatthew Dillon static
1351258223a3SMatthew Dillon void
1352258223a3SMatthew Dillon ahci_ata_complete_disk_synchronize_cache(struct ata_xfer *xa)
1353258223a3SMatthew Dillon {
1354258223a3SMatthew Dillon 	union ccb *ccb = xa->atascsi_private;
1355258223a3SMatthew Dillon 	struct ccb_hdr *ccbh = &ccb->ccb_h;
1356258223a3SMatthew Dillon 	struct ahci_port *ap = ccb->ccb_h.sim_priv.entries[0].ptr;
1357258223a3SMatthew Dillon 
1358258223a3SMatthew Dillon 	switch(xa->state) {
1359258223a3SMatthew Dillon 	case ATA_S_COMPLETE:
1360258223a3SMatthew Dillon 		ccbh->status = CAM_REQ_CMP;
1361b4189e5eSMatthew Dillon 		ccb->csio.scsi_status = SCSI_STATUS_OK;
1362258223a3SMatthew Dillon 		break;
1363258223a3SMatthew Dillon 	case ATA_S_ERROR:
13641980eff3SMatthew Dillon 		kprintf("%s: synchronize_cache: error\n",
13651980eff3SMatthew Dillon 			ATANAME(ap, xa->at));
1366b4189e5eSMatthew Dillon 		ccbh->status = CAM_SCSI_STATUS_ERROR | CAM_AUTOSNS_VALID;
1367b4189e5eSMatthew Dillon 		ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND;
1368b4189e5eSMatthew Dillon 		ahci_ata_dummy_sense(&ccb->csio.sense_data);
1369258223a3SMatthew Dillon 		break;
1370258223a3SMatthew Dillon 	case ATA_S_TIMEOUT:
13711980eff3SMatthew Dillon 		kprintf("%s: synchronize_cache: timeout\n",
13721980eff3SMatthew Dillon 			ATANAME(ap, xa->at));
1373258223a3SMatthew Dillon 		ccbh->status = CAM_CMD_TIMEOUT;
1374258223a3SMatthew Dillon 		break;
1375258223a3SMatthew Dillon 	default:
1376258223a3SMatthew Dillon 		kprintf("%s: synchronize_cache: unknown state %d\n",
13771980eff3SMatthew Dillon 			ATANAME(ap, xa->at), xa->state);
1378258223a3SMatthew Dillon 		ccbh->status = CAM_REQ_CMP_ERR;
1379258223a3SMatthew Dillon 		break;
1380258223a3SMatthew Dillon 	}
1381258223a3SMatthew Dillon 	ahci_ata_put_xfer(xa);
1382f4553de1SMatthew Dillon 	ahci_os_unlock_port(ap);
1383258223a3SMatthew Dillon 	xpt_done(ccb);
1384f4553de1SMatthew Dillon 	ahci_os_lock_port(ap);
1385258223a3SMatthew Dillon }
1386258223a3SMatthew Dillon 
1387b4189e5eSMatthew Dillon /*
1388b4189e5eSMatthew Dillon  * Completion function for ATA_PORT_T_DISK I/O
1389b4189e5eSMatthew Dillon  */
1390258223a3SMatthew Dillon static
1391258223a3SMatthew Dillon void
1392258223a3SMatthew Dillon ahci_ata_complete_disk_rw(struct ata_xfer *xa)
1393258223a3SMatthew Dillon {
1394258223a3SMatthew Dillon 	union ccb *ccb = xa->atascsi_private;
1395258223a3SMatthew Dillon 	struct ccb_hdr *ccbh = &ccb->ccb_h;
1396258223a3SMatthew Dillon 	struct ahci_port *ap = ccb->ccb_h.sim_priv.entries[0].ptr;
1397258223a3SMatthew Dillon 
1398258223a3SMatthew Dillon 	switch(xa->state) {
1399258223a3SMatthew Dillon 	case ATA_S_COMPLETE:
1400258223a3SMatthew Dillon 		ccbh->status = CAM_REQ_CMP;
1401b4189e5eSMatthew Dillon 		ccb->csio.scsi_status = SCSI_STATUS_OK;
1402258223a3SMatthew Dillon 		break;
1403258223a3SMatthew Dillon 	case ATA_S_ERROR:
14041980eff3SMatthew Dillon 		kprintf("%s: disk_rw: error\n", ATANAME(ap, xa->at));
1405b4189e5eSMatthew Dillon 		ccbh->status = CAM_SCSI_STATUS_ERROR | CAM_AUTOSNS_VALID;
1406b4189e5eSMatthew Dillon 		ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND;
1407b4189e5eSMatthew Dillon 		ahci_ata_dummy_sense(&ccb->csio.sense_data);
1408258223a3SMatthew Dillon 		break;
1409258223a3SMatthew Dillon 	case ATA_S_TIMEOUT:
14101980eff3SMatthew Dillon 		kprintf("%s: disk_rw: timeout\n", ATANAME(ap, xa->at));
1411258223a3SMatthew Dillon 		ccbh->status = CAM_CMD_TIMEOUT;
14124c339a5fSMatthew Dillon 		ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND;
14134c339a5fSMatthew Dillon 		ahci_ata_dummy_sense(&ccb->csio.sense_data);
1414258223a3SMatthew Dillon 		break;
1415258223a3SMatthew Dillon 	default:
1416258223a3SMatthew Dillon 		kprintf("%s: disk_rw: unknown state %d\n",
14171980eff3SMatthew Dillon 			ATANAME(ap, xa->at), xa->state);
1418258223a3SMatthew Dillon 		ccbh->status = CAM_REQ_CMP_ERR;
1419258223a3SMatthew Dillon 		break;
1420258223a3SMatthew Dillon 	}
1421258223a3SMatthew Dillon 	ccb->csio.resid = xa->resid;
1422258223a3SMatthew Dillon 	ahci_ata_put_xfer(xa);
1423f4553de1SMatthew Dillon 	ahci_os_unlock_port(ap);
1424258223a3SMatthew Dillon 	xpt_done(ccb);
1425f4553de1SMatthew Dillon 	ahci_os_lock_port(ap);
1426258223a3SMatthew Dillon }
1427b4189e5eSMatthew Dillon 
14287d4fcf34SMatthew Dillon /*
14297d4fcf34SMatthew Dillon  * Completion function for ATA_PORT_T_ATAPI I/O
14307d4fcf34SMatthew Dillon  *
14317d4fcf34SMatthew Dillon  * Sense data is returned in the rfis.
14327d4fcf34SMatthew Dillon  */
1433b4189e5eSMatthew Dillon static
1434b4189e5eSMatthew Dillon void
1435b4189e5eSMatthew Dillon ahci_atapi_complete_cmd(struct ata_xfer *xa)
1436b4189e5eSMatthew Dillon {
1437b4189e5eSMatthew Dillon 	union ccb *ccb = xa->atascsi_private;
1438b4189e5eSMatthew Dillon 	struct ccb_hdr *ccbh = &ccb->ccb_h;
1439b4189e5eSMatthew Dillon 	struct ahci_port *ap = ccb->ccb_h.sim_priv.entries[0].ptr;
1440b4189e5eSMatthew Dillon 	scsi_cdb_t cdb;
1441b4189e5eSMatthew Dillon 
1442b4189e5eSMatthew Dillon 	cdb = (void *)((ccb->ccb_h.flags & CAM_CDB_POINTER) ?
1443b4189e5eSMatthew Dillon 			ccb->csio.cdb_io.cdb_ptr : ccb->csio.cdb_io.cdb_bytes);
1444b4189e5eSMatthew Dillon 
1445b4189e5eSMatthew Dillon 	switch(xa->state) {
1446b4189e5eSMatthew Dillon 	case ATA_S_COMPLETE:
1447b4189e5eSMatthew Dillon 		ccbh->status = CAM_REQ_CMP;
1448b4189e5eSMatthew Dillon 		ccb->csio.scsi_status = SCSI_STATUS_OK;
1449b4189e5eSMatthew Dillon 		break;
1450b4189e5eSMatthew Dillon 	case ATA_S_ERROR:
1451b4189e5eSMatthew Dillon 		ccbh->status = CAM_SCSI_STATUS_ERROR;
1452b4189e5eSMatthew Dillon 		ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND;
14537d4fcf34SMatthew Dillon 		ahci_ata_atapi_sense(&xa->rfis, &ccb->csio.sense_data);
1454b4189e5eSMatthew Dillon 		break;
1455b4189e5eSMatthew Dillon 	case ATA_S_TIMEOUT:
1456b4189e5eSMatthew Dillon 		kprintf("%s: cmd %d: timeout\n",
1457b4189e5eSMatthew Dillon 			PORTNAME(ap), cdb->generic.opcode);
1458b4189e5eSMatthew Dillon 		ccbh->status = CAM_CMD_TIMEOUT;
14594c339a5fSMatthew Dillon 		ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND;
14604c339a5fSMatthew Dillon 		ahci_ata_dummy_sense(&ccb->csio.sense_data);
1461b4189e5eSMatthew Dillon 		break;
1462b4189e5eSMatthew Dillon 	default:
1463b4189e5eSMatthew Dillon 		kprintf("%s: cmd %d: unknown state %d\n",
1464b4189e5eSMatthew Dillon 			PORTNAME(ap), cdb->generic.opcode, xa->state);
1465b4189e5eSMatthew Dillon 		ccbh->status = CAM_REQ_CMP_ERR;
1466b4189e5eSMatthew Dillon 		break;
1467b4189e5eSMatthew Dillon 	}
1468b4189e5eSMatthew Dillon 	ccb->csio.resid = xa->resid;
1469b4189e5eSMatthew Dillon 	ahci_ata_put_xfer(xa);
1470f4553de1SMatthew Dillon 	ahci_os_unlock_port(ap);
1471b4189e5eSMatthew Dillon 	xpt_done(ccb);
1472f4553de1SMatthew Dillon 	ahci_os_lock_port(ap);
1473b4189e5eSMatthew Dillon }
1474b4189e5eSMatthew Dillon 
14757d4fcf34SMatthew Dillon /*
14767d4fcf34SMatthew Dillon  * Construct dummy sense data for errors on DISKs
14777d4fcf34SMatthew Dillon  */
1478b4189e5eSMatthew Dillon static
1479b4189e5eSMatthew Dillon void
1480b4189e5eSMatthew Dillon ahci_ata_dummy_sense(struct scsi_sense_data *sense_data)
1481b4189e5eSMatthew Dillon {
1482b4189e5eSMatthew Dillon 	sense_data->error_code = SSD_ERRCODE_VALID | SSD_CURRENT_ERROR;
1483b4189e5eSMatthew Dillon 	sense_data->segment = 0;
1484b4189e5eSMatthew Dillon 	sense_data->flags = SSD_KEY_MEDIUM_ERROR;
1485b4189e5eSMatthew Dillon 	sense_data->info[0] = 0;
1486b4189e5eSMatthew Dillon 	sense_data->info[1] = 0;
1487b4189e5eSMatthew Dillon 	sense_data->info[2] = 0;
1488b4189e5eSMatthew Dillon 	sense_data->info[3] = 0;
1489b4189e5eSMatthew Dillon 	sense_data->extra_len = 0;
1490b4189e5eSMatthew Dillon }
14917d4fcf34SMatthew Dillon 
14927d4fcf34SMatthew Dillon /*
14937d4fcf34SMatthew Dillon  * Construct atapi sense data for errors on ATAPI
14947d4fcf34SMatthew Dillon  *
14957d4fcf34SMatthew Dillon  * The ATAPI sense data is stored in the passed rfis and must be converted
14967d4fcf34SMatthew Dillon  * to SCSI sense data.
14977d4fcf34SMatthew Dillon  */
14987d4fcf34SMatthew Dillon static
14997d4fcf34SMatthew Dillon void
15007d4fcf34SMatthew Dillon ahci_ata_atapi_sense(struct ata_fis_d2h *rfis,
15017d4fcf34SMatthew Dillon 		     struct scsi_sense_data *sense_data)
15027d4fcf34SMatthew Dillon {
15037d4fcf34SMatthew Dillon 	sense_data->error_code = SSD_ERRCODE_VALID | SSD_CURRENT_ERROR;
15047d4fcf34SMatthew Dillon 	sense_data->segment = 0;
15057d4fcf34SMatthew Dillon 	sense_data->flags = (rfis->error & 0xF0) >> 4;
15067d4fcf34SMatthew Dillon 	if (rfis->error & 0x04)
15077d4fcf34SMatthew Dillon 		sense_data->flags |= SSD_KEY_ILLEGAL_REQUEST;
15087d4fcf34SMatthew Dillon 	if (rfis->error & 0x02)
15097d4fcf34SMatthew Dillon 		sense_data->flags |= SSD_EOM;
15107d4fcf34SMatthew Dillon 	if (rfis->error & 0x01)
15117d4fcf34SMatthew Dillon 		sense_data->flags |= SSD_ILI;
15127d4fcf34SMatthew Dillon 	sense_data->info[0] = 0;
15137d4fcf34SMatthew Dillon 	sense_data->info[1] = 0;
15147d4fcf34SMatthew Dillon 	sense_data->info[2] = 0;
15157d4fcf34SMatthew Dillon 	sense_data->info[3] = 0;
15167d4fcf34SMatthew Dillon 	sense_data->extra_len = 0;
15177d4fcf34SMatthew Dillon }
1518