xref: /dflybsd-src/sys/dev/disk/ahci/ahci_cam.c (revision cec85a37534fc8449308734c4d53fe8a31c2772c)
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);
100258223a3SMatthew Dillon static void ahci_xpt_scsi_disk_io(struct cam_sim *sim, union ccb *ccb);
101258223a3SMatthew Dillon static void ahci_xpt_scsi_atapi_io(struct cam_sim *sim, union ccb *ccb);
102258223a3SMatthew Dillon 
103258223a3SMatthew Dillon static void ahci_ata_complete_disk_rw(struct ata_xfer *xa);
104258223a3SMatthew Dillon static void ahci_ata_complete_disk_synchronize_cache(struct ata_xfer *xa);
105b4189e5eSMatthew Dillon static void ahci_atapi_complete_cmd(struct ata_xfer *xa);
106b4189e5eSMatthew Dillon static void ahci_ata_dummy_sense(struct scsi_sense_data *sense_data);
1077d4fcf34SMatthew Dillon static void ahci_ata_atapi_sense(struct ata_fis_d2h *rfis,
1087d4fcf34SMatthew Dillon 		     struct scsi_sense_data *sense_data);
109258223a3SMatthew Dillon 
110258223a3SMatthew Dillon static int ahci_cam_probe(struct ahci_port *ap);
111b4189e5eSMatthew Dillon static int ahci_cam_probe_disk(struct ahci_port *ap);
112b4189e5eSMatthew Dillon static int ahci_cam_probe_atapi(struct ahci_port *ap);
113b4189e5eSMatthew Dillon static void ahci_ata_dummy_done(struct ata_xfer *xa);
114258223a3SMatthew Dillon static void ata_fix_identify(struct ata_identify *id);
115258223a3SMatthew Dillon static void ahci_cam_rescan(struct ahci_port *ap);
116258223a3SMatthew Dillon 
117258223a3SMatthew Dillon int
118258223a3SMatthew Dillon ahci_cam_attach(struct ahci_port *ap)
119258223a3SMatthew Dillon {
120258223a3SMatthew Dillon 	struct cam_devq *devq;
121258223a3SMatthew Dillon 	struct cam_sim *sim;
122258223a3SMatthew Dillon 	int error;
123258223a3SMatthew Dillon 	int unit;
124258223a3SMatthew Dillon 
125*cec85a37SMatthew Dillon 	/*
126*cec85a37SMatthew Dillon 	 * We want at least one ccb to be available for error processing
127*cec85a37SMatthew Dillon 	 * so don't let CAM use more then ncmds - 1.
128*cec85a37SMatthew Dillon 	 */
129258223a3SMatthew Dillon 	unit = device_get_unit(ap->ap_sc->sc_dev);
130*cec85a37SMatthew Dillon 	if (ap->ap_sc->sc_ncmds > 1)
131*cec85a37SMatthew Dillon 		devq = cam_simq_alloc(ap->ap_sc->sc_ncmds - 1);
132*cec85a37SMatthew Dillon 	else
133258223a3SMatthew Dillon 		devq = cam_simq_alloc(ap->ap_sc->sc_ncmds);
134258223a3SMatthew Dillon 	if (devq == NULL) {
135258223a3SMatthew Dillon 		return (ENOMEM);
136258223a3SMatthew Dillon 	}
137258223a3SMatthew Dillon 	sim = cam_sim_alloc(ahci_xpt_action, ahci_xpt_poll, "ahci",
138258223a3SMatthew Dillon 			   (void *)ap, unit, &sim_mplock, 1, 1, devq);
139258223a3SMatthew Dillon 	cam_simq_release(devq);
140258223a3SMatthew Dillon 	if (sim == NULL) {
141258223a3SMatthew Dillon 		return (ENOMEM);
142258223a3SMatthew Dillon 	}
143258223a3SMatthew Dillon 	ap->ap_sim = sim;
144258223a3SMatthew Dillon 	error = xpt_bus_register(ap->ap_sim, ap->ap_num);
145258223a3SMatthew Dillon 	if (error != CAM_SUCCESS) {
146258223a3SMatthew Dillon 		ahci_cam_detach(ap);
147258223a3SMatthew Dillon 		return (EINVAL);
148258223a3SMatthew Dillon 	}
149258223a3SMatthew Dillon 	ap->ap_flags |= AP_F_BUS_REGISTERED;
150258223a3SMatthew Dillon 	error = xpt_create_path(&ap->ap_path, NULL, cam_sim_path(sim),
151258223a3SMatthew Dillon 				CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD);
152258223a3SMatthew Dillon 	if (error != CAM_REQ_CMP) {
153258223a3SMatthew Dillon 		ahci_cam_detach(ap);
154258223a3SMatthew Dillon 		return (ENOMEM);
155258223a3SMatthew Dillon 	}
156258223a3SMatthew Dillon 
157258223a3SMatthew Dillon 	error = ahci_cam_probe(ap);
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 	ahci_cam_rescan(ap);
165258223a3SMatthew Dillon 
166258223a3SMatthew Dillon 	return(0);
167258223a3SMatthew Dillon }
168258223a3SMatthew Dillon 
169258223a3SMatthew Dillon void
170fd8bd957SMatthew Dillon ahci_cam_changed(struct ahci_port *ap, int found)
171258223a3SMatthew Dillon {
172fd8bd957SMatthew Dillon 	struct cam_path *tmppath;
173fd8bd957SMatthew Dillon 
174258223a3SMatthew Dillon 	if (ap->ap_sim == NULL)
175258223a3SMatthew Dillon 		return;
176fd8bd957SMatthew Dillon 	if (xpt_create_path(&tmppath, NULL, cam_sim_path(ap->ap_sim),
177fd8bd957SMatthew Dillon 			    0, CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
178fd8bd957SMatthew Dillon 		return;
179fd8bd957SMatthew Dillon 	}
180fd8bd957SMatthew Dillon 	if (found) {
181258223a3SMatthew Dillon 		ahci_cam_probe(ap);
182fd8bd957SMatthew Dillon 		/*
183fd8bd957SMatthew Dillon 		 * XXX calling AC_FOUND_DEVICE with inquiry data is
184fd8bd957SMatthew Dillon 		 *     basically a NOP.  For now just tell CAM to
185fd8bd957SMatthew Dillon 		 *     rescan the bus.
186fd8bd957SMatthew Dillon 		 */
187fd8bd957SMatthew Dillon 		xpt_async(AC_FOUND_DEVICE, tmppath, NULL);
188258223a3SMatthew Dillon 		ahci_cam_rescan(ap);
189fd8bd957SMatthew Dillon 	} else {
190fd8bd957SMatthew Dillon 		xpt_async(AC_LOST_DEVICE, tmppath, NULL);
191fd8bd957SMatthew Dillon 	}
192fd8bd957SMatthew Dillon 	xpt_free_path(tmppath);
193258223a3SMatthew Dillon }
194258223a3SMatthew Dillon 
195258223a3SMatthew Dillon void
196258223a3SMatthew Dillon ahci_cam_detach(struct ahci_port *ap)
197258223a3SMatthew Dillon {
198258223a3SMatthew Dillon 	int error;
199258223a3SMatthew Dillon 
200258223a3SMatthew Dillon 	if ((ap->ap_flags & AP_F_CAM_ATTACHED) == 0)
201258223a3SMatthew Dillon 		return;
202258223a3SMatthew Dillon 	get_mplock();
203258223a3SMatthew Dillon 	if (ap->ap_sim) {
204258223a3SMatthew Dillon 		xpt_freeze_simq(ap->ap_sim, 1);
205258223a3SMatthew Dillon 	}
206258223a3SMatthew Dillon 	if (ap->ap_path) {
207258223a3SMatthew Dillon 		xpt_free_path(ap->ap_path);
208258223a3SMatthew Dillon 		ap->ap_path = NULL;
209258223a3SMatthew Dillon 	}
210258223a3SMatthew Dillon 	if (ap->ap_flags & AP_F_BUS_REGISTERED) {
211258223a3SMatthew Dillon 		error = xpt_bus_deregister(cam_sim_path(ap->ap_sim));
212258223a3SMatthew Dillon 		KKASSERT(error == CAM_REQ_CMP);
213258223a3SMatthew Dillon 		ap->ap_flags &= ~AP_F_BUS_REGISTERED;
214258223a3SMatthew Dillon 	}
215258223a3SMatthew Dillon 	if (ap->ap_sim) {
216258223a3SMatthew Dillon 		cam_sim_free(ap->ap_sim);
217258223a3SMatthew Dillon 		ap->ap_sim = NULL;
218258223a3SMatthew Dillon 	}
219258223a3SMatthew Dillon 	rel_mplock();
220258223a3SMatthew Dillon 	ap->ap_flags &= ~AP_F_CAM_ATTACHED;
221258223a3SMatthew Dillon }
222258223a3SMatthew Dillon 
223258223a3SMatthew Dillon /*
224258223a3SMatthew Dillon  * Once the AHCI port has been attched we need to probe for a device or
225258223a3SMatthew Dillon  * devices on the port and setup various options.
226258223a3SMatthew Dillon  */
227258223a3SMatthew Dillon static int
228258223a3SMatthew Dillon ahci_cam_probe(struct ahci_port *ap)
229258223a3SMatthew Dillon {
230258223a3SMatthew Dillon 	struct ata_xfer	*xa;
231258223a3SMatthew Dillon 	u_int64_t	capacity;
232258223a3SMatthew Dillon 	u_int64_t	capacity_bytes;
233258223a3SMatthew Dillon 	int		model_len;
234258223a3SMatthew Dillon 	int		status;
235fd8bd957SMatthew Dillon 	int		error;
236258223a3SMatthew Dillon 	int		devncqdepth;
237258223a3SMatthew Dillon 	int		i;
238669fbbf7SMatthew Dillon 	const char	*wcstr;
239669fbbf7SMatthew Dillon 	const char	*rastr;
240fd8bd957SMatthew Dillon 	const char	*scstr;
241fd8bd957SMatthew Dillon 	const char	*type;
242fd8bd957SMatthew Dillon 
243fd8bd957SMatthew Dillon 	if (ap->ap_ata.ap_type == ATA_PORT_T_NONE)
244fd8bd957SMatthew Dillon 		return (EIO);
245258223a3SMatthew Dillon 
246258223a3SMatthew Dillon 	/*
247258223a3SMatthew Dillon 	 * Issue identify, saving the result
248258223a3SMatthew Dillon 	 */
249258223a3SMatthew Dillon 	xa = ahci_ata_get_xfer(ap);
250258223a3SMatthew Dillon 	xa->complete = ahci_ata_dummy_done;
251258223a3SMatthew Dillon 	xa->data = &ap->ap_ata.ap_identify;
252258223a3SMatthew Dillon 	xa->datalen = sizeof(ap->ap_ata.ap_identify);
253258223a3SMatthew Dillon 	xa->fis->flags = ATA_H2D_FLAGS_CMD;
254fd8bd957SMatthew Dillon 	if (ap->ap_ata.ap_type == ATA_PORT_T_ATAPI) {
255fd8bd957SMatthew Dillon 		xa->fis->command = ATA_C_ATAPI_IDENTIFY;
256fd8bd957SMatthew Dillon 		type = "ATAPI";
257fd8bd957SMatthew Dillon 	} else {
258258223a3SMatthew Dillon 		xa->fis->command = ATA_C_IDENTIFY;
259fd8bd957SMatthew Dillon 		type = "DISK";
260fd8bd957SMatthew Dillon 	}
261258223a3SMatthew Dillon 	xa->fis->features = 0;
262258223a3SMatthew Dillon 	xa->fis->device = 0;
263258223a3SMatthew Dillon 	xa->flags = ATA_F_READ | ATA_F_PIO | ATA_F_POLL;
264258223a3SMatthew Dillon 	xa->timeout = hz;
265258223a3SMatthew Dillon 
266258223a3SMatthew Dillon 	status = ahci_ata_cmd(xa);
267258223a3SMatthew Dillon 	if (status != ATA_COMPLETE) {
268fd8bd957SMatthew Dillon 		kprintf("%s: Detected %s device but unable to IDENTIFY\n",
269fd8bd957SMatthew Dillon 			PORTNAME(ap), type);
270258223a3SMatthew Dillon 		ahci_ata_put_xfer(xa);
271258223a3SMatthew Dillon 		return(EIO);
272258223a3SMatthew Dillon 	}
273258223a3SMatthew Dillon 	if (xa->state != ATA_S_COMPLETE) {
274fd8bd957SMatthew Dillon 		kprintf("%s: Detected %s device but unable to IDENTIFY "
275258223a3SMatthew Dillon 			" xa->state=%d\n",
276fd8bd957SMatthew Dillon 			PORTNAME(ap), type, xa->state);
277258223a3SMatthew Dillon 		ahci_ata_put_xfer(xa);
278258223a3SMatthew Dillon 		return(EIO);
279258223a3SMatthew Dillon 	}
280258223a3SMatthew Dillon 	ahci_ata_put_xfer(xa);
281258223a3SMatthew Dillon 
282258223a3SMatthew Dillon 	ata_fix_identify(&ap->ap_ata.ap_identify);
283258223a3SMatthew Dillon 
284258223a3SMatthew Dillon 	/*
285258223a3SMatthew Dillon 	 * Read capacity using SATA probe info.
286258223a3SMatthew Dillon 	 */
287258223a3SMatthew Dillon 	if (le16toh(ap->ap_ata.ap_identify.cmdset83) & 0x0400) {
288258223a3SMatthew Dillon 		/* LBA48 feature set supported */
289258223a3SMatthew Dillon 		capacity = 0;
290258223a3SMatthew Dillon 		for (i = 3; i >= 0; --i) {
291258223a3SMatthew Dillon 			capacity <<= 16;
292258223a3SMatthew Dillon 			capacity +=
293258223a3SMatthew Dillon 			    le16toh(ap->ap_ata.ap_identify.addrsecxt[i]);
294258223a3SMatthew Dillon 		}
295258223a3SMatthew Dillon 	} else {
296258223a3SMatthew Dillon 		capacity = le16toh(ap->ap_ata.ap_identify.addrsec[1]);
297258223a3SMatthew Dillon 		capacity <<= 16;
298258223a3SMatthew Dillon 		capacity += le16toh(ap->ap_ata.ap_identify.addrsec[0]);
299258223a3SMatthew Dillon 	}
300258223a3SMatthew Dillon 	ap->ap_ata.ap_capacity = capacity;
301258223a3SMatthew Dillon 	ap->ap_ata.ap_features |= ATA_PORT_F_PROBED;
302258223a3SMatthew Dillon 
303258223a3SMatthew Dillon 	capacity_bytes = capacity * 512;
304258223a3SMatthew Dillon 
305258223a3SMatthew Dillon 	/*
306258223a3SMatthew Dillon 	 * Negotiate NCQ, throw away any ata_xfer's beyond the negotiated
307258223a3SMatthew Dillon 	 * number of slots and limit the number of CAM ccb's to one less
308258223a3SMatthew Dillon 	 * so we always have a slot available for recovery.
309258223a3SMatthew Dillon 	 *
310258223a3SMatthew Dillon 	 * NCQ is not used if ap_ncqdepth is 1 or the host controller does
311258223a3SMatthew Dillon 	 * not support it, and in that case the driver can handle extra
312258223a3SMatthew Dillon 	 * ccb's.
313*cec85a37SMatthew Dillon 	 *
314*cec85a37SMatthew Dillon 	 * Remember at least one extra CCB needs to be reserved for the
315*cec85a37SMatthew Dillon 	 * error ccb.
316258223a3SMatthew Dillon 	 */
317258223a3SMatthew Dillon 	if ((ap->ap_sc->sc_cap & AHCI_REG_CAP_SNCQ) &&
318258223a3SMatthew Dillon 	    (le16toh(ap->ap_ata.ap_identify.satacap) & (1 << 8))) {
319258223a3SMatthew Dillon 		ap->ap_ata.ap_ncqdepth = (le16toh(ap->ap_ata.ap_identify.qdepth) & 0x1F) + 1;
320258223a3SMatthew Dillon 		devncqdepth = ap->ap_ata.ap_ncqdepth;
321258223a3SMatthew Dillon 		if (ap->ap_ata.ap_ncqdepth > ap->ap_sc->sc_ncmds)
322258223a3SMatthew Dillon 			ap->ap_ata.ap_ncqdepth = ap->ap_sc->sc_ncmds;
323*cec85a37SMatthew Dillon 		if (ap->ap_ata.ap_ncqdepth > 1) {
324258223a3SMatthew Dillon 			for (i = 0; i < ap->ap_sc->sc_ncmds; ++i) {
325258223a3SMatthew Dillon 				xa = ahci_ata_get_xfer(ap);
326258223a3SMatthew Dillon 				if (xa->tag < ap->ap_ata.ap_ncqdepth) {
327258223a3SMatthew Dillon 					xa->state = ATA_S_COMPLETE;
328258223a3SMatthew Dillon 					ahci_ata_put_xfer(xa);
329258223a3SMatthew Dillon 				}
330258223a3SMatthew Dillon 			}
331*cec85a37SMatthew Dillon 			if (ap->ap_ata.ap_ncqdepth >= ap->ap_sc->sc_ncmds) {
332258223a3SMatthew Dillon 				cam_devq_resize(ap->ap_sim->devq,
333258223a3SMatthew Dillon 						ap->ap_ata.ap_ncqdepth - 1);
334258223a3SMatthew Dillon 			}
335*cec85a37SMatthew Dillon 		}
336258223a3SMatthew Dillon 	} else {
337258223a3SMatthew Dillon 		devncqdepth = 0;
338258223a3SMatthew Dillon 	}
339258223a3SMatthew Dillon 
340fd8bd957SMatthew Dillon 	/*
341fd8bd957SMatthew Dillon 	 * Make the model string a bit more presentable
342fd8bd957SMatthew Dillon 	 */
343258223a3SMatthew Dillon 	for (model_len = 40; model_len; --model_len) {
344258223a3SMatthew Dillon 		if (ap->ap_ata.ap_identify.model[model_len-1] == ' ')
345258223a3SMatthew Dillon 			continue;
346258223a3SMatthew Dillon 		if (ap->ap_ata.ap_identify.model[model_len-1] == 0)
347258223a3SMatthew Dillon 			continue;
348258223a3SMatthew Dillon 		break;
349258223a3SMatthew Dillon 	}
350669fbbf7SMatthew Dillon 
351fd8bd957SMatthew Dillon 	/*
352fd8bd957SMatthew Dillon 	 * Generate informatiive strings.
353fd8bd957SMatthew Dillon 	 *
354fd8bd957SMatthew Dillon 	 * NOTE: We do not automatically set write caching, lookahead,
355fd8bd957SMatthew Dillon 	 *	 or the security state for ATAPI devices.
356fd8bd957SMatthew Dillon 	 */
357669fbbf7SMatthew Dillon 	if (ap->ap_ata.ap_identify.cmdset82 & ATA_IDENTIFY_WRITECACHE) {
358669fbbf7SMatthew Dillon 		if (ap->ap_ata.ap_identify.features85 & ATA_IDENTIFY_WRITECACHE)
359669fbbf7SMatthew Dillon 			wcstr = "enabled";
360fd8bd957SMatthew Dillon 		else if (ap->ap_ata.ap_type == ATA_PORT_T_ATAPI)
361fd8bd957SMatthew Dillon 			wcstr = "disabled";
362669fbbf7SMatthew Dillon 		else
363669fbbf7SMatthew Dillon 			wcstr = "enabling";
364669fbbf7SMatthew Dillon 	} else {
365669fbbf7SMatthew Dillon 		    wcstr = "notsupp";
366669fbbf7SMatthew Dillon 	}
367669fbbf7SMatthew Dillon 
368669fbbf7SMatthew Dillon 	if (ap->ap_ata.ap_identify.cmdset82 & ATA_IDENTIFY_LOOKAHEAD) {
369669fbbf7SMatthew Dillon 		if (ap->ap_ata.ap_identify.features85 & ATA_IDENTIFY_LOOKAHEAD)
370669fbbf7SMatthew Dillon 			rastr = "enabled";
371fd8bd957SMatthew Dillon 		else if (ap->ap_ata.ap_type == ATA_PORT_T_ATAPI)
372fd8bd957SMatthew Dillon 			rastr = "disabled";
373669fbbf7SMatthew Dillon 		else
374669fbbf7SMatthew Dillon 			rastr = "enabling";
375669fbbf7SMatthew Dillon 	} else {
376669fbbf7SMatthew Dillon 		    rastr = "notsupp";
377669fbbf7SMatthew Dillon 	}
378669fbbf7SMatthew Dillon 
379fd8bd957SMatthew Dillon 	if (ap->ap_ata.ap_identify.cmdset82 & ATA_IDENTIFY_SECURITY) {
380fd8bd957SMatthew Dillon 		if (ap->ap_ata.ap_identify.securestatus & ATA_SECURE_FROZEN)
381fd8bd957SMatthew Dillon 			scstr = "frozen";
382fd8bd957SMatthew Dillon 		else if (ap->ap_ata.ap_type == ATA_PORT_T_ATAPI)
383fd8bd957SMatthew Dillon 			scstr = "unfrozen";
384fd8bd957SMatthew Dillon 		else
385fd8bd957SMatthew Dillon 			scstr = "freezing";
386fd8bd957SMatthew Dillon 	} else {
387fd8bd957SMatthew Dillon 		    scstr = "notsupp";
388fd8bd957SMatthew Dillon 	}
389fd8bd957SMatthew Dillon 
390fd8bd957SMatthew Dillon 	kprintf("%s: Found %s \"%*.*s %8.8s\" serial=\"%20.20s\"\n"
391258223a3SMatthew Dillon 		"%s: tags=%d/%d satacaps=%04x satafeat=%04x "
392258223a3SMatthew Dillon 		"capacity=%lld.%02dMB\n"
393fd8bd957SMatthew Dillon 		"%s: f85=%04x f86=%04x f87=%04x WC=%s RA=%s SEC=%s\n",
394258223a3SMatthew Dillon 		PORTNAME(ap),
395fd8bd957SMatthew Dillon 		type,
396258223a3SMatthew Dillon 		model_len, model_len,
397258223a3SMatthew Dillon 		ap->ap_ata.ap_identify.model,
398258223a3SMatthew Dillon 		ap->ap_ata.ap_identify.firmware,
399258223a3SMatthew Dillon 		ap->ap_ata.ap_identify.serial,
400258223a3SMatthew Dillon 
401258223a3SMatthew Dillon 		PORTNAME(ap),
402258223a3SMatthew Dillon 		devncqdepth, ap->ap_sc->sc_ncmds,
403258223a3SMatthew Dillon 		ap->ap_ata.ap_identify.satacap,
404258223a3SMatthew Dillon 		ap->ap_ata.ap_identify.satafsup,
405258223a3SMatthew Dillon 		(long long)capacity_bytes / (1024 * 1024),
406258223a3SMatthew Dillon 		(int)(capacity_bytes % (1024 * 1024)) * 100 / (1024 * 1024),
407258223a3SMatthew Dillon 
408258223a3SMatthew Dillon 		PORTNAME(ap),
409258223a3SMatthew Dillon 		ap->ap_ata.ap_identify.features85,
410258223a3SMatthew Dillon 		ap->ap_ata.ap_identify.features86,
411669fbbf7SMatthew Dillon 		ap->ap_ata.ap_identify.features87,
412669fbbf7SMatthew Dillon 		wcstr,
413fd8bd957SMatthew Dillon 		rastr,
414fd8bd957SMatthew Dillon 		scstr
415258223a3SMatthew Dillon 	);
416258223a3SMatthew Dillon 
417258223a3SMatthew Dillon 	/*
418fd8bd957SMatthew Dillon 	 * Additional type-specific probing
419fd8bd957SMatthew Dillon 	 */
420fd8bd957SMatthew Dillon 	switch(ap->ap_ata.ap_type) {
421fd8bd957SMatthew Dillon 	case ATA_PORT_T_DISK:
422fd8bd957SMatthew Dillon 		error = ahci_cam_probe_disk(ap);
423fd8bd957SMatthew Dillon 		break;
424fd8bd957SMatthew Dillon 	default:
425fd8bd957SMatthew Dillon 		error = ahci_cam_probe_atapi(ap);
426fd8bd957SMatthew Dillon 		break;
427fd8bd957SMatthew Dillon 	}
428fd8bd957SMatthew Dillon 	return (0);
429fd8bd957SMatthew Dillon }
430fd8bd957SMatthew Dillon 
431fd8bd957SMatthew Dillon /*
432fd8bd957SMatthew Dillon  * DISK-specific probe after initial ident
433fd8bd957SMatthew Dillon  */
434fd8bd957SMatthew Dillon static int
435fd8bd957SMatthew Dillon ahci_cam_probe_disk(struct ahci_port *ap)
436fd8bd957SMatthew Dillon {
437fd8bd957SMatthew Dillon 	struct ata_xfer	*xa;
438fd8bd957SMatthew Dillon 	int status;
439fd8bd957SMatthew Dillon 
440fd8bd957SMatthew Dillon 	/*
441258223a3SMatthew Dillon 	 * Enable write cache if supported
442fd8bd957SMatthew Dillon 	 *
443fd8bd957SMatthew Dillon 	 * NOTE: "WD My Book" external disk devices have a very poor
444fd8bd957SMatthew Dillon 	 *	 daughter board between the the ESATA and the HD.  Sending
445fd8bd957SMatthew Dillon 	 *	 any ATA_C_SET_FEATURES commands will break the hardware port
446fd8bd957SMatthew Dillon 	 *	 with a fatal protocol error.  However, this device also
447fd8bd957SMatthew Dillon 	 *	 indicates that WRITECACHE is already on and READAHEAD is
448fd8bd957SMatthew Dillon 	 *	 not supported so we avoid the issue.
449258223a3SMatthew Dillon 	 */
450669fbbf7SMatthew Dillon 	if ((ap->ap_ata.ap_identify.cmdset82 & ATA_IDENTIFY_WRITECACHE) &&
451669fbbf7SMatthew Dillon 	    (ap->ap_ata.ap_identify.features85 & ATA_IDENTIFY_WRITECACHE) == 0) {
452258223a3SMatthew Dillon 		xa = ahci_ata_get_xfer(ap);
453258223a3SMatthew Dillon 		xa->complete = ahci_ata_dummy_done;
454258223a3SMatthew Dillon 		xa->fis->command = ATA_C_SET_FEATURES;
455669fbbf7SMatthew Dillon 		/*xa->fis->features = ATA_SF_WRITECACHE_EN;*/
456669fbbf7SMatthew Dillon 		xa->fis->features = ATA_SF_LOOKAHEAD_EN;
457258223a3SMatthew Dillon 		xa->fis->flags = ATA_H2D_FLAGS_CMD;
458669fbbf7SMatthew Dillon 		xa->fis->device = 0;
459258223a3SMatthew Dillon 		xa->flags = ATA_F_READ | ATA_F_PIO | ATA_F_POLL;
460258223a3SMatthew Dillon 		xa->timeout = hz;
461669fbbf7SMatthew Dillon 		xa->datalen = 0;
462258223a3SMatthew Dillon 		status = ahci_ata_cmd(xa);
463258223a3SMatthew Dillon 		if (status == ATA_COMPLETE)
464258223a3SMatthew Dillon 			ap->ap_ata.ap_features |= ATA_PORT_F_WCACHE;
465258223a3SMatthew Dillon 		ahci_ata_put_xfer(xa);
466258223a3SMatthew Dillon 	}
467258223a3SMatthew Dillon 
468258223a3SMatthew Dillon 	/*
469258223a3SMatthew Dillon 	 * Enable readahead if supported
470258223a3SMatthew Dillon 	 */
471669fbbf7SMatthew Dillon 	if ((ap->ap_ata.ap_identify.cmdset82 & ATA_IDENTIFY_LOOKAHEAD) &&
472669fbbf7SMatthew Dillon 	    (ap->ap_ata.ap_identify.features85 & ATA_IDENTIFY_LOOKAHEAD) == 0) {
473258223a3SMatthew Dillon 		xa = ahci_ata_get_xfer(ap);
474258223a3SMatthew Dillon 		xa->complete = ahci_ata_dummy_done;
475258223a3SMatthew Dillon 		xa->fis->command = ATA_C_SET_FEATURES;
476258223a3SMatthew Dillon 		xa->fis->features = ATA_SF_LOOKAHEAD_EN;
477258223a3SMatthew Dillon 		xa->fis->flags = ATA_H2D_FLAGS_CMD;
478669fbbf7SMatthew Dillon 		xa->fis->device = 0;
479258223a3SMatthew Dillon 		xa->flags = ATA_F_READ | ATA_F_PIO | ATA_F_POLL;
480258223a3SMatthew Dillon 		xa->timeout = hz;
481669fbbf7SMatthew Dillon 		xa->datalen = 0;
482258223a3SMatthew Dillon 		status = ahci_ata_cmd(xa);
483258223a3SMatthew Dillon 		if (status == ATA_COMPLETE)
484258223a3SMatthew Dillon 			ap->ap_ata.ap_features |= ATA_PORT_F_RAHEAD;
485258223a3SMatthew Dillon 		ahci_ata_put_xfer(xa);
486258223a3SMatthew Dillon 	}
487258223a3SMatthew Dillon 
488258223a3SMatthew Dillon 	/*
489258223a3SMatthew Dillon 	 * FREEZE LOCK the device so malicious users can't lock it on us.
490258223a3SMatthew Dillon 	 * As there is no harm in issuing this to devices that don't
491258223a3SMatthew Dillon 	 * support the security feature set we just send it, and don't bother
492258223a3SMatthew Dillon 	 * checking if the device sends a command abort to tell us it doesn't
493258223a3SMatthew Dillon 	 * support it
494258223a3SMatthew Dillon 	 */
495fd8bd957SMatthew Dillon 	if ((ap->ap_ata.ap_identify.cmdset82 & ATA_IDENTIFY_SECURITY) &&
496fd8bd957SMatthew Dillon 	    (ap->ap_ata.ap_identify.securestatus & ATA_SECURE_FROZEN) == 0) {
497258223a3SMatthew Dillon 		xa = ahci_ata_get_xfer(ap);
498258223a3SMatthew Dillon 		xa->complete = ahci_ata_dummy_done;
499258223a3SMatthew Dillon 		xa->fis->command = ATA_C_SEC_FREEZE_LOCK;
500258223a3SMatthew Dillon 		xa->fis->flags = ATA_H2D_FLAGS_CMD;
501258223a3SMatthew Dillon 		xa->flags = ATA_F_READ | ATA_F_PIO | ATA_F_POLL;
502258223a3SMatthew Dillon 		xa->timeout = hz;
503669fbbf7SMatthew Dillon 		xa->datalen = 0;
504258223a3SMatthew Dillon 		status = ahci_ata_cmd(xa);
505258223a3SMatthew Dillon 		if (status == ATA_COMPLETE)
506258223a3SMatthew Dillon 			ap->ap_ata.ap_features |= ATA_PORT_F_FRZLCK;
507258223a3SMatthew Dillon 		ahci_ata_put_xfer(xa);
508669fbbf7SMatthew Dillon 	}
509258223a3SMatthew Dillon 
510b4189e5eSMatthew Dillon 	return (0);
511b4189e5eSMatthew Dillon }
512b4189e5eSMatthew Dillon 
513fd8bd957SMatthew Dillon /*
514fd8bd957SMatthew Dillon  * ATAPI-specific probe after initial ident
515fd8bd957SMatthew Dillon  */
516b4189e5eSMatthew Dillon static int
517b4189e5eSMatthew Dillon ahci_cam_probe_atapi(struct ahci_port *ap)
518b4189e5eSMatthew Dillon {
519fd8bd957SMatthew Dillon 	return(0);
520fd8bd957SMatthew Dillon }
521fd8bd957SMatthew Dillon 
522fd8bd957SMatthew Dillon #if 0
523fd8bd957SMatthew Dillon 	/*
524fd8bd957SMatthew Dillon 	 * Keep this old code around for a little bit, it is another way
525fd8bd957SMatthew Dillon 	 * to probe an ATAPI device by using a ATAPI (SCSI) INQUIRY
526fd8bd957SMatthew Dillon 	 */
527b4189e5eSMatthew Dillon 	struct ata_xfer	*xa;
528b4189e5eSMatthew Dillon 	int		status;
529b4189e5eSMatthew Dillon 	int		devncqdepth;
530b4189e5eSMatthew Dillon 	struct scsi_inquiry_data *inq_data;
531b4189e5eSMatthew Dillon 	struct scsi_inquiry *inq_cmd;
532b4189e5eSMatthew Dillon 
533b4189e5eSMatthew Dillon 	inq_data = kmalloc(sizeof(*inq_data), M_TEMP, M_WAITOK | M_ZERO);
534b4189e5eSMatthew Dillon 
535258223a3SMatthew Dillon 	/*
536b4189e5eSMatthew Dillon 	 * Issue identify, saving the result
537258223a3SMatthew Dillon 	 */
538b4189e5eSMatthew Dillon 	xa = ahci_ata_get_xfer(ap);
539b4189e5eSMatthew Dillon 	xa->complete = ahci_ata_dummy_done;
540b4189e5eSMatthew Dillon 	xa->data = inq_data;
541b4189e5eSMatthew Dillon 	xa->datalen = sizeof(*inq_data);
542b4189e5eSMatthew Dillon 	xa->flags = ATA_F_READ | ATA_F_PACKET | ATA_F_PIO | ATA_F_POLL;
543b4189e5eSMatthew Dillon 	xa->timeout = hz;
544b4189e5eSMatthew Dillon 
545b4189e5eSMatthew Dillon 	xa->fis->flags = ATA_H2D_FLAGS_CMD;
546b4189e5eSMatthew Dillon 	xa->fis->command = ATA_C_PACKET;
547b4189e5eSMatthew Dillon 	xa->fis->device = 0;
548b4189e5eSMatthew Dillon 	xa->fis->sector_count = xa->tag << 3;
549b4189e5eSMatthew Dillon 	xa->fis->features = ATA_H2D_FEATURES_DMA |
550b4189e5eSMatthew Dillon 		    ((xa->flags & ATA_F_WRITE) ?
551b4189e5eSMatthew Dillon 		    ATA_H2D_FEATURES_DIR_WRITE : ATA_H2D_FEATURES_DIR_READ);
552b4189e5eSMatthew Dillon 	xa->fis->lba_mid = 0x00;
553b4189e5eSMatthew Dillon 	xa->fis->lba_high = 0x20;
554b4189e5eSMatthew Dillon 
555b4189e5eSMatthew Dillon 	inq_cmd = (void *)xa->packetcmd;
556b4189e5eSMatthew Dillon 	inq_cmd->opcode = INQUIRY;
557b4189e5eSMatthew Dillon 	inq_cmd->length = SHORT_INQUIRY_LENGTH;
558b4189e5eSMatthew Dillon 
559b4189e5eSMatthew Dillon 	status = ahci_ata_cmd(xa);
560b4189e5eSMatthew Dillon 	if (status != ATA_COMPLETE) {
561b4189e5eSMatthew Dillon 		kprintf("%s: Detected ATAPI device but unable to INQUIRY\n",
562b4189e5eSMatthew Dillon 			PORTNAME(ap));
563b4189e5eSMatthew Dillon 		ahci_ata_put_xfer(xa);
564b4189e5eSMatthew Dillon 		kfree(inq_data, M_TEMP);
565b4189e5eSMatthew Dillon 		return(EIO);
566b4189e5eSMatthew Dillon 	}
567b4189e5eSMatthew Dillon 	if (xa->state != ATA_S_COMPLETE) {
568b4189e5eSMatthew Dillon 		kprintf("%s: Detected ATAPI device but unable to INQUIRY "
569b4189e5eSMatthew Dillon 			" xa->state=%d\n",
570b4189e5eSMatthew Dillon 			PORTNAME(ap), xa->state);
571b4189e5eSMatthew Dillon 		ahci_ata_put_xfer(xa);
572b4189e5eSMatthew Dillon 		kfree(inq_data, M_TEMP);
573b4189e5eSMatthew Dillon 		return(EIO);
574b4189e5eSMatthew Dillon 	}
575b4189e5eSMatthew Dillon 	ahci_ata_put_xfer(xa);
576b4189e5eSMatthew Dillon 
577b4189e5eSMatthew Dillon 	ap->ap_ata.ap_features |= ATA_PORT_F_PROBED;
578b4189e5eSMatthew Dillon 
579b4189e5eSMatthew Dillon 	/*
580b4189e5eSMatthew Dillon 	 * XXX Negotiate NCQ with ATAPI?  How do we do this?
581b4189e5eSMatthew Dillon 	 */
582b4189e5eSMatthew Dillon 
583b4189e5eSMatthew Dillon 	devncqdepth = 0;
584b4189e5eSMatthew Dillon 
585b4189e5eSMatthew Dillon 	kprintf("%s: Found ATAPI %s \"%8.8s %16.16s\" rev=\"%4.4s\"\n"
586b4189e5eSMatthew Dillon 		"%s: tags=%d/%d\n",
587b4189e5eSMatthew Dillon 		PORTNAME(ap),
588b4189e5eSMatthew Dillon 		ScsiTypeArray[SID_TYPE(inq_data)],
589b4189e5eSMatthew Dillon 		inq_data->vendor,
590b4189e5eSMatthew Dillon 		inq_data->product,
591b4189e5eSMatthew Dillon 		inq_data->revision,
592b4189e5eSMatthew Dillon 
593b4189e5eSMatthew Dillon 		PORTNAME(ap),
594b4189e5eSMatthew Dillon 		devncqdepth, ap->ap_sc->sc_ncmds
595b4189e5eSMatthew Dillon 	);
596b4189e5eSMatthew Dillon 	kfree(inq_data, M_TEMP);
597b4189e5eSMatthew Dillon #endif
598258223a3SMatthew 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 /*
631fd8bd957SMatthew Dillon  * Initiate a bus scan.
632fd8bd957SMatthew Dillon  *
633fd8bd957SMatthew Dillon  * An asynchronous bus scan is used to avoid reentrancy issues
634258223a3SMatthew Dillon  */
635258223a3SMatthew Dillon static void
636258223a3SMatthew Dillon ahci_cam_rescan_callback(struct cam_periph *periph, union ccb *ccb)
637258223a3SMatthew Dillon {
638258223a3SMatthew Dillon 	kfree(ccb, M_TEMP);
639258223a3SMatthew Dillon }
640258223a3SMatthew Dillon 
641258223a3SMatthew Dillon static void
642258223a3SMatthew Dillon ahci_cam_rescan(struct ahci_port *ap)
643258223a3SMatthew Dillon {
644258223a3SMatthew Dillon 	struct cam_path *path;
645258223a3SMatthew Dillon 	union ccb *ccb;
646258223a3SMatthew Dillon 	int status;
647258223a3SMatthew Dillon 
648258223a3SMatthew Dillon 	ccb = kmalloc(sizeof(*ccb), M_TEMP, M_WAITOK | M_ZERO);
649258223a3SMatthew Dillon 	status = xpt_create_path(&path, xpt_periph, cam_sim_path(ap->ap_sim),
650258223a3SMatthew Dillon 				 CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD);
651258223a3SMatthew Dillon 	if (status != CAM_REQ_CMP)
652258223a3SMatthew Dillon 		return;
653258223a3SMatthew Dillon 
654258223a3SMatthew Dillon 	xpt_setup_ccb(&ccb->ccb_h, path, 5);	/* 5 = low priority */
655fd8bd957SMatthew Dillon 	ccb->ccb_h.func_code = XPT_SCAN_BUS | XPT_FC_QUEUED;
656258223a3SMatthew Dillon 	ccb->ccb_h.cbfcnp = ahci_cam_rescan_callback;
657258223a3SMatthew Dillon 	ccb->crcn.flags = CAM_FLAG_NONE;
658258223a3SMatthew Dillon 	xpt_action(ccb);
659258223a3SMatthew Dillon 
660258223a3SMatthew Dillon 	/* scan is now underway */
661258223a3SMatthew Dillon }
662258223a3SMatthew Dillon 
663258223a3SMatthew Dillon /*
664258223a3SMatthew Dillon  * Action function - dispatch command
665258223a3SMatthew Dillon  */
666258223a3SMatthew Dillon static
667258223a3SMatthew Dillon void
668258223a3SMatthew Dillon ahci_xpt_action(struct cam_sim *sim, union ccb *ccb)
669258223a3SMatthew Dillon {
670258223a3SMatthew Dillon 	struct ahci_port *ap;
671258223a3SMatthew Dillon 	struct ccb_hdr *ccbh;
672258223a3SMatthew Dillon 	int unit;
673258223a3SMatthew Dillon 
674258223a3SMatthew Dillon 	/* XXX lock */
675258223a3SMatthew Dillon 	ap = cam_sim_softc(sim);
676258223a3SMatthew Dillon 	KKASSERT(ap != NULL);
677258223a3SMatthew Dillon 	ccbh = &ccb->ccb_h;
678258223a3SMatthew Dillon 	unit = cam_sim_unit(sim);
679258223a3SMatthew Dillon 
680258223a3SMatthew Dillon 	/*
681258223a3SMatthew Dillon 	 * Non-zero target and lun ids will be used for future
682258223a3SMatthew Dillon 	 * port multiplication(?).  A target wildcard indicates only
683258223a3SMatthew Dillon 	 * the general bus is being probed.
684258223a3SMatthew Dillon 	 *
685258223a3SMatthew Dillon 	 * XXX What do we do with a LUN wildcard?
686258223a3SMatthew Dillon 	 */
687258223a3SMatthew Dillon 	if (ccbh->target_id != CAM_TARGET_WILDCARD) {
688258223a3SMatthew Dillon 		if (ap->ap_ata.ap_type == ATA_PORT_T_NONE) {
689258223a3SMatthew Dillon 			ccbh->status = CAM_REQ_INVALID;
690258223a3SMatthew Dillon 			xpt_done(ccb);
691258223a3SMatthew Dillon 			return;
692258223a3SMatthew Dillon 		}
693258223a3SMatthew Dillon 		if (ccbh->target_id) {
694258223a3SMatthew Dillon 			ccbh->status = CAM_DEV_NOT_THERE;
695258223a3SMatthew Dillon 			xpt_done(ccb);
696258223a3SMatthew Dillon 			return;
697258223a3SMatthew Dillon 		}
698258223a3SMatthew Dillon 		if (ccbh->target_lun != CAM_LUN_WILDCARD && ccbh->target_lun) {
699258223a3SMatthew Dillon 			ccbh->status = CAM_DEV_NOT_THERE;
700258223a3SMatthew Dillon 			xpt_done(ccb);
701258223a3SMatthew Dillon 			return;
702258223a3SMatthew Dillon 		}
703258223a3SMatthew Dillon 	}
704258223a3SMatthew Dillon 
705258223a3SMatthew Dillon 	/*
706258223a3SMatthew Dillon 	 * Switch on the meta XPT command
707258223a3SMatthew Dillon 	 */
708258223a3SMatthew Dillon 	switch(ccbh->func_code) {
709258223a3SMatthew Dillon 	case XPT_PATH_INQ:
710258223a3SMatthew Dillon 		ccb->cpi.version_num = 1;
711258223a3SMatthew Dillon 		ccb->cpi.hba_inquiry = 0;
712258223a3SMatthew Dillon 		ccb->cpi.target_sprt = 0;
713258223a3SMatthew Dillon 		ccb->cpi.hba_misc = 0;
714258223a3SMatthew Dillon 		ccb->cpi.hba_eng_cnt = 0;
715258223a3SMatthew Dillon 		bzero(ccb->cpi.vuhba_flags, sizeof(ccb->cpi.vuhba_flags));
716258223a3SMatthew Dillon 		ccb->cpi.max_target = 7;
717258223a3SMatthew Dillon 		ccb->cpi.max_lun = 0;
718258223a3SMatthew Dillon 		ccb->cpi.async_flags = 0;
719258223a3SMatthew Dillon 		ccb->cpi.hpath_id = 0;
720258223a3SMatthew Dillon 		ccb->cpi.initiator_id = 7;
721258223a3SMatthew Dillon 		ccb->cpi.unit_number = cam_sim_unit(sim);
722258223a3SMatthew Dillon 		ccb->cpi.bus_id = cam_sim_bus(sim);
723258223a3SMatthew Dillon 		ccb->cpi.base_transfer_speed = 150000;
724258223a3SMatthew Dillon 		ccb->cpi.transport = XPORT_AHCI;
725258223a3SMatthew Dillon 		ccb->cpi.transport_version = 1;
726258223a3SMatthew Dillon 		ccb->cpi.protocol = PROTO_SCSI;
727258223a3SMatthew Dillon 		ccb->cpi.protocol_version = SCSI_REV_2;
728258223a3SMatthew Dillon 
729258223a3SMatthew Dillon 		/*
730258223a3SMatthew Dillon 		 * Non-zero target and lun ids will be used for future
731258223a3SMatthew Dillon 		 * port multiplication(?).  A target wildcard indicates only
732258223a3SMatthew Dillon 		 * the general bus is being probed.
733258223a3SMatthew Dillon 		 *
734258223a3SMatthew Dillon 		 * XXX What do we do with a LUN wildcard?
735258223a3SMatthew Dillon 		 */
736258223a3SMatthew Dillon 		if (ccbh->target_id != CAM_TARGET_WILDCARD) {
737258223a3SMatthew Dillon 			switch(ahci_pread(ap, AHCI_PREG_SSTS) &
738258223a3SMatthew Dillon 			       AHCI_PREG_SSTS_SPD) {
739258223a3SMatthew Dillon 			case AHCI_PREG_SSTS_SPD_GEN1:
740258223a3SMatthew Dillon 				ccb->cpi.base_transfer_speed = 150000;
741258223a3SMatthew Dillon 				break;
742258223a3SMatthew Dillon 			case AHCI_PREG_SSTS_SPD_GEN2:
743258223a3SMatthew Dillon 				ccb->cpi.base_transfer_speed = 300000;
744258223a3SMatthew Dillon 				break;
745258223a3SMatthew Dillon 			default:
746258223a3SMatthew Dillon 				/* unknown */
747258223a3SMatthew Dillon 				ccb->cpi.base_transfer_speed = 1000;
748258223a3SMatthew Dillon 				break;
749258223a3SMatthew Dillon 			}
750258223a3SMatthew Dillon 			/* XXX check attached, set base xfer speed */
751258223a3SMatthew Dillon 		}
752258223a3SMatthew Dillon 		ccbh->status = CAM_REQ_CMP;
753258223a3SMatthew Dillon 		xpt_done(ccb);
754258223a3SMatthew Dillon 		break;
755258223a3SMatthew Dillon 	case XPT_RESET_DEV:
756fd8bd957SMatthew Dillon 		lwkt_serialize_enter(&ap->ap_sc->sc_serializer);
757fd8bd957SMatthew Dillon 		ahci_port_softreset(ap);
758fd8bd957SMatthew Dillon 		lwkt_serialize_exit(&ap->ap_sc->sc_serializer);
759fd8bd957SMatthew Dillon 
760fd8bd957SMatthew Dillon 		ccbh->status = CAM_REQ_CMP;
761258223a3SMatthew Dillon 		xpt_done(ccb);
762258223a3SMatthew Dillon 		break;
763258223a3SMatthew Dillon 	case XPT_RESET_BUS:
764fd8bd957SMatthew Dillon 		lwkt_serialize_enter(&ap->ap_sc->sc_serializer);
765fd8bd957SMatthew Dillon 		ahci_port_portreset(ap);
766fd8bd957SMatthew Dillon 		ahci_port_softreset(ap);
767fd8bd957SMatthew Dillon 		lwkt_serialize_exit(&ap->ap_sc->sc_serializer);
768fd8bd957SMatthew Dillon 
769fd8bd957SMatthew Dillon 		xpt_async(AC_BUS_RESET, ap->ap_path, NULL);
770fd8bd957SMatthew Dillon 
771fd8bd957SMatthew Dillon 		ccbh->status = CAM_REQ_CMP;
772258223a3SMatthew Dillon 		xpt_done(ccb);
773258223a3SMatthew Dillon 		break;
774258223a3SMatthew Dillon 	case XPT_SET_TRAN_SETTINGS:
775258223a3SMatthew Dillon 		ccbh->status = CAM_FUNC_NOTAVAIL;
776258223a3SMatthew Dillon 		xpt_done(ccb);
777258223a3SMatthew Dillon 		break;
778258223a3SMatthew Dillon 	case XPT_GET_TRAN_SETTINGS:
779258223a3SMatthew Dillon 		ccb->cts.protocol = PROTO_SCSI;
780258223a3SMatthew Dillon 		ccb->cts.protocol_version = SCSI_REV_2;
781258223a3SMatthew Dillon 		ccb->cts.transport = XPORT_AHCI;
782258223a3SMatthew Dillon 		ccb->cts.transport_version = XPORT_VERSION_UNSPECIFIED;
783258223a3SMatthew Dillon 		ccb->cts.proto_specific.valid = 0;
784258223a3SMatthew Dillon 		ccb->cts.xport_specific.valid = 0;
785258223a3SMatthew Dillon 		ccbh->status = CAM_REQ_CMP;
786258223a3SMatthew Dillon 		xpt_done(ccb);
787258223a3SMatthew Dillon 		break;
788258223a3SMatthew Dillon 	case XPT_CALC_GEOMETRY:
789258223a3SMatthew Dillon 		cam_calc_geometry(&ccb->ccg, 1);
790258223a3SMatthew Dillon 		xpt_done(ccb);
791258223a3SMatthew Dillon 		break;
792258223a3SMatthew Dillon 	case XPT_SCSI_IO:
793258223a3SMatthew Dillon 		switch(ap->ap_ata.ap_type) {
794258223a3SMatthew Dillon 		case ATA_PORT_T_DISK:
795258223a3SMatthew Dillon 			ahci_xpt_scsi_disk_io(sim, ccb);
796258223a3SMatthew Dillon 			break;
797258223a3SMatthew Dillon 		case ATA_PORT_T_ATAPI:
798258223a3SMatthew Dillon 			ahci_xpt_scsi_atapi_io(sim, ccb);
799258223a3SMatthew Dillon 			break;
800258223a3SMatthew Dillon 		default:
801258223a3SMatthew Dillon 			ccbh->status = CAM_REQ_INVALID;
802258223a3SMatthew Dillon 			xpt_done(ccb);
803258223a3SMatthew Dillon 			break;
804258223a3SMatthew Dillon 		}
805258223a3SMatthew Dillon 		break;
806258223a3SMatthew Dillon 	default:
807258223a3SMatthew Dillon 		kprintf("xpt_unknown\n");
808258223a3SMatthew Dillon 		ccbh->status = CAM_REQ_INVALID;
809258223a3SMatthew Dillon 		xpt_done(ccb);
810258223a3SMatthew Dillon 		break;
811258223a3SMatthew Dillon 	}
812258223a3SMatthew Dillon }
813258223a3SMatthew Dillon 
814258223a3SMatthew Dillon /*
815258223a3SMatthew Dillon  * Poll function (unused?)
816258223a3SMatthew Dillon  */
817258223a3SMatthew Dillon static
818258223a3SMatthew Dillon void
819258223a3SMatthew Dillon ahci_xpt_poll(struct cam_sim *sim)
820258223a3SMatthew Dillon {
821258223a3SMatthew Dillon 	/*struct ahci_port *ap = cam_sim_softc(sim);*/
822258223a3SMatthew Dillon 
823258223a3SMatthew Dillon 	kprintf("ahci_xpt_poll\n");
824258223a3SMatthew Dillon 	/* XXX lock */
825258223a3SMatthew Dillon }
826258223a3SMatthew Dillon 
827258223a3SMatthew Dillon /*
828b4189e5eSMatthew Dillon  * Convert the SCSI command in ccb to an ata_xfer command in xa
829b4189e5eSMatthew Dillon  * for ATA_PORT_T_DISK operations.  Set the completion function
830b4189e5eSMatthew Dillon  * to convert the response back, then dispatch to the OpenBSD AHCI
831b4189e5eSMatthew Dillon  * layer.
832258223a3SMatthew Dillon  *
833b4189e5eSMatthew Dillon  * AHCI DISK commands only support a limited command set, and we
834b4189e5eSMatthew Dillon  * fake additional commands to make it play nice with the CAM subsystem.
835258223a3SMatthew Dillon  */
836258223a3SMatthew Dillon static
837258223a3SMatthew Dillon void
838258223a3SMatthew Dillon ahci_xpt_scsi_disk_io(struct cam_sim *sim, union ccb *ccb)
839258223a3SMatthew Dillon {
840258223a3SMatthew Dillon 	struct ahci_port *ap;
841258223a3SMatthew Dillon 	struct ccb_hdr *ccbh;
842258223a3SMatthew Dillon 	struct ccb_scsiio *csio;
843258223a3SMatthew Dillon 	struct ata_xfer *xa;
844258223a3SMatthew Dillon 	struct ata_fis_h2d *fis;
845258223a3SMatthew Dillon 	scsi_cdb_t cdb;
846258223a3SMatthew Dillon 	union scsi_data *rdata;
847258223a3SMatthew Dillon 	int rdata_len;
848258223a3SMatthew Dillon 	u_int64_t capacity;
849258223a3SMatthew Dillon 	u_int64_t lba;
850258223a3SMatthew Dillon 	u_int32_t count;
851258223a3SMatthew Dillon 
852258223a3SMatthew Dillon 	ap = cam_sim_softc(sim);
853258223a3SMatthew Dillon 	ccbh = &ccb->csio.ccb_h;
854258223a3SMatthew Dillon 	csio = &ccb->csio;
855258223a3SMatthew Dillon 	xa = ahci_ata_get_xfer(ap);
856258223a3SMatthew Dillon 	rdata = (void *)csio->data_ptr;
857258223a3SMatthew Dillon 	rdata_len = csio->dxfer_len;
858258223a3SMatthew Dillon 
859258223a3SMatthew Dillon 	/*
860258223a3SMatthew Dillon 	 * Build the FIS or process the csio to completion.
861258223a3SMatthew Dillon 	 */
862258223a3SMatthew Dillon 	cdb = (void *)((ccbh->flags & CAM_CDB_POINTER) ?
863258223a3SMatthew Dillon 			csio->cdb_io.cdb_ptr : csio->cdb_io.cdb_bytes);
864258223a3SMatthew Dillon 
865258223a3SMatthew Dillon 	switch(cdb->generic.opcode) {
866258223a3SMatthew Dillon 	case REQUEST_SENSE:
867258223a3SMatthew Dillon 		/*
868258223a3SMatthew Dillon 		 * Auto-sense everything, so explicit sense requests
869258223a3SMatthew Dillon 		 * return no-sense.
870258223a3SMatthew Dillon 		 */
871258223a3SMatthew Dillon 		ccbh->status = CAM_SCSI_STATUS_ERROR;
872258223a3SMatthew Dillon 		break;
873258223a3SMatthew Dillon 	case INQUIRY:
874258223a3SMatthew Dillon 		/*
875258223a3SMatthew Dillon 		 * Inquiry supported features
876258223a3SMatthew Dillon 		 *
877258223a3SMatthew Dillon 		 * [opcode, byte2, page_code, length, control]
878258223a3SMatthew Dillon 		 */
879258223a3SMatthew Dillon 		if (cdb->inquiry.byte2 & SI_EVPD) {
880258223a3SMatthew Dillon 			switch(cdb->inquiry.page_code) {
881258223a3SMatthew Dillon 			case SVPD_SUPPORTED_PAGE_LIST:
882258223a3SMatthew Dillon 				/* XXX atascsi_disk_vpd_supported */
883258223a3SMatthew Dillon 			case SVPD_UNIT_SERIAL_NUMBER:
884258223a3SMatthew Dillon 				/* XXX atascsi_disk_vpd_serial */
885258223a3SMatthew Dillon 			case SVPD_UNIT_DEVID:
886258223a3SMatthew Dillon 				/* XXX atascsi_disk_vpd_ident */
887258223a3SMatthew Dillon 			default:
888258223a3SMatthew Dillon 				ccbh->status = CAM_FUNC_NOTAVAIL;
889258223a3SMatthew Dillon 				break;
890258223a3SMatthew Dillon 			}
891258223a3SMatthew Dillon 		} else {
892258223a3SMatthew Dillon 			bzero(rdata, rdata_len);
893258223a3SMatthew Dillon 			if (rdata_len < SHORT_INQUIRY_LENGTH) {
894258223a3SMatthew Dillon 				ccbh->status = CAM_CCB_LEN_ERR;
895258223a3SMatthew Dillon 				break;
896258223a3SMatthew Dillon 			}
897258223a3SMatthew Dillon 			if (rdata_len > sizeof(rdata->inquiry_data))
898258223a3SMatthew Dillon 				rdata_len = sizeof(rdata->inquiry_data);
899258223a3SMatthew Dillon 			rdata->inquiry_data.device = T_DIRECT;
900258223a3SMatthew Dillon 			rdata->inquiry_data.version = SCSI_REV_SPC2;
901258223a3SMatthew Dillon 			rdata->inquiry_data.response_format = 2;
902258223a3SMatthew Dillon 			rdata->inquiry_data.additional_length = 32;
903258223a3SMatthew Dillon 			bcopy("SATA    ", rdata->inquiry_data.vendor, 8);
904258223a3SMatthew Dillon 			bcopy(ap->ap_ata.ap_identify.model,
905258223a3SMatthew Dillon 			      rdata->inquiry_data.product,
906258223a3SMatthew Dillon 			      sizeof(rdata->inquiry_data.product));
907258223a3SMatthew Dillon 			bcopy(ap->ap_ata.ap_identify.firmware,
908258223a3SMatthew Dillon 			      rdata->inquiry_data.revision,
909258223a3SMatthew Dillon 			      sizeof(rdata->inquiry_data.revision));
910258223a3SMatthew Dillon 			ccbh->status = CAM_REQ_CMP;
911258223a3SMatthew Dillon 		}
912258223a3SMatthew Dillon 		break;
913258223a3SMatthew Dillon 	case READ_CAPACITY_16:
914258223a3SMatthew Dillon 		if (cdb->read_capacity_16.service_action != SRC16_SERVICE_ACTION) {
915258223a3SMatthew Dillon 			ccbh->status = CAM_REQ_INVALID;
916258223a3SMatthew Dillon 			break;
917258223a3SMatthew Dillon 		}
918258223a3SMatthew Dillon 		if (rdata_len < sizeof(rdata->read_capacity_data_16)) {
919258223a3SMatthew Dillon 			ccbh->status = CAM_CCB_LEN_ERR;
920258223a3SMatthew Dillon 			break;
921258223a3SMatthew Dillon 		}
922258223a3SMatthew Dillon 		/* fall through */
923258223a3SMatthew Dillon 	case READ_CAPACITY:
924258223a3SMatthew Dillon 		if (rdata_len < sizeof(rdata->read_capacity_data)) {
925258223a3SMatthew Dillon 			ccbh->status = CAM_CCB_LEN_ERR;
926258223a3SMatthew Dillon 			break;
927258223a3SMatthew Dillon 		}
928258223a3SMatthew Dillon 
929258223a3SMatthew Dillon 		capacity = ap->ap_ata.ap_capacity;
930258223a3SMatthew Dillon 
931258223a3SMatthew Dillon 		bzero(rdata, rdata_len);
932258223a3SMatthew Dillon 		if (cdb->generic.opcode == READ_CAPACITY) {
933258223a3SMatthew Dillon 			rdata_len = sizeof(rdata->read_capacity_data);
934258223a3SMatthew Dillon 			if (capacity > 0xFFFFFFFFU)
935258223a3SMatthew Dillon 				capacity = 0xFFFFFFFFU;
936258223a3SMatthew Dillon 			bzero(&rdata->read_capacity_data, rdata_len);
937258223a3SMatthew Dillon 			scsi_ulto4b((u_int32_t)capacity - 1,
938258223a3SMatthew Dillon 				    rdata->read_capacity_data.addr);
939258223a3SMatthew Dillon 			scsi_ulto4b(512, rdata->read_capacity_data.length);
940258223a3SMatthew Dillon 		} else {
941258223a3SMatthew Dillon 			rdata_len = sizeof(rdata->read_capacity_data_16);
942258223a3SMatthew Dillon 			bzero(&rdata->read_capacity_data_16, rdata_len);
943258223a3SMatthew Dillon 			scsi_u64to8b(capacity - 1,
944258223a3SMatthew Dillon 				     rdata->read_capacity_data_16.addr);
945258223a3SMatthew Dillon 			scsi_ulto4b(512, rdata->read_capacity_data_16.length);
946258223a3SMatthew Dillon 		}
947258223a3SMatthew Dillon 		ccbh->status = CAM_REQ_CMP;
948258223a3SMatthew Dillon 		break;
949258223a3SMatthew Dillon 	case SYNCHRONIZE_CACHE:
950258223a3SMatthew Dillon 		/*
951258223a3SMatthew Dillon 		 * Synchronize cache.  Specification says this can take
952258223a3SMatthew Dillon 		 * greater then 30 seconds so give it at least 45.
953258223a3SMatthew Dillon 		 */
954258223a3SMatthew Dillon 		fis = xa->fis;
955258223a3SMatthew Dillon 		xa->datalen = 0;
956258223a3SMatthew Dillon 		xa->flags = ATA_F_READ;
957258223a3SMatthew Dillon 		xa->complete = ahci_ata_complete_disk_synchronize_cache;
958258223a3SMatthew Dillon 		if (xa->timeout < 45 * hz)
959258223a3SMatthew Dillon 			xa->timeout = 45 * hz;
960258223a3SMatthew Dillon 		fis->flags = ATA_H2D_FLAGS_CMD;
961258223a3SMatthew Dillon 		fis->command = ATA_C_FLUSH_CACHE;
962258223a3SMatthew Dillon 		fis->device = 0;
963258223a3SMatthew Dillon 		break;
964258223a3SMatthew Dillon 	case TEST_UNIT_READY:
965258223a3SMatthew Dillon 	case START_STOP_UNIT:
966258223a3SMatthew Dillon 	case PREVENT_ALLOW:
967258223a3SMatthew Dillon 		/*
968258223a3SMatthew Dillon 		 * Just silently return success
969258223a3SMatthew Dillon 		 */
970258223a3SMatthew Dillon 		ccbh->status = CAM_REQ_CMP;
971258223a3SMatthew Dillon 		rdata_len = 0;
972258223a3SMatthew Dillon 		break;
973258223a3SMatthew Dillon 	case ATA_PASS_12:
974258223a3SMatthew Dillon 	case ATA_PASS_16:
975258223a3SMatthew Dillon 		/*
976258223a3SMatthew Dillon 		 * XXX implement pass-through
977258223a3SMatthew Dillon 		 */
978258223a3SMatthew Dillon 		ccbh->status = CAM_FUNC_NOTAVAIL;
979258223a3SMatthew Dillon 		break;
980258223a3SMatthew Dillon 	default:
981258223a3SMatthew Dillon 		switch(cdb->generic.opcode) {
982258223a3SMatthew Dillon 		case READ_6:
983258223a3SMatthew Dillon 			lba = scsi_3btoul(cdb->rw_6.addr) & 0x1FFFFF;
984258223a3SMatthew Dillon 			count = cdb->rw_6.length ? cdb->rw_6.length : 0x100;
985258223a3SMatthew Dillon 			xa->flags = ATA_F_READ;
986258223a3SMatthew Dillon 			break;
987258223a3SMatthew Dillon 		case READ_10:
988258223a3SMatthew Dillon 			lba = scsi_4btoul(cdb->rw_10.addr);
989258223a3SMatthew Dillon 			count = scsi_2btoul(cdb->rw_10.length);
990258223a3SMatthew Dillon 			xa->flags = ATA_F_READ;
991258223a3SMatthew Dillon 			break;
992258223a3SMatthew Dillon 		case READ_12:
993258223a3SMatthew Dillon 			lba = scsi_4btoul(cdb->rw_12.addr);
994258223a3SMatthew Dillon 			count = scsi_4btoul(cdb->rw_12.length);
995258223a3SMatthew Dillon 			xa->flags = ATA_F_READ;
996258223a3SMatthew Dillon 			break;
997258223a3SMatthew Dillon 		case READ_16:
998258223a3SMatthew Dillon 			lba = scsi_8btou64(cdb->rw_16.addr);
999258223a3SMatthew Dillon 			count = scsi_4btoul(cdb->rw_16.length);
1000258223a3SMatthew Dillon 			xa->flags = ATA_F_READ;
1001258223a3SMatthew Dillon 			break;
1002258223a3SMatthew Dillon 		case WRITE_6:
1003258223a3SMatthew Dillon 			lba = scsi_3btoul(cdb->rw_6.addr) & 0x1FFFFF;
1004258223a3SMatthew Dillon 			count = cdb->rw_6.length ? cdb->rw_6.length : 0x100;
1005258223a3SMatthew Dillon 			xa->flags = ATA_F_WRITE;
1006258223a3SMatthew Dillon 			break;
1007258223a3SMatthew Dillon 		case WRITE_10:
1008258223a3SMatthew Dillon 			lba = scsi_4btoul(cdb->rw_10.addr);
1009258223a3SMatthew Dillon 			count = scsi_2btoul(cdb->rw_10.length);
1010258223a3SMatthew Dillon 			xa->flags = ATA_F_WRITE;
1011258223a3SMatthew Dillon 			break;
1012258223a3SMatthew Dillon 		case WRITE_12:
1013258223a3SMatthew Dillon 			lba = scsi_4btoul(cdb->rw_12.addr);
1014258223a3SMatthew Dillon 			count = scsi_4btoul(cdb->rw_12.length);
1015258223a3SMatthew Dillon 			xa->flags = ATA_F_WRITE;
1016258223a3SMatthew Dillon 			break;
1017258223a3SMatthew Dillon 		case WRITE_16:
1018258223a3SMatthew Dillon 			lba = scsi_8btou64(cdb->rw_16.addr);
1019258223a3SMatthew Dillon 			count = scsi_4btoul(cdb->rw_16.length);
1020258223a3SMatthew Dillon 			xa->flags = ATA_F_WRITE;
1021258223a3SMatthew Dillon 			break;
1022258223a3SMatthew Dillon 		default:
1023258223a3SMatthew Dillon 			ccbh->status = CAM_REQ_INVALID;
1024258223a3SMatthew Dillon 			break;
1025258223a3SMatthew Dillon 		}
1026258223a3SMatthew Dillon 		if (ccbh->status != CAM_REQ_INPROG)
1027258223a3SMatthew Dillon 			break;
1028258223a3SMatthew Dillon 
1029258223a3SMatthew Dillon 		fis = xa->fis;
1030258223a3SMatthew Dillon 		fis->flags = ATA_H2D_FLAGS_CMD;
1031258223a3SMatthew Dillon 		fis->lba_low = (u_int8_t)lba;
1032258223a3SMatthew Dillon 		fis->lba_mid = (u_int8_t)(lba >> 8);
1033258223a3SMatthew Dillon 		fis->lba_high = (u_int8_t)(lba >> 16);
1034258223a3SMatthew Dillon 		fis->device = ATA_H2D_DEVICE_LBA;
1035258223a3SMatthew Dillon 
1036258223a3SMatthew Dillon 		if (ap->ap_ata.ap_ncqdepth > 1 &&
1037258223a3SMatthew Dillon 		    (ap->ap_sc->sc_cap & AHCI_REG_CAP_SNCQ) &&
1038258223a3SMatthew Dillon 		    (ccbh->flags & CAM_POLLED) == 0) {
1039258223a3SMatthew Dillon 			/*
1040258223a3SMatthew Dillon 			 * Use NCQ - always uses 48 bit addressing
1041258223a3SMatthew Dillon 			 */
1042258223a3SMatthew Dillon 			xa->flags |= ATA_F_NCQ;
1043258223a3SMatthew Dillon 			fis->command = (xa->flags & ATA_F_WRITE) ?
1044258223a3SMatthew Dillon 					ATA_C_WRITE_FPDMA : ATA_C_READ_FPDMA;
1045258223a3SMatthew Dillon 			fis->lba_low_exp = (u_int8_t)(lba >> 24);
1046258223a3SMatthew Dillon 			fis->lba_mid_exp = (u_int8_t)(lba >> 32);
1047258223a3SMatthew Dillon 			fis->lba_high_exp = (u_int8_t)(lba >> 40);
1048258223a3SMatthew Dillon 			fis->sector_count = xa->tag << 3;
1049258223a3SMatthew Dillon 			fis->features = (u_int8_t)count;
1050258223a3SMatthew Dillon 			fis->features_exp = (u_int8_t)(count >> 8);
1051258223a3SMatthew Dillon 		} else if (count > 0x100 || lba > 0xFFFFFFFFU) {
1052258223a3SMatthew Dillon 			/*
1053258223a3SMatthew Dillon 			 * Use LBA48
1054258223a3SMatthew Dillon 			 */
1055258223a3SMatthew Dillon 			fis->command = (xa->flags & ATA_F_WRITE) ?
1056258223a3SMatthew Dillon 					ATA_C_WRITEDMA_EXT : ATA_C_READDMA_EXT;
1057258223a3SMatthew Dillon 			fis->lba_low_exp = (u_int8_t)(lba >> 24);
1058258223a3SMatthew Dillon 			fis->lba_mid_exp = (u_int8_t)(lba >> 32);
1059258223a3SMatthew Dillon 			fis->lba_high_exp = (u_int8_t)(lba >> 40);
1060258223a3SMatthew Dillon 			fis->sector_count = (u_int8_t)count;
1061258223a3SMatthew Dillon 			fis->sector_count_exp = (u_int8_t)(count >> 8);
1062258223a3SMatthew Dillon 		} else {
1063258223a3SMatthew Dillon 			/*
1064258223a3SMatthew Dillon 			 * Use LBA
1065258223a3SMatthew Dillon 			 *
1066258223a3SMatthew Dillon 			 * NOTE: 256 sectors is supported, stored as 0.
1067258223a3SMatthew Dillon 			 */
1068258223a3SMatthew Dillon 			fis->command = (xa->flags & ATA_F_WRITE) ?
1069258223a3SMatthew Dillon 					ATA_C_WRITEDMA : ATA_C_READDMA;
1070258223a3SMatthew Dillon 			fis->device |= (u_int8_t)(lba >> 24) & 0x0F;
1071258223a3SMatthew Dillon 			fis->sector_count = (u_int8_t)count;
1072258223a3SMatthew Dillon 		}
1073258223a3SMatthew Dillon 
1074258223a3SMatthew Dillon 		xa->data = csio->data_ptr;
1075258223a3SMatthew Dillon 		xa->datalen = csio->dxfer_len;
1076258223a3SMatthew Dillon 		xa->complete = ahci_ata_complete_disk_rw;
1077258223a3SMatthew Dillon 		xa->timeout = ccbh->timeout * hz / 1000;
1078258223a3SMatthew Dillon 		if (ccbh->flags & CAM_POLLED)
1079258223a3SMatthew Dillon 			xa->flags |= ATA_F_POLL;
1080258223a3SMatthew Dillon 		break;
1081258223a3SMatthew Dillon 	}
1082258223a3SMatthew Dillon 
1083258223a3SMatthew Dillon 	/*
1084258223a3SMatthew Dillon 	 * If the request is still in progress the xa and FIS have
1085258223a3SMatthew Dillon 	 * been set up and must be dispatched.  Otherwise the request
1086258223a3SMatthew Dillon 	 * is complete.
1087258223a3SMatthew Dillon 	 */
1088258223a3SMatthew Dillon 	if (ccbh->status == CAM_REQ_INPROG) {
1089258223a3SMatthew Dillon 		KKASSERT(xa->complete != NULL);
1090258223a3SMatthew Dillon 		xa->atascsi_private = ccb;
1091258223a3SMatthew Dillon 		ccb->ccb_h.sim_priv.entries[0].ptr = ap;
1092258223a3SMatthew Dillon 		lwkt_serialize_enter(&ap->ap_sc->sc_serializer);
1093258223a3SMatthew Dillon 		ahci_ata_cmd(xa);
1094258223a3SMatthew Dillon 		lwkt_serialize_exit(&ap->ap_sc->sc_serializer);
1095258223a3SMatthew Dillon 	} else {
1096258223a3SMatthew Dillon 		ahci_ata_put_xfer(xa);
1097258223a3SMatthew Dillon 		xpt_done(ccb);
1098258223a3SMatthew Dillon 	}
1099258223a3SMatthew Dillon }
1100258223a3SMatthew Dillon 
1101b4189e5eSMatthew Dillon /*
1102b4189e5eSMatthew Dillon  * Convert the SCSI command in ccb to an ata_xfer command in xa
1103b4189e5eSMatthew Dillon  * for ATA_PORT_T_ATAPI operations.  Set the completion function
1104b4189e5eSMatthew Dillon  * to convert the response back, then dispatch to the OpenBSD AHCI
1105b4189e5eSMatthew Dillon  * layer.
1106b4189e5eSMatthew Dillon  */
1107258223a3SMatthew Dillon static
1108258223a3SMatthew Dillon void
1109258223a3SMatthew Dillon ahci_xpt_scsi_atapi_io(struct cam_sim *sim, union ccb *ccb)
1110258223a3SMatthew Dillon {
1111258223a3SMatthew Dillon 	struct ahci_port *ap;
1112258223a3SMatthew Dillon 	struct ccb_hdr *ccbh;
1113258223a3SMatthew Dillon 	struct ccb_scsiio *csio;
1114258223a3SMatthew Dillon 	struct ata_xfer *xa;
1115258223a3SMatthew Dillon 	struct ata_fis_h2d *fis;
1116b4189e5eSMatthew Dillon 	scsi_cdb_t cdbs;
1117b4189e5eSMatthew Dillon 	scsi_cdb_t cdbd;
1118b4189e5eSMatthew Dillon 	int flags;
1119258223a3SMatthew Dillon 
1120258223a3SMatthew Dillon 	ap = cam_sim_softc(sim);
1121258223a3SMatthew Dillon 	ccbh = &ccb->csio.ccb_h;
1122258223a3SMatthew Dillon 	csio = &ccb->csio;
1123b4189e5eSMatthew Dillon 
1124b4189e5eSMatthew Dillon 	switch (ccbh->flags & CAM_DIR_MASK) {
1125b4189e5eSMatthew Dillon 	case CAM_DIR_IN:
1126b4189e5eSMatthew Dillon 		flags = ATA_F_PACKET | ATA_F_READ;
1127b4189e5eSMatthew Dillon 		break;
1128b4189e5eSMatthew Dillon 	case CAM_DIR_OUT:
1129b4189e5eSMatthew Dillon 		flags = ATA_F_PACKET | ATA_F_WRITE;
1130b4189e5eSMatthew Dillon 		break;
1131b4189e5eSMatthew Dillon 	case CAM_DIR_NONE:
1132b4189e5eSMatthew Dillon 		flags = ATA_F_PACKET;
1133b4189e5eSMatthew Dillon 		break;
1134b4189e5eSMatthew Dillon 	default:
1135b4189e5eSMatthew Dillon 		ccbh->status = CAM_REQ_INVALID;
1136b4189e5eSMatthew Dillon 		xpt_done(ccb);
1137b4189e5eSMatthew Dillon 		return;
1138b4189e5eSMatthew Dillon 		/* NOT REACHED */
1139b4189e5eSMatthew Dillon 	}
1140b4189e5eSMatthew Dillon 
1141b4189e5eSMatthew Dillon 	/*
1142b4189e5eSMatthew Dillon 	 * The command has to fit in the packet command buffer.
1143b4189e5eSMatthew Dillon 	 */
1144b4189e5eSMatthew Dillon 	if (csio->cdb_len < 6 || csio->cdb_len > 16) {
1145b4189e5eSMatthew Dillon 		ccbh->status = CAM_CCB_LEN_ERR;
1146b4189e5eSMatthew Dillon 		xpt_done(ccb);
1147b4189e5eSMatthew Dillon 		return;
1148b4189e5eSMatthew Dillon 	}
1149b4189e5eSMatthew Dillon 
1150b4189e5eSMatthew Dillon 	/*
1151b4189e5eSMatthew Dillon 	 * Initialize the XA and FIS.
1152b4189e5eSMatthew Dillon 	 */
1153258223a3SMatthew Dillon 	xa = ahci_ata_get_xfer(ap);
1154258223a3SMatthew Dillon 	fis = xa->fis;
1155258223a3SMatthew Dillon 
1156b4189e5eSMatthew Dillon 	xa->flags = flags;
1157b4189e5eSMatthew Dillon 	xa->data = csio->data_ptr;
1158b4189e5eSMatthew Dillon 	xa->datalen = csio->dxfer_len;
1159b4189e5eSMatthew Dillon 	xa->timeout = ccbh->timeout * hz / 1000;
1160b4189e5eSMatthew Dillon 	if (ccbh->flags & CAM_POLLED)
1161b4189e5eSMatthew Dillon 		xa->flags |= ATA_F_POLL;
1162258223a3SMatthew Dillon 
1163b4189e5eSMatthew Dillon 	fis->flags = ATA_H2D_FLAGS_CMD;
1164b4189e5eSMatthew Dillon 	fis->command = ATA_C_PACKET;
1165b4189e5eSMatthew Dillon 	fis->device = 0;
1166b4189e5eSMatthew Dillon 	fis->sector_count = xa->tag << 3;
1167b4189e5eSMatthew Dillon 	fis->features = ATA_H2D_FEATURES_DMA |
1168b4189e5eSMatthew Dillon 		    ((xa->flags & ATA_F_WRITE) ?
1169b4189e5eSMatthew Dillon 		    ATA_H2D_FEATURES_DIR_WRITE : ATA_H2D_FEATURES_DIR_READ);
1170b4189e5eSMatthew Dillon 	fis->lba_mid = 0x00;
1171b4189e5eSMatthew Dillon 	fis->lba_high = 0x20;
1172b4189e5eSMatthew Dillon 
1173258223a3SMatthew Dillon 	/*
1174b4189e5eSMatthew Dillon 	 * Copy the cdb to the packetcmd buffer in the FIS using a
1175b4189e5eSMatthew Dillon 	 * convenient pointer in the xa.
1176258223a3SMatthew Dillon 	 */
1177b4189e5eSMatthew Dillon 	cdbs = (void *)((ccbh->flags & CAM_CDB_POINTER) ?
1178b4189e5eSMatthew Dillon 			csio->cdb_io.cdb_ptr : csio->cdb_io.cdb_bytes);
1179b4189e5eSMatthew Dillon 	bcopy(cdbs, xa->packetcmd, csio->cdb_len);
1180b4189e5eSMatthew Dillon 
1181669fbbf7SMatthew Dillon #if 0
1182b4189e5eSMatthew Dillon 	kprintf("opcode %d cdb_len %d dxfer_len %d\n",
1183b4189e5eSMatthew Dillon 		cdbs->generic.opcode,
1184b4189e5eSMatthew Dillon 		csio->cdb_len, csio->dxfer_len);
1185669fbbf7SMatthew Dillon #endif
1186b4189e5eSMatthew Dillon 
1187b4189e5eSMatthew Dillon 	/*
1188b4189e5eSMatthew Dillon 	 * Some ATAPI commands do not actually follow the SCSI standard.
1189b4189e5eSMatthew Dillon 	 */
1190b4189e5eSMatthew Dillon 	cdbd = (void *)xa->packetcmd;
1191b4189e5eSMatthew Dillon 
1192b4189e5eSMatthew Dillon 	switch(cdbd->generic.opcode) {
1193258223a3SMatthew Dillon 	case INQUIRY:
1194b4189e5eSMatthew Dillon 		/*
1195b4189e5eSMatthew Dillon 		 * Some ATAPI devices can't handle SI_EVPD being set
1196b4189e5eSMatthew Dillon 		 * for a basic inquiry (page_code == 0).
1197b4189e5eSMatthew Dillon 		 *
1198b4189e5eSMatthew Dillon 		 * Some ATAPI devices can't handle long inquiry lengths,
1199b4189e5eSMatthew Dillon 		 * don't ask me why.  Truncate the inquiry length.
1200b4189e5eSMatthew Dillon 		 */
1201b4189e5eSMatthew Dillon 		if ((cdbd->inquiry.byte2 & SI_EVPD) &&
1202b4189e5eSMatthew Dillon 		    cdbd->inquiry.page_code == 0) {
1203b4189e5eSMatthew Dillon 			cdbd->inquiry.byte2 &= ~SI_EVPD;
1204b4189e5eSMatthew Dillon 		}
1205b4189e5eSMatthew Dillon 		if (cdbd->inquiry.page_code == 0 &&
1206b4189e5eSMatthew Dillon 		    cdbd->inquiry.length > SHORT_INQUIRY_LENGTH) {
1207b4189e5eSMatthew Dillon 			cdbd->inquiry.length = SHORT_INQUIRY_LENGTH;
1208b4189e5eSMatthew Dillon 		}
1209b4189e5eSMatthew Dillon 		break;
1210258223a3SMatthew Dillon 	case READ_6:
1211258223a3SMatthew Dillon 	case WRITE_6:
1212b4189e5eSMatthew Dillon 		/*
1213b4189e5eSMatthew Dillon 		 * Convert *_6 to *_10 commands.  Most ATAPI devices
1214b4189e5eSMatthew Dillon 		 * cannot handle the SCSI READ_6 and WRITE_6 commands.
1215b4189e5eSMatthew Dillon 		 */
1216b4189e5eSMatthew Dillon 		cdbd->rw_10.opcode |= 0x20;
1217b4189e5eSMatthew Dillon 		cdbd->rw_10.byte2 = 0;
1218b4189e5eSMatthew Dillon 		cdbd->rw_10.addr[0] = cdbs->rw_6.addr[0] & 0x1F;
1219b4189e5eSMatthew Dillon 		cdbd->rw_10.addr[1] = cdbs->rw_6.addr[1];
1220b4189e5eSMatthew Dillon 		cdbd->rw_10.addr[2] = cdbs->rw_6.addr[2];
1221b4189e5eSMatthew Dillon 		cdbd->rw_10.addr[3] = 0;
1222b4189e5eSMatthew Dillon 		cdbd->rw_10.reserved = 0;
1223b4189e5eSMatthew Dillon 		cdbd->rw_10.length[0] = 0;
1224b4189e5eSMatthew Dillon 		cdbd->rw_10.length[1] = cdbs->rw_6.length;
1225b4189e5eSMatthew Dillon 		cdbd->rw_10.control = cdbs->rw_6.control;
1226b4189e5eSMatthew Dillon 		break;
1227258223a3SMatthew Dillon 	default:
1228258223a3SMatthew Dillon 		break;
1229258223a3SMatthew Dillon 	}
1230258223a3SMatthew Dillon 
1231b4189e5eSMatthew Dillon 	/*
1232b4189e5eSMatthew Dillon 	 * And dispatch
1233b4189e5eSMatthew Dillon 	 */
1234b4189e5eSMatthew Dillon 	xa->complete = ahci_atapi_complete_cmd;
1235258223a3SMatthew Dillon 	xa->atascsi_private = ccb;
1236258223a3SMatthew Dillon 	ccb->ccb_h.sim_priv.entries[0].ptr = ap;
1237258223a3SMatthew Dillon 	ahci_ata_cmd(xa);
1238258223a3SMatthew Dillon }
1239258223a3SMatthew Dillon 
1240b4189e5eSMatthew Dillon /*
1241b4189e5eSMatthew Dillon  * Completion function for ATA_PORT_T_DISK cache synchronization.
1242b4189e5eSMatthew Dillon  */
1243258223a3SMatthew Dillon static
1244258223a3SMatthew Dillon void
1245258223a3SMatthew Dillon ahci_ata_complete_disk_synchronize_cache(struct ata_xfer *xa)
1246258223a3SMatthew Dillon {
1247258223a3SMatthew Dillon 	union ccb *ccb = xa->atascsi_private;
1248258223a3SMatthew Dillon 	struct ccb_hdr *ccbh = &ccb->ccb_h;
1249258223a3SMatthew Dillon 	struct ahci_port *ap = ccb->ccb_h.sim_priv.entries[0].ptr;
1250258223a3SMatthew Dillon 
1251258223a3SMatthew Dillon 	switch(xa->state) {
1252258223a3SMatthew Dillon 	case ATA_S_COMPLETE:
1253258223a3SMatthew Dillon 		ccbh->status = CAM_REQ_CMP;
1254b4189e5eSMatthew Dillon 		ccb->csio.scsi_status = SCSI_STATUS_OK;
1255258223a3SMatthew Dillon 		break;
1256258223a3SMatthew Dillon 	case ATA_S_ERROR:
1257258223a3SMatthew Dillon 		kprintf("%s: synchronize_cache: error\n", PORTNAME(ap));
1258b4189e5eSMatthew Dillon 		ccbh->status = CAM_SCSI_STATUS_ERROR | CAM_AUTOSNS_VALID;
1259b4189e5eSMatthew Dillon 		ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND;
1260b4189e5eSMatthew Dillon 		ahci_ata_dummy_sense(&ccb->csio.sense_data);
1261258223a3SMatthew Dillon 		break;
1262258223a3SMatthew Dillon 	case ATA_S_TIMEOUT:
1263258223a3SMatthew Dillon 		kprintf("%s: synchronize_cache: timeout\n", PORTNAME(ap));
1264258223a3SMatthew Dillon 		ccbh->status = CAM_CMD_TIMEOUT;
1265258223a3SMatthew Dillon 		break;
1266258223a3SMatthew Dillon 	default:
1267258223a3SMatthew Dillon 		kprintf("%s: synchronize_cache: unknown state %d\n",
1268258223a3SMatthew Dillon 			PORTNAME(ap), xa->state);
1269258223a3SMatthew Dillon 		ccbh->status = CAM_REQ_CMP_ERR;
1270258223a3SMatthew Dillon 		break;
1271258223a3SMatthew Dillon 	}
1272258223a3SMatthew Dillon 	ahci_ata_put_xfer(xa);
1273258223a3SMatthew Dillon 	lwkt_serialize_exit(&ap->ap_sc->sc_serializer);
1274258223a3SMatthew Dillon 	xpt_done(ccb);
1275258223a3SMatthew Dillon 	lwkt_serialize_enter(&ap->ap_sc->sc_serializer);
1276258223a3SMatthew Dillon }
1277258223a3SMatthew Dillon 
1278b4189e5eSMatthew Dillon /*
1279b4189e5eSMatthew Dillon  * Completion function for ATA_PORT_T_DISK I/O
1280b4189e5eSMatthew Dillon  */
1281258223a3SMatthew Dillon static
1282258223a3SMatthew Dillon void
1283258223a3SMatthew Dillon ahci_ata_complete_disk_rw(struct ata_xfer *xa)
1284258223a3SMatthew Dillon {
1285258223a3SMatthew Dillon 	union ccb *ccb = xa->atascsi_private;
1286258223a3SMatthew Dillon 	struct ccb_hdr *ccbh = &ccb->ccb_h;
1287258223a3SMatthew Dillon 	struct ahci_port *ap = ccb->ccb_h.sim_priv.entries[0].ptr;
1288258223a3SMatthew Dillon 
1289258223a3SMatthew Dillon 	switch(xa->state) {
1290258223a3SMatthew Dillon 	case ATA_S_COMPLETE:
1291258223a3SMatthew Dillon 		ccbh->status = CAM_REQ_CMP;
1292b4189e5eSMatthew Dillon 		ccb->csio.scsi_status = SCSI_STATUS_OK;
1293258223a3SMatthew Dillon 		break;
1294258223a3SMatthew Dillon 	case ATA_S_ERROR:
1295258223a3SMatthew Dillon 		kprintf("%s: disk_rw: error\n", PORTNAME(ap));
1296b4189e5eSMatthew Dillon 		ccbh->status = CAM_SCSI_STATUS_ERROR | CAM_AUTOSNS_VALID;
1297b4189e5eSMatthew Dillon 		ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND;
1298b4189e5eSMatthew Dillon 		ahci_ata_dummy_sense(&ccb->csio.sense_data);
1299258223a3SMatthew Dillon 		break;
1300258223a3SMatthew Dillon 	case ATA_S_TIMEOUT:
1301258223a3SMatthew Dillon 		kprintf("%s: disk_rw: timeout\n", PORTNAME(ap));
1302258223a3SMatthew Dillon 		ccbh->status = CAM_CMD_TIMEOUT;
1303258223a3SMatthew Dillon 		break;
1304258223a3SMatthew Dillon 	default:
1305258223a3SMatthew Dillon 		kprintf("%s: disk_rw: unknown state %d\n",
1306258223a3SMatthew Dillon 			PORTNAME(ap), xa->state);
1307258223a3SMatthew Dillon 		ccbh->status = CAM_REQ_CMP_ERR;
1308258223a3SMatthew Dillon 		break;
1309258223a3SMatthew Dillon 	}
1310258223a3SMatthew Dillon 	ccb->csio.resid = xa->resid;
1311258223a3SMatthew Dillon 	ahci_ata_put_xfer(xa);
1312258223a3SMatthew Dillon 	lwkt_serialize_exit(&ap->ap_sc->sc_serializer);
1313258223a3SMatthew Dillon 	xpt_done(ccb);
1314258223a3SMatthew Dillon 	lwkt_serialize_enter(&ap->ap_sc->sc_serializer);
1315258223a3SMatthew Dillon }
1316b4189e5eSMatthew Dillon 
13177d4fcf34SMatthew Dillon /*
13187d4fcf34SMatthew Dillon  * Completion function for ATA_PORT_T_ATAPI I/O
13197d4fcf34SMatthew Dillon  *
13207d4fcf34SMatthew Dillon  * Sense data is returned in the rfis.
13217d4fcf34SMatthew Dillon  */
1322b4189e5eSMatthew Dillon static
1323b4189e5eSMatthew Dillon void
1324b4189e5eSMatthew Dillon ahci_atapi_complete_cmd(struct ata_xfer *xa)
1325b4189e5eSMatthew Dillon {
1326b4189e5eSMatthew Dillon 	union ccb *ccb = xa->atascsi_private;
1327b4189e5eSMatthew Dillon 	struct ccb_hdr *ccbh = &ccb->ccb_h;
1328b4189e5eSMatthew Dillon 	struct ahci_port *ap = ccb->ccb_h.sim_priv.entries[0].ptr;
1329b4189e5eSMatthew Dillon 	scsi_cdb_t cdb;
1330b4189e5eSMatthew Dillon 
1331b4189e5eSMatthew Dillon 	cdb = (void *)((ccb->ccb_h.flags & CAM_CDB_POINTER) ?
1332b4189e5eSMatthew Dillon 			ccb->csio.cdb_io.cdb_ptr : ccb->csio.cdb_io.cdb_bytes);
1333b4189e5eSMatthew Dillon 
1334b4189e5eSMatthew Dillon 	switch(xa->state) {
1335b4189e5eSMatthew Dillon 	case ATA_S_COMPLETE:
1336b4189e5eSMatthew Dillon 		ccbh->status = CAM_REQ_CMP;
1337b4189e5eSMatthew Dillon 		ccb->csio.scsi_status = SCSI_STATUS_OK;
1338b4189e5eSMatthew Dillon 		break;
1339b4189e5eSMatthew Dillon 	case ATA_S_ERROR:
1340b4189e5eSMatthew Dillon 		kprintf("%s: cmd %d: error\n",
1341b4189e5eSMatthew Dillon 			PORTNAME(ap), cdb->generic.opcode);
1342b4189e5eSMatthew Dillon 		ccbh->status = CAM_SCSI_STATUS_ERROR;
1343b4189e5eSMatthew Dillon 		ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND;
13447d4fcf34SMatthew Dillon 		ahci_ata_atapi_sense(&xa->rfis, &ccb->csio.sense_data);
1345b4189e5eSMatthew Dillon 		break;
1346b4189e5eSMatthew Dillon 	case ATA_S_TIMEOUT:
1347b4189e5eSMatthew Dillon 		kprintf("%s: cmd %d: timeout\n",
1348b4189e5eSMatthew Dillon 			PORTNAME(ap), cdb->generic.opcode);
1349b4189e5eSMatthew Dillon 		ccbh->status = CAM_CMD_TIMEOUT;
1350b4189e5eSMatthew Dillon 		break;
1351b4189e5eSMatthew Dillon 	default:
1352b4189e5eSMatthew Dillon 		kprintf("%s: cmd %d: unknown state %d\n",
1353b4189e5eSMatthew Dillon 			PORTNAME(ap), cdb->generic.opcode, xa->state);
1354b4189e5eSMatthew Dillon 		ccbh->status = CAM_REQ_CMP_ERR;
1355b4189e5eSMatthew Dillon 		break;
1356b4189e5eSMatthew Dillon 	}
1357b4189e5eSMatthew Dillon 	ccb->csio.resid = xa->resid;
1358b4189e5eSMatthew Dillon 	ahci_ata_put_xfer(xa);
1359b4189e5eSMatthew Dillon 	lwkt_serialize_exit(&ap->ap_sc->sc_serializer);
1360b4189e5eSMatthew Dillon 	xpt_done(ccb);
1361b4189e5eSMatthew Dillon 	lwkt_serialize_enter(&ap->ap_sc->sc_serializer);
1362b4189e5eSMatthew Dillon }
1363b4189e5eSMatthew Dillon 
13647d4fcf34SMatthew Dillon /*
13657d4fcf34SMatthew Dillon  * Construct dummy sense data for errors on DISKs
13667d4fcf34SMatthew Dillon  */
1367b4189e5eSMatthew Dillon static
1368b4189e5eSMatthew Dillon void
1369b4189e5eSMatthew Dillon ahci_ata_dummy_sense(struct scsi_sense_data *sense_data)
1370b4189e5eSMatthew Dillon {
1371b4189e5eSMatthew Dillon 	sense_data->error_code = SSD_ERRCODE_VALID | SSD_CURRENT_ERROR;
1372b4189e5eSMatthew Dillon 	sense_data->segment = 0;
1373b4189e5eSMatthew Dillon 	sense_data->flags = SSD_KEY_MEDIUM_ERROR;
1374b4189e5eSMatthew Dillon 	sense_data->info[0] = 0;
1375b4189e5eSMatthew Dillon 	sense_data->info[1] = 0;
1376b4189e5eSMatthew Dillon 	sense_data->info[2] = 0;
1377b4189e5eSMatthew Dillon 	sense_data->info[3] = 0;
1378b4189e5eSMatthew Dillon 	sense_data->extra_len = 0;
1379b4189e5eSMatthew Dillon }
13807d4fcf34SMatthew Dillon 
13817d4fcf34SMatthew Dillon /*
13827d4fcf34SMatthew Dillon  * Construct atapi sense data for errors on ATAPI
13837d4fcf34SMatthew Dillon  *
13847d4fcf34SMatthew Dillon  * The ATAPI sense data is stored in the passed rfis and must be converted
13857d4fcf34SMatthew Dillon  * to SCSI sense data.
13867d4fcf34SMatthew Dillon  */
13877d4fcf34SMatthew Dillon static
13887d4fcf34SMatthew Dillon void
13897d4fcf34SMatthew Dillon ahci_ata_atapi_sense(struct ata_fis_d2h *rfis,
13907d4fcf34SMatthew Dillon 		     struct scsi_sense_data *sense_data)
13917d4fcf34SMatthew Dillon {
13927d4fcf34SMatthew Dillon 	sense_data->error_code = SSD_ERRCODE_VALID | SSD_CURRENT_ERROR;
13937d4fcf34SMatthew Dillon 	sense_data->segment = 0;
13947d4fcf34SMatthew Dillon 	sense_data->flags = (rfis->error & 0xF0) >> 4;
13957d4fcf34SMatthew Dillon 	if (rfis->error & 0x04)
13967d4fcf34SMatthew Dillon 		sense_data->flags |= SSD_KEY_ILLEGAL_REQUEST;
13977d4fcf34SMatthew Dillon 	if (rfis->error & 0x02)
13987d4fcf34SMatthew Dillon 		sense_data->flags |= SSD_EOM;
13997d4fcf34SMatthew Dillon 	if (rfis->error & 0x01)
14007d4fcf34SMatthew Dillon 		sense_data->flags |= SSD_ILI;
14017d4fcf34SMatthew Dillon 	sense_data->info[0] = 0;
14027d4fcf34SMatthew Dillon 	sense_data->info[1] = 0;
14037d4fcf34SMatthew Dillon 	sense_data->info[2] = 0;
14047d4fcf34SMatthew Dillon 	sense_data->info[3] = 0;
14057d4fcf34SMatthew Dillon 	sense_data->extra_len = 0;
14067d4fcf34SMatthew Dillon }
1407