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 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 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 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 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 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 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 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 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 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 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 * 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 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 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 * 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 * 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 * 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