xref: /freebsd/usr.sbin/mfiutil/mfi_drive.c (revision 6b7b8575e97980166416886935e8cf376e50d88d)
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