1763fae79SScott Long /*-
21de7b4b8SPedro F. Giffuni * SPDX-License-Identifier: BSD-3-Clause
31de7b4b8SPedro F. Giffuni *
4763fae79SScott Long * Copyright (c) 2008, 2009 Yahoo!, Inc.
5763fae79SScott Long * All rights reserved.
6763fae79SScott Long *
7763fae79SScott Long * Redistribution and use in source and binary forms, with or without
8763fae79SScott Long * modification, are permitted provided that the following conditions
9763fae79SScott Long * are met:
10763fae79SScott Long * 1. Redistributions of source code must retain the above copyright
11763fae79SScott Long * notice, this list of conditions and the following disclaimer.
12763fae79SScott Long * 2. Redistributions in binary form must reproduce the above copyright
13763fae79SScott Long * notice, this list of conditions and the following disclaimer in the
14763fae79SScott Long * documentation and/or other materials provided with the distribution.
15763fae79SScott Long * 3. The names of the authors may not be used to endorse or promote
16763fae79SScott Long * products derived from this software without specific prior written
17763fae79SScott Long * permission.
18763fae79SScott Long *
19763fae79SScott Long * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20763fae79SScott Long * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21763fae79SScott Long * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22763fae79SScott Long * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23763fae79SScott Long * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24763fae79SScott Long * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25763fae79SScott Long * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26763fae79SScott Long * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27763fae79SScott Long * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28763fae79SScott Long * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29763fae79SScott Long * SUCH DAMAGE.
30763fae79SScott Long */
31763fae79SScott Long
32763fae79SScott Long #include <sys/types.h>
33763fae79SScott Long #include <sys/errno.h>
34763fae79SScott Long #include <ctype.h>
35763fae79SScott Long #include <err.h>
36bf4ec4dfSEitan Adler #include <fcntl.h>
37763fae79SScott Long #include <libutil.h>
38763fae79SScott Long #include <limits.h>
39763fae79SScott Long #include <stdio.h>
40763fae79SScott Long #include <stdlib.h>
41763fae79SScott Long #include <string.h>
42763fae79SScott Long #include <strings.h>
43763fae79SScott Long #include <unistd.h>
44763fae79SScott Long #include <cam/scsi/scsi_all.h>
45763fae79SScott Long #include "mfiutil.h"
46763fae79SScott Long
47763fae79SScott Long MFI_TABLE(top, drive);
48763fae79SScott Long
497bbae305SBjoern A. Zeeb /*
507bbae305SBjoern A. Zeeb * Print the name of a drive either by drive number as %2u or by enclosure:slot
517bbae305SBjoern A. Zeeb * as Exx:Sxx (or both). Use default unless command line options override it
527bbae305SBjoern A. Zeeb * and the command allows this (which we usually do unless we already print
537bbae305SBjoern A. Zeeb * both). We prefer pinfo if given, otherwise try to look it up by device_id.
547bbae305SBjoern A. Zeeb */
557bbae305SBjoern A. Zeeb const char *
mfi_drive_name(struct mfi_pd_info * pinfo,uint16_t device_id,uint32_t def)567bbae305SBjoern A. Zeeb mfi_drive_name(struct mfi_pd_info *pinfo, uint16_t device_id, uint32_t def)
577bbae305SBjoern A. Zeeb {
587bbae305SBjoern A. Zeeb struct mfi_pd_info info;
597bbae305SBjoern A. Zeeb static char buf[16];
607bbae305SBjoern A. Zeeb char *p;
617bbae305SBjoern A. Zeeb int error, fd, len;
627bbae305SBjoern A. Zeeb
637bbae305SBjoern A. Zeeb if ((def & MFI_DNAME_HONOR_OPTS) != 0 &&
647bbae305SBjoern A. Zeeb (mfi_opts & (MFI_DNAME_ES|MFI_DNAME_DEVICE_ID)) != 0)
657bbae305SBjoern A. Zeeb def = mfi_opts & (MFI_DNAME_ES|MFI_DNAME_DEVICE_ID);
667bbae305SBjoern A. Zeeb
677bbae305SBjoern A. Zeeb buf[0] = '\0';
687bbae305SBjoern A. Zeeb if (pinfo == NULL && def & MFI_DNAME_ES) {
697bbae305SBjoern A. Zeeb /* Fallback in case of error, just ignore flags. */
707bbae305SBjoern A. Zeeb if (device_id == 0xffff)
717bbae305SBjoern A. Zeeb snprintf(buf, sizeof(buf), "MISSING");
727bbae305SBjoern A. Zeeb else
737bbae305SBjoern A. Zeeb snprintf(buf, sizeof(buf), "%2u", device_id);
747bbae305SBjoern A. Zeeb
75*7e0f8b79SDoug Ambrisko fd = mfi_open(mfi_device, O_RDWR);
767bbae305SBjoern A. Zeeb if (fd < 0) {
777bbae305SBjoern A. Zeeb warn("mfi_open");
787bbae305SBjoern A. Zeeb return (buf);
797bbae305SBjoern A. Zeeb }
807bbae305SBjoern A. Zeeb
817bbae305SBjoern A. Zeeb /* Get the info for this drive. */
827bbae305SBjoern A. Zeeb if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) {
837bbae305SBjoern A. Zeeb warn("Failed to fetch info for drive %2u", device_id);
847bbae305SBjoern A. Zeeb close(fd);
857bbae305SBjoern A. Zeeb return (buf);
867bbae305SBjoern A. Zeeb }
877bbae305SBjoern A. Zeeb
887bbae305SBjoern A. Zeeb close(fd);
897bbae305SBjoern A. Zeeb pinfo = &info;
907bbae305SBjoern A. Zeeb }
917bbae305SBjoern A. Zeeb
927bbae305SBjoern A. Zeeb p = buf;
937bbae305SBjoern A. Zeeb len = sizeof(buf);
947bbae305SBjoern A. Zeeb if (def & MFI_DNAME_DEVICE_ID) {
957bbae305SBjoern A. Zeeb if (device_id == 0xffff)
967bbae305SBjoern A. Zeeb error = snprintf(p, len, "MISSING");
977bbae305SBjoern A. Zeeb else
987bbae305SBjoern A. Zeeb error = snprintf(p, len, "%2u", device_id);
997bbae305SBjoern A. Zeeb if (error >= 0) {
1007bbae305SBjoern A. Zeeb p += error;
1017bbae305SBjoern A. Zeeb len -= error;
1027bbae305SBjoern A. Zeeb }
1037bbae305SBjoern A. Zeeb }
1047bbae305SBjoern A. Zeeb if ((def & (MFI_DNAME_ES|MFI_DNAME_DEVICE_ID)) ==
1057bbae305SBjoern A. Zeeb (MFI_DNAME_ES|MFI_DNAME_DEVICE_ID) && len >= 2) {
1067bbae305SBjoern A. Zeeb *p++ = ' ';
1077bbae305SBjoern A. Zeeb len--;
1087bbae305SBjoern A. Zeeb *p = '\0';
1097bbae305SBjoern A. Zeeb len--;
1107bbae305SBjoern A. Zeeb }
1117bbae305SBjoern A. Zeeb if (def & MFI_DNAME_ES) {
1127bbae305SBjoern A. Zeeb if (pinfo->encl_device_id == 0xffff)
1137bbae305SBjoern A. Zeeb error = snprintf(p, len, "S%u",
1147bbae305SBjoern A. Zeeb pinfo->slot_number);
1157bbae305SBjoern A. Zeeb else if (pinfo->encl_device_id == pinfo->ref.v.device_id)
1167bbae305SBjoern A. Zeeb error = snprintf(p, len, "E%u",
1177bbae305SBjoern A. Zeeb pinfo->encl_index);
1187bbae305SBjoern A. Zeeb else
1197bbae305SBjoern A. Zeeb error = snprintf(p, len, "E%u:S%u",
1207bbae305SBjoern A. Zeeb pinfo->encl_index, pinfo->slot_number);
1217bbae305SBjoern A. Zeeb if (error >= 0) {
1227bbae305SBjoern A. Zeeb p += error;
1237bbae305SBjoern A. Zeeb len -= error;
1247bbae305SBjoern A. Zeeb }
1257bbae305SBjoern A. Zeeb }
1267bbae305SBjoern A. Zeeb
1277bbae305SBjoern A. Zeeb return (buf);
1287bbae305SBjoern A. Zeeb }
1297bbae305SBjoern A. Zeeb
130763fae79SScott Long const char *
mfi_pdstate(enum mfi_pd_state state)131763fae79SScott Long mfi_pdstate(enum mfi_pd_state state)
132763fae79SScott Long {
133763fae79SScott Long static char buf[16];
134763fae79SScott Long
135763fae79SScott Long switch (state) {
136763fae79SScott Long case MFI_PD_STATE_UNCONFIGURED_GOOD:
137763fae79SScott Long return ("UNCONFIGURED GOOD");
138763fae79SScott Long case MFI_PD_STATE_UNCONFIGURED_BAD:
139763fae79SScott Long return ("UNCONFIGURED BAD");
140763fae79SScott Long case MFI_PD_STATE_HOT_SPARE:
141763fae79SScott Long return ("HOT SPARE");
142763fae79SScott Long case MFI_PD_STATE_OFFLINE:
143763fae79SScott Long return ("OFFLINE");
144763fae79SScott Long case MFI_PD_STATE_FAILED:
145763fae79SScott Long return ("FAILED");
146763fae79SScott Long case MFI_PD_STATE_REBUILD:
147763fae79SScott Long return ("REBUILD");
148763fae79SScott Long case MFI_PD_STATE_ONLINE:
149763fae79SScott Long return ("ONLINE");
15008f24630SSergey Kandaurov case MFI_PD_STATE_COPYBACK:
15108f24630SSergey Kandaurov return ("COPYBACK");
15208f24630SSergey Kandaurov case MFI_PD_STATE_SYSTEM:
1531d8f043aSDoug Ambrisko return ("JBOD");
154763fae79SScott Long default:
155763fae79SScott Long sprintf(buf, "PSTATE 0x%04x", state);
156763fae79SScott Long return (buf);
157763fae79SScott Long }
158763fae79SScott Long }
159763fae79SScott Long
160763fae79SScott Long int
mfi_lookup_drive(int fd,char * drive,uint16_t * device_id)161763fae79SScott Long mfi_lookup_drive(int fd, char *drive, uint16_t *device_id)
162763fae79SScott Long {
163763fae79SScott Long struct mfi_pd_list *list;
1646d600732SScott Long long val;
165c02999d9SJohn Baldwin int error;
1666d600732SScott Long u_int i;
167763fae79SScott Long char *cp;
168c02999d9SJohn Baldwin uint8_t encl, slot;
169763fae79SScott Long
170763fae79SScott Long /* Look for a raw device id first. */
171763fae79SScott Long val = strtol(drive, &cp, 0);
172763fae79SScott Long if (*cp == '\0') {
173763fae79SScott Long if (val < 0 || val >= 0xffff)
174763fae79SScott Long goto bad;
175763fae79SScott Long *device_id = val;
176763fae79SScott Long return (0);
177763fae79SScott Long }
178763fae79SScott Long
179763fae79SScott Long /* Support for MegaCli style [Exx]:Syy notation. */
180763fae79SScott Long if (toupper(drive[0]) == 'E' || toupper(drive[0]) == 'S') {
181763fae79SScott Long if (drive[1] == '\0')
182763fae79SScott Long goto bad;
183763fae79SScott Long cp = drive;
184763fae79SScott Long if (toupper(drive[0]) == 'E') {
185763fae79SScott Long cp++; /* Eat 'E' */
186763fae79SScott Long val = strtol(cp, &cp, 0);
187763fae79SScott Long if (val < 0 || val > 0xff || *cp != ':')
188763fae79SScott Long goto bad;
189763fae79SScott Long encl = val;
190763fae79SScott Long cp++; /* Eat ':' */
191763fae79SScott Long if (toupper(*cp) != 'S')
192763fae79SScott Long goto bad;
193763fae79SScott Long } else
194763fae79SScott Long encl = 0xff;
195763fae79SScott Long cp++; /* Eat 'S' */
196763fae79SScott Long if (*cp == '\0')
197763fae79SScott Long goto bad;
198763fae79SScott Long val = strtol(cp, &cp, 0);
199763fae79SScott Long if (val < 0 || val > 0xff || *cp != '\0')
200763fae79SScott Long goto bad;
201763fae79SScott Long slot = val;
202763fae79SScott Long
203763fae79SScott Long if (mfi_pd_get_list(fd, &list, NULL) < 0) {
204c02999d9SJohn Baldwin error = errno;
205763fae79SScott Long warn("Failed to fetch drive list");
206c02999d9SJohn Baldwin return (error);
207763fae79SScott Long }
208763fae79SScott Long
2096d600732SScott Long for (i = 0; i < list->count; i++) {
2106d600732SScott Long if (list->addr[i].scsi_dev_type != 0)
211763fae79SScott Long continue;
212763fae79SScott Long
213763fae79SScott Long if (((encl == 0xff &&
2146d600732SScott Long list->addr[i].encl_device_id == 0xffff) ||
2156d600732SScott Long list->addr[i].encl_index == encl) &&
2166d600732SScott Long list->addr[i].slot_number == slot) {
2176d600732SScott Long *device_id = list->addr[i].device_id;
218763fae79SScott Long free(list);
219763fae79SScott Long return (0);
220763fae79SScott Long }
221763fae79SScott Long }
222763fae79SScott Long free(list);
223763fae79SScott Long warnx("Unknown drive %s", drive);
224763fae79SScott Long return (EINVAL);
225763fae79SScott Long }
226763fae79SScott Long
227763fae79SScott Long bad:
228763fae79SScott Long warnx("Invalid drive number %s", drive);
229763fae79SScott Long return (EINVAL);
230763fae79SScott Long }
231763fae79SScott Long
232763fae79SScott Long static void
mbox_store_device_id(uint8_t * mbox,uint16_t device_id)233763fae79SScott Long mbox_store_device_id(uint8_t *mbox, uint16_t device_id)
234763fae79SScott Long {
235763fae79SScott Long
236763fae79SScott Long mbox[0] = device_id & 0xff;
237763fae79SScott Long mbox[1] = device_id >> 8;
238763fae79SScott Long }
239763fae79SScott Long
240763fae79SScott Long void
mbox_store_pdref(uint8_t * mbox,union mfi_pd_ref * ref)241763fae79SScott Long mbox_store_pdref(uint8_t *mbox, union mfi_pd_ref *ref)
242763fae79SScott Long {
243763fae79SScott Long
244763fae79SScott Long mbox[0] = ref->v.device_id & 0xff;
245763fae79SScott Long mbox[1] = ref->v.device_id >> 8;
246763fae79SScott Long mbox[2] = ref->v.seq_num & 0xff;
247763fae79SScott Long mbox[3] = ref->v.seq_num >> 8;
248763fae79SScott Long }
249763fae79SScott Long
250763fae79SScott Long int
mfi_pd_get_list(int fd,struct mfi_pd_list ** listp,uint8_t * statusp)251763fae79SScott Long mfi_pd_get_list(int fd, struct mfi_pd_list **listp, uint8_t *statusp)
252763fae79SScott Long {
253763fae79SScott Long struct mfi_pd_list *list;
254763fae79SScott Long uint32_t list_size;
255763fae79SScott Long
256763fae79SScott Long /*
257763fae79SScott Long * Keep fetching the list in a loop until we have a large enough
258763fae79SScott Long * buffer to hold the entire list.
259763fae79SScott Long */
260763fae79SScott Long list = NULL;
261763fae79SScott Long list_size = 1024;
262763fae79SScott Long fetch:
263763fae79SScott Long list = reallocf(list, list_size);
264763fae79SScott Long if (list == NULL)
265763fae79SScott Long return (-1);
266763fae79SScott Long if (mfi_dcmd_command(fd, MFI_DCMD_PD_GET_LIST, list, list_size, NULL,
267763fae79SScott Long 0, statusp) < 0) {
268763fae79SScott Long free(list);
269763fae79SScott Long return (-1);
270763fae79SScott Long }
271763fae79SScott Long
272763fae79SScott Long if (list->size > list_size) {
273763fae79SScott Long list_size = list->size;
274763fae79SScott Long goto fetch;
275763fae79SScott Long }
276763fae79SScott Long
277763fae79SScott Long *listp = list;
278763fae79SScott Long return (0);
279763fae79SScott Long }
280763fae79SScott Long
281763fae79SScott Long int
mfi_pd_get_info(int fd,uint16_t device_id,struct mfi_pd_info * info,uint8_t * statusp)282763fae79SScott Long mfi_pd_get_info(int fd, uint16_t device_id, struct mfi_pd_info *info,
283763fae79SScott Long uint8_t *statusp)
284763fae79SScott Long {
285763fae79SScott Long uint8_t mbox[2];
286763fae79SScott Long
287763fae79SScott Long mbox_store_device_id(&mbox[0], device_id);
288763fae79SScott Long return (mfi_dcmd_command(fd, MFI_DCMD_PD_GET_INFO, info,
289763fae79SScott Long sizeof(struct mfi_pd_info), mbox, 2, statusp));
290763fae79SScott Long }
291763fae79SScott Long
292763fae79SScott Long static void
cam_strvis(char * dst,const char * src,int srclen,int dstlen)293763fae79SScott Long cam_strvis(char *dst, const char *src, int srclen, int dstlen)
294763fae79SScott Long {
295763fae79SScott Long
296763fae79SScott Long /* Trim leading/trailing spaces, nulls. */
297763fae79SScott Long while (srclen > 0 && src[0] == ' ')
298763fae79SScott Long src++, srclen--;
299763fae79SScott Long while (srclen > 0
300763fae79SScott Long && (src[srclen-1] == ' ' || src[srclen-1] == '\0'))
301763fae79SScott Long srclen--;
302763fae79SScott Long
303763fae79SScott Long while (srclen > 0 && dstlen > 1) {
304763fae79SScott Long char *cur_pos = dst;
305763fae79SScott Long
306763fae79SScott Long if (*src < 0x20) {
307763fae79SScott Long /* SCSI-II Specifies that these should never occur. */
308763fae79SScott Long /* non-printable character */
309763fae79SScott Long if (dstlen > 4) {
310763fae79SScott Long *cur_pos++ = '\\';
311763fae79SScott Long *cur_pos++ = ((*src & 0300) >> 6) + '0';
312763fae79SScott Long *cur_pos++ = ((*src & 0070) >> 3) + '0';
313763fae79SScott Long *cur_pos++ = ((*src & 0007) >> 0) + '0';
314763fae79SScott Long } else {
315763fae79SScott Long *cur_pos++ = '?';
316763fae79SScott Long }
317763fae79SScott Long } else {
318763fae79SScott Long /* normal character */
319763fae79SScott Long *cur_pos++ = *src;
320763fae79SScott Long }
321763fae79SScott Long src++;
322763fae79SScott Long srclen--;
323763fae79SScott Long dstlen -= cur_pos - dst;
324763fae79SScott Long dst = cur_pos;
325763fae79SScott Long }
326763fae79SScott Long *dst = '\0';
327763fae79SScott Long }
328763fae79SScott Long
329763fae79SScott Long /* Borrowed heavily from scsi_all.c:scsi_print_inquiry(). */
330763fae79SScott Long const char *
mfi_pd_inq_string(struct mfi_pd_info * info)331763fae79SScott Long mfi_pd_inq_string(struct mfi_pd_info *info)
332763fae79SScott Long {
3330896d525SMatt Jacob struct scsi_inquiry_data iqd, *inq_data = &iqd;
334763fae79SScott Long char vendor[16], product[48], revision[16], rstr[12], serial[SID_VENDOR_SPECIFIC_0_SIZE];
335763fae79SScott Long static char inq_string[64];
336763fae79SScott Long
3370896d525SMatt Jacob memcpy(inq_data, info->inquiry_data,
3380896d525SMatt Jacob (sizeof (iqd) < sizeof (info->inquiry_data))?
3390896d525SMatt Jacob sizeof (iqd) : sizeof (info->inquiry_data));
340763fae79SScott Long if (SID_QUAL_IS_VENDOR_UNIQUE(inq_data))
341763fae79SScott Long return (NULL);
342763fae79SScott Long if (SID_TYPE(inq_data) != T_DIRECT)
343763fae79SScott Long return (NULL);
344763fae79SScott Long if (SID_QUAL(inq_data) != SID_QUAL_LU_CONNECTED)
345763fae79SScott Long return (NULL);
346763fae79SScott Long
347763fae79SScott Long cam_strvis(vendor, inq_data->vendor, sizeof(inq_data->vendor),
348763fae79SScott Long sizeof(vendor));
349763fae79SScott Long cam_strvis(product, inq_data->product, sizeof(inq_data->product),
350763fae79SScott Long sizeof(product));
351763fae79SScott Long cam_strvis(revision, inq_data->revision, sizeof(inq_data->revision),
352763fae79SScott Long sizeof(revision));
353763fae79SScott Long cam_strvis(serial, (char *)inq_data->vendor_specific0, sizeof(inq_data->vendor_specific0),
354763fae79SScott Long sizeof(serial));
355763fae79SScott Long
356763fae79SScott Long /* Hack for SATA disks, no idea how to tell speed. */
357763fae79SScott Long if (strcmp(vendor, "ATA") == 0) {
358763fae79SScott Long snprintf(inq_string, sizeof(inq_string), "<%s %s serial=%s> SATA",
359763fae79SScott Long product, revision, serial);
360763fae79SScott Long return (inq_string);
361763fae79SScott Long }
362763fae79SScott Long
363763fae79SScott Long switch (SID_ANSI_REV(inq_data)) {
364763fae79SScott Long case SCSI_REV_CCS:
365763fae79SScott Long strcpy(rstr, "SCSI-CCS");
366763fae79SScott Long break;
367763fae79SScott Long case 5:
368763fae79SScott Long strcpy(rstr, "SAS");
369763fae79SScott Long break;
370763fae79SScott Long default:
371763fae79SScott Long snprintf(rstr, sizeof (rstr), "SCSI-%d",
372763fae79SScott Long SID_ANSI_REV(inq_data));
373763fae79SScott Long break;
374763fae79SScott Long }
375763fae79SScott Long snprintf(inq_string, sizeof(inq_string), "<%s %s %s serial=%s> %s", vendor,
376763fae79SScott Long product, revision, serial, rstr);
377763fae79SScott Long return (inq_string);
378763fae79SScott Long }
379763fae79SScott Long
380763fae79SScott Long /* Helper function to set a drive to a given state. */
381763fae79SScott Long static int
drive_set_state(char * drive,uint16_t new_state)382763fae79SScott Long drive_set_state(char *drive, uint16_t new_state)
383763fae79SScott Long {
384763fae79SScott Long struct mfi_pd_info info;
385763fae79SScott Long uint16_t device_id;
386763fae79SScott Long uint8_t mbox[6];
387763fae79SScott Long int error, fd;
388763fae79SScott Long
389*7e0f8b79SDoug Ambrisko fd = mfi_open(mfi_device, O_RDWR);
390763fae79SScott Long if (fd < 0) {
391c02999d9SJohn Baldwin error = errno;
392763fae79SScott Long warn("mfi_open");
393c02999d9SJohn Baldwin return (error);
394763fae79SScott Long }
395763fae79SScott Long
396763fae79SScott Long error = mfi_lookup_drive(fd, drive, &device_id);
397375c4656SBjoern A. Zeeb if (error) {
398375c4656SBjoern A. Zeeb close(fd);
399763fae79SScott Long return (error);
400375c4656SBjoern A. Zeeb }
401763fae79SScott Long
402763fae79SScott Long /* Get the info for this drive. */
403763fae79SScott Long if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) {
404c02999d9SJohn Baldwin error = errno;
405763fae79SScott Long warn("Failed to fetch info for drive %u", device_id);
406375c4656SBjoern A. Zeeb close(fd);
407c02999d9SJohn Baldwin return (error);
408763fae79SScott Long }
409763fae79SScott Long
410763fae79SScott Long /* Try to change the state. */
411763fae79SScott Long if (info.fw_state == new_state) {
412763fae79SScott Long warnx("Drive %u is already in the desired state", device_id);
413375c4656SBjoern A. Zeeb close(fd);
414763fae79SScott Long return (EINVAL);
415763fae79SScott Long }
416763fae79SScott Long
417763fae79SScott Long mbox_store_pdref(&mbox[0], &info.ref);
418763fae79SScott Long mbox[4] = new_state & 0xff;
419763fae79SScott Long mbox[5] = new_state >> 8;
420763fae79SScott Long if (mfi_dcmd_command(fd, MFI_DCMD_PD_STATE_SET, NULL, 0, mbox, 6,
421763fae79SScott Long NULL) < 0) {
422c02999d9SJohn Baldwin error = errno;
423763fae79SScott Long warn("Failed to set drive %u to %s", device_id,
424763fae79SScott Long mfi_pdstate(new_state));
425375c4656SBjoern A. Zeeb close(fd);
426c02999d9SJohn Baldwin return (error);
427763fae79SScott Long }
428763fae79SScott Long
429763fae79SScott Long close(fd);
430763fae79SScott Long
431763fae79SScott Long return (0);
432763fae79SScott Long }
433763fae79SScott Long
434763fae79SScott Long static int
fail_drive(int ac,char ** av)435763fae79SScott Long fail_drive(int ac, char **av)
436763fae79SScott Long {
437763fae79SScott Long
438763fae79SScott Long if (ac != 2) {
439763fae79SScott Long warnx("fail: %s", ac > 2 ? "extra arguments" :
440763fae79SScott Long "drive required");
441763fae79SScott Long return (EINVAL);
442763fae79SScott Long }
443763fae79SScott Long
444763fae79SScott Long return (drive_set_state(av[1], MFI_PD_STATE_FAILED));
445763fae79SScott Long }
446763fae79SScott Long MFI_COMMAND(top, fail, fail_drive);
447763fae79SScott Long
448763fae79SScott Long static int
good_drive(int ac,char ** av)449763fae79SScott Long good_drive(int ac, char **av)
450763fae79SScott Long {
451763fae79SScott Long
452763fae79SScott Long if (ac != 2) {
453763fae79SScott Long warnx("good: %s", ac > 2 ? "extra arguments" :
454763fae79SScott Long "drive required");
455763fae79SScott Long return (EINVAL);
456763fae79SScott Long }
457763fae79SScott Long
458763fae79SScott Long return (drive_set_state(av[1], MFI_PD_STATE_UNCONFIGURED_GOOD));
459763fae79SScott Long }
460763fae79SScott Long MFI_COMMAND(top, good, good_drive);
461763fae79SScott Long
462763fae79SScott Long static int
rebuild_drive(int ac,char ** av)463763fae79SScott Long rebuild_drive(int ac, char **av)
464763fae79SScott Long {
465763fae79SScott Long
466763fae79SScott Long if (ac != 2) {
467763fae79SScott Long warnx("rebuild: %s", ac > 2 ? "extra arguments" :
468763fae79SScott Long "drive required");
469763fae79SScott Long return (EINVAL);
470763fae79SScott Long }
471763fae79SScott Long
472763fae79SScott Long return (drive_set_state(av[1], MFI_PD_STATE_REBUILD));
473763fae79SScott Long }
474763fae79SScott Long MFI_COMMAND(top, rebuild, rebuild_drive);
475763fae79SScott Long
476763fae79SScott Long static int
syspd_drive(int ac,char ** av)4777c2ad1eeSSean Bruno syspd_drive(int ac, char **av)
4787c2ad1eeSSean Bruno {
4797c2ad1eeSSean Bruno
4807c2ad1eeSSean Bruno if (ac != 2) {
4817c2ad1eeSSean Bruno warnx("syspd: %s", ac > 2 ? "extra arguments" :
4827c2ad1eeSSean Bruno "drive required");
4837c2ad1eeSSean Bruno return (EINVAL);
4847c2ad1eeSSean Bruno }
4857c2ad1eeSSean Bruno
4867c2ad1eeSSean Bruno return (drive_set_state(av[1], MFI_PD_STATE_SYSTEM));
4877c2ad1eeSSean Bruno }
4887c2ad1eeSSean Bruno MFI_COMMAND(top, syspd, syspd_drive);
4897c2ad1eeSSean Bruno
4907c2ad1eeSSean Bruno static int
start_rebuild(int ac,char ** av)491763fae79SScott Long start_rebuild(int ac, char **av)
492763fae79SScott Long {
493763fae79SScott Long struct mfi_pd_info info;
494763fae79SScott Long uint16_t device_id;
495763fae79SScott Long uint8_t mbox[4];
496763fae79SScott Long int error, fd;
497763fae79SScott Long
498763fae79SScott Long if (ac != 2) {
499763fae79SScott Long warnx("start rebuild: %s", ac > 2 ? "extra arguments" :
500763fae79SScott Long "drive required");
501763fae79SScott Long return (EINVAL);
502763fae79SScott Long }
503763fae79SScott Long
504*7e0f8b79SDoug Ambrisko fd = mfi_open(mfi_device, O_RDWR);
505763fae79SScott Long if (fd < 0) {
506c02999d9SJohn Baldwin error = errno;
507763fae79SScott Long warn("mfi_open");
508c02999d9SJohn Baldwin return (error);
509763fae79SScott Long }
510763fae79SScott Long
511763fae79SScott Long error = mfi_lookup_drive(fd, av[1], &device_id);
512375c4656SBjoern A. Zeeb if (error) {
513375c4656SBjoern A. Zeeb close(fd);
514763fae79SScott Long return (error);
515375c4656SBjoern A. Zeeb }
516763fae79SScott Long
517763fae79SScott Long /* Get the info for this drive. */
518763fae79SScott Long if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) {
519c02999d9SJohn Baldwin error = errno;
520763fae79SScott Long warn("Failed to fetch info for drive %u", device_id);
521375c4656SBjoern A. Zeeb close(fd);
522c02999d9SJohn Baldwin return (error);
523763fae79SScott Long }
524763fae79SScott Long
525763fae79SScott Long /* Check the state, must be REBUILD. */
526763fae79SScott Long if (info.fw_state != MFI_PD_STATE_REBUILD) {
527c02999d9SJohn Baldwin warnx("Drive %d is not in the REBUILD state", device_id);
528375c4656SBjoern A. Zeeb close(fd);
529763fae79SScott Long return (EINVAL);
530763fae79SScott Long }
531763fae79SScott Long
532763fae79SScott Long /* Start the rebuild. */
533763fae79SScott Long mbox_store_pdref(&mbox[0], &info.ref);
534763fae79SScott Long if (mfi_dcmd_command(fd, MFI_DCMD_PD_REBUILD_START, NULL, 0, mbox, 4,
535763fae79SScott Long NULL) < 0) {
536c02999d9SJohn Baldwin error = errno;
537763fae79SScott Long warn("Failed to start rebuild on drive %u", device_id);
538375c4656SBjoern A. Zeeb close(fd);
539c02999d9SJohn Baldwin return (error);
540763fae79SScott Long }
541763fae79SScott Long close(fd);
542763fae79SScott Long
543763fae79SScott Long return (0);
544763fae79SScott Long }
545763fae79SScott Long MFI_COMMAND(start, rebuild, start_rebuild);
546763fae79SScott Long
547763fae79SScott Long static int
abort_rebuild(int ac,char ** av)548763fae79SScott Long abort_rebuild(int ac, char **av)
549763fae79SScott Long {
550763fae79SScott Long struct mfi_pd_info info;
551763fae79SScott Long uint16_t device_id;
552763fae79SScott Long uint8_t mbox[4];
553763fae79SScott Long int error, fd;
554763fae79SScott Long
555763fae79SScott Long if (ac != 2) {
556763fae79SScott Long warnx("abort rebuild: %s", ac > 2 ? "extra arguments" :
557763fae79SScott Long "drive required");
558763fae79SScott Long return (EINVAL);
559763fae79SScott Long }
560763fae79SScott Long
561*7e0f8b79SDoug Ambrisko fd = mfi_open(mfi_device, O_RDWR);
562763fae79SScott Long if (fd < 0) {
563c02999d9SJohn Baldwin error = errno;
564763fae79SScott Long warn("mfi_open");
565c02999d9SJohn Baldwin return (error);
566763fae79SScott Long }
567763fae79SScott Long
568763fae79SScott Long error = mfi_lookup_drive(fd, av[1], &device_id);
569375c4656SBjoern A. Zeeb if (error) {
570375c4656SBjoern A. Zeeb close(fd);
571763fae79SScott Long return (error);
572375c4656SBjoern A. Zeeb }
573763fae79SScott Long
574763fae79SScott Long /* Get the info for this drive. */
575763fae79SScott Long if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) {
576c02999d9SJohn Baldwin error = errno;
577763fae79SScott Long warn("Failed to fetch info for drive %u", device_id);
578375c4656SBjoern A. Zeeb close(fd);
579c02999d9SJohn Baldwin return (error);
580763fae79SScott Long }
581763fae79SScott Long
582763fae79SScott Long /* Check the state, must be REBUILD. */
583763fae79SScott Long if (info.fw_state != MFI_PD_STATE_REBUILD) {
584763fae79SScott Long warn("Drive %d is not in the REBUILD state", device_id);
585375c4656SBjoern A. Zeeb close(fd);
586763fae79SScott Long return (EINVAL);
587763fae79SScott Long }
588763fae79SScott Long
589763fae79SScott Long /* Abort the rebuild. */
590763fae79SScott Long mbox_store_pdref(&mbox[0], &info.ref);
591763fae79SScott Long if (mfi_dcmd_command(fd, MFI_DCMD_PD_REBUILD_ABORT, NULL, 0, mbox, 4,
592763fae79SScott Long NULL) < 0) {
593c02999d9SJohn Baldwin error = errno;
594763fae79SScott Long warn("Failed to abort rebuild on drive %u", device_id);
595375c4656SBjoern A. Zeeb close(fd);
596c02999d9SJohn Baldwin return (error);
597763fae79SScott Long }
598763fae79SScott Long close(fd);
599763fae79SScott Long
600763fae79SScott Long return (0);
601763fae79SScott Long }
602763fae79SScott Long MFI_COMMAND(abort, rebuild, abort_rebuild);
603763fae79SScott Long
604763fae79SScott Long static int
drive_progress(int ac,char ** av)605763fae79SScott Long drive_progress(int ac, char **av)
606763fae79SScott Long {
607763fae79SScott Long struct mfi_pd_info info;
608763fae79SScott Long uint16_t device_id;
609763fae79SScott Long int error, fd;
610763fae79SScott Long
611763fae79SScott Long if (ac != 2) {
612763fae79SScott Long warnx("drive progress: %s", ac > 2 ? "extra arguments" :
613763fae79SScott Long "drive required");
614763fae79SScott Long return (EINVAL);
615763fae79SScott Long }
616763fae79SScott Long
617*7e0f8b79SDoug Ambrisko fd = mfi_open(mfi_device, O_RDWR);
618763fae79SScott Long if (fd < 0) {
619c02999d9SJohn Baldwin error = errno;
620763fae79SScott Long warn("mfi_open");
621c02999d9SJohn Baldwin return (error);
622763fae79SScott Long }
623763fae79SScott Long
624763fae79SScott Long error = mfi_lookup_drive(fd, av[1], &device_id);
625375c4656SBjoern A. Zeeb if (error) {
626375c4656SBjoern A. Zeeb close(fd);
627763fae79SScott Long return (error);
628375c4656SBjoern A. Zeeb }
629763fae79SScott Long
630763fae79SScott Long /* Get the info for this drive. */
631763fae79SScott Long if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) {
632c02999d9SJohn Baldwin error = errno;
633763fae79SScott Long warn("Failed to fetch info for drive %u", device_id);
634375c4656SBjoern A. Zeeb close(fd);
635c02999d9SJohn Baldwin return (error);
636763fae79SScott Long }
637763fae79SScott Long close(fd);
638763fae79SScott Long
639763fae79SScott Long /* Display any of the active events. */
640763fae79SScott Long if (info.prog_info.active & MFI_PD_PROGRESS_REBUILD)
641763fae79SScott Long mfi_display_progress("Rebuild", &info.prog_info.rbld);
642763fae79SScott Long if (info.prog_info.active & MFI_PD_PROGRESS_PATROL)
643763fae79SScott Long mfi_display_progress("Patrol Read", &info.prog_info.patrol);
644763fae79SScott Long if (info.prog_info.active & MFI_PD_PROGRESS_CLEAR)
645763fae79SScott Long mfi_display_progress("Clear", &info.prog_info.clear);
646763fae79SScott Long if ((info.prog_info.active & (MFI_PD_PROGRESS_REBUILD |
647763fae79SScott Long MFI_PD_PROGRESS_PATROL | MFI_PD_PROGRESS_CLEAR)) == 0)
6487bbae305SBjoern A. Zeeb printf("No activity in progress for drive %s.\n",
6497bbae305SBjoern A. Zeeb mfi_drive_name(NULL, device_id,
6507bbae305SBjoern A. Zeeb MFI_DNAME_DEVICE_ID|MFI_DNAME_HONOR_OPTS));
651763fae79SScott Long
652763fae79SScott Long return (0);
653763fae79SScott Long }
654763fae79SScott Long MFI_COMMAND(drive, progress, drive_progress);
655763fae79SScott Long
656763fae79SScott Long static int
drive_clear(int ac,char ** av)657763fae79SScott Long drive_clear(int ac, char **av)
658763fae79SScott Long {
659763fae79SScott Long struct mfi_pd_info info;
660763fae79SScott Long uint32_t opcode;
661763fae79SScott Long uint16_t device_id;
662763fae79SScott Long uint8_t mbox[4];
663763fae79SScott Long char *s1;
664763fae79SScott Long int error, fd;
665763fae79SScott Long
666763fae79SScott Long if (ac != 3) {
667763fae79SScott Long warnx("drive clear: %s", ac > 3 ? "extra arguments" :
668763fae79SScott Long "drive and action requires");
669763fae79SScott Long return (EINVAL);
670763fae79SScott Long }
671763fae79SScott Long
672763fae79SScott Long for (s1 = av[2]; *s1 != '\0'; s1++)
673763fae79SScott Long *s1 = tolower(*s1);
674763fae79SScott Long if (strcmp(av[2], "start") == 0)
675763fae79SScott Long opcode = MFI_DCMD_PD_CLEAR_START;
676763fae79SScott Long else if ((strcmp(av[2], "stop") == 0) || (strcmp(av[2], "abort") == 0))
677763fae79SScott Long opcode = MFI_DCMD_PD_CLEAR_ABORT;
678763fae79SScott Long else {
679763fae79SScott Long warnx("drive clear: invalid action, must be 'start' or 'stop'\n");
680763fae79SScott Long return (EINVAL);
681763fae79SScott Long }
682763fae79SScott Long
683*7e0f8b79SDoug Ambrisko fd = mfi_open(mfi_device, O_RDWR);
684763fae79SScott Long if (fd < 0) {
685c02999d9SJohn Baldwin error = errno;
686763fae79SScott Long warn("mfi_open");
687c02999d9SJohn Baldwin return (error);
688763fae79SScott Long }
689763fae79SScott Long
690763fae79SScott Long error = mfi_lookup_drive(fd, av[1], &device_id);
691375c4656SBjoern A. Zeeb if (error) {
692375c4656SBjoern A. Zeeb close(fd);
693763fae79SScott Long return (error);
694375c4656SBjoern A. Zeeb }
695763fae79SScott Long
696763fae79SScott Long /* Get the info for this drive. */
697763fae79SScott Long if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) {
698c02999d9SJohn Baldwin error = errno;
699763fae79SScott Long warn("Failed to fetch info for drive %u", device_id);
700375c4656SBjoern A. Zeeb close(fd);
701c02999d9SJohn Baldwin return (error);
702763fae79SScott Long }
703763fae79SScott Long
704763fae79SScott Long mbox_store_pdref(&mbox[0], &info.ref);
705763fae79SScott Long if (mfi_dcmd_command(fd, opcode, NULL, 0, mbox, 4, NULL) < 0) {
706c02999d9SJohn Baldwin error = errno;
707763fae79SScott Long warn("Failed to %s clear on drive %u",
708763fae79SScott Long opcode == MFI_DCMD_PD_CLEAR_START ? "start" : "stop",
709763fae79SScott Long device_id);
710375c4656SBjoern A. Zeeb close(fd);
711c02999d9SJohn Baldwin return (error);
712763fae79SScott Long }
713763fae79SScott Long
714763fae79SScott Long close(fd);
715763fae79SScott Long return (0);
716763fae79SScott Long }
717763fae79SScott Long MFI_COMMAND(drive, clear, drive_clear);
718763fae79SScott Long
719763fae79SScott Long static int
drive_locate(int ac,char ** av)720763fae79SScott Long drive_locate(int ac, char **av)
721763fae79SScott Long {
722763fae79SScott Long uint16_t device_id;
723763fae79SScott Long uint32_t opcode;
724763fae79SScott Long int error, fd;
725763fae79SScott Long uint8_t mbox[4];
726763fae79SScott Long
727763fae79SScott Long if (ac != 3) {
728763fae79SScott Long warnx("locate: %s", ac > 3 ? "extra arguments" :
729763fae79SScott Long "drive and state required");
730763fae79SScott Long return (EINVAL);
731763fae79SScott Long }
732763fae79SScott Long
733763fae79SScott Long if (strcasecmp(av[2], "on") == 0 || strcasecmp(av[2], "start") == 0)
734763fae79SScott Long opcode = MFI_DCMD_PD_LOCATE_START;
735763fae79SScott Long else if (strcasecmp(av[2], "off") == 0 ||
736763fae79SScott Long strcasecmp(av[2], "stop") == 0)
737763fae79SScott Long opcode = MFI_DCMD_PD_LOCATE_STOP;
738763fae79SScott Long else {
739763fae79SScott Long warnx("locate: invalid state %s", av[2]);
740763fae79SScott Long return (EINVAL);
741763fae79SScott Long }
742763fae79SScott Long
743*7e0f8b79SDoug Ambrisko fd = mfi_open(mfi_device, O_RDWR);
744763fae79SScott Long if (fd < 0) {
745c02999d9SJohn Baldwin error = errno;
746763fae79SScott Long warn("mfi_open");
747c02999d9SJohn Baldwin return (error);
748763fae79SScott Long }
749763fae79SScott Long
750763fae79SScott Long error = mfi_lookup_drive(fd, av[1], &device_id);
751375c4656SBjoern A. Zeeb if (error) {
752375c4656SBjoern A. Zeeb close(fd);
753763fae79SScott Long return (error);
754375c4656SBjoern A. Zeeb }
755763fae79SScott Long
756763fae79SScott Long
757763fae79SScott Long mbox_store_device_id(&mbox[0], device_id);
758763fae79SScott Long mbox[2] = 0;
759763fae79SScott Long mbox[3] = 0;
760763fae79SScott Long if (mfi_dcmd_command(fd, opcode, NULL, 0, mbox, 4, NULL) < 0) {
761c02999d9SJohn Baldwin error = errno;
762763fae79SScott Long warn("Failed to %s locate on drive %u",
763763fae79SScott Long opcode == MFI_DCMD_PD_LOCATE_START ? "start" : "stop",
764763fae79SScott Long device_id);
765375c4656SBjoern A. Zeeb close(fd);
766c02999d9SJohn Baldwin return (error);
767763fae79SScott Long }
768763fae79SScott Long close(fd);
769763fae79SScott Long
770763fae79SScott Long return (0);
771763fae79SScott Long }
772763fae79SScott Long MFI_COMMAND(top, locate, drive_locate);
773