xref: /freebsd/usr.sbin/mptutil/mpt_cam.c (revision db4fcadf52c891afffded7fc72b9d9298bc39e77)
1fc58801cSScott Long /*-
2fc58801cSScott Long  * Copyright (c) 2008 Yahoo!, Inc.
3fc58801cSScott Long  * All rights reserved.
4fc58801cSScott Long  * Written by: John Baldwin <jhb@FreeBSD.org>
5fc58801cSScott Long  *
6fc58801cSScott Long  * Redistribution and use in source and binary forms, with or without
7fc58801cSScott Long  * modification, are permitted provided that the following conditions
8fc58801cSScott Long  * are met:
9fc58801cSScott Long  * 1. Redistributions of source code must retain the above copyright
10fc58801cSScott Long  *    notice, this list of conditions and the following disclaimer.
11fc58801cSScott Long  * 2. Redistributions in binary form must reproduce the above copyright
12fc58801cSScott Long  *    notice, this list of conditions and the following disclaimer in the
13fc58801cSScott Long  *    documentation and/or other materials provided with the distribution.
14fc58801cSScott Long  * 3. Neither the name of the author nor the names of any co-contributors
15fc58801cSScott Long  *    may be used to endorse or promote products derived from this software
16fc58801cSScott Long  *    without specific prior written permission.
17fc58801cSScott Long  *
18fc58801cSScott Long  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19fc58801cSScott Long  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20fc58801cSScott Long  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21fc58801cSScott Long  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22fc58801cSScott Long  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23fc58801cSScott Long  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24fc58801cSScott Long  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25fc58801cSScott Long  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26fc58801cSScott Long  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27fc58801cSScott Long  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28fc58801cSScott Long  * SUCH DAMAGE.
29fc58801cSScott Long  */
30fc58801cSScott Long 
31fc58801cSScott Long #include <sys/cdefs.h>
32fc58801cSScott Long __RCSID("$FreeBSD$");
33fc58801cSScott Long 
34fc58801cSScott Long #include <sys/param.h>
35fc58801cSScott Long #include <err.h>
36fc58801cSScott Long #include <errno.h>
37fc58801cSScott Long #include <fcntl.h>
38fc58801cSScott Long #include <stdio.h>
39fc58801cSScott Long #include <stdlib.h>
40fc58801cSScott Long #include <string.h>
41fc58801cSScott Long 
42fc58801cSScott Long #include <camlib.h>
43fc58801cSScott Long #include <cam/scsi/scsi_message.h>
44fc58801cSScott Long #include <cam/scsi/scsi_pass.h>
45fc58801cSScott Long 
46fc58801cSScott Long #include "mptutil.h"
47fc58801cSScott Long 
48fc58801cSScott Long static int xptfd;
49fc58801cSScott Long 
50fc58801cSScott Long static int
51fc58801cSScott Long xpt_open(void)
52fc58801cSScott Long {
53fc58801cSScott Long 
54fc58801cSScott Long 	if (xptfd == 0)
55fc58801cSScott Long 		xptfd = open(XPT_DEVICE, O_RDWR);
56fc58801cSScott Long 	return (xptfd);
57fc58801cSScott Long }
58fc58801cSScott Long 
59fe2f8087SJohn Baldwin /* Fetch the path id of bus 0 for the opened mpt controller. */
60fe2f8087SJohn Baldwin static int
61fe2f8087SJohn Baldwin fetch_path_id(path_id_t *path_id)
62fe2f8087SJohn Baldwin {
63fe2f8087SJohn Baldwin 	struct bus_match_pattern *b;
64fe2f8087SJohn Baldwin 	union ccb ccb;
65fe2f8087SJohn Baldwin 	size_t bufsize;
66c5f2b79dSJohn Baldwin 	int error;
67fe2f8087SJohn Baldwin 
68fe2f8087SJohn Baldwin 	if (xpt_open() < 0)
69fe2f8087SJohn Baldwin 		return (ENXIO);
70fe2f8087SJohn Baldwin 
71fe2f8087SJohn Baldwin 	/* First, find the path id of bus 0 for this mpt controller. */
72fe2f8087SJohn Baldwin 	bzero(&ccb, sizeof(ccb));
73fe2f8087SJohn Baldwin 
74fe2f8087SJohn Baldwin 	ccb.ccb_h.func_code = XPT_DEV_MATCH;
75fe2f8087SJohn Baldwin 
76fe2f8087SJohn Baldwin 	bufsize = sizeof(struct dev_match_result) * 1;
77fe2f8087SJohn Baldwin 	ccb.cdm.num_matches = 0;
78fe2f8087SJohn Baldwin 	ccb.cdm.match_buf_len = bufsize;
79fe2f8087SJohn Baldwin 	ccb.cdm.matches = calloc(1, bufsize);
80fe2f8087SJohn Baldwin 
81fe2f8087SJohn Baldwin 	bufsize = sizeof(struct dev_match_pattern) * 1;
82fe2f8087SJohn Baldwin 	ccb.cdm.num_patterns = 1;
83fe2f8087SJohn Baldwin 	ccb.cdm.pattern_buf_len = bufsize;
84fe2f8087SJohn Baldwin 	ccb.cdm.patterns = calloc(1, bufsize);
85fe2f8087SJohn Baldwin 
86fe2f8087SJohn Baldwin 	/* Match mptX bus 0. */
87fe2f8087SJohn Baldwin 	ccb.cdm.patterns[0].type = DEV_MATCH_BUS;
88fe2f8087SJohn Baldwin 	b = &ccb.cdm.patterns[0].pattern.bus_pattern;
89fe2f8087SJohn Baldwin 	snprintf(b->dev_name, sizeof(b->dev_name), "mpt");
90fe2f8087SJohn Baldwin 	b->unit_number = mpt_unit;
91fe2f8087SJohn Baldwin 	b->bus_id = 0;
92fe2f8087SJohn Baldwin 	b->flags = BUS_MATCH_NAME | BUS_MATCH_UNIT | BUS_MATCH_BUS_ID;
93fe2f8087SJohn Baldwin 
94fe2f8087SJohn Baldwin 	if (ioctl(xptfd, CAMIOCOMMAND, &ccb) < 0) {
95c5f2b79dSJohn Baldwin 		error = errno;
96fe2f8087SJohn Baldwin 		free(ccb.cdm.matches);
97fe2f8087SJohn Baldwin 		free(ccb.cdm.patterns);
98c5f2b79dSJohn Baldwin 		return (error);
99fe2f8087SJohn Baldwin 	}
100fe2f8087SJohn Baldwin 	free(ccb.cdm.patterns);
101fe2f8087SJohn Baldwin 
102fe2f8087SJohn Baldwin 	if (((ccb.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) ||
103fe2f8087SJohn Baldwin 	    (ccb.cdm.status != CAM_DEV_MATCH_LAST)) {
104fe2f8087SJohn Baldwin 		warnx("fetch_path_id got CAM error %#x, CDM error %d\n",
105fe2f8087SJohn Baldwin 		    ccb.ccb_h.status, ccb.cdm.status);
106fe2f8087SJohn Baldwin 		free(ccb.cdm.matches);
107fe2f8087SJohn Baldwin 		return (EIO);
108fe2f8087SJohn Baldwin 	}
109fe2f8087SJohn Baldwin 
110fe2f8087SJohn Baldwin 	/* We should have exactly 1 match for the bus. */
111fe2f8087SJohn Baldwin 	if (ccb.cdm.num_matches != 1 ||
112fe2f8087SJohn Baldwin 	    ccb.cdm.matches[0].type != DEV_MATCH_BUS) {
113fe2f8087SJohn Baldwin 		free(ccb.cdm.matches);
114fe2f8087SJohn Baldwin 		return (ENOENT);
115fe2f8087SJohn Baldwin 	}
116fe2f8087SJohn Baldwin 	*path_id = ccb.cdm.matches[0].result.bus_result.path_id;
117fe2f8087SJohn Baldwin 	free(ccb.cdm.matches);
118fe2f8087SJohn Baldwin 	return (0);
119fe2f8087SJohn Baldwin }
120fe2f8087SJohn Baldwin 
121fc58801cSScott Long int
122fc58801cSScott Long mpt_query_disk(U8 VolumeBus, U8 VolumeID, struct mpt_query_disk *qd)
123fc58801cSScott Long {
124fc58801cSScott Long 	struct periph_match_pattern *p;
125fc58801cSScott Long 	struct periph_match_result *r;
126fc58801cSScott Long 	union ccb ccb;
127fe2f8087SJohn Baldwin 	path_id_t path_id;
128fc58801cSScott Long 	size_t bufsize;
129c5f2b79dSJohn Baldwin 	int error;
130fc58801cSScott Long 
131fc58801cSScott Long 	/* mpt(4) only handles devices on bus 0. */
132fc58801cSScott Long 	if (VolumeBus != 0)
133fc58801cSScott Long 		return (ENXIO);
134fc58801cSScott Long 
135fc58801cSScott Long 	if (xpt_open() < 0)
136fc58801cSScott Long 		return (ENXIO);
137fc58801cSScott Long 
138fe2f8087SJohn Baldwin 	/* Find the path ID of bus 0. */
139fe2f8087SJohn Baldwin 	error = fetch_path_id(&path_id);
140fe2f8087SJohn Baldwin 	if (error)
141fe2f8087SJohn Baldwin 		return (error);
142fe2f8087SJohn Baldwin 
143fc58801cSScott Long 	bzero(&ccb, sizeof(ccb));
144fc58801cSScott Long 
145fc58801cSScott Long 	ccb.ccb_h.func_code = XPT_DEV_MATCH;
146fc58801cSScott Long 	ccb.ccb_h.path_id = CAM_XPT_PATH_ID;
147fc58801cSScott Long 	ccb.ccb_h.target_id = CAM_TARGET_WILDCARD;
148fc58801cSScott Long 	ccb.ccb_h.target_lun = CAM_LUN_WILDCARD;
149fc58801cSScott Long 
150fc58801cSScott Long 	bufsize = sizeof(struct dev_match_result) * 5;
151fc58801cSScott Long 	ccb.cdm.num_matches = 0;
152fc58801cSScott Long 	ccb.cdm.match_buf_len = bufsize;
153fc58801cSScott Long 	ccb.cdm.matches = calloc(1, bufsize);
154fc58801cSScott Long 
155fe2f8087SJohn Baldwin 	bufsize = sizeof(struct dev_match_pattern) * 1;
156fe2f8087SJohn Baldwin 	ccb.cdm.num_patterns = 1;
157fc58801cSScott Long 	ccb.cdm.pattern_buf_len = bufsize;
158fc58801cSScott Long 	ccb.cdm.patterns = calloc(1, bufsize);
159fc58801cSScott Long 
160fc58801cSScott Long 	/* Look for a "da" device at the specified target and lun. */
161fe2f8087SJohn Baldwin 	ccb.cdm.patterns[0].type = DEV_MATCH_PERIPH;
162fe2f8087SJohn Baldwin 	p = &ccb.cdm.patterns[0].pattern.periph_pattern;
163fe2f8087SJohn Baldwin 	p->path_id = path_id;
164fc58801cSScott Long 	snprintf(p->periph_name, sizeof(p->periph_name), "da");
165fc58801cSScott Long 	p->target_id = VolumeID;
166fe2f8087SJohn Baldwin 	p->flags = PERIPH_MATCH_PATH | PERIPH_MATCH_NAME | PERIPH_MATCH_TARGET;
167fc58801cSScott Long 
168fc58801cSScott Long 	if (ioctl(xptfd, CAMIOCOMMAND, &ccb) < 0) {
169c5f2b79dSJohn Baldwin 		error = errno;
170fc58801cSScott Long 		free(ccb.cdm.matches);
171fc58801cSScott Long 		free(ccb.cdm.patterns);
172c5f2b79dSJohn Baldwin 		return (error);
173fc58801cSScott Long 	}
174fc58801cSScott Long 	free(ccb.cdm.patterns);
175fc58801cSScott Long 
176fc58801cSScott Long 	if (((ccb.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) ||
177fc58801cSScott Long 	    (ccb.cdm.status != CAM_DEV_MATCH_LAST)) {
178fc58801cSScott Long 		warnx("mpt_query_disk got CAM error %#x, CDM error %d\n",
179fc58801cSScott Long 		    ccb.ccb_h.status, ccb.cdm.status);
180fc58801cSScott Long 		free(ccb.cdm.matches);
181fc58801cSScott Long 		return (EIO);
182fc58801cSScott Long 	}
183fc58801cSScott Long 
184fc58801cSScott Long 	/*
185fe2f8087SJohn Baldwin 	 * We should have exactly 1 match for the peripheral.
186fe2f8087SJohn Baldwin 	 * However, if we don't get a match, don't print an error
187fe2f8087SJohn Baldwin 	 * message and return ENOENT.
188fc58801cSScott Long 	 */
189fe2f8087SJohn Baldwin 	if (ccb.cdm.num_matches == 0) {
190fc58801cSScott Long 		free(ccb.cdm.matches);
191fc58801cSScott Long 		return (ENOENT);
192fc58801cSScott Long 	}
193fe2f8087SJohn Baldwin 	if (ccb.cdm.num_matches != 1) {
194fe2f8087SJohn Baldwin 		warnx("mpt_query_disk got %d matches, expected 1",
195fc58801cSScott Long 		    ccb.cdm.num_matches);
196fc58801cSScott Long 		free(ccb.cdm.matches);
197fc58801cSScott Long 		return (EIO);
198fc58801cSScott Long 	}
199fe2f8087SJohn Baldwin 	if (ccb.cdm.matches[0].type != DEV_MATCH_PERIPH) {
200fe2f8087SJohn Baldwin 		warnx("mpt_query_disk got wrong CAM match");
201fc58801cSScott Long 		free(ccb.cdm.matches);
202fc58801cSScott Long 		return (EIO);
203fc58801cSScott Long 	}
204fc58801cSScott Long 
205fc58801cSScott Long 	/* Copy out the data. */
206fc58801cSScott Long 	r = &ccb.cdm.matches[1].result.periph_result;
207fc58801cSScott Long 	snprintf(qd->devname, sizeof(qd->devname), "%s%d", r->periph_name,
208fc58801cSScott Long 	    r->unit_number);
209fc58801cSScott Long 	free(ccb.cdm.matches);
210fc58801cSScott Long 
211fc58801cSScott Long 	return (0);
212fc58801cSScott Long }
213fc58801cSScott Long 
214fc58801cSScott Long static int
215fc58801cSScott Long periph_is_volume(CONFIG_PAGE_IOC_2 *ioc2, struct periph_match_result *r)
216fc58801cSScott Long {
217fc58801cSScott Long 	CONFIG_PAGE_IOC_2_RAID_VOL *vol;
218fc58801cSScott Long 	int i;
219fc58801cSScott Long 
220fc58801cSScott Long 	if (ioc2 == NULL)
221fc58801cSScott Long 		return (0);
222fc58801cSScott Long 	vol = ioc2->RaidVolume;
223fc58801cSScott Long 	for (i = 0; i < ioc2->NumActiveVolumes; vol++, i++) {
224fc58801cSScott Long 		if (vol->VolumeBus == 0 && vol->VolumeID == r->target_id)
225fc58801cSScott Long 			return (1);
226fc58801cSScott Long 	}
227fc58801cSScott Long 	return (0);
228fc58801cSScott Long }
229fc58801cSScott Long 
230fc58801cSScott Long /* Much borrowed from scsireadcapacity() in src/sbin/camcontrol/camcontrol.c. */
231fc58801cSScott Long static int
232fc58801cSScott Long fetch_scsi_capacity(struct cam_device *dev, struct mpt_standalone_disk *disk)
233fc58801cSScott Long {
234fc58801cSScott Long 	struct scsi_read_capacity_data rcap;
235fc58801cSScott Long 	struct scsi_read_capacity_data_long rcaplong;
236fc58801cSScott Long 	union ccb *ccb;
237fc58801cSScott Long 	int error;
238fc58801cSScott Long 
239fc58801cSScott Long 	ccb = cam_getccb(dev);
240fc58801cSScott Long 	if (ccb == NULL)
241fc58801cSScott Long 		return (ENOMEM);
242fc58801cSScott Long 
243fc58801cSScott Long 	/* Zero the rest of the ccb. */
24495320aceSDon Lewis 	CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->csio);
245fc58801cSScott Long 
246fc58801cSScott Long 	scsi_read_capacity(&ccb->csio, 1, NULL, MSG_SIMPLE_Q_TAG, &rcap,
247fc58801cSScott Long 	    SSD_FULL_SIZE, 5000);
248fc58801cSScott Long 
249fc58801cSScott Long 	/* Disable freezing the device queue */
250fc58801cSScott Long 	ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
251fc58801cSScott Long 
252fc58801cSScott Long 	if (cam_send_ccb(dev, ccb) < 0) {
253fc58801cSScott Long 		error = errno;
254fc58801cSScott Long 		cam_freeccb(ccb);
255fc58801cSScott Long 		return (error);
256fc58801cSScott Long 	}
257fc58801cSScott Long 
258fc58801cSScott Long 	if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
259fc58801cSScott Long 		cam_freeccb(ccb);
260fc58801cSScott Long 		return (EIO);
261fc58801cSScott Long 	}
262fc58801cSScott Long 
263fc58801cSScott Long 	/*
264fc58801cSScott Long 	 * A last block of 2^32-1 means that the true capacity is over 2TB,
265fc58801cSScott Long 	 * and we need to issue the long READ CAPACITY to get the real
266fc58801cSScott Long 	 * capacity.  Otherwise, we're all set.
267fc58801cSScott Long 	 */
268fc58801cSScott Long 	if (scsi_4btoul(rcap.addr) != 0xffffffff) {
269fc58801cSScott Long 		disk->maxlba = scsi_4btoul(rcap.addr);
270ae092f75SDon Lewis 		cam_freeccb(ccb);
271fc58801cSScott Long 		return (0);
272fc58801cSScott Long 	}
273fc58801cSScott Long 
274fc58801cSScott Long 	/* Zero the rest of the ccb. */
27595320aceSDon Lewis 	CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->csio);
276fc58801cSScott Long 
277fc58801cSScott Long 	scsi_read_capacity_16(&ccb->csio, 1, NULL, MSG_SIMPLE_Q_TAG, 0, 0, 0,
278e6bd5983SKenneth D. Merry 	    (uint8_t *)&rcaplong, sizeof(rcaplong), SSD_FULL_SIZE, 5000);
279fc58801cSScott Long 
280fc58801cSScott Long 	/* Disable freezing the device queue */
281fc58801cSScott Long 	ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
282fc58801cSScott Long 
283fc58801cSScott Long 	if (cam_send_ccb(dev, ccb) < 0) {
284fc58801cSScott Long 		error = errno;
285fc58801cSScott Long 		cam_freeccb(ccb);
286fc58801cSScott Long 		return (error);
287fc58801cSScott Long 	}
288fc58801cSScott Long 
289fc58801cSScott Long 	if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
290fc58801cSScott Long 		cam_freeccb(ccb);
291fc58801cSScott Long 		return (EIO);
292fc58801cSScott Long 	}
293fc58801cSScott Long 	cam_freeccb(ccb);
294fc58801cSScott Long 
295fc58801cSScott Long 	disk->maxlba = scsi_8btou64(rcaplong.addr);
296fc58801cSScott Long 	return (0);
297fc58801cSScott Long }
298fc58801cSScott Long 
299fc58801cSScott Long /* Borrowed heavily from scsi_all.c:scsi_print_inquiry(). */
300fc58801cSScott Long static void
301fc58801cSScott Long format_scsi_inquiry(struct mpt_standalone_disk *disk,
302fc58801cSScott Long     struct scsi_inquiry_data *inq_data)
303fc58801cSScott Long {
304fc58801cSScott Long 	char vendor[16], product[48], revision[16], rstr[12];
305fc58801cSScott Long 
306fc58801cSScott Long 	if (SID_QUAL_IS_VENDOR_UNIQUE(inq_data))
307fc58801cSScott Long 		return;
308fc58801cSScott Long 	if (SID_TYPE(inq_data) != T_DIRECT)
309fc58801cSScott Long 		return;
310fc58801cSScott Long 	if (SID_QUAL(inq_data) != SID_QUAL_LU_CONNECTED)
311fc58801cSScott Long 		return;
312fc58801cSScott Long 
313fc58801cSScott Long 	cam_strvis(vendor, inq_data->vendor, sizeof(inq_data->vendor),
314fc58801cSScott Long 	    sizeof(vendor));
315fc58801cSScott Long 	cam_strvis(product, inq_data->product, sizeof(inq_data->product),
316fc58801cSScott Long 	    sizeof(product));
317fc58801cSScott Long 	cam_strvis(revision, inq_data->revision, sizeof(inq_data->revision),
318fc58801cSScott Long 	    sizeof(revision));
319fc58801cSScott Long 
320fc58801cSScott Long 	/* Hack for SATA disks, no idea how to tell speed. */
321fc58801cSScott Long 	if (strcmp(vendor, "ATA") == 0) {
322fc58801cSScott Long 		snprintf(disk->inqstring, sizeof(disk->inqstring),
323fc58801cSScott Long 		    "<%s %s> SATA", product, revision);
324fc58801cSScott Long 		return;
325fc58801cSScott Long 	}
326fc58801cSScott Long 
327fc58801cSScott Long 	switch (SID_ANSI_REV(inq_data)) {
328fc58801cSScott Long 	case SCSI_REV_CCS:
329fc58801cSScott Long 		strcpy(rstr, "SCSI-CCS");
330fc58801cSScott Long 		break;
331fc58801cSScott Long 	case 5:
332fc58801cSScott Long 		strcpy(rstr, "SAS");
333fc58801cSScott Long 		break;
334fc58801cSScott Long 	default:
335fc58801cSScott Long 		snprintf(rstr, sizeof (rstr), "SCSI-%d",
336fc58801cSScott Long 		    SID_ANSI_REV(inq_data));
337fc58801cSScott Long 		break;
338fc58801cSScott Long 	}
339fc58801cSScott Long 	snprintf(disk->inqstring, sizeof(disk->inqstring), "<%s %s %s> %s",
340fc58801cSScott Long 	    vendor, product, revision, rstr);
341fc58801cSScott Long }
342fc58801cSScott Long 
343fc58801cSScott Long /* Much borrowed from scsiinquiry() in src/sbin/camcontrol/camcontrol.c. */
344fc58801cSScott Long static int
345fc58801cSScott Long fetch_scsi_inquiry(struct cam_device *dev, struct mpt_standalone_disk *disk)
346fc58801cSScott Long {
347fc58801cSScott Long 	struct scsi_inquiry_data *inq_buf;
348fc58801cSScott Long 	union ccb *ccb;
349fc58801cSScott Long 	int error;
350fc58801cSScott Long 
351fc58801cSScott Long 	ccb = cam_getccb(dev);
352fc58801cSScott Long 	if (ccb == NULL)
353fc58801cSScott Long 		return (ENOMEM);
354fc58801cSScott Long 
355fc58801cSScott Long 	/* Zero the rest of the ccb. */
35695320aceSDon Lewis 	CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->csio);
357fc58801cSScott Long 
358fc58801cSScott Long 	inq_buf = calloc(1, sizeof(*inq_buf));
359fc58801cSScott Long 	if (inq_buf == NULL) {
360fc58801cSScott Long 		cam_freeccb(ccb);
361fc58801cSScott Long 		return (ENOMEM);
362fc58801cSScott Long 	}
363fc58801cSScott Long 	scsi_inquiry(&ccb->csio, 1, NULL, MSG_SIMPLE_Q_TAG, (void *)inq_buf,
364fc58801cSScott Long 	    SHORT_INQUIRY_LENGTH, 0, 0, SSD_FULL_SIZE, 5000);
365fc58801cSScott Long 
366fc58801cSScott Long 	/* Disable freezing the device queue */
367fc58801cSScott Long 	ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
368fc58801cSScott Long 
369fc58801cSScott Long 	if (cam_send_ccb(dev, ccb) < 0) {
370fc58801cSScott Long 		error = errno;
371fc58801cSScott Long 		free(inq_buf);
372fc58801cSScott Long 		cam_freeccb(ccb);
373fc58801cSScott Long 		return (error);
374fc58801cSScott Long 	}
375fc58801cSScott Long 
376fc58801cSScott Long 	if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
377fc58801cSScott Long 		free(inq_buf);
378fc58801cSScott Long 		cam_freeccb(ccb);
379fc58801cSScott Long 		return (EIO);
380fc58801cSScott Long 	}
381fc58801cSScott Long 
382fc58801cSScott Long 	cam_freeccb(ccb);
383fc58801cSScott Long 	format_scsi_inquiry(disk, inq_buf);
384fc58801cSScott Long 	free(inq_buf);
385fc58801cSScott Long 	return (0);
386fc58801cSScott Long }
387fc58801cSScott Long 
388fc58801cSScott Long int
389fc58801cSScott Long mpt_fetch_disks(int fd, int *ndisks, struct mpt_standalone_disk **disksp)
390fc58801cSScott Long {
391fc58801cSScott Long 	CONFIG_PAGE_IOC_2 *ioc2;
392fc58801cSScott Long 	struct mpt_standalone_disk *disks;
393fc58801cSScott Long 	struct periph_match_pattern *p;
394fc58801cSScott Long 	struct periph_match_result *r;
395fc58801cSScott Long 	struct cam_device *dev;
396fc58801cSScott Long 	union ccb ccb;
397fe2f8087SJohn Baldwin 	path_id_t path_id;
398fc58801cSScott Long 	size_t bufsize;
399fe2f8087SJohn Baldwin 	int count, error;
400c5f2b79dSJohn Baldwin 	uint32_t i;
401fc58801cSScott Long 
402fc58801cSScott Long 	if (xpt_open() < 0)
403fc58801cSScott Long 		return (ENXIO);
404fc58801cSScott Long 
405fe2f8087SJohn Baldwin 	error = fetch_path_id(&path_id);
406fe2f8087SJohn Baldwin 	if (error)
407fe2f8087SJohn Baldwin 		return (error);
408fe2f8087SJohn Baldwin 
409fc58801cSScott Long 	for (count = 100;; count+= 100) {
410fc58801cSScott Long 		/* Try to fetch 'count' disks in one go. */
411fc58801cSScott Long 		bzero(&ccb, sizeof(ccb));
412fc58801cSScott Long 
413fc58801cSScott Long 		ccb.ccb_h.func_code = XPT_DEV_MATCH;
414fc58801cSScott Long 
415fe2f8087SJohn Baldwin 		bufsize = sizeof(struct dev_match_result) * (count + 1);
416fc58801cSScott Long 		ccb.cdm.num_matches = 0;
417fc58801cSScott Long 		ccb.cdm.match_buf_len = bufsize;
418fc58801cSScott Long 		ccb.cdm.matches = calloc(1, bufsize);
419fc58801cSScott Long 
420fe2f8087SJohn Baldwin 		bufsize = sizeof(struct dev_match_pattern) * 1;
421fe2f8087SJohn Baldwin 		ccb.cdm.num_patterns = 1;
422fc58801cSScott Long 		ccb.cdm.pattern_buf_len = bufsize;
423fc58801cSScott Long 		ccb.cdm.patterns = calloc(1, bufsize);
424fc58801cSScott Long 
425fc58801cSScott Long 		/* Match any "da" peripherals. */
426fe2f8087SJohn Baldwin 		ccb.cdm.patterns[0].type = DEV_MATCH_PERIPH;
427fe2f8087SJohn Baldwin 		p = &ccb.cdm.patterns[0].pattern.periph_pattern;
428fe2f8087SJohn Baldwin 		p->path_id = path_id;
429fc58801cSScott Long 		snprintf(p->periph_name, sizeof(p->periph_name), "da");
430fe2f8087SJohn Baldwin 		p->flags = PERIPH_MATCH_PATH | PERIPH_MATCH_NAME;
431fc58801cSScott Long 
432fc58801cSScott Long 		if (ioctl(xptfd, CAMIOCOMMAND, &ccb) < 0) {
433c5f2b79dSJohn Baldwin 			error = errno;
434fc58801cSScott Long 			free(ccb.cdm.matches);
435fc58801cSScott Long 			free(ccb.cdm.patterns);
436c5f2b79dSJohn Baldwin 			return (error);
437fc58801cSScott Long 		}
438fc58801cSScott Long 		free(ccb.cdm.patterns);
439fc58801cSScott Long 
440fc58801cSScott Long 		/* Check for CCB errors. */
441fc58801cSScott Long 		if ((ccb.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
442fc58801cSScott Long 			free(ccb.cdm.matches);
443fc58801cSScott Long 			return (EIO);
444fc58801cSScott Long 		}
445fc58801cSScott Long 
446fc58801cSScott Long 		/* If we need a longer list, try again. */
447fc58801cSScott Long 		if (ccb.cdm.status == CAM_DEV_MATCH_MORE) {
448fc58801cSScott Long 			free(ccb.cdm.matches);
449fc58801cSScott Long 			continue;
450fc58801cSScott Long 		}
451fc58801cSScott Long 
452fc58801cSScott Long 		/* If we got an error, abort. */
453fc58801cSScott Long 		if (ccb.cdm.status != CAM_DEV_MATCH_LAST) {
454fc58801cSScott Long 			free(ccb.cdm.matches);
455fc58801cSScott Long 			return (EIO);
456fc58801cSScott Long 		}
457fc58801cSScott Long 		break;
458fc58801cSScott Long 	}
459fc58801cSScott Long 
460fe2f8087SJohn Baldwin 	/* Shortcut if we don't have any "da" devices. */
461fe2f8087SJohn Baldwin 	if (ccb.cdm.num_matches == 0) {
462fc58801cSScott Long 		free(ccb.cdm.matches);
463fe2f8087SJohn Baldwin 		*ndisks = 0;
464fe2f8087SJohn Baldwin 		*disksp = NULL;
465fe2f8087SJohn Baldwin 		return (0);
466fc58801cSScott Long 	}
467fe2f8087SJohn Baldwin 
468fe2f8087SJohn Baldwin 	/* We should have N matches, 1 for each "da" device. */
469fe2f8087SJohn Baldwin 	for (i = 0; i < ccb.cdm.num_matches; i++) {
470fc58801cSScott Long 		if (ccb.cdm.matches[i].type != DEV_MATCH_PERIPH) {
471fc58801cSScott Long 			warnx("mpt_fetch_disks got wrong CAM matches");
472fc58801cSScott Long 			free(ccb.cdm.matches);
473fc58801cSScott Long 			return (EIO);
474fc58801cSScott Long 		}
475fc58801cSScott Long 	}
476fc58801cSScott Long 
477fc58801cSScott Long 	/*
478fc58801cSScott Long 	 * Some of the "da" peripherals may be for RAID volumes, so
479fc58801cSScott Long 	 * fetch the IOC 2 page (list of RAID volumes) so we can
480fc58801cSScott Long 	 * exclude them from the list.
481fc58801cSScott Long 	 */
482fc58801cSScott Long 	ioc2 = mpt_read_ioc_page(fd, 2, NULL);
483c5f2b79dSJohn Baldwin 	if (ioc2 == NULL)
484c5f2b79dSJohn Baldwin 		return (errno);
485fc58801cSScott Long 	disks = calloc(ccb.cdm.num_matches, sizeof(*disks));
486fc58801cSScott Long 	count = 0;
487fe2f8087SJohn Baldwin 	for (i = 0; i < ccb.cdm.num_matches; i++) {
488fc58801cSScott Long 		r = &ccb.cdm.matches[i].result.periph_result;
489fc58801cSScott Long 		if (periph_is_volume(ioc2, r))
490fc58801cSScott Long 			continue;
491fc58801cSScott Long 		disks[count].bus = 0;
492fc58801cSScott Long 		disks[count].target = r->target_id;
493fc58801cSScott Long 		snprintf(disks[count].devname, sizeof(disks[count].devname),
494fc58801cSScott Long 		    "%s%d", r->periph_name, r->unit_number);
495fc58801cSScott Long 
496fc58801cSScott Long 		dev = cam_open_device(disks[count].devname, O_RDWR);
497fc58801cSScott Long 		if (dev != NULL) {
498fc58801cSScott Long 			fetch_scsi_capacity(dev, &disks[count]);
499fc58801cSScott Long 			fetch_scsi_inquiry(dev, &disks[count]);
500fc58801cSScott Long 			cam_close_device(dev);
501fc58801cSScott Long 		}
502fc58801cSScott Long 		count++;
503fc58801cSScott Long 	}
504fc58801cSScott Long 	free(ccb.cdm.matches);
505fc58801cSScott Long 	free(ioc2);
506fc58801cSScott Long 
507fc58801cSScott Long 	*ndisks = count;
508fc58801cSScott Long 	*disksp = disks;
509fc58801cSScott Long 	return (0);
510fc58801cSScott Long }
511fc58801cSScott Long 
512fc58801cSScott Long /*
513*db4fcadfSConrad Meyer  * Instruct the mpt(4) device to rescan its buses to find new devices
514fc58801cSScott Long  * such as disks whose RAID physdisk page was removed or volumes that
515fc58801cSScott Long  * were created.  If id is -1, the entire bus is rescanned.
516fc58801cSScott Long  * Otherwise, only devices at the specified ID are rescanned.  If bus
517*db4fcadfSConrad Meyer  * is -1, then all buses are scanned instead of the specified bus.
518fc58801cSScott Long  * Note that currently, only bus 0 is supported.
519fc58801cSScott Long  */
520fc58801cSScott Long int
521fc58801cSScott Long mpt_rescan_bus(int bus, int id)
522fc58801cSScott Long {
523fc58801cSScott Long 	union ccb ccb;
524fc58801cSScott Long 	path_id_t path_id;
525fe2f8087SJohn Baldwin 	int error;
526fc58801cSScott Long 
527fc58801cSScott Long 	/* mpt(4) only handles devices on bus 0. */
528fc58801cSScott Long 	if (bus != -1 && bus != 0)
529fc58801cSScott Long 		return (EINVAL);
530fc58801cSScott Long 
531fc58801cSScott Long 	if (xpt_open() < 0)
532fc58801cSScott Long 		return (ENXIO);
533fc58801cSScott Long 
534fe2f8087SJohn Baldwin 	error = fetch_path_id(&path_id);
535fe2f8087SJohn Baldwin 	if (error)
536fe2f8087SJohn Baldwin 		return (error);
537fe2f8087SJohn Baldwin 
538fe2f8087SJohn Baldwin 	/* Perform the actual rescan. */
539fc58801cSScott Long 	bzero(&ccb, sizeof(ccb));
540fc58801cSScott Long 	ccb.ccb_h.path_id = path_id;
541fc58801cSScott Long 	if (id == -1) {
542fc58801cSScott Long 		ccb.ccb_h.func_code = XPT_SCAN_BUS;
543fc58801cSScott Long 		ccb.ccb_h.target_id = CAM_TARGET_WILDCARD;
544fc58801cSScott Long 		ccb.ccb_h.target_lun = CAM_LUN_WILDCARD;
545fc58801cSScott Long 		ccb.ccb_h.timeout = 5000;
546fc58801cSScott Long 	} else {
547fc58801cSScott Long 		ccb.ccb_h.func_code = XPT_SCAN_LUN;
548fc58801cSScott Long 		ccb.ccb_h.target_id = id;
549fc58801cSScott Long 		ccb.ccb_h.target_lun = 0;
550fc58801cSScott Long 	}
551fc58801cSScott Long 	ccb.crcn.flags = CAM_FLAG_NONE;
552fc58801cSScott Long 
553fc58801cSScott Long 	/* Run this at a low priority. */
554fc58801cSScott Long 	ccb.ccb_h.pinfo.priority = 5;
555fc58801cSScott Long 
556fc58801cSScott Long 	if (ioctl(xptfd, CAMIOCOMMAND, &ccb) == -1)
557fc58801cSScott Long 		return (errno);
558fc58801cSScott Long 
559fc58801cSScott Long 	if ((ccb.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
560fc58801cSScott Long 		warnx("mpt_rescan_bus rescan got CAM error %#x\n",
561fc58801cSScott Long 		    ccb.ccb_h.status & CAM_STATUS_MASK);
562fc58801cSScott Long 		return (EIO);
563fc58801cSScott Long 	}
564fc58801cSScott Long 
565fc58801cSScott Long 	return (0);
566fc58801cSScott Long }
567