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