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; 66*c5f2b79dSJohn 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) { 95*c5f2b79dSJohn Baldwin error = errno; 96fe2f8087SJohn Baldwin free(ccb.cdm.matches); 97fe2f8087SJohn Baldwin free(ccb.cdm.patterns); 98*c5f2b79dSJohn 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; 129*c5f2b79dSJohn 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) { 169*c5f2b79dSJohn Baldwin error = errno; 170fc58801cSScott Long free(ccb.cdm.matches); 171fc58801cSScott Long free(ccb.cdm.patterns); 172*c5f2b79dSJohn 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. */ 244fc58801cSScott Long bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_scsiio) - 245fc58801cSScott Long sizeof(struct ccb_hdr)); 246fc58801cSScott Long 247fc58801cSScott Long scsi_read_capacity(&ccb->csio, 1, NULL, MSG_SIMPLE_Q_TAG, &rcap, 248fc58801cSScott Long SSD_FULL_SIZE, 5000); 249fc58801cSScott Long 250fc58801cSScott Long /* Disable freezing the device queue */ 251fc58801cSScott Long ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; 252fc58801cSScott Long 253fc58801cSScott Long if (cam_send_ccb(dev, ccb) < 0) { 254fc58801cSScott Long error = errno; 255fc58801cSScott Long cam_freeccb(ccb); 256fc58801cSScott Long return (error); 257fc58801cSScott Long } 258fc58801cSScott Long 259fc58801cSScott Long if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { 260fc58801cSScott Long cam_freeccb(ccb); 261fc58801cSScott Long return (EIO); 262fc58801cSScott Long } 263fc58801cSScott Long cam_freeccb(ccb); 264fc58801cSScott Long 265fc58801cSScott Long /* 266fc58801cSScott Long * A last block of 2^32-1 means that the true capacity is over 2TB, 267fc58801cSScott Long * and we need to issue the long READ CAPACITY to get the real 268fc58801cSScott Long * capacity. Otherwise, we're all set. 269fc58801cSScott Long */ 270fc58801cSScott Long if (scsi_4btoul(rcap.addr) != 0xffffffff) { 271fc58801cSScott Long disk->maxlba = scsi_4btoul(rcap.addr); 272fc58801cSScott Long return (0); 273fc58801cSScott Long } 274fc58801cSScott Long 275fc58801cSScott Long /* Zero the rest of the ccb. */ 276fc58801cSScott Long bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_scsiio) - 277fc58801cSScott Long sizeof(struct ccb_hdr)); 278fc58801cSScott Long 279fc58801cSScott Long scsi_read_capacity_16(&ccb->csio, 1, NULL, MSG_SIMPLE_Q_TAG, 0, 0, 0, 280fc58801cSScott Long &rcaplong, SSD_FULL_SIZE, 5000); 281fc58801cSScott Long 282fc58801cSScott Long /* Disable freezing the device queue */ 283fc58801cSScott Long ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; 284fc58801cSScott Long 285fc58801cSScott Long if (cam_send_ccb(dev, ccb) < 0) { 286fc58801cSScott Long error = errno; 287fc58801cSScott Long cam_freeccb(ccb); 288fc58801cSScott Long return (error); 289fc58801cSScott Long } 290fc58801cSScott Long 291fc58801cSScott Long if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { 292fc58801cSScott Long cam_freeccb(ccb); 293fc58801cSScott Long return (EIO); 294fc58801cSScott Long } 295fc58801cSScott Long cam_freeccb(ccb); 296fc58801cSScott Long 297fc58801cSScott Long disk->maxlba = scsi_8btou64(rcaplong.addr); 298fc58801cSScott Long return (0); 299fc58801cSScott Long } 300fc58801cSScott Long 301fc58801cSScott Long /* Borrowed heavily from scsi_all.c:scsi_print_inquiry(). */ 302fc58801cSScott Long static void 303fc58801cSScott Long format_scsi_inquiry(struct mpt_standalone_disk *disk, 304fc58801cSScott Long struct scsi_inquiry_data *inq_data) 305fc58801cSScott Long { 306fc58801cSScott Long char vendor[16], product[48], revision[16], rstr[12]; 307fc58801cSScott Long 308fc58801cSScott Long if (SID_QUAL_IS_VENDOR_UNIQUE(inq_data)) 309fc58801cSScott Long return; 310fc58801cSScott Long if (SID_TYPE(inq_data) != T_DIRECT) 311fc58801cSScott Long return; 312fc58801cSScott Long if (SID_QUAL(inq_data) != SID_QUAL_LU_CONNECTED) 313fc58801cSScott Long return; 314fc58801cSScott Long 315fc58801cSScott Long cam_strvis(vendor, inq_data->vendor, sizeof(inq_data->vendor), 316fc58801cSScott Long sizeof(vendor)); 317fc58801cSScott Long cam_strvis(product, inq_data->product, sizeof(inq_data->product), 318fc58801cSScott Long sizeof(product)); 319fc58801cSScott Long cam_strvis(revision, inq_data->revision, sizeof(inq_data->revision), 320fc58801cSScott Long sizeof(revision)); 321fc58801cSScott Long 322fc58801cSScott Long /* Hack for SATA disks, no idea how to tell speed. */ 323fc58801cSScott Long if (strcmp(vendor, "ATA") == 0) { 324fc58801cSScott Long snprintf(disk->inqstring, sizeof(disk->inqstring), 325fc58801cSScott Long "<%s %s> SATA", product, revision); 326fc58801cSScott Long return; 327fc58801cSScott Long } 328fc58801cSScott Long 329fc58801cSScott Long switch (SID_ANSI_REV(inq_data)) { 330fc58801cSScott Long case SCSI_REV_CCS: 331fc58801cSScott Long strcpy(rstr, "SCSI-CCS"); 332fc58801cSScott Long break; 333fc58801cSScott Long case 5: 334fc58801cSScott Long strcpy(rstr, "SAS"); 335fc58801cSScott Long break; 336fc58801cSScott Long default: 337fc58801cSScott Long snprintf(rstr, sizeof (rstr), "SCSI-%d", 338fc58801cSScott Long SID_ANSI_REV(inq_data)); 339fc58801cSScott Long break; 340fc58801cSScott Long } 341fc58801cSScott Long snprintf(disk->inqstring, sizeof(disk->inqstring), "<%s %s %s> %s", 342fc58801cSScott Long vendor, product, revision, rstr); 343fc58801cSScott Long } 344fc58801cSScott Long 345fc58801cSScott Long /* Much borrowed from scsiinquiry() in src/sbin/camcontrol/camcontrol.c. */ 346fc58801cSScott Long static int 347fc58801cSScott Long fetch_scsi_inquiry(struct cam_device *dev, struct mpt_standalone_disk *disk) 348fc58801cSScott Long { 349fc58801cSScott Long struct scsi_inquiry_data *inq_buf; 350fc58801cSScott Long union ccb *ccb; 351fc58801cSScott Long int error; 352fc58801cSScott Long 353fc58801cSScott Long ccb = cam_getccb(dev); 354fc58801cSScott Long if (ccb == NULL) 355fc58801cSScott Long return (ENOMEM); 356fc58801cSScott Long 357fc58801cSScott Long /* Zero the rest of the ccb. */ 358fc58801cSScott Long bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_scsiio) - 359fc58801cSScott Long sizeof(struct ccb_hdr)); 360fc58801cSScott Long 361fc58801cSScott Long inq_buf = calloc(1, sizeof(*inq_buf)); 362fc58801cSScott Long if (inq_buf == NULL) { 363fc58801cSScott Long cam_freeccb(ccb); 364fc58801cSScott Long return (ENOMEM); 365fc58801cSScott Long } 366fc58801cSScott Long scsi_inquiry(&ccb->csio, 1, NULL, MSG_SIMPLE_Q_TAG, (void *)inq_buf, 367fc58801cSScott Long SHORT_INQUIRY_LENGTH, 0, 0, SSD_FULL_SIZE, 5000); 368fc58801cSScott Long 369fc58801cSScott Long /* Disable freezing the device queue */ 370fc58801cSScott Long ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; 371fc58801cSScott Long 372fc58801cSScott Long if (cam_send_ccb(dev, ccb) < 0) { 373fc58801cSScott Long error = errno; 374fc58801cSScott Long free(inq_buf); 375fc58801cSScott Long cam_freeccb(ccb); 376fc58801cSScott Long return (error); 377fc58801cSScott Long } 378fc58801cSScott Long 379fc58801cSScott Long if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { 380fc58801cSScott Long free(inq_buf); 381fc58801cSScott Long cam_freeccb(ccb); 382fc58801cSScott Long return (EIO); 383fc58801cSScott Long } 384fc58801cSScott Long 385fc58801cSScott Long cam_freeccb(ccb); 386fc58801cSScott Long format_scsi_inquiry(disk, inq_buf); 387fc58801cSScott Long free(inq_buf); 388fc58801cSScott Long return (0); 389fc58801cSScott Long } 390fc58801cSScott Long 391fc58801cSScott Long int 392fc58801cSScott Long mpt_fetch_disks(int fd, int *ndisks, struct mpt_standalone_disk **disksp) 393fc58801cSScott Long { 394fc58801cSScott Long CONFIG_PAGE_IOC_2 *ioc2; 395fc58801cSScott Long struct mpt_standalone_disk *disks; 396fc58801cSScott Long struct periph_match_pattern *p; 397fc58801cSScott Long struct periph_match_result *r; 398fc58801cSScott Long struct cam_device *dev; 399fc58801cSScott Long union ccb ccb; 400fe2f8087SJohn Baldwin path_id_t path_id; 401fc58801cSScott Long size_t bufsize; 402fe2f8087SJohn Baldwin int count, error; 403*c5f2b79dSJohn Baldwin uint32_t i; 404fc58801cSScott Long 405fc58801cSScott Long if (xpt_open() < 0) 406fc58801cSScott Long return (ENXIO); 407fc58801cSScott Long 408fe2f8087SJohn Baldwin error = fetch_path_id(&path_id); 409fe2f8087SJohn Baldwin if (error) 410fe2f8087SJohn Baldwin return (error); 411fe2f8087SJohn Baldwin 412fc58801cSScott Long for (count = 100;; count+= 100) { 413fc58801cSScott Long /* Try to fetch 'count' disks in one go. */ 414fc58801cSScott Long bzero(&ccb, sizeof(ccb)); 415fc58801cSScott Long 416fc58801cSScott Long ccb.ccb_h.func_code = XPT_DEV_MATCH; 417fc58801cSScott Long 418fe2f8087SJohn Baldwin bufsize = sizeof(struct dev_match_result) * (count + 1); 419fc58801cSScott Long ccb.cdm.num_matches = 0; 420fc58801cSScott Long ccb.cdm.match_buf_len = bufsize; 421fc58801cSScott Long ccb.cdm.matches = calloc(1, bufsize); 422fc58801cSScott Long 423fe2f8087SJohn Baldwin bufsize = sizeof(struct dev_match_pattern) * 1; 424fe2f8087SJohn Baldwin ccb.cdm.num_patterns = 1; 425fc58801cSScott Long ccb.cdm.pattern_buf_len = bufsize; 426fc58801cSScott Long ccb.cdm.patterns = calloc(1, bufsize); 427fc58801cSScott Long 428fc58801cSScott Long /* Match any "da" peripherals. */ 429fe2f8087SJohn Baldwin ccb.cdm.patterns[0].type = DEV_MATCH_PERIPH; 430fe2f8087SJohn Baldwin p = &ccb.cdm.patterns[0].pattern.periph_pattern; 431fe2f8087SJohn Baldwin p->path_id = path_id; 432fc58801cSScott Long snprintf(p->periph_name, sizeof(p->periph_name), "da"); 433fe2f8087SJohn Baldwin p->flags = PERIPH_MATCH_PATH | PERIPH_MATCH_NAME; 434fc58801cSScott Long 435fc58801cSScott Long if (ioctl(xptfd, CAMIOCOMMAND, &ccb) < 0) { 436*c5f2b79dSJohn Baldwin error = errno; 437fc58801cSScott Long free(ccb.cdm.matches); 438fc58801cSScott Long free(ccb.cdm.patterns); 439*c5f2b79dSJohn Baldwin return (error); 440fc58801cSScott Long } 441fc58801cSScott Long free(ccb.cdm.patterns); 442fc58801cSScott Long 443fc58801cSScott Long /* Check for CCB errors. */ 444fc58801cSScott Long if ((ccb.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { 445fc58801cSScott Long free(ccb.cdm.matches); 446fc58801cSScott Long return (EIO); 447fc58801cSScott Long } 448fc58801cSScott Long 449fc58801cSScott Long /* If we need a longer list, try again. */ 450fc58801cSScott Long if (ccb.cdm.status == CAM_DEV_MATCH_MORE) { 451fc58801cSScott Long free(ccb.cdm.matches); 452fc58801cSScott Long continue; 453fc58801cSScott Long } 454fc58801cSScott Long 455fc58801cSScott Long /* If we got an error, abort. */ 456fc58801cSScott Long if (ccb.cdm.status != CAM_DEV_MATCH_LAST) { 457fc58801cSScott Long free(ccb.cdm.matches); 458fc58801cSScott Long return (EIO); 459fc58801cSScott Long } 460fc58801cSScott Long break; 461fc58801cSScott Long } 462fc58801cSScott Long 463fe2f8087SJohn Baldwin /* Shortcut if we don't have any "da" devices. */ 464fe2f8087SJohn Baldwin if (ccb.cdm.num_matches == 0) { 465fc58801cSScott Long free(ccb.cdm.matches); 466fe2f8087SJohn Baldwin *ndisks = 0; 467fe2f8087SJohn Baldwin *disksp = NULL; 468fe2f8087SJohn Baldwin return (0); 469fc58801cSScott Long } 470fe2f8087SJohn Baldwin 471fe2f8087SJohn Baldwin /* We should have N matches, 1 for each "da" device. */ 472fe2f8087SJohn Baldwin for (i = 0; i < ccb.cdm.num_matches; i++) { 473fc58801cSScott Long if (ccb.cdm.matches[i].type != DEV_MATCH_PERIPH) { 474fc58801cSScott Long warnx("mpt_fetch_disks got wrong CAM matches"); 475fc58801cSScott Long free(ccb.cdm.matches); 476fc58801cSScott Long return (EIO); 477fc58801cSScott Long } 478fc58801cSScott Long } 479fc58801cSScott Long 480fc58801cSScott Long /* 481fc58801cSScott Long * Some of the "da" peripherals may be for RAID volumes, so 482fc58801cSScott Long * fetch the IOC 2 page (list of RAID volumes) so we can 483fc58801cSScott Long * exclude them from the list. 484fc58801cSScott Long */ 485fc58801cSScott Long ioc2 = mpt_read_ioc_page(fd, 2, NULL); 486*c5f2b79dSJohn Baldwin if (ioc2 == NULL) 487*c5f2b79dSJohn Baldwin return (errno); 488fc58801cSScott Long disks = calloc(ccb.cdm.num_matches, sizeof(*disks)); 489fc58801cSScott Long count = 0; 490fe2f8087SJohn Baldwin for (i = 0; i < ccb.cdm.num_matches; i++) { 491fc58801cSScott Long r = &ccb.cdm.matches[i].result.periph_result; 492fc58801cSScott Long if (periph_is_volume(ioc2, r)) 493fc58801cSScott Long continue; 494fc58801cSScott Long disks[count].bus = 0; 495fc58801cSScott Long disks[count].target = r->target_id; 496fc58801cSScott Long snprintf(disks[count].devname, sizeof(disks[count].devname), 497fc58801cSScott Long "%s%d", r->periph_name, r->unit_number); 498fc58801cSScott Long 499fc58801cSScott Long dev = cam_open_device(disks[count].devname, O_RDWR); 500fc58801cSScott Long if (dev != NULL) { 501fc58801cSScott Long fetch_scsi_capacity(dev, &disks[count]); 502fc58801cSScott Long fetch_scsi_inquiry(dev, &disks[count]); 503fc58801cSScott Long cam_close_device(dev); 504fc58801cSScott Long } 505fc58801cSScott Long count++; 506fc58801cSScott Long } 507fc58801cSScott Long free(ccb.cdm.matches); 508fc58801cSScott Long free(ioc2); 509fc58801cSScott Long 510fc58801cSScott Long *ndisks = count; 511fc58801cSScott Long *disksp = disks; 512fc58801cSScott Long return (0); 513fc58801cSScott Long } 514fc58801cSScott Long 515fc58801cSScott Long /* 516fc58801cSScott Long * Instruct the mpt(4) device to rescan its busses to find new devices 517fc58801cSScott Long * such as disks whose RAID physdisk page was removed or volumes that 518fc58801cSScott Long * were created. If id is -1, the entire bus is rescanned. 519fc58801cSScott Long * Otherwise, only devices at the specified ID are rescanned. If bus 520fc58801cSScott Long * is -1, then all busses are scanned instead of the specified bus. 521fc58801cSScott Long * Note that currently, only bus 0 is supported. 522fc58801cSScott Long */ 523fc58801cSScott Long int 524fc58801cSScott Long mpt_rescan_bus(int bus, int id) 525fc58801cSScott Long { 526fc58801cSScott Long union ccb ccb; 527fc58801cSScott Long path_id_t path_id; 528fe2f8087SJohn Baldwin int error; 529fc58801cSScott Long 530fc58801cSScott Long /* mpt(4) only handles devices on bus 0. */ 531fc58801cSScott Long if (bus != -1 && bus != 0) 532fc58801cSScott Long return (EINVAL); 533fc58801cSScott Long 534fc58801cSScott Long if (xpt_open() < 0) 535fc58801cSScott Long return (ENXIO); 536fc58801cSScott Long 537fe2f8087SJohn Baldwin error = fetch_path_id(&path_id); 538fe2f8087SJohn Baldwin if (error) 539fe2f8087SJohn Baldwin return (error); 540fe2f8087SJohn Baldwin 541fe2f8087SJohn Baldwin /* Perform the actual rescan. */ 542fc58801cSScott Long bzero(&ccb, sizeof(ccb)); 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