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