xref: /illumos-gate/usr/src/lib/libdevice/devctl.c (revision f012ee0c3db17469b492c2cf757226f3d7b1ebbc)
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
70 _libdevice_init()
71 {
72 	_libdevice_debug = getenv("LIBDEVICE_DEBUG") != NULL;
73 }
74 
75 /*
76  * release a devctl_hdl structure
77  */
78 void
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 *
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
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