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