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