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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include <sys/types.h> 28 #include <sys/param.h> 29 #include <sys/stat.h> 30 #include <fcntl.h> 31 #include <string.h> 32 #include <strings.h> 33 #include <errno.h> 34 #include <string.h> 35 #include <stdlib.h> 36 #include <stdio.h> 37 #include <unistd.h> 38 #include <sys/nvpair.h> 39 #include "libdevice.h" 40 41 static int _libdevice_debug = 0; 42 static const char *devctl_minorname = ":devctl"; 43 static const char *nullptr = "<null>"; 44 static const char *devctl_target_raw = "a,raw"; 45 46 typedef enum { DEVCTL_BUS, DEVCTL_DEVICE, DEVCTL_AP, DEVCTL_CLONE, 47 DEVCTL_PM_DEV, DEVCTL_PM_BUS } dc_type_t; 48 49 /* 50 * devctl_hdl structures are allocated by the devctl_XX_acquire() 51 * interfaces and passed to the remaining interfaces in this library. 52 */ 53 struct devctl_hdl { 54 char *opath; /* copy of the original path */ 55 dc_type_t hdltype; /* handle type */ 56 int fd; /* nexus device node */ 57 char *nodename; /* DEVCTL_DEVICE handles only */ 58 char *unitaddr; /* DEVCTL_DEVICE handles only */ 59 }; 60 #define DCP(x) ((struct devctl_hdl *)(x)) 61 62 static int dc_cmd(uint_t, uint_t, struct devctl_hdl *, nvlist_t *, void *); 63 static devctl_hdl_t dc_mkhndl(dc_type_t, char *, uint_t, devctl_hdl_t); 64 65 66 #pragma init(_libdevice_init) 67 void 68 _libdevice_init() 69 { 70 _libdevice_debug = getenv("LIBDEVICE_DEBUG") != NULL; 71 } 72 73 /* 74 * release a devctl_hdl structure 75 */ 76 void 77 devctl_release(devctl_hdl_t hdl) 78 { 79 if (_libdevice_debug) 80 (void) printf("devctl_release: %p\n", (void *)hdl); 81 82 if (hdl == NULL) 83 return; 84 85 if (DCP(hdl)->fd != -1) 86 (void) close(DCP(hdl)->fd); 87 88 if (DCP(hdl)->opath != NULL) 89 free(DCP(hdl)->opath); 90 91 if (DCP(hdl)->nodename != NULL) 92 free(DCP(hdl)->nodename); 93 94 if (DCP(hdl)->unitaddr != NULL) 95 free(DCP(hdl)->unitaddr); 96 97 free(hdl); 98 } 99 100 /* 101 * construct a handle suitable for devctl_bus_*() operations 102 */ 103 devctl_hdl_t 104 devctl_bus_acquire(char *devfs_path, uint_t flags) 105 { 106 uint_t oflags; 107 108 if (_libdevice_debug) 109 (void) printf("devctl_bus_acquire: %s (%d)\n", 110 ((devfs_path != NULL) ? devfs_path : nullptr), flags); 111 112 if ((devfs_path == NULL) || ((flags != 0) && (flags != DC_EXCL))) { 113 errno = EINVAL; 114 return (NULL); 115 } 116 117 oflags = ((flags & DC_EXCL) != 0) ? O_EXCL|O_RDWR : O_RDWR; 118 return (dc_mkhndl(DEVCTL_BUS, devfs_path, oflags, NULL)); 119 } 120 121 122 /* 123 * construct a handle suitable for devctl_bus_*() and 124 * devctl_device_*() operations. 125 */ 126 devctl_hdl_t 127 devctl_device_acquire(char *devfs_path, uint_t flags) 128 { 129 uint_t oflags; 130 131 if (_libdevice_debug) 132 (void) printf("devctl_device_acquire: %s (%d)\n", 133 ((devfs_path != NULL) ? devfs_path : nullptr), flags); 134 135 if ((devfs_path == NULL) || ((flags != 0) && (flags != DC_EXCL))) { 136 errno = EINVAL; 137 return (NULL); 138 } 139 140 oflags = ((flags & DC_EXCL) != 0) ? O_EXCL|O_RDWR : O_RDWR; 141 return (dc_mkhndl(DEVCTL_DEVICE, devfs_path, oflags, NULL)); 142 } 143 144 145 /* 146 * given a devfs (/devices) pathname to an attachment point device, 147 * access the device and return a handle to be passed to the 148 * devctl_ap_XXX() functions. 149 */ 150 devctl_hdl_t 151 devctl_ap_acquire(char *devfs_path, uint_t flags) 152 { 153 uint_t oflags; 154 155 if (_libdevice_debug) 156 (void) printf("devctl_ap_acquire: %s (%d)\n", 157 ((devfs_path != NULL) ? devfs_path : nullptr), flags); 158 159 if ((devfs_path == NULL) || 160 ((flags != 0) && ((flags & DC_EXCL) != 0) && 161 ((flags & DC_RDONLY) != 0))) { 162 errno = EINVAL; 163 return (NULL); 164 } 165 166 oflags = ((flags & DC_EXCL) != 0) ? O_EXCL : 0; 167 oflags |= ((flags & DC_RDONLY) != 0) ? O_RDONLY : O_RDWR; 168 169 return (dc_mkhndl(DEVCTL_AP, devfs_path, oflags, NULL)); 170 } 171 172 173 /* 174 * given a devfs (/devices) pathname access the device and return 175 * a handle to be passed to the devctl_pm_XXX() functions. 176 * The minor name ":devctl" is appended. 177 */ 178 devctl_hdl_t 179 devctl_pm_bus_acquire(char *devfs_path, uint_t flags) 180 { 181 uint_t oflags; 182 183 if (_libdevice_debug) 184 (void) printf("devctl_pm_bus_acquire: %s (%d)\n", 185 ((devfs_path != NULL) ? devfs_path : nullptr), flags); 186 187 if ((devfs_path == NULL) || ((flags != 0) && (flags != DC_EXCL))) { 188 errno = EINVAL; 189 return (NULL); 190 } 191 192 oflags = ((flags & DC_EXCL) != 0) ? (O_EXCL | O_RDWR) : O_RDWR; 193 return (dc_mkhndl(DEVCTL_PM_BUS, devfs_path, oflags, NULL)); 194 } 195 196 197 /* 198 * given a devfs (/devices) pathname access the device and return 199 * a handle to be passed to the devctl_pm_XXX() functions. 200 * The minor name is derived from the device name. 201 */ 202 devctl_hdl_t 203 devctl_pm_dev_acquire(char *devfs_path, uint_t flags) 204 { 205 uint_t oflags; 206 207 if (_libdevice_debug) 208 (void) printf("devctl_pm_dev_acquire: %s (%d)\n", 209 ((devfs_path != NULL) ? devfs_path : nullptr), flags); 210 211 if ((devfs_path == NULL) || ((flags != 0) && (flags != DC_EXCL))) { 212 errno = EINVAL; 213 return (NULL); 214 } 215 216 oflags = ((flags & DC_EXCL) != 0) ? (O_EXCL | O_RDWR) : O_RDWR; 217 return (dc_mkhndl(DEVCTL_PM_DEV, devfs_path, oflags, NULL)); 218 } 219 220 221 /* 222 * allocate and initalize the devctl_hdl structure for the 223 * particular handle type. 224 */ 225 static devctl_hdl_t 226 dc_mkhndl(dc_type_t type, char *path, uint_t oflags, devctl_hdl_t pc) 227 { 228 struct devctl_hdl *dcp; 229 struct stat sb; 230 char iocpath[MAXPATHLEN]; 231 char *nodename, *unitsep, *minorsep, *chop; 232 char *minorname; 233 size_t strlcpy_size; 234 char *iocpath_dup; 235 char *tok; 236 237 if ((path == NULL) || (strlen(path) > MAXPATHLEN - 1)) { 238 errno = EINVAL; 239 return (NULL); 240 } 241 242 /* 243 * allocate handle and make a copy of the original path 244 */ 245 if ((dcp = calloc(1, sizeof (*dcp))) == NULL) { 246 errno = ENOMEM; 247 return (NULL); 248 } 249 if ((dcp->opath = strdup(path)) == NULL) { 250 devctl_release((devctl_hdl_t)dcp); 251 errno = ENOMEM; 252 return (NULL); 253 } 254 255 (void) strcpy(iocpath, path); 256 dcp->hdltype = type; 257 dcp->fd = -1; 258 259 /* 260 * break apart the pathname according to the type handle 261 */ 262 switch (type) { 263 case DEVCTL_PM_BUS: 264 /* 265 * chop off any minor name and concatenate the 266 * ":devctl" minor node name string. 267 */ 268 if ((chop = strrchr(iocpath, ':')) != NULL) 269 *chop = '\0'; 270 271 if (strlcat(iocpath, devctl_minorname, MAXPATHLEN) >= 272 MAXPATHLEN) { 273 devctl_release((devctl_hdl_t)dcp); 274 errno = EINVAL; 275 return (NULL); 276 } else if (_libdevice_debug) { 277 (void) printf("DEVCTL_PM_BUS: iocpath %s\n", iocpath); 278 } 279 break; 280 281 case DEVCTL_PM_DEV: 282 /* 283 * Chop up the last device component in the pathname. 284 * Concatenate either the device name itself, or the 285 * "a,raw" string, as the minor node name, to the iocpath. 286 */ 287 if ((iocpath_dup = strdup(iocpath)) == NULL) { 288 devctl_release((devctl_hdl_t)dcp); 289 errno = ENOMEM; 290 return (NULL); 291 } 292 if ((chop = strrchr(iocpath_dup, '/')) == NULL) { 293 devctl_release((devctl_hdl_t)dcp); 294 errno = EINVAL; 295 return (NULL); 296 } 297 *chop = '\0'; 298 nodename = chop + 1; 299 300 /* 301 * remove the "@0,0" string 302 */ 303 tok = strtok(nodename, "@"); 304 if ((minorname = malloc(strlen(tok) +1)) == NULL) { 305 if (_libdevice_debug) 306 (void) printf("DEVCTL_PM_DEV: failed malloc for" 307 " minorname\n"); 308 devctl_release((devctl_hdl_t)dcp); 309 errno = ENOMEM; 310 return (NULL); 311 } 312 (void) strcpy(minorname, tok); 313 if (_libdevice_debug) { 314 (void) printf("DEVCTL_PM_DEV: minorname %s\n", 315 minorname); 316 } 317 318 /* 319 * construct the name of the ioctl device 320 * by concatenating either ":a,raw" or ":"minorname 321 */ 322 (void) strlcat(iocpath, ":", MAXPATHLEN); 323 if (strcmp(minorname, "disk_chan") == 0 || 324 strcmp(minorname, "disk_wwn") == 0 || 325 strcmp(minorname, "disk_cdrom") == 0) { 326 strlcpy_size = strlcat(iocpath, devctl_target_raw, 327 MAXPATHLEN); 328 } else { 329 strlcpy_size = strlcat(iocpath, minorname, MAXPATHLEN); 330 } 331 if (strlcpy_size >= MAXPATHLEN) { 332 devctl_release((devctl_hdl_t)dcp); 333 errno = EINVAL; 334 return (NULL); 335 } else if (_libdevice_debug) { 336 (void) printf("DEVCTL_PM_DEV: iocpath %s\n", 337 iocpath); 338 } 339 break; 340 341 case DEVCTL_AP: 342 /* 343 * take the pathname as provided. 344 */ 345 break; 346 347 case DEVCTL_BUS: 348 /* 349 * chop off any minor name and concatenate the 350 * ":devctl" minor node name string. 351 */ 352 if ((chop = strrchr(iocpath, ':')) != NULL) 353 *chop = '\0'; 354 355 if (strlcat(iocpath, devctl_minorname, MAXPATHLEN) >= 356 MAXPATHLEN) { 357 devctl_release((devctl_hdl_t)dcp); 358 errno = EINVAL; 359 return (NULL); 360 } 361 break; 362 363 case DEVCTL_CLONE: 364 /* 365 * create a device handle for a new device created 366 * from a call to devctl_bus_dev_create() 367 */ 368 dcp->hdltype = DEVCTL_DEVICE; 369 370 /* FALLTHRU */ 371 372 case DEVCTL_DEVICE: 373 374 /* 375 * Chop up the last device component in the pathname. 376 * The componets are passed as nodename and unitaddr 377 * in the IOCTL data for DEVCTL ops on devices. 378 */ 379 if ((chop = strrchr(iocpath, '/')) == NULL) { 380 devctl_release((devctl_hdl_t)dcp); 381 errno = EINVAL; 382 return (NULL); 383 } 384 *chop = '\0'; 385 386 nodename = chop + 1; 387 unitsep = strchr(nodename, '@'); 388 minorsep = strchr(nodename, ':'); 389 390 if (unitsep == NULL) { 391 devctl_release((devctl_hdl_t)dcp); 392 errno = EINVAL; 393 return (NULL); 394 } 395 396 /* 397 * copy the nodename and unit address 398 */ 399 if (((dcp->nodename = malloc(MAXNAMELEN)) == NULL) || 400 ((dcp->unitaddr = malloc(MAXNAMELEN)) == NULL)) { 401 devctl_release((devctl_hdl_t)dcp); 402 errno = ENOMEM; 403 return (NULL); 404 } 405 *unitsep = '\0'; 406 if (minorsep != NULL) 407 *minorsep = '\0'; 408 (void) snprintf(dcp->nodename, MAXNAMELEN, "%s", nodename); 409 (void) snprintf(dcp->unitaddr, MAXNAMELEN, "%s", unitsep+1); 410 411 /* 412 * construct the name of the ioctl device 413 */ 414 if (strlcat(iocpath, devctl_minorname, MAXPATHLEN) >= 415 MAXPATHLEN) { 416 devctl_release((devctl_hdl_t)dcp); 417 errno = EINVAL; 418 return (NULL); 419 } 420 break; 421 422 default: 423 devctl_release((devctl_hdl_t)dcp); 424 errno = EINVAL; 425 return (NULL); 426 } 427 428 if (_libdevice_debug) 429 (void) printf("dc_mkhndl: iocpath %s ", iocpath); 430 431 /* 432 * verify the devctl or ap device exists and is a 433 * character device interface. 434 */ 435 if (stat(iocpath, &sb) == 0) { 436 if ((sb.st_mode & S_IFMT) != S_IFCHR) { 437 if (_libdevice_debug) 438 (void) printf(" - not character device\n"); 439 errno = ENODEV; 440 devctl_release((devctl_hdl_t)dcp); 441 return (NULL); 442 } 443 } else { 444 /* 445 * return failure with errno value set by stat 446 */ 447 if (_libdevice_debug) 448 (void) printf(" - stat failed\n"); 449 devctl_release((devctl_hdl_t)dcp); 450 return (NULL); 451 } 452 453 /* 454 * if this was a new device, dup the parents handle, otherwise 455 * just open the device. 456 */ 457 if (type == DEVCTL_CLONE) 458 dcp->fd = dup(DCP(pc)->fd); 459 else 460 dcp->fd = open(iocpath, oflags); 461 462 if (dcp->fd == -1) { 463 if (_libdevice_debug) 464 (void) printf(" - open/dup failed %d\n", errno); 465 /* 466 * leave errno as set by open/dup 467 */ 468 devctl_release((devctl_hdl_t)dcp); 469 return (NULL); 470 } 471 472 if (_libdevice_debug) 473 (void) printf(" - open success\n"); 474 475 return ((devctl_hdl_t)dcp); 476 } 477 478 /* 479 * Power up component 0, to level MAXPWR, via a pm_raise_power() call 480 */ 481 int 482 devctl_pm_raisepower(devctl_hdl_t dcp) 483 { 484 int rv; 485 486 if (dcp == NULL || (DCP(dcp)->hdltype != DEVCTL_PM_DEV && 487 DCP(dcp)->hdltype != DEVCTL_PM_BUS)) { 488 errno = EINVAL; 489 return (-1); 490 } 491 492 rv = dc_cmd(DEVCTL_PM_RAISE_PWR, 0, DCP(dcp), NULL, NULL); 493 494 if (_libdevice_debug) 495 (void) printf("devctl_pm_raisepower: %d\n", rv); 496 497 return (rv); 498 } 499 500 /* 501 * Power up component 0, to level MAXPWR, via a power_has_changed() call 502 */ 503 int 504 devctl_pm_changepowerhigh(devctl_hdl_t dcp) 505 { 506 int rv; 507 508 if (dcp == NULL || (DCP(dcp)->hdltype != DEVCTL_PM_DEV && 509 DCP(dcp)->hdltype != DEVCTL_PM_BUS)) { 510 errno = EINVAL; 511 return (-1); 512 } 513 514 rv = dc_cmd(DEVCTL_PM_CHANGE_PWR_HIGH, 0, DCP(dcp), NULL, NULL); 515 516 if (_libdevice_debug) 517 (void) printf("devctl_pm_changepowerhigh: %d\n", rv); 518 519 return (rv); 520 } 521 522 /* 523 * Power down component 0, to level 0, via a pm_change_power() call 524 */ 525 int 526 devctl_pm_changepowerlow(devctl_hdl_t dcp) 527 { 528 int rv; 529 530 if (dcp == NULL || (DCP(dcp)->hdltype != DEVCTL_PM_DEV && 531 DCP(dcp)->hdltype != DEVCTL_PM_BUS)) { 532 errno = EINVAL; 533 return (-1); 534 } 535 536 rv = dc_cmd(DEVCTL_PM_CHANGE_PWR_LOW, 0, DCP(dcp), NULL, NULL); 537 538 if (_libdevice_debug) 539 (void) printf("devctl_pm_changepowerlow: %d\n", rv); 540 541 return (rv); 542 } 543 544 /* 545 * mark component 0 idle 546 */ 547 int 548 devctl_pm_idlecomponent(devctl_hdl_t dcp) 549 { 550 int rv; 551 552 if (dcp == NULL || (DCP(dcp)->hdltype != DEVCTL_PM_DEV && 553 DCP(dcp)->hdltype != DEVCTL_PM_BUS)) { 554 errno = EINVAL; 555 return (-1); 556 } 557 558 rv = dc_cmd(DEVCTL_PM_IDLE_COMP, 0, DCP(dcp), NULL, NULL); 559 560 if (_libdevice_debug) 561 (void) printf("devctl_pm_idlecomponent: %d\n", rv); 562 563 return (rv); 564 } 565 566 /* 567 * mark component 0 busy 568 */ 569 int 570 devctl_pm_busycomponent(devctl_hdl_t dcp) 571 { 572 int rv; 573 574 if (dcp == NULL || (DCP(dcp)->hdltype != DEVCTL_PM_DEV && 575 DCP(dcp)->hdltype != DEVCTL_PM_BUS)) { 576 errno = EINVAL; 577 return (-1); 578 } 579 580 rv = dc_cmd(DEVCTL_PM_BUSY_COMP, 0, DCP(dcp), NULL, NULL); 581 582 if (_libdevice_debug) 583 (void) printf("devctl_pm_busycomponent: %d\n", rv); 584 585 return (rv); 586 } 587 588 /* 589 * test pm busy state 590 */ 591 int 592 devctl_pm_testbusy(devctl_hdl_t dcp, uint_t *busystate) 593 { 594 int rv; 595 uint_t busy_state = 0; 596 597 if (busystate == NULL) { 598 errno = EINVAL; 599 return (-1); 600 } 601 602 if (dcp == NULL || (DCP(dcp)->hdltype != DEVCTL_PM_DEV && 603 DCP(dcp)->hdltype != DEVCTL_PM_BUS)) { 604 errno = EINVAL; 605 return (-1); 606 } 607 608 rv = dc_cmd(DEVCTL_PM_BUSY_COMP_TEST, 0, DCP(dcp), NULL, 609 (void *)&busy_state); 610 611 if (rv == -1) 612 *busystate = 0; 613 else 614 *busystate = busy_state; 615 616 if (_libdevice_debug) 617 (void) printf("devctl_pm_bus_testbusy: rv %d busystate %x\n", 618 rv, *busystate); 619 620 return (rv); 621 } 622 623 /* 624 * set flag to fail DDI_SUSPEND 625 */ 626 int 627 devctl_pm_failsuspend(devctl_hdl_t dcp) 628 { 629 int rv; 630 631 if (dcp == NULL || (DCP(dcp)->hdltype != DEVCTL_PM_DEV && 632 DCP(dcp)->hdltype != DEVCTL_PM_BUS)) { 633 errno = EINVAL; 634 return (-1); 635 } 636 637 rv = dc_cmd(DEVCTL_PM_FAIL_SUSPEND, 0, DCP(dcp), NULL, NULL); 638 639 if (_libdevice_debug) 640 (void) printf("devctl_pm_failsuspend: %d\n", rv); 641 return (rv); 642 } 643 644 int 645 devctl_pm_bus_teststrict(devctl_hdl_t dcp, uint_t *strict) 646 { 647 int rv; 648 uint_t strict_state; 649 650 if (strict == NULL) { 651 errno = EINVAL; 652 return (-1); 653 } 654 655 if (dcp == NULL || (DCP(dcp)->hdltype != DEVCTL_PM_BUS)) { 656 errno = EINVAL; 657 return (-1); 658 } 659 660 rv = dc_cmd(DEVCTL_PM_BUS_STRICT_TEST, 0, DCP(dcp), NULL, 661 (void *)&strict_state); 662 663 if (rv == -1) 664 *strict = 0; 665 else 666 *strict = strict_state; 667 668 if (_libdevice_debug) 669 (void) printf("devctl_pm_bus_teststrict: rv %d strict %x\n", 670 rv, *strict); 671 672 return (rv); 673 } 674 675 /* 676 * issue prom_printf() call 677 */ 678 int 679 devctl_pm_device_promprintf(devctl_hdl_t dcp) 680 { 681 int rv; 682 683 if (dcp == NULL || (DCP(dcp)->hdltype != DEVCTL_PM_DEV && 684 DCP(dcp)->hdltype != DEVCTL_PM_BUS)) { 685 errno = EINVAL; 686 return (-1); 687 } 688 689 rv = dc_cmd(DEVCTL_PM_PROM_PRINTF, 0, DCP(dcp), NULL, NULL); 690 691 if (_libdevice_debug) 692 (void) printf("devctl_pm_device_promprintf: %d\n", rv); 693 return (rv); 694 } 695 696 /* 697 * set flag to power up the device via 698 * pm_power_has_changed() calls vs. 699 * pm_raise_power(), during DDI_RESUME 700 */ 701 int 702 devctl_pm_device_changeonresume(devctl_hdl_t dcp) 703 { 704 int rv; 705 706 if (dcp == NULL || (DCP(dcp)->hdltype != DEVCTL_PM_DEV && 707 DCP(dcp)->hdltype != DEVCTL_PM_BUS)) { 708 errno = EINVAL; 709 return (-1); 710 } 711 712 rv = dc_cmd(DEVCTL_PM_PWR_HAS_CHANGED_ON_RESUME, 0, 713 DCP(dcp), NULL, NULL); 714 715 if (_libdevice_debug) 716 (void) printf("devctl_pm_device_changeonresume: %d\n", rv); 717 return (rv); 718 } 719 720 /* 721 * issue DEVCTL_PM_NO_LOWER_POWER to clear the LOWER_POWER_FLAG 722 * flag: pm_lower_power() will not be called on device detach 723 */ 724 int 725 devctl_pm_device_no_lower_power(devctl_hdl_t dcp) 726 { 727 int rv; 728 729 if (dcp == NULL || DCP(dcp)->hdltype != DEVCTL_PM_DEV) { 730 errno = EINVAL; 731 return (-1); 732 } 733 734 rv = dc_cmd(DEVCTL_PM_NO_LOWER_POWER, 0, DCP(dcp), NULL, NULL); 735 736 if (_libdevice_debug) 737 (void) printf("devctl_pm_device_no_lower_power: %d\n", rv); 738 return (rv); 739 } 740 741 /* 742 * issue DEVCTL_PM_BUS_NO_INVOL ioctl to set the NO_INVOL_FLAG 743 * flag: parent driver will mark itself idle twice in 744 * DDI_CTLOPS_DETACH(POST) 745 */ 746 int 747 devctl_pm_bus_no_invol(devctl_hdl_t dcp) 748 { 749 int rv; 750 751 if (dcp == NULL || DCP(dcp)->hdltype != DEVCTL_PM_BUS) { 752 errno = EINVAL; 753 return (-1); 754 } 755 756 rv = dc_cmd(DEVCTL_PM_BUS_NO_INVOL, 0, DCP(dcp), NULL, NULL); 757 758 if (_libdevice_debug) 759 (void) printf("devctl_pm_bus_no_invol: %d\n", rv); 760 return (rv); 761 } 762 763 /* 764 * Place the device ONLINE 765 */ 766 int 767 devctl_device_online(devctl_hdl_t dcp) 768 { 769 int rv; 770 771 if (dcp == NULL || DCP(dcp)->hdltype != DEVCTL_DEVICE) { 772 errno = EINVAL; 773 return (-1); 774 } 775 776 rv = dc_cmd(DEVCTL_DEVICE_ONLINE, 0, DCP(dcp), NULL, NULL); 777 778 if (_libdevice_debug) 779 (void) printf("devctl_device_online: %d\n", rv); 780 781 return (rv); 782 } 783 784 /* 785 * take device OFFLINE 786 */ 787 int 788 devctl_device_offline(devctl_hdl_t dcp) 789 { 790 int rv; 791 792 if (dcp == NULL || DCP(dcp)->hdltype != DEVCTL_DEVICE) { 793 errno = EINVAL; 794 return (-1); 795 } 796 797 rv = dc_cmd(DEVCTL_DEVICE_OFFLINE, 0, DCP(dcp), NULL, NULL); 798 799 if (_libdevice_debug) 800 (void) printf("devctl_device_offline: %d\n", rv); 801 802 return (rv); 803 } 804 805 /* 806 * take the device OFFLINE and remove its dev_info node 807 */ 808 int 809 devctl_device_remove(devctl_hdl_t dcp) 810 { 811 int rv; 812 813 if (dcp == NULL || DCP(dcp)->hdltype != DEVCTL_DEVICE) { 814 errno = EINVAL; 815 return (-1); 816 } 817 818 rv = dc_cmd(DEVCTL_DEVICE_REMOVE, 0, DCP(dcp), NULL, NULL); 819 820 if (_libdevice_debug) 821 (void) printf("devctl_device_remove: %d\n", rv); 822 823 return (rv); 824 } 825 826 827 /* 828 * QUIESCE the bus 829 */ 830 int 831 devctl_bus_quiesce(devctl_hdl_t dcp) 832 { 833 int rv; 834 835 rv = dc_cmd(DEVCTL_BUS_QUIESCE, 0, DCP(dcp), NULL, NULL); 836 837 if (_libdevice_debug) 838 (void) printf("devctl_bus_quiesce: %d\n", rv); 839 840 return (rv); 841 } 842 843 int 844 devctl_bus_unquiesce(devctl_hdl_t dcp) 845 { 846 int rv; 847 848 rv = dc_cmd(DEVCTL_BUS_UNQUIESCE, 0, DCP(dcp), NULL, NULL); 849 850 if (_libdevice_debug) 851 (void) printf("devctl_bus_unquiesce: %d\n", rv); 852 853 return (rv); 854 } 855 856 int 857 devctl_bus_reset(devctl_hdl_t dcp) 858 { 859 int rv; 860 861 rv = dc_cmd(DEVCTL_BUS_RESET, 0, DCP(dcp), NULL, NULL); 862 863 if (_libdevice_debug) 864 (void) printf("devctl_bus_reset: %d\n", rv); 865 866 return (rv); 867 } 868 869 int 870 devctl_bus_resetall(devctl_hdl_t dcp) 871 { 872 int rv; 873 874 rv = dc_cmd(DEVCTL_BUS_RESETALL, 0, DCP(dcp), NULL, NULL); 875 876 if (_libdevice_debug) 877 (void) printf("devctl_bus_resetall: %d\n", rv); 878 879 return (rv); 880 } 881 882 int 883 devctl_device_reset(devctl_hdl_t dcp) 884 { 885 int rv; 886 887 rv = dc_cmd(DEVCTL_DEVICE_RESET, 0, DCP(dcp), NULL, NULL); 888 889 if (_libdevice_debug) 890 (void) printf("devctl_device_reset: %d\n", rv); 891 892 return (rv); 893 } 894 895 int 896 devctl_device_getstate(devctl_hdl_t dcp, uint_t *devstate) 897 { 898 int rv; 899 uint_t device_state; 900 901 if (devstate == NULL) { 902 errno = EINVAL; 903 return (-1); 904 } 905 906 rv = dc_cmd(DEVCTL_DEVICE_GETSTATE, 0, DCP(dcp), NULL, 907 (void *)&device_state); 908 909 if (rv == -1) 910 *devstate = 0; 911 else 912 *devstate = device_state; 913 914 if (_libdevice_debug) 915 (void) printf("devctl_device_getstate: rv %d state %x\n", 916 rv, *devstate); 917 918 return (rv); 919 } 920 921 int 922 devctl_bus_getstate(devctl_hdl_t dcp, uint_t *devstate) 923 { 924 int rv; 925 uint_t device_state; 926 927 if (devstate == NULL) { 928 errno = EINVAL; 929 return (-1); 930 } 931 932 rv = dc_cmd(DEVCTL_BUS_GETSTATE, 0, DCP(dcp), NULL, 933 (void *)&device_state); 934 935 if (rv == -1) 936 *devstate = 0; 937 else 938 *devstate = device_state; 939 940 if (_libdevice_debug) 941 (void) printf("devctl_bus_getstate: rv %d, state %x\n", 942 rv, *devstate); 943 944 return (rv); 945 } 946 947 int 948 devctl_bus_configure(devctl_hdl_t dcp) 949 { 950 int rv; 951 952 rv = dc_cmd(DEVCTL_BUS_CONFIGURE, 0, DCP(dcp), NULL, NULL); 953 954 if (_libdevice_debug) 955 (void) printf("devctl_bus_configure: %d\n", rv); 956 957 return (rv); 958 } 959 960 int 961 devctl_bus_unconfigure(devctl_hdl_t dcp) 962 { 963 int rv; 964 965 rv = dc_cmd(DEVCTL_BUS_UNCONFIGURE, 0, DCP(dcp), NULL, NULL); 966 967 if (_libdevice_debug) 968 (void) printf("devctl_bus_unconfigure: %d\n", rv); 969 970 return (rv); 971 } 972 973 /* 974 * devctl_bus_dev_create() - create a new child device 975 * Attempt to construct and attach a new child device below a 976 * bus nexus (dcp). The device is defined using the devctl_ddef_*() 977 * routines to specify the set of bus-specific properties required 978 * to initalize and attach the device. 979 */ 980 int 981 devctl_bus_dev_create(devctl_hdl_t dcp, devctl_ddef_t ddef_hdl, 982 uint_t flags, devctl_hdl_t *new_dcp) 983 { 984 char devname[MAXNAMELEN]; 985 char devpath[MAXPATHLEN]; 986 int rv = 0; 987 988 if (dcp == NULL || ddef_hdl == NULL) { 989 errno = EINVAL; 990 return (-1); 991 } 992 993 (void) memset(devname, 0, sizeof (devname)); 994 rv = dc_cmd(DEVCTL_BUS_DEV_CREATE, flags, DCP(dcp), 995 (nvlist_t *)ddef_hdl, devname); 996 997 /* 998 * construct a device handle for the new device 999 */ 1000 if ((rv == 0) && (new_dcp != NULL)) { 1001 char *minorname, *lastslash; 1002 1003 (void) memset(devpath, 0, sizeof (devpath)); 1004 (void) strcat(devpath, DCP(dcp)->opath); 1005 1006 /* 1007 * Take the pathname of the parent device, chop off 1008 * any minor name info, and append the name@addr of 1009 * the new child device. 1010 * Call dc_mkhndl() with this constructed path and 1011 * the CLONE handle type to create a new handle which 1012 * references the new child device. 1013 */ 1014 lastslash = strrchr(devpath, '/'); 1015 if (*(lastslash + 1) == '\0') { 1016 *lastslash = '\0'; 1017 } else { 1018 if ((minorname = strchr(lastslash, ':')) != NULL) 1019 *minorname = '\0'; 1020 } 1021 (void) strcat(devpath, "/"); 1022 (void) strlcat(devpath, devname, MAXPATHLEN); 1023 *new_dcp = dc_mkhndl(DEVCTL_CLONE, devpath, 0, dcp); 1024 if (*new_dcp == NULL) 1025 rv = -1; 1026 } 1027 1028 return (rv); 1029 } 1030 1031 int 1032 devctl_ap_connect(devctl_hdl_t dcp, nvlist_t *ap_data) 1033 { 1034 int rv; 1035 1036 rv = dc_cmd(DEVCTL_AP_CONNECT, 0, DCP(dcp), ap_data, NULL); 1037 1038 if (_libdevice_debug) 1039 (void) printf("devctl_ap_connect: %d\n", rv); 1040 1041 return (rv); 1042 } 1043 1044 int 1045 devctl_ap_disconnect(devctl_hdl_t dcp, nvlist_t *ap_data) 1046 { 1047 int rv; 1048 1049 rv = dc_cmd(DEVCTL_AP_DISCONNECT, 0, DCP(dcp), ap_data, NULL); 1050 1051 if (_libdevice_debug) 1052 (void) printf("devctl_ap_disconnect: %d\n", rv); 1053 1054 return (rv); 1055 } 1056 1057 int 1058 devctl_ap_insert(devctl_hdl_t dcp, nvlist_t *ap_data) 1059 { 1060 int rv; 1061 1062 rv = dc_cmd(DEVCTL_AP_INSERT, 0, DCP(dcp), ap_data, NULL); 1063 1064 if (_libdevice_debug) 1065 (void) printf("devctl_ap_insert: %d\n", rv); 1066 1067 return (rv); 1068 } 1069 1070 int 1071 devctl_ap_remove(devctl_hdl_t dcp, nvlist_t *ap_data) 1072 { 1073 int rv; 1074 1075 rv = dc_cmd(DEVCTL_AP_REMOVE, 0, DCP(dcp), ap_data, NULL); 1076 1077 if (_libdevice_debug) 1078 (void) printf("devctl_ap_remove: %d\n", rv); 1079 1080 return (rv); 1081 } 1082 1083 int 1084 devctl_ap_configure(devctl_hdl_t dcp, nvlist_t *ap_data) 1085 { 1086 int rv; 1087 1088 rv = dc_cmd(DEVCTL_AP_CONFIGURE, 0, DCP(dcp), ap_data, NULL); 1089 1090 if (_libdevice_debug) 1091 (void) printf("devctl_ap_configure: %d\n", rv); 1092 1093 return (rv); 1094 } 1095 1096 int 1097 devctl_ap_unconfigure(devctl_hdl_t dcp, nvlist_t *ap_data) 1098 { 1099 int rv; 1100 1101 rv = dc_cmd(DEVCTL_AP_UNCONFIGURE, 0, DCP(dcp), ap_data, NULL); 1102 1103 if (_libdevice_debug) 1104 (void) printf("devctl_ap_unconfigure: %d\n", rv); 1105 1106 return (rv); 1107 } 1108 1109 int 1110 devctl_ap_getstate(devctl_hdl_t dcp, nvlist_t *ap_data, 1111 devctl_ap_state_t *apstate) 1112 { 1113 int rv; 1114 devctl_ap_state_t ap_state; 1115 1116 rv = dc_cmd(DEVCTL_AP_GETSTATE, 0, DCP(dcp), ap_data, 1117 (void *)&ap_state); 1118 1119 if (rv == -1) 1120 (void) memset(apstate, 0, sizeof (struct devctl_ap_state)); 1121 else 1122 *apstate = ap_state; 1123 1124 if (_libdevice_debug) 1125 (void) printf("devctl_ap_getstate: %d\n", rv); 1126 1127 return (rv); 1128 } 1129 1130 /* 1131 * Allocate a device 'definition' handle, in reality a list of 1132 * nvpair data. 1133 */ 1134 /* ARGSUSED */ 1135 devctl_ddef_t 1136 devctl_ddef_alloc(char *nodename, int flags) 1137 { 1138 1139 nvlist_t *nvlp; 1140 1141 if ((nodename == NULL) || *nodename == '\0') { 1142 errno = EINVAL; 1143 return (NULL); 1144 } 1145 1146 /* 1147 * allocate nvlist structure which is returned as an 1148 * opaque handle to the caller. If this fails, return 1149 * NULL with errno left set to the value 1150 */ 1151 if (nvlist_alloc(&nvlp, NV_UNIQUE_NAME_TYPE, 0) != 0) { 1152 errno = ENOMEM; 1153 return (NULL); 1154 } 1155 1156 /* 1157 * add the nodename of the new device to the list 1158 */ 1159 if (nvlist_add_string(nvlp, DC_DEVI_NODENAME, nodename) != 0) { 1160 nvlist_free(nvlp); 1161 errno = ENOMEM; 1162 return (NULL); 1163 } 1164 1165 if (_libdevice_debug) 1166 (void) printf("devctl_ddef_alloc: node %s nvp %p\n", nodename, 1167 (void *)nvlp); 1168 1169 return ((devctl_ddef_t)nvlp); 1170 } 1171 1172 /* 1173 * free the definition handle 1174 */ 1175 void 1176 devctl_ddef_free(devctl_ddef_t ddef_hdl) 1177 { 1178 if (_libdevice_debug) 1179 (void) printf("devctl_ddef_free: nvp %p\n", (void *)ddef_hdl); 1180 1181 if (ddef_hdl != NULL) { 1182 nvlist_free((nvlist_t *)ddef_hdl); 1183 } 1184 } 1185 1186 /* 1187 * define an integer property 1188 */ 1189 int 1190 devctl_ddef_int(devctl_ddef_t ddef_hdl, char *name, int32_t value) 1191 { 1192 1193 int rv; 1194 1195 if (ddef_hdl == NULL || name == NULL || *name == '\0') { 1196 errno = EINVAL; 1197 return (-1); 1198 } 1199 1200 rv = nvlist_add_int32((nvlist_t *)ddef_hdl, name, value); 1201 1202 if (_libdevice_debug) 1203 (void) printf("devctl_ddef_int: rv %d nvp %p name %s val %d\n", 1204 rv, (void *)ddef_hdl, name, value); 1205 1206 return (rv); 1207 } 1208 1209 /* 1210 * define an integer array property 1211 */ 1212 int 1213 devctl_ddef_int_array(devctl_ddef_t ddef_hdl, char *name, int nelements, 1214 int32_t *value) 1215 { 1216 int rv, i; 1217 1218 if (ddef_hdl == NULL || name == NULL || *name == '\0') { 1219 errno = EINVAL; 1220 return (-1); 1221 } 1222 1223 rv = nvlist_add_int32_array((nvlist_t *)ddef_hdl, name, value, 1224 nelements); 1225 1226 if (_libdevice_debug) { 1227 (void) printf("devctl_ddef_int_array: rv %d nvp %p name %s: ", 1228 rv, (void *)ddef_hdl, name); 1229 for (i = 0; i < nelements; i++) 1230 (void) printf("0x%x ", value[i]); 1231 (void) printf("\n"); 1232 } 1233 1234 return (rv); 1235 } 1236 1237 /* 1238 * define a string property 1239 */ 1240 int 1241 devctl_ddef_string(devctl_ddef_t ddef_hdl, char *name, char *value) 1242 { 1243 int rv; 1244 1245 if (ddef_hdl == NULL || name == NULL || *name == '\0') { 1246 errno = EINVAL; 1247 return (-1); 1248 } 1249 1250 rv = nvlist_add_string((nvlist_t *)ddef_hdl, name, value); 1251 1252 if (_libdevice_debug) 1253 (void) printf("devctl_ddef_string: rv %d nvp %p %s=\"%s\"\n", 1254 rv, (void *)ddef_hdl, name, value); 1255 1256 return (rv); 1257 } 1258 1259 /* 1260 * define a string array property 1261 */ 1262 int 1263 devctl_ddef_string_array(devctl_ddef_t ddef_hdl, char *name, int nelements, 1264 char **value) 1265 { 1266 int rv, i; 1267 1268 if (ddef_hdl == NULL || name == NULL || *name == '\0') { 1269 errno = EINVAL; 1270 return (-1); 1271 } 1272 1273 rv = nvlist_add_string_array((nvlist_t *)ddef_hdl, name, 1274 value, nelements); 1275 1276 if (_libdevice_debug) { 1277 (void) printf("devctl_ddef_string_array: rv %d nvp %p " 1278 "name %s:\n", rv, (void *)ddef_hdl, name); 1279 for (i = 0; i < nelements; i++) 1280 (void) printf("\t%d: \"%s\"\n", i, value[i]); 1281 } 1282 return (rv); 1283 } 1284 1285 /* 1286 * define a byte array property 1287 */ 1288 int 1289 devctl_ddef_byte_array(devctl_ddef_t ddef_hdl, char *name, int nelements, 1290 uchar_t *value) 1291 { 1292 int rv; 1293 1294 if (ddef_hdl == NULL || name == NULL || *name == '\0') { 1295 errno = EINVAL; 1296 return (-1); 1297 } 1298 1299 rv = nvlist_add_byte_array((nvlist_t *)ddef_hdl, name, value, 1300 nelements); 1301 1302 return (rv); 1303 } 1304 1305 /* 1306 * return the pathname which was used to acquire the handle 1307 */ 1308 char * 1309 devctl_get_pathname(devctl_hdl_t dcp, char *pathbuf, size_t bufsz) 1310 { 1311 if (dcp == NULL || pathbuf == NULL || bufsz == 0) { 1312 errno = EINVAL; 1313 return (NULL); 1314 } 1315 1316 (void) snprintf(pathbuf, bufsz, "%s", DCP(dcp)->opath); 1317 return (pathbuf); 1318 } 1319 1320 1321 /* 1322 * execute the IOCTL request 1323 */ 1324 static int 1325 dc_cmd(uint_t cmd, uint_t flags, struct devctl_hdl *dcp, nvlist_t *ulp, 1326 void *retinfo) 1327 { 1328 struct devctl_iocdata iocdata; 1329 int rv = 0; 1330 1331 if (_libdevice_debug) 1332 (void) printf("dc_cmd: %x dcp %p ulp %p flags %x rv %p\n", cmd, 1333 (void *)dcp, (void *)ulp, flags, retinfo); 1334 1335 if ((dcp == NULL) || (DCP(dcp)->fd == -1)) { 1336 errno = EINVAL; 1337 return (-1); 1338 } 1339 1340 (void) memset(&iocdata, 0, sizeof (struct devctl_iocdata)); 1341 1342 /* 1343 * if there was any user supplied data in the form of a nvlist, 1344 * pack the list prior to copyin. 1345 */ 1346 if (ulp != NULL) { 1347 if (rv = nvlist_pack(ulp, (char **)&iocdata.nvl_user, 1348 &iocdata.nvl_usersz, NV_ENCODE_NATIVE, 0)) { 1349 /* 1350 * exit with errno set by nvlist_pack() 1351 */ 1352 goto exit; 1353 } 1354 } else { 1355 iocdata.nvl_user = NULL; 1356 iocdata.nvl_usersz = 0; 1357 } 1358 1359 /* 1360 * finish initalizing the request and execute the IOCTL 1361 */ 1362 iocdata.cmd = cmd; 1363 iocdata.flags = flags; 1364 iocdata.c_nodename = dcp->nodename; 1365 iocdata.c_unitaddr = dcp->unitaddr; 1366 iocdata.cpyout_buf = retinfo; 1367 rv = ioctl(dcp->fd, cmd, &iocdata); 1368 if (rv < 0 && _libdevice_debug) { 1369 (void) printf("dc_cmd: exited with rv %d, errno(%d):%s\n", 1370 rv, errno, strerror(errno)); 1371 } 1372 1373 exit: 1374 if (iocdata.nvl_user != NULL) 1375 free(iocdata.nvl_user); 1376 1377 return (rv); 1378 } 1379