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