sdhci - Implement ADMA2 transfer support. Keep SDMA support for now.
[dragonfly.git] / usr.sbin / mlxcontrol / command.c
blobb9b8f36c1f4010e4abd569d2a70cdc0956dd9989
1 /*-
2 * Copyright (c) 1999 Michael Smith
3 * All rights reserved.
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.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
26 * $FreeBSD: src/usr.sbin/mlxcontrol/command.c,v 1.3 2008/09/12 17:40:17 sepotvin Exp $
29 #include <fcntl.h>
30 #include <paths.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <err.h>
37 #include <dev/raid/mlx/mlxio.h>
38 #include <dev/raid/mlx/mlxreg.h>
40 #include "mlxcontrol.h"
42 static int cmd_status(int argc, char *argv[]);
43 static int cmd_rescan(int argc, char *argv[]);
44 static int cmd_detach(int argc, char *argv[]);
45 static int cmd_check(int argc, char *argv[]);
46 static int cmd_rebuild(int argc, char *argv[]);
47 #ifdef SUPPORT_PAUSE
48 static int cmd_pause(int argc, char *argv[]);
49 #endif
50 static int cmd_help(int argc, char *argv[]);
51 static int cmd_config(int argc, char *argv[]);
53 static void print_span(struct mlx_sys_drv_span *span, int arms);
54 static void print_sys_drive(struct conf_config *conf, int drvno);
55 static void print_phys_drive(struct conf_config *conf, int chn, int targ);
57 struct
59 const char *cmd;
60 int (*func)(int argc, char *argv[]);
61 const char *desc;
62 const char *text;
63 } commands[] = {
64 {"status", cmd_status,
65 "displays device status",
66 " status [-qv] [<drive>...]\n"
67 " Display status for <drive> or all drives if none is listed\n"
68 " -q Suppress output.\n"
69 " -v Display verbose information.\n"
70 " Returns 0 if all drives tested are online, 1 if one or more are\n"
71 " critical, and 2 if one or more are offline."},
72 {"rescan", cmd_rescan,
73 "scan for new system drives",
74 " rescan <controller> [<controller>...]\n"
75 " Rescan <controller> for system drives.\n"
76 " rescan -a\n"
77 " Rescan all controllers for system drives."},
78 {"detach", cmd_detach,
79 "detach system drives",
80 " detach <drive> [<drive>...]\n"
81 " Detaches <drive> from the controller.\n"
82 " detach -a <controller>\n"
83 " Detaches all drives on <controller>."},
84 {"check", cmd_check,
85 "consistency-check a system drive",
86 " check <drive>\n"
87 " Requests a check and rebuild of the parity information on <drive>.\n"
88 " Note that each controller can only check one system drive at a time."},
89 {"rebuild", cmd_rebuild,
90 "initiate a rebuild of a dead physical drive",
91 " rebuild <controller> <physdrive>\n"
92 " All system drives using space on the physical drive <physdrive>\n"
93 " are rebuilt, reconstructing all data on the drive.\n"
94 " Note that each controller can only perform one rebuild at a time."},
95 #ifdef SUPPORT_PAUSE
96 {"pause", cmd_pause,
97 "pauses controller channels",
98 " pause [-t <howlong>] [-d <delay>] <controller> [<channel>...]\n"
99 " Pauses SCSI I/O on <channel> and <controller>. If no channel is specified,\n"
100 " all channels are paused.\n"
101 " <howlong> How long (seconds) to pause for (default 30).\n"
102 " <delay> How long (seconds) to wait before pausing (default 30).\n"
103 " pause <controller> -c\n"
104 " Cancels any pending pause operation on <controller>."},
105 #endif
106 {"config", cmd_config,
107 "examine and update controller configuration",
108 " config <controller>\n"
109 " Print configuration for <controller>."},
110 {"help", cmd_help,
111 "give help on usage",
112 ""},
113 {NULL, NULL, NULL, NULL}
116 /********************************************************************************
117 * Command dispatch and global options parsing.
121 main(int argc, char *argv[])
123 int ch, i, oargc;
124 char **oargv;
126 oargc = argc;
127 oargv = argv;
128 while ((ch = getopt(argc, argv, "")) != -1)
129 switch(ch) {
130 default:
131 return(cmd_help(0, NULL));
134 argc -= optind;
135 argv += optind;
137 if (argc > 0)
138 for (i = 0; commands[i].cmd != NULL; i++)
139 if (!strcmp(argv[0], commands[i].cmd))
140 return(commands[i].func(argc, argv));
142 return(cmd_help(oargc, oargv));
145 /********************************************************************************
146 * Helptext output
148 static int
149 cmd_help(int argc, char *argv[])
151 int i;
153 if (argc > 1)
154 for (i = 0; commands[i].cmd != NULL; i++)
155 if (!strcmp(argv[1], commands[i].cmd)) {
156 fprintf(stderr, "%s\n", commands[i].text);
157 fflush(stderr);
158 return(0);
161 if (argv != NULL)
162 fprintf(stderr, "Unknown command '%s'.\n", argv[1]);
163 fprintf(stderr, "Valid commands are:\n");
164 for (i = 0; commands[i].cmd != NULL; i++)
165 fprintf(stderr, " %-20s %s\n", commands[i].cmd, commands[i].desc);
166 fflush(stderr);
167 return(0);
170 /********************************************************************************
171 * Status output
173 * status [-qv] [<device> ...]
174 * Prints status for <device>, or all if none listed.
176 * -q Suppresses output, command returns 0 if devices are OK, 1 if one or
177 * more devices are critical, 2 if one or more devices are offline.
179 static struct mlx_rebuild_status rs;
180 static int rs_ctrlr = -1;
181 static int status_result = 0;
183 /* XXX more verbosity! */
184 static void
185 status_print(int unit, void *arg)
187 int verbosity = *(int *)arg;
188 int fd, result, statvalid;
189 int ctrlr = -1, sysdrive = -1;
191 /* Find which controller and what system drive we are */
192 statvalid = 0;
193 if (mlxd_find_ctrlr(unit, &ctrlr, &sysdrive)) {
194 warnx("couldn't get controller/drive for %s", drivepath(unit));
195 } else {
196 /* If we don't have rebuild stats for this controller, get them */
197 if (rs_ctrlr == ctrlr) {
198 statvalid = 1;
199 } else {
200 if ((fd = open(ctrlrpath(ctrlr), 0)) < 0) {
201 warn("can't open %s", ctrlrpath(ctrlr));
202 } else {
203 if (ioctl(fd, MLX_REBUILDSTAT, &rs) < 0) {
204 warn("ioctl MLX_REBUILDSTAT");
205 } else {
206 rs_ctrlr = ctrlr;
207 statvalid = 1;
209 close(fd);
214 /* Get the device */
215 if ((fd = open(drivepath(unit), 0)) < 0) {
216 warn("can't open %s", drivepath(unit));
217 return;
220 /* Get its status */
221 if (ioctl(fd, MLXD_STATUS, &result) < 0) {
222 warn("ioctl MLXD_STATUS");
223 } else {
224 switch(result) {
225 case MLX_SYSD_ONLINE:
226 if (verbosity > 0)
227 printf("%s: online", drivename(unit));
228 break;
229 case MLX_SYSD_CRITICAL:
230 if (verbosity > 0)
231 printf("%s: critical", drivename(unit));
232 if (status_result < 1)
233 status_result = 1;
234 break;
235 case MLX_SYSD_OFFLINE:
236 if (verbosity > 0)
237 printf("%s: offline", drivename(unit));
238 if (status_result < 2)
239 status_result = 2;
240 break;
241 default:
242 if (verbosity > 0) {
243 printf("%s: unknown status 0x%x", drivename(unit), result);
246 if (verbosity > 0) {
247 /* rebuild/check in progress on this drive? */
248 if (statvalid && (rs_ctrlr == ctrlr) &&
249 (rs.rs_drive == sysdrive) && (rs.rs_code != MLX_REBUILDSTAT_IDLE)) {
250 switch(rs.rs_code) {
251 case MLX_REBUILDSTAT_REBUILDCHECK:
252 printf(" [consistency check");
253 break;
254 case MLX_REBUILDSTAT_ADDCAPACITY:
255 printf(" [add capacity");
256 break;
257 case MLX_REBUILDSTAT_ADDCAPACITYINIT:
258 printf(" [add capacity init");
259 break;
260 default:
261 printf(" [unknown operation");
263 printf(": %d/%d, %d%% complete]",
264 rs.rs_remaining, rs.rs_size,
265 ((rs.rs_size - rs.rs_remaining) / (rs.rs_size / 100)));
267 printf("\n");
270 close(fd);
273 /********************************************************************************
274 * Print details for the system drive (drvno) in a format that we will be
275 * able to parse later.
277 * drive?? <raidlevel> <writemode>
278 * span? 0x????????-0x???????? ????MB on <disk> [...]
279 * ...
281 static void
282 print_span(struct mlx_sys_drv_span *span, int arms)
284 int i;
286 printf("0x%08x-0x%08x %uMB on", span->sp_start_lba, span->sp_start_lba + span->sp_nblks, span->sp_nblks / 2048);
287 for (i = 0; i < arms; i++)
288 printf(" disk%02d%02d", span->sp_arm[i] >> 4, span->sp_arm[i] & 0x0f);
289 printf("\n");
292 static void
293 print_sys_drive(struct conf_config *conf, int drvno)
295 struct mlx_sys_drv *drv = &conf->cc_cfg.cc_sys_drives[drvno];
296 int i;
298 printf("drive%02d ", drvno);
299 switch(drv->sd_raidlevel & 0xf) {
300 case MLX_SYS_DRV_RAID0:
301 printf("RAID0");
302 break;
303 case MLX_SYS_DRV_RAID1:
304 printf("RAID1");
305 break;
306 case MLX_SYS_DRV_RAID3:
307 printf("RAID3");
308 break;
309 case MLX_SYS_DRV_RAID5:
310 printf("RAID5");
311 break;
312 case MLX_SYS_DRV_RAID6:
313 printf("RAID6");
314 break;
315 case MLX_SYS_DRV_JBOD:
316 printf("JBOD");
317 break;
318 default:
319 printf("RAID?");
321 printf(" write%s\n", drv->sd_raidlevel & MLX_SYS_DRV_WRITEBACK ? "back" : "through");
323 for (i = 0; i < drv->sd_valid_spans; i++) {
324 printf(" span%d ", i);
325 print_span(&drv->sd_span[i], drv->sd_valid_arms);
329 /********************************************************************************
330 * Print details for the physical drive at chn/targ in a format suitable for
331 * human consumption.
333 * <type>CCTT (<state>) "<vendor>/<model>"
334 * ????MB <features>
337 static void
338 print_phys_drive(struct conf_config *conf, int chn, int targ)
340 struct mlx_phys_drv *drv = &conf->cc_cfg.cc_phys_drives[chn * 16 + targ];
342 /* if the drive isn't present, don't print it */
343 if (!(drv->pd_flags1 & MLX_PHYS_DRV_PRESENT))
344 return;
346 mlx_print_phys_drv(drv, chn, targ, "# ", 1);
349 static struct
351 int hwid;
352 const char *name;
353 } mlx_controller_names[] = {
354 {0x01, "960P/PD"},
355 {0x02, "960PL"},
356 {0x10, "960PG"},
357 {0x11, "960PJ"},
358 {0x12, "960PR"},
359 {0x13, "960PT"},
360 {0x14, "960PTL0"},
361 {0x15, "960PRL"},
362 {0x16, "960PTL1"},
363 {0x20, "1100PVX"},
364 {-1, NULL}
367 static void
368 controller_print(int unit, void *arg)
370 struct mlx_enquiry2 enq;
371 struct mlx_phys_drv pd;
372 int verbosity = *(int *)arg;
373 static char buf[80];
374 const char *model;
375 int i, channel, target;
377 if (verbosity == 0)
378 return;
380 /* fetch and print controller data */
381 if (mlx_enquiry(unit, &enq)) {
382 printf("mlx%d: error submitting ENQUIRY2\n", unit);
383 } else {
385 for (i = 0, model = NULL; mlx_controller_names[i].name != NULL; i++) {
386 if ((int)(enq.me_hardware_id & 0xff) == mlx_controller_names[i].hwid) {
387 model = mlx_controller_names[i].name;
388 break;
391 if (model == NULL) {
392 sprintf(buf, " model 0x%x", enq.me_hardware_id & 0xff);
393 model = buf;
396 printf("mlx%d: DAC%s, %d channel%s, firmware %d.%02d-%c-%02d, %dMB RAM\n",
397 unit, model,
398 enq.me_actual_channels,
399 enq.me_actual_channels > 1 ? "s" : "",
400 enq.me_firmware_id & 0xff,
401 (enq.me_firmware_id >> 8) & 0xff,
402 (enq.me_firmware_id >> 16),
403 (enq.me_firmware_id >> 24) & 0xff,
404 enq.me_mem_size / (1024 * 1024));
406 if (verbosity > 1) {
407 printf(" Hardware ID 0x%08x\n", enq.me_hardware_id);
408 printf(" Firmware ID 0x%08x\n", enq.me_firmware_id);
409 printf(" Configured/Actual channels %d/%d\n", enq.me_configured_channels,
410 enq.me_actual_channels);
411 printf(" Max Targets %d\n", enq.me_max_targets);
412 printf(" Max Tags %d\n", enq.me_max_tags);
413 printf(" Max System Drives %d\n", enq.me_max_sys_drives);
414 printf(" Max Arms %d\n", enq.me_max_arms);
415 printf(" Max Spans %d\n", enq.me_max_spans);
416 printf(" DRAM/cache/flash/NVRAM size %d/%d/%d/%d\n", enq.me_mem_size,
417 enq.me_cache_size, enq.me_flash_size, enq.me_nvram_size);
418 printf(" DRAM type %d\n", enq.me_mem_type);
419 printf(" Clock Speed %dns\n", enq.me_clock_speed);
420 printf(" Hardware Speed %dns\n", enq.me_hardware_speed);
421 printf(" Max Commands %d\n", enq.me_max_commands);
422 printf(" Max SG Entries %d\n", enq.me_max_sg);
423 printf(" Max DP %d\n", enq.me_max_dp);
424 printf(" Max IOD %d\n", enq.me_max_iod);
425 printf(" Max Comb %d\n", enq.me_max_comb);
426 printf(" Latency %ds\n", enq.me_latency);
427 printf(" SCSI Timeout %ds\n", enq.me_scsi_timeout);
428 printf(" Min Free Lines %d\n", enq.me_min_freelines);
429 printf(" Rate Constant %d\n", enq.me_rate_const);
430 printf(" MAXBLK %d\n", enq.me_maxblk);
431 printf(" Blocking Factor %d sectors\n", enq.me_blocking_factor);
432 printf(" Cache Line Size %d blocks\n", enq.me_cacheline);
433 printf(" SCSI Capability %s%dMHz, %d bit\n",
434 enq.me_scsi_cap & (1<<4) ? "differential " : "",
435 (1 << ((enq.me_scsi_cap >> 2) & 3)) * 10,
436 8 << (enq.me_scsi_cap & 0x3));
437 printf(" Firmware Build Number %d\n", enq.me_firmware_build);
438 printf(" Fault Management Type %d\n", enq.me_fault_mgmt_type);
439 #if 0
440 printf(" Features %b\n", enq.me_firmware_features,
441 "\20\4Background Init\3Read Ahead\2MORE\1Cluster\n");
442 #endif
445 /* fetch and print physical drive data */
446 for (channel = 0; channel < enq.me_configured_channels; channel++) {
447 for (target = 0; target < enq.me_max_targets; target++) {
448 if ((mlx_get_device_state(unit, channel, target, &pd) == 0) &&
449 (pd.pd_flags1 & MLX_PHYS_DRV_PRESENT)) {
450 mlx_print_phys_drv(&pd, channel, target, " ", verbosity - 1);
451 if (verbosity > 1) {
452 /* XXX print device statistics? */
460 static int
461 cmd_status(int argc, char *argv[])
463 int ch, verbosity = 1, i, unit;
465 optreset = 1;
466 optind = 1;
467 while ((ch = getopt(argc, argv, "qv")) != -1)
468 switch(ch) {
469 case 'q':
470 verbosity = 0;
471 break;
472 case 'v':
473 verbosity = 2;
474 break;
475 default:
476 return(cmd_help(argc, argv));
478 argc -= optind;
479 argv += optind;
481 if (argc < 1) {
482 mlx_foreach(controller_print, &verbosity);
483 mlxd_foreach(status_print, &verbosity);
484 } else {
485 for (i = 0; i < argc; i++) {
486 if ((unit = driveunit(argv[i])) == -1) {
487 warnx("'%s' is not a valid drive", argv[i]);
488 } else {
489 status_print(unit, &verbosity);
493 return(status_result);
496 /********************************************************************************
497 * Recscan for system drives on one or more controllers.
499 * rescan <controller> [<controller>...]
500 * rescan -a
502 static void
503 rescan_ctrlr(int unit, void *junk __unused)
505 int fd;
507 /* Get the device */
508 if ((fd = open(ctrlrpath(unit), 0)) < 0) {
509 warn("can't open %s", ctrlrpath(unit));
510 return;
513 if (ioctl(fd, MLX_RESCAN_DRIVES) < 0)
514 warn("can't rescan %s", ctrlrname(unit));
515 close(fd);
518 static int
519 cmd_rescan(int argc, char *argv[])
521 int all = 0, i, ch, unit;
523 optreset = 1;
524 optind = 1;
525 while ((ch = getopt(argc, argv, "a")) != -1)
526 switch(ch) {
527 case 'a':
528 all = 1;
529 break;
530 default:
531 return(cmd_help(argc, argv));
533 argc -= optind;
534 argv += optind;
536 if (all) {
537 mlx_foreach(rescan_ctrlr, NULL);
538 } else {
539 for (i = 0; i < argc; i++) {
540 if ((unit = ctrlrunit(argv[i])) == -1) {
541 warnx("'%s' is not a valid controller", argv[i]);
542 } else {
543 rescan_ctrlr(unit, NULL);
547 return(0);
550 /********************************************************************************
551 * Detach one or more system drives from a controller.
553 * detach <drive> [<drive>...]
554 * Detach <drive>.
556 * detach -a <controller> [<controller>...]
557 * Detach all drives on <controller>.
560 static void
561 detach_drive(int unit, void *arg __unused)
563 int fd;
565 /* Get the device */
566 if ((fd = open(ctrlrpath(unit), 0)) < 0) {
567 warn("can't open %s", ctrlrpath(unit));
568 return;
571 if (ioctl(fd, MLX_DETACH_DRIVE, &unit) < 0)
572 warn("can't detach %s", drivename(unit));
573 close(fd);
576 static int
577 cmd_detach(int argc, char *argv[])
579 struct mlxd_foreach_action ma;
580 int all = 0, i, ch, unit;
582 optreset = 1;
583 optind = 1;
584 while ((ch = getopt(argc, argv, "a")) != -1)
585 switch(ch) {
586 case 'a':
587 all = 1;
588 break;
589 default:
590 return(cmd_help(argc, argv));
592 argc -= optind;
593 argv += optind;
595 if (all) {
596 ma.func = detach_drive;
597 ma.arg = &unit;
598 for (i = 0; i < argc; i++) {
599 if ((unit = ctrlrunit(argv[i])) == -1) {
600 warnx("'%s' is not a valid controller", argv[i]);
601 } else {
602 mlxd_foreach_ctrlr(unit, &ma);
605 } else {
606 for (i = 0; i < argc; i++) {
607 if ((unit = driveunit(argv[i])) == -1) {
608 warnx("'%s' is not a valid drive", argv[i]);
609 } else {
610 /* run across all controllers to find this drive */
611 mlx_foreach(detach_drive, &unit);
615 return(0);
618 /********************************************************************************
619 * Initiate a consistency check on a system drive.
621 * check [<drive>]
622 * Start a check of <drive>
625 static int
626 cmd_check(int argc, char *argv[])
628 int unit, fd, result;
630 if (argc != 2)
631 return(cmd_help(argc, argv));
633 if ((unit = driveunit(argv[1])) == -1) {
634 warnx("'%s' is not a valid drive", argv[1]);
635 } else {
637 /* Get the device */
638 if ((fd = open(drivepath(unit), 0)) < 0) {
639 warn("can't open %s", drivepath(unit));
640 } else {
641 /* Try to start the check */
642 if ((ioctl(fd, MLXD_CHECKASYNC, &result)) < 0) {
643 switch(result) {
644 case 0x0002:
645 warnx("one or more of the SCSI disks on which the drive '%s' depends is DEAD", argv[1]);
646 break;
647 case 0x0105:
648 warnx("drive %s is invalid, or not a drive which can be checked", argv[1]);
649 break;
650 case 0x0106:
651 warnx("drive rebuild or consistency check is already in progress on this controller");
652 break;
653 default:
654 warn("ioctl MLXD_CHECKASYNC");
659 return(0);
662 /********************************************************************************
663 * Initiate a physical drive rebuild
665 * rebuild <controller> <channel>:<target>
666 * Start a rebuild of <controller>:<channel>:<target>
669 static int
670 cmd_rebuild(int argc, char *argv[])
672 struct mlx_rebuild_request rb;
673 int unit, fd;
675 if (argc != 3)
676 return(cmd_help(argc, argv));
678 /* parse arguments */
679 if ((unit = ctrlrunit(argv[1])) == -1) {
680 warnx("'%s' is not a valid controller", argv[1]);
681 return(1);
683 /* try diskXXXX and unknownXXXX as we report the latter for a dead drive ... */
684 if ((sscanf(argv[2], "disk%2d%2d", &rb.rr_channel, &rb.rr_target) != 2) &&
685 (sscanf(argv[2], "unknown%2d%2d", &rb.rr_channel, &rb.rr_target) != 2)) {
686 warnx("'%s' is not a valid physical drive", argv[2]);
687 return(1);
689 /* get the device */
690 if ((fd = open(ctrlrpath(unit), 0)) < 0) {
691 warn("can't open %s", ctrlrpath(unit));
692 return(1);
694 /* try to start the rebuild */
695 if ((ioctl(fd, MLX_REBUILDASYNC, &rb)) < 0) {
696 switch(rb.rr_status) {
697 case 0x0002:
698 warnx("the drive at %d:%d is already ONLINE", rb.rr_channel, rb.rr_target);
699 break;
700 case 0x0004:
701 warnx("drive failed during rebuild");
702 break;
703 case 0x0105:
704 warnx("there is no drive at channel %d, target %d", rb.rr_channel, rb.rr_target);
705 break;
706 case 0x0106:
707 warnx("drive rebuild or consistency check is already in progress on this controller");
708 break;
709 default:
710 warn("ioctl MLXD_REBUILDASYNC");
713 return(0);
716 /********************************************************************************
717 * Get the configuration from the selected controller.
719 * config <controller>
720 * Print the configuration for <controller>
722 * XXX update to support adding/deleting drives.
726 cmd_config(int argc __unused, char *argv[] __unused)
728 struct conf_config conf;
729 int unit = 0; /* XXX */
730 int i, j;
732 bzero(&conf.cc_cfg, sizeof(conf.cc_cfg));
733 if (mlx_read_configuration(unit, &conf.cc_cfg)) {
734 printf("mlx%d: error submitting READ CONFIGURATION\n", unit);
735 } else {
737 printf("# Controller <INSERT DETAILS HERE>\n");
738 printf("#\n# Physical devices connected:\n");
739 for (i = 0; i < 5; i++)
740 for (j = 0; j < 16; j++)
741 print_phys_drive(&conf, i, j);
742 printf("#\n# System Drives defined:\n");
744 for (i = 0; i < conf.cc_cfg.cc_num_sys_drives; i++)
745 print_sys_drive(&conf, i);
747 return(0);
750 #ifdef SUPPORT_PAUSE
751 /********************************************************************************
752 * Pause one or more channels on a controller
754 * pause [-d <delay>] [-t <time>] <controller> [<channel>...]
755 * Pauses <channel> (or all channels) for <time> seconds after a
756 * delay of <delay> seconds.
757 * pause <controller> -c
758 * Cancels pending pause
760 static int
761 cmd_pause(int argc, char *argv[])
763 struct mlx_pause mp;
764 int unit, i, ch, fd, cancel = 0;
765 char *cp;
766 int oargc = argc;
767 char **oargv = argv;
769 mp.mp_which = 0;
770 mp.mp_when = 30;
771 mp.mp_howlong = 30;
772 optreset = 1;
773 optind = 1;
774 while ((ch = getopt(argc, argv, "cd:t:")) != -1)
775 switch(ch) {
776 case 'c':
777 cancel = 1;
778 break;
779 case 'd':
780 mp.mp_when = strtol(optarg, &cp, 0);
781 if (*cp != 0)
782 return(cmd_help(argc, argv));
783 break;
784 case 't':
785 mp.mp_howlong = strtol(optarg, &cp, 0);
786 if (*cp != 0)
787 return(cmd_help(argc, argv));
788 break;
789 default:
790 return(cmd_help(argc, argv));
792 argc -= optind;
793 argv += optind;
795 /* get controller unit number that we're working on */
796 if ((argc < 1) || ((unit = ctrlrunit(argv[0])) == -1))
797 return(cmd_help(oargc, oargv));
799 /* Get the device */
800 if ((fd = open(ctrlrpath(unit), 0)) < 0) {
801 warn("can't open %s", ctrlrpath(unit));
802 return(1);
805 if (argc == 1) {
806 /* controller-wide pause/cancel */
807 mp.mp_which = cancel ? MLX_PAUSE_CANCEL : MLX_PAUSE_ALL;
808 } else {
809 for (i = 1; i < argc; i++) {
810 ch = strtol(argv[i], &cp, 0);
811 if (*cp != 0) {
812 warnx("bad channel number '%s'", argv[i]);
813 continue;
814 } else {
815 mp.mp_which |= (1 << ch);
819 if ((ioctl(fd, MLX_PAUSE_CHANNEL, &mp)) < 0)
820 warn("couldn't %s %s", cancel ? "cancel pause on" : "pause", ctrlrname(unit));
821 close(fd);
822 return(0);
824 #endif /* SUPPORT_PAUSE */