xref: /freebsd/usr.sbin/mptutil/mpt_cam.c (revision fc58801ccc318e391722e23c01826884edd25bf9)
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 
59fc58801cSScott Long int
60fc58801cSScott Long mpt_query_disk(U8 VolumeBus, U8 VolumeID, struct mpt_query_disk *qd)
61fc58801cSScott Long {
62fc58801cSScott Long 	struct bus_match_pattern *b;
63fc58801cSScott Long 	struct periph_match_pattern *p;
64fc58801cSScott Long 	struct periph_match_result *r;
65fc58801cSScott Long 	union ccb ccb;
66fc58801cSScott Long 	size_t bufsize;
67fc58801cSScott Long 	int i;
68fc58801cSScott Long 
69fc58801cSScott Long 	/* mpt(4) only handles devices on bus 0. */
70fc58801cSScott Long 	if (VolumeBus != 0)
71fc58801cSScott Long 		return (ENXIO);
72fc58801cSScott Long 
73fc58801cSScott Long 	if (xpt_open() < 0)
74fc58801cSScott Long 		return (ENXIO);
75fc58801cSScott Long 
76fc58801cSScott Long 	bzero(&ccb, sizeof(ccb));
77fc58801cSScott Long 
78fc58801cSScott Long 	ccb.ccb_h.func_code = XPT_DEV_MATCH;
79fc58801cSScott Long 	ccb.ccb_h.path_id = CAM_XPT_PATH_ID;
80fc58801cSScott Long 	ccb.ccb_h.target_id = CAM_TARGET_WILDCARD;
81fc58801cSScott Long 	ccb.ccb_h.target_lun = CAM_LUN_WILDCARD;
82fc58801cSScott Long 
83fc58801cSScott Long 	bufsize = sizeof(struct dev_match_result) * 5;
84fc58801cSScott Long 	ccb.cdm.num_matches = 0;
85fc58801cSScott Long 	ccb.cdm.match_buf_len = bufsize;
86fc58801cSScott Long 	ccb.cdm.matches = calloc(1, bufsize);
87fc58801cSScott Long 
88fc58801cSScott Long 	bufsize = sizeof(struct dev_match_pattern) * 2;
89fc58801cSScott Long 	ccb.cdm.num_patterns = 2;
90fc58801cSScott Long 	ccb.cdm.pattern_buf_len = bufsize;
91fc58801cSScott Long 	ccb.cdm.patterns = calloc(1, bufsize);
92fc58801cSScott Long 
93fc58801cSScott Long 	/* Match mptX bus 0. */
94fc58801cSScott Long 	ccb.cdm.patterns[0].type = DEV_MATCH_BUS;
95fc58801cSScott Long 	b = &ccb.cdm.patterns[0].pattern.bus_pattern;
96fc58801cSScott Long 	snprintf(b->dev_name, sizeof(b->dev_name), "mpt");
97fc58801cSScott Long 	b->unit_number = mpt_unit;
98fc58801cSScott Long 	b->bus_id = 0;
99fc58801cSScott Long 	b->flags = BUS_MATCH_NAME | BUS_MATCH_UNIT | BUS_MATCH_BUS_ID;
100fc58801cSScott Long 
101fc58801cSScott Long 	/* Look for a "da" device at the specified target and lun. */
102fc58801cSScott Long 	ccb.cdm.patterns[1].type = DEV_MATCH_PERIPH;
103fc58801cSScott Long 	p = &ccb.cdm.patterns[1].pattern.periph_pattern;
104fc58801cSScott Long 	snprintf(p->periph_name, sizeof(p->periph_name), "da");
105fc58801cSScott Long 	p->target_id = VolumeID;
106fc58801cSScott Long 	p->flags = PERIPH_MATCH_NAME | PERIPH_MATCH_TARGET;
107fc58801cSScott Long 
108fc58801cSScott Long 	if (ioctl(xptfd, CAMIOCOMMAND, &ccb) < 0) {
109fc58801cSScott Long 		i = errno;
110fc58801cSScott Long 		free(ccb.cdm.matches);
111fc58801cSScott Long 		free(ccb.cdm.patterns);
112fc58801cSScott Long 		return (i);
113fc58801cSScott Long 	}
114fc58801cSScott Long 	free(ccb.cdm.patterns);
115fc58801cSScott Long 
116fc58801cSScott Long 	if (((ccb.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) ||
117fc58801cSScott Long 	    (ccb.cdm.status != CAM_DEV_MATCH_LAST)) {
118fc58801cSScott Long 		warnx("mpt_query_disk got CAM error %#x, CDM error %d\n",
119fc58801cSScott Long 		    ccb.ccb_h.status, ccb.cdm.status);
120fc58801cSScott Long 		free(ccb.cdm.matches);
121fc58801cSScott Long 		return (EIO);
122fc58801cSScott Long 	}
123fc58801cSScott Long 
124fc58801cSScott Long 	/*
125fc58801cSScott Long 	 * We should have exactly 2 matches, 1 for the bus and 1 for
126fc58801cSScott Long 	 * the peripheral.  However, if we only have 1 match and it is
127fc58801cSScott Long 	 * for the bus, don't print an error message and return
128fc58801cSScott Long 	 * ENOENT.
129fc58801cSScott Long 	 */
130fc58801cSScott Long 	if (ccb.cdm.num_matches == 1 &&
131fc58801cSScott Long 	    ccb.cdm.matches[0].type == DEV_MATCH_BUS) {
132fc58801cSScott Long 		free(ccb.cdm.matches);
133fc58801cSScott Long 		return (ENOENT);
134fc58801cSScott Long 	}
135fc58801cSScott Long 	if (ccb.cdm.num_matches != 2) {
136fc58801cSScott Long 		warnx("mpt_query_disk got %d matches, expected 2",
137fc58801cSScott Long 		    ccb.cdm.num_matches);
138fc58801cSScott Long 		free(ccb.cdm.matches);
139fc58801cSScott Long 		return (EIO);
140fc58801cSScott Long 	}
141fc58801cSScott Long 	if (ccb.cdm.matches[0].type != DEV_MATCH_BUS ||
142fc58801cSScott Long 	    ccb.cdm.matches[1].type != DEV_MATCH_PERIPH) {
143fc58801cSScott Long 		warnx("mpt_query_disk got wrong CAM matches");
144fc58801cSScott Long 		free(ccb.cdm.matches);
145fc58801cSScott Long 		return (EIO);
146fc58801cSScott Long 	}
147fc58801cSScott Long 
148fc58801cSScott Long 	/* Copy out the data. */
149fc58801cSScott Long 	r = &ccb.cdm.matches[1].result.periph_result;
150fc58801cSScott Long 	snprintf(qd->devname, sizeof(qd->devname), "%s%d", r->periph_name,
151fc58801cSScott Long 	    r->unit_number);
152fc58801cSScott Long 	free(ccb.cdm.matches);
153fc58801cSScott Long 
154fc58801cSScott Long 	return (0);
155fc58801cSScott Long }
156fc58801cSScott Long 
157fc58801cSScott Long static int
158fc58801cSScott Long periph_is_volume(CONFIG_PAGE_IOC_2 *ioc2, struct periph_match_result *r)
159fc58801cSScott Long {
160fc58801cSScott Long 	CONFIG_PAGE_IOC_2_RAID_VOL *vol;
161fc58801cSScott Long 	int i;
162fc58801cSScott Long 
163fc58801cSScott Long 	if (ioc2 == NULL)
164fc58801cSScott Long 		return (0);
165fc58801cSScott Long 	vol = ioc2->RaidVolume;
166fc58801cSScott Long 	for (i = 0; i < ioc2->NumActiveVolumes; vol++, i++) {
167fc58801cSScott Long 		if (vol->VolumeBus == 0 && vol->VolumeID == r->target_id)
168fc58801cSScott Long 			return (1);
169fc58801cSScott Long 	}
170fc58801cSScott Long 	return (0);
171fc58801cSScott Long }
172fc58801cSScott Long 
173fc58801cSScott Long /* Much borrowed from scsireadcapacity() in src/sbin/camcontrol/camcontrol.c. */
174fc58801cSScott Long static int
175fc58801cSScott Long fetch_scsi_capacity(struct cam_device *dev, struct mpt_standalone_disk *disk)
176fc58801cSScott Long {
177fc58801cSScott Long 	struct scsi_read_capacity_data rcap;
178fc58801cSScott Long 	struct scsi_read_capacity_data_long rcaplong;
179fc58801cSScott Long 	union ccb *ccb;
180fc58801cSScott Long 	int error;
181fc58801cSScott Long 
182fc58801cSScott Long 	ccb = cam_getccb(dev);
183fc58801cSScott Long 	if (ccb == NULL)
184fc58801cSScott Long 		return (ENOMEM);
185fc58801cSScott Long 
186fc58801cSScott Long 	/* Zero the rest of the ccb. */
187fc58801cSScott Long 	bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_scsiio) -
188fc58801cSScott Long 	    sizeof(struct ccb_hdr));
189fc58801cSScott Long 
190fc58801cSScott Long 	scsi_read_capacity(&ccb->csio, 1, NULL, MSG_SIMPLE_Q_TAG, &rcap,
191fc58801cSScott Long 	    SSD_FULL_SIZE, 5000);
192fc58801cSScott Long 
193fc58801cSScott Long 	/* Disable freezing the device queue */
194fc58801cSScott Long 	ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
195fc58801cSScott Long 
196fc58801cSScott Long 	if (cam_send_ccb(dev, ccb) < 0) {
197fc58801cSScott Long 		error = errno;
198fc58801cSScott Long 		cam_freeccb(ccb);
199fc58801cSScott Long 		return (error);
200fc58801cSScott Long 	}
201fc58801cSScott Long 
202fc58801cSScott Long 	if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
203fc58801cSScott Long 		cam_freeccb(ccb);
204fc58801cSScott Long 		return (EIO);
205fc58801cSScott Long 	}
206fc58801cSScott Long 	cam_freeccb(ccb);
207fc58801cSScott Long 
208fc58801cSScott Long 	/*
209fc58801cSScott Long 	 * A last block of 2^32-1 means that the true capacity is over 2TB,
210fc58801cSScott Long 	 * and we need to issue the long READ CAPACITY to get the real
211fc58801cSScott Long 	 * capacity.  Otherwise, we're all set.
212fc58801cSScott Long 	 */
213fc58801cSScott Long 	if (scsi_4btoul(rcap.addr) != 0xffffffff) {
214fc58801cSScott Long 		disk->maxlba = scsi_4btoul(rcap.addr);
215fc58801cSScott Long 		return (0);
216fc58801cSScott Long 	}
217fc58801cSScott Long 
218fc58801cSScott Long 	/* Zero the rest of the ccb. */
219fc58801cSScott Long 	bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_scsiio) -
220fc58801cSScott Long 	    sizeof(struct ccb_hdr));
221fc58801cSScott Long 
222fc58801cSScott Long 	scsi_read_capacity_16(&ccb->csio, 1, NULL, MSG_SIMPLE_Q_TAG, 0, 0, 0,
223fc58801cSScott Long 	    &rcaplong, SSD_FULL_SIZE, 5000);
224fc58801cSScott Long 
225fc58801cSScott Long 	/* Disable freezing the device queue */
226fc58801cSScott Long 	ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
227fc58801cSScott Long 
228fc58801cSScott Long 	if (cam_send_ccb(dev, ccb) < 0) {
229fc58801cSScott Long 		error = errno;
230fc58801cSScott Long 		cam_freeccb(ccb);
231fc58801cSScott Long 		return (error);
232fc58801cSScott Long 	}
233fc58801cSScott Long 
234fc58801cSScott Long 	if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
235fc58801cSScott Long 		cam_freeccb(ccb);
236fc58801cSScott Long 		return (EIO);
237fc58801cSScott Long 	}
238fc58801cSScott Long 	cam_freeccb(ccb);
239fc58801cSScott Long 
240fc58801cSScott Long 	disk->maxlba = scsi_8btou64(rcaplong.addr);
241fc58801cSScott Long 	return (0);
242fc58801cSScott Long }
243fc58801cSScott Long 
244fc58801cSScott Long /* Borrowed heavily from scsi_all.c:scsi_print_inquiry(). */
245fc58801cSScott Long static void
246fc58801cSScott Long format_scsi_inquiry(struct mpt_standalone_disk *disk,
247fc58801cSScott Long     struct scsi_inquiry_data *inq_data)
248fc58801cSScott Long {
249fc58801cSScott Long 	char vendor[16], product[48], revision[16], rstr[12];
250fc58801cSScott Long 
251fc58801cSScott Long 	if (SID_QUAL_IS_VENDOR_UNIQUE(inq_data))
252fc58801cSScott Long 		return;
253fc58801cSScott Long 	if (SID_TYPE(inq_data) != T_DIRECT)
254fc58801cSScott Long 		return;
255fc58801cSScott Long 	if (SID_QUAL(inq_data) != SID_QUAL_LU_CONNECTED)
256fc58801cSScott Long 		return;
257fc58801cSScott Long 
258fc58801cSScott Long 	cam_strvis(vendor, inq_data->vendor, sizeof(inq_data->vendor),
259fc58801cSScott Long 	    sizeof(vendor));
260fc58801cSScott Long 	cam_strvis(product, inq_data->product, sizeof(inq_data->product),
261fc58801cSScott Long 	    sizeof(product));
262fc58801cSScott Long 	cam_strvis(revision, inq_data->revision, sizeof(inq_data->revision),
263fc58801cSScott Long 	    sizeof(revision));
264fc58801cSScott Long 
265fc58801cSScott Long 	/* Hack for SATA disks, no idea how to tell speed. */
266fc58801cSScott Long 	if (strcmp(vendor, "ATA") == 0) {
267fc58801cSScott Long 		snprintf(disk->inqstring, sizeof(disk->inqstring),
268fc58801cSScott Long 		    "<%s %s> SATA", product, revision);
269fc58801cSScott Long 		return;
270fc58801cSScott Long 	}
271fc58801cSScott Long 
272fc58801cSScott Long 	switch (SID_ANSI_REV(inq_data)) {
273fc58801cSScott Long 	case SCSI_REV_CCS:
274fc58801cSScott Long 		strcpy(rstr, "SCSI-CCS");
275fc58801cSScott Long 		break;
276fc58801cSScott Long 	case 5:
277fc58801cSScott Long 		strcpy(rstr, "SAS");
278fc58801cSScott Long 		break;
279fc58801cSScott Long 	default:
280fc58801cSScott Long 		snprintf(rstr, sizeof (rstr), "SCSI-%d",
281fc58801cSScott Long 		    SID_ANSI_REV(inq_data));
282fc58801cSScott Long 		break;
283fc58801cSScott Long 	}
284fc58801cSScott Long 	snprintf(disk->inqstring, sizeof(disk->inqstring), "<%s %s %s> %s",
285fc58801cSScott Long 	    vendor, product, revision, rstr);
286fc58801cSScott Long }
287fc58801cSScott Long 
288fc58801cSScott Long /* Much borrowed from scsiinquiry() in src/sbin/camcontrol/camcontrol.c. */
289fc58801cSScott Long static int
290fc58801cSScott Long fetch_scsi_inquiry(struct cam_device *dev, struct mpt_standalone_disk *disk)
291fc58801cSScott Long {
292fc58801cSScott Long 	struct scsi_inquiry_data *inq_buf;
293fc58801cSScott Long 	union ccb *ccb;
294fc58801cSScott Long 	int error;
295fc58801cSScott Long 
296fc58801cSScott Long 	ccb = cam_getccb(dev);
297fc58801cSScott Long 	if (ccb == NULL)
298fc58801cSScott Long 		return (ENOMEM);
299fc58801cSScott Long 
300fc58801cSScott Long 	/* Zero the rest of the ccb. */
301fc58801cSScott Long 	bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_scsiio) -
302fc58801cSScott Long 	    sizeof(struct ccb_hdr));
303fc58801cSScott Long 
304fc58801cSScott Long 	inq_buf = calloc(1, sizeof(*inq_buf));
305fc58801cSScott Long 	if (inq_buf == NULL) {
306fc58801cSScott Long 		cam_freeccb(ccb);
307fc58801cSScott Long 		return (ENOMEM);
308fc58801cSScott Long 	}
309fc58801cSScott Long 	scsi_inquiry(&ccb->csio, 1, NULL, MSG_SIMPLE_Q_TAG, (void *)inq_buf,
310fc58801cSScott Long 	    SHORT_INQUIRY_LENGTH, 0, 0, SSD_FULL_SIZE, 5000);
311fc58801cSScott Long 
312fc58801cSScott Long 	/* Disable freezing the device queue */
313fc58801cSScott Long 	ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
314fc58801cSScott Long 
315fc58801cSScott Long 	if (cam_send_ccb(dev, ccb) < 0) {
316fc58801cSScott Long 		error = errno;
317fc58801cSScott Long 		free(inq_buf);
318fc58801cSScott Long 		cam_freeccb(ccb);
319fc58801cSScott Long 		return (error);
320fc58801cSScott Long 	}
321fc58801cSScott Long 
322fc58801cSScott Long 	if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
323fc58801cSScott Long 		free(inq_buf);
324fc58801cSScott Long 		cam_freeccb(ccb);
325fc58801cSScott Long 		return (EIO);
326fc58801cSScott Long 	}
327fc58801cSScott Long 
328fc58801cSScott Long 	cam_freeccb(ccb);
329fc58801cSScott Long 	format_scsi_inquiry(disk, inq_buf);
330fc58801cSScott Long 	free(inq_buf);
331fc58801cSScott Long 	return (0);
332fc58801cSScott Long }
333fc58801cSScott Long 
334fc58801cSScott Long int
335fc58801cSScott Long mpt_fetch_disks(int fd, int *ndisks, struct mpt_standalone_disk **disksp)
336fc58801cSScott Long {
337fc58801cSScott Long 	CONFIG_PAGE_IOC_2 *ioc2;
338fc58801cSScott Long 	struct mpt_standalone_disk *disks;
339fc58801cSScott Long 	struct bus_match_pattern *b;
340fc58801cSScott Long 	struct periph_match_pattern *p;
341fc58801cSScott Long 	struct periph_match_result *r;
342fc58801cSScott Long 	struct cam_device *dev;
343fc58801cSScott Long 	union ccb ccb;
344fc58801cSScott Long 	size_t bufsize;
345fc58801cSScott Long 	u_int i;
346fc58801cSScott Long 	int count;
347fc58801cSScott Long 
348fc58801cSScott Long 	if (xpt_open() < 0)
349fc58801cSScott Long 		return (ENXIO);
350fc58801cSScott Long 
351fc58801cSScott Long 	for (count = 100;; count+= 100) {
352fc58801cSScott Long 		/* Try to fetch 'count' disks in one go. */
353fc58801cSScott Long 		bzero(&ccb, sizeof(ccb));
354fc58801cSScott Long 
355fc58801cSScott Long 		ccb.ccb_h.func_code = XPT_DEV_MATCH;
356fc58801cSScott Long 
357fc58801cSScott Long 		bufsize = sizeof(struct dev_match_result) * (count + 2);
358fc58801cSScott Long 		ccb.cdm.num_matches = 0;
359fc58801cSScott Long 		ccb.cdm.match_buf_len = bufsize;
360fc58801cSScott Long 		ccb.cdm.matches = calloc(1, bufsize);
361fc58801cSScott Long 
362fc58801cSScott Long 		bufsize = sizeof(struct dev_match_pattern) * 2;
363fc58801cSScott Long 		ccb.cdm.num_patterns = 2;
364fc58801cSScott Long 		ccb.cdm.pattern_buf_len = bufsize;
365fc58801cSScott Long 		ccb.cdm.patterns = calloc(1, bufsize);
366fc58801cSScott Long 
367fc58801cSScott Long 		/* Match mptX bus 0. */
368fc58801cSScott Long 		ccb.cdm.patterns[0].type = DEV_MATCH_BUS;
369fc58801cSScott Long 		b = &ccb.cdm.patterns[0].pattern.bus_pattern;
370fc58801cSScott Long 		snprintf(b->dev_name, sizeof(b->dev_name), "mpt");
371fc58801cSScott Long 		b->unit_number = mpt_unit;
372fc58801cSScott Long 		b->bus_id = 0;
373fc58801cSScott Long 		b->flags = BUS_MATCH_NAME | BUS_MATCH_UNIT | BUS_MATCH_BUS_ID;
374fc58801cSScott Long 
375fc58801cSScott Long 		/* Match any "da" peripherals. */
376fc58801cSScott Long 		ccb.cdm.patterns[1].type = DEV_MATCH_PERIPH;
377fc58801cSScott Long 		p = &ccb.cdm.patterns[1].pattern.periph_pattern;
378fc58801cSScott Long 		snprintf(p->periph_name, sizeof(p->periph_name), "da");
379fc58801cSScott Long 		p->flags = PERIPH_MATCH_NAME;
380fc58801cSScott Long 
381fc58801cSScott Long 		if (ioctl(xptfd, CAMIOCOMMAND, &ccb) < 0) {
382fc58801cSScott Long 			i = errno;
383fc58801cSScott Long 			free(ccb.cdm.matches);
384fc58801cSScott Long 			free(ccb.cdm.patterns);
385fc58801cSScott Long 			return (i);
386fc58801cSScott Long 		}
387fc58801cSScott Long 		free(ccb.cdm.patterns);
388fc58801cSScott Long 
389fc58801cSScott Long 		/* Check for CCB errors. */
390fc58801cSScott Long 		if ((ccb.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
391fc58801cSScott Long 			free(ccb.cdm.matches);
392fc58801cSScott Long 			return (EIO);
393fc58801cSScott Long 		}
394fc58801cSScott Long 
395fc58801cSScott Long 		/* If we need a longer list, try again. */
396fc58801cSScott Long 		if (ccb.cdm.status == CAM_DEV_MATCH_MORE) {
397fc58801cSScott Long 			free(ccb.cdm.matches);
398fc58801cSScott Long 			continue;
399fc58801cSScott Long 		}
400fc58801cSScott Long 
401fc58801cSScott Long 		/* If we got an error, abort. */
402fc58801cSScott Long 		if (ccb.cdm.status != CAM_DEV_MATCH_LAST) {
403fc58801cSScott Long 			free(ccb.cdm.matches);
404fc58801cSScott Long 			return (EIO);
405fc58801cSScott Long 		}
406fc58801cSScott Long 		break;
407fc58801cSScott Long 	}
408fc58801cSScott Long 
409fc58801cSScott Long 	/*
410fc58801cSScott Long 	 * We should have N + 1 matches, 1 for the bus and 1 for each
411fc58801cSScott Long 	 * "da" device.
412fc58801cSScott Long 	 */
413fc58801cSScott Long 	if (ccb.cdm.num_matches < 1) {
414fc58801cSScott Long 		warnx("mpt_fetch_disks didn't get any matches");
415fc58801cSScott Long 		free(ccb.cdm.matches);
416fc58801cSScott Long 		return (EIO);
417fc58801cSScott Long 	}
418fc58801cSScott Long 	if (ccb.cdm.matches[0].type != DEV_MATCH_BUS) {
419fc58801cSScott Long 		warnx("mpt_fetch_disks got wrong CAM matches");
420fc58801cSScott Long 		free(ccb.cdm.matches);
421fc58801cSScott Long 		return (EIO);
422fc58801cSScott Long 	}
423fc58801cSScott Long 	for (i = 1; i < ccb.cdm.num_matches; i++) {
424fc58801cSScott Long 		if (ccb.cdm.matches[i].type != DEV_MATCH_PERIPH) {
425fc58801cSScott Long 			warnx("mpt_fetch_disks got wrong CAM matches");
426fc58801cSScott Long 			free(ccb.cdm.matches);
427fc58801cSScott Long 			return (EIO);
428fc58801cSScott Long 		}
429fc58801cSScott Long 	}
430fc58801cSScott Long 
431fc58801cSScott Long 	/* Shortcut if we don't have any "da" devices. */
432fc58801cSScott Long 	if (ccb.cdm.num_matches == 1) {
433fc58801cSScott Long 		free(ccb.cdm.matches);
434fc58801cSScott Long 		*ndisks = 0;
435fc58801cSScott Long 		*disksp = NULL;
436fc58801cSScott Long 		return (0);
437fc58801cSScott Long 	}
438fc58801cSScott Long 
439fc58801cSScott Long 	/*
440fc58801cSScott Long 	 * Some of the "da" peripherals may be for RAID volumes, so
441fc58801cSScott Long 	 * fetch the IOC 2 page (list of RAID volumes) so we can
442fc58801cSScott Long 	 * exclude them from the list.
443fc58801cSScott Long 	 */
444fc58801cSScott Long 	ioc2 = mpt_read_ioc_page(fd, 2, NULL);
445fc58801cSScott Long 	disks = calloc(ccb.cdm.num_matches, sizeof(*disks));
446fc58801cSScott Long 	count = 0;
447fc58801cSScott Long 	for (i = 1; i < ccb.cdm.num_matches; i++) {
448fc58801cSScott Long 		r = &ccb.cdm.matches[i].result.periph_result;
449fc58801cSScott Long 		if (periph_is_volume(ioc2, r))
450fc58801cSScott Long 			continue;
451fc58801cSScott Long 		disks[count].bus = 0;
452fc58801cSScott Long 		disks[count].target = r->target_id;
453fc58801cSScott Long 		snprintf(disks[count].devname, sizeof(disks[count].devname),
454fc58801cSScott Long 		    "%s%d", r->periph_name, r->unit_number);
455fc58801cSScott Long 
456fc58801cSScott Long 		dev = cam_open_device(disks[count].devname, O_RDWR);
457fc58801cSScott Long 		if (dev != NULL) {
458fc58801cSScott Long 			fetch_scsi_capacity(dev, &disks[count]);
459fc58801cSScott Long 			fetch_scsi_inquiry(dev, &disks[count]);
460fc58801cSScott Long 			cam_close_device(dev);
461fc58801cSScott Long 		}
462fc58801cSScott Long 		count++;
463fc58801cSScott Long 	}
464fc58801cSScott Long 	free(ccb.cdm.matches);
465fc58801cSScott Long 	free(ioc2);
466fc58801cSScott Long 
467fc58801cSScott Long 	*ndisks = count;
468fc58801cSScott Long 	*disksp = disks;
469fc58801cSScott Long 	return (0);
470fc58801cSScott Long }
471fc58801cSScott Long 
472fc58801cSScott Long /*
473fc58801cSScott Long  * Instruct the mpt(4) device to rescan its busses to find new devices
474fc58801cSScott Long  * such as disks whose RAID physdisk page was removed or volumes that
475fc58801cSScott Long  * were created.  If id is -1, the entire bus is rescanned.
476fc58801cSScott Long  * Otherwise, only devices at the specified ID are rescanned.  If bus
477fc58801cSScott Long  * is -1, then all busses are scanned instead of the specified bus.
478fc58801cSScott Long  * Note that currently, only bus 0 is supported.
479fc58801cSScott Long  */
480fc58801cSScott Long int
481fc58801cSScott Long mpt_rescan_bus(int bus, int id)
482fc58801cSScott Long {
483fc58801cSScott Long 	struct bus_match_pattern *b;
484fc58801cSScott Long 	union ccb ccb;
485fc58801cSScott Long 	path_id_t path_id;
486fc58801cSScott Long 	size_t bufsize;
487fc58801cSScott Long 
488fc58801cSScott Long 	/* mpt(4) only handles devices on bus 0. */
489fc58801cSScott Long 	if (bus != -1 && bus != 0)
490fc58801cSScott Long 		return (EINVAL);
491fc58801cSScott Long 
492fc58801cSScott Long 	if (xpt_open() < 0)
493fc58801cSScott Long 		return (ENXIO);
494fc58801cSScott Long 
495fc58801cSScott Long 	/* First, find the path id of bus 0 for this mpt controller. */
496fc58801cSScott Long 	bzero(&ccb, sizeof(ccb));
497fc58801cSScott Long 
498fc58801cSScott Long 	ccb.ccb_h.func_code = XPT_DEV_MATCH;
499fc58801cSScott Long 
500fc58801cSScott Long 	bufsize = sizeof(struct dev_match_result) * 1;
501fc58801cSScott Long 	ccb.cdm.num_matches = 0;
502fc58801cSScott Long 	ccb.cdm.match_buf_len = bufsize;
503fc58801cSScott Long 	ccb.cdm.matches = calloc(1, bufsize);
504fc58801cSScott Long 
505fc58801cSScott Long 	bufsize = sizeof(struct dev_match_pattern) * 1;
506fc58801cSScott Long 	ccb.cdm.num_patterns = 1;
507fc58801cSScott Long 	ccb.cdm.pattern_buf_len = bufsize;
508fc58801cSScott Long 	ccb.cdm.patterns = calloc(1, bufsize);
509fc58801cSScott Long 
510fc58801cSScott Long 	/* Match mptX bus 0. */
511fc58801cSScott Long 	ccb.cdm.patterns[0].type = DEV_MATCH_BUS;
512fc58801cSScott Long 	b = &ccb.cdm.patterns[0].pattern.bus_pattern;
513fc58801cSScott Long 	snprintf(b->dev_name, sizeof(b->dev_name), "mpt");
514fc58801cSScott Long 	b->unit_number = mpt_unit;
515fc58801cSScott Long 	b->bus_id = 0;
516fc58801cSScott Long 	b->flags = BUS_MATCH_NAME | BUS_MATCH_UNIT | BUS_MATCH_BUS_ID;
517fc58801cSScott Long 
518fc58801cSScott Long 	if (ioctl(xptfd, CAMIOCOMMAND, &ccb) < 0) {
519fc58801cSScott Long 		free(ccb.cdm.matches);
520fc58801cSScott Long 		free(ccb.cdm.patterns);
521fc58801cSScott Long 		return (errno);
522fc58801cSScott Long 	}
523fc58801cSScott Long 	free(ccb.cdm.patterns);
524fc58801cSScott Long 
525fc58801cSScott Long 	if (((ccb.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) ||
526fc58801cSScott Long 	    (ccb.cdm.status != CAM_DEV_MATCH_LAST)) {
527fc58801cSScott Long 		warnx("mpt_rescan_bus got CAM error %#x, CDM error %d\n",
528fc58801cSScott Long 		    ccb.ccb_h.status, ccb.cdm.status);
529fc58801cSScott Long 		free(ccb.cdm.matches);
530fc58801cSScott Long 		return (EIO);
531fc58801cSScott Long 	}
532fc58801cSScott Long 
533fc58801cSScott Long 	/* We should have exactly 1 match for the bus. */
534fc58801cSScott Long 	if (ccb.cdm.num_matches != 1 ||
535fc58801cSScott Long 	    ccb.cdm.matches[0].type != DEV_MATCH_BUS) {
536fc58801cSScott Long 		free(ccb.cdm.matches);
537fc58801cSScott Long 		return (ENOENT);
538fc58801cSScott Long 	}
539fc58801cSScott Long 	path_id = ccb.cdm.matches[0].result.bus_result.path_id;
540fc58801cSScott Long 	free(ccb.cdm.matches);
541fc58801cSScott Long 
542fc58801cSScott Long 	/* Now perform the actual rescan. */
543fc58801cSScott Long 	ccb.ccb_h.path_id = path_id;
544fc58801cSScott Long 	if (id == -1) {
545fc58801cSScott Long 		ccb.ccb_h.func_code = XPT_SCAN_BUS;
546fc58801cSScott Long 		ccb.ccb_h.target_id = CAM_TARGET_WILDCARD;
547fc58801cSScott Long 		ccb.ccb_h.target_lun = CAM_LUN_WILDCARD;
548fc58801cSScott Long 		ccb.ccb_h.timeout = 5000;
549fc58801cSScott Long 	} else {
550fc58801cSScott Long 		ccb.ccb_h.func_code = XPT_SCAN_LUN;
551fc58801cSScott Long 		ccb.ccb_h.target_id = id;
552fc58801cSScott Long 		ccb.ccb_h.target_lun = 0;
553fc58801cSScott Long 	}
554fc58801cSScott Long 	ccb.crcn.flags = CAM_FLAG_NONE;
555fc58801cSScott Long 
556fc58801cSScott Long 	/* Run this at a low priority. */
557fc58801cSScott Long 	ccb.ccb_h.pinfo.priority = 5;
558fc58801cSScott Long 
559fc58801cSScott Long 	if (ioctl(xptfd, CAMIOCOMMAND, &ccb) == -1)
560fc58801cSScott Long 		return (errno);
561fc58801cSScott Long 
562fc58801cSScott Long 	if ((ccb.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
563fc58801cSScott Long 		warnx("mpt_rescan_bus rescan got CAM error %#x\n",
564fc58801cSScott Long 		    ccb.ccb_h.status & CAM_STATUS_MASK);
565fc58801cSScott Long 		return (EIO);
566fc58801cSScott Long 	}
567fc58801cSScott Long 
568fc58801cSScott Long 	return (0);
569fc58801cSScott Long }
570