xref: /dflybsd-src/sys/dev/disk/ahci/ahci_cam.c (revision 6e0003ad5d730ad6829e66a55444f490e9e41cb4)
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);
104*6e0003adSMatthew Dillon static void ahci_xpt_page_inquiry(struct ahci_port *ap,
105*6e0003adSMatthew Dillon 			struct ata_port *at, union ccb *ccb);
106258223a3SMatthew Dillon 
107258223a3SMatthew Dillon static void ahci_ata_complete_disk_rw(struct ata_xfer *xa);
108258223a3SMatthew Dillon static void ahci_ata_complete_disk_synchronize_cache(struct ata_xfer *xa);
109b4189e5eSMatthew Dillon static void ahci_atapi_complete_cmd(struct ata_xfer *xa);
110b4189e5eSMatthew Dillon static void ahci_ata_dummy_sense(struct scsi_sense_data *sense_data);
1117d4fcf34SMatthew Dillon static void ahci_ata_atapi_sense(struct ata_fis_d2h *rfis,
1127d4fcf34SMatthew Dillon 		     struct scsi_sense_data *sense_data);
113258223a3SMatthew Dillon 
1141980eff3SMatthew Dillon static int ahci_cam_probe_disk(struct ahci_port *ap, struct ata_port *at);
1151980eff3SMatthew Dillon static int ahci_cam_probe_atapi(struct ahci_port *ap, struct ata_port *at);
116b4189e5eSMatthew Dillon static void ahci_ata_dummy_done(struct ata_xfer *xa);
117258223a3SMatthew Dillon static void ata_fix_identify(struct ata_identify *id);
118258223a3SMatthew Dillon static void ahci_cam_rescan(struct ahci_port *ap);
119*6e0003adSMatthew Dillon static void ahci_strip_string(const char **basep, int *lenp);
120258223a3SMatthew Dillon 
121258223a3SMatthew Dillon int
122258223a3SMatthew Dillon ahci_cam_attach(struct ahci_port *ap)
123258223a3SMatthew Dillon {
124258223a3SMatthew Dillon 	struct cam_devq *devq;
125258223a3SMatthew Dillon 	struct cam_sim *sim;
126258223a3SMatthew Dillon 	int error;
127258223a3SMatthew Dillon 	int unit;
128258223a3SMatthew Dillon 
129cec85a37SMatthew Dillon 	/*
130cec85a37SMatthew Dillon 	 * We want at least one ccb to be available for error processing
131cec85a37SMatthew Dillon 	 * so don't let CAM use more then ncmds - 1.
132cec85a37SMatthew Dillon 	 */
133258223a3SMatthew Dillon 	unit = device_get_unit(ap->ap_sc->sc_dev);
134cec85a37SMatthew Dillon 	if (ap->ap_sc->sc_ncmds > 1)
135cec85a37SMatthew Dillon 		devq = cam_simq_alloc(ap->ap_sc->sc_ncmds - 1);
136cec85a37SMatthew Dillon 	else
137258223a3SMatthew Dillon 		devq = cam_simq_alloc(ap->ap_sc->sc_ncmds);
138258223a3SMatthew Dillon 	if (devq == NULL) {
139258223a3SMatthew Dillon 		return (ENOMEM);
140258223a3SMatthew Dillon 	}
141258223a3SMatthew Dillon 	sim = cam_sim_alloc(ahci_xpt_action, ahci_xpt_poll, "ahci",
142258223a3SMatthew Dillon 			   (void *)ap, unit, &sim_mplock, 1, 1, devq);
143258223a3SMatthew Dillon 	cam_simq_release(devq);
144258223a3SMatthew Dillon 	if (sim == NULL) {
145258223a3SMatthew Dillon 		return (ENOMEM);
146258223a3SMatthew Dillon 	}
147258223a3SMatthew Dillon 	ap->ap_sim = sim;
148831bc9e3SMatthew Dillon 	ahci_os_unlock_port(ap);
149258223a3SMatthew Dillon 	error = xpt_bus_register(ap->ap_sim, ap->ap_num);
150831bc9e3SMatthew Dillon 	ahci_os_lock_port(ap);
151258223a3SMatthew Dillon 	if (error != CAM_SUCCESS) {
152258223a3SMatthew Dillon 		ahci_cam_detach(ap);
153258223a3SMatthew Dillon 		return (EINVAL);
154258223a3SMatthew Dillon 	}
155258223a3SMatthew Dillon 	ap->ap_flags |= AP_F_BUS_REGISTERED;
156258223a3SMatthew Dillon 
157c408a8b3SMatthew Dillon 	if (ap->ap_probe == ATA_PROBE_NEED_IDENT)
1581980eff3SMatthew Dillon 		error = ahci_cam_probe(ap, NULL);
159c408a8b3SMatthew Dillon 	else
160c408a8b3SMatthew Dillon 		error = 0;
161258223a3SMatthew Dillon 	if (error) {
162258223a3SMatthew Dillon 		ahci_cam_detach(ap);
163258223a3SMatthew Dillon 		return (EIO);
164258223a3SMatthew Dillon 	}
165258223a3SMatthew Dillon 	ap->ap_flags |= AP_F_CAM_ATTACHED;
166258223a3SMatthew Dillon 
167258223a3SMatthew Dillon 	return(0);
168258223a3SMatthew Dillon }
169258223a3SMatthew Dillon 
1701980eff3SMatthew Dillon /*
1713209f581SMatthew Dillon  * The state of the port has changed.
1723209f581SMatthew Dillon  *
1733209f581SMatthew Dillon  * If at is NULL the physical port has changed state.
1743209f581SMatthew Dillon  * If at is non-NULL a particular target behind a PM has changed state.
1753209f581SMatthew Dillon  *
1763209f581SMatthew Dillon  * If found is -1 the target state must be queued to a non-interrupt context.
1773209f581SMatthew Dillon  * (only works with at == NULL).
1783209f581SMatthew Dillon  *
1793209f581SMatthew Dillon  * If found is 0 the target was removed.
1803209f581SMatthew Dillon  * If found is 1 the target was inserted.
1811980eff3SMatthew Dillon  */
182258223a3SMatthew Dillon void
1833209f581SMatthew Dillon ahci_cam_changed(struct ahci_port *ap, struct ata_port *atx, int found)
184258223a3SMatthew Dillon {
185fd8bd957SMatthew Dillon 	struct cam_path *tmppath;
1863209f581SMatthew Dillon 	int status;
1873209f581SMatthew Dillon 	int target;
1883209f581SMatthew Dillon 
1893209f581SMatthew Dillon 	target = atx ? atx->at_target : CAM_TARGET_WILDCARD;
190fd8bd957SMatthew Dillon 
191258223a3SMatthew Dillon 	if (ap->ap_sim == NULL)
192258223a3SMatthew Dillon 		return;
1933209f581SMatthew Dillon 	if (found == CAM_TARGET_WILDCARD) {
1943209f581SMatthew Dillon 		status = xpt_create_path(&tmppath, NULL,
1953209f581SMatthew Dillon 					 cam_sim_path(ap->ap_sim),
1963209f581SMatthew Dillon 					 target, CAM_LUN_WILDCARD);
1973209f581SMatthew Dillon 		if (status != CAM_REQ_CMP)
198fd8bd957SMatthew Dillon 			return;
199258223a3SMatthew Dillon 		ahci_cam_rescan(ap);
200fd8bd957SMatthew Dillon 	} else {
2013209f581SMatthew Dillon 		status = xpt_create_path(&tmppath, NULL,
2023209f581SMatthew Dillon 					 cam_sim_path(ap->ap_sim),
2033209f581SMatthew Dillon 					 target,
2043209f581SMatthew Dillon 					 CAM_LUN_WILDCARD);
2053209f581SMatthew Dillon 		if (status != CAM_REQ_CMP)
2063209f581SMatthew Dillon 			return;
2073209f581SMatthew Dillon #if 0
2083209f581SMatthew Dillon 		/*
2093209f581SMatthew Dillon 		 * This confuses CAM
2103209f581SMatthew Dillon 		 */
2113209f581SMatthew Dillon 		if (found)
2123209f581SMatthew Dillon 			xpt_async(AC_FOUND_DEVICE, tmppath, NULL);
2133209f581SMatthew Dillon 		else
214fd8bd957SMatthew Dillon 			xpt_async(AC_LOST_DEVICE, tmppath, NULL);
2153209f581SMatthew Dillon #endif
216fd8bd957SMatthew Dillon 	}
217fd8bd957SMatthew Dillon 	xpt_free_path(tmppath);
218258223a3SMatthew Dillon }
219258223a3SMatthew Dillon 
220258223a3SMatthew Dillon void
221258223a3SMatthew Dillon ahci_cam_detach(struct ahci_port *ap)
222258223a3SMatthew Dillon {
223258223a3SMatthew Dillon 	int error;
224258223a3SMatthew Dillon 
225258223a3SMatthew Dillon 	if ((ap->ap_flags & AP_F_CAM_ATTACHED) == 0)
226258223a3SMatthew Dillon 		return;
227258223a3SMatthew Dillon 	get_mplock();
228258223a3SMatthew Dillon 	if (ap->ap_sim) {
229258223a3SMatthew Dillon 		xpt_freeze_simq(ap->ap_sim, 1);
230258223a3SMatthew Dillon 	}
231258223a3SMatthew Dillon 	if (ap->ap_flags & AP_F_BUS_REGISTERED) {
232258223a3SMatthew Dillon 		error = xpt_bus_deregister(cam_sim_path(ap->ap_sim));
233258223a3SMatthew Dillon 		KKASSERT(error == CAM_REQ_CMP);
234258223a3SMatthew Dillon 		ap->ap_flags &= ~AP_F_BUS_REGISTERED;
235258223a3SMatthew Dillon 	}
236258223a3SMatthew Dillon 	if (ap->ap_sim) {
237258223a3SMatthew Dillon 		cam_sim_free(ap->ap_sim);
238258223a3SMatthew Dillon 		ap->ap_sim = NULL;
239258223a3SMatthew Dillon 	}
240258223a3SMatthew Dillon 	rel_mplock();
241258223a3SMatthew Dillon 	ap->ap_flags &= ~AP_F_CAM_ATTACHED;
242258223a3SMatthew Dillon }
243258223a3SMatthew Dillon 
244258223a3SMatthew Dillon /*
2451980eff3SMatthew Dillon  * Once the AHCI port has been attached we need to probe for a device or
246258223a3SMatthew Dillon  * devices on the port and setup various options.
2471980eff3SMatthew Dillon  *
2481980eff3SMatthew Dillon  * If at is NULL we are probing the direct-attached device on the port,
2491980eff3SMatthew Dillon  * which may or may not be a port multiplier.
250258223a3SMatthew Dillon  */
2513209f581SMatthew Dillon int
2521980eff3SMatthew Dillon ahci_cam_probe(struct ahci_port *ap, struct ata_port *atx)
253258223a3SMatthew Dillon {
2541980eff3SMatthew Dillon 	struct ata_port	*at;
255258223a3SMatthew Dillon 	struct ata_xfer	*xa;
256258223a3SMatthew Dillon 	u_int64_t	capacity;
257258223a3SMatthew Dillon 	u_int64_t	capacity_bytes;
258258223a3SMatthew Dillon 	int		model_len;
259*6e0003adSMatthew Dillon 	int		firmware_len;
260*6e0003adSMatthew Dillon 	int		serial_len;
261fd8bd957SMatthew Dillon 	int		error;
262258223a3SMatthew Dillon 	int		devncqdepth;
263258223a3SMatthew Dillon 	int		i;
264*6e0003adSMatthew Dillon 	const char	*model_id;
265*6e0003adSMatthew Dillon 	const char	*firmware_id;
266*6e0003adSMatthew Dillon 	const char	*serial_id;
267669fbbf7SMatthew Dillon 	const char	*wcstr;
268669fbbf7SMatthew Dillon 	const char	*rastr;
269fd8bd957SMatthew Dillon 	const char	*scstr;
270fd8bd957SMatthew Dillon 	const char	*type;
271fd8bd957SMatthew Dillon 
2723209f581SMatthew Dillon 	error = EIO;
2731980eff3SMatthew Dillon 
2741980eff3SMatthew Dillon 	/*
275f4553de1SMatthew Dillon 	 * Delayed CAM attachment for initial probe, sim may be NULL
276f4553de1SMatthew Dillon 	 */
277f4553de1SMatthew Dillon 	if (ap->ap_sim == NULL)
278f4553de1SMatthew Dillon 		return(0);
279f4553de1SMatthew Dillon 
280f4553de1SMatthew Dillon 	/*
2811980eff3SMatthew Dillon 	 * A NULL atx indicates a probe of the directly connected device.
2821980eff3SMatthew Dillon 	 * A non-NULL atx indicates a device connected via a port multiplier.
2831980eff3SMatthew Dillon 	 * We need to preserve atx for calls to ahci_ata_get_xfer().
2841980eff3SMatthew Dillon 	 *
2851980eff3SMatthew Dillon 	 * at is always non-NULL.  For directly connected devices we supply
2861980eff3SMatthew Dillon 	 * an (at) pointing to target 0.
2871980eff3SMatthew Dillon 	 */
2881980eff3SMatthew Dillon 	if (atx == NULL) {
2893209f581SMatthew Dillon 		at = ap->ap_ata;	/* direct attached - device 0 */
2901980eff3SMatthew Dillon 		if (ap->ap_type == ATA_PORT_T_PM) {
291831bc9e3SMatthew Dillon 			kprintf("%s: Found Port Multiplier\n",
292831bc9e3SMatthew Dillon 				ATANAME(ap, atx));
2931980eff3SMatthew Dillon 			return (0);
2941980eff3SMatthew Dillon 		}
2951980eff3SMatthew Dillon 		at->at_type = ap->ap_type;
2961980eff3SMatthew Dillon 	} else {
2973209f581SMatthew Dillon 		at = atx;
2981980eff3SMatthew Dillon 		if (atx->at_type == ATA_PORT_T_PM) {
2991980eff3SMatthew Dillon 			kprintf("%s: Bogus device, reducing port count to %d\n",
3001980eff3SMatthew Dillon 				ATANAME(ap, atx), atx->at_target);
3011980eff3SMatthew Dillon 			if (ap->ap_pmcount > atx->at_target)
3021980eff3SMatthew Dillon 				ap->ap_pmcount = atx->at_target;
3033209f581SMatthew Dillon 			goto err;
3041980eff3SMatthew Dillon 		}
3051980eff3SMatthew Dillon 	}
3063209f581SMatthew Dillon 	if (ap->ap_type == ATA_PORT_T_NONE)
3073209f581SMatthew Dillon 		goto err;
3081980eff3SMatthew Dillon 	if (at->at_type == ATA_PORT_T_NONE)
3093209f581SMatthew Dillon 		goto err;
310258223a3SMatthew Dillon 
311258223a3SMatthew Dillon 	/*
312258223a3SMatthew Dillon 	 * Issue identify, saving the result
313258223a3SMatthew Dillon 	 */
3141980eff3SMatthew Dillon 	xa = ahci_ata_get_xfer(ap, atx);
315258223a3SMatthew Dillon 	xa->complete = ahci_ata_dummy_done;
3161980eff3SMatthew Dillon 	xa->data = &at->at_identify;
3171980eff3SMatthew Dillon 	xa->datalen = sizeof(at->at_identify);
31812feb904SMatthew Dillon 	xa->flags = ATA_F_READ | ATA_F_PIO | ATA_F_POLL;
3191980eff3SMatthew Dillon 	xa->fis->flags = ATA_H2D_FLAGS_CMD | at->at_target;
3201980eff3SMatthew Dillon 
3211980eff3SMatthew Dillon 	switch(at->at_type) {
3221980eff3SMatthew Dillon 	case ATA_PORT_T_DISK:
323258223a3SMatthew Dillon 		xa->fis->command = ATA_C_IDENTIFY;
324fd8bd957SMatthew Dillon 		type = "DISK";
3251980eff3SMatthew Dillon 		break;
3261980eff3SMatthew Dillon 	case ATA_PORT_T_ATAPI:
3271980eff3SMatthew Dillon 		xa->fis->command = ATA_C_ATAPI_IDENTIFY;
32812feb904SMatthew Dillon 		xa->flags |= ATA_F_AUTOSENSE;
3291980eff3SMatthew Dillon 		type = "ATAPI";
3301980eff3SMatthew Dillon 		break;
3311980eff3SMatthew Dillon 	default:
3321980eff3SMatthew Dillon 		xa->fis->command = ATA_C_ATAPI_IDENTIFY;
3331980eff3SMatthew Dillon 		type = "UNKNOWN(ATAPI?)";
3341980eff3SMatthew Dillon 		break;
335fd8bd957SMatthew Dillon 	}
336258223a3SMatthew Dillon 	xa->fis->features = 0;
337258223a3SMatthew Dillon 	xa->fis->device = 0;
3383209f581SMatthew Dillon 	xa->timeout = 1000;
339258223a3SMatthew Dillon 
340831bc9e3SMatthew Dillon 	if (ahci_ata_cmd(xa) != ATA_S_COMPLETE) {
341fd8bd957SMatthew Dillon 		kprintf("%s: Detected %s device but unable to IDENTIFY\n",
3421980eff3SMatthew Dillon 			ATANAME(ap, atx), type);
343258223a3SMatthew Dillon 		ahci_ata_put_xfer(xa);
3443209f581SMatthew Dillon 		goto err;
345258223a3SMatthew Dillon 	}
346258223a3SMatthew Dillon 	ahci_ata_put_xfer(xa);
347258223a3SMatthew Dillon 
3481980eff3SMatthew Dillon 	ata_fix_identify(&at->at_identify);
349258223a3SMatthew Dillon 
350258223a3SMatthew Dillon 	/*
351258223a3SMatthew Dillon 	 * Read capacity using SATA probe info.
352258223a3SMatthew Dillon 	 */
3531980eff3SMatthew Dillon 	if (le16toh(at->at_identify.cmdset83) & 0x0400) {
354258223a3SMatthew Dillon 		/* LBA48 feature set supported */
355258223a3SMatthew Dillon 		capacity = 0;
356258223a3SMatthew Dillon 		for (i = 3; i >= 0; --i) {
357258223a3SMatthew Dillon 			capacity <<= 16;
358258223a3SMatthew Dillon 			capacity +=
3591980eff3SMatthew Dillon 			    le16toh(at->at_identify.addrsecxt[i]);
360258223a3SMatthew Dillon 		}
361258223a3SMatthew Dillon 	} else {
3621980eff3SMatthew Dillon 		capacity = le16toh(at->at_identify.addrsec[1]);
363258223a3SMatthew Dillon 		capacity <<= 16;
3641980eff3SMatthew Dillon 		capacity += le16toh(at->at_identify.addrsec[0]);
365258223a3SMatthew Dillon 	}
36612feb904SMatthew Dillon 	if (capacity == 0)
36712feb904SMatthew Dillon 		capacity = 1024 * 1024 / 512;
3681980eff3SMatthew Dillon 	at->at_capacity = capacity;
3691980eff3SMatthew Dillon 	if (atx == NULL)
3701980eff3SMatthew Dillon 		ap->ap_probe = ATA_PROBE_GOOD;
371258223a3SMatthew Dillon 
372258223a3SMatthew Dillon 	capacity_bytes = capacity * 512;
373258223a3SMatthew Dillon 
374258223a3SMatthew Dillon 	/*
375258223a3SMatthew Dillon 	 * Negotiate NCQ, throw away any ata_xfer's beyond the negotiated
376258223a3SMatthew Dillon 	 * number of slots and limit the number of CAM ccb's to one less
377258223a3SMatthew Dillon 	 * so we always have a slot available for recovery.
378258223a3SMatthew Dillon 	 *
379258223a3SMatthew Dillon 	 * NCQ is not used if ap_ncqdepth is 1 or the host controller does
380258223a3SMatthew Dillon 	 * not support it, and in that case the driver can handle extra
381258223a3SMatthew Dillon 	 * ccb's.
382cec85a37SMatthew Dillon 	 *
3831980eff3SMatthew Dillon 	 * NCQ is currently used only with direct-attached disks.  It is
3841980eff3SMatthew Dillon 	 * not used with port multipliers or direct-attached ATAPI devices.
3851980eff3SMatthew Dillon 	 *
386cec85a37SMatthew Dillon 	 * Remember at least one extra CCB needs to be reserved for the
387cec85a37SMatthew Dillon 	 * error ccb.
388258223a3SMatthew Dillon 	 */
389258223a3SMatthew Dillon 	if ((ap->ap_sc->sc_cap & AHCI_REG_CAP_SNCQ) &&
3901980eff3SMatthew Dillon 	    ap->ap_type == ATA_PORT_T_DISK &&
3911980eff3SMatthew Dillon 	    (le16toh(at->at_identify.satacap) & (1 << 8))) {
3921980eff3SMatthew Dillon 		at->at_ncqdepth = (le16toh(at->at_identify.qdepth) & 0x1F) + 1;
3931980eff3SMatthew Dillon 		devncqdepth = at->at_ncqdepth;
3941980eff3SMatthew Dillon 		if (at->at_ncqdepth > ap->ap_sc->sc_ncmds)
3951980eff3SMatthew Dillon 			at->at_ncqdepth = ap->ap_sc->sc_ncmds;
3961980eff3SMatthew Dillon 		if (at->at_ncqdepth > 1) {
397258223a3SMatthew Dillon 			for (i = 0; i < ap->ap_sc->sc_ncmds; ++i) {
3981980eff3SMatthew Dillon 				xa = ahci_ata_get_xfer(ap, atx);
3991980eff3SMatthew Dillon 				if (xa->tag < at->at_ncqdepth) {
400258223a3SMatthew Dillon 					xa->state = ATA_S_COMPLETE;
401258223a3SMatthew Dillon 					ahci_ata_put_xfer(xa);
402258223a3SMatthew Dillon 				}
403258223a3SMatthew Dillon 			}
4041980eff3SMatthew Dillon 			if (at->at_ncqdepth >= ap->ap_sc->sc_ncmds) {
405258223a3SMatthew Dillon 				cam_devq_resize(ap->ap_sim->devq,
4061980eff3SMatthew Dillon 						at->at_ncqdepth - 1);
407258223a3SMatthew Dillon 			}
408cec85a37SMatthew Dillon 		}
409258223a3SMatthew Dillon 	} else {
410258223a3SMatthew Dillon 		devncqdepth = 0;
411258223a3SMatthew Dillon 	}
412258223a3SMatthew Dillon 
413*6e0003adSMatthew Dillon 	model_len = sizeof(at->at_identify.model);
414*6e0003adSMatthew Dillon 	model_id = at->at_identify.model;
415*6e0003adSMatthew Dillon 	ahci_strip_string(&model_id, &model_len);
416*6e0003adSMatthew Dillon 
417*6e0003adSMatthew Dillon 	firmware_len = sizeof(at->at_identify.firmware);
418*6e0003adSMatthew Dillon 	firmware_id = at->at_identify.firmware;
419*6e0003adSMatthew Dillon 	ahci_strip_string(&firmware_id, &firmware_len);
420*6e0003adSMatthew Dillon 
421*6e0003adSMatthew Dillon 	serial_len = sizeof(at->at_identify.serial);
422*6e0003adSMatthew Dillon 	serial_id = at->at_identify.serial;
423*6e0003adSMatthew Dillon 	ahci_strip_string(&serial_id, &serial_len);
424669fbbf7SMatthew Dillon 
425fd8bd957SMatthew Dillon 	/*
426fd8bd957SMatthew Dillon 	 * Generate informatiive strings.
427fd8bd957SMatthew Dillon 	 *
428fd8bd957SMatthew Dillon 	 * NOTE: We do not automatically set write caching, lookahead,
429fd8bd957SMatthew Dillon 	 *	 or the security state for ATAPI devices.
430fd8bd957SMatthew Dillon 	 */
4311980eff3SMatthew Dillon 	if (at->at_identify.cmdset82 & ATA_IDENTIFY_WRITECACHE) {
4321980eff3SMatthew Dillon 		if (at->at_identify.features85 & ATA_IDENTIFY_WRITECACHE)
433669fbbf7SMatthew Dillon 			wcstr = "enabled";
4341980eff3SMatthew Dillon 		else if (at->at_type == ATA_PORT_T_ATAPI)
435fd8bd957SMatthew Dillon 			wcstr = "disabled";
436669fbbf7SMatthew Dillon 		else
437669fbbf7SMatthew Dillon 			wcstr = "enabling";
438669fbbf7SMatthew Dillon 	} else {
439669fbbf7SMatthew Dillon 		    wcstr = "notsupp";
440669fbbf7SMatthew Dillon 	}
441669fbbf7SMatthew Dillon 
4421980eff3SMatthew Dillon 	if (at->at_identify.cmdset82 & ATA_IDENTIFY_LOOKAHEAD) {
4431980eff3SMatthew Dillon 		if (at->at_identify.features85 & ATA_IDENTIFY_LOOKAHEAD)
444669fbbf7SMatthew Dillon 			rastr = "enabled";
4451980eff3SMatthew Dillon 		else if (at->at_type == ATA_PORT_T_ATAPI)
446fd8bd957SMatthew Dillon 			rastr = "disabled";
447669fbbf7SMatthew Dillon 		else
448669fbbf7SMatthew Dillon 			rastr = "enabling";
449669fbbf7SMatthew Dillon 	} else {
450669fbbf7SMatthew Dillon 		    rastr = "notsupp";
451669fbbf7SMatthew Dillon 	}
452669fbbf7SMatthew Dillon 
4531980eff3SMatthew Dillon 	if (at->at_identify.cmdset82 & ATA_IDENTIFY_SECURITY) {
4541980eff3SMatthew Dillon 		if (at->at_identify.securestatus & ATA_SECURE_FROZEN)
455fd8bd957SMatthew Dillon 			scstr = "frozen";
4561980eff3SMatthew Dillon 		else if (at->at_type == ATA_PORT_T_ATAPI)
457fd8bd957SMatthew Dillon 			scstr = "unfrozen";
458afa796d2SMatthew Dillon 		else if (AhciNoFeatures & (1 << ap->ap_num))
459afa796d2SMatthew Dillon 			scstr = "<disabled>";
460fd8bd957SMatthew Dillon 		else
461fd8bd957SMatthew Dillon 			scstr = "freezing";
462fd8bd957SMatthew Dillon 	} else {
463fd8bd957SMatthew Dillon 		    scstr = "notsupp";
464fd8bd957SMatthew Dillon 	}
465fd8bd957SMatthew Dillon 
466*6e0003adSMatthew Dillon 	kprintf("%s: Found %s \"%*.*s %*.*s\" serial=\"%*.*s\"\n"
467074579dfSMatthew Dillon 		"%s: tags=%d/%d satacap=%04x satafea=%04x NCQ=%s "
468074579dfSMatthew Dillon 		"capacity=%lld.%02dMB\n",
469074579dfSMatthew Dillon 
4701980eff3SMatthew Dillon 		ATANAME(ap, atx),
471fd8bd957SMatthew Dillon 		type,
472*6e0003adSMatthew Dillon 		model_len, model_len, model_id,
473*6e0003adSMatthew Dillon 		firmware_len, firmware_len, firmware_id,
474*6e0003adSMatthew Dillon 		serial_len, serial_len, serial_id,
475258223a3SMatthew Dillon 
4761980eff3SMatthew Dillon 		ATANAME(ap, atx),
477258223a3SMatthew Dillon 		devncqdepth, ap->ap_sc->sc_ncmds,
4781980eff3SMatthew Dillon 		at->at_identify.satacap,
4791980eff3SMatthew Dillon 		at->at_identify.satafsup,
480074579dfSMatthew Dillon 		(at->at_ncqdepth > 1 ? "YES" : "NO"),
481258223a3SMatthew Dillon 		(long long)capacity_bytes / (1024 * 1024),
482074579dfSMatthew Dillon 		(int)(capacity_bytes % (1024 * 1024)) * 100 / (1024 * 1024)
483074579dfSMatthew Dillon 	);
484074579dfSMatthew Dillon 	kprintf("%s: f85=%04x f86=%04x f87=%04x WC=%s RA=%s SEC=%s\n",
4851980eff3SMatthew Dillon 		ATANAME(ap, atx),
4861980eff3SMatthew Dillon 		at->at_identify.features85,
4871980eff3SMatthew Dillon 		at->at_identify.features86,
4881980eff3SMatthew Dillon 		at->at_identify.features87,
489669fbbf7SMatthew Dillon 		wcstr,
490fd8bd957SMatthew Dillon 		rastr,
491fd8bd957SMatthew Dillon 		scstr
492258223a3SMatthew Dillon 	);
493258223a3SMatthew Dillon 
494258223a3SMatthew Dillon 	/*
495fd8bd957SMatthew Dillon 	 * Additional type-specific probing
496fd8bd957SMatthew Dillon 	 */
4971980eff3SMatthew Dillon 	switch(at->at_type) {
498fd8bd957SMatthew Dillon 	case ATA_PORT_T_DISK:
4991980eff3SMatthew Dillon 		error = ahci_cam_probe_disk(ap, atx);
5001980eff3SMatthew Dillon 		break;
5011980eff3SMatthew Dillon 	case ATA_PORT_T_ATAPI:
5021980eff3SMatthew Dillon 		error = ahci_cam_probe_atapi(ap, atx);
503fd8bd957SMatthew Dillon 		break;
504fd8bd957SMatthew Dillon 	default:
5051980eff3SMatthew Dillon 		error = EIO;
506fd8bd957SMatthew Dillon 		break;
507fd8bd957SMatthew Dillon 	}
5083209f581SMatthew Dillon err:
5093209f581SMatthew Dillon 	if (error) {
5103209f581SMatthew Dillon 		at->at_probe = ATA_PROBE_FAILED;
5113209f581SMatthew Dillon 		if (atx == NULL)
5123209f581SMatthew Dillon 			ap->ap_probe = at->at_probe;
5133209f581SMatthew Dillon 	} else {
5143209f581SMatthew Dillon 		at->at_probe = ATA_PROBE_GOOD;
5153209f581SMatthew Dillon 		if (atx == NULL)
5163209f581SMatthew Dillon 			ap->ap_probe = at->at_probe;
5173209f581SMatthew Dillon 	}
5183209f581SMatthew Dillon 	return (error);
519fd8bd957SMatthew Dillon }
520fd8bd957SMatthew Dillon 
521fd8bd957SMatthew Dillon /*
522fd8bd957SMatthew Dillon  * DISK-specific probe after initial ident
523fd8bd957SMatthew Dillon  */
524fd8bd957SMatthew Dillon static int
5251980eff3SMatthew Dillon ahci_cam_probe_disk(struct ahci_port *ap, struct ata_port *atx)
526fd8bd957SMatthew Dillon {
5271980eff3SMatthew Dillon 	struct ata_port *at;
528fd8bd957SMatthew Dillon 	struct ata_xfer	*xa;
529fd8bd957SMatthew Dillon 
5301980eff3SMatthew Dillon 	at = atx ? atx : ap->ap_ata;
5311980eff3SMatthew Dillon 
532fd8bd957SMatthew Dillon 	/*
533258223a3SMatthew Dillon 	 * Enable write cache if supported
534fd8bd957SMatthew Dillon 	 *
535fd8bd957SMatthew Dillon 	 * NOTE: "WD My Book" external disk devices have a very poor
536fd8bd957SMatthew Dillon 	 *	 daughter board between the the ESATA and the HD.  Sending
537fd8bd957SMatthew Dillon 	 *	 any ATA_C_SET_FEATURES commands will break the hardware port
538fd8bd957SMatthew Dillon 	 *	 with a fatal protocol error.  However, this device also
539fd8bd957SMatthew Dillon 	 *	 indicates that WRITECACHE is already on and READAHEAD is
540fd8bd957SMatthew Dillon 	 *	 not supported so we avoid the issue.
541258223a3SMatthew Dillon 	 */
5421980eff3SMatthew Dillon 	if ((at->at_identify.cmdset82 & ATA_IDENTIFY_WRITECACHE) &&
5431980eff3SMatthew Dillon 	    (at->at_identify.features85 & ATA_IDENTIFY_WRITECACHE) == 0) {
5441980eff3SMatthew Dillon 		xa = ahci_ata_get_xfer(ap, atx);
545258223a3SMatthew Dillon 		xa->complete = ahci_ata_dummy_done;
546258223a3SMatthew Dillon 		xa->fis->command = ATA_C_SET_FEATURES;
547669fbbf7SMatthew Dillon 		/*xa->fis->features = ATA_SF_WRITECACHE_EN;*/
548669fbbf7SMatthew Dillon 		xa->fis->features = ATA_SF_LOOKAHEAD_EN;
5491980eff3SMatthew Dillon 		xa->fis->flags = ATA_H2D_FLAGS_CMD | at->at_target;
550669fbbf7SMatthew Dillon 		xa->fis->device = 0;
551258223a3SMatthew Dillon 		xa->flags = ATA_F_READ | ATA_F_PIO | ATA_F_POLL;
5523209f581SMatthew Dillon 		xa->timeout = 1000;
553669fbbf7SMatthew Dillon 		xa->datalen = 0;
554831bc9e3SMatthew Dillon 		if (ahci_ata_cmd(xa) == ATA_S_COMPLETE)
5551980eff3SMatthew Dillon 			at->at_features |= ATA_PORT_F_WCACHE;
556afa796d2SMatthew Dillon 		else
557afa796d2SMatthew Dillon 			kprintf("%s: Unable to enable write-caching\n",
558afa796d2SMatthew Dillon 				ATANAME(ap, atx));
559258223a3SMatthew Dillon 		ahci_ata_put_xfer(xa);
560258223a3SMatthew Dillon 	}
561258223a3SMatthew Dillon 
562258223a3SMatthew Dillon 	/*
563258223a3SMatthew Dillon 	 * Enable readahead if supported
564258223a3SMatthew Dillon 	 */
5651980eff3SMatthew Dillon 	if ((at->at_identify.cmdset82 & ATA_IDENTIFY_LOOKAHEAD) &&
5661980eff3SMatthew Dillon 	    (at->at_identify.features85 & ATA_IDENTIFY_LOOKAHEAD) == 0) {
5671980eff3SMatthew Dillon 		xa = ahci_ata_get_xfer(ap, atx);
568258223a3SMatthew Dillon 		xa->complete = ahci_ata_dummy_done;
569258223a3SMatthew Dillon 		xa->fis->command = ATA_C_SET_FEATURES;
570258223a3SMatthew Dillon 		xa->fis->features = ATA_SF_LOOKAHEAD_EN;
5711980eff3SMatthew Dillon 		xa->fis->flags = ATA_H2D_FLAGS_CMD | at->at_target;
572669fbbf7SMatthew Dillon 		xa->fis->device = 0;
573258223a3SMatthew Dillon 		xa->flags = ATA_F_READ | ATA_F_PIO | ATA_F_POLL;
5743209f581SMatthew Dillon 		xa->timeout = 1000;
575669fbbf7SMatthew Dillon 		xa->datalen = 0;
576831bc9e3SMatthew Dillon 		if (ahci_ata_cmd(xa) == ATA_S_COMPLETE)
5771980eff3SMatthew Dillon 			at->at_features |= ATA_PORT_F_RAHEAD;
578afa796d2SMatthew Dillon 		else
579afa796d2SMatthew Dillon 			kprintf("%s: Unable to enable read-ahead\n",
580afa796d2SMatthew Dillon 				ATANAME(ap, atx));
581258223a3SMatthew Dillon 		ahci_ata_put_xfer(xa);
582258223a3SMatthew Dillon 	}
583258223a3SMatthew Dillon 
584258223a3SMatthew Dillon 	/*
585258223a3SMatthew Dillon 	 * FREEZE LOCK the device so malicious users can't lock it on us.
586258223a3SMatthew Dillon 	 * As there is no harm in issuing this to devices that don't
587258223a3SMatthew Dillon 	 * support the security feature set we just send it, and don't bother
588258223a3SMatthew Dillon 	 * checking if the device sends a command abort to tell us it doesn't
589258223a3SMatthew Dillon 	 * support it
590258223a3SMatthew Dillon 	 */
5911980eff3SMatthew Dillon 	if ((at->at_identify.cmdset82 & ATA_IDENTIFY_SECURITY) &&
592afa796d2SMatthew Dillon 	    (at->at_identify.securestatus & ATA_SECURE_FROZEN) == 0 &&
593afa796d2SMatthew Dillon 	    (AhciNoFeatures & (1 << ap->ap_num)) == 0) {
5941980eff3SMatthew Dillon 		xa = ahci_ata_get_xfer(ap, atx);
595258223a3SMatthew Dillon 		xa->complete = ahci_ata_dummy_done;
596258223a3SMatthew Dillon 		xa->fis->command = ATA_C_SEC_FREEZE_LOCK;
5971980eff3SMatthew Dillon 		xa->fis->flags = ATA_H2D_FLAGS_CMD | at->at_target;
598258223a3SMatthew Dillon 		xa->flags = ATA_F_READ | ATA_F_PIO | ATA_F_POLL;
5993209f581SMatthew Dillon 		xa->timeout = 1000;
600669fbbf7SMatthew Dillon 		xa->datalen = 0;
601831bc9e3SMatthew Dillon 		if (ahci_ata_cmd(xa) == ATA_S_COMPLETE)
6021980eff3SMatthew Dillon 			at->at_features |= ATA_PORT_F_FRZLCK;
603afa796d2SMatthew Dillon 		else
604afa796d2SMatthew Dillon 			kprintf("%s: Unable to set security freeze\n",
605afa796d2SMatthew Dillon 				ATANAME(ap, atx));
606258223a3SMatthew Dillon 		ahci_ata_put_xfer(xa);
607669fbbf7SMatthew Dillon 	}
608258223a3SMatthew Dillon 
609b4189e5eSMatthew Dillon 	return (0);
610b4189e5eSMatthew Dillon }
611b4189e5eSMatthew Dillon 
612fd8bd957SMatthew Dillon /*
613fd8bd957SMatthew Dillon  * ATAPI-specific probe after initial ident
614fd8bd957SMatthew Dillon  */
615b4189e5eSMatthew Dillon static int
6161980eff3SMatthew Dillon ahci_cam_probe_atapi(struct ahci_port *ap, struct ata_port *atx)
617b4189e5eSMatthew Dillon {
618fd8bd957SMatthew Dillon 	return(0);
619fd8bd957SMatthew Dillon }
620fd8bd957SMatthew Dillon 
621b4189e5eSMatthew Dillon /*
622b4189e5eSMatthew Dillon  * Fix byte ordering so buffers can be accessed as
623b4189e5eSMatthew Dillon  * strings.
624b4189e5eSMatthew Dillon  */
625258223a3SMatthew Dillon static void
626258223a3SMatthew Dillon ata_fix_identify(struct ata_identify *id)
627258223a3SMatthew Dillon {
628258223a3SMatthew Dillon 	u_int16_t	*swap;
629258223a3SMatthew Dillon 	int		i;
630258223a3SMatthew Dillon 
631258223a3SMatthew Dillon 	swap = (u_int16_t *)id->serial;
632258223a3SMatthew Dillon 	for (i = 0; i < sizeof(id->serial) / sizeof(u_int16_t); i++)
633258223a3SMatthew Dillon 		swap[i] = bswap16(swap[i]);
634258223a3SMatthew Dillon 
635258223a3SMatthew Dillon 	swap = (u_int16_t *)id->firmware;
636258223a3SMatthew Dillon 	for (i = 0; i < sizeof(id->firmware) / sizeof(u_int16_t); i++)
637258223a3SMatthew Dillon 		swap[i] = bswap16(swap[i]);
638258223a3SMatthew Dillon 
639258223a3SMatthew Dillon 	swap = (u_int16_t *)id->model;
640258223a3SMatthew Dillon 	for (i = 0; i < sizeof(id->model) / sizeof(u_int16_t); i++)
641258223a3SMatthew Dillon 		swap[i] = bswap16(swap[i]);
642258223a3SMatthew Dillon }
643258223a3SMatthew Dillon 
644258223a3SMatthew Dillon /*
645b4189e5eSMatthew Dillon  * Dummy done callback for xa.
646b4189e5eSMatthew Dillon  */
647b4189e5eSMatthew Dillon static void
648b4189e5eSMatthew Dillon ahci_ata_dummy_done(struct ata_xfer *xa)
649b4189e5eSMatthew Dillon {
650b4189e5eSMatthew Dillon }
651b4189e5eSMatthew Dillon 
652b4189e5eSMatthew Dillon /*
6533209f581SMatthew Dillon  * Use an engineering request to initiate a target scan for devices
6543209f581SMatthew Dillon  * behind a port multiplier.
655fd8bd957SMatthew Dillon  *
6563209f581SMatthew Dillon  * An asynchronous bus scan is used to avoid reentrancy issues.
657258223a3SMatthew Dillon  */
658258223a3SMatthew Dillon static void
659258223a3SMatthew Dillon ahci_cam_rescan_callback(struct cam_periph *periph, union ccb *ccb)
660258223a3SMatthew Dillon {
6613209f581SMatthew Dillon 	struct ahci_port *ap = ccb->ccb_h.sim_priv.entries[0].ptr;
6623209f581SMatthew Dillon 
663f4553de1SMatthew Dillon 	if (ccb->ccb_h.func_code == XPT_SCAN_BUS) {
6643209f581SMatthew Dillon 		ap->ap_flags &= ~AP_F_SCAN_RUNNING;
6653209f581SMatthew Dillon 		if (ap->ap_flags & AP_F_SCAN_REQUESTED) {
6663209f581SMatthew Dillon 			ap->ap_flags &= ~AP_F_SCAN_REQUESTED;
6673209f581SMatthew Dillon 			ahci_cam_rescan(ap);
6683209f581SMatthew Dillon 		}
669f4553de1SMatthew Dillon 		ap->ap_flags |= AP_F_SCAN_COMPLETED;
670f4553de1SMatthew Dillon 		wakeup(&ap->ap_flags);
671f4553de1SMatthew Dillon 	}
672f4553de1SMatthew Dillon 	xpt_free_ccb(ccb);
673258223a3SMatthew Dillon }
674258223a3SMatthew Dillon 
675258223a3SMatthew Dillon static void
676258223a3SMatthew Dillon ahci_cam_rescan(struct ahci_port *ap)
677258223a3SMatthew Dillon {
678258223a3SMatthew Dillon 	struct cam_path *path;
679258223a3SMatthew Dillon 	union ccb *ccb;
680258223a3SMatthew Dillon 	int status;
6813209f581SMatthew Dillon 	int i;
6823209f581SMatthew Dillon 
6833209f581SMatthew Dillon 	if (ap->ap_flags & AP_F_SCAN_RUNNING) {
6843209f581SMatthew Dillon 		ap->ap_flags |= AP_F_SCAN_REQUESTED;
6853209f581SMatthew Dillon 		return;
6863209f581SMatthew Dillon 	}
6873209f581SMatthew Dillon 	ap->ap_flags |= AP_F_SCAN_RUNNING;
6883209f581SMatthew Dillon 	for (i = 0; i < AHCI_MAX_PMPORTS; ++i) {
6893209f581SMatthew Dillon 		ap->ap_ata[i].at_features |= ATA_PORT_F_RESCAN;
6903209f581SMatthew Dillon 	}
691258223a3SMatthew Dillon 
692258223a3SMatthew Dillon 	status = xpt_create_path(&path, xpt_periph, cam_sim_path(ap->ap_sim),
693258223a3SMatthew Dillon 				 CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD);
694258223a3SMatthew Dillon 	if (status != CAM_REQ_CMP)
695258223a3SMatthew Dillon 		return;
696258223a3SMatthew Dillon 
697f4553de1SMatthew Dillon 	ccb = xpt_alloc_ccb();
698258223a3SMatthew Dillon 	xpt_setup_ccb(&ccb->ccb_h, path, 5);	/* 5 = low priority */
6992de5e9baSMatthew Dillon 	ccb->ccb_h.func_code = XPT_ENG_EXEC;
700258223a3SMatthew Dillon 	ccb->ccb_h.cbfcnp = ahci_cam_rescan_callback;
7013209f581SMatthew Dillon 	ccb->ccb_h.sim_priv.entries[0].ptr = ap;
702258223a3SMatthew Dillon 	ccb->crcn.flags = CAM_FLAG_NONE;
7032de5e9baSMatthew Dillon 	xpt_action_async(ccb);
704258223a3SMatthew Dillon }
705258223a3SMatthew Dillon 
7063209f581SMatthew Dillon static void
7073209f581SMatthew Dillon ahci_xpt_rescan(struct ahci_port *ap)
7083209f581SMatthew Dillon {
7093209f581SMatthew Dillon 	struct cam_path *path;
7103209f581SMatthew Dillon 	union ccb *ccb;
7113209f581SMatthew Dillon 	int status;
7123209f581SMatthew Dillon 
7133209f581SMatthew Dillon 	status = xpt_create_path(&path, xpt_periph, cam_sim_path(ap->ap_sim),
7143209f581SMatthew Dillon 				 CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD);
7153209f581SMatthew Dillon 	if (status != CAM_REQ_CMP)
7163209f581SMatthew Dillon 		return;
717f4553de1SMatthew Dillon 
718f4553de1SMatthew Dillon 	ccb = xpt_alloc_ccb();
7193209f581SMatthew Dillon 	xpt_setup_ccb(&ccb->ccb_h, path, 5);	/* 5 = low priority */
7202de5e9baSMatthew Dillon 	ccb->ccb_h.func_code = XPT_SCAN_BUS;
7213209f581SMatthew Dillon 	ccb->ccb_h.cbfcnp = ahci_cam_rescan_callback;
7223209f581SMatthew Dillon 	ccb->ccb_h.sim_priv.entries[0].ptr = ap;
7233209f581SMatthew Dillon 	ccb->crcn.flags = CAM_FLAG_NONE;
724baef7501SMatthew Dillon 	xpt_action_async(ccb);
7253209f581SMatthew Dillon }
7263209f581SMatthew Dillon 
727258223a3SMatthew Dillon /*
728258223a3SMatthew Dillon  * Action function - dispatch command
729258223a3SMatthew Dillon  */
730258223a3SMatthew Dillon static
731258223a3SMatthew Dillon void
732258223a3SMatthew Dillon ahci_xpt_action(struct cam_sim *sim, union ccb *ccb)
733258223a3SMatthew Dillon {
734258223a3SMatthew Dillon 	struct ahci_port *ap;
7351980eff3SMatthew Dillon 	struct ata_port	 *at, *atx;
736258223a3SMatthew Dillon 	struct ccb_hdr *ccbh;
737258223a3SMatthew Dillon 	int unit;
738258223a3SMatthew Dillon 
739258223a3SMatthew Dillon 	/* XXX lock */
740258223a3SMatthew Dillon 	ap = cam_sim_softc(sim);
7411980eff3SMatthew Dillon 	at = ap->ap_ata;
7421980eff3SMatthew Dillon 	atx = NULL;
743258223a3SMatthew Dillon 	KKASSERT(ap != NULL);
744258223a3SMatthew Dillon 	ccbh = &ccb->ccb_h;
745258223a3SMatthew Dillon 	unit = cam_sim_unit(sim);
746258223a3SMatthew Dillon 
747258223a3SMatthew Dillon 	/*
7483209f581SMatthew Dillon 	 * Early failure checks.  These checks do not apply to XPT_PATH_INQ,
7493209f581SMatthew Dillon 	 * otherwise the bus rescan will not remove the dead devices when
7503209f581SMatthew Dillon 	 * unplugging a PM.
7513209f581SMatthew Dillon 	 *
7521980eff3SMatthew Dillon 	 * For non-wildcards we have one target (0) and one lun (0),
7531980eff3SMatthew Dillon 	 * unless we have a port multiplier.
7541980eff3SMatthew Dillon 	 *
7551980eff3SMatthew Dillon 	 * A wildcard target indicates only the general bus is being
7561980eff3SMatthew Dillon 	 * probed.
7571980eff3SMatthew Dillon 	 *
7581980eff3SMatthew Dillon 	 * Calculate at and atx.  at is always non-NULL.  atx is only
7591980eff3SMatthew Dillon 	 * non-NULL for direct-attached devices.  It will be NULL for
7601980eff3SMatthew Dillon 	 * devices behind a port multiplier.
761258223a3SMatthew Dillon 	 *
762258223a3SMatthew Dillon 	 * XXX What do we do with a LUN wildcard?
763258223a3SMatthew Dillon 	 */
7643209f581SMatthew Dillon 	if (ccbh->target_id != CAM_TARGET_WILDCARD &&
7653209f581SMatthew Dillon 	    ccbh->func_code != XPT_PATH_INQ) {
7661980eff3SMatthew Dillon 		if (ap->ap_type == ATA_PORT_T_NONE) {
7673209f581SMatthew Dillon 			ccbh->status = CAM_DEV_NOT_THERE;
768258223a3SMatthew Dillon 			xpt_done(ccb);
769258223a3SMatthew Dillon 			return;
770258223a3SMatthew Dillon 		}
7711980eff3SMatthew Dillon 		if (ccbh->target_id < 0 || ccbh->target_id >= ap->ap_pmcount) {
772258223a3SMatthew Dillon 			ccbh->status = CAM_DEV_NOT_THERE;
773258223a3SMatthew Dillon 			xpt_done(ccb);
774258223a3SMatthew Dillon 			return;
775258223a3SMatthew Dillon 		}
7761980eff3SMatthew Dillon 		at += ccbh->target_id;
7771980eff3SMatthew Dillon 		if (ap->ap_type == ATA_PORT_T_PM)
7781980eff3SMatthew Dillon 			atx = at;
7791980eff3SMatthew Dillon 
780258223a3SMatthew Dillon 		if (ccbh->target_lun != CAM_LUN_WILDCARD && ccbh->target_lun) {
781258223a3SMatthew Dillon 			ccbh->status = CAM_DEV_NOT_THERE;
782258223a3SMatthew Dillon 			xpt_done(ccb);
783258223a3SMatthew Dillon 			return;
784258223a3SMatthew Dillon 		}
785258223a3SMatthew Dillon 	}
786258223a3SMatthew Dillon 
787258223a3SMatthew Dillon 	/*
788258223a3SMatthew Dillon 	 * Switch on the meta XPT command
789258223a3SMatthew Dillon 	 */
790258223a3SMatthew Dillon 	switch(ccbh->func_code) {
7913209f581SMatthew Dillon 	case XPT_ENG_EXEC:
7923209f581SMatthew Dillon 		/*
7933209f581SMatthew Dillon 		 * This routine is called after a port multiplier has been
7943209f581SMatthew Dillon 		 * probed.
7953209f581SMatthew Dillon 		 */
7963209f581SMatthew Dillon 		ccbh->status = CAM_REQ_CMP;
797f4553de1SMatthew Dillon 		ahci_os_lock_port(ap);
798831bc9e3SMatthew Dillon 		ahci_port_state_machine(ap, 0);
799f4553de1SMatthew Dillon 		ahci_os_unlock_port(ap);
8003209f581SMatthew Dillon 		xpt_done(ccb);
8013209f581SMatthew Dillon 		ahci_xpt_rescan(ap);
8023209f581SMatthew Dillon 		break;
803258223a3SMatthew Dillon 	case XPT_PATH_INQ:
8043209f581SMatthew Dillon 		/*
8053209f581SMatthew Dillon 		 * This command always succeeds, otherwise the bus scan
8063209f581SMatthew Dillon 		 * will not detach dead devices.
8073209f581SMatthew Dillon 		 */
808258223a3SMatthew Dillon 		ccb->cpi.version_num = 1;
809258223a3SMatthew Dillon 		ccb->cpi.hba_inquiry = 0;
810258223a3SMatthew Dillon 		ccb->cpi.target_sprt = 0;
8111980eff3SMatthew Dillon 		ccb->cpi.hba_misc = PIM_SEQSCAN;
812258223a3SMatthew Dillon 		ccb->cpi.hba_eng_cnt = 0;
813258223a3SMatthew Dillon 		bzero(ccb->cpi.vuhba_flags, sizeof(ccb->cpi.vuhba_flags));
81476497a9cSMatthew Dillon 		ccb->cpi.max_target = AHCI_MAX_PMPORTS - 1;
815258223a3SMatthew Dillon 		ccb->cpi.max_lun = 0;
816258223a3SMatthew Dillon 		ccb->cpi.async_flags = 0;
817258223a3SMatthew Dillon 		ccb->cpi.hpath_id = 0;
8181980eff3SMatthew Dillon 		ccb->cpi.initiator_id = AHCI_MAX_PMPORTS - 1;
819258223a3SMatthew Dillon 		ccb->cpi.unit_number = cam_sim_unit(sim);
820258223a3SMatthew Dillon 		ccb->cpi.bus_id = cam_sim_bus(sim);
821258223a3SMatthew Dillon 		ccb->cpi.base_transfer_speed = 150000;
8222cc2e845SMatthew Dillon 		ccb->cpi.transport = XPORT_SATA;
823258223a3SMatthew Dillon 		ccb->cpi.transport_version = 1;
824258223a3SMatthew Dillon 		ccb->cpi.protocol = PROTO_SCSI;
825258223a3SMatthew Dillon 		ccb->cpi.protocol_version = SCSI_REV_2;
826258223a3SMatthew Dillon 
8273209f581SMatthew Dillon 		ccbh->status = CAM_REQ_CMP;
828831bc9e3SMatthew Dillon 		if (ccbh->target_id == CAM_TARGET_WILDCARD) {
829831bc9e3SMatthew Dillon 			ahci_os_lock_port(ap);
830831bc9e3SMatthew Dillon 			ahci_port_state_machine(ap, 0);
831831bc9e3SMatthew Dillon 			ahci_os_unlock_port(ap);
832831bc9e3SMatthew Dillon 		} else {
833258223a3SMatthew Dillon 			switch(ahci_pread(ap, AHCI_PREG_SSTS) &
834258223a3SMatthew Dillon 			       AHCI_PREG_SSTS_SPD) {
835258223a3SMatthew Dillon 			case AHCI_PREG_SSTS_SPD_GEN1:
836258223a3SMatthew Dillon 				ccb->cpi.base_transfer_speed = 150000;
837258223a3SMatthew Dillon 				break;
838258223a3SMatthew Dillon 			case AHCI_PREG_SSTS_SPD_GEN2:
839258223a3SMatthew Dillon 				ccb->cpi.base_transfer_speed = 300000;
840258223a3SMatthew Dillon 				break;
841258223a3SMatthew Dillon 			default:
842258223a3SMatthew Dillon 				/* unknown */
843258223a3SMatthew Dillon 				ccb->cpi.base_transfer_speed = 1000;
844258223a3SMatthew Dillon 				break;
845258223a3SMatthew Dillon 			}
8463209f581SMatthew Dillon #if 0
8471980eff3SMatthew Dillon 			if (ap->ap_type == ATA_PORT_T_NONE)
8481980eff3SMatthew Dillon 				ccbh->status = CAM_DEV_NOT_THERE;
8493209f581SMatthew Dillon #endif
8501980eff3SMatthew Dillon 		}
851258223a3SMatthew Dillon 		xpt_done(ccb);
852258223a3SMatthew Dillon 		break;
853258223a3SMatthew Dillon 	case XPT_RESET_DEV:
854f4553de1SMatthew Dillon 		ahci_os_lock_port(ap);
8551980eff3SMatthew Dillon 		if (ap->ap_type == ATA_PORT_T_NONE) {
8563209f581SMatthew Dillon 			ccbh->status = CAM_DEV_NOT_THERE;
8571980eff3SMatthew Dillon 		} else {
8581980eff3SMatthew Dillon 			ahci_port_reset(ap, atx, 0);
859fd8bd957SMatthew Dillon 			ccbh->status = CAM_REQ_CMP;
8601980eff3SMatthew Dillon 		}
861f4553de1SMatthew Dillon 		ahci_os_unlock_port(ap);
862258223a3SMatthew Dillon 		xpt_done(ccb);
863258223a3SMatthew Dillon 		break;
864258223a3SMatthew Dillon 	case XPT_RESET_BUS:
865f4553de1SMatthew Dillon 		ahci_os_lock_port(ap);
8661980eff3SMatthew Dillon 		ahci_port_reset(ap, NULL, 1);
867f4553de1SMatthew Dillon 		ahci_os_unlock_port(ap);
868fd8bd957SMatthew Dillon 		ccbh->status = CAM_REQ_CMP;
869258223a3SMatthew Dillon 		xpt_done(ccb);
870258223a3SMatthew Dillon 		break;
871258223a3SMatthew Dillon 	case XPT_SET_TRAN_SETTINGS:
872258223a3SMatthew Dillon 		ccbh->status = CAM_FUNC_NOTAVAIL;
873258223a3SMatthew Dillon 		xpt_done(ccb);
874258223a3SMatthew Dillon 		break;
875258223a3SMatthew Dillon 	case XPT_GET_TRAN_SETTINGS:
876258223a3SMatthew Dillon 		ccb->cts.protocol = PROTO_SCSI;
877258223a3SMatthew Dillon 		ccb->cts.protocol_version = SCSI_REV_2;
8782cc2e845SMatthew Dillon 		ccb->cts.transport = XPORT_SATA;
879258223a3SMatthew Dillon 		ccb->cts.transport_version = XPORT_VERSION_UNSPECIFIED;
880258223a3SMatthew Dillon 		ccb->cts.proto_specific.valid = 0;
881258223a3SMatthew Dillon 		ccb->cts.xport_specific.valid = 0;
882258223a3SMatthew Dillon 		ccbh->status = CAM_REQ_CMP;
883258223a3SMatthew Dillon 		xpt_done(ccb);
884258223a3SMatthew Dillon 		break;
885258223a3SMatthew Dillon 	case XPT_CALC_GEOMETRY:
886258223a3SMatthew Dillon 		cam_calc_geometry(&ccb->ccg, 1);
887258223a3SMatthew Dillon 		xpt_done(ccb);
888258223a3SMatthew Dillon 		break;
889258223a3SMatthew Dillon 	case XPT_SCSI_IO:
890f4553de1SMatthew Dillon 		/*
891f4553de1SMatthew Dillon 		 * Our parallel startup code might have only probed through
892f4553de1SMatthew Dillon 		 * to the IDENT, so do the last step if necessary.
893f4553de1SMatthew Dillon 		 */
894f4553de1SMatthew Dillon 		if (at->at_probe == ATA_PROBE_NEED_IDENT)
895f4553de1SMatthew Dillon 			ahci_cam_probe(ap, atx);
896f4553de1SMatthew Dillon 		if (at->at_probe != ATA_PROBE_GOOD) {
897f4553de1SMatthew Dillon 			ccbh->status = CAM_DEV_NOT_THERE;
898f4553de1SMatthew Dillon 			xpt_done(ccb);
899f4553de1SMatthew Dillon 			break;
900f4553de1SMatthew Dillon 		}
9011980eff3SMatthew Dillon 		switch(at->at_type) {
902258223a3SMatthew Dillon 		case ATA_PORT_T_DISK:
9031980eff3SMatthew Dillon 			ahci_xpt_scsi_disk_io(ap, atx, ccb);
904258223a3SMatthew Dillon 			break;
905258223a3SMatthew Dillon 		case ATA_PORT_T_ATAPI:
9061980eff3SMatthew Dillon 			ahci_xpt_scsi_atapi_io(ap, atx, ccb);
907258223a3SMatthew Dillon 			break;
908258223a3SMatthew Dillon 		default:
909258223a3SMatthew Dillon 			ccbh->status = CAM_REQ_INVALID;
910258223a3SMatthew Dillon 			xpt_done(ccb);
911258223a3SMatthew Dillon 			break;
912258223a3SMatthew Dillon 		}
913258223a3SMatthew Dillon 		break;
914258223a3SMatthew Dillon 	default:
915258223a3SMatthew Dillon 		ccbh->status = CAM_REQ_INVALID;
916258223a3SMatthew Dillon 		xpt_done(ccb);
917258223a3SMatthew Dillon 		break;
918258223a3SMatthew Dillon 	}
919258223a3SMatthew Dillon }
920258223a3SMatthew Dillon 
921258223a3SMatthew Dillon /*
922de68d532SMatthew Dillon  * Poll function.
923de68d532SMatthew Dillon  *
924de68d532SMatthew Dillon  * Generally this function gets called heavily when interrupts might be
925de68d532SMatthew Dillon  * non-operational, during a halt/reboot or panic.
926258223a3SMatthew Dillon  */
927258223a3SMatthew Dillon static
928258223a3SMatthew Dillon void
929258223a3SMatthew Dillon ahci_xpt_poll(struct cam_sim *sim)
930258223a3SMatthew Dillon {
931de68d532SMatthew Dillon 	struct ahci_port *ap;
932258223a3SMatthew Dillon 
933de68d532SMatthew Dillon 	ap = cam_sim_softc(sim);
934de68d532SMatthew Dillon 	crit_enter();
935f4553de1SMatthew Dillon 	ahci_os_lock_port(ap);
936f4553de1SMatthew Dillon 	ahci_port_intr(ap, 1);
937f4553de1SMatthew Dillon 	ahci_os_unlock_port(ap);
938de68d532SMatthew Dillon 	crit_exit();
939258223a3SMatthew Dillon }
940258223a3SMatthew Dillon 
941258223a3SMatthew Dillon /*
942b4189e5eSMatthew Dillon  * Convert the SCSI command in ccb to an ata_xfer command in xa
943b4189e5eSMatthew Dillon  * for ATA_PORT_T_DISK operations.  Set the completion function
944b4189e5eSMatthew Dillon  * to convert the response back, then dispatch to the OpenBSD AHCI
945b4189e5eSMatthew Dillon  * layer.
946258223a3SMatthew Dillon  *
947b4189e5eSMatthew Dillon  * AHCI DISK commands only support a limited command set, and we
948b4189e5eSMatthew Dillon  * fake additional commands to make it play nice with the CAM subsystem.
949258223a3SMatthew Dillon  */
950258223a3SMatthew Dillon static
951258223a3SMatthew Dillon void
9521980eff3SMatthew Dillon ahci_xpt_scsi_disk_io(struct ahci_port *ap, struct ata_port *atx,
9531980eff3SMatthew Dillon 		      union ccb *ccb)
954258223a3SMatthew Dillon {
955258223a3SMatthew Dillon 	struct ccb_hdr *ccbh;
956258223a3SMatthew Dillon 	struct ccb_scsiio *csio;
957258223a3SMatthew Dillon 	struct ata_xfer *xa;
9581980eff3SMatthew Dillon 	struct ata_port	*at;
959258223a3SMatthew Dillon 	struct ata_fis_h2d *fis;
960258223a3SMatthew Dillon 	scsi_cdb_t cdb;
961258223a3SMatthew Dillon 	union scsi_data *rdata;
962258223a3SMatthew Dillon 	int rdata_len;
963258223a3SMatthew Dillon 	u_int64_t capacity;
964258223a3SMatthew Dillon 	u_int64_t lba;
965258223a3SMatthew Dillon 	u_int32_t count;
966258223a3SMatthew Dillon 
967258223a3SMatthew Dillon 	ccbh = &ccb->csio.ccb_h;
968258223a3SMatthew Dillon 	csio = &ccb->csio;
9691980eff3SMatthew Dillon 	at = atx ? atx : &ap->ap_ata[0];
9701980eff3SMatthew Dillon 
9711980eff3SMatthew Dillon 	/*
9721980eff3SMatthew Dillon 	 * XXX not passing NULL at for direct attach!
9731980eff3SMatthew Dillon 	 */
9741980eff3SMatthew Dillon 	xa = ahci_ata_get_xfer(ap, atx);
975258223a3SMatthew Dillon 	rdata = (void *)csio->data_ptr;
976258223a3SMatthew Dillon 	rdata_len = csio->dxfer_len;
977258223a3SMatthew Dillon 
978258223a3SMatthew Dillon 	/*
979258223a3SMatthew Dillon 	 * Build the FIS or process the csio to completion.
980258223a3SMatthew Dillon 	 */
981258223a3SMatthew Dillon 	cdb = (void *)((ccbh->flags & CAM_CDB_POINTER) ?
982258223a3SMatthew Dillon 			csio->cdb_io.cdb_ptr : csio->cdb_io.cdb_bytes);
983258223a3SMatthew Dillon 
984258223a3SMatthew Dillon 	switch(cdb->generic.opcode) {
985258223a3SMatthew Dillon 	case REQUEST_SENSE:
986258223a3SMatthew Dillon 		/*
987258223a3SMatthew Dillon 		 * Auto-sense everything, so explicit sense requests
988258223a3SMatthew Dillon 		 * return no-sense.
989258223a3SMatthew Dillon 		 */
990258223a3SMatthew Dillon 		ccbh->status = CAM_SCSI_STATUS_ERROR;
991258223a3SMatthew Dillon 		break;
992258223a3SMatthew Dillon 	case INQUIRY:
993258223a3SMatthew Dillon 		/*
994258223a3SMatthew Dillon 		 * Inquiry supported features
995258223a3SMatthew Dillon 		 *
996258223a3SMatthew Dillon 		 * [opcode, byte2, page_code, length, control]
997258223a3SMatthew Dillon 		 */
998258223a3SMatthew Dillon 		if (cdb->inquiry.byte2 & SI_EVPD) {
999*6e0003adSMatthew Dillon 			ahci_xpt_page_inquiry(ap, at, ccb);
1000258223a3SMatthew Dillon 		} else {
1001258223a3SMatthew Dillon 			bzero(rdata, rdata_len);
1002258223a3SMatthew Dillon 			if (rdata_len < SHORT_INQUIRY_LENGTH) {
1003258223a3SMatthew Dillon 				ccbh->status = CAM_CCB_LEN_ERR;
1004258223a3SMatthew Dillon 				break;
1005258223a3SMatthew Dillon 			}
1006258223a3SMatthew Dillon 			if (rdata_len > sizeof(rdata->inquiry_data))
1007258223a3SMatthew Dillon 				rdata_len = sizeof(rdata->inquiry_data);
1008258223a3SMatthew Dillon 			rdata->inquiry_data.device = T_DIRECT;
1009258223a3SMatthew Dillon 			rdata->inquiry_data.version = SCSI_REV_SPC2;
1010258223a3SMatthew Dillon 			rdata->inquiry_data.response_format = 2;
1011258223a3SMatthew Dillon 			rdata->inquiry_data.additional_length = 32;
1012258223a3SMatthew Dillon 			bcopy("SATA    ", rdata->inquiry_data.vendor, 8);
10131980eff3SMatthew Dillon 			bcopy(at->at_identify.model,
1014258223a3SMatthew Dillon 			      rdata->inquiry_data.product,
1015258223a3SMatthew Dillon 			      sizeof(rdata->inquiry_data.product));
10161980eff3SMatthew Dillon 			bcopy(at->at_identify.firmware,
1017258223a3SMatthew Dillon 			      rdata->inquiry_data.revision,
1018258223a3SMatthew Dillon 			      sizeof(rdata->inquiry_data.revision));
1019258223a3SMatthew Dillon 			ccbh->status = CAM_REQ_CMP;
1020258223a3SMatthew Dillon 		}
1021258223a3SMatthew Dillon 		break;
1022258223a3SMatthew Dillon 	case READ_CAPACITY_16:
1023258223a3SMatthew Dillon 		if (cdb->read_capacity_16.service_action != SRC16_SERVICE_ACTION) {
1024258223a3SMatthew Dillon 			ccbh->status = CAM_REQ_INVALID;
1025258223a3SMatthew Dillon 			break;
1026258223a3SMatthew Dillon 		}
1027258223a3SMatthew Dillon 		if (rdata_len < sizeof(rdata->read_capacity_data_16)) {
1028258223a3SMatthew Dillon 			ccbh->status = CAM_CCB_LEN_ERR;
1029258223a3SMatthew Dillon 			break;
1030258223a3SMatthew Dillon 		}
1031258223a3SMatthew Dillon 		/* fall through */
1032258223a3SMatthew Dillon 	case READ_CAPACITY:
1033258223a3SMatthew Dillon 		if (rdata_len < sizeof(rdata->read_capacity_data)) {
1034258223a3SMatthew Dillon 			ccbh->status = CAM_CCB_LEN_ERR;
1035258223a3SMatthew Dillon 			break;
1036258223a3SMatthew Dillon 		}
1037258223a3SMatthew Dillon 
10381980eff3SMatthew Dillon 		capacity = at->at_capacity;
1039258223a3SMatthew Dillon 
1040258223a3SMatthew Dillon 		bzero(rdata, rdata_len);
1041258223a3SMatthew Dillon 		if (cdb->generic.opcode == READ_CAPACITY) {
1042258223a3SMatthew Dillon 			rdata_len = sizeof(rdata->read_capacity_data);
1043258223a3SMatthew Dillon 			if (capacity > 0xFFFFFFFFU)
1044258223a3SMatthew Dillon 				capacity = 0xFFFFFFFFU;
1045258223a3SMatthew Dillon 			bzero(&rdata->read_capacity_data, rdata_len);
1046258223a3SMatthew Dillon 			scsi_ulto4b((u_int32_t)capacity - 1,
1047258223a3SMatthew Dillon 				    rdata->read_capacity_data.addr);
1048258223a3SMatthew Dillon 			scsi_ulto4b(512, rdata->read_capacity_data.length);
1049258223a3SMatthew Dillon 		} else {
1050258223a3SMatthew Dillon 			rdata_len = sizeof(rdata->read_capacity_data_16);
1051258223a3SMatthew Dillon 			bzero(&rdata->read_capacity_data_16, rdata_len);
1052258223a3SMatthew Dillon 			scsi_u64to8b(capacity - 1,
1053258223a3SMatthew Dillon 				     rdata->read_capacity_data_16.addr);
1054258223a3SMatthew Dillon 			scsi_ulto4b(512, rdata->read_capacity_data_16.length);
1055258223a3SMatthew Dillon 		}
1056258223a3SMatthew Dillon 		ccbh->status = CAM_REQ_CMP;
1057258223a3SMatthew Dillon 		break;
1058258223a3SMatthew Dillon 	case SYNCHRONIZE_CACHE:
1059258223a3SMatthew Dillon 		/*
1060258223a3SMatthew Dillon 		 * Synchronize cache.  Specification says this can take
1061258223a3SMatthew Dillon 		 * greater then 30 seconds so give it at least 45.
1062258223a3SMatthew Dillon 		 */
1063258223a3SMatthew Dillon 		fis = xa->fis;
1064258223a3SMatthew Dillon 		fis->flags = ATA_H2D_FLAGS_CMD;
1065258223a3SMatthew Dillon 		fis->command = ATA_C_FLUSH_CACHE;
1066258223a3SMatthew Dillon 		fis->device = 0;
10673209f581SMatthew Dillon 		if (xa->timeout < 45000)
10683209f581SMatthew Dillon 			xa->timeout = 45000;
10691980eff3SMatthew Dillon 		xa->datalen = 0;
10701980eff3SMatthew Dillon 		xa->flags = ATA_F_READ;
10711980eff3SMatthew Dillon 		xa->complete = ahci_ata_complete_disk_synchronize_cache;
1072258223a3SMatthew Dillon 		break;
1073258223a3SMatthew Dillon 	case TEST_UNIT_READY:
1074258223a3SMatthew Dillon 	case START_STOP_UNIT:
1075258223a3SMatthew Dillon 	case PREVENT_ALLOW:
1076258223a3SMatthew Dillon 		/*
1077258223a3SMatthew Dillon 		 * Just silently return success
1078258223a3SMatthew Dillon 		 */
1079258223a3SMatthew Dillon 		ccbh->status = CAM_REQ_CMP;
1080258223a3SMatthew Dillon 		rdata_len = 0;
1081258223a3SMatthew Dillon 		break;
1082258223a3SMatthew Dillon 	case ATA_PASS_12:
1083258223a3SMatthew Dillon 	case ATA_PASS_16:
1084258223a3SMatthew Dillon 		/*
1085258223a3SMatthew Dillon 		 * XXX implement pass-through
1086258223a3SMatthew Dillon 		 */
1087258223a3SMatthew Dillon 		ccbh->status = CAM_FUNC_NOTAVAIL;
1088258223a3SMatthew Dillon 		break;
1089258223a3SMatthew Dillon 	default:
1090258223a3SMatthew Dillon 		switch(cdb->generic.opcode) {
1091258223a3SMatthew Dillon 		case READ_6:
1092258223a3SMatthew Dillon 			lba = scsi_3btoul(cdb->rw_6.addr) & 0x1FFFFF;
1093258223a3SMatthew Dillon 			count = cdb->rw_6.length ? cdb->rw_6.length : 0x100;
1094258223a3SMatthew Dillon 			xa->flags = ATA_F_READ;
1095258223a3SMatthew Dillon 			break;
1096258223a3SMatthew Dillon 		case READ_10:
1097258223a3SMatthew Dillon 			lba = scsi_4btoul(cdb->rw_10.addr);
1098258223a3SMatthew Dillon 			count = scsi_2btoul(cdb->rw_10.length);
1099258223a3SMatthew Dillon 			xa->flags = ATA_F_READ;
1100258223a3SMatthew Dillon 			break;
1101258223a3SMatthew Dillon 		case READ_12:
1102258223a3SMatthew Dillon 			lba = scsi_4btoul(cdb->rw_12.addr);
1103258223a3SMatthew Dillon 			count = scsi_4btoul(cdb->rw_12.length);
1104258223a3SMatthew Dillon 			xa->flags = ATA_F_READ;
1105258223a3SMatthew Dillon 			break;
1106258223a3SMatthew Dillon 		case READ_16:
1107258223a3SMatthew Dillon 			lba = scsi_8btou64(cdb->rw_16.addr);
1108258223a3SMatthew Dillon 			count = scsi_4btoul(cdb->rw_16.length);
1109258223a3SMatthew Dillon 			xa->flags = ATA_F_READ;
1110258223a3SMatthew Dillon 			break;
1111258223a3SMatthew Dillon 		case WRITE_6:
1112258223a3SMatthew Dillon 			lba = scsi_3btoul(cdb->rw_6.addr) & 0x1FFFFF;
1113258223a3SMatthew Dillon 			count = cdb->rw_6.length ? cdb->rw_6.length : 0x100;
1114258223a3SMatthew Dillon 			xa->flags = ATA_F_WRITE;
1115258223a3SMatthew Dillon 			break;
1116258223a3SMatthew Dillon 		case WRITE_10:
1117258223a3SMatthew Dillon 			lba = scsi_4btoul(cdb->rw_10.addr);
1118258223a3SMatthew Dillon 			count = scsi_2btoul(cdb->rw_10.length);
1119258223a3SMatthew Dillon 			xa->flags = ATA_F_WRITE;
1120258223a3SMatthew Dillon 			break;
1121258223a3SMatthew Dillon 		case WRITE_12:
1122258223a3SMatthew Dillon 			lba = scsi_4btoul(cdb->rw_12.addr);
1123258223a3SMatthew Dillon 			count = scsi_4btoul(cdb->rw_12.length);
1124258223a3SMatthew Dillon 			xa->flags = ATA_F_WRITE;
1125258223a3SMatthew Dillon 			break;
1126258223a3SMatthew Dillon 		case WRITE_16:
1127258223a3SMatthew Dillon 			lba = scsi_8btou64(cdb->rw_16.addr);
1128258223a3SMatthew Dillon 			count = scsi_4btoul(cdb->rw_16.length);
1129258223a3SMatthew Dillon 			xa->flags = ATA_F_WRITE;
1130258223a3SMatthew Dillon 			break;
1131258223a3SMatthew Dillon 		default:
1132258223a3SMatthew Dillon 			ccbh->status = CAM_REQ_INVALID;
1133258223a3SMatthew Dillon 			break;
1134258223a3SMatthew Dillon 		}
1135258223a3SMatthew Dillon 		if (ccbh->status != CAM_REQ_INPROG)
1136258223a3SMatthew Dillon 			break;
1137258223a3SMatthew Dillon 
1138258223a3SMatthew Dillon 		fis = xa->fis;
1139258223a3SMatthew Dillon 		fis->flags = ATA_H2D_FLAGS_CMD;
1140258223a3SMatthew Dillon 		fis->lba_low = (u_int8_t)lba;
1141258223a3SMatthew Dillon 		fis->lba_mid = (u_int8_t)(lba >> 8);
1142258223a3SMatthew Dillon 		fis->lba_high = (u_int8_t)(lba >> 16);
1143258223a3SMatthew Dillon 		fis->device = ATA_H2D_DEVICE_LBA;
1144258223a3SMatthew Dillon 
11451980eff3SMatthew Dillon 		/*
11461980eff3SMatthew Dillon 		 * NCQ only for direct-attached disks, do not currently
11471980eff3SMatthew Dillon 		 * try to use NCQ with port multipliers.
11481980eff3SMatthew Dillon 		 */
11491980eff3SMatthew Dillon 		if (at->at_ncqdepth > 1 &&
11501980eff3SMatthew Dillon 		    ap->ap_type == ATA_PORT_T_DISK &&
1151258223a3SMatthew Dillon 		    (ap->ap_sc->sc_cap & AHCI_REG_CAP_SNCQ) &&
1152258223a3SMatthew Dillon 		    (ccbh->flags & CAM_POLLED) == 0) {
1153258223a3SMatthew Dillon 			/*
1154258223a3SMatthew Dillon 			 * Use NCQ - always uses 48 bit addressing
1155258223a3SMatthew Dillon 			 */
1156258223a3SMatthew Dillon 			xa->flags |= ATA_F_NCQ;
1157258223a3SMatthew Dillon 			fis->command = (xa->flags & ATA_F_WRITE) ?
1158258223a3SMatthew Dillon 					ATA_C_WRITE_FPDMA : ATA_C_READ_FPDMA;
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 = xa->tag << 3;
1163258223a3SMatthew Dillon 			fis->features = (u_int8_t)count;
1164258223a3SMatthew Dillon 			fis->features_exp = (u_int8_t)(count >> 8);
1165258223a3SMatthew Dillon 		} else if (count > 0x100 || lba > 0xFFFFFFFFU) {
1166258223a3SMatthew Dillon 			/*
1167258223a3SMatthew Dillon 			 * Use LBA48
1168258223a3SMatthew Dillon 			 */
1169258223a3SMatthew Dillon 			fis->command = (xa->flags & ATA_F_WRITE) ?
1170258223a3SMatthew Dillon 					ATA_C_WRITEDMA_EXT : ATA_C_READDMA_EXT;
1171258223a3SMatthew Dillon 			fis->lba_low_exp = (u_int8_t)(lba >> 24);
1172258223a3SMatthew Dillon 			fis->lba_mid_exp = (u_int8_t)(lba >> 32);
1173258223a3SMatthew Dillon 			fis->lba_high_exp = (u_int8_t)(lba >> 40);
1174258223a3SMatthew Dillon 			fis->sector_count = (u_int8_t)count;
1175258223a3SMatthew Dillon 			fis->sector_count_exp = (u_int8_t)(count >> 8);
1176258223a3SMatthew Dillon 		} else {
1177258223a3SMatthew Dillon 			/*
1178258223a3SMatthew Dillon 			 * Use LBA
1179258223a3SMatthew Dillon 			 *
1180258223a3SMatthew Dillon 			 * NOTE: 256 sectors is supported, stored as 0.
1181258223a3SMatthew Dillon 			 */
1182258223a3SMatthew Dillon 			fis->command = (xa->flags & ATA_F_WRITE) ?
1183258223a3SMatthew Dillon 					ATA_C_WRITEDMA : ATA_C_READDMA;
1184258223a3SMatthew Dillon 			fis->device |= (u_int8_t)(lba >> 24) & 0x0F;
1185258223a3SMatthew Dillon 			fis->sector_count = (u_int8_t)count;
1186258223a3SMatthew Dillon 		}
1187258223a3SMatthew Dillon 
1188258223a3SMatthew Dillon 		xa->data = csio->data_ptr;
1189258223a3SMatthew Dillon 		xa->datalen = csio->dxfer_len;
1190258223a3SMatthew Dillon 		xa->complete = ahci_ata_complete_disk_rw;
11913209f581SMatthew Dillon 		xa->timeout = ccbh->timeout;	/* milliseconds */
119212feb904SMatthew Dillon #if 0
119312feb904SMatthew Dillon 		if (xa->timeout > 10000)	/* XXX - debug */
119412feb904SMatthew Dillon 			xa->timeout = 10000;
119512feb904SMatthew Dillon #endif
1196258223a3SMatthew Dillon 		if (ccbh->flags & CAM_POLLED)
1197258223a3SMatthew Dillon 			xa->flags |= ATA_F_POLL;
1198258223a3SMatthew Dillon 		break;
1199258223a3SMatthew Dillon 	}
1200258223a3SMatthew Dillon 
1201258223a3SMatthew Dillon 	/*
1202258223a3SMatthew Dillon 	 * If the request is still in progress the xa and FIS have
1203*6e0003adSMatthew Dillon 	 * been set up (except for the PM target), and must be dispatched.
1204*6e0003adSMatthew Dillon 	 * Otherwise the request was completed.
1205258223a3SMatthew Dillon 	 */
1206258223a3SMatthew Dillon 	if (ccbh->status == CAM_REQ_INPROG) {
1207258223a3SMatthew Dillon 		KKASSERT(xa->complete != NULL);
1208258223a3SMatthew Dillon 		xa->atascsi_private = ccb;
1209258223a3SMatthew Dillon 		ccb->ccb_h.sim_priv.entries[0].ptr = ap;
1210f4553de1SMatthew Dillon 		ahci_os_lock_port(ap);
1211*6e0003adSMatthew Dillon 		xa->fis->flags |= at->at_target;
1212258223a3SMatthew Dillon 		ahci_ata_cmd(xa);
1213f4553de1SMatthew Dillon 		ahci_os_unlock_port(ap);
1214258223a3SMatthew Dillon 	} else {
1215258223a3SMatthew Dillon 		ahci_ata_put_xfer(xa);
1216258223a3SMatthew Dillon 		xpt_done(ccb);
1217258223a3SMatthew Dillon 	}
1218258223a3SMatthew Dillon }
1219258223a3SMatthew Dillon 
1220b4189e5eSMatthew Dillon /*
1221b4189e5eSMatthew Dillon  * Convert the SCSI command in ccb to an ata_xfer command in xa
1222b4189e5eSMatthew Dillon  * for ATA_PORT_T_ATAPI operations.  Set the completion function
1223b4189e5eSMatthew Dillon  * to convert the response back, then dispatch to the OpenBSD AHCI
1224b4189e5eSMatthew Dillon  * layer.
1225b4189e5eSMatthew Dillon  */
1226258223a3SMatthew Dillon static
1227258223a3SMatthew Dillon void
12281980eff3SMatthew Dillon ahci_xpt_scsi_atapi_io(struct ahci_port *ap, struct ata_port *atx,
12291980eff3SMatthew Dillon 			union ccb *ccb)
1230258223a3SMatthew Dillon {
1231258223a3SMatthew Dillon 	struct ccb_hdr *ccbh;
1232258223a3SMatthew Dillon 	struct ccb_scsiio *csio;
1233258223a3SMatthew Dillon 	struct ata_xfer *xa;
1234258223a3SMatthew Dillon 	struct ata_fis_h2d *fis;
1235b4189e5eSMatthew Dillon 	scsi_cdb_t cdbs;
1236b4189e5eSMatthew Dillon 	scsi_cdb_t cdbd;
1237b4189e5eSMatthew Dillon 	int flags;
12381980eff3SMatthew Dillon 	struct ata_port	*at;
1239258223a3SMatthew Dillon 
1240258223a3SMatthew Dillon 	ccbh = &ccb->csio.ccb_h;
1241258223a3SMatthew Dillon 	csio = &ccb->csio;
12421980eff3SMatthew Dillon 	at = atx ? atx : &ap->ap_ata[0];
1243b4189e5eSMatthew Dillon 
1244b4189e5eSMatthew Dillon 	switch (ccbh->flags & CAM_DIR_MASK) {
1245b4189e5eSMatthew Dillon 	case CAM_DIR_IN:
1246b4189e5eSMatthew Dillon 		flags = ATA_F_PACKET | ATA_F_READ;
1247b4189e5eSMatthew Dillon 		break;
1248b4189e5eSMatthew Dillon 	case CAM_DIR_OUT:
1249b4189e5eSMatthew Dillon 		flags = ATA_F_PACKET | ATA_F_WRITE;
1250b4189e5eSMatthew Dillon 		break;
1251b4189e5eSMatthew Dillon 	case CAM_DIR_NONE:
1252b4189e5eSMatthew Dillon 		flags = ATA_F_PACKET;
1253b4189e5eSMatthew Dillon 		break;
1254b4189e5eSMatthew Dillon 	default:
1255b4189e5eSMatthew Dillon 		ccbh->status = CAM_REQ_INVALID;
1256b4189e5eSMatthew Dillon 		xpt_done(ccb);
1257b4189e5eSMatthew Dillon 		return;
1258b4189e5eSMatthew Dillon 		/* NOT REACHED */
1259b4189e5eSMatthew Dillon 	}
1260b4189e5eSMatthew Dillon 
1261b4189e5eSMatthew Dillon 	/*
126212feb904SMatthew Dillon 	 * Special handling to get the rfis back into host memory while
126312feb904SMatthew Dillon 	 * still allowing the Sili chip to run commands in parallel to
126412feb904SMatthew Dillon 	 * ATAPI devices behind a PM.
126512feb904SMatthew Dillon 	 */
126612feb904SMatthew Dillon 	flags |= ATA_F_AUTOSENSE;
126712feb904SMatthew Dillon 
126812feb904SMatthew Dillon 	/*
1269b4189e5eSMatthew Dillon 	 * The command has to fit in the packet command buffer.
1270b4189e5eSMatthew Dillon 	 */
1271b4189e5eSMatthew Dillon 	if (csio->cdb_len < 6 || csio->cdb_len > 16) {
1272b4189e5eSMatthew Dillon 		ccbh->status = CAM_CCB_LEN_ERR;
1273b4189e5eSMatthew Dillon 		xpt_done(ccb);
1274b4189e5eSMatthew Dillon 		return;
1275b4189e5eSMatthew Dillon 	}
1276b4189e5eSMatthew Dillon 
1277b4189e5eSMatthew Dillon 	/*
1278b4189e5eSMatthew Dillon 	 * Initialize the XA and FIS.
12791980eff3SMatthew Dillon 	 *
12801980eff3SMatthew Dillon 	 * XXX not passing NULL at for direct attach!
1281b4189e5eSMatthew Dillon 	 */
12821980eff3SMatthew Dillon 	xa = ahci_ata_get_xfer(ap, atx);
1283258223a3SMatthew Dillon 	fis = xa->fis;
1284258223a3SMatthew Dillon 
12851980eff3SMatthew Dillon 	fis->flags = ATA_H2D_FLAGS_CMD | at->at_target;
12861980eff3SMatthew Dillon 	fis->command = ATA_C_PACKET;
12871980eff3SMatthew Dillon 	fis->device = 0;
12881980eff3SMatthew Dillon 	fis->sector_count = xa->tag << 3;
12891980eff3SMatthew Dillon 	fis->features = ATA_H2D_FEATURES_DMA |
12901980eff3SMatthew Dillon 		    ((flags & ATA_F_WRITE) ?
12911980eff3SMatthew Dillon 		    ATA_H2D_FEATURES_DIR_WRITE : ATA_H2D_FEATURES_DIR_READ);
12921980eff3SMatthew Dillon 	fis->lba_mid = 0x00;
12931980eff3SMatthew Dillon 	fis->lba_high = 0x20;
12941980eff3SMatthew Dillon 
1295b4189e5eSMatthew Dillon 	xa->flags = flags;
1296b4189e5eSMatthew Dillon 	xa->data = csio->data_ptr;
1297b4189e5eSMatthew Dillon 	xa->datalen = csio->dxfer_len;
12983209f581SMatthew Dillon 	xa->timeout = ccbh->timeout;	/* milliseconds */
12991980eff3SMatthew Dillon 
1300b4189e5eSMatthew Dillon 	if (ccbh->flags & CAM_POLLED)
1301b4189e5eSMatthew Dillon 		xa->flags |= ATA_F_POLL;
1302258223a3SMatthew Dillon 
1303258223a3SMatthew Dillon 	/*
1304b4189e5eSMatthew Dillon 	 * Copy the cdb to the packetcmd buffer in the FIS using a
1305b4189e5eSMatthew Dillon 	 * convenient pointer in the xa.
1306258223a3SMatthew Dillon 	 */
1307b4189e5eSMatthew Dillon 	cdbs = (void *)((ccbh->flags & CAM_CDB_POINTER) ?
1308b4189e5eSMatthew Dillon 			csio->cdb_io.cdb_ptr : csio->cdb_io.cdb_bytes);
1309b4189e5eSMatthew Dillon 	bcopy(cdbs, xa->packetcmd, csio->cdb_len);
1310b4189e5eSMatthew Dillon 
1311669fbbf7SMatthew Dillon #if 0
1312b4189e5eSMatthew Dillon 	kprintf("opcode %d cdb_len %d dxfer_len %d\n",
1313b4189e5eSMatthew Dillon 		cdbs->generic.opcode,
1314b4189e5eSMatthew Dillon 		csio->cdb_len, csio->dxfer_len);
1315669fbbf7SMatthew Dillon #endif
1316b4189e5eSMatthew Dillon 
1317b4189e5eSMatthew Dillon 	/*
1318b4189e5eSMatthew Dillon 	 * Some ATAPI commands do not actually follow the SCSI standard.
1319b4189e5eSMatthew Dillon 	 */
1320b4189e5eSMatthew Dillon 	cdbd = (void *)xa->packetcmd;
1321b4189e5eSMatthew Dillon 
1322b4189e5eSMatthew Dillon 	switch(cdbd->generic.opcode) {
1323258223a3SMatthew Dillon 	case INQUIRY:
1324b4189e5eSMatthew Dillon 		/*
1325b4189e5eSMatthew Dillon 		 * Some ATAPI devices can't handle long inquiry lengths,
1326b4189e5eSMatthew Dillon 		 * don't ask me why.  Truncate the inquiry length.
1327b4189e5eSMatthew Dillon 		 */
1328b4189e5eSMatthew Dillon 		if (cdbd->inquiry.page_code == 0 &&
1329b4189e5eSMatthew Dillon 		    cdbd->inquiry.length > SHORT_INQUIRY_LENGTH) {
1330b4189e5eSMatthew Dillon 			cdbd->inquiry.length = SHORT_INQUIRY_LENGTH;
1331b4189e5eSMatthew Dillon 		}
1332b4189e5eSMatthew Dillon 		break;
1333258223a3SMatthew Dillon 	case READ_6:
1334258223a3SMatthew Dillon 	case WRITE_6:
1335b4189e5eSMatthew Dillon 		/*
1336b4189e5eSMatthew Dillon 		 * Convert *_6 to *_10 commands.  Most ATAPI devices
1337b4189e5eSMatthew Dillon 		 * cannot handle the SCSI READ_6 and WRITE_6 commands.
1338b4189e5eSMatthew Dillon 		 */
1339b4189e5eSMatthew Dillon 		cdbd->rw_10.opcode |= 0x20;
1340b4189e5eSMatthew Dillon 		cdbd->rw_10.byte2 = 0;
1341b4189e5eSMatthew Dillon 		cdbd->rw_10.addr[0] = cdbs->rw_6.addr[0] & 0x1F;
1342b4189e5eSMatthew Dillon 		cdbd->rw_10.addr[1] = cdbs->rw_6.addr[1];
1343b4189e5eSMatthew Dillon 		cdbd->rw_10.addr[2] = cdbs->rw_6.addr[2];
1344b4189e5eSMatthew Dillon 		cdbd->rw_10.addr[3] = 0;
1345b4189e5eSMatthew Dillon 		cdbd->rw_10.reserved = 0;
1346b4189e5eSMatthew Dillon 		cdbd->rw_10.length[0] = 0;
1347b4189e5eSMatthew Dillon 		cdbd->rw_10.length[1] = cdbs->rw_6.length;
1348b4189e5eSMatthew Dillon 		cdbd->rw_10.control = cdbs->rw_6.control;
1349b4189e5eSMatthew Dillon 		break;
1350258223a3SMatthew Dillon 	default:
1351258223a3SMatthew Dillon 		break;
1352258223a3SMatthew Dillon 	}
1353258223a3SMatthew Dillon 
1354b4189e5eSMatthew Dillon 	/*
1355b4189e5eSMatthew Dillon 	 * And dispatch
1356b4189e5eSMatthew Dillon 	 */
1357b4189e5eSMatthew Dillon 	xa->complete = ahci_atapi_complete_cmd;
1358258223a3SMatthew Dillon 	xa->atascsi_private = ccb;
1359258223a3SMatthew Dillon 	ccb->ccb_h.sim_priv.entries[0].ptr = ap;
136076497a9cSMatthew Dillon 	ahci_os_lock_port(ap);
1361258223a3SMatthew Dillon 	ahci_ata_cmd(xa);
136276497a9cSMatthew Dillon 	ahci_os_unlock_port(ap);
1363258223a3SMatthew Dillon }
1364258223a3SMatthew Dillon 
1365b4189e5eSMatthew Dillon /*
1366*6e0003adSMatthew Dillon  * Simulate page inquiries for disk attachments.
1367*6e0003adSMatthew Dillon  */
1368*6e0003adSMatthew Dillon static
1369*6e0003adSMatthew Dillon void
1370*6e0003adSMatthew Dillon ahci_xpt_page_inquiry(struct ahci_port *ap, struct ata_port *at, union ccb *ccb)
1371*6e0003adSMatthew Dillon {
1372*6e0003adSMatthew Dillon 	union {
1373*6e0003adSMatthew Dillon 		struct scsi_vpd_supported_page_list	list;
1374*6e0003adSMatthew Dillon 		struct scsi_vpd_unit_serial_number	serno;
1375*6e0003adSMatthew Dillon 		struct scsi_vpd_unit_devid		devid;
1376*6e0003adSMatthew Dillon 		char					buf[256];
1377*6e0003adSMatthew Dillon 	} *page;
1378*6e0003adSMatthew Dillon 	scsi_cdb_t cdb;
1379*6e0003adSMatthew Dillon 	int i;
1380*6e0003adSMatthew Dillon 	int j;
1381*6e0003adSMatthew Dillon 	int len;
1382*6e0003adSMatthew Dillon 
1383*6e0003adSMatthew Dillon 	page = kmalloc(sizeof(*page), M_DEVBUF, M_WAITOK | M_ZERO);
1384*6e0003adSMatthew Dillon 
1385*6e0003adSMatthew Dillon 	cdb = (void *)((ccb->ccb_h.flags & CAM_CDB_POINTER) ?
1386*6e0003adSMatthew Dillon 			ccb->csio.cdb_io.cdb_ptr : ccb->csio.cdb_io.cdb_bytes);
1387*6e0003adSMatthew Dillon 
1388*6e0003adSMatthew Dillon 	switch(cdb->inquiry.page_code) {
1389*6e0003adSMatthew Dillon 	case SVPD_SUPPORTED_PAGE_LIST:
1390*6e0003adSMatthew Dillon 		i = 0;
1391*6e0003adSMatthew Dillon 		page->list.device = T_DIRECT;
1392*6e0003adSMatthew Dillon 		page->list.page_code = SVPD_SUPPORTED_PAGE_LIST;
1393*6e0003adSMatthew Dillon 		page->list.list[i++] = SVPD_SUPPORTED_PAGE_LIST;
1394*6e0003adSMatthew Dillon 		page->list.list[i++] = SVPD_UNIT_SERIAL_NUMBER;
1395*6e0003adSMatthew Dillon 		page->list.list[i++] = SVPD_UNIT_DEVID;
1396*6e0003adSMatthew Dillon 		page->list.length = i;
1397*6e0003adSMatthew Dillon 		len = offsetof(struct scsi_vpd_supported_page_list, list[3]);
1398*6e0003adSMatthew Dillon 		break;
1399*6e0003adSMatthew Dillon 	case SVPD_UNIT_SERIAL_NUMBER:
1400*6e0003adSMatthew Dillon 		i = 0;
1401*6e0003adSMatthew Dillon 		j = sizeof(at->at_identify.serial);
1402*6e0003adSMatthew Dillon 		for (i = 0; i < j && at->at_identify.serial[i] == ' '; ++i)
1403*6e0003adSMatthew Dillon 			;
1404*6e0003adSMatthew Dillon 		while (j > i && at->at_identify.serial[j-1] == ' ')
1405*6e0003adSMatthew Dillon 			--j;
1406*6e0003adSMatthew Dillon 		page->serno.device = T_DIRECT;
1407*6e0003adSMatthew Dillon 		page->serno.page_code = SVPD_UNIT_SERIAL_NUMBER;
1408*6e0003adSMatthew Dillon 		page->serno.length = j - i;
1409*6e0003adSMatthew Dillon 		bcopy(at->at_identify.serial + i,
1410*6e0003adSMatthew Dillon 		      page->serno.serial_num, j - i);
1411*6e0003adSMatthew Dillon 		len = offsetof(struct scsi_vpd_unit_serial_number,
1412*6e0003adSMatthew Dillon 			       serial_num[j-i]);
1413*6e0003adSMatthew Dillon 		break;
1414*6e0003adSMatthew Dillon 	case SVPD_UNIT_DEVID:
1415*6e0003adSMatthew Dillon 		/* fall through for now */
1416*6e0003adSMatthew Dillon 	default:
1417*6e0003adSMatthew Dillon 		ccb->ccb_h.status = CAM_FUNC_NOTAVAIL;
1418*6e0003adSMatthew Dillon 		len = 0;
1419*6e0003adSMatthew Dillon 		break;
1420*6e0003adSMatthew Dillon 	}
1421*6e0003adSMatthew Dillon 	if (ccb->ccb_h.status == CAM_REQ_INPROG) {
1422*6e0003adSMatthew Dillon 		if (len <= ccb->csio.dxfer_len) {
1423*6e0003adSMatthew Dillon 			ccb->ccb_h.status = CAM_REQ_CMP;
1424*6e0003adSMatthew Dillon 			bzero(ccb->csio.data_ptr, ccb->csio.dxfer_len);
1425*6e0003adSMatthew Dillon 			bcopy(page, ccb->csio.data_ptr, len);
1426*6e0003adSMatthew Dillon 			ccb->csio.resid = ccb->csio.dxfer_len - len;
1427*6e0003adSMatthew Dillon 		} else {
1428*6e0003adSMatthew Dillon 			ccb->ccb_h.status = CAM_CCB_LEN_ERR;
1429*6e0003adSMatthew Dillon 		}
1430*6e0003adSMatthew Dillon 	}
1431*6e0003adSMatthew Dillon 	kfree(page, M_DEVBUF);
1432*6e0003adSMatthew Dillon }
1433*6e0003adSMatthew Dillon 
1434*6e0003adSMatthew Dillon /*
1435b4189e5eSMatthew Dillon  * Completion function for ATA_PORT_T_DISK cache synchronization.
1436b4189e5eSMatthew Dillon  */
1437258223a3SMatthew Dillon static
1438258223a3SMatthew Dillon void
1439258223a3SMatthew Dillon ahci_ata_complete_disk_synchronize_cache(struct ata_xfer *xa)
1440258223a3SMatthew Dillon {
1441258223a3SMatthew Dillon 	union ccb *ccb = xa->atascsi_private;
1442258223a3SMatthew Dillon 	struct ccb_hdr *ccbh = &ccb->ccb_h;
1443258223a3SMatthew Dillon 	struct ahci_port *ap = ccb->ccb_h.sim_priv.entries[0].ptr;
1444258223a3SMatthew Dillon 
1445258223a3SMatthew Dillon 	switch(xa->state) {
1446258223a3SMatthew Dillon 	case ATA_S_COMPLETE:
1447258223a3SMatthew Dillon 		ccbh->status = CAM_REQ_CMP;
1448b4189e5eSMatthew Dillon 		ccb->csio.scsi_status = SCSI_STATUS_OK;
1449258223a3SMatthew Dillon 		break;
1450258223a3SMatthew Dillon 	case ATA_S_ERROR:
14511980eff3SMatthew Dillon 		kprintf("%s: synchronize_cache: error\n",
14521980eff3SMatthew Dillon 			ATANAME(ap, xa->at));
1453b4189e5eSMatthew Dillon 		ccbh->status = CAM_SCSI_STATUS_ERROR | CAM_AUTOSNS_VALID;
1454b4189e5eSMatthew Dillon 		ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND;
1455b4189e5eSMatthew Dillon 		ahci_ata_dummy_sense(&ccb->csio.sense_data);
1456258223a3SMatthew Dillon 		break;
1457258223a3SMatthew Dillon 	case ATA_S_TIMEOUT:
14581980eff3SMatthew Dillon 		kprintf("%s: synchronize_cache: timeout\n",
14591980eff3SMatthew Dillon 			ATANAME(ap, xa->at));
1460258223a3SMatthew Dillon 		ccbh->status = CAM_CMD_TIMEOUT;
1461258223a3SMatthew Dillon 		break;
1462258223a3SMatthew Dillon 	default:
1463258223a3SMatthew Dillon 		kprintf("%s: synchronize_cache: unknown state %d\n",
14641980eff3SMatthew Dillon 			ATANAME(ap, xa->at), xa->state);
1465258223a3SMatthew Dillon 		ccbh->status = CAM_REQ_CMP_ERR;
1466258223a3SMatthew Dillon 		break;
1467258223a3SMatthew Dillon 	}
1468258223a3SMatthew Dillon 	ahci_ata_put_xfer(xa);
1469f4553de1SMatthew Dillon 	ahci_os_unlock_port(ap);
1470258223a3SMatthew Dillon 	xpt_done(ccb);
1471f4553de1SMatthew Dillon 	ahci_os_lock_port(ap);
1472258223a3SMatthew Dillon }
1473258223a3SMatthew Dillon 
1474b4189e5eSMatthew Dillon /*
1475b4189e5eSMatthew Dillon  * Completion function for ATA_PORT_T_DISK I/O
1476b4189e5eSMatthew Dillon  */
1477258223a3SMatthew Dillon static
1478258223a3SMatthew Dillon void
1479258223a3SMatthew Dillon ahci_ata_complete_disk_rw(struct ata_xfer *xa)
1480258223a3SMatthew Dillon {
1481258223a3SMatthew Dillon 	union ccb *ccb = xa->atascsi_private;
1482258223a3SMatthew Dillon 	struct ccb_hdr *ccbh = &ccb->ccb_h;
1483258223a3SMatthew Dillon 	struct ahci_port *ap = ccb->ccb_h.sim_priv.entries[0].ptr;
1484258223a3SMatthew Dillon 
1485258223a3SMatthew Dillon 	switch(xa->state) {
1486258223a3SMatthew Dillon 	case ATA_S_COMPLETE:
1487258223a3SMatthew Dillon 		ccbh->status = CAM_REQ_CMP;
1488b4189e5eSMatthew Dillon 		ccb->csio.scsi_status = SCSI_STATUS_OK;
1489258223a3SMatthew Dillon 		break;
1490258223a3SMatthew Dillon 	case ATA_S_ERROR:
14911980eff3SMatthew Dillon 		kprintf("%s: disk_rw: error\n", ATANAME(ap, xa->at));
1492b4189e5eSMatthew Dillon 		ccbh->status = CAM_SCSI_STATUS_ERROR | CAM_AUTOSNS_VALID;
1493b4189e5eSMatthew Dillon 		ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND;
1494b4189e5eSMatthew Dillon 		ahci_ata_dummy_sense(&ccb->csio.sense_data);
1495258223a3SMatthew Dillon 		break;
1496258223a3SMatthew Dillon 	case ATA_S_TIMEOUT:
14971980eff3SMatthew Dillon 		kprintf("%s: disk_rw: timeout\n", ATANAME(ap, xa->at));
1498258223a3SMatthew Dillon 		ccbh->status = CAM_CMD_TIMEOUT;
14994c339a5fSMatthew Dillon 		ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND;
15004c339a5fSMatthew Dillon 		ahci_ata_dummy_sense(&ccb->csio.sense_data);
1501258223a3SMatthew Dillon 		break;
1502258223a3SMatthew Dillon 	default:
1503258223a3SMatthew Dillon 		kprintf("%s: disk_rw: unknown state %d\n",
15041980eff3SMatthew Dillon 			ATANAME(ap, xa->at), xa->state);
1505258223a3SMatthew Dillon 		ccbh->status = CAM_REQ_CMP_ERR;
1506258223a3SMatthew Dillon 		break;
1507258223a3SMatthew Dillon 	}
1508258223a3SMatthew Dillon 	ccb->csio.resid = xa->resid;
1509258223a3SMatthew Dillon 	ahci_ata_put_xfer(xa);
1510f4553de1SMatthew Dillon 	ahci_os_unlock_port(ap);
1511258223a3SMatthew Dillon 	xpt_done(ccb);
1512f4553de1SMatthew Dillon 	ahci_os_lock_port(ap);
1513258223a3SMatthew Dillon }
1514b4189e5eSMatthew Dillon 
15157d4fcf34SMatthew Dillon /*
15167d4fcf34SMatthew Dillon  * Completion function for ATA_PORT_T_ATAPI I/O
15177d4fcf34SMatthew Dillon  *
15187d4fcf34SMatthew Dillon  * Sense data is returned in the rfis.
15197d4fcf34SMatthew Dillon  */
1520b4189e5eSMatthew Dillon static
1521b4189e5eSMatthew Dillon void
1522b4189e5eSMatthew Dillon ahci_atapi_complete_cmd(struct ata_xfer *xa)
1523b4189e5eSMatthew Dillon {
1524b4189e5eSMatthew Dillon 	union ccb *ccb = xa->atascsi_private;
1525b4189e5eSMatthew Dillon 	struct ccb_hdr *ccbh = &ccb->ccb_h;
1526b4189e5eSMatthew Dillon 	struct ahci_port *ap = ccb->ccb_h.sim_priv.entries[0].ptr;
1527b4189e5eSMatthew Dillon 	scsi_cdb_t cdb;
1528b4189e5eSMatthew Dillon 
1529b4189e5eSMatthew Dillon 	cdb = (void *)((ccb->ccb_h.flags & CAM_CDB_POINTER) ?
1530b4189e5eSMatthew Dillon 			ccb->csio.cdb_io.cdb_ptr : ccb->csio.cdb_io.cdb_bytes);
1531b4189e5eSMatthew Dillon 
1532b4189e5eSMatthew Dillon 	switch(xa->state) {
1533b4189e5eSMatthew Dillon 	case ATA_S_COMPLETE:
1534b4189e5eSMatthew Dillon 		ccbh->status = CAM_REQ_CMP;
1535b4189e5eSMatthew Dillon 		ccb->csio.scsi_status = SCSI_STATUS_OK;
1536b4189e5eSMatthew Dillon 		break;
1537b4189e5eSMatthew Dillon 	case ATA_S_ERROR:
1538b4189e5eSMatthew Dillon 		ccbh->status = CAM_SCSI_STATUS_ERROR;
1539b4189e5eSMatthew Dillon 		ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND;
15407d4fcf34SMatthew Dillon 		ahci_ata_atapi_sense(&xa->rfis, &ccb->csio.sense_data);
1541b4189e5eSMatthew Dillon 		break;
1542b4189e5eSMatthew Dillon 	case ATA_S_TIMEOUT:
1543b4189e5eSMatthew Dillon 		kprintf("%s: cmd %d: timeout\n",
1544b4189e5eSMatthew Dillon 			PORTNAME(ap), cdb->generic.opcode);
1545b4189e5eSMatthew Dillon 		ccbh->status = CAM_CMD_TIMEOUT;
15464c339a5fSMatthew Dillon 		ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND;
15474c339a5fSMatthew Dillon 		ahci_ata_dummy_sense(&ccb->csio.sense_data);
1548b4189e5eSMatthew Dillon 		break;
1549b4189e5eSMatthew Dillon 	default:
1550b4189e5eSMatthew Dillon 		kprintf("%s: cmd %d: unknown state %d\n",
1551b4189e5eSMatthew Dillon 			PORTNAME(ap), cdb->generic.opcode, xa->state);
1552b4189e5eSMatthew Dillon 		ccbh->status = CAM_REQ_CMP_ERR;
1553b4189e5eSMatthew Dillon 		break;
1554b4189e5eSMatthew Dillon 	}
1555b4189e5eSMatthew Dillon 	ccb->csio.resid = xa->resid;
1556b4189e5eSMatthew Dillon 	ahci_ata_put_xfer(xa);
1557f4553de1SMatthew Dillon 	ahci_os_unlock_port(ap);
1558b4189e5eSMatthew Dillon 	xpt_done(ccb);
1559f4553de1SMatthew Dillon 	ahci_os_lock_port(ap);
1560b4189e5eSMatthew Dillon }
1561b4189e5eSMatthew Dillon 
15627d4fcf34SMatthew Dillon /*
15637d4fcf34SMatthew Dillon  * Construct dummy sense data for errors on DISKs
15647d4fcf34SMatthew Dillon  */
1565b4189e5eSMatthew Dillon static
1566b4189e5eSMatthew Dillon void
1567b4189e5eSMatthew Dillon ahci_ata_dummy_sense(struct scsi_sense_data *sense_data)
1568b4189e5eSMatthew Dillon {
1569b4189e5eSMatthew Dillon 	sense_data->error_code = SSD_ERRCODE_VALID | SSD_CURRENT_ERROR;
1570b4189e5eSMatthew Dillon 	sense_data->segment = 0;
1571b4189e5eSMatthew Dillon 	sense_data->flags = SSD_KEY_MEDIUM_ERROR;
1572b4189e5eSMatthew Dillon 	sense_data->info[0] = 0;
1573b4189e5eSMatthew Dillon 	sense_data->info[1] = 0;
1574b4189e5eSMatthew Dillon 	sense_data->info[2] = 0;
1575b4189e5eSMatthew Dillon 	sense_data->info[3] = 0;
1576b4189e5eSMatthew Dillon 	sense_data->extra_len = 0;
1577b4189e5eSMatthew Dillon }
15787d4fcf34SMatthew Dillon 
15797d4fcf34SMatthew Dillon /*
15807d4fcf34SMatthew Dillon  * Construct atapi sense data for errors on ATAPI
15817d4fcf34SMatthew Dillon  *
15827d4fcf34SMatthew Dillon  * The ATAPI sense data is stored in the passed rfis and must be converted
15837d4fcf34SMatthew Dillon  * to SCSI sense data.
15847d4fcf34SMatthew Dillon  */
15857d4fcf34SMatthew Dillon static
15867d4fcf34SMatthew Dillon void
15877d4fcf34SMatthew Dillon ahci_ata_atapi_sense(struct ata_fis_d2h *rfis,
15887d4fcf34SMatthew Dillon 		     struct scsi_sense_data *sense_data)
15897d4fcf34SMatthew Dillon {
15907d4fcf34SMatthew Dillon 	sense_data->error_code = SSD_ERRCODE_VALID | SSD_CURRENT_ERROR;
15917d4fcf34SMatthew Dillon 	sense_data->segment = 0;
15927d4fcf34SMatthew Dillon 	sense_data->flags = (rfis->error & 0xF0) >> 4;
15937d4fcf34SMatthew Dillon 	if (rfis->error & 0x04)
15947d4fcf34SMatthew Dillon 		sense_data->flags |= SSD_KEY_ILLEGAL_REQUEST;
15957d4fcf34SMatthew Dillon 	if (rfis->error & 0x02)
15967d4fcf34SMatthew Dillon 		sense_data->flags |= SSD_EOM;
15977d4fcf34SMatthew Dillon 	if (rfis->error & 0x01)
15987d4fcf34SMatthew Dillon 		sense_data->flags |= SSD_ILI;
15997d4fcf34SMatthew Dillon 	sense_data->info[0] = 0;
16007d4fcf34SMatthew Dillon 	sense_data->info[1] = 0;
16017d4fcf34SMatthew Dillon 	sense_data->info[2] = 0;
16027d4fcf34SMatthew Dillon 	sense_data->info[3] = 0;
16037d4fcf34SMatthew Dillon 	sense_data->extra_len = 0;
16047d4fcf34SMatthew Dillon }
1605*6e0003adSMatthew Dillon 
1606*6e0003adSMatthew Dillon static
1607*6e0003adSMatthew Dillon void
1608*6e0003adSMatthew Dillon ahci_strip_string(const char **basep, int *lenp)
1609*6e0003adSMatthew Dillon {
1610*6e0003adSMatthew Dillon 	const char *base = *basep;
1611*6e0003adSMatthew Dillon 	int len = *lenp;
1612*6e0003adSMatthew Dillon 
1613*6e0003adSMatthew Dillon 	while (len && (*base == 0 || *base == ' ')) {
1614*6e0003adSMatthew Dillon 		--len;
1615*6e0003adSMatthew Dillon 		++base;
1616*6e0003adSMatthew Dillon 	}
1617*6e0003adSMatthew Dillon 	while (len && (base[len-1] == 0 || base[len-1] == ' '))
1618*6e0003adSMatthew Dillon 		--len;
1619*6e0003adSMatthew Dillon 	*basep = base;
1620*6e0003adSMatthew Dillon 	*lenp = len;
1621*6e0003adSMatthew Dillon }
1622