xref: /onnv-gate/usr/src/uts/sun4u/io/ppm_xgsubr.c (revision 11311:639e7bc0b42f)
14667Smh27603 /*
24667Smh27603  * CDDL HEADER START
34667Smh27603  *
44667Smh27603  * The contents of this file are subject to the terms of the
54667Smh27603  * Common Development and Distribution License (the "License").
64667Smh27603  * You may not use this file except in compliance with the License.
74667Smh27603  *
84667Smh27603  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
94667Smh27603  * or http://www.opensolaris.org/os/licensing.
104667Smh27603  * See the License for the specific language governing permissions
114667Smh27603  * and limitations under the License.
124667Smh27603  *
134667Smh27603  * When distributing Covered Code, include this CDDL HEADER in each
144667Smh27603  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
154667Smh27603  * If applicable, add the following below this CDDL HEADER, with the
164667Smh27603  * fields enclosed by brackets "[]" replaced with your own identifying
174667Smh27603  * information: Portions Copyright [yyyy] [name of copyright owner]
184667Smh27603  *
194667Smh27603  * CDDL HEADER END
204667Smh27603  */
21*11311SSurya.Prakki@Sun.COM 
224667Smh27603 /*
23*11311SSurya.Prakki@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
244667Smh27603  * Use is subject to license terms.
254667Smh27603  */
264667Smh27603 
274667Smh27603 /*
284667Smh27603  * common code for ppm drivers
294667Smh27603  */
304667Smh27603 #include <sys/modctl.h>
314667Smh27603 #include <sys/ddi.h>
324667Smh27603 #include <sys/sunddi.h>
334667Smh27603 #include <sys/ddi_impldefs.h>
344667Smh27603 #include <sys/ppmvar.h>
354667Smh27603 #include <sys/ppmio.h>
364667Smh27603 #include <sys/epm.h>
374667Smh27603 #include <sys/open.h>
384667Smh27603 #include <sys/file.h>
394667Smh27603 #include <sys/policy.h>
404667Smh27603 
414667Smh27603 
424667Smh27603 #ifdef DEBUG
434667Smh27603 uint_t	ppm_debug = 0;
444667Smh27603 #endif
454667Smh27603 
464667Smh27603 int	ppm_inst = -1;
474667Smh27603 char	*ppm_prefix;
484667Smh27603 void	*ppm_statep;
494667Smh27603 
504667Smh27603 
514667Smh27603 /*
524667Smh27603  * common module _init
534667Smh27603  */
544667Smh27603 int
ppm_init(struct modlinkage * mlp,size_t size,char * prefix)554667Smh27603 ppm_init(struct modlinkage *mlp, size_t size, char *prefix)
564667Smh27603 {
574667Smh27603 #ifdef DEBUG
584667Smh27603 	char *str = "ppm_init";
594667Smh27603 #endif
604667Smh27603 	int error;
614667Smh27603 
624667Smh27603 	ppm_prefix = prefix;
634667Smh27603 
644667Smh27603 	error = ddi_soft_state_init(&ppm_statep, size, 1);
654667Smh27603 	DPRINTF(D_INIT, ("%s: ss init %d\n", str, error));
664667Smh27603 	if (error != DDI_SUCCESS)
674667Smh27603 		return (error);
684667Smh27603 
694667Smh27603 	if (error = mod_install(mlp))
704667Smh27603 		ddi_soft_state_fini(&ppm_statep);
714667Smh27603 	DPRINTF(D_INIT, ("%s: mod_install %d\n", str, error));
724667Smh27603 
734667Smh27603 	return (error);
744667Smh27603 }
754667Smh27603 
764667Smh27603 
774667Smh27603 /* ARGSUSED */
784667Smh27603 int
ppm_getinfo(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** resultp)794667Smh27603 ppm_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
804667Smh27603 {
814667Smh27603 	struct ppm_unit *overlay;
824667Smh27603 	int rval;
834667Smh27603 
844667Smh27603 	if (ppm_inst == -1)
854667Smh27603 		return (DDI_FAILURE);
864667Smh27603 
874667Smh27603 	switch (cmd) {
884667Smh27603 	case DDI_INFO_DEVT2DEVINFO:
894667Smh27603 		if (overlay = ddi_get_soft_state(ppm_statep, ppm_inst)) {
904667Smh27603 			*resultp = overlay->dip;
914667Smh27603 			rval = DDI_SUCCESS;
924667Smh27603 		} else
934667Smh27603 			rval = DDI_FAILURE;
944667Smh27603 		return (rval);
954667Smh27603 
964667Smh27603 	case DDI_INFO_DEVT2INSTANCE:
974667Smh27603 		*resultp = (void *)(uintptr_t)ppm_inst;
984667Smh27603 		return (DDI_SUCCESS);
994667Smh27603 
1004667Smh27603 	default:
1014667Smh27603 		return (DDI_FAILURE);
1024667Smh27603 	}
1034667Smh27603 }
1044667Smh27603 
1054667Smh27603 
1064667Smh27603 /* ARGSUSED */
1074667Smh27603 int
ppm_open(dev_t * devp,int flag,int otyp,cred_t * cred_p)1084667Smh27603 ppm_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
1094667Smh27603 {
1104667Smh27603 	if (otyp != OTYP_CHR)
1114667Smh27603 		return (EINVAL);
1124667Smh27603 	DPRINTF(D_OPEN, ("ppm_open: \"%s\", devp 0x%p, flag 0x%x, otyp %d\n",
113*11311SSurya.Prakki@Sun.COM 	    ppm_prefix, (void *)devp, flag, otyp));
1144667Smh27603 	return (0);
1154667Smh27603 }
1164667Smh27603 
1174667Smh27603 
1184667Smh27603 /* ARGSUSED */
1194667Smh27603 int
ppm_close(dev_t dev,int flag,int otyp,cred_t * credp)1204667Smh27603 ppm_close(dev_t dev, int flag, int otyp, cred_t *credp)
1214667Smh27603 {
1224667Smh27603 	DPRINTF(D_CLOSE, ("ppm_close: \"%s\", dev 0x%lx, flag 0x%x, otyp %d\n",
1234667Smh27603 	    ppm_prefix, dev, flag, otyp));
1244667Smh27603 	return (DDI_SUCCESS);
1254667Smh27603 }
1264667Smh27603 
1274667Smh27603 
1284667Smh27603 /*
1294667Smh27603  * lookup arrays of strings from configuration data (XXppm.conf)
1304667Smh27603  */
1314667Smh27603 static int
ppm_get_confdata(struct ppm_cdata ** cdp,dev_info_t * dip)1324667Smh27603 ppm_get_confdata(struct ppm_cdata **cdp, dev_info_t *dip)
1334667Smh27603 {
1344667Smh27603 	struct ppm_cdata *cinfo;
1354667Smh27603 	int err;
1364667Smh27603 
1374667Smh27603 	for (; (cinfo = *cdp) != NULL; cdp++) {
1384667Smh27603 		err = ddi_prop_lookup_string_array(
1394667Smh27603 		    DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
1404667Smh27603 		    cinfo->name, &cinfo->strings, &cinfo->cnt);
1414667Smh27603 		if (err != DDI_PROP_SUCCESS) {
1424667Smh27603 			DPRINTF(D_ERROR,
1434667Smh27603 			    ("ppm_get_confdata: no %s found\n", cinfo->name));
1444667Smh27603 			break;
1454667Smh27603 		}
1464667Smh27603 	}
1474667Smh27603 	return (err);
1484667Smh27603 }
1494667Smh27603 
1504667Smh27603 
1514667Smh27603 /*
1524667Smh27603  * free allocated ddi prop strings, and free
1534667Smh27603  * ppm_db_t lists where there's an error.
1544667Smh27603  */
1554667Smh27603 static int
ppm_attach_err(struct ppm_cdata ** cdp,int err)1564667Smh27603 ppm_attach_err(struct ppm_cdata **cdp, int err)
1574667Smh27603 {
1584667Smh27603 	ppm_domain_t **dompp;
1594667Smh27603 	ppm_db_t *db, *tmp;
1604667Smh27603 
1614667Smh27603 	if (cdp) {
1624667Smh27603 		for (; *cdp; cdp++) {
1634667Smh27603 			if ((*cdp)->strings) {
1644667Smh27603 				ddi_prop_free((*cdp)->strings);
1654667Smh27603 				(*cdp)->strings = NULL;
1664667Smh27603 			}
1674667Smh27603 		}
1684667Smh27603 	}
1694667Smh27603 
1704667Smh27603 	if (err != DDI_SUCCESS) {
1714667Smh27603 		for (dompp = ppm_domains; *dompp; dompp++) {
1724667Smh27603 			for (db = (*dompp)->conflist; (tmp = db) != NULL; ) {
1734667Smh27603 				db = db->next;
1744667Smh27603 				kmem_free(tmp->name, strlen(tmp->name) + 1);
1754667Smh27603 				kmem_free(tmp, sizeof (*tmp));
1764667Smh27603 			}
1774667Smh27603 			(*dompp)->conflist = NULL;
1784667Smh27603 		}
1794667Smh27603 		err = DDI_FAILURE;
1804667Smh27603 	}
1814667Smh27603 
1824667Smh27603 	return (err);
1834667Smh27603 }
1844667Smh27603 
1854667Smh27603 
1864667Smh27603 ppm_domain_t *
ppm_lookup_domain(char * dname)1874667Smh27603 ppm_lookup_domain(char *dname)
1884667Smh27603 {
1894667Smh27603 	ppm_domain_t **dompp;
1904667Smh27603 
1914667Smh27603 	for (dompp = ppm_domains; *dompp; dompp++)
1924667Smh27603 		if (strcmp(dname, (*dompp)->name) == 0)
1934667Smh27603 			break;
1944667Smh27603 	return (*dompp);
1954667Smh27603 }
1964667Smh27603 
1974667Smh27603 
1984667Smh27603 /*
1994667Smh27603  * create a ppm-private database from parsed .conf data; we start with
2004667Smh27603  * two string arrays (device pathnames and domain names) and treat them
2014667Smh27603  * as matched pairs where device[N] is part of domain[N]
2024667Smh27603  */
2034667Smh27603 int
ppm_create_db(dev_info_t * dip)2044667Smh27603 ppm_create_db(dev_info_t *dip)
2054667Smh27603 {
2064667Smh27603 #ifdef DEBUG
2074667Smh27603 	char *str = "ppm_create_db";
2084667Smh27603 #endif
2094667Smh27603 	struct ppm_cdata devdata, domdata, *cdata[3];
2104667Smh27603 	ppm_domain_t *domp;
2114667Smh27603 	ppm_db_t *new;
2124667Smh27603 	char **dev_namep, **dom_namep;
2134667Smh27603 	char *wild;
2144667Smh27603 	int err;
2154667Smh27603 
2164667Smh27603 	bzero(&devdata, sizeof (devdata));
2174667Smh27603 	bzero(&domdata, sizeof (domdata));
2184667Smh27603 	devdata.name = "ppm-devices";
2194667Smh27603 	domdata.name = "ppm-domains";
2204667Smh27603 	cdata[0] = &devdata;
2214667Smh27603 	cdata[1] = &domdata;
2224667Smh27603 	cdata[2] = NULL;
2234667Smh27603 	if (err = ppm_get_confdata(cdata, dip))
2244667Smh27603 		return (ppm_attach_err(cdata, err));
2254667Smh27603 	else if (devdata.cnt != domdata.cnt) {
2264667Smh27603 		DPRINTF(D_ERROR,
2274667Smh27603 		    ("%s: %sppm.conf has a mismatched number of %s and %s\n",
2284667Smh27603 		    str, ppm_prefix, devdata.name, domdata.name));
2294667Smh27603 		return (ppm_attach_err(cdata, DDI_FAILURE));
2304667Smh27603 	}
2314667Smh27603 
2324667Smh27603 	/*
2334667Smh27603 	 * loop through device/domain pairs and build
2344667Smh27603 	 * a linked list of devices within known domains
2354667Smh27603 	 */
2364667Smh27603 	for (dev_namep = devdata.strings, dom_namep = domdata.strings;
2374667Smh27603 	    *dev_namep; dev_namep++, dom_namep++) {
2384667Smh27603 		domp = ppm_lookup_domain(*dom_namep);
2394667Smh27603 		if (domp == NULL) {
2404667Smh27603 			DPRINTF(D_ERROR, ("%s: invalid domain \"%s\" for "
2414667Smh27603 			    "device \"%s\"\n", str, *dom_namep, *dev_namep));
2424667Smh27603 			return (ppm_attach_err(cdata, DDI_FAILURE));
2434667Smh27603 		}
2444667Smh27603 
2454667Smh27603 		/*
2464667Smh27603 		 * allocate a new ppm db entry and link it to
2474667Smh27603 		 * the front of conflist within this domain
2484667Smh27603 		 */
2494667Smh27603 		new = kmem_zalloc(sizeof (*new), KM_SLEEP);
2504667Smh27603 		new->name = kmem_zalloc(strlen(*dev_namep) + 1, KM_SLEEP);
2514667Smh27603 		(void) strcpy(new->name, *dev_namep);
2524667Smh27603 		new->next = domp->conflist;
2534667Smh27603 		domp->conflist = new;
2544667Smh27603 
2554667Smh27603 		/*
2564667Smh27603 		 * when the device name contains a wildcard,
2574667Smh27603 		 * save the length of the preceding string
2584667Smh27603 		 */
2594667Smh27603 		if (wild = strchr(new->name, '*'))
2604667Smh27603 			new->plen = (wild - new->name);
2614667Smh27603 		DPRINTF(D_CREATEDB, ("%s: \"%s\", added \"%s\"\n",
2624667Smh27603 		    str, domp->name, new->name));
2634667Smh27603 	}
2644667Smh27603 
2654667Smh27603 	return (ppm_attach_err(cdata, DDI_SUCCESS));
2664667Smh27603 }
2674667Smh27603 
2684667Smh27603 
2694667Smh27603 /*
2704667Smh27603  * scan conf devices within each domain for a matching device name
2714667Smh27603  */
2724667Smh27603 ppm_domain_t *
ppm_lookup_dev(dev_info_t * dip)2734667Smh27603 ppm_lookup_dev(dev_info_t *dip)
2744667Smh27603 {
2754667Smh27603 	char path[MAXNAMELEN];
2764667Smh27603 	ppm_domain_t **dompp;
2774667Smh27603 	ppm_db_t *dbp;
2784667Smh27603 
2794667Smh27603 	(void) ddi_pathname(dip, path);
2804667Smh27603 	for (dompp = ppm_domains; *dompp; dompp++) {
2814667Smh27603 		for (dbp = (*dompp)->conflist; dbp; dbp = dbp->next) {
2824667Smh27603 			if (dbp->plen == 0) {
2834667Smh27603 				if (strcmp(path, dbp->name) == 0)
2844667Smh27603 					return (*dompp);
2854667Smh27603 			} else if (strncmp(path, dbp->name, dbp->plen) == 0)
2864667Smh27603 				return (*dompp);
2874667Smh27603 		}
2884667Smh27603 	}
2894667Smh27603 
2904667Smh27603 	return (NULL);
2914667Smh27603 }
2924667Smh27603 
2934667Smh27603 
2944667Smh27603 /*
2954667Smh27603  * returns 1 (claimed), 0 (not claimed)
2964667Smh27603  */
2974667Smh27603 int
ppm_claim_dev(dev_info_t * dip)2984667Smh27603 ppm_claim_dev(dev_info_t *dip)
2994667Smh27603 {
3004667Smh27603 	ppm_domain_t *domp;
3014667Smh27603 
3024667Smh27603 	domp = ppm_lookup_dev(dip);
3034667Smh27603 
3044667Smh27603 #ifdef DEBUG
3054667Smh27603 	if (domp) {
3064667Smh27603 		char path[MAXNAMELEN];
3074667Smh27603 		DPRINTF(D_CLAIMDEV,
3084667Smh27603 		    ("ppm_claim_dev: \"%s\", matched \"%s\"\n",
3094667Smh27603 		    domp->name, ddi_pathname(dip, path)));
3104667Smh27603 	}
3114667Smh27603 
3124667Smh27603 #endif
3134667Smh27603 
3144667Smh27603 	return (domp != NULL);
3154667Smh27603 }
3164667Smh27603 
3174667Smh27603 
3184667Smh27603 /*
3194667Smh27603  * create/init a new ppm device and link into the domain
3204667Smh27603  */
3214667Smh27603 ppm_dev_t *
ppm_add_dev(dev_info_t * dip,ppm_domain_t * domp)3224667Smh27603 ppm_add_dev(dev_info_t *dip, ppm_domain_t *domp)
3234667Smh27603 {
3244667Smh27603 	char path[MAXNAMELEN];
3254667Smh27603 	ppm_dev_t *new = NULL;
3264667Smh27603 	int cmpt;
3274667Smh27603 
3284667Smh27603 	ASSERT(MUTEX_HELD(&domp->lock));
3294667Smh27603 	(void) ddi_pathname(dip, path);
3304667Smh27603 	/*
3314667Smh27603 	 * For devs which have exported "pm-components" we want to create
3324667Smh27603 	 * a data structure for each component.  When a driver chooses not
3334667Smh27603 	 * to export the prop we treat its device as having a single
3344667Smh27603 	 * component and build a structure for it anyway.  All other ppm
3354667Smh27603 	 * logic will act as if this device were always up and can thus
3364667Smh27603 	 * make correct decisions about it in relation to other devices
3374667Smh27603 	 * in its domain.
3384667Smh27603 	 */
3394667Smh27603 	for (cmpt = PM_GET_PM_INFO(dip) ? PM_NUMCMPTS(dip) : 1; cmpt--; ) {
3404667Smh27603 		new = kmem_zalloc(sizeof (*new), KM_SLEEP);
3414667Smh27603 		new->path = kmem_zalloc(strlen(path) + 1, KM_SLEEP);
3424667Smh27603 		(void) strcpy(new->path, path);
3434667Smh27603 		new->domp = domp;
3444667Smh27603 		new->dip = dip;
3454667Smh27603 		new->cmpt = cmpt;
3464667Smh27603 		if (ppmf.dev_init)
3474667Smh27603 			(*ppmf.dev_init)(new);
3484667Smh27603 		new->next = domp->devlist;
3494667Smh27603 		domp->devlist = new;
3504667Smh27603 		DPRINTF(D_ADDDEV,
3514667Smh27603 		    ("ppm_add_dev: \"%s\", \"%s\", ppm_dev 0x%p\n",
352*11311SSurya.Prakki@Sun.COM 		    new->path, domp->name, (void *)new));
3534667Smh27603 	}
3544667Smh27603 
3554667Smh27603 	ASSERT(new != NULL);
3564667Smh27603 	/*
3574667Smh27603 	 * devi_pm_ppm_private should be set only after all
3584667Smh27603 	 * ppm_dev s related to all components have been
3594667Smh27603 	 * initialized and domain's pwr_cnt is incremented
3604667Smh27603 	 * for each of them.
3614667Smh27603 	 */
3624667Smh27603 	PPM_SET_PRIVATE(dip, new);
3634667Smh27603 
3644667Smh27603 	return (new);
3654667Smh27603 }
3664667Smh27603 
3674667Smh27603 
3684667Smh27603 /*
3694667Smh27603  * returns an existing or newly created ppm device reference
3704667Smh27603  */
3714667Smh27603 ppm_dev_t *
ppm_get_dev(dev_info_t * dip,ppm_domain_t * domp)3724667Smh27603 ppm_get_dev(dev_info_t *dip, ppm_domain_t *domp)
3734667Smh27603 {
3744667Smh27603 	ppm_dev_t *pdp;
3754667Smh27603 
3764667Smh27603 	mutex_enter(&domp->lock);
3774667Smh27603 	pdp = PPM_GET_PRIVATE(dip);
3784667Smh27603 	if (pdp == NULL)
3794667Smh27603 		pdp = ppm_add_dev(dip, domp);
3804667Smh27603 	mutex_exit(&domp->lock);
3814667Smh27603 
3824667Smh27603 	return (pdp);
3834667Smh27603 }
3844667Smh27603 
3854667Smh27603 
3864667Smh27603 /*
3874667Smh27603  * scan a domain's device list and remove those with .dip
3884667Smh27603  * matching the arg *dip; we need to scan the entire list
3894667Smh27603  * for the case of devices with multiple components
3904667Smh27603  */
3914667Smh27603 void
ppm_rem_dev(dev_info_t * dip)3924667Smh27603 ppm_rem_dev(dev_info_t *dip)
3934667Smh27603 {
3944667Smh27603 	ppm_dev_t *pdp, **devpp;
3954667Smh27603 	ppm_domain_t *domp;
3964667Smh27603 
3974667Smh27603 	pdp = PPM_GET_PRIVATE(dip);
3984667Smh27603 	ASSERT(pdp);
3994667Smh27603 	domp = pdp->domp;
4004667Smh27603 	ASSERT(domp);
4014667Smh27603 
4024667Smh27603 	mutex_enter(&domp->lock);
4034667Smh27603 	for (devpp = &domp->devlist; (pdp = *devpp) != NULL; ) {
4044667Smh27603 		if (pdp->dip != dip) {
4054667Smh27603 			devpp = &pdp->next;
4064667Smh27603 			continue;
4074667Smh27603 		}
4084667Smh27603 
4094667Smh27603 		DPRINTF(D_REMDEV, ("ppm_rem_dev: path \"%s\", ppm_dev 0x%p\n",
410*11311SSurya.Prakki@Sun.COM 		    pdp->path, (void *)pdp));
4114667Smh27603 
4124667Smh27603 		PPM_SET_PRIVATE(dip, NULL);
4134667Smh27603 		*devpp = pdp->next;
4144667Smh27603 		if (ppmf.dev_fini)
4154667Smh27603 			(*ppmf.dev_fini)(pdp);
4164667Smh27603 		kmem_free(pdp->path, strlen(pdp->path) + 1);
4174667Smh27603 		kmem_free(pdp, sizeof (*pdp));
4184667Smh27603 	}
4194667Smh27603 	mutex_exit(&domp->lock);
4204667Smh27603 }
4214667Smh27603 
4224667Smh27603 
4234667Smh27603 /* ARGSUSED */
4244667Smh27603 int
ppm_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * cred_p,int * rval_p)4254667Smh27603 ppm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
4264667Smh27603     cred_t *cred_p, int *rval_p)
4274667Smh27603 {
4284667Smh27603 #ifdef DEBUG
4294667Smh27603 	char *str = "ppm_ioctl";
4304667Smh27603 	char *rwfmt = "%s: mode error: 0x%x is missing %s perm, cmd 0x%x\n";
4314667Smh27603 	char *iofmt = "%s: copy%s error, arg 0x%p\n";
4324667Smh27603 #endif
4334667Smh27603 	ppmreq_t req;
4344667Smh27603 	uint8_t level;
4354667Smh27603 
4364667Smh27603 	DPRINTF(D_IOCTL, ("%s: dev 0x%lx, cmd 0x%x, arg 0x%lx, mode 0x%x\n",
4374667Smh27603 	    str, dev, cmd, arg, mode));
4384667Smh27603 
4394667Smh27603 	if (ddi_copyin((caddr_t)arg, &req, sizeof (req), mode)) {
4404667Smh27603 		DPRINTF(D_IOCTL, (iofmt, str, "in", arg));
4414667Smh27603 		return (EFAULT);
4424667Smh27603 	}
4434667Smh27603 
4444667Smh27603 	/*
4454667Smh27603 	 * Currently, only PPM_INTERNAL_DEVICE_POWER device type is supported
4464667Smh27603 	 */
4474667Smh27603 	if (req.ppmdev != PPM_INTERNAL_DEVICE_POWER) {
4484667Smh27603 		DPRINTF(D_IOCTL, ("%s: unrecognized device type %d\n",
4494667Smh27603 		    str, req.ppmdev));
4504667Smh27603 		return (EINVAL);
4514667Smh27603 	}
4524667Smh27603 
4534667Smh27603 	switch (cmd) {
4544667Smh27603 	case PPMIOCSET:
4554667Smh27603 		if (secpolicy_power_mgmt(cred_p) != 0) {
4564667Smh27603 			DPRINTF(D_IOCTL, ("%s: bad cred for cmd 0x%x\n",
4574667Smh27603 			    str, cmd));
4584667Smh27603 			return (EPERM);
4594667Smh27603 		} else if (!(mode & FWRITE)) {
4604667Smh27603 			DPRINTF(D_IOCTL, (rwfmt, str, mode, "write"));
4614667Smh27603 			return (EPERM);
4624667Smh27603 		}
4634667Smh27603 
4644667Smh27603 		level = req.ppmop.idev_power.level;
4654667Smh27603 		if ((level != PPM_IDEV_POWER_ON) &&
4664667Smh27603 		    (level != PPM_IDEV_POWER_OFF)) {
4674667Smh27603 			DPRINTF(D_IOCTL,
4684667Smh27603 			    ("%s: invalid power level %d, cmd 0x%x\n",
4694667Smh27603 			    str, level, cmd));
4704667Smh27603 			return (EINVAL);
4714667Smh27603 		}
4724667Smh27603 		if (ppmf.iocset == NULL)
4734667Smh27603 			return (ENOTSUP);
4744667Smh27603 		(*ppmf.iocset)(level);
4754667Smh27603 		break;
4764667Smh27603 
4774667Smh27603 	case PPMIOCGET:
4784667Smh27603 		if (!(mode & FREAD)) {
4794667Smh27603 			DPRINTF(D_IOCTL, (rwfmt, str, mode, "read"));
4804667Smh27603 			return (EPERM);
4814667Smh27603 		}
4824667Smh27603 
4834667Smh27603 		if (ppmf.iocget == NULL)
4844667Smh27603 			return (ENOTSUP);
4854667Smh27603 		req.ppmop.idev_power.level = (*ppmf.iocget)();
4864667Smh27603 		if (ddi_copyout((const void *)&req, (void *)arg,
4874667Smh27603 		    sizeof (req), mode)) {
4884667Smh27603 			DPRINTF(D_ERROR, (iofmt, str, "out", arg));
4894667Smh27603 			return (EFAULT);
4904667Smh27603 		}
4914667Smh27603 		break;
4924667Smh27603 
4934667Smh27603 	default:
4944667Smh27603 		DPRINTF(D_IOCTL, ("%s: unrecognized cmd 0x%x\n", str, cmd));
4954667Smh27603 		return (EINVAL);
4964667Smh27603 	}
4974667Smh27603 
4984667Smh27603 	return (0);
4994667Smh27603 }
5004667Smh27603 
5014667Smh27603 
5024667Smh27603 #ifdef DEBUG
5034667Smh27603 #define	FLINTSTR(flags, sym) { flags, sym, #sym }
5044667Smh27603 #define	PMR_UNKNOWN -1
5054667Smh27603 /*
5064667Smh27603  * convert a ctlop integer to a char string.  this helps printing
5074667Smh27603  * meaningful info when cltops are received from the pm framework.
5084667Smh27603  * since some ctlops are so frequent, we use mask to limit output:
5094667Smh27603  * a valid string is returned when ctlop is found and when
5104667Smh27603  * (cmd.flags & mask) is true; otherwise NULL is returned.
5114667Smh27603  */
5124667Smh27603 char *
ppm_get_ctlstr(int ctlop,uint_t mask)5134667Smh27603 ppm_get_ctlstr(int ctlop, uint_t mask)
5144667Smh27603 {
5154667Smh27603 	struct ctlop_cmd {
5164667Smh27603 		uint_t flags;
5174667Smh27603 		int ctlop;
5184667Smh27603 		char *str;
5194667Smh27603 	};
5204667Smh27603 
5214667Smh27603 	struct ctlop_cmd *ccp;
5224667Smh27603 	static struct ctlop_cmd cmds[] = {
5234667Smh27603 		FLINTSTR(D_SETPWR, PMR_SET_POWER),
5244667Smh27603 		FLINTSTR(D_CTLOPS2, PMR_SUSPEND),
5254667Smh27603 		FLINTSTR(D_CTLOPS2, PMR_RESUME),
5264667Smh27603 		FLINTSTR(D_CTLOPS2, PMR_PRE_SET_POWER),
5274667Smh27603 		FLINTSTR(D_CTLOPS2, PMR_POST_SET_POWER),
5284667Smh27603 		FLINTSTR(D_CTLOPS2, PMR_PPM_SET_POWER),
5294667Smh27603 		FLINTSTR(0, PMR_PPM_ATTACH),
5304667Smh27603 		FLINTSTR(0, PMR_PPM_DETACH),
5314667Smh27603 		FLINTSTR(D_CTLOPS1, PMR_PPM_POWER_CHANGE_NOTIFY),
5324667Smh27603 		FLINTSTR(D_CTLOPS1, PMR_REPORT_PMCAP),
5334667Smh27603 		FLINTSTR(D_CTLOPS1, PMR_CHANGED_POWER),
5344667Smh27603 		FLINTSTR(D_CTLOPS2, PMR_PPM_PRE_PROBE),
5354667Smh27603 		FLINTSTR(D_CTLOPS2, PMR_PPM_POST_PROBE),
5364667Smh27603 		FLINTSTR(D_CTLOPS2, PMR_PPM_PRE_ATTACH),
5374667Smh27603 		FLINTSTR(D_CTLOPS2, PMR_PPM_POST_ATTACH),
5384667Smh27603 		FLINTSTR(D_CTLOPS2, PMR_PPM_PRE_DETACH),
5394667Smh27603 		FLINTSTR(D_CTLOPS2, PMR_PPM_POST_DETACH),
5404667Smh27603 		FLINTSTR(D_CTLOPS1, PMR_PPM_UNMANAGE),
5414667Smh27603 		FLINTSTR(D_CTLOPS2, PMR_PPM_PRE_RESUME),
5424667Smh27603 		FLINTSTR(D_CTLOPS1, PMR_PPM_ALL_LOWEST),
5434667Smh27603 		FLINTSTR(D_LOCKS, PMR_PPM_LOCK_POWER),
5444667Smh27603 		FLINTSTR(D_LOCKS, PMR_PPM_UNLOCK_POWER),
5454667Smh27603 		FLINTSTR(D_LOCKS, PMR_PPM_TRY_LOCK_POWER),
5464667Smh27603 		FLINTSTR(D_CTLOPS1 | D_CTLOPS2, PMR_UNKNOWN),
5474667Smh27603 	};
5484667Smh27603 
5494667Smh27603 	for (ccp = cmds; ccp->ctlop != PMR_UNKNOWN; ccp++)
5504667Smh27603 		if (ctlop == ccp->ctlop)
5514667Smh27603 			break;
5524667Smh27603 
5534667Smh27603 	if (ccp->flags & mask)
5544667Smh27603 		return (ccp->str);
5554667Smh27603 	return (NULL);
5564667Smh27603 }
5574667Smh27603 #endif
558