1196212Sscottl/*-
2196212Sscottl * Copyright (c) 2008 Yahoo!, Inc.
3196212Sscottl * All rights reserved.
4196212Sscottl * Written by: John Baldwin <jhb@FreeBSD.org>
5196212Sscottl *
6196212Sscottl * Redistribution and use in source and binary forms, with or without
7196212Sscottl * modification, are permitted provided that the following conditions
8196212Sscottl * are met:
9196212Sscottl * 1. Redistributions of source code must retain the above copyright
10196212Sscottl *    notice, this list of conditions and the following disclaimer.
11196212Sscottl * 2. Redistributions in binary form must reproduce the above copyright
12196212Sscottl *    notice, this list of conditions and the following disclaimer in the
13196212Sscottl *    documentation and/or other materials provided with the distribution.
14196212Sscottl * 3. Neither the name of the author nor the names of any co-contributors
15196212Sscottl *    may be used to endorse or promote products derived from this software
16196212Sscottl *    without specific prior written permission.
17196212Sscottl *
18196212Sscottl * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19196212Sscottl * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20196212Sscottl * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21196212Sscottl * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22196212Sscottl * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23196212Sscottl * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24196212Sscottl * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25196212Sscottl * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26196212Sscottl * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27196212Sscottl * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28196212Sscottl * SUCH DAMAGE.
29196212Sscottl */
30196212Sscottl
31196212Sscottl#include <sys/cdefs.h>
32196212Sscottl__RCSID("$FreeBSD$");
33196212Sscottl
34196212Sscottl#include <sys/param.h>
35196212Sscottl#include <err.h>
36196212Sscottl#include <errno.h>
37196212Sscottl#include <fcntl.h>
38196212Sscottl#include <stdio.h>
39196212Sscottl#include <stdlib.h>
40196212Sscottl#include <string.h>
41196212Sscottl
42196212Sscottl#include <camlib.h>
43196212Sscottl#include <cam/scsi/scsi_message.h>
44196212Sscottl#include <cam/scsi/scsi_pass.h>
45196212Sscottl
46196212Sscottl#include "mptutil.h"
47196212Sscottl
48196212Sscottlstatic int xptfd;
49196212Sscottl
50196212Sscottlstatic int
51196212Sscottlxpt_open(void)
52196212Sscottl{
53196212Sscottl
54196212Sscottl	if (xptfd == 0)
55196212Sscottl		xptfd = open(XPT_DEVICE, O_RDWR);
56196212Sscottl	return (xptfd);
57196212Sscottl}
58196212Sscottl
59204090Sjhb/* Fetch the path id of bus 0 for the opened mpt controller. */
60204090Sjhbstatic int
61204090Sjhbfetch_path_id(path_id_t *path_id)
62204090Sjhb{
63204090Sjhb	struct bus_match_pattern *b;
64204090Sjhb	union ccb ccb;
65204090Sjhb	size_t bufsize;
66215046Sjhb	int error;
67204090Sjhb
68204090Sjhb	if (xpt_open() < 0)
69204090Sjhb		return (ENXIO);
70204090Sjhb
71204090Sjhb	/* First, find the path id of bus 0 for this mpt controller. */
72204090Sjhb	bzero(&ccb, sizeof(ccb));
73204090Sjhb
74204090Sjhb	ccb.ccb_h.func_code = XPT_DEV_MATCH;
75204090Sjhb
76204090Sjhb	bufsize = sizeof(struct dev_match_result) * 1;
77204090Sjhb	ccb.cdm.num_matches = 0;
78204090Sjhb	ccb.cdm.match_buf_len = bufsize;
79204090Sjhb	ccb.cdm.matches = calloc(1, bufsize);
80204090Sjhb
81204090Sjhb	bufsize = sizeof(struct dev_match_pattern) * 1;
82204090Sjhb	ccb.cdm.num_patterns = 1;
83204090Sjhb	ccb.cdm.pattern_buf_len = bufsize;
84204090Sjhb	ccb.cdm.patterns = calloc(1, bufsize);
85204090Sjhb
86204090Sjhb	/* Match mptX bus 0. */
87204090Sjhb	ccb.cdm.patterns[0].type = DEV_MATCH_BUS;
88204090Sjhb	b = &ccb.cdm.patterns[0].pattern.bus_pattern;
89204090Sjhb	snprintf(b->dev_name, sizeof(b->dev_name), "mpt");
90204090Sjhb	b->unit_number = mpt_unit;
91204090Sjhb	b->bus_id = 0;
92204090Sjhb	b->flags = BUS_MATCH_NAME | BUS_MATCH_UNIT | BUS_MATCH_BUS_ID;
93204090Sjhb
94204090Sjhb	if (ioctl(xptfd, CAMIOCOMMAND, &ccb) < 0) {
95215046Sjhb		error = errno;
96204090Sjhb		free(ccb.cdm.matches);
97204090Sjhb		free(ccb.cdm.patterns);
98215046Sjhb		return (error);
99204090Sjhb	}
100204090Sjhb	free(ccb.cdm.patterns);
101204090Sjhb
102204090Sjhb	if (((ccb.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) ||
103204090Sjhb	    (ccb.cdm.status != CAM_DEV_MATCH_LAST)) {
104204090Sjhb		warnx("fetch_path_id got CAM error %#x, CDM error %d\n",
105204090Sjhb		    ccb.ccb_h.status, ccb.cdm.status);
106204090Sjhb		free(ccb.cdm.matches);
107204090Sjhb		return (EIO);
108204090Sjhb	}
109204090Sjhb
110204090Sjhb	/* We should have exactly 1 match for the bus. */
111204090Sjhb	if (ccb.cdm.num_matches != 1 ||
112204090Sjhb	    ccb.cdm.matches[0].type != DEV_MATCH_BUS) {
113204090Sjhb		free(ccb.cdm.matches);
114204090Sjhb		return (ENOENT);
115204090Sjhb	}
116204090Sjhb	*path_id = ccb.cdm.matches[0].result.bus_result.path_id;
117204090Sjhb	free(ccb.cdm.matches);
118204090Sjhb	return (0);
119204090Sjhb}
120204090Sjhb
121196212Sscottlint
122196212Sscottlmpt_query_disk(U8 VolumeBus, U8 VolumeID, struct mpt_query_disk *qd)
123196212Sscottl{
124196212Sscottl	struct periph_match_pattern *p;
125196212Sscottl	struct periph_match_result *r;
126196212Sscottl	union ccb ccb;
127204090Sjhb	path_id_t path_id;
128196212Sscottl	size_t bufsize;
129215046Sjhb	int error;
130196212Sscottl
131196212Sscottl	/* mpt(4) only handles devices on bus 0. */
132196212Sscottl	if (VolumeBus != 0)
133196212Sscottl		return (ENXIO);
134196212Sscottl
135196212Sscottl	if (xpt_open() < 0)
136196212Sscottl		return (ENXIO);
137196212Sscottl
138204090Sjhb	/* Find the path ID of bus 0. */
139204090Sjhb	error = fetch_path_id(&path_id);
140204090Sjhb	if (error)
141204090Sjhb		return (error);
142204090Sjhb
143196212Sscottl	bzero(&ccb, sizeof(ccb));
144196212Sscottl
145196212Sscottl	ccb.ccb_h.func_code = XPT_DEV_MATCH;
146196212Sscottl	ccb.ccb_h.path_id = CAM_XPT_PATH_ID;
147196212Sscottl	ccb.ccb_h.target_id = CAM_TARGET_WILDCARD;
148196212Sscottl	ccb.ccb_h.target_lun = CAM_LUN_WILDCARD;
149196212Sscottl
150196212Sscottl	bufsize = sizeof(struct dev_match_result) * 5;
151196212Sscottl	ccb.cdm.num_matches = 0;
152196212Sscottl	ccb.cdm.match_buf_len = bufsize;
153196212Sscottl	ccb.cdm.matches = calloc(1, bufsize);
154196212Sscottl
155204090Sjhb	bufsize = sizeof(struct dev_match_pattern) * 1;
156204090Sjhb	ccb.cdm.num_patterns = 1;
157196212Sscottl	ccb.cdm.pattern_buf_len = bufsize;
158196212Sscottl	ccb.cdm.patterns = calloc(1, bufsize);
159196212Sscottl
160196212Sscottl	/* Look for a "da" device at the specified target and lun. */
161204090Sjhb	ccb.cdm.patterns[0].type = DEV_MATCH_PERIPH;
162204090Sjhb	p = &ccb.cdm.patterns[0].pattern.periph_pattern;
163204090Sjhb	p->path_id = path_id;
164196212Sscottl	snprintf(p->periph_name, sizeof(p->periph_name), "da");
165196212Sscottl	p->target_id = VolumeID;
166204090Sjhb	p->flags = PERIPH_MATCH_PATH | PERIPH_MATCH_NAME | PERIPH_MATCH_TARGET;
167196212Sscottl
168196212Sscottl	if (ioctl(xptfd, CAMIOCOMMAND, &ccb) < 0) {
169215046Sjhb		error = errno;
170196212Sscottl		free(ccb.cdm.matches);
171196212Sscottl		free(ccb.cdm.patterns);
172215046Sjhb		return (error);
173196212Sscottl	}
174196212Sscottl	free(ccb.cdm.patterns);
175196212Sscottl
176196212Sscottl	if (((ccb.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) ||
177196212Sscottl	    (ccb.cdm.status != CAM_DEV_MATCH_LAST)) {
178196212Sscottl		warnx("mpt_query_disk got CAM error %#x, CDM error %d\n",
179196212Sscottl		    ccb.ccb_h.status, ccb.cdm.status);
180196212Sscottl		free(ccb.cdm.matches);
181196212Sscottl		return (EIO);
182196212Sscottl	}
183196212Sscottl
184196212Sscottl	/*
185204090Sjhb	 * We should have exactly 1 match for the peripheral.
186204090Sjhb	 * However, if we don't get a match, don't print an error
187204090Sjhb	 * message and return ENOENT.
188196212Sscottl	 */
189204090Sjhb	if (ccb.cdm.num_matches == 0) {
190196212Sscottl		free(ccb.cdm.matches);
191196212Sscottl		return (ENOENT);
192196212Sscottl	}
193204090Sjhb	if (ccb.cdm.num_matches != 1) {
194204090Sjhb		warnx("mpt_query_disk got %d matches, expected 1",
195196212Sscottl		    ccb.cdm.num_matches);
196196212Sscottl		free(ccb.cdm.matches);
197196212Sscottl		return (EIO);
198196212Sscottl	}
199204090Sjhb	if (ccb.cdm.matches[0].type != DEV_MATCH_PERIPH) {
200204090Sjhb		warnx("mpt_query_disk got wrong CAM match");
201196212Sscottl		free(ccb.cdm.matches);
202196212Sscottl		return (EIO);
203196212Sscottl	}
204196212Sscottl
205196212Sscottl	/* Copy out the data. */
206196212Sscottl	r = &ccb.cdm.matches[1].result.periph_result;
207196212Sscottl	snprintf(qd->devname, sizeof(qd->devname), "%s%d", r->periph_name,
208196212Sscottl	    r->unit_number);
209196212Sscottl	free(ccb.cdm.matches);
210196212Sscottl
211196212Sscottl	return (0);
212196212Sscottl}
213196212Sscottl
214196212Sscottlstatic int
215196212Sscottlperiph_is_volume(CONFIG_PAGE_IOC_2 *ioc2, struct periph_match_result *r)
216196212Sscottl{
217196212Sscottl	CONFIG_PAGE_IOC_2_RAID_VOL *vol;
218196212Sscottl	int i;
219196212Sscottl
220196212Sscottl	if (ioc2 == NULL)
221196212Sscottl		return (0);
222196212Sscottl	vol = ioc2->RaidVolume;
223196212Sscottl	for (i = 0; i < ioc2->NumActiveVolumes; vol++, i++) {
224196212Sscottl		if (vol->VolumeBus == 0 && vol->VolumeID == r->target_id)
225196212Sscottl			return (1);
226196212Sscottl	}
227196212Sscottl	return (0);
228196212Sscottl}
229196212Sscottl
230196212Sscottl/* Much borrowed from scsireadcapacity() in src/sbin/camcontrol/camcontrol.c. */
231196212Sscottlstatic int
232196212Sscottlfetch_scsi_capacity(struct cam_device *dev, struct mpt_standalone_disk *disk)
233196212Sscottl{
234196212Sscottl	struct scsi_read_capacity_data rcap;
235196212Sscottl	struct scsi_read_capacity_data_long rcaplong;
236196212Sscottl	union ccb *ccb;
237196212Sscottl	int error;
238196212Sscottl
239196212Sscottl	ccb = cam_getccb(dev);
240196212Sscottl	if (ccb == NULL)
241196212Sscottl		return (ENOMEM);
242196212Sscottl
243196212Sscottl	/* Zero the rest of the ccb. */
244196212Sscottl	bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_scsiio) -
245196212Sscottl	    sizeof(struct ccb_hdr));
246196212Sscottl
247196212Sscottl	scsi_read_capacity(&ccb->csio, 1, NULL, MSG_SIMPLE_Q_TAG, &rcap,
248196212Sscottl	    SSD_FULL_SIZE, 5000);
249196212Sscottl
250196212Sscottl	/* Disable freezing the device queue */
251196212Sscottl	ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
252196212Sscottl
253196212Sscottl	if (cam_send_ccb(dev, ccb) < 0) {
254196212Sscottl		error = errno;
255196212Sscottl		cam_freeccb(ccb);
256196212Sscottl		return (error);
257196212Sscottl	}
258196212Sscottl
259196212Sscottl	if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
260196212Sscottl		cam_freeccb(ccb);
261196212Sscottl		return (EIO);
262196212Sscottl	}
263196212Sscottl	cam_freeccb(ccb);
264196212Sscottl
265196212Sscottl	/*
266196212Sscottl	 * A last block of 2^32-1 means that the true capacity is over 2TB,
267196212Sscottl	 * and we need to issue the long READ CAPACITY to get the real
268196212Sscottl	 * capacity.  Otherwise, we're all set.
269196212Sscottl	 */
270196212Sscottl	if (scsi_4btoul(rcap.addr) != 0xffffffff) {
271196212Sscottl		disk->maxlba = scsi_4btoul(rcap.addr);
272196212Sscottl		return (0);
273196212Sscottl	}
274196212Sscottl
275196212Sscottl	/* Zero the rest of the ccb. */
276196212Sscottl	bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_scsiio) -
277196212Sscottl	    sizeof(struct ccb_hdr));
278196212Sscottl
279196212Sscottl	scsi_read_capacity_16(&ccb->csio, 1, NULL, MSG_SIMPLE_Q_TAG, 0, 0, 0,
280230590Sken	    (uint8_t *)&rcaplong, sizeof(rcaplong), SSD_FULL_SIZE, 5000);
281196212Sscottl
282196212Sscottl	/* Disable freezing the device queue */
283196212Sscottl	ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
284196212Sscottl
285196212Sscottl	if (cam_send_ccb(dev, ccb) < 0) {
286196212Sscottl		error = errno;
287196212Sscottl		cam_freeccb(ccb);
288196212Sscottl		return (error);
289196212Sscottl	}
290196212Sscottl
291196212Sscottl	if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
292196212Sscottl		cam_freeccb(ccb);
293196212Sscottl		return (EIO);
294196212Sscottl	}
295196212Sscottl	cam_freeccb(ccb);
296196212Sscottl
297196212Sscottl	disk->maxlba = scsi_8btou64(rcaplong.addr);
298196212Sscottl	return (0);
299196212Sscottl}
300196212Sscottl
301196212Sscottl/* Borrowed heavily from scsi_all.c:scsi_print_inquiry(). */
302196212Sscottlstatic void
303196212Sscottlformat_scsi_inquiry(struct mpt_standalone_disk *disk,
304196212Sscottl    struct scsi_inquiry_data *inq_data)
305196212Sscottl{
306196212Sscottl	char vendor[16], product[48], revision[16], rstr[12];
307196212Sscottl
308196212Sscottl	if (SID_QUAL_IS_VENDOR_UNIQUE(inq_data))
309196212Sscottl		return;
310196212Sscottl	if (SID_TYPE(inq_data) != T_DIRECT)
311196212Sscottl		return;
312196212Sscottl	if (SID_QUAL(inq_data) != SID_QUAL_LU_CONNECTED)
313196212Sscottl		return;
314196212Sscottl
315196212Sscottl	cam_strvis(vendor, inq_data->vendor, sizeof(inq_data->vendor),
316196212Sscottl	    sizeof(vendor));
317196212Sscottl	cam_strvis(product, inq_data->product, sizeof(inq_data->product),
318196212Sscottl	    sizeof(product));
319196212Sscottl	cam_strvis(revision, inq_data->revision, sizeof(inq_data->revision),
320196212Sscottl	    sizeof(revision));
321196212Sscottl
322196212Sscottl	/* Hack for SATA disks, no idea how to tell speed. */
323196212Sscottl	if (strcmp(vendor, "ATA") == 0) {
324196212Sscottl		snprintf(disk->inqstring, sizeof(disk->inqstring),
325196212Sscottl		    "<%s %s> SATA", product, revision);
326196212Sscottl		return;
327196212Sscottl	}
328196212Sscottl
329196212Sscottl	switch (SID_ANSI_REV(inq_data)) {
330196212Sscottl	case SCSI_REV_CCS:
331196212Sscottl		strcpy(rstr, "SCSI-CCS");
332196212Sscottl		break;
333196212Sscottl	case 5:
334196212Sscottl		strcpy(rstr, "SAS");
335196212Sscottl		break;
336196212Sscottl	default:
337196212Sscottl		snprintf(rstr, sizeof (rstr), "SCSI-%d",
338196212Sscottl		    SID_ANSI_REV(inq_data));
339196212Sscottl		break;
340196212Sscottl	}
341196212Sscottl	snprintf(disk->inqstring, sizeof(disk->inqstring), "<%s %s %s> %s",
342196212Sscottl	    vendor, product, revision, rstr);
343196212Sscottl}
344196212Sscottl
345196212Sscottl/* Much borrowed from scsiinquiry() in src/sbin/camcontrol/camcontrol.c. */
346196212Sscottlstatic int
347196212Sscottlfetch_scsi_inquiry(struct cam_device *dev, struct mpt_standalone_disk *disk)
348196212Sscottl{
349196212Sscottl	struct scsi_inquiry_data *inq_buf;
350196212Sscottl	union ccb *ccb;
351196212Sscottl	int error;
352196212Sscottl
353196212Sscottl	ccb = cam_getccb(dev);
354196212Sscottl	if (ccb == NULL)
355196212Sscottl		return (ENOMEM);
356196212Sscottl
357196212Sscottl	/* Zero the rest of the ccb. */
358196212Sscottl	bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_scsiio) -
359196212Sscottl	    sizeof(struct ccb_hdr));
360196212Sscottl
361196212Sscottl	inq_buf = calloc(1, sizeof(*inq_buf));
362196212Sscottl	if (inq_buf == NULL) {
363196212Sscottl		cam_freeccb(ccb);
364196212Sscottl		return (ENOMEM);
365196212Sscottl	}
366196212Sscottl	scsi_inquiry(&ccb->csio, 1, NULL, MSG_SIMPLE_Q_TAG, (void *)inq_buf,
367196212Sscottl	    SHORT_INQUIRY_LENGTH, 0, 0, SSD_FULL_SIZE, 5000);
368196212Sscottl
369196212Sscottl	/* Disable freezing the device queue */
370196212Sscottl	ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
371196212Sscottl
372196212Sscottl	if (cam_send_ccb(dev, ccb) < 0) {
373196212Sscottl		error = errno;
374196212Sscottl		free(inq_buf);
375196212Sscottl		cam_freeccb(ccb);
376196212Sscottl		return (error);
377196212Sscottl	}
378196212Sscottl
379196212Sscottl	if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
380196212Sscottl		free(inq_buf);
381196212Sscottl		cam_freeccb(ccb);
382196212Sscottl		return (EIO);
383196212Sscottl	}
384196212Sscottl
385196212Sscottl	cam_freeccb(ccb);
386196212Sscottl	format_scsi_inquiry(disk, inq_buf);
387196212Sscottl	free(inq_buf);
388196212Sscottl	return (0);
389196212Sscottl}
390196212Sscottl
391196212Sscottlint
392196212Sscottlmpt_fetch_disks(int fd, int *ndisks, struct mpt_standalone_disk **disksp)
393196212Sscottl{
394196212Sscottl	CONFIG_PAGE_IOC_2 *ioc2;
395196212Sscottl	struct mpt_standalone_disk *disks;
396196212Sscottl	struct periph_match_pattern *p;
397196212Sscottl	struct periph_match_result *r;
398196212Sscottl	struct cam_device *dev;
399196212Sscottl	union ccb ccb;
400204090Sjhb	path_id_t path_id;
401196212Sscottl	size_t bufsize;
402204090Sjhb	int count, error;
403215046Sjhb	uint32_t i;
404196212Sscottl
405196212Sscottl	if (xpt_open() < 0)
406196212Sscottl		return (ENXIO);
407196212Sscottl
408204090Sjhb	error = fetch_path_id(&path_id);
409204090Sjhb	if (error)
410204090Sjhb		return (error);
411204090Sjhb
412196212Sscottl	for (count = 100;; count+= 100) {
413196212Sscottl		/* Try to fetch 'count' disks in one go. */
414196212Sscottl		bzero(&ccb, sizeof(ccb));
415196212Sscottl
416196212Sscottl		ccb.ccb_h.func_code = XPT_DEV_MATCH;
417196212Sscottl
418204090Sjhb		bufsize = sizeof(struct dev_match_result) * (count + 1);
419196212Sscottl		ccb.cdm.num_matches = 0;
420196212Sscottl		ccb.cdm.match_buf_len = bufsize;
421196212Sscottl		ccb.cdm.matches = calloc(1, bufsize);
422196212Sscottl
423204090Sjhb		bufsize = sizeof(struct dev_match_pattern) * 1;
424204090Sjhb		ccb.cdm.num_patterns = 1;
425196212Sscottl		ccb.cdm.pattern_buf_len = bufsize;
426196212Sscottl		ccb.cdm.patterns = calloc(1, bufsize);
427196212Sscottl
428196212Sscottl		/* Match any "da" peripherals. */
429204090Sjhb		ccb.cdm.patterns[0].type = DEV_MATCH_PERIPH;
430204090Sjhb		p = &ccb.cdm.patterns[0].pattern.periph_pattern;
431204090Sjhb		p->path_id = path_id;
432196212Sscottl		snprintf(p->periph_name, sizeof(p->periph_name), "da");
433204090Sjhb		p->flags = PERIPH_MATCH_PATH | PERIPH_MATCH_NAME;
434196212Sscottl
435196212Sscottl		if (ioctl(xptfd, CAMIOCOMMAND, &ccb) < 0) {
436215046Sjhb			error = errno;
437196212Sscottl			free(ccb.cdm.matches);
438196212Sscottl			free(ccb.cdm.patterns);
439215046Sjhb			return (error);
440196212Sscottl		}
441196212Sscottl		free(ccb.cdm.patterns);
442196212Sscottl
443196212Sscottl		/* Check for CCB errors. */
444196212Sscottl		if ((ccb.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
445196212Sscottl			free(ccb.cdm.matches);
446196212Sscottl			return (EIO);
447196212Sscottl		}
448196212Sscottl
449196212Sscottl		/* If we need a longer list, try again. */
450196212Sscottl		if (ccb.cdm.status == CAM_DEV_MATCH_MORE) {
451196212Sscottl			free(ccb.cdm.matches);
452196212Sscottl			continue;
453196212Sscottl		}
454196212Sscottl
455196212Sscottl		/* If we got an error, abort. */
456196212Sscottl		if (ccb.cdm.status != CAM_DEV_MATCH_LAST) {
457196212Sscottl			free(ccb.cdm.matches);
458196212Sscottl			return (EIO);
459196212Sscottl		}
460196212Sscottl		break;
461196212Sscottl	}
462196212Sscottl
463204090Sjhb	/* Shortcut if we don't have any "da" devices. */
464204090Sjhb	if (ccb.cdm.num_matches == 0) {
465196212Sscottl		free(ccb.cdm.matches);
466204090Sjhb		*ndisks = 0;
467204090Sjhb		*disksp = NULL;
468204090Sjhb		return (0);
469196212Sscottl	}
470204090Sjhb
471204090Sjhb	/* We should have N matches, 1 for each "da" device. */
472204090Sjhb	for (i = 0; i < ccb.cdm.num_matches; i++) {
473196212Sscottl		if (ccb.cdm.matches[i].type != DEV_MATCH_PERIPH) {
474196212Sscottl			warnx("mpt_fetch_disks got wrong CAM matches");
475196212Sscottl			free(ccb.cdm.matches);
476196212Sscottl			return (EIO);
477196212Sscottl		}
478196212Sscottl	}
479196212Sscottl
480196212Sscottl	/*
481196212Sscottl	 * Some of the "da" peripherals may be for RAID volumes, so
482196212Sscottl	 * fetch the IOC 2 page (list of RAID volumes) so we can
483196212Sscottl	 * exclude them from the list.
484196212Sscottl	 */
485196212Sscottl	ioc2 = mpt_read_ioc_page(fd, 2, NULL);
486215046Sjhb	if (ioc2 == NULL)
487215046Sjhb		return (errno);
488196212Sscottl	disks = calloc(ccb.cdm.num_matches, sizeof(*disks));
489196212Sscottl	count = 0;
490204090Sjhb	for (i = 0; i < ccb.cdm.num_matches; i++) {
491196212Sscottl		r = &ccb.cdm.matches[i].result.periph_result;
492196212Sscottl		if (periph_is_volume(ioc2, r))
493196212Sscottl			continue;
494196212Sscottl		disks[count].bus = 0;
495196212Sscottl		disks[count].target = r->target_id;
496196212Sscottl		snprintf(disks[count].devname, sizeof(disks[count].devname),
497196212Sscottl		    "%s%d", r->periph_name, r->unit_number);
498196212Sscottl
499196212Sscottl		dev = cam_open_device(disks[count].devname, O_RDWR);
500196212Sscottl		if (dev != NULL) {
501196212Sscottl			fetch_scsi_capacity(dev, &disks[count]);
502196212Sscottl			fetch_scsi_inquiry(dev, &disks[count]);
503196212Sscottl			cam_close_device(dev);
504196212Sscottl		}
505196212Sscottl		count++;
506196212Sscottl	}
507196212Sscottl	free(ccb.cdm.matches);
508196212Sscottl	free(ioc2);
509196212Sscottl
510196212Sscottl	*ndisks = count;
511196212Sscottl	*disksp = disks;
512196212Sscottl	return (0);
513196212Sscottl}
514196212Sscottl
515196212Sscottl/*
516196212Sscottl * Instruct the mpt(4) device to rescan its busses to find new devices
517196212Sscottl * such as disks whose RAID physdisk page was removed or volumes that
518196212Sscottl * were created.  If id is -1, the entire bus is rescanned.
519196212Sscottl * Otherwise, only devices at the specified ID are rescanned.  If bus
520196212Sscottl * is -1, then all busses are scanned instead of the specified bus.
521196212Sscottl * Note that currently, only bus 0 is supported.
522196212Sscottl */
523196212Sscottlint
524196212Sscottlmpt_rescan_bus(int bus, int id)
525196212Sscottl{
526196212Sscottl	union ccb ccb;
527196212Sscottl	path_id_t path_id;
528204090Sjhb	int error;
529196212Sscottl
530196212Sscottl	/* mpt(4) only handles devices on bus 0. */
531196212Sscottl	if (bus != -1 && bus != 0)
532196212Sscottl		return (EINVAL);
533196212Sscottl
534196212Sscottl	if (xpt_open() < 0)
535196212Sscottl		return (ENXIO);
536196212Sscottl
537204090Sjhb	error = fetch_path_id(&path_id);
538204090Sjhb	if (error)
539204090Sjhb		return (error);
540204090Sjhb
541204090Sjhb	/* Perform the actual rescan. */
542196212Sscottl	bzero(&ccb, sizeof(ccb));
543196212Sscottl	ccb.ccb_h.path_id = path_id;
544196212Sscottl	if (id == -1) {
545196212Sscottl		ccb.ccb_h.func_code = XPT_SCAN_BUS;
546196212Sscottl		ccb.ccb_h.target_id = CAM_TARGET_WILDCARD;
547196212Sscottl		ccb.ccb_h.target_lun = CAM_LUN_WILDCARD;
548196212Sscottl		ccb.ccb_h.timeout = 5000;
549196212Sscottl	} else {
550196212Sscottl		ccb.ccb_h.func_code = XPT_SCAN_LUN;
551196212Sscottl		ccb.ccb_h.target_id = id;
552196212Sscottl		ccb.ccb_h.target_lun = 0;
553196212Sscottl	}
554196212Sscottl	ccb.crcn.flags = CAM_FLAG_NONE;
555196212Sscottl
556196212Sscottl	/* Run this at a low priority. */
557196212Sscottl	ccb.ccb_h.pinfo.priority = 5;
558196212Sscottl
559196212Sscottl	if (ioctl(xptfd, CAMIOCOMMAND, &ccb) == -1)
560196212Sscottl		return (errno);
561196212Sscottl
562196212Sscottl	if ((ccb.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
563196212Sscottl		warnx("mpt_rescan_bus rescan got CAM error %#x\n",
564196212Sscottl		    ccb.ccb_h.status & CAM_STATUS_MASK);
565196212Sscottl		return (EIO);
566196212Sscottl	}
567196212Sscottl
568196212Sscottl	return (0);
569196212Sscottl}
570