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