]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - usr.sbin/mlxcontrol/command.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / usr.sbin / mlxcontrol / command.c
1 /*-
2  * Copyright (c) 1999 Michael Smith
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  *
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.
25  *
26  *      $FreeBSD$
27  */
28
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>
36
37 #include <dev/mlx/mlxio.h>
38 #include <dev/mlx/mlxreg.h>
39
40 #include "mlxcontrol.h"
41
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
52 extern int      cmd_config(int argc, char *argv[]);
53
54
55 struct 
56 {
57     char        *cmd;
58     int         (*func)(int argc, char *argv[]);
59     char        *desc;
60     char        *text;
61 } commands[] = {
62     {"status",  cmd_status, 
63      "displays device status",
64      "  status [-qv] [<drive>...]\n"
65      "      Display status for <drive> or all drives if none is listed\n"
66      "  -q    Suppress output.\n"
67      "  -v    Display verbose information.\n"
68      "  Returns 0 if all drives tested are online, 1 if one or more are\n"
69      "  critical, and 2 if one or more are offline."},
70     {"rescan",  cmd_rescan, 
71      "scan for new system drives",
72      "  rescan <controller> [<controller>...]\n"
73      "      Rescan <controller> for system drives.\n"
74      "  rescan -a\n"
75      "      Rescan all controllers for system drives."},
76     {"detach",  cmd_detach,
77      "detach system drives",
78      "  detach <drive> [<drive>...]\n"
79      "      Detaches <drive> from the controller.\n"
80      "  detach -a <controller>\n"
81      "      Detaches all drives on <controller>."},
82     {"check",   cmd_check,
83      "consistency-check a system drive",
84      "  check <drive>\n"
85      "      Requests a check and rebuild of the parity information on <drive>.\n"
86      "      Note that each controller can only check one system drive at a time."},
87     {"rebuild", cmd_rebuild,
88      "initiate a rebuild of a dead physical drive",
89      "  rebuild <controller> <physdrive>\n"
90      "      All system drives using space on the physical drive <physdrive>\n"
91      "      are rebuilt, reconstructing all data on the drive.\n"
92      "      Note that each controller can only perform one rebuild at a time."},
93 #ifdef SUPPORT_PAUSE
94     {"pause",   cmd_pause,
95      "pauses controller channels",
96      "  pause [-t <howlong>] [-d <delay>] <controller> [<channel>...]\n"
97      "      Pauses SCSI I/O on <channel> and <controller>.  If no channel is specified,\n"
98      "      all channels are paused.\n"
99      "  <howlong>   How long (seconds) to pause for (default 30).\n"
100      "  <delay>     How long (seconds) to wait before pausing (default 30).\n"
101      "  pause <controller> -c\n"
102      "      Cancels any pending pause operation on <controller>."},
103 #endif
104     {"config",  cmd_config,
105      "examine and update controller configuration",
106      "  config <controller>\n"
107      "      Print configuration for <controller>."},
108     {"help",    cmd_help,   
109      "give help on usage",
110      ""},
111     {NULL, NULL, NULL, NULL}
112 };
113
114 /********************************************************************************
115  * Command dispatch and global options parsing.
116  */
117
118 int
119 main(int argc, char *argv[])
120 {
121     int         ch, i, oargc;
122     char        **oargv;
123     
124     oargc = argc;
125     oargv = argv;
126     while ((ch = getopt(argc, argv, "")) != -1)
127         switch(ch) {
128         default:
129             return(cmd_help(0, NULL));
130         }
131
132     argc -= optind;
133     argv += optind;
134     
135     if (argc > 0)
136         for (i = 0; commands[i].cmd != NULL; i++)
137             if (!strcmp(argv[0], commands[i].cmd))
138                 return(commands[i].func(argc, argv));
139
140     return(cmd_help(oargc, oargv));
141 }
142
143 /********************************************************************************
144  * Helptext output
145  */
146 static int
147 cmd_help(int argc, char *argv[]) 
148 {
149     int         i;
150     
151     if (argc > 1)
152         for (i = 0; commands[i].cmd != NULL; i++)
153             if (!strcmp(argv[1], commands[i].cmd)) {
154                 fprintf(stderr, "%s\n", commands[i].text);
155                 fflush(stderr);
156                 return(0);
157             }
158
159     if (argv != NULL)
160         fprintf(stderr, "Unknown command '%s'.\n", argv[1]);    
161     fprintf(stderr, "Valid commands are:\n");
162     for (i = 0; commands[i].cmd != NULL; i++)
163         fprintf(stderr, "  %-20s %s\n", commands[i].cmd, commands[i].desc);
164     fflush(stderr);
165     return(0);
166 }
167
168 /********************************************************************************
169  * Status output
170  *
171  * status [-qv] [<device> ...]
172  *              Prints status for <device>, or all if none listed.
173  *
174  * -q   Suppresses output, command returns 0 if devices are OK, 1 if one or
175  *      more devices are critical, 2 if one or more devices are offline.
176  */
177 static struct mlx_rebuild_status        rs;
178 static int                              rs_ctrlr = -1;
179 static int                              status_result = 0;
180
181 /* XXX more verbosity! */
182 static void
183 status_print(int unit, void *arg)
184 {
185     int                         verbosity = *(int *)arg;
186     int                         fd, result, ctrlr, sysdrive, statvalid;
187     
188     /* Find which controller and what system drive we are */
189     statvalid = 0;
190     if (mlxd_find_ctrlr(unit, &ctrlr, &sysdrive)) {
191         warnx("couldn't get controller/drive for %s", drivepath(unit));
192     } else {
193         /* If we don't have rebuild stats for this controller, get them */
194         if (rs_ctrlr == ctrlr) {
195             statvalid = 1;
196         } else {
197             if ((fd = open(ctrlrpath(ctrlr), 0)) < 0) {
198                 warn("can't open %s", ctrlrpath(ctrlr));
199             } else {
200                 if (ioctl(fd, MLX_REBUILDSTAT, &rs) < 0) {
201                     warn("ioctl MLX_REBUILDSTAT");
202                 } else {
203                     rs_ctrlr = ctrlr;
204                     statvalid = 1;
205                 }
206                 close(fd);
207             }
208         }
209     }
210
211     /* Get the device */
212     if ((fd = open(drivepath(unit), 0)) < 0) {
213         warn("can't open %s", drivepath(unit));
214         return;
215     }
216
217     /* Get its status */
218     if (ioctl(fd, MLXD_STATUS, &result) < 0) {
219         warn("ioctl MLXD_STATUS");
220     } else {
221         switch(result) {
222         case MLX_SYSD_ONLINE:
223             if (verbosity > 0)
224                 printf("%s: online", drivename(unit));
225             break;
226         case MLX_SYSD_CRITICAL:
227             if (verbosity > 0)
228                 printf("%s: critical", drivename(unit));
229             if (status_result < 1)
230                 status_result = 1;
231             break;
232         case MLX_SYSD_OFFLINE:
233             if (verbosity > 0)
234                 printf("%s: offline", drivename(unit));
235             if (status_result < 2)
236                 status_result = 2;
237             break;
238         default:
239             if (verbosity > 0) {
240                 printf("%s: unknown status 0x%x", drivename(unit), result);
241             }
242         }
243         if (verbosity > 0) {
244             /* rebuild/check in progress on this drive? */
245             if (statvalid && (rs_ctrlr == ctrlr) && 
246                 (rs.rs_drive == sysdrive) && (rs.rs_code != MLX_REBUILDSTAT_IDLE)) {
247                 switch(rs.rs_code) {
248                 case MLX_REBUILDSTAT_REBUILDCHECK:
249                     printf(" [consistency check");
250                     break;
251                 case MLX_REBUILDSTAT_ADDCAPACITY:
252                     printf(" [add capacity");
253                     break;
254                 case MLX_REBUILDSTAT_ADDCAPACITYINIT:
255                     printf(" [add capacity init");
256                     break;
257                 default:
258                     printf(" [unknown operation");
259                 }
260                 printf(": %d/%d, %d%% complete]",
261                        rs.rs_remaining, rs.rs_size, 
262                        ((rs.rs_size - rs.rs_remaining) / (rs.rs_size / 100)));
263             }
264             printf("\n");
265         }
266     }
267     close(fd);
268 }
269
270 static struct 
271 {
272     int         hwid;
273     char        *name;
274 } mlx_controller_names[] = {
275     {0x01,      "960P/PD"},
276     {0x02,      "960PL"},
277     {0x10,      "960PG"},
278     {0x11,      "960PJ"},
279     {0x12,      "960PR"},
280     {0x13,      "960PT"},
281     {0x14,      "960PTL0"},
282     {0x15,      "960PRL"},
283     {0x16,      "960PTL1"},
284     {0x20,      "1100PVX"},
285     {-1, NULL}
286 };
287
288 static void
289 controller_print(int unit, void *arg)
290 {
291     struct mlx_enquiry2 enq;
292     struct mlx_phys_drv pd;
293     int                 verbosity = *(int *)arg;
294     static char         buf[80];
295     char                *model;
296     int                 i, channel, target;
297
298     if (verbosity == 0)
299         return;
300
301     /* fetch and print controller data */
302     if (mlx_enquiry(unit, &enq)) {
303         printf("mlx%d: error submitting ENQUIRY2\n", unit);
304     } else {
305         
306         for (i = 0, model = NULL; mlx_controller_names[i].name != NULL; i++) {
307             if ((enq.me_hardware_id & 0xff) == mlx_controller_names[i].hwid) {
308                 model = mlx_controller_names[i].name;
309                 break;
310             }
311         }
312         if (model == NULL) {
313             sprintf(buf, " model 0x%x", enq.me_hardware_id & 0xff);
314             model = buf;
315         }
316
317         printf("mlx%d: DAC%s, %d channel%s, firmware %d.%02d-%c-%02d, %dMB RAM\n",
318                unit, model, 
319                enq.me_actual_channels, 
320                enq.me_actual_channels > 1 ? "s" : "",
321                enq.me_firmware_id & 0xff,
322                (enq.me_firmware_id >> 8) & 0xff,
323                (enq.me_firmware_id >> 16),
324                (enq.me_firmware_id >> 24) & 0xff,
325                enq.me_mem_size / (1024 * 1024));
326
327         if (verbosity > 1) {
328             printf("  Hardware ID                 0x%08x\n", enq.me_hardware_id);
329             printf("  Firmware ID                 0x%08x\n", enq.me_firmware_id);
330             printf("  Configured/Actual channels  %d/%d\n", enq.me_configured_channels,
331                       enq.me_actual_channels);
332             printf("  Max Targets                 %d\n", enq.me_max_targets);
333             printf("  Max Tags                    %d\n", enq.me_max_tags);
334             printf("  Max System Drives           %d\n", enq.me_max_sys_drives);
335             printf("  Max Arms                    %d\n", enq.me_max_arms);
336             printf("  Max Spans                   %d\n", enq.me_max_spans);
337             printf("  DRAM/cache/flash/NVRAM size %d/%d/%d/%d\n", enq.me_mem_size,
338                       enq.me_cache_size, enq.me_flash_size, enq.me_nvram_size);
339             printf("  DRAM type                   %d\n", enq.me_mem_type);
340             printf("  Clock Speed                 %dns\n", enq.me_clock_speed);
341             printf("  Hardware Speed              %dns\n", enq.me_hardware_speed);
342             printf("  Max Commands                %d\n", enq.me_max_commands);
343             printf("  Max SG Entries              %d\n", enq.me_max_sg);
344             printf("  Max DP                      %d\n", enq.me_max_dp);
345             printf("  Max IOD                     %d\n", enq.me_max_iod);
346             printf("  Max Comb                    %d\n", enq.me_max_comb);
347             printf("  Latency                     %ds\n", enq.me_latency);
348             printf("  SCSI Timeout                %ds\n", enq.me_scsi_timeout);
349             printf("  Min Free Lines              %d\n", enq.me_min_freelines);
350             printf("  Rate Constant               %d\n", enq.me_rate_const);
351             printf("  MAXBLK                      %d\n", enq.me_maxblk);
352             printf("  Blocking Factor             %d sectors\n", enq.me_blocking_factor);
353             printf("  Cache Line Size             %d blocks\n", enq.me_cacheline);
354             printf("  SCSI Capability             %s%dMHz, %d bit\n", 
355                       enq.me_scsi_cap & (1<<4) ? "differential " : "",
356                       (1 << ((enq.me_scsi_cap >> 2) & 3)) * 10,
357                       8 << (enq.me_scsi_cap & 0x3));
358             printf("  Firmware Build Number       %d\n", enq.me_firmware_build);
359             printf("  Fault Management Type       %d\n", enq.me_fault_mgmt_type);
360 #if 0
361             printf("  Features                    %b\n", enq.me_firmware_features,
362                       "\20\4Background Init\3Read Ahead\2MORE\1Cluster\n");
363 #endif
364         }
365
366         /* fetch and print physical drive data */
367         for (channel = 0; channel < enq.me_configured_channels; channel++) {
368             for (target = 0; target < enq.me_max_targets; target++) {
369                 if ((mlx_get_device_state(unit, channel, target, &pd) == 0) &&
370                     (pd.pd_flags1 & MLX_PHYS_DRV_PRESENT)) {
371                     mlx_print_phys_drv(&pd, channel, target, "  ", verbosity - 1);
372                     if (verbosity > 1) {
373                         /* XXX print device statistics? */
374                     }
375                 }
376             }
377         }
378     }
379 }
380
381 static int
382 cmd_status(int argc, char *argv[])
383 {
384     int         ch, verbosity = 1, i, unit;
385
386     optreset = 1;
387     optind = 1;
388     while ((ch = getopt(argc, argv, "qv")) != -1)
389         switch(ch) {
390         case 'q':
391             verbosity = 0;
392             break;
393         case 'v':
394             verbosity = 2;
395             break;
396         default:
397             return(cmd_help(argc, argv));
398         }
399     argc -= optind;
400     argv += optind;
401
402     if (argc < 1) {
403         mlx_foreach(controller_print, &verbosity);
404         mlxd_foreach(status_print, &verbosity);
405     } else {
406         for (i = 0; i < argc; i++) {
407             if ((unit = driveunit(argv[i])) == -1) {
408                 warnx("'%s' is not a valid drive", argv[i]);
409             } else {
410                 status_print(unit, &verbosity);
411             }
412         }
413     }
414     return(status_result);
415 }
416
417 /********************************************************************************
418  * Recscan for system drives on one or more controllers.
419  *
420  * rescan <controller> [<controller>...]
421  * rescan -a
422  */
423 static void
424 rescan_ctrlr(int unit, void *junk)
425 {
426     int         fd;
427     
428     /* Get the device */
429     if ((fd = open(ctrlrpath(unit), 0)) < 0) {
430         warn("can't open %s", ctrlrpath(unit));
431         return;
432     }
433
434     if (ioctl(fd, MLX_RESCAN_DRIVES) < 0)
435         warn("can't rescan %s", ctrlrname(unit));
436     close(fd);
437 }
438
439 static int
440 cmd_rescan(int argc, char *argv[]) 
441 {
442     int         all = 0, i, ch, unit;
443
444     optreset = 1;
445     optind = 1;
446     while ((ch = getopt(argc, argv, "a")) != -1)
447         switch(ch) {
448         case 'a':
449             all = 1;
450             break;
451         default:
452             return(cmd_help(argc, argv));
453         }
454     argc -= optind;
455     argv += optind;
456
457     if (all) {
458         mlx_foreach(rescan_ctrlr, NULL);
459     } else {
460         for (i = 0; i < argc; i++) {
461             if ((unit = ctrlrunit(argv[i])) == -1) {
462                 warnx("'%s' is not a valid controller", argv[i]);
463             } else {
464                 rescan_ctrlr(unit, NULL);
465             }
466         }
467     }
468     return(0);
469 }
470
471 /********************************************************************************
472  * Detach one or more system drives from a controller.
473  *
474  * detach <drive> [<drive>...]
475  *              Detach <drive>.
476  *
477  * detach -a <controller> [<controller>...]
478  *              Detach all drives on <controller>.
479  *
480  */
481 static void
482 detach_drive(int unit, void *arg)
483 {
484     int         fd;
485     
486     /* Get the device */
487     if ((fd = open(ctrlrpath(unit), 0)) < 0) {
488         warn("can't open %s", ctrlrpath(unit));
489         return;
490     }
491
492     if (ioctl(fd, MLX_DETACH_DRIVE, &unit) < 0)
493         warn("can't detach %s", drivename(unit));
494     close(fd);
495 }
496
497 static int
498 cmd_detach(int argc, char *argv[]) 
499 {
500     struct mlxd_foreach_action  ma;
501     int                         all = 0, i, ch, unit;
502
503     optreset = 1;
504     optind = 1;
505     while ((ch = getopt(argc, argv, "a")) != -1)
506         switch(ch) {
507         case 'a':
508             all = 1;
509             break;
510         default:
511             return(cmd_help(argc, argv));
512         }
513     argc -= optind;
514     argv += optind;
515
516     if (all) {
517         ma.func = detach_drive;
518         ma.arg = &unit;
519         for (i = 0; i < argc; i++) {
520             if ((unit = ctrlrunit(argv[i])) == -1) {
521                 warnx("'%s' is not a valid controller", argv[i]);
522             } else {
523                 mlxd_foreach_ctrlr(unit, &ma);
524             }
525         }
526     } else {
527         for (i = 0; i < argc; i++) {
528             if ((unit = driveunit(argv[i])) == -1) {
529                 warnx("'%s' is not a valid drive", argv[i]);
530             } else {
531                 /* run across all controllers to find this drive */
532                 mlx_foreach(detach_drive, &unit);
533             }
534         }
535     }
536     return(0);
537 }
538
539 /********************************************************************************
540  * Initiate a consistency check on a system drive.
541  *
542  * check [<drive>]
543  *      Start a check of <drive>
544  *
545  */
546 static int
547 cmd_check(int argc, char *argv[])
548 {
549     int         unit, fd, result;
550
551     if (argc != 2)
552         return(cmd_help(argc, argv));
553
554     if ((unit = driveunit(argv[1])) == -1) {
555         warnx("'%s' is not a valid drive", argv[1]);
556     } else {
557         
558         /* Get the device */
559         if ((fd = open(drivepath(unit), 0)) < 0) {
560             warn("can't open %s", drivepath(unit));
561         } else {
562             /* Try to start the check */
563             if ((ioctl(fd, MLXD_CHECKASYNC, &result)) < 0) {
564                 switch(result) {
565                 case 0x0002:
566                     warnx("one or more of the SCSI disks on which the drive '%s' depends is DEAD", argv[1]);
567                     break;
568                 case 0x0105:
569                     warnx("drive %s is invalid, or not a drive which can be checked", argv[1]);
570                     break;
571                 case 0x0106:
572                     warnx("drive rebuild or consistency check is already in progress on this controller");
573                     break;
574                 default:
575                     warn("ioctl MLXD_CHECKASYNC");
576                 }
577             }
578         }
579     }
580     return(0);
581 }
582
583 /********************************************************************************
584  * Initiate a physical drive rebuild
585  *
586  * rebuild <controller> <channel>:<target>
587  *      Start a rebuild of <controller>:<channel>:<target>
588  *
589  */
590 static int
591 cmd_rebuild(int argc, char *argv[])
592 {
593     struct mlx_rebuild_request  rb;
594     int                         unit, fd;
595
596     if (argc != 3)
597         return(cmd_help(argc, argv));
598
599     /* parse arguments */
600     if ((unit = ctrlrunit(argv[1])) == -1) {
601         warnx("'%s' is not a valid controller", argv[1]);
602         return(1);
603     }
604     /* try diskXXXX and unknownXXXX as we report the latter for a dead drive ... */
605     if ((sscanf(argv[2], "disk%2d%2d", &rb.rr_channel, &rb.rr_target) != 2) &&
606         (sscanf(argv[2], "unknown%2d%2d", &rb.rr_channel, &rb.rr_target) != 2)) {       
607         warnx("'%s' is not a valid physical drive", argv[2]);
608         return(1);
609     }
610     /* get the device */
611     if ((fd = open(ctrlrpath(unit), 0)) < 0) {
612         warn("can't open %s", ctrlrpath(unit));
613         return(1);
614     }
615     /* try to start the rebuild */
616     if ((ioctl(fd, MLX_REBUILDASYNC, &rb)) < 0) {
617         switch(rb.rr_status) {
618         case 0x0002:
619             warnx("the drive at %d:%d is already ONLINE", rb.rr_channel, rb.rr_target);
620             break;
621         case 0x0004:
622             warnx("drive failed during rebuild");
623             break;
624         case 0x0105:
625             warnx("there is no drive at channel %d, target %d", rb.rr_channel, rb.rr_target);
626             break;
627         case 0x0106:
628             warnx("drive rebuild or consistency check is already in progress on this controller");
629             break;
630         default:
631             warn("ioctl MLXD_REBUILDASYNC");
632         }
633     }
634     return(0);
635 }
636
637 #ifdef SUPPORT_PAUSE
638 /********************************************************************************
639  * Pause one or more channels on a controller
640  *
641  * pause [-d <delay>] [-t <time>] <controller> [<channel>...]
642  *              Pauses <channel> (or all channels) for <time> seconds after a
643  *              delay of <delay> seconds.
644  * pause <controller> -c
645  *              Cancels pending pause
646  */
647 static int
648 cmd_pause(int argc, char *argv[]) 
649 {
650     struct mlx_pause    mp;
651     int                 unit, i, ch, fd, cancel = 0;
652     char                *cp;
653     int                 oargc = argc;
654     char                **oargv = argv;
655
656     mp.mp_which = 0;
657     mp.mp_when = 30;
658     mp.mp_howlong = 30;
659     optreset = 1;
660     optind = 1;
661     while ((ch = getopt(argc, argv, "cd:t:")) != -1)
662         switch(ch) {
663         case 'c':
664             cancel = 1;
665             break;
666         case 'd':
667             mp.mp_when = strtol(optarg, &cp, 0);
668             if (*cp != 0)
669                 return(cmd_help(argc, argv));
670             break;
671         case 't':
672             mp.mp_howlong = strtol(optarg, &cp, 0);
673             if (*cp != 0)
674                 return(cmd_help(argc, argv));
675             break;
676         default:
677             return(cmd_help(argc, argv));
678         }
679     argc -= optind;
680     argv += optind;
681
682     /* get controller unit number that we're working on */
683     if ((argc < 1) || ((unit = ctrlrunit(argv[0])) == -1))
684         return(cmd_help(oargc, oargv));
685
686     /* Get the device */
687     if ((fd = open(ctrlrpath(unit), 0)) < 0) {
688         warn("can't open %s", ctrlrpath(unit));
689         return(1);
690     }
691
692     if (argc == 1) {
693         /* controller-wide pause/cancel */
694         mp.mp_which = cancel ? MLX_PAUSE_CANCEL : MLX_PAUSE_ALL;
695     } else {
696         for (i = 1; i < argc; i++) {
697             ch = strtol(argv[i], &cp, 0);
698             if (*cp != 0) {
699                 warnx("bad channel number '%s'", argv[i]);
700                 continue;
701             } else {
702                 mp.mp_which |= (1 << ch);
703             }
704         }
705     }
706     if ((ioctl(fd, MLX_PAUSE_CHANNEL, &mp)) < 0)
707         warn("couldn't %s %s", cancel ? "cancel pause on" : "pause", ctrlrname(unit));
708     close(fd);
709     return(0);
710 }
711 #endif  /* SUPPORT_PAUSE */
712