1158115Sume/*-
2158115Sume * Copyright (c) 2008 Yahoo!, Inc.
3158115Sume * All rights reserved.
4158115Sume * Written by: John Baldwin <jhb@FreeBSD.org>
5158115Sume *
6158115Sume * Redistribution and use in source and binary forms, with or without
7158115Sume * modification, are permitted provided that the following conditions
8158115Sume * are met:
9158115Sume * 1. Redistributions of source code must retain the above copyright
10158115Sume *    notice, this list of conditions and the following disclaimer.
11158115Sume * 2. Redistributions in binary form must reproduce the above copyright
12158115Sume *    notice, this list of conditions and the following disclaimer in the
13158115Sume *    documentation and/or other materials provided with the distribution.
14158115Sume * 3. Neither the name of the author nor the names of any co-contributors
15158115Sume *    may be used to endorse or promote products derived from this software
16158115Sume *    without specific prior written permission.
17158115Sume *
18158115Sume * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19158115Sume * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20158115Sume * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21158115Sume * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22158115Sume * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23158115Sume * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24158115Sume * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25158115Sume * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26158115Sume * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27158115Sume * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28158115Sume * SUCH DAMAGE.
29158115Sume */
30158115Sume
31194089Sdes#include <sys/cdefs.h>
32194089Sdes__RCSID("$FreeBSD$");
33158115Sume
34158115Sume#include <sys/param.h>
35194089Sdes#include <err.h>
36158115Sume#include <errno.h>
37194089Sdes#include <fcntl.h>
38158115Sume#include <stdio.h>
39158115Sume#include <stdlib.h>
40158115Sume#include <string.h>
41158115Sume
42158115Sume#include <camlib.h>
43158115Sume#include <cam/scsi/scsi_message.h>
44158115Sume#include <cam/scsi/scsi_pass.h>
45158115Sume
46158115Sume#include "mptutil.h"
47158115Sume
48158115Sumestatic int xptfd;
49158115Sume
50158115Sumestatic int
51158115Sumexpt_open(void)
52158115Sume{
53158115Sume
54158115Sume	if (xptfd == 0)
55158115Sume		xptfd = open(XPT_DEVICE, O_RDWR);
56158115Sume	return (xptfd);
57158115Sume}
58158115Sume
59158115Sume/* Fetch the path id of bus 0 for the opened mpt controller. */
60158115Sumestatic int
61158115Sumefetch_path_id(path_id_t *path_id)
62158115Sume{
63158115Sume	struct bus_match_pattern *b;
64158115Sume	union ccb ccb;
65158115Sume	size_t bufsize;
66158115Sume	int error;
67158115Sume
68158115Sume	if (xpt_open() < 0)
69158115Sume		return (ENXIO);
70158115Sume
71158115Sume	/* First, find the path id of bus 0 for this mpt controller. */
72158115Sume	bzero(&ccb, sizeof(ccb));
73158115Sume
74158115Sume	ccb.ccb_h.func_code = XPT_DEV_MATCH;
75158115Sume
76158115Sume	bufsize = sizeof(struct dev_match_result) * 1;
77158115Sume	ccb.cdm.num_matches = 0;
78158115Sume	ccb.cdm.match_buf_len = bufsize;
79158115Sume	ccb.cdm.matches = calloc(1, bufsize);
80158115Sume
81158115Sume	bufsize = sizeof(struct dev_match_pattern) * 1;
82158115Sume	ccb.cdm.num_patterns = 1;
83158115Sume	ccb.cdm.pattern_buf_len = bufsize;
84158115Sume	ccb.cdm.patterns = calloc(1, bufsize);
85158115Sume
86158115Sume	/* Match mptX bus 0. */
87158115Sume	ccb.cdm.patterns[0].type = DEV_MATCH_BUS;
88158115Sume	b = &ccb.cdm.patterns[0].pattern.bus_pattern;
89158115Sume	snprintf(b->dev_name, sizeof(b->dev_name), "mpt");
90158115Sume	b->unit_number = mpt_unit;
91158115Sume	b->bus_id = 0;
92158115Sume	b->flags = BUS_MATCH_NAME | BUS_MATCH_UNIT | BUS_MATCH_BUS_ID;
93158115Sume
94158115Sume	if (ioctl(xptfd, CAMIOCOMMAND, &ccb) < 0) {
95158115Sume		error = errno;
96158115Sume		free(ccb.cdm.matches);
97158115Sume		free(ccb.cdm.patterns);
98158115Sume		return (error);
99158115Sume	}
100158115Sume	free(ccb.cdm.patterns);
101158115Sume
102158115Sume	if (((ccb.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) ||
103158115Sume	    (ccb.cdm.status != CAM_DEV_MATCH_LAST)) {
104158115Sume		warnx("fetch_path_id got CAM error %#x, CDM error %d\n",
105158115Sume		    ccb.ccb_h.status, ccb.cdm.status);
106158115Sume		free(ccb.cdm.matches);
107158115Sume		return (EIO);
108158115Sume	}
109158115Sume
110158115Sume	/* We should have exactly 1 match for the bus. */
111158115Sume	if (ccb.cdm.num_matches != 1 ||
112158115Sume	    ccb.cdm.matches[0].type != DEV_MATCH_BUS) {
113158115Sume		free(ccb.cdm.matches);
114158115Sume		return (ENOENT);
115158115Sume	}
116158115Sume	*path_id = ccb.cdm.matches[0].result.bus_result.path_id;
117158115Sume	free(ccb.cdm.matches);
118158115Sume	return (0);
119158115Sume}
120158115Sume
121158115Sumeint
122158115Sumempt_query_disk(U8 VolumeBus, U8 VolumeID, struct mpt_query_disk *qd)
123158115Sume{
124158115Sume	struct periph_match_pattern *p;
125158115Sume	struct periph_match_result *r;
126158115Sume	union ccb ccb;
127158115Sume	path_id_t path_id;
128158115Sume	size_t bufsize;
129158115Sume	int error;
130158115Sume
131158115Sume	/* mpt(4) only handles devices on bus 0. */
132158115Sume	if (VolumeBus != 0)
133158115Sume		return (ENXIO);
134158115Sume
135158115Sume	if (xpt_open() < 0)
136158115Sume		return (ENXIO);
137158115Sume
138158115Sume	/* Find the path ID of bus 0. */
139158115Sume	error = fetch_path_id(&path_id);
140158115Sume	if (error)
141158115Sume		return (error);
142158115Sume
143158115Sume	bzero(&ccb, sizeof(ccb));
144158115Sume
145158115Sume	ccb.ccb_h.func_code = XPT_DEV_MATCH;
146158115Sume	ccb.ccb_h.path_id = CAM_XPT_PATH_ID;
147158115Sume	ccb.ccb_h.target_id = CAM_TARGET_WILDCARD;
148158115Sume	ccb.ccb_h.target_lun = CAM_LUN_WILDCARD;
149158115Sume
150158115Sume	bufsize = sizeof(struct dev_match_result) * 5;
151158115Sume	ccb.cdm.num_matches = 0;
152158115Sume	ccb.cdm.match_buf_len = bufsize;
153158115Sume	ccb.cdm.matches = calloc(1, bufsize);
154158115Sume
155158115Sume	bufsize = sizeof(struct dev_match_pattern) * 1;
156158115Sume	ccb.cdm.num_patterns = 1;
157158115Sume	ccb.cdm.pattern_buf_len = bufsize;
158158115Sume	ccb.cdm.patterns = calloc(1, bufsize);
159158115Sume
160158115Sume	/* Look for a "da" device at the specified target and lun. */
161158115Sume	ccb.cdm.patterns[0].type = DEV_MATCH_PERIPH;
162158115Sume	p = &ccb.cdm.patterns[0].pattern.periph_pattern;
163158115Sume	p->path_id = path_id;
164158115Sume	snprintf(p->periph_name, sizeof(p->periph_name), "da");
165158115Sume	p->target_id = VolumeID;
166158115Sume	p->flags = PERIPH_MATCH_PATH | PERIPH_MATCH_NAME | PERIPH_MATCH_TARGET;
167158115Sume
168158115Sume	if (ioctl(xptfd, CAMIOCOMMAND, &ccb) < 0) {
169158115Sume		error = errno;
170238094Sse		free(ccb.cdm.matches);
171238094Sse		free(ccb.cdm.patterns);
172238094Sse		return (error);
173238094Sse	}
174238094Sse	free(ccb.cdm.patterns);
175238094Sse
176238094Sse	if (((ccb.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) ||
177238094Sse	    (ccb.cdm.status != CAM_DEV_MATCH_LAST)) {
178238094Sse		warnx("mpt_query_disk got CAM error %#x, CDM error %d\n",
179238094Sse		    ccb.ccb_h.status, ccb.cdm.status);
180238094Sse		free(ccb.cdm.matches);
181238094Sse		return (EIO);
182238094Sse	}
183238094Sse
184238094Sse	/*
185238094Sse	 * We should have exactly 1 match for the peripheral.
186238094Sse	 * However, if we don't get a match, don't print an error
187238094Sse	 * message and return ENOENT.
188238094Sse	 */
189238094Sse	if (ccb.cdm.num_matches == 0) {
190238094Sse		free(ccb.cdm.matches);
191238094Sse		return (ENOENT);
192238094Sse	}
193238094Sse	if (ccb.cdm.num_matches != 1) {
194238094Sse		warnx("mpt_query_disk got %d matches, expected 1",
195238094Sse		    ccb.cdm.num_matches);
196238094Sse		free(ccb.cdm.matches);
197238094Sse		return (EIO);
198238094Sse	}
199238094Sse	if (ccb.cdm.matches[0].type != DEV_MATCH_PERIPH) {
200238094Sse		warnx("mpt_query_disk got wrong CAM match");
201238094Sse		free(ccb.cdm.matches);
202158115Sume		return (EIO);
203158115Sume	}
204158115Sume
205158115Sume	/* Copy out the data. */
206158115Sume	r = &ccb.cdm.matches[1].result.periph_result;
207158115Sume	snprintf(qd->devname, sizeof(qd->devname), "%s%d", r->periph_name,
208158115Sume	    r->unit_number);
209158115Sume	free(ccb.cdm.matches);
210158115Sume
211158115Sume	return (0);
212158115Sume}
213158115Sume
214158115Sumestatic int
215158115Sumeperiph_is_volume(CONFIG_PAGE_IOC_2 *ioc2, struct periph_match_result *r)
216158115Sume{
217158115Sume	CONFIG_PAGE_IOC_2_RAID_VOL *vol;
218158115Sume	int i;
219158115Sume
220158115Sume	if (ioc2 == NULL)
221158115Sume		return (0);
222158115Sume	vol = ioc2->RaidVolume;
223158115Sume	for (i = 0; i < ioc2->NumActiveVolumes; vol++, i++) {
224158115Sume		if (vol->VolumeBus == 0 && vol->VolumeID == r->target_id)
225158115Sume			return (1);
226158115Sume	}
227158115Sume	return (0);
228158115Sume}
229158115Sume
230158115Sume/* Much borrowed from scsireadcapacity() in src/sbin/camcontrol/camcontrol.c. */
231158115Sumestatic int
232158115Sumefetch_scsi_capacity(struct cam_device *dev, struct mpt_standalone_disk *disk)
233158115Sume{
234158115Sume	struct scsi_read_capacity_data rcap;
235158115Sume	struct scsi_read_capacity_data_long rcaplong;
236158115Sume	union ccb *ccb;
237158115Sume	int error;
238158115Sume
239158115Sume	ccb = cam_getccb(dev);
240158115Sume	if (ccb == NULL)
241158115Sume		return (ENOMEM);
242158115Sume
243158115Sume	/* Zero the rest of the ccb. */
244158115Sume	bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_scsiio) -
245158115Sume	    sizeof(struct ccb_hdr));
246158115Sume
247158115Sume	scsi_read_capacity(&ccb->csio, 1, NULL, MSG_SIMPLE_Q_TAG, &rcap,
248158115Sume	    SSD_FULL_SIZE, 5000);
249158115Sume
250158115Sume	/* Disable freezing the device queue */
251158115Sume	ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
252158115Sume
253158115Sume	if (cam_send_ccb(dev, ccb) < 0) {
254158115Sume		error = errno;
255158115Sume		cam_freeccb(ccb);
256158115Sume		return (error);
257158115Sume	}
258158115Sume
259158115Sume	if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
260158115Sume		cam_freeccb(ccb);
261158115Sume		return (EIO);
262158115Sume	}
263158115Sume	cam_freeccb(ccb);
264158115Sume
265158115Sume	/*
266158115Sume	 * A last block of 2^32-1 means that the true capacity is over 2TB,
267158115Sume	 * and we need to issue the long READ CAPACITY to get the real
268158115Sume	 * capacity.  Otherwise, we're all set.
269158115Sume	 */
270158115Sume	if (scsi_4btoul(rcap.addr) != 0xffffffff) {
271158115Sume		disk->maxlba = scsi_4btoul(rcap.addr);
272158115Sume		return (0);
273158115Sume	}
274158115Sume
275158115Sume	/* Zero the rest of the ccb. */
276158115Sume	bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_scsiio) -
277158115Sume	    sizeof(struct ccb_hdr));
278158115Sume
279158115Sume	scsi_read_capacity_16(&ccb->csio, 1, NULL, MSG_SIMPLE_Q_TAG, 0, 0, 0,
280158115Sume	    (uint8_t *)&rcaplong, sizeof(rcaplong), SSD_FULL_SIZE, 5000);
281158115Sume
282158115Sume	/* Disable freezing the device queue */
283158115Sume	ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
284158115Sume
285158115Sume	if (cam_send_ccb(dev, ccb) < 0) {
286158115Sume		error = errno;
287158115Sume		cam_freeccb(ccb);
288158115Sume		return (error);
289158115Sume	}
290158115Sume
291158115Sume	if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
292158115Sume		cam_freeccb(ccb);
293158115Sume		return (EIO);
294158115Sume	}
295158115Sume	cam_freeccb(ccb);
296158115Sume
297158115Sume	disk->maxlba = scsi_8btou64(rcaplong.addr);
298158115Sume	return (0);
299158115Sume}
300158115Sume
301158115Sume/* Borrowed heavily from scsi_all.c:scsi_print_inquiry(). */
302158115Sumestatic void
303158115Sumeformat_scsi_inquiry(struct mpt_standalone_disk *disk,
304158115Sume    struct scsi_inquiry_data *inq_data)
305158115Sume{
306158115Sume	char vendor[16], product[48], revision[16], rstr[12];
307158115Sume
308158115Sume	if (SID_QUAL_IS_VENDOR_UNIQUE(inq_data))
309158115Sume		return;
310158115Sume	if (SID_TYPE(inq_data) != T_DIRECT)
311158115Sume		return;
312158115Sume	if (SID_QUAL(inq_data) != SID_QUAL_LU_CONNECTED)
313158115Sume		return;
314158115Sume
315158115Sume	cam_strvis(vendor, inq_data->vendor, sizeof(inq_data->vendor),
316158115Sume	    sizeof(vendor));
317158115Sume	cam_strvis(product, inq_data->product, sizeof(inq_data->product),
318158115Sume	    sizeof(product));
319158115Sume	cam_strvis(revision, inq_data->revision, sizeof(inq_data->revision),
320158115Sume	    sizeof(revision));
321158115Sume
322158115Sume	/* Hack for SATA disks, no idea how to tell speed. */
323158115Sume	if (strcmp(vendor, "ATA") == 0) {
324158115Sume		snprintf(disk->inqstring, sizeof(disk->inqstring),
325158115Sume		    "<%s %s> SATA", product, revision);
326158115Sume		return;
327158115Sume	}
328158115Sume
329158115Sume	switch (SID_ANSI_REV(inq_data)) {
330158115Sume	case SCSI_REV_CCS:
331158115Sume		strcpy(rstr, "SCSI-CCS");
332158115Sume		break;
333158115Sume	case 5:
334158115Sume		strcpy(rstr, "SAS");
335158115Sume		break;
336158115Sume	default:
337158115Sume		snprintf(rstr, sizeof (rstr), "SCSI-%d",
338158115Sume		    SID_ANSI_REV(inq_data));
339158115Sume		break;
340158115Sume	}
341158115Sume	snprintf(disk->inqstring, sizeof(disk->inqstring), "<%s %s %s> %s",
342158115Sume	    vendor, product, revision, rstr);
343158115Sume}
344158115Sume
345158115Sume/* Much borrowed from scsiinquiry() in src/sbin/camcontrol/camcontrol.c. */
346158115Sumestatic int
347158115Sumefetch_scsi_inquiry(struct cam_device *dev, struct mpt_standalone_disk *disk)
348158115Sume{
349158115Sume	struct scsi_inquiry_data *inq_buf;
350158115Sume	union ccb *ccb;
351158115Sume	int error;
352158115Sume
353158115Sume	ccb = cam_getccb(dev);
354158115Sume	if (ccb == NULL)
355158115Sume		return (ENOMEM);
356158115Sume
357158115Sume	/* Zero the rest of the ccb. */
358158115Sume	bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_scsiio) -
359158115Sume	    sizeof(struct ccb_hdr));
360158115Sume
361158115Sume	inq_buf = calloc(1, sizeof(*inq_buf));
362158115Sume	if (inq_buf == NULL) {
363158115Sume		cam_freeccb(ccb);
364158115Sume		return (ENOMEM);
365158115Sume	}
366158115Sume	scsi_inquiry(&ccb->csio, 1, NULL, MSG_SIMPLE_Q_TAG, (void *)inq_buf,
367158115Sume	    SHORT_INQUIRY_LENGTH, 0, 0, SSD_FULL_SIZE, 5000);
368158115Sume
369158115Sume	/* Disable freezing the device queue */
370158115Sume	ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
371158115Sume
372158115Sume	if (cam_send_ccb(dev, ccb) < 0) {
373158115Sume		error = errno;
374158115Sume		free(inq_buf);
375158115Sume		cam_freeccb(ccb);
376158115Sume		return (error);
377158115Sume	}
378158115Sume
379158115Sume	if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
380158115Sume		free(inq_buf);
381158115Sume		cam_freeccb(ccb);
382158115Sume		return (EIO);
383158115Sume	}
384158115Sume
385158115Sume	cam_freeccb(ccb);
386158115Sume	format_scsi_inquiry(disk, inq_buf);
387158115Sume	free(inq_buf);
388158115Sume	return (0);
389158115Sume}
390158115Sume
391158115Sumeint
392158115Sumempt_fetch_disks(int fd, int *ndisks, struct mpt_standalone_disk **disksp)
393158115Sume{
394158115Sume	CONFIG_PAGE_IOC_2 *ioc2;
395158115Sume	struct mpt_standalone_disk *disks;
396158115Sume	struct periph_match_pattern *p;
397158115Sume	struct periph_match_result *r;
398158115Sume	struct cam_device *dev;
399158115Sume	union ccb ccb;
400158115Sume	path_id_t path_id;
401158115Sume	size_t bufsize;
402158115Sume	int count, error;
403158115Sume	uint32_t i;
404158115Sume
405158115Sume	if (xpt_open() < 0)
406158115Sume		return (ENXIO);
407158115Sume
408158115Sume	error = fetch_path_id(&path_id);
409158115Sume	if (error)
410158115Sume		return (error);
411158115Sume
412158115Sume	for (count = 100;; count+= 100) {
413158115Sume		/* Try to fetch 'count' disks in one go. */
414158115Sume		bzero(&ccb, sizeof(ccb));
415158115Sume
416158115Sume		ccb.ccb_h.func_code = XPT_DEV_MATCH;
417158115Sume
418158115Sume		bufsize = sizeof(struct dev_match_result) * (count + 1);
419158115Sume		ccb.cdm.num_matches = 0;
420158115Sume		ccb.cdm.match_buf_len = bufsize;
421158115Sume		ccb.cdm.matches = calloc(1, bufsize);
422158115Sume
423158115Sume		bufsize = sizeof(struct dev_match_pattern) * 1;
424158115Sume		ccb.cdm.num_patterns = 1;
425158115Sume		ccb.cdm.pattern_buf_len = bufsize;
426158115Sume		ccb.cdm.patterns = calloc(1, bufsize);
427158115Sume
428238094Sse		/* Match any "da" peripherals. */
429238094Sse		ccb.cdm.patterns[0].type = DEV_MATCH_PERIPH;
430238094Sse		p = &ccb.cdm.patterns[0].pattern.periph_pattern;
431238094Sse		p->path_id = path_id;
432238094Sse		snprintf(p->periph_name, sizeof(p->periph_name), "da");
433238094Sse		p->flags = PERIPH_MATCH_PATH | PERIPH_MATCH_NAME;
434158115Sume
435158115Sume		if (ioctl(xptfd, CAMIOCOMMAND, &ccb) < 0) {
436158115Sume			error = errno;
437158115Sume			free(ccb.cdm.matches);
438158115Sume			free(ccb.cdm.patterns);
439158115Sume			return (error);
440158115Sume		}
441158115Sume		free(ccb.cdm.patterns);
442158115Sume
443158115Sume		/* Check for CCB errors. */
444158115Sume		if ((ccb.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
445158115Sume			free(ccb.cdm.matches);
446158115Sume			return (EIO);
447158115Sume		}
448158115Sume
449158115Sume		/* If we need a longer list, try again. */
450158115Sume		if (ccb.cdm.status == CAM_DEV_MATCH_MORE) {
451158115Sume			free(ccb.cdm.matches);
452158115Sume			continue;
453158115Sume		}
454158115Sume
455158115Sume		/* If we got an error, abort. */
456158115Sume		if (ccb.cdm.status != CAM_DEV_MATCH_LAST) {
457238094Sse			free(ccb.cdm.matches);
458238094Sse			return (EIO);
459238094Sse		}
460238094Sse		break;
461238094Sse	}
462238094Sse
463158115Sume	/* Shortcut if we don't have any "da" devices. */
464158115Sume	if (ccb.cdm.num_matches == 0) {
465158115Sume		free(ccb.cdm.matches);
466158115Sume		*ndisks = 0;
467158115Sume		*disksp = NULL;
468158115Sume		return (0);
469158115Sume	}
470158115Sume
471158115Sume	/* We should have N matches, 1 for each "da" device. */
472158115Sume	for (i = 0; i < ccb.cdm.num_matches; i++) {
473158115Sume		if (ccb.cdm.matches[i].type != DEV_MATCH_PERIPH) {
474158115Sume			warnx("mpt_fetch_disks got wrong CAM matches");
475158115Sume			free(ccb.cdm.matches);
476158115Sume			return (EIO);
477158115Sume		}
478158115Sume	}
479158115Sume
480158115Sume	/*
481158115Sume	 * Some of the "da" peripherals may be for RAID volumes, so
482158115Sume	 * fetch the IOC 2 page (list of RAID volumes) so we can
483158115Sume	 * exclude them from the list.
484158115Sume	 */
485158115Sume	ioc2 = mpt_read_ioc_page(fd, 2, NULL);
486158115Sume	if (ioc2 == NULL)
487158115Sume		return (errno);
488158115Sume	disks = calloc(ccb.cdm.num_matches, sizeof(*disks));
489158115Sume	count = 0;
490158115Sume	for (i = 0; i < ccb.cdm.num_matches; i++) {
491158115Sume		r = &ccb.cdm.matches[i].result.periph_result;
492158115Sume		if (periph_is_volume(ioc2, r))
493158115Sume			continue;
494158115Sume		disks[count].bus = 0;
495158115Sume		disks[count].target = r->target_id;
496158115Sume		snprintf(disks[count].devname, sizeof(disks[count].devname),
497158115Sume		    "%s%d", r->periph_name, r->unit_number);
498158115Sume
499158115Sume		dev = cam_open_device(disks[count].devname, O_RDWR);
500158115Sume		if (dev != NULL) {
501158115Sume			fetch_scsi_capacity(dev, &disks[count]);
502158115Sume			fetch_scsi_inquiry(dev, &disks[count]);
503158115Sume			cam_close_device(dev);
504158115Sume		}
505158115Sume		count++;
506158115Sume	}
507158115Sume	free(ccb.cdm.matches);
508158115Sume	free(ioc2);
509158115Sume
510158115Sume	*ndisks = count;
511158115Sume	*disksp = disks;
512158115Sume	return (0);
513158115Sume}
514158115Sume
515158115Sume/*
516158115Sume * Instruct the mpt(4) device to rescan its busses to find new devices
517158115Sume * such as disks whose RAID physdisk page was removed or volumes that
518158115Sume * were created.  If id is -1, the entire bus is rescanned.
519158115Sume * Otherwise, only devices at the specified ID are rescanned.  If bus
520158115Sume * is -1, then all busses are scanned instead of the specified bus.
521158115Sume * Note that currently, only bus 0 is supported.
522158115Sume */
523int
524mpt_rescan_bus(int bus, int id)
525{
526	union ccb ccb;
527	path_id_t path_id;
528	int error;
529
530	/* mpt(4) only handles devices on bus 0. */
531	if (bus != -1 && bus != 0)
532		return (EINVAL);
533
534	if (xpt_open() < 0)
535		return (ENXIO);
536
537	error = fetch_path_id(&path_id);
538	if (error)
539		return (error);
540
541	/* Perform the actual rescan. */
542	bzero(&ccb, sizeof(ccb));
543	ccb.ccb_h.path_id = path_id;
544	if (id == -1) {
545		ccb.ccb_h.func_code = XPT_SCAN_BUS;
546		ccb.ccb_h.target_id = CAM_TARGET_WILDCARD;
547		ccb.ccb_h.target_lun = CAM_LUN_WILDCARD;
548		ccb.ccb_h.timeout = 5000;
549	} else {
550		ccb.ccb_h.func_code = XPT_SCAN_LUN;
551		ccb.ccb_h.target_id = id;
552		ccb.ccb_h.target_lun = 0;
553	}
554	ccb.crcn.flags = CAM_FLAG_NONE;
555
556	/* Run this at a low priority. */
557	ccb.ccb_h.pinfo.priority = 5;
558
559	if (ioctl(xptfd, CAMIOCOMMAND, &ccb) == -1)
560		return (errno);
561
562	if ((ccb.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
563		warnx("mpt_rescan_bus rescan got CAM error %#x\n",
564		    ccb.ccb_h.status & CAM_STATUS_MASK);
565		return (EIO);
566	}
567
568	return (0);
569}
570