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