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
_libdevice_init()68 _libdevice_init()
69 {
70 _libdevice_debug = getenv("LIBDEVICE_DEBUG") != NULL;
71 }
72
73 /*
74 * release a devctl_hdl structure
75 */
76 void
devctl_release(devctl_hdl_t hdl)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
devctl_bus_acquire(char * devfs_path,uint_t flags)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
devctl_device_acquire(char * devfs_path,uint_t flags)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
devctl_ap_acquire(char * devfs_path,uint_t flags)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
devctl_pm_bus_acquire(char * devfs_path,uint_t flags)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
devctl_pm_dev_acquire(char * devfs_path,uint_t flags)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
dc_mkhndl(dc_type_t type,char * path,uint_t oflags,devctl_hdl_t pc)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
devctl_pm_raisepower(devctl_hdl_t dcp)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
devctl_pm_changepowerhigh(devctl_hdl_t dcp)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
devctl_pm_changepowerlow(devctl_hdl_t dcp)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
devctl_pm_idlecomponent(devctl_hdl_t dcp)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
devctl_pm_busycomponent(devctl_hdl_t dcp)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
devctl_pm_testbusy(devctl_hdl_t dcp,uint_t * busystate)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
devctl_pm_failsuspend(devctl_hdl_t dcp)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
devctl_pm_bus_teststrict(devctl_hdl_t dcp,uint_t * strict)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
devctl_pm_device_promprintf(devctl_hdl_t dcp)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
devctl_pm_device_changeonresume(devctl_hdl_t dcp)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
devctl_pm_device_no_lower_power(devctl_hdl_t dcp)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
devctl_pm_bus_no_invol(devctl_hdl_t dcp)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
devctl_device_online(devctl_hdl_t dcp)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
devctl_device_offline(devctl_hdl_t dcp)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
devctl_device_remove(devctl_hdl_t dcp)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
devctl_bus_quiesce(devctl_hdl_t dcp)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
devctl_bus_unquiesce(devctl_hdl_t dcp)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
devctl_bus_reset(devctl_hdl_t dcp)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
devctl_bus_resetall(devctl_hdl_t dcp)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
devctl_device_reset(devctl_hdl_t dcp)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
devctl_device_getstate(devctl_hdl_t dcp,uint_t * devstate)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
devctl_bus_getstate(devctl_hdl_t dcp,uint_t * devstate)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
devctl_bus_configure(devctl_hdl_t dcp)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
devctl_bus_unconfigure(devctl_hdl_t dcp)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
devctl_bus_dev_create(devctl_hdl_t dcp,devctl_ddef_t ddef_hdl,uint_t flags,devctl_hdl_t * new_dcp)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
devctl_ap_connect(devctl_hdl_t dcp,nvlist_t * ap_data)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
devctl_ap_disconnect(devctl_hdl_t dcp,nvlist_t * ap_data)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
devctl_ap_insert(devctl_hdl_t dcp,nvlist_t * ap_data)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
devctl_ap_remove(devctl_hdl_t dcp,nvlist_t * ap_data)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
devctl_ap_configure(devctl_hdl_t dcp,nvlist_t * ap_data)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
devctl_ap_unconfigure(devctl_hdl_t dcp,nvlist_t * ap_data)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
devctl_ap_getstate(devctl_hdl_t dcp,nvlist_t * ap_data,devctl_ap_state_t * apstate)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
devctl_ddef_alloc(char * nodename,int flags)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
devctl_ddef_free(devctl_ddef_t ddef_hdl)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
devctl_ddef_int(devctl_ddef_t ddef_hdl,char * name,int32_t value)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
devctl_ddef_int_array(devctl_ddef_t ddef_hdl,char * name,int nelements,int32_t * value)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
devctl_ddef_string(devctl_ddef_t ddef_hdl,char * name,char * value)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
devctl_ddef_string_array(devctl_ddef_t ddef_hdl,char * name,int nelements,char ** value)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
devctl_ddef_byte_array(devctl_ddef_t ddef_hdl,char * name,int nelements,uchar_t * value)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 *
devctl_get_pathname(devctl_hdl_t dcp,char * pathbuf,size_t bufsz)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
dc_cmd(uint_t cmd,uint_t flags,struct devctl_hdl * dcp,nvlist_t * ulp,void * retinfo)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