1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 2008, 2009 Yahoo!, Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. The names of the authors may not be used to endorse or promote 16 * products derived from this software without specific prior written 17 * permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/types.h> 33 #include <sys/errno.h> 34 #include <ctype.h> 35 #include <err.h> 36 #include <fcntl.h> 37 #include <libutil.h> 38 #include <limits.h> 39 #include <stdio.h> 40 #include <stdlib.h> 41 #include <string.h> 42 #include <strings.h> 43 #include <unistd.h> 44 #include <cam/scsi/scsi_all.h> 45 #include "mfiutil.h" 46 47 MFI_TABLE(top, drive); 48 49 /* 50 * Print the name of a drive either by drive number as %2u or by enclosure:slot 51 * as Exx:Sxx (or both). Use default unless command line options override it 52 * and the command allows this (which we usually do unless we already print 53 * both). We prefer pinfo if given, otherwise try to look it up by device_id. 54 */ 55 const char * 56 mfi_drive_name(struct mfi_pd_info *pinfo, uint16_t device_id, uint32_t def) 57 { 58 struct mfi_pd_info info; 59 static char buf[16]; 60 char *p; 61 int error, fd, len; 62 63 if ((def & MFI_DNAME_HONOR_OPTS) != 0 && 64 (mfi_opts & (MFI_DNAME_ES|MFI_DNAME_DEVICE_ID)) != 0) 65 def = mfi_opts & (MFI_DNAME_ES|MFI_DNAME_DEVICE_ID); 66 67 buf[0] = '\0'; 68 if (pinfo == NULL && def & MFI_DNAME_ES) { 69 /* Fallback in case of error, just ignore flags. */ 70 if (device_id == 0xffff) 71 snprintf(buf, sizeof(buf), "MISSING"); 72 else 73 snprintf(buf, sizeof(buf), "%2u", device_id); 74 75 fd = mfi_open(mfi_device, O_RDWR); 76 if (fd < 0) { 77 warn("mfi_open"); 78 return (buf); 79 } 80 81 /* Get the info for this drive. */ 82 if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) { 83 warn("Failed to fetch info for drive %2u", device_id); 84 close(fd); 85 return (buf); 86 } 87 88 close(fd); 89 pinfo = &info; 90 } 91 92 p = buf; 93 len = sizeof(buf); 94 if (def & MFI_DNAME_DEVICE_ID) { 95 if (device_id == 0xffff) 96 error = snprintf(p, len, "MISSING"); 97 else 98 error = snprintf(p, len, "%2u", device_id); 99 if (error >= 0) { 100 p += error; 101 len -= error; 102 } 103 } 104 if ((def & (MFI_DNAME_ES|MFI_DNAME_DEVICE_ID)) == 105 (MFI_DNAME_ES|MFI_DNAME_DEVICE_ID) && len >= 2) { 106 *p++ = ' '; 107 len--; 108 *p = '\0'; 109 len--; 110 } 111 if (def & MFI_DNAME_ES) { 112 if (pinfo->encl_device_id == 0xffff) 113 error = snprintf(p, len, "S%u", 114 pinfo->slot_number); 115 else if (pinfo->encl_device_id == pinfo->ref.v.device_id) 116 error = snprintf(p, len, "E%u", 117 pinfo->encl_index); 118 else 119 error = snprintf(p, len, "E%u:S%u", 120 pinfo->encl_index, pinfo->slot_number); 121 if (error >= 0) { 122 p += error; 123 len -= error; 124 } 125 } 126 127 return (buf); 128 } 129 130 const char * 131 mfi_pdstate(enum mfi_pd_state state) 132 { 133 static char buf[16]; 134 135 switch (state) { 136 case MFI_PD_STATE_UNCONFIGURED_GOOD: 137 return ("UNCONFIGURED GOOD"); 138 case MFI_PD_STATE_UNCONFIGURED_BAD: 139 return ("UNCONFIGURED BAD"); 140 case MFI_PD_STATE_HOT_SPARE: 141 return ("HOT SPARE"); 142 case MFI_PD_STATE_OFFLINE: 143 return ("OFFLINE"); 144 case MFI_PD_STATE_FAILED: 145 return ("FAILED"); 146 case MFI_PD_STATE_REBUILD: 147 return ("REBUILD"); 148 case MFI_PD_STATE_ONLINE: 149 return ("ONLINE"); 150 case MFI_PD_STATE_COPYBACK: 151 return ("COPYBACK"); 152 case MFI_PD_STATE_SYSTEM: 153 return ("JBOD"); 154 default: 155 sprintf(buf, "PSTATE 0x%04x", state); 156 return (buf); 157 } 158 } 159 160 int 161 mfi_lookup_drive(int fd, char *drive, uint16_t *device_id) 162 { 163 struct mfi_pd_list *list; 164 long val; 165 int error; 166 u_int i; 167 char *cp; 168 uint8_t encl, slot; 169 170 /* Look for a raw device id first. */ 171 val = strtol(drive, &cp, 0); 172 if (*cp == '\0') { 173 if (val < 0 || val >= 0xffff) 174 goto bad; 175 *device_id = val; 176 return (0); 177 } 178 179 /* Support for MegaCli style [Exx]:Syy notation. */ 180 if (toupper(drive[0]) == 'E' || toupper(drive[0]) == 'S') { 181 if (drive[1] == '\0') 182 goto bad; 183 cp = drive; 184 if (toupper(drive[0]) == 'E') { 185 cp++; /* Eat 'E' */ 186 val = strtol(cp, &cp, 0); 187 if (val < 0 || val > 0xff || *cp != ':') 188 goto bad; 189 encl = val; 190 cp++; /* Eat ':' */ 191 if (toupper(*cp) != 'S') 192 goto bad; 193 } else 194 encl = 0xff; 195 cp++; /* Eat 'S' */ 196 if (*cp == '\0') 197 goto bad; 198 val = strtol(cp, &cp, 0); 199 if (val < 0 || val > 0xff || *cp != '\0') 200 goto bad; 201 slot = val; 202 203 if (mfi_pd_get_list(fd, &list, NULL) < 0) { 204 error = errno; 205 warn("Failed to fetch drive list"); 206 return (error); 207 } 208 209 for (i = 0; i < list->count; i++) { 210 if (list->addr[i].scsi_dev_type != 0) 211 continue; 212 213 if (((encl == 0xff && 214 list->addr[i].encl_device_id == 0xffff) || 215 list->addr[i].encl_index == encl) && 216 list->addr[i].slot_number == slot) { 217 *device_id = list->addr[i].device_id; 218 free(list); 219 return (0); 220 } 221 } 222 free(list); 223 warnx("Unknown drive %s", drive); 224 return (EINVAL); 225 } 226 227 bad: 228 warnx("Invalid drive number %s", drive); 229 return (EINVAL); 230 } 231 232 static void 233 mbox_store_device_id(uint8_t *mbox, uint16_t device_id) 234 { 235 236 mbox[0] = device_id & 0xff; 237 mbox[1] = device_id >> 8; 238 } 239 240 void 241 mbox_store_pdref(uint8_t *mbox, union mfi_pd_ref *ref) 242 { 243 244 mbox[0] = ref->v.device_id & 0xff; 245 mbox[1] = ref->v.device_id >> 8; 246 mbox[2] = ref->v.seq_num & 0xff; 247 mbox[3] = ref->v.seq_num >> 8; 248 } 249 250 int 251 mfi_pd_get_list(int fd, struct mfi_pd_list **listp, uint8_t *statusp) 252 { 253 struct mfi_pd_list *list; 254 uint32_t list_size; 255 256 /* 257 * Keep fetching the list in a loop until we have a large enough 258 * buffer to hold the entire list. 259 */ 260 list = NULL; 261 list_size = 1024; 262 fetch: 263 list = reallocf(list, list_size); 264 if (list == NULL) 265 return (-1); 266 if (mfi_dcmd_command(fd, MFI_DCMD_PD_GET_LIST, list, list_size, NULL, 267 0, statusp) < 0) { 268 free(list); 269 return (-1); 270 } 271 272 if (list->size > list_size) { 273 list_size = list->size; 274 goto fetch; 275 } 276 277 *listp = list; 278 return (0); 279 } 280 281 int 282 mfi_pd_get_info(int fd, uint16_t device_id, struct mfi_pd_info *info, 283 uint8_t *statusp) 284 { 285 uint8_t mbox[2]; 286 287 mbox_store_device_id(&mbox[0], device_id); 288 return (mfi_dcmd_command(fd, MFI_DCMD_PD_GET_INFO, info, 289 sizeof(struct mfi_pd_info), mbox, 2, statusp)); 290 } 291 292 static void 293 cam_strvis(char *dst, const char *src, int srclen, int dstlen) 294 { 295 296 /* Trim leading/trailing spaces, nulls. */ 297 while (srclen > 0 && src[0] == ' ') 298 src++, srclen--; 299 while (srclen > 0 300 && (src[srclen-1] == ' ' || src[srclen-1] == '\0')) 301 srclen--; 302 303 while (srclen > 0 && dstlen > 1) { 304 char *cur_pos = dst; 305 306 if (*src < 0x20) { 307 /* SCSI-II Specifies that these should never occur. */ 308 /* non-printable character */ 309 if (dstlen > 4) { 310 *cur_pos++ = '\\'; 311 *cur_pos++ = ((*src & 0300) >> 6) + '0'; 312 *cur_pos++ = ((*src & 0070) >> 3) + '0'; 313 *cur_pos++ = ((*src & 0007) >> 0) + '0'; 314 } else { 315 *cur_pos++ = '?'; 316 } 317 } else { 318 /* normal character */ 319 *cur_pos++ = *src; 320 } 321 src++; 322 srclen--; 323 dstlen -= cur_pos - dst; 324 dst = cur_pos; 325 } 326 *dst = '\0'; 327 } 328 329 /* Borrowed heavily from scsi_all.c:scsi_print_inquiry(). */ 330 const char * 331 mfi_pd_inq_string(struct mfi_pd_info *info) 332 { 333 struct scsi_inquiry_data iqd, *inq_data = &iqd; 334 char vendor[16], product[48], revision[16], rstr[12], serial[SID_VENDOR_SPECIFIC_0_SIZE]; 335 static char inq_string[64]; 336 337 memcpy(inq_data, info->inquiry_data, 338 (sizeof (iqd) < sizeof (info->inquiry_data))? 339 sizeof (iqd) : sizeof (info->inquiry_data)); 340 if (SID_QUAL_IS_VENDOR_UNIQUE(inq_data)) 341 return (NULL); 342 if (SID_TYPE(inq_data) != T_DIRECT) 343 return (NULL); 344 if (SID_QUAL(inq_data) != SID_QUAL_LU_CONNECTED) 345 return (NULL); 346 347 cam_strvis(vendor, inq_data->vendor, sizeof(inq_data->vendor), 348 sizeof(vendor)); 349 cam_strvis(product, inq_data->product, sizeof(inq_data->product), 350 sizeof(product)); 351 cam_strvis(revision, inq_data->revision, sizeof(inq_data->revision), 352 sizeof(revision)); 353 cam_strvis(serial, (char *)inq_data->vendor_specific0, sizeof(inq_data->vendor_specific0), 354 sizeof(serial)); 355 356 /* Hack for SATA disks, no idea how to tell speed. */ 357 if (strcmp(vendor, "ATA") == 0) { 358 snprintf(inq_string, sizeof(inq_string), "<%s %s serial=%s> SATA", 359 product, revision, serial); 360 return (inq_string); 361 } 362 363 switch (SID_ANSI_REV(inq_data)) { 364 case SCSI_REV_CCS: 365 strcpy(rstr, "SCSI-CCS"); 366 break; 367 case 5: 368 strcpy(rstr, "SAS"); 369 break; 370 default: 371 snprintf(rstr, sizeof (rstr), "SCSI-%d", 372 SID_ANSI_REV(inq_data)); 373 break; 374 } 375 snprintf(inq_string, sizeof(inq_string), "<%s %s %s serial=%s> %s", vendor, 376 product, revision, serial, rstr); 377 return (inq_string); 378 } 379 380 /* Helper function to set a drive to a given state. */ 381 static int 382 drive_set_state(char *drive, uint16_t new_state) 383 { 384 struct mfi_pd_info info; 385 uint16_t device_id; 386 uint8_t mbox[6]; 387 int error, fd; 388 389 fd = mfi_open(mfi_device, O_RDWR); 390 if (fd < 0) { 391 error = errno; 392 warn("mfi_open"); 393 return (error); 394 } 395 396 error = mfi_lookup_drive(fd, drive, &device_id); 397 if (error) { 398 close(fd); 399 return (error); 400 } 401 402 /* Get the info for this drive. */ 403 if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) { 404 error = errno; 405 warn("Failed to fetch info for drive %u", device_id); 406 close(fd); 407 return (error); 408 } 409 410 /* Try to change the state. */ 411 if (info.fw_state == new_state) { 412 warnx("Drive %u is already in the desired state", device_id); 413 close(fd); 414 return (EINVAL); 415 } 416 417 mbox_store_pdref(&mbox[0], &info.ref); 418 mbox[4] = new_state & 0xff; 419 mbox[5] = new_state >> 8; 420 if (mfi_dcmd_command(fd, MFI_DCMD_PD_STATE_SET, NULL, 0, mbox, 6, 421 NULL) < 0) { 422 error = errno; 423 warn("Failed to set drive %u to %s", device_id, 424 mfi_pdstate(new_state)); 425 close(fd); 426 return (error); 427 } 428 429 close(fd); 430 431 return (0); 432 } 433 434 static int 435 fail_drive(int ac, char **av) 436 { 437 438 if (ac != 2) { 439 warnx("fail: %s", ac > 2 ? "extra arguments" : 440 "drive required"); 441 return (EINVAL); 442 } 443 444 return (drive_set_state(av[1], MFI_PD_STATE_FAILED)); 445 } 446 MFI_COMMAND(top, fail, fail_drive); 447 448 static int 449 good_drive(int ac, char **av) 450 { 451 452 if (ac != 2) { 453 warnx("good: %s", ac > 2 ? "extra arguments" : 454 "drive required"); 455 return (EINVAL); 456 } 457 458 return (drive_set_state(av[1], MFI_PD_STATE_UNCONFIGURED_GOOD)); 459 } 460 MFI_COMMAND(top, good, good_drive); 461 462 static int 463 rebuild_drive(int ac, char **av) 464 { 465 466 if (ac != 2) { 467 warnx("rebuild: %s", ac > 2 ? "extra arguments" : 468 "drive required"); 469 return (EINVAL); 470 } 471 472 return (drive_set_state(av[1], MFI_PD_STATE_REBUILD)); 473 } 474 MFI_COMMAND(top, rebuild, rebuild_drive); 475 476 static int 477 syspd_drive(int ac, char **av) 478 { 479 480 if (ac != 2) { 481 warnx("syspd: %s", ac > 2 ? "extra arguments" : 482 "drive required"); 483 return (EINVAL); 484 } 485 486 return (drive_set_state(av[1], MFI_PD_STATE_SYSTEM)); 487 } 488 MFI_COMMAND(top, syspd, syspd_drive); 489 490 static int 491 start_rebuild(int ac, char **av) 492 { 493 struct mfi_pd_info info; 494 uint16_t device_id; 495 uint8_t mbox[4]; 496 int error, fd; 497 498 if (ac != 2) { 499 warnx("start rebuild: %s", ac > 2 ? "extra arguments" : 500 "drive required"); 501 return (EINVAL); 502 } 503 504 fd = mfi_open(mfi_device, O_RDWR); 505 if (fd < 0) { 506 error = errno; 507 warn("mfi_open"); 508 return (error); 509 } 510 511 error = mfi_lookup_drive(fd, av[1], &device_id); 512 if (error) { 513 close(fd); 514 return (error); 515 } 516 517 /* Get the info for this drive. */ 518 if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) { 519 error = errno; 520 warn("Failed to fetch info for drive %u", device_id); 521 close(fd); 522 return (error); 523 } 524 525 /* Check the state, must be REBUILD. */ 526 if (info.fw_state != MFI_PD_STATE_REBUILD) { 527 warnx("Drive %d is not in the REBUILD state", device_id); 528 close(fd); 529 return (EINVAL); 530 } 531 532 /* Start the rebuild. */ 533 mbox_store_pdref(&mbox[0], &info.ref); 534 if (mfi_dcmd_command(fd, MFI_DCMD_PD_REBUILD_START, NULL, 0, mbox, 4, 535 NULL) < 0) { 536 error = errno; 537 warn("Failed to start rebuild on drive %u", device_id); 538 close(fd); 539 return (error); 540 } 541 close(fd); 542 543 return (0); 544 } 545 MFI_COMMAND(start, rebuild, start_rebuild); 546 547 static int 548 abort_rebuild(int ac, char **av) 549 { 550 struct mfi_pd_info info; 551 uint16_t device_id; 552 uint8_t mbox[4]; 553 int error, fd; 554 555 if (ac != 2) { 556 warnx("abort rebuild: %s", ac > 2 ? "extra arguments" : 557 "drive required"); 558 return (EINVAL); 559 } 560 561 fd = mfi_open(mfi_device, O_RDWR); 562 if (fd < 0) { 563 error = errno; 564 warn("mfi_open"); 565 return (error); 566 } 567 568 error = mfi_lookup_drive(fd, av[1], &device_id); 569 if (error) { 570 close(fd); 571 return (error); 572 } 573 574 /* Get the info for this drive. */ 575 if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) { 576 error = errno; 577 warn("Failed to fetch info for drive %u", device_id); 578 close(fd); 579 return (error); 580 } 581 582 /* Check the state, must be REBUILD. */ 583 if (info.fw_state != MFI_PD_STATE_REBUILD) { 584 warn("Drive %d is not in the REBUILD state", device_id); 585 close(fd); 586 return (EINVAL); 587 } 588 589 /* Abort the rebuild. */ 590 mbox_store_pdref(&mbox[0], &info.ref); 591 if (mfi_dcmd_command(fd, MFI_DCMD_PD_REBUILD_ABORT, NULL, 0, mbox, 4, 592 NULL) < 0) { 593 error = errno; 594 warn("Failed to abort rebuild on drive %u", device_id); 595 close(fd); 596 return (error); 597 } 598 close(fd); 599 600 return (0); 601 } 602 MFI_COMMAND(abort, rebuild, abort_rebuild); 603 604 static int 605 drive_progress(int ac, char **av) 606 { 607 struct mfi_pd_info info; 608 uint16_t device_id; 609 int error, fd; 610 611 if (ac != 2) { 612 warnx("drive progress: %s", ac > 2 ? "extra arguments" : 613 "drive required"); 614 return (EINVAL); 615 } 616 617 fd = mfi_open(mfi_device, O_RDWR); 618 if (fd < 0) { 619 error = errno; 620 warn("mfi_open"); 621 return (error); 622 } 623 624 error = mfi_lookup_drive(fd, av[1], &device_id); 625 if (error) { 626 close(fd); 627 return (error); 628 } 629 630 /* Get the info for this drive. */ 631 if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) { 632 error = errno; 633 warn("Failed to fetch info for drive %u", device_id); 634 close(fd); 635 return (error); 636 } 637 close(fd); 638 639 /* Display any of the active events. */ 640 if (info.prog_info.active & MFI_PD_PROGRESS_REBUILD) 641 mfi_display_progress("Rebuild", &info.prog_info.rbld); 642 if (info.prog_info.active & MFI_PD_PROGRESS_PATROL) 643 mfi_display_progress("Patrol Read", &info.prog_info.patrol); 644 if (info.prog_info.active & MFI_PD_PROGRESS_CLEAR) 645 mfi_display_progress("Clear", &info.prog_info.clear); 646 if ((info.prog_info.active & (MFI_PD_PROGRESS_REBUILD | 647 MFI_PD_PROGRESS_PATROL | MFI_PD_PROGRESS_CLEAR)) == 0) 648 printf("No activity in progress for drive %s.\n", 649 mfi_drive_name(NULL, device_id, 650 MFI_DNAME_DEVICE_ID|MFI_DNAME_HONOR_OPTS)); 651 652 return (0); 653 } 654 MFI_COMMAND(drive, progress, drive_progress); 655 656 static int 657 drive_clear(int ac, char **av) 658 { 659 struct mfi_pd_info info; 660 uint32_t opcode; 661 uint16_t device_id; 662 uint8_t mbox[4]; 663 char *s1; 664 int error, fd; 665 666 if (ac != 3) { 667 warnx("drive clear: %s", ac > 3 ? "extra arguments" : 668 "drive and action requires"); 669 return (EINVAL); 670 } 671 672 for (s1 = av[2]; *s1 != '\0'; s1++) 673 *s1 = tolower(*s1); 674 if (strcmp(av[2], "start") == 0) 675 opcode = MFI_DCMD_PD_CLEAR_START; 676 else if ((strcmp(av[2], "stop") == 0) || (strcmp(av[2], "abort") == 0)) 677 opcode = MFI_DCMD_PD_CLEAR_ABORT; 678 else { 679 warnx("drive clear: invalid action, must be 'start' or 'stop'\n"); 680 return (EINVAL); 681 } 682 683 fd = mfi_open(mfi_device, O_RDWR); 684 if (fd < 0) { 685 error = errno; 686 warn("mfi_open"); 687 return (error); 688 } 689 690 error = mfi_lookup_drive(fd, av[1], &device_id); 691 if (error) { 692 close(fd); 693 return (error); 694 } 695 696 /* Get the info for this drive. */ 697 if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) { 698 error = errno; 699 warn("Failed to fetch info for drive %u", device_id); 700 close(fd); 701 return (error); 702 } 703 704 mbox_store_pdref(&mbox[0], &info.ref); 705 if (mfi_dcmd_command(fd, opcode, NULL, 0, mbox, 4, NULL) < 0) { 706 error = errno; 707 warn("Failed to %s clear on drive %u", 708 opcode == MFI_DCMD_PD_CLEAR_START ? "start" : "stop", 709 device_id); 710 close(fd); 711 return (error); 712 } 713 714 close(fd); 715 return (0); 716 } 717 MFI_COMMAND(drive, clear, drive_clear); 718 719 static int 720 drive_locate(int ac, char **av) 721 { 722 uint16_t device_id; 723 uint32_t opcode; 724 int error, fd; 725 uint8_t mbox[4]; 726 727 if (ac != 3) { 728 warnx("locate: %s", ac > 3 ? "extra arguments" : 729 "drive and state required"); 730 return (EINVAL); 731 } 732 733 if (strcasecmp(av[2], "on") == 0 || strcasecmp(av[2], "start") == 0) 734 opcode = MFI_DCMD_PD_LOCATE_START; 735 else if (strcasecmp(av[2], "off") == 0 || 736 strcasecmp(av[2], "stop") == 0) 737 opcode = MFI_DCMD_PD_LOCATE_STOP; 738 else { 739 warnx("locate: invalid state %s", av[2]); 740 return (EINVAL); 741 } 742 743 fd = mfi_open(mfi_device, O_RDWR); 744 if (fd < 0) { 745 error = errno; 746 warn("mfi_open"); 747 return (error); 748 } 749 750 error = mfi_lookup_drive(fd, av[1], &device_id); 751 if (error) { 752 close(fd); 753 return (error); 754 } 755 756 757 mbox_store_device_id(&mbox[0], device_id); 758 mbox[2] = 0; 759 mbox[3] = 0; 760 if (mfi_dcmd_command(fd, opcode, NULL, 0, mbox, 4, NULL) < 0) { 761 error = errno; 762 warn("Failed to %s locate on drive %u", 763 opcode == MFI_DCMD_PD_LOCATE_START ? "start" : "stop", 764 device_id); 765 close(fd); 766 return (error); 767 } 768 close(fd); 769 770 return (0); 771 } 772 MFI_COMMAND(top, locate, drive_locate); 773