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