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