xref: /freebsd/usr.sbin/mfiutil/mfi_show.c (revision f7c32ed617858bcd22f8d1b03199099d50125721)
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 <err.h>
37 #include <fcntl.h>
38 #include <libutil.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <unistd.h>
43 #include "mfiutil.h"
44 
45 static const char* foreign_state = " (FOREIGN)";
46 
47 MFI_TABLE(top, show);
48 
49 void
50 format_stripe(char *buf, size_t buflen, uint8_t stripe)
51 {
52 
53 	humanize_number(buf, buflen, (1 << stripe) * 512, "", HN_AUTOSCALE,
54 	    HN_B | HN_NOSPACE);
55 }
56 
57 static int
58 show_adapter(int ac, char **av __unused)
59 {
60 	struct mfi_ctrl_info info;
61 	char stripe[5];
62 	int error, fd, comma;
63 
64 	if (ac != 1) {
65 		warnx("show adapter: extra arguments");
66 		return (EINVAL);
67 	}
68 
69 	fd = mfi_open(mfi_unit, O_RDONLY);
70 	if (fd < 0) {
71 		error = errno;
72 		warn("mfi_open");
73 		return (error);
74 	}
75 
76 	if (mfi_ctrl_get_info(fd, &info, NULL) < 0) {
77 		error = errno;
78 		warn("Failed to get controller info");
79 		close(fd);
80 		return (error);
81 	}
82 	printf("mfi%d Adapter:\n", mfi_unit);
83 	printf("    Product Name: %.80s\n", info.product_name);
84 	printf("   Serial Number: %.32s\n", info.serial_number);
85 	if (info.package_version[0] != '\0')
86 		printf("        Firmware: %s\n", info.package_version);
87 	printf("     RAID Levels:");
88 #ifdef DEBUG
89 	printf(" (%#x)", info.raid_levels);
90 #endif
91 	comma = 0;
92 	if (info.raid_levels & MFI_INFO_RAID_0) {
93 		printf(" JBOD, RAID0");
94 		comma = 1;
95 	}
96 	if (info.raid_levels & MFI_INFO_RAID_1) {
97 		printf("%s RAID1", comma ? "," : "");
98 		comma = 1;
99 	}
100 	if (info.raid_levels & MFI_INFO_RAID_5) {
101 		printf("%s RAID5", comma ? "," : "");
102 		comma = 1;
103 	}
104 	if (info.raid_levels & MFI_INFO_RAID_1E) {
105 		printf("%s RAID1E", comma ? "," : "");
106 		comma = 1;
107 	}
108 	if (info.raid_levels & MFI_INFO_RAID_6) {
109 		printf("%s RAID6", comma ? "," : "");
110 		comma = 1;
111 	}
112 	if ((info.raid_levels & (MFI_INFO_RAID_0 | MFI_INFO_RAID_1)) ==
113 	    (MFI_INFO_RAID_0 | MFI_INFO_RAID_1)) {
114 		printf("%s RAID10", comma ? "," : "");
115 		comma = 1;
116 	}
117 	if ((info.raid_levels & (MFI_INFO_RAID_0 | MFI_INFO_RAID_5)) ==
118 	    (MFI_INFO_RAID_0 | MFI_INFO_RAID_5)) {
119 		printf("%s RAID50", comma ? "," : "");
120 		comma = 1;
121 	}
122 	printf("\n");
123 	printf("  Battery Backup: ");
124 	if (info.hw_present & MFI_INFO_HW_BBU)
125 		printf("present\n");
126 	else
127 		printf("not present\n");
128 	if (info.hw_present & MFI_INFO_HW_NVRAM)
129 		printf("           NVRAM: %uK\n", info.nvram_size);
130 	printf("  Onboard Memory: %uM\n", info.memory_size);
131 	format_stripe(stripe, sizeof(stripe), info.stripe_sz_ops.min);
132 	printf("  Minimum Stripe: %s\n", stripe);
133 	format_stripe(stripe, sizeof(stripe), info.stripe_sz_ops.max);
134 	printf("  Maximum Stripe: %s\n", stripe);
135 
136 	close(fd);
137 
138 	return (0);
139 }
140 MFI_COMMAND(show, adapter, show_adapter);
141 
142 static int
143 show_battery(int ac, char **av __unused)
144 {
145 	struct mfi_bbu_capacity_info cap;
146 	struct mfi_bbu_design_info design;
147 	struct mfi_bbu_properties props;
148 	struct mfi_bbu_status stat;
149 	uint8_t status;
150 	int comma, error, fd, show_capacity, show_props;
151 	char buf[32];
152 
153 	if (ac != 1) {
154 		warnx("show battery: extra arguments");
155 		return (EINVAL);
156 	}
157 
158 	fd = mfi_open(mfi_unit, O_RDONLY);
159 	if (fd < 0) {
160 		error = errno;
161 		warn("mfi_open");
162 		return (error);
163 	}
164 
165 	if (mfi_dcmd_command(fd, MFI_DCMD_BBU_GET_CAPACITY_INFO, &cap,
166 	    sizeof(cap), NULL, 0, &status) < 0) {
167 		error = errno;
168 		warn("Failed to get capacity info");
169 		close(fd);
170 		return (error);
171 	}
172 	if (status == MFI_STAT_NO_HW_PRESENT) {
173 		printf("mfi%d: No battery present\n", mfi_unit);
174 		close(fd);
175 		return (0);
176 	}
177 	show_capacity = (status == MFI_STAT_OK);
178 
179 	if (mfi_dcmd_command(fd, MFI_DCMD_BBU_GET_DESIGN_INFO, &design,
180 	    sizeof(design), NULL, 0, NULL) < 0) {
181 		error = errno;
182 		warn("Failed to get design info");
183 		close(fd);
184 		return (error);
185 	}
186 
187 	if (mfi_dcmd_command(fd, MFI_DCMD_BBU_GET_STATUS, &stat, sizeof(stat),
188 	    NULL, 0, NULL) < 0) {
189 		error = errno;
190 		warn("Failed to get status");
191 		close(fd);
192 		return (error);
193 	}
194 
195 	if (mfi_bbu_get_props(fd, &props, &status) < 0) {
196 		error = errno;
197 		warn("Failed to get properties");
198 		close(fd);
199 		return (error);
200 	}
201 	show_props = (status == MFI_STAT_OK);
202 
203 	printf("mfi%d: Battery State:\n", mfi_unit);
204 	printf("     Manufacture Date: %d/%d/%d\n", design.mfg_date >> 5 & 0x0f,
205 	    design.mfg_date & 0x1f, design.mfg_date >> 9 & 0xffff);
206 	printf("        Serial Number: %d\n", design.serial_number);
207 	printf("         Manufacturer: %s\n", design.mfg_name);
208 	printf("                Model: %s\n", design.device_name);
209 	printf("            Chemistry: %s\n", design.device_chemistry);
210 	printf("      Design Capacity: %d mAh\n", design.design_capacity);
211 	if (show_capacity) {
212 		printf(" Full Charge Capacity: %d mAh\n",
213 		    cap.full_charge_capacity);
214 		printf("     Current Capacity: %d mAh\n",
215 		    cap.remaining_capacity);
216 		printf("        Charge Cycles: %d\n", cap.cycle_count);
217 		printf("       Current Charge: %d%%\n", cap.relative_charge);
218 	}
219 	printf("       Design Voltage: %d mV\n", design.design_voltage);
220 	printf("      Current Voltage: %d mV\n", stat.voltage);
221 	printf("          Temperature: %d C\n", stat.temperature);
222 	if (show_props) {
223 		mfi_autolearn_period(props.auto_learn_period, buf, sizeof(buf));
224 		printf("     Autolearn period: %s\n", buf);
225 		if (props.auto_learn_mode != 0)
226 			snprintf(buf, sizeof(buf), "never");
227 		else
228 			mfi_next_learn_time(props.next_learn_time, buf,
229 			    sizeof(buf));
230 		printf("      Next learn time: %s\n", buf);
231 		printf(" Learn delay interval: %u hour%s\n",
232 		    props.learn_delay_interval,
233 		    props.learn_delay_interval != 1 ? "s" : "");
234 		mfi_autolearn_mode(props.auto_learn_mode, buf, sizeof(buf));
235 		printf("       Autolearn mode: %s\n", buf);
236 		if (props.bbu_mode != 0)
237 			printf("             BBU Mode: %d\n", props.bbu_mode);
238 	}
239 	printf("               Status:");
240 	comma = 0;
241 	if (stat.fw_status & MFI_BBU_STATE_PACK_MISSING) {
242 		printf(" PACK_MISSING");
243 		comma = 1;
244 	}
245 	if (stat.fw_status & MFI_BBU_STATE_VOLTAGE_LOW) {
246 		printf("%s VOLTAGE_LOW", comma ? "," : "");
247 		comma = 1;
248 	}
249 	if (stat.fw_status & MFI_BBU_STATE_TEMPERATURE_HIGH) {
250 		printf("%s TEMPERATURE_HIGH", comma ? "," : "");
251 		comma = 1;
252 	}
253 	if (stat.fw_status & MFI_BBU_STATE_CHARGE_ACTIVE) {
254 		printf("%s CHARGING", comma ? "," : "");
255 		comma = 1;
256 	}
257 	if (stat.fw_status & MFI_BBU_STATE_DISCHARGE_ACTIVE) {
258 		printf("%s DISCHARGING", comma ? "," : "");
259 		comma = 1;
260 	}
261 	if (stat.fw_status & MFI_BBU_STATE_LEARN_CYC_REQ) {
262 		printf("%s LEARN_CYCLE_REQUESTED", comma ? "," : "");
263 		comma = 1;
264 	}
265 	if (stat.fw_status & MFI_BBU_STATE_LEARN_CYC_ACTIVE) {
266 		printf("%s LEARN_CYCLE_ACTIVE", comma ? "," : "");
267 		comma = 1;
268 	}
269 	if (stat.fw_status & MFI_BBU_STATE_LEARN_CYC_FAIL) {
270 		printf("%s LEARN_CYCLE_FAIL", comma ? "," : "");
271 		comma = 1;
272 	}
273 	if (stat.fw_status & MFI_BBU_STATE_LEARN_CYC_TIMEOUT) {
274 		printf("%s LEARN_CYCLE_TIMEOUT", comma ? "," : "");
275 		comma = 1;
276 	}
277 	if (stat.fw_status & MFI_BBU_STATE_I2C_ERR_DETECT) {
278 		printf("%s I2C_ERROR_DETECT", comma ? "," : "");
279 		comma = 1;
280 	}
281 
282 	if (!comma)
283 		printf(" normal");
284 	printf("\n");
285 	switch (stat.battery_type) {
286 	case MFI_BBU_TYPE_BBU:
287 		printf("      State of Health: %s\n",
288 		    stat.detail.bbu.is_SOH_good ? "good" : "bad");
289 		break;
290 	}
291 
292 	close(fd);
293 
294 	return (0);
295 }
296 MFI_COMMAND(show, battery, show_battery);
297 
298 void
299 print_ld(struct mfi_ld_info *info, int state_len)
300 {
301 	struct mfi_ld_params *params = &info->ld_config.params;
302 	const char *level;
303 	char size[6], stripe[5];
304 
305 	humanize_number(size, sizeof(size), info->size * 512,
306 	    "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
307 	format_stripe(stripe, sizeof(stripe),
308 	    info->ld_config.params.stripe_size);
309 	level = mfi_raid_level(params->primary_raid_level,
310 	    params->secondary_raid_level);
311 	if (state_len > 0)
312 		printf("(%6s) %-8s %6s %-*s", size, level, stripe, state_len,
313 		    mfi_ldstate(params->state));
314 	else
315 		printf("(%s) %s %s %s", size, level, stripe,
316 		    mfi_ldstate(params->state));
317 }
318 
319 void
320 print_pd(struct mfi_pd_info *info, int state_len)
321 {
322 	const char *s;
323 	char buf[256];
324 
325 	humanize_number(buf, 6, info->raw_size * 512, "",
326 	    HN_AUTOSCALE, HN_B | HN_NOSPACE |HN_DECIMAL);
327 	printf("(%6s) ", buf);
328 	if (info->state.ddf.v.pd_type.is_foreign) {
329 		sprintf(buf, "%s%s", mfi_pdstate(info->fw_state), foreign_state);
330 		s = buf;
331 	} else
332 		s = mfi_pdstate(info->fw_state);
333 	if (state_len > 0)
334 		printf("%-*s", state_len, s);
335 	else
336 		printf("%s",s);
337 	s = mfi_pd_inq_string(info);
338 	if (s != NULL)
339 		printf(" %s", s);
340 }
341 
342 static int
343 show_config(int ac, char **av __unused)
344 {
345 	struct mfi_config_data *config;
346 	struct mfi_array *ar;
347 	struct mfi_ld_config *ld;
348 	struct mfi_spare *sp;
349 	struct mfi_ld_info linfo;
350 	struct mfi_pd_info pinfo;
351 	uint16_t device_id;
352 	char *p;
353 	int error, fd, i, j;
354 
355 	if (ac != 1) {
356 		warnx("show config: extra arguments");
357 		return (EINVAL);
358 	}
359 
360 	fd = mfi_open(mfi_unit, O_RDONLY);
361 	if (fd < 0) {
362 		error = errno;
363 		warn("mfi_open");
364 		return (error);
365 	}
366 
367 	/* Get the config from the controller. */
368 	if (mfi_config_read(fd, &config) < 0) {
369 		error = errno;
370 		warn("Failed to get config");
371 		close(fd);
372 		return (error);
373 	}
374 
375 	/* Dump out the configuration. */
376 	printf("mfi%d Configuration: %d arrays, %d volumes, %d spares\n",
377 	    mfi_unit, config->array_count, config->log_drv_count,
378 	    config->spares_count);
379 	p = (char *)config->array;
380 
381 	for (i = 0; i < config->array_count; i++) {
382 		ar = (struct mfi_array *)p;
383 		printf("    array %u of %u drives:\n", ar->array_ref,
384 		    ar->num_drives);
385 		for (j = 0; j < ar->num_drives; j++) {
386 			device_id = ar->pd[j].ref.v.device_id;
387 			printf("        drive %s ", mfi_drive_name(NULL,
388 			    device_id,
389 			    MFI_DNAME_DEVICE_ID|MFI_DNAME_HONOR_OPTS));
390 			if (device_id != 0xffff) {
391 				if (mfi_pd_get_info(fd, device_id, &pinfo,
392 				    NULL) < 0)
393 					printf("%s",
394 					    mfi_pdstate(ar->pd[j].fw_state));
395 				else
396 					print_pd(&pinfo, -1);
397 			}
398 			printf("\n");
399 		}
400 		p += config->array_size;
401 	}
402 
403 	for (i = 0; i < config->log_drv_count; i++) {
404 		ld = (struct mfi_ld_config *)p;
405 		printf("    volume %s ",
406 		    mfi_volume_name(fd, ld->properties.ld.v.target_id));
407 		if (mfi_ld_get_info(fd, ld->properties.ld.v.target_id, &linfo,
408 		    NULL) < 0) {
409 			printf("%s %s",
410 			    mfi_raid_level(ld->params.primary_raid_level,
411 				ld->params.secondary_raid_level),
412 			    mfi_ldstate(ld->params.state));
413 		} else
414 			print_ld(&linfo, -1);
415 		if (ld->properties.name[0] != '\0')
416 			printf(" <%s>", ld->properties.name);
417 		printf(" spans:\n");
418 		for (j = 0; j < ld->params.span_depth; j++)
419 			printf("        array %u\n", ld->span[j].array_ref);
420 		p += config->log_drv_size;
421 	}
422 
423 	for (i = 0; i < config->spares_count; i++) {
424 		sp = (struct mfi_spare *)p;
425 		printf("    %s spare %s ",
426 		    sp->spare_type & MFI_SPARE_DEDICATED ? "dedicated" :
427 		    "global", mfi_drive_name(NULL, sp->ref.v.device_id,
428 		    MFI_DNAME_DEVICE_ID|MFI_DNAME_HONOR_OPTS));
429 		if (mfi_pd_get_info(fd, sp->ref.v.device_id, &pinfo, NULL) < 0)
430 			printf("%s", mfi_pdstate(MFI_PD_STATE_HOT_SPARE));
431 		else
432 			print_pd(&pinfo, -1);
433 		if (sp->spare_type & MFI_SPARE_DEDICATED) {
434 			printf(" backs:\n");
435 			for (j = 0; j < sp->array_count; j++)
436 				printf("        array %u\n", sp->array_ref[j]);
437 		} else
438 			printf("\n");
439 		p += config->spares_size;
440 	}
441 	free(config);
442 	close(fd);
443 
444 	return (0);
445 }
446 MFI_COMMAND(show, config, show_config);
447 
448 static int
449 show_volumes(int ac, char **av __unused)
450 {
451 	struct mfi_ld_list list;
452 	struct mfi_ld_info info;
453 	int error, fd;
454 	u_int i, len, state_len;
455 
456 	if (ac != 1) {
457 		warnx("show volumes: extra arguments");
458 		return (EINVAL);
459 	}
460 
461 	fd = mfi_open(mfi_unit, O_RDONLY);
462 	if (fd < 0) {
463 		error = errno;
464 		warn("mfi_open");
465 		return (error);
466 	}
467 
468 	/* Get the logical drive list from the controller. */
469 	if (mfi_ld_get_list(fd, &list, NULL) < 0) {
470 		error = errno;
471 		warn("Failed to get volume list");
472 		close(fd);
473 		return (error);
474 	}
475 
476 	/* List the volumes. */
477 	printf("mfi%d Volumes:\n", mfi_unit);
478 	state_len = strlen("State");
479 	for (i = 0; i < list.ld_count; i++) {
480 		len = strlen(mfi_ldstate(list.ld_list[i].state));
481 		if (len > state_len)
482 			state_len = len;
483 	}
484 	printf("  Id     Size    Level   Stripe ");
485 	len = state_len - strlen("State");
486 	for (i = 0; i < (len + 1) / 2; i++)
487 		printf(" ");
488 	printf("State");
489 	for (i = 0; i < len / 2; i++)
490 		printf(" ");
491 	printf("  Cache   Name\n");
492 	for (i = 0; i < list.ld_count; i++) {
493 		if (mfi_ld_get_info(fd, list.ld_list[i].ld.v.target_id, &info,
494 		    NULL) < 0) {
495 			error = errno;
496 			warn("Failed to get info for volume %d",
497 			    list.ld_list[i].ld.v.target_id);
498 			close(fd);
499 			return (error);
500 		}
501 		printf("%6s ",
502 		    mfi_volume_name(fd, list.ld_list[i].ld.v.target_id));
503 		print_ld(&info, state_len);
504 		switch (info.ld_config.properties.current_cache_policy &
505 		    (MR_LD_CACHE_ALLOW_WRITE_CACHE |
506 		    MR_LD_CACHE_ALLOW_READ_CACHE)) {
507 		case 0:
508 			printf(" Disabled");
509 			break;
510 		case MR_LD_CACHE_ALLOW_READ_CACHE:
511 			printf(" Reads   ");
512 			break;
513 		case MR_LD_CACHE_ALLOW_WRITE_CACHE:
514 			printf(" Writes  ");
515 			break;
516 		case MR_LD_CACHE_ALLOW_WRITE_CACHE |
517 		    MR_LD_CACHE_ALLOW_READ_CACHE:
518 			printf(" Enabled ");
519 			break;
520 		}
521 		if (info.ld_config.properties.name[0] != '\0')
522 			printf(" <%s>", info.ld_config.properties.name);
523 		printf("\n");
524 	}
525 	close(fd);
526 
527 	return (0);
528 }
529 MFI_COMMAND(show, volumes, show_volumes);
530 
531 static int
532 show_drives(int ac, char **av __unused)
533 {
534 	struct mfi_pd_list *list;
535 	struct mfi_pd_info info;
536 	u_int i, len, state_len;
537 	int error, fd;
538 
539 	if (ac != 1) {
540 		warnx("show drives: extra arguments");
541 		return (EINVAL);
542 	}
543 
544 	fd = mfi_open(mfi_unit, O_RDONLY);
545 	if (fd < 0) {
546 		error = errno;
547 		warn("mfi_open");
548 		return (error);
549 	}
550 
551 	list = NULL;
552 	if (mfi_pd_get_list(fd, &list, NULL) < 0) {
553 		error = errno;
554 		warn("Failed to get drive list");
555 		goto error;
556 	}
557 
558 	/* Walk the list of drives to determine width of state column. */
559 	state_len = 0;
560 	for (i = 0; i < list->count; i++) {
561 		if (list->addr[i].scsi_dev_type != 0)
562 			continue;
563 
564 		if (mfi_pd_get_info(fd, list->addr[i].device_id, &info,
565 		    NULL) < 0) {
566 			error = errno;
567 			warn("Failed to fetch info for drive %u",
568 			    list->addr[i].device_id);
569 			goto error;
570 		}
571 		len = strlen(mfi_pdstate(info.fw_state));
572 		if (info.state.ddf.v.pd_type.is_foreign)
573 			len += strlen(foreign_state);
574 		if (len > state_len)
575 			state_len = len;
576 	}
577 
578 	/* List the drives. */
579 	printf("mfi%d Physical Drives:\n", mfi_unit);
580 	for (i = 0; i < list->count; i++) {
581 
582 		/* Skip non-hard disks. */
583 		if (list->addr[i].scsi_dev_type != 0)
584 			continue;
585 
586 		/* Fetch details for this drive. */
587 		if (mfi_pd_get_info(fd, list->addr[i].device_id, &info,
588 		    NULL) < 0) {
589 			error = errno;
590 			warn("Failed to fetch info for drive %u",
591 			    list->addr[i].device_id);
592 			goto error;
593 		}
594 
595 		printf("%s ", mfi_drive_name(&info, list->addr[i].device_id,
596 		    MFI_DNAME_DEVICE_ID));
597 		print_pd(&info, state_len);
598 		printf(" %s", mfi_drive_name(&info, list->addr[i].device_id,
599 		    MFI_DNAME_ES));
600 		printf("\n");
601 	}
602 	error = 0;
603 error:
604 	free(list);
605 	close(fd);
606 
607 	return (error);
608 }
609 MFI_COMMAND(show, drives, show_drives);
610 
611 static int
612 show_firmware(int ac, char **av __unused)
613 {
614 	struct mfi_ctrl_info info;
615 	struct mfi_info_component header;
616 	int error, fd;
617 	u_int i;
618 
619 	if (ac != 1) {
620 		warnx("show firmware: extra arguments");
621 		return (EINVAL);
622 	}
623 
624 	fd = mfi_open(mfi_unit, O_RDONLY);
625 	if (fd < 0) {
626 		error = errno;
627 		warn("mfi_open");
628 		return (error);
629 	}
630 
631 	if (mfi_ctrl_get_info(fd, &info, NULL) < 0) {
632 		error = errno;
633 		warn("Failed to get controller info");
634 		close(fd);
635 		return (error);
636 	}
637 
638 	if (info.package_version[0] != '\0')
639 		printf("mfi%d Firmware Package Version: %s\n", mfi_unit,
640 		    info.package_version);
641 	printf("mfi%d Firmware Images:\n", mfi_unit);
642 	strcpy(header.name, "Name");
643 	strcpy(header.version, "Version");
644 	strcpy(header.build_date, "Date");
645 	strcpy(header.build_time, "Time");
646 	scan_firmware(&header);
647 	if (info.image_component_count > 8)
648 		info.image_component_count = 8;
649 	for (i = 0; i < info.image_component_count; i++)
650 		scan_firmware(&info.image_component[i]);
651 	if (info.pending_image_component_count > 8)
652 		info.pending_image_component_count = 8;
653 	for (i = 0; i < info.pending_image_component_count; i++)
654 		scan_firmware(&info.pending_image_component[i]);
655 	display_firmware(&header, "Status");
656 	for (i = 0; i < info.image_component_count; i++)
657 		display_firmware(&info.image_component[i], "active");
658 	for (i = 0; i < info.pending_image_component_count; i++)
659 		display_firmware(&info.pending_image_component[i], "pending");
660 
661 	close(fd);
662 
663 	return (0);
664 }
665 MFI_COMMAND(show, firmware, show_firmware);
666 
667 static int
668 show_progress(int ac, char **av __unused)
669 {
670 	struct mfi_ld_list llist;
671 	struct mfi_pd_list *plist;
672 	struct mfi_ld_info linfo;
673 	struct mfi_pd_info pinfo;
674 	int busy, error, fd;
675 	u_int i;
676 	uint16_t device_id;
677 	uint8_t target_id;
678 
679 	if (ac != 1) {
680 		warnx("show progress: extra arguments");
681 		return (EINVAL);
682 	}
683 
684 	fd = mfi_open(mfi_unit, O_RDONLY);
685 	if (fd < 0) {
686 		error = errno;
687 		warn("mfi_open");
688 		return (error);
689 	}
690 
691 	if (mfi_ld_get_list(fd, &llist, NULL) < 0) {
692 		error = errno;
693 		warn("Failed to get volume list");
694 		close(fd);
695 		return (error);
696 	}
697 	if (mfi_pd_get_list(fd, &plist, NULL) < 0) {
698 		error = errno;
699 		warn("Failed to get drive list");
700 		close(fd);
701 		return (error);
702 	}
703 
704 	busy = 0;
705 	for (i = 0; i < llist.ld_count; i++) {
706 		target_id = llist.ld_list[i].ld.v.target_id;
707 		if (mfi_ld_get_info(fd, target_id, &linfo, NULL) < 0) {
708 			error = errno;
709 			warn("Failed to get info for volume %s",
710 			    mfi_volume_name(fd, target_id));
711 			free(plist);
712 			close(fd);
713 			return (error);
714 		}
715 		if (linfo.progress.active & MFI_LD_PROGRESS_CC) {
716 			printf("volume %s ", mfi_volume_name(fd, target_id));
717 			mfi_display_progress("Consistency Check",
718 			    &linfo.progress.cc);
719 			busy = 1;
720 		}
721 		if (linfo.progress.active & MFI_LD_PROGRESS_BGI) {
722 			printf("volume %s ", mfi_volume_name(fd, target_id));
723 			mfi_display_progress("Background Init",
724 			    &linfo.progress.bgi);
725 			busy = 1;
726 		}
727 		if (linfo.progress.active & MFI_LD_PROGRESS_FGI) {
728 			printf("volume %s ", mfi_volume_name(fd, target_id));
729 			mfi_display_progress("Foreground Init",
730 			    &linfo.progress.fgi);
731 			busy = 1;
732 		}
733 		if (linfo.progress.active & MFI_LD_PROGRESS_RECON) {
734 			printf("volume %s ", mfi_volume_name(fd, target_id));
735 			mfi_display_progress("Reconstruction",
736 			    &linfo.progress.recon);
737 			busy = 1;
738 		}
739 	}
740 
741 	for (i = 0; i < plist->count; i++) {
742 		if (plist->addr[i].scsi_dev_type != 0)
743 			continue;
744 
745 		device_id = plist->addr[i].device_id;
746 		if (mfi_pd_get_info(fd, device_id, &pinfo, NULL) < 0) {
747 			error = errno;
748 			warn("Failed to fetch info for drive %u", device_id);
749 			free(plist);
750 			close(fd);
751 			return (error);
752 		}
753 
754 		if (pinfo.prog_info.active & MFI_PD_PROGRESS_REBUILD) {
755 			printf("drive %s ", mfi_drive_name(NULL, device_id,
756 			    MFI_DNAME_DEVICE_ID|MFI_DNAME_HONOR_OPTS));
757 			mfi_display_progress("Rebuild", &pinfo.prog_info.rbld);
758 			busy = 1;
759 		}
760 		if (pinfo.prog_info.active & MFI_PD_PROGRESS_PATROL) {
761 			printf("drive %s ", mfi_drive_name(NULL, device_id,
762 			    MFI_DNAME_DEVICE_ID|MFI_DNAME_HONOR_OPTS));
763 			mfi_display_progress("Patrol Read",
764 			    &pinfo.prog_info.patrol);
765 			busy = 1;
766 		}
767 		if (pinfo.prog_info.active & MFI_PD_PROGRESS_CLEAR) {
768 			printf("drive %s ", mfi_drive_name(NULL, device_id,
769 			    MFI_DNAME_DEVICE_ID|MFI_DNAME_HONOR_OPTS));
770 			mfi_display_progress("Clear", &pinfo.prog_info.clear);
771 			busy = 1;
772 		}
773 	}
774 
775 	free(plist);
776 	close(fd);
777 
778 	if (!busy)
779 		printf("No activity in progress for adapter mfi%d\n", mfi_unit);
780 
781 	return (0);
782 }
783 MFI_COMMAND(show, progress, show_progress);
784 
785 static int
786 show_foreign(int ac, char **av)
787 {
788 	return(display_format(ac, av, 0/*normal display*/, MFI_DCMD_CFG_FOREIGN_DISPLAY));
789 }
790 MFI_COMMAND(show, foreign, show_foreign);
791