1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26 /*
27 * devctl - device control utility
28 *
29 * to compile:
30 * cc -o devctl -ldevice -ldevinfo devctl.c
31 *
32 * usage: devctl [-v] command [device/bus pathname]
33 *
34 * Commands:
35 * list - list all controllers exporting the devctl interface
36 * online - online a device
37 * offline - offline a device
38 * remove - remove a device from the device tree
39 * quiesce - quiesce the bus
40 * unquiesce - resume bus activity
41 * configure - configure a bus's child devices
42 * unconfigure - unconfigure a bus's child devices
43 * bus-reset - reset a bus
44 * dev-reset - reset a device
45 * bus-getstate - return the current state of the bus
46 * dev-getstate - return the current state of the device
47 * bus-devcreate - create a new device, bus specific
48 * dev-raisepower - power up a device via pm_raise_power() (pm)
49 * dev-idlecomp - idle a device's component 0 (pm)
50 * dev-busycomp - busy a device's component 0 (pm)
51 * dev-testbusy - test a device's component 0's busy state (pm)
52 * dev-changepowerhigh - power up a device via pm_power_has_changed()
53 * (pm)
54 * dev-changepowerlow - power off a device via pm_power_has_changed()
55 * (pm)
56 * dev-failsuspend - fail DDI_SUSPEND (pm)
57 * dev-changeonresume - issue pm_power_has_changed() vs,
58 * pm_raise_power() on device resume (pm)
59 * dev-nolowerpower - don't call pm_lower_power() on detach (pm)
60 * dev-promprintf - issue a prom_printf() call (pm)
61 * bus-raisepower - power up a bus via pm_raise_power() (pm)
62 * bus-idlecomp - idle a bus' component (pm)
63 * bus-busycomp - busy a bus' component (pm)
64 * bus-testbusy - test a bus' component busy state (pm)
65 * bus-changepowerhigh - power up a bus via pm_power_has_changed() (pm)
66 * bus-changepowerlow - power off a bus via pm_power_has_changed()
67 * (pm)
68 * bus-failsuspend - fail DDI_SUSPEND (pm)
69 * bus-teststrict - test is bus driver is strict or involved (pm)
70 * bus-noinvol - mark idle twice when child detaches
71 *
72 *
73 * Returns:
74 * - Success
75 * - Operation not supported by device
76 * - No Permission
77 * - No Such Device
78 *
79 * Examples:
80 * devctl list - list all controllers exporting a :devctl node
81 * devctl offline /dev/dsk/c0t3d0s0 - offline disk
82 * devctl dev-getstate /devices/sbus@1f,0/espdma@e,8400000/esp@e,8800000\
83 * sd@3,0
84 *
85 */
86
87 #include <stdio.h>
88 #include <string.h>
89 #include <unistd.h>
90 #include <stdlib.h>
91 #include <sys/types.h>
92 #include <sys/errno.h>
93 #include <sys/stat.h>
94 #include <sys/param.h>
95 #include <libdevice.h>
96 #include <libdevinfo.h>
97 #include <sys/sunddi.h>
98
99 typedef struct cmds {
100 char *cmdname;
101 int (*cmdf)(devctl_hdl_t);
102 } cmds_t;
103
104 extern int errno;
105
106 static void setpname(char *name);
107 static void print_bus_state(char *devname, uint_t state);
108 static void print_dev_state(char *devname, uint_t state);
109 static int dev_getstate(devctl_hdl_t);
110 static int bus_getstate(devctl_hdl_t);
111 static int bus_devcreate(devctl_hdl_t);
112 static void run_list_ctlrs(void);
113 static struct cmds *dc_cmd();
114 static int nexif(di_node_t din, di_minor_t dim, void *arg);
115 static void *s_malloc(size_t);
116 static void *s_realloc(void *, size_t);
117 static char *s_strdup(char *);
118 static int dev_pm_testbusy(devctl_hdl_t);
119 static int bus_pm_teststrict(devctl_hdl_t);
120
121 static char *devctl_device;
122 static char *orig_path;
123 static char *devctl_cmdname;
124 static char *progname;
125 static int verbose;
126 static int debug;
127 static char *dev_name;
128 static char **dev_props;
129
130 static const char *usage = "%s [-v] list | online | offline | remove |\n"
131 "\tquiesce | unquiesce | configure | unconfigure |\n"
132 "\t{bus,dev}-reset {bus,dev}-getstate | {bus,dev}-raisepower |\n"
133 "\t{bus,dev}-idlecomp | {bus,dev}-busycomp |\n"
134 "\t{bus,dev}-changepowerhigh | {bus,dev}-changepowerlow |\n"
135 "\t{bus,dev}-testbusy | {bus,dev}-failsuspend | dev-changeonresume |\n"
136 "\tdev-promprintf | dev-nolowerpower | bus-teststrict |\n"
137 "\tbus-noinvol [/dev/... | /devices/...]\n";
138
139 static struct cmds device_cmds[] = {
140 {"online", devctl_device_online},
141 {"offline", devctl_device_offline},
142 {"remove", devctl_device_remove},
143 {"dev-reset", devctl_device_reset},
144 {"dev-getstate", dev_getstate},
145 {"dev-raisepower", devctl_pm_raisepower},
146 {"dev-busycomp", devctl_pm_busycomponent},
147 {"dev-idlecomp", devctl_pm_idlecomponent},
148 {"dev-testbusy", dev_pm_testbusy},
149 {"dev-changepowerlow", devctl_pm_changepowerlow},
150 {"dev-changepowerhigh", devctl_pm_changepowerhigh},
151 {"dev-failsuspend", devctl_pm_failsuspend},
152 {"dev-changeonresume", devctl_pm_device_changeonresume},
153 {"dev-promprintf", devctl_pm_device_promprintf},
154 {"dev-nolowerpower", devctl_pm_device_no_lower_power},
155 {NULL, NULL},
156 };
157
158 static struct cmds bus_cmds[] = {
159 {"quiesce", devctl_bus_quiesce},
160 {"unquiesce", devctl_bus_unquiesce},
161 {"bus-reset", devctl_bus_reset},
162 {"configure", devctl_bus_configure},
163 {"unconfigure", devctl_bus_unconfigure},
164 {"bus-getstate", bus_getstate},
165 {"bus-devcreate", bus_devcreate},
166 {"bus-raisepower", devctl_pm_raisepower},
167 {"bus-busycomp", devctl_pm_busycomponent},
168 {"bus-idlecomp", devctl_pm_idlecomponent},
169 {"bus-changepowerlow", devctl_pm_changepowerlow},
170 {"bus-changepowerhigh", devctl_pm_changepowerhigh},
171 {"bus-testbusy", dev_pm_testbusy},
172 {"bus-failsuspend", devctl_pm_failsuspend},
173 {"bus-teststrict", bus_pm_teststrict},
174 {"bus-noinvol", devctl_pm_bus_no_invol},
175 {NULL, NULL},
176 };
177
178
179
180 int
main(int argc,char * argv[])181 main(int argc, char *argv[])
182 {
183 int c;
184 int rv;
185 int pathlen;
186 struct cmds *dcmd;
187 devctl_hdl_t dcp;
188 struct stat stat_buf;
189
190 setpname(argv[0]);
191 while ((c = getopt(argc, argv, "vd")) != -1) {
192 switch (c) {
193 case 'v':
194 ++verbose;
195 break;
196 case 'd':
197 ++debug;
198 (void) putenv("LIBDEVICE_DEBUG");
199 break;
200 default:
201 (void) fprintf(stderr, usage, progname);
202 exit(1);
203 /*NOTREACHED*/
204 }
205 }
206
207 if (optind == argc) {
208 (void) fprintf(stderr, usage, progname);
209 exit(-1);
210 }
211
212 devctl_cmdname = argv[optind++];
213
214 if (strcmp(devctl_cmdname, "list") == 0) {
215 run_list_ctlrs();
216 exit(0);
217 }
218
219 /*
220 * any command other than "list" requires a device path
221 */
222 if (((optind + 1) > argc)) {
223 (void) fprintf(stderr, usage, progname);
224 exit(-1);
225 }
226
227 orig_path = s_strdup(argv[optind]);
228 devctl_device = s_malloc(MAXPATHLEN);
229 (void) strcpy(devctl_device, orig_path);
230
231 /*
232 * Additional properties follow for bus-devcreate
233 */
234 if ((optind + 1 < argc) &&
235 strcmp(devctl_cmdname, "bus-devcreate") == 0) {
236 int i;
237 optind++;
238 dev_name = s_strdup(argv[optind]);
239 i = argc - optind;
240 dev_props = s_malloc(i * sizeof (char *));
241 while (--i) {
242 dev_props[i - 1] = s_strdup(argv[optind + i]);
243 }
244 }
245
246 /*
247 * if the device is a logical name, get the physical name
248 */
249 if (lstat(orig_path, &stat_buf) == 0) {
250 if (S_ISLNK(stat_buf.st_mode)) {
251 if ((pathlen = readlink(orig_path, devctl_device,
252 MAXPATHLEN)) == -1) {
253 (void) fprintf(stderr,
254 "devctl: readlink(%s) - %s\n",
255 orig_path, strerror(errno));
256 exit(-1);
257 }
258 devctl_device[pathlen] = '\0';
259 }
260 }
261
262 if ((dcmd = dc_cmd(device_cmds, devctl_cmdname)) == NULL) {
263 dcmd = dc_cmd(bus_cmds, devctl_cmdname);
264 if (dcmd == NULL) {
265 (void) fprintf(stderr, "unrecognized command (%s)\n",
266 devctl_cmdname);
267 (void) fprintf(stderr, usage, progname);
268 exit(1);
269 } else if (strcmp(devctl_cmdname, "bus-raisepower") == 0 ||
270 strcmp(devctl_cmdname, "bus-changepowerlow") == 0 ||
271 strcmp(devctl_cmdname, "bus-changepowerhigh") == 0 ||
272 strcmp(devctl_cmdname, "bus-idlecomp") == 0 ||
273 strcmp(devctl_cmdname, "bus-busycomp") == 0 ||
274 strcmp(devctl_cmdname, "bus-testbusy") == 0 ||
275 strcmp(devctl_cmdname, "bus-failsuspend") == 0 ||
276 strcmp(devctl_cmdname, "bus-teststrict") == 0 ||
277 strcmp(devctl_cmdname, "bus-noinvol") == 0) {
278 dcp = devctl_pm_bus_acquire(devctl_device, 0);
279 if (dcp == NULL) {
280 (void) fprintf(stderr,
281 "devctl: device_pm_bus_acquire %s - %s\n",
282 devctl_device, strerror(errno));
283 exit(-1);
284 }
285 } else {
286 dcp = devctl_bus_acquire(devctl_device, 0);
287 if (dcp == NULL) {
288 (void) fprintf(stderr, "devctl: bus_acquire "
289 "%s - %s\n",
290 devctl_device, strerror(errno));
291 exit(-1);
292 }
293 }
294 } else if (strcmp(devctl_cmdname, "dev-raisepower") == 0 ||
295 strcmp(devctl_cmdname, "dev-changepowerlow") == 0 ||
296 strcmp(devctl_cmdname, "dev-changepowerhigh") == 0 ||
297 strcmp(devctl_cmdname, "dev-idlecomp") == 0 ||
298 strcmp(devctl_cmdname, "dev-busycomp") == 0 ||
299 strcmp(devctl_cmdname, "dev-testbusy") == 0 ||
300 strcmp(devctl_cmdname, "dev-failsuspend") == 0 ||
301 strcmp(devctl_cmdname, "dev-changeonresume") == 0 ||
302 strcmp(devctl_cmdname, "dev-promprintf") == 0 ||
303 strcmp(devctl_cmdname, "dev-nolowerpower") == 0) {
304 dcp = devctl_pm_dev_acquire(devctl_device, 0);
305 if (dcp == NULL) {
306 (void) fprintf(stderr,
307 "devctl: device_pm_dev_acquire %s - %s\n",
308 devctl_device, strerror(errno));
309 exit(-1);
310 }
311 } else {
312 dcp = devctl_device_acquire(devctl_device, 0);
313 if (dcp == NULL) {
314 (void) fprintf(stderr,
315 "devctl: device_acquire %s - %s\n",
316 devctl_device, strerror(errno));
317 exit(-1);
318 }
319 }
320
321 if (verbose)
322 (void) printf("devctl: cmd (%s) device (%s)\n",
323 devctl_cmdname, orig_path);
324
325 (void) fflush(NULL); /* get output out of the way */
326
327 rv = (dcmd->cmdf)(dcp);
328
329 if (rv == -1) {
330 perror("devctl");
331 exit(-1);
332 }
333 return (0);
334 } /* main */
335
336 static int
dev_pm_testbusy(devctl_hdl_t dcp)337 dev_pm_testbusy(devctl_hdl_t dcp)
338 {
339 int rv;
340 uint_t *busyp;
341
342 busyp = s_malloc(sizeof (uint_t));
343 rv = devctl_pm_testbusy(dcp, busyp);
344 if (rv != -1)
345 (void) printf("%s: busy state %d\n", orig_path, *busyp);
346
347 return (0);
348 }
349
350 static int
bus_pm_teststrict(devctl_hdl_t dcp)351 bus_pm_teststrict(devctl_hdl_t dcp)
352 {
353 int rv;
354 uint_t *strict;
355
356 strict = s_malloc(sizeof (uint_t));
357
358 rv = devctl_pm_bus_teststrict(dcp, strict);
359 if (rv != -1)
360 (void) printf("%s: strict %d\n", orig_path, *strict);
361
362 return (0);
363 }
364
365 static int
dev_getstate(devctl_hdl_t dcp)366 dev_getstate(devctl_hdl_t dcp)
367 {
368 int rv;
369 uint_t state;
370
371 rv = devctl_device_getstate(dcp, &state);
372 if (rv != -1)
373 print_dev_state(orig_path, state);
374
375 return (0);
376 }
377
378 static int
bus_getstate(devctl_hdl_t dcp)379 bus_getstate(devctl_hdl_t dcp)
380 {
381 int rv;
382 uint_t state;
383
384 rv = devctl_bus_getstate(dcp, &state);
385 if (rv != -1)
386 print_bus_state(orig_path, state);
387
388 return (0);
389 }
390
391 /*
392 * Only string property is supported now.
393 * Will add more later.
394 */
395 static void
add_prop(devctl_ddef_t ddef_hdl,char * prop_str)396 add_prop(devctl_ddef_t ddef_hdl, char *prop_str)
397 {
398 char *pname, *pval, *tmp;
399 char **strs = NULL;
400 int nstr;
401
402 tmp = strchr(prop_str, '=');
403 if (tmp == NULL) {
404 (void) fprintf(stderr, "invalid property %s", prop_str);
405 exit(-1);
406 }
407
408 (void) printf("prop string: %s\n", prop_str);
409 pname = prop_str;
410 *tmp++ = '\0';
411 if (*tmp != '"') {
412 (void) devctl_ddef_string(ddef_hdl, pname, tmp);
413 return;
414 }
415
416 nstr = 0;
417 while (*tmp != '\0') {
418 pval = tmp + 1;
419 tmp = strchr(pval, '"');
420 if (tmp == NULL) {
421 (void) fprintf(stderr, "missing quote in %s", tmp);
422 exit(-1);
423 }
424 nstr++;
425 strs = (char **)s_realloc(strs, nstr * sizeof (char *));
426 strs[nstr - 1] = pval;
427 *tmp++ = '\0';
428 (void) printf("string[%d] = %s\n", nstr - 1, pval);
429 if (*tmp)
430 tmp = strchr(tmp, '"');
431 if (tmp == NULL) {
432 (void) fprintf(stderr, "string not ending with quote");
433 exit(-1);
434 }
435 }
436
437 (void) devctl_ddef_string_array(ddef_hdl, pname, nstr, strs);
438 free(strs);
439 }
440
441 static int
bus_devcreate(devctl_hdl_t bus_dcp)442 bus_devcreate(devctl_hdl_t bus_dcp)
443 {
444 int rv;
445 char **propp = dev_props;
446 devctl_ddef_t ddef_hdl = NULL;
447 devctl_hdl_t dev_hdl = NULL;
448
449 ddef_hdl = devctl_ddef_alloc(dev_name, 0);
450 if (dev_props == NULL) {
451 (void) fprintf(stderr, "dev-create: missing device props\n");
452 return (-1);
453 }
454
455 while (*propp) {
456 add_prop(ddef_hdl, *propp);
457 propp++;
458 }
459
460 if (devctl_bus_dev_create(bus_dcp, ddef_hdl, 0, &dev_hdl)) {
461 (void) fprintf(stderr,
462 "bus-devcreate: failed to create device node\n");
463 rv = -1;
464 } else if (devctl_get_pathname(dev_hdl, devctl_device, MAXPATHLEN)
465 == NULL) {
466 (void) fprintf(stderr,
467 "bus-devcreate: failed to get device path\n");
468 rv = -1;
469 } else {
470 (void) printf("created device %s\n", devctl_device);
471 rv = 0;
472 }
473
474 devctl_ddef_free(ddef_hdl);
475 if (dev_hdl)
476 devctl_release(dev_hdl);
477
478 return (rv);
479 }
480
481 static void
print_bus_state(char * devname,uint_t state)482 print_bus_state(char *devname, uint_t state)
483 {
484 (void) printf("\t%s: ", devname);
485 if (state == BUS_QUIESCED)
486 (void) printf("Quiesced");
487 else if (state == BUS_ACTIVE)
488 (void) printf("Active");
489 else if (state == BUS_SHUTDOWN)
490 (void) printf("Shutdown");
491 (void) printf("\n");
492 }
493
494 static void
print_dev_state(char * devname,uint_t state)495 print_dev_state(char *devname, uint_t state)
496 {
497 (void) printf("\t%s: ", devname);
498 if (state & DEVICE_ONLINE) {
499 (void) printf("Online");
500 if (state & DEVICE_BUSY)
501 (void) printf(" Busy");
502 if (state & DEVICE_DOWN)
503 (void) printf(" Down");
504 } else {
505 if (state & DEVICE_OFFLINE) {
506 (void) printf("Offline");
507 if (state & DEVICE_DOWN)
508 (void) printf(" Down");
509 }
510 }
511 (void) printf("\n");
512 }
513
514 static void
setpname(char * name)515 setpname(char *name)
516 {
517 register char *p;
518
519 if (p = strrchr(name, '/'))
520 progname = p + 1;
521 else
522 progname = name;
523 }
524
525 static struct cmds *
dc_cmd(struct cmds * cmd_tbl,char * devctl_cmdname)526 dc_cmd(struct cmds *cmd_tbl, char *devctl_cmdname)
527 {
528 int i;
529
530 for (i = 0; cmd_tbl[i].cmdname != NULL; i++) {
531 if (strcasecmp(cmd_tbl[i].cmdname, devctl_cmdname) == 0)
532 return (&cmd_tbl[i]);
533 }
534
535 return (NULL);
536 }
537
538 /*
539 * list all nexus drivers exporting the :devctl minor device
540 */
541 static void
run_list_ctlrs(void)542 run_list_ctlrs(void)
543 {
544 di_node_t dinode;
545
546 if ((dinode = di_init("/", DINFOSUBTREE|DINFOMINOR)) == NULL) {
547 (void) fprintf(stderr, "%s: di_init() failed\n",
548 progname);
549 exit(-1);
550 }
551 (void) di_walk_minor(dinode, DDI_NT_NEXUS, 0, NULL, &nexif);
552 di_fini(dinode);
553 exit(0);
554 }
555
556 /*ARGSUSED*/
557 static int
nexif(di_node_t din,di_minor_t dim,void * arg)558 nexif(di_node_t din, di_minor_t dim, void *arg)
559 {
560 char *devname;
561
562 if ((devname = di_devfs_path(din)) != NULL) {
563 (void) printf("%s%d: /devices%s\n", di_driver_name(din),
564 di_instance(din), devname);
565 di_devfs_path_free(devname);
566 }
567
568 return (DI_WALK_CONTINUE);
569 }
570
571 void *
s_malloc(size_t len)572 s_malloc(size_t len)
573 {
574 void *buf = malloc(len);
575
576 if (buf == NULL) {
577 perror("s_malloc failed");
578 exit(-1);
579 }
580 return (buf);
581 }
582
583 void *
s_realloc(void * ptr,size_t len)584 s_realloc(void *ptr, size_t len)
585 {
586 void *buf = realloc(ptr, len);
587
588 if (buf == NULL) {
589 perror("s_realloc failed");
590 exit(-1);
591 }
592 return (buf);
593 }
594
595 char *
s_strdup(char * str)596 s_strdup(char *str)
597 {
598 char *buf = strdup(str);
599
600 if (buf == NULL) {
601 perror("s_malloc failed");
602 exit(-1);
603 }
604 return (buf);
605 }
606