xref: /onnv-gate/usr/src/uts/sun4u/io/ppm_xgsubr.c (revision 4667:2cb417b1d90c)
1*4667Smh27603 /*
2*4667Smh27603  * CDDL HEADER START
3*4667Smh27603  *
4*4667Smh27603  * The contents of this file are subject to the terms of the
5*4667Smh27603  * Common Development and Distribution License (the "License").
6*4667Smh27603  * You may not use this file except in compliance with the License.
7*4667Smh27603  *
8*4667Smh27603  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*4667Smh27603  * or http://www.opensolaris.org/os/licensing.
10*4667Smh27603  * See the License for the specific language governing permissions
11*4667Smh27603  * and limitations under the License.
12*4667Smh27603  *
13*4667Smh27603  * When distributing Covered Code, include this CDDL HEADER in each
14*4667Smh27603  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*4667Smh27603  * If applicable, add the following below this CDDL HEADER, with the
16*4667Smh27603  * fields enclosed by brackets "[]" replaced with your own identifying
17*4667Smh27603  * information: Portions Copyright [yyyy] [name of copyright owner]
18*4667Smh27603  *
19*4667Smh27603  * CDDL HEADER END
20*4667Smh27603  */
21*4667Smh27603 /*
22*4667Smh27603  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23*4667Smh27603  * Use is subject to license terms.
24*4667Smh27603  */
25*4667Smh27603 
26*4667Smh27603 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27*4667Smh27603 
28*4667Smh27603 
29*4667Smh27603 /*
30*4667Smh27603  * common code for ppm drivers
31*4667Smh27603  */
32*4667Smh27603 #include <sys/modctl.h>
33*4667Smh27603 #include <sys/ddi.h>
34*4667Smh27603 #include <sys/sunddi.h>
35*4667Smh27603 #include <sys/ddi_impldefs.h>
36*4667Smh27603 #include <sys/ppmvar.h>
37*4667Smh27603 #include <sys/ppmio.h>
38*4667Smh27603 #include <sys/epm.h>
39*4667Smh27603 #include <sys/open.h>
40*4667Smh27603 #include <sys/file.h>
41*4667Smh27603 #include <sys/policy.h>
42*4667Smh27603 
43*4667Smh27603 
44*4667Smh27603 #ifdef DEBUG
45*4667Smh27603 uint_t	ppm_debug = 0;
46*4667Smh27603 #endif
47*4667Smh27603 
48*4667Smh27603 int	ppm_inst = -1;
49*4667Smh27603 char	*ppm_prefix;
50*4667Smh27603 void	*ppm_statep;
51*4667Smh27603 
52*4667Smh27603 
53*4667Smh27603 /*
54*4667Smh27603  * common module _init
55*4667Smh27603  */
56*4667Smh27603 int
57*4667Smh27603 ppm_init(struct modlinkage *mlp, size_t size, char *prefix)
58*4667Smh27603 {
59*4667Smh27603 #ifdef DEBUG
60*4667Smh27603 	char *str = "ppm_init";
61*4667Smh27603 #endif
62*4667Smh27603 	int error;
63*4667Smh27603 
64*4667Smh27603 	ppm_prefix = prefix;
65*4667Smh27603 
66*4667Smh27603 	error = ddi_soft_state_init(&ppm_statep, size, 1);
67*4667Smh27603 	DPRINTF(D_INIT, ("%s: ss init %d\n", str, error));
68*4667Smh27603 	if (error != DDI_SUCCESS)
69*4667Smh27603 		return (error);
70*4667Smh27603 
71*4667Smh27603 	if (error = mod_install(mlp))
72*4667Smh27603 		ddi_soft_state_fini(&ppm_statep);
73*4667Smh27603 	DPRINTF(D_INIT, ("%s: mod_install %d\n", str, error));
74*4667Smh27603 
75*4667Smh27603 	return (error);
76*4667Smh27603 }
77*4667Smh27603 
78*4667Smh27603 
79*4667Smh27603 /* ARGSUSED */
80*4667Smh27603 int
81*4667Smh27603 ppm_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
82*4667Smh27603 {
83*4667Smh27603 	struct ppm_unit *overlay;
84*4667Smh27603 	int rval;
85*4667Smh27603 
86*4667Smh27603 	if (ppm_inst == -1)
87*4667Smh27603 		return (DDI_FAILURE);
88*4667Smh27603 
89*4667Smh27603 	switch (cmd) {
90*4667Smh27603 	case DDI_INFO_DEVT2DEVINFO:
91*4667Smh27603 		if (overlay = ddi_get_soft_state(ppm_statep, ppm_inst)) {
92*4667Smh27603 			*resultp = overlay->dip;
93*4667Smh27603 			rval = DDI_SUCCESS;
94*4667Smh27603 		} else
95*4667Smh27603 			rval = DDI_FAILURE;
96*4667Smh27603 		return (rval);
97*4667Smh27603 
98*4667Smh27603 	case DDI_INFO_DEVT2INSTANCE:
99*4667Smh27603 		*resultp = (void *)(uintptr_t)ppm_inst;
100*4667Smh27603 		return (DDI_SUCCESS);
101*4667Smh27603 
102*4667Smh27603 	default:
103*4667Smh27603 		return (DDI_FAILURE);
104*4667Smh27603 	}
105*4667Smh27603 }
106*4667Smh27603 
107*4667Smh27603 
108*4667Smh27603 /* ARGSUSED */
109*4667Smh27603 int
110*4667Smh27603 ppm_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
111*4667Smh27603 {
112*4667Smh27603 	if (otyp != OTYP_CHR)
113*4667Smh27603 		return (EINVAL);
114*4667Smh27603 	DPRINTF(D_OPEN, ("ppm_open: \"%s\", devp 0x%p, flag 0x%x, otyp %d\n",
115*4667Smh27603 	    ppm_prefix, devp, flag, otyp));
116*4667Smh27603 	return (0);
117*4667Smh27603 }
118*4667Smh27603 
119*4667Smh27603 
120*4667Smh27603 /* ARGSUSED */
121*4667Smh27603 int
122*4667Smh27603 ppm_close(dev_t dev, int flag, int otyp, cred_t *credp)
123*4667Smh27603 {
124*4667Smh27603 	DPRINTF(D_CLOSE, ("ppm_close: \"%s\", dev 0x%lx, flag 0x%x, otyp %d\n",
125*4667Smh27603 	    ppm_prefix, dev, flag, otyp));
126*4667Smh27603 	return (DDI_SUCCESS);
127*4667Smh27603 }
128*4667Smh27603 
129*4667Smh27603 
130*4667Smh27603 /*
131*4667Smh27603  * lookup arrays of strings from configuration data (XXppm.conf)
132*4667Smh27603  */
133*4667Smh27603 static int
134*4667Smh27603 ppm_get_confdata(struct ppm_cdata **cdp, dev_info_t *dip)
135*4667Smh27603 {
136*4667Smh27603 	struct ppm_cdata *cinfo;
137*4667Smh27603 	int err;
138*4667Smh27603 
139*4667Smh27603 	for (; (cinfo = *cdp) != NULL; cdp++) {
140*4667Smh27603 		err = ddi_prop_lookup_string_array(
141*4667Smh27603 		    DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
142*4667Smh27603 		    cinfo->name, &cinfo->strings, &cinfo->cnt);
143*4667Smh27603 		if (err != DDI_PROP_SUCCESS) {
144*4667Smh27603 			DPRINTF(D_ERROR,
145*4667Smh27603 			    ("ppm_get_confdata: no %s found\n", cinfo->name));
146*4667Smh27603 			break;
147*4667Smh27603 		}
148*4667Smh27603 	}
149*4667Smh27603 	return (err);
150*4667Smh27603 }
151*4667Smh27603 
152*4667Smh27603 
153*4667Smh27603 /*
154*4667Smh27603  * free allocated ddi prop strings, and free
155*4667Smh27603  * ppm_db_t lists where there's an error.
156*4667Smh27603  */
157*4667Smh27603 static int
158*4667Smh27603 ppm_attach_err(struct ppm_cdata **cdp, int err)
159*4667Smh27603 {
160*4667Smh27603 	ppm_domain_t **dompp;
161*4667Smh27603 	ppm_db_t *db, *tmp;
162*4667Smh27603 
163*4667Smh27603 	if (cdp) {
164*4667Smh27603 		for (; *cdp; cdp++) {
165*4667Smh27603 			if ((*cdp)->strings) {
166*4667Smh27603 				ddi_prop_free((*cdp)->strings);
167*4667Smh27603 				(*cdp)->strings = NULL;
168*4667Smh27603 			}
169*4667Smh27603 		}
170*4667Smh27603 	}
171*4667Smh27603 
172*4667Smh27603 	if (err != DDI_SUCCESS) {
173*4667Smh27603 		for (dompp = ppm_domains; *dompp; dompp++) {
174*4667Smh27603 			for (db = (*dompp)->conflist; (tmp = db) != NULL; ) {
175*4667Smh27603 				db = db->next;
176*4667Smh27603 				kmem_free(tmp->name, strlen(tmp->name) + 1);
177*4667Smh27603 				kmem_free(tmp, sizeof (*tmp));
178*4667Smh27603 			}
179*4667Smh27603 			(*dompp)->conflist = NULL;
180*4667Smh27603 		}
181*4667Smh27603 		err = DDI_FAILURE;
182*4667Smh27603 	}
183*4667Smh27603 
184*4667Smh27603 	return (err);
185*4667Smh27603 }
186*4667Smh27603 
187*4667Smh27603 
188*4667Smh27603 ppm_domain_t *
189*4667Smh27603 ppm_lookup_domain(char *dname)
190*4667Smh27603 {
191*4667Smh27603 	ppm_domain_t **dompp;
192*4667Smh27603 
193*4667Smh27603 	for (dompp = ppm_domains; *dompp; dompp++)
194*4667Smh27603 		if (strcmp(dname, (*dompp)->name) == 0)
195*4667Smh27603 			break;
196*4667Smh27603 	return (*dompp);
197*4667Smh27603 }
198*4667Smh27603 
199*4667Smh27603 
200*4667Smh27603 /*
201*4667Smh27603  * create a ppm-private database from parsed .conf data; we start with
202*4667Smh27603  * two string arrays (device pathnames and domain names) and treat them
203*4667Smh27603  * as matched pairs where device[N] is part of domain[N]
204*4667Smh27603  */
205*4667Smh27603 int
206*4667Smh27603 ppm_create_db(dev_info_t *dip)
207*4667Smh27603 {
208*4667Smh27603 #ifdef DEBUG
209*4667Smh27603 	char *str = "ppm_create_db";
210*4667Smh27603 #endif
211*4667Smh27603 	struct ppm_cdata devdata, domdata, *cdata[3];
212*4667Smh27603 	ppm_domain_t *domp;
213*4667Smh27603 	ppm_db_t *new;
214*4667Smh27603 	char **dev_namep, **dom_namep;
215*4667Smh27603 	char *wild;
216*4667Smh27603 	int err;
217*4667Smh27603 
218*4667Smh27603 	bzero(&devdata, sizeof (devdata));
219*4667Smh27603 	bzero(&domdata, sizeof (domdata));
220*4667Smh27603 	devdata.name = "ppm-devices";
221*4667Smh27603 	domdata.name = "ppm-domains";
222*4667Smh27603 	cdata[0] = &devdata;
223*4667Smh27603 	cdata[1] = &domdata;
224*4667Smh27603 	cdata[2] = NULL;
225*4667Smh27603 	if (err = ppm_get_confdata(cdata, dip))
226*4667Smh27603 		return (ppm_attach_err(cdata, err));
227*4667Smh27603 	else if (devdata.cnt != domdata.cnt) {
228*4667Smh27603 		DPRINTF(D_ERROR,
229*4667Smh27603 		    ("%s: %sppm.conf has a mismatched number of %s and %s\n",
230*4667Smh27603 		    str, ppm_prefix, devdata.name, domdata.name));
231*4667Smh27603 		return (ppm_attach_err(cdata, DDI_FAILURE));
232*4667Smh27603 	}
233*4667Smh27603 
234*4667Smh27603 	/*
235*4667Smh27603 	 * loop through device/domain pairs and build
236*4667Smh27603 	 * a linked list of devices within known domains
237*4667Smh27603 	 */
238*4667Smh27603 	for (dev_namep = devdata.strings, dom_namep = domdata.strings;
239*4667Smh27603 	    *dev_namep; dev_namep++, dom_namep++) {
240*4667Smh27603 		domp = ppm_lookup_domain(*dom_namep);
241*4667Smh27603 		if (domp == NULL) {
242*4667Smh27603 			DPRINTF(D_ERROR, ("%s: invalid domain \"%s\" for "
243*4667Smh27603 			    "device \"%s\"\n", str, *dom_namep, *dev_namep));
244*4667Smh27603 			return (ppm_attach_err(cdata, DDI_FAILURE));
245*4667Smh27603 		}
246*4667Smh27603 
247*4667Smh27603 		/*
248*4667Smh27603 		 * allocate a new ppm db entry and link it to
249*4667Smh27603 		 * the front of conflist within this domain
250*4667Smh27603 		 */
251*4667Smh27603 		new = kmem_zalloc(sizeof (*new), KM_SLEEP);
252*4667Smh27603 		new->name = kmem_zalloc(strlen(*dev_namep) + 1, KM_SLEEP);
253*4667Smh27603 		(void) strcpy(new->name, *dev_namep);
254*4667Smh27603 		new->next = domp->conflist;
255*4667Smh27603 		domp->conflist = new;
256*4667Smh27603 
257*4667Smh27603 		/*
258*4667Smh27603 		 * when the device name contains a wildcard,
259*4667Smh27603 		 * save the length of the preceding string
260*4667Smh27603 		 */
261*4667Smh27603 		if (wild = strchr(new->name, '*'))
262*4667Smh27603 			new->plen = (wild - new->name);
263*4667Smh27603 		DPRINTF(D_CREATEDB, ("%s: \"%s\", added \"%s\"\n",
264*4667Smh27603 		    str, domp->name, new->name));
265*4667Smh27603 	}
266*4667Smh27603 
267*4667Smh27603 	return (ppm_attach_err(cdata, DDI_SUCCESS));
268*4667Smh27603 }
269*4667Smh27603 
270*4667Smh27603 
271*4667Smh27603 /*
272*4667Smh27603  * scan conf devices within each domain for a matching device name
273*4667Smh27603  */
274*4667Smh27603 ppm_domain_t *
275*4667Smh27603 ppm_lookup_dev(dev_info_t *dip)
276*4667Smh27603 {
277*4667Smh27603 	char path[MAXNAMELEN];
278*4667Smh27603 	ppm_domain_t **dompp;
279*4667Smh27603 	ppm_db_t *dbp;
280*4667Smh27603 
281*4667Smh27603 	(void) ddi_pathname(dip, path);
282*4667Smh27603 	for (dompp = ppm_domains; *dompp; dompp++) {
283*4667Smh27603 		for (dbp = (*dompp)->conflist; dbp; dbp = dbp->next) {
284*4667Smh27603 			if (dbp->plen == 0) {
285*4667Smh27603 				if (strcmp(path, dbp->name) == 0)
286*4667Smh27603 					return (*dompp);
287*4667Smh27603 			} else if (strncmp(path, dbp->name, dbp->plen) == 0)
288*4667Smh27603 				return (*dompp);
289*4667Smh27603 		}
290*4667Smh27603 	}
291*4667Smh27603 
292*4667Smh27603 	return (NULL);
293*4667Smh27603 }
294*4667Smh27603 
295*4667Smh27603 
296*4667Smh27603 /*
297*4667Smh27603  * returns 1 (claimed), 0 (not claimed)
298*4667Smh27603  */
299*4667Smh27603 int
300*4667Smh27603 ppm_claim_dev(dev_info_t *dip)
301*4667Smh27603 {
302*4667Smh27603 	ppm_domain_t *domp;
303*4667Smh27603 
304*4667Smh27603 	domp = ppm_lookup_dev(dip);
305*4667Smh27603 
306*4667Smh27603 #ifdef DEBUG
307*4667Smh27603 	if (domp) {
308*4667Smh27603 		char path[MAXNAMELEN];
309*4667Smh27603 		DPRINTF(D_CLAIMDEV,
310*4667Smh27603 		    ("ppm_claim_dev: \"%s\", matched \"%s\"\n",
311*4667Smh27603 		    domp->name, ddi_pathname(dip, path)));
312*4667Smh27603 	}
313*4667Smh27603 
314*4667Smh27603 #endif
315*4667Smh27603 
316*4667Smh27603 	return (domp != NULL);
317*4667Smh27603 }
318*4667Smh27603 
319*4667Smh27603 
320*4667Smh27603 /*
321*4667Smh27603  * create/init a new ppm device and link into the domain
322*4667Smh27603  */
323*4667Smh27603 ppm_dev_t *
324*4667Smh27603 ppm_add_dev(dev_info_t *dip, ppm_domain_t *domp)
325*4667Smh27603 {
326*4667Smh27603 	char path[MAXNAMELEN];
327*4667Smh27603 	ppm_dev_t *new = NULL;
328*4667Smh27603 	int cmpt;
329*4667Smh27603 
330*4667Smh27603 	ASSERT(MUTEX_HELD(&domp->lock));
331*4667Smh27603 	(void) ddi_pathname(dip, path);
332*4667Smh27603 	/*
333*4667Smh27603 	 * For devs which have exported "pm-components" we want to create
334*4667Smh27603 	 * a data structure for each component.  When a driver chooses not
335*4667Smh27603 	 * to export the prop we treat its device as having a single
336*4667Smh27603 	 * component and build a structure for it anyway.  All other ppm
337*4667Smh27603 	 * logic will act as if this device were always up and can thus
338*4667Smh27603 	 * make correct decisions about it in relation to other devices
339*4667Smh27603 	 * in its domain.
340*4667Smh27603 	 */
341*4667Smh27603 	for (cmpt = PM_GET_PM_INFO(dip) ? PM_NUMCMPTS(dip) : 1; cmpt--; ) {
342*4667Smh27603 		new = kmem_zalloc(sizeof (*new), KM_SLEEP);
343*4667Smh27603 		new->path = kmem_zalloc(strlen(path) + 1, KM_SLEEP);
344*4667Smh27603 		(void) strcpy(new->path, path);
345*4667Smh27603 		new->domp = domp;
346*4667Smh27603 		new->dip = dip;
347*4667Smh27603 		new->cmpt = cmpt;
348*4667Smh27603 		if (ppmf.dev_init)
349*4667Smh27603 			(*ppmf.dev_init)(new);
350*4667Smh27603 		new->next = domp->devlist;
351*4667Smh27603 		domp->devlist = new;
352*4667Smh27603 		DPRINTF(D_ADDDEV,
353*4667Smh27603 		    ("ppm_add_dev: \"%s\", \"%s\", ppm_dev 0x%p\n",
354*4667Smh27603 		    new->path, domp->name, new));
355*4667Smh27603 	}
356*4667Smh27603 
357*4667Smh27603 	ASSERT(new != NULL);
358*4667Smh27603 	/*
359*4667Smh27603 	 * devi_pm_ppm_private should be set only after all
360*4667Smh27603 	 * ppm_dev s related to all components have been
361*4667Smh27603 	 * initialized and domain's pwr_cnt is incremented
362*4667Smh27603 	 * for each of them.
363*4667Smh27603 	 */
364*4667Smh27603 	PPM_SET_PRIVATE(dip, new);
365*4667Smh27603 
366*4667Smh27603 	return (new);
367*4667Smh27603 }
368*4667Smh27603 
369*4667Smh27603 
370*4667Smh27603 /*
371*4667Smh27603  * returns an existing or newly created ppm device reference
372*4667Smh27603  */
373*4667Smh27603 ppm_dev_t *
374*4667Smh27603 ppm_get_dev(dev_info_t *dip, ppm_domain_t *domp)
375*4667Smh27603 {
376*4667Smh27603 	ppm_dev_t *pdp;
377*4667Smh27603 
378*4667Smh27603 	mutex_enter(&domp->lock);
379*4667Smh27603 	pdp = PPM_GET_PRIVATE(dip);
380*4667Smh27603 	if (pdp == NULL)
381*4667Smh27603 		pdp = ppm_add_dev(dip, domp);
382*4667Smh27603 	mutex_exit(&domp->lock);
383*4667Smh27603 
384*4667Smh27603 	return (pdp);
385*4667Smh27603 }
386*4667Smh27603 
387*4667Smh27603 
388*4667Smh27603 /*
389*4667Smh27603  * scan a domain's device list and remove those with .dip
390*4667Smh27603  * matching the arg *dip; we need to scan the entire list
391*4667Smh27603  * for the case of devices with multiple components
392*4667Smh27603  */
393*4667Smh27603 void
394*4667Smh27603 ppm_rem_dev(dev_info_t *dip)
395*4667Smh27603 {
396*4667Smh27603 	ppm_dev_t *pdp, **devpp;
397*4667Smh27603 	ppm_domain_t *domp;
398*4667Smh27603 
399*4667Smh27603 	pdp = PPM_GET_PRIVATE(dip);
400*4667Smh27603 	ASSERT(pdp);
401*4667Smh27603 	domp = pdp->domp;
402*4667Smh27603 	ASSERT(domp);
403*4667Smh27603 
404*4667Smh27603 	mutex_enter(&domp->lock);
405*4667Smh27603 	for (devpp = &domp->devlist; (pdp = *devpp) != NULL; ) {
406*4667Smh27603 		if (pdp->dip != dip) {
407*4667Smh27603 			devpp = &pdp->next;
408*4667Smh27603 			continue;
409*4667Smh27603 		}
410*4667Smh27603 
411*4667Smh27603 		DPRINTF(D_REMDEV, ("ppm_rem_dev: path \"%s\", ppm_dev 0x%p\n",
412*4667Smh27603 		    pdp->path, pdp));
413*4667Smh27603 
414*4667Smh27603 		PPM_SET_PRIVATE(dip, NULL);
415*4667Smh27603 		*devpp = pdp->next;
416*4667Smh27603 		if (ppmf.dev_fini)
417*4667Smh27603 			(*ppmf.dev_fini)(pdp);
418*4667Smh27603 		kmem_free(pdp->path, strlen(pdp->path) + 1);
419*4667Smh27603 		kmem_free(pdp, sizeof (*pdp));
420*4667Smh27603 	}
421*4667Smh27603 	mutex_exit(&domp->lock);
422*4667Smh27603 }
423*4667Smh27603 
424*4667Smh27603 
425*4667Smh27603 /* ARGSUSED */
426*4667Smh27603 int
427*4667Smh27603 ppm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
428*4667Smh27603     cred_t *cred_p, int *rval_p)
429*4667Smh27603 {
430*4667Smh27603 #ifdef DEBUG
431*4667Smh27603 	char *str = "ppm_ioctl";
432*4667Smh27603 	char *rwfmt = "%s: mode error: 0x%x is missing %s perm, cmd 0x%x\n";
433*4667Smh27603 	char *iofmt = "%s: copy%s error, arg 0x%p\n";
434*4667Smh27603 #endif
435*4667Smh27603 	ppmreq_t req;
436*4667Smh27603 	uint8_t level;
437*4667Smh27603 
438*4667Smh27603 	DPRINTF(D_IOCTL, ("%s: dev 0x%lx, cmd 0x%x, arg 0x%lx, mode 0x%x\n",
439*4667Smh27603 	    str, dev, cmd, arg, mode));
440*4667Smh27603 
441*4667Smh27603 	if (ddi_copyin((caddr_t)arg, &req, sizeof (req), mode)) {
442*4667Smh27603 		DPRINTF(D_IOCTL, (iofmt, str, "in", arg));
443*4667Smh27603 		return (EFAULT);
444*4667Smh27603 	}
445*4667Smh27603 
446*4667Smh27603 	/*
447*4667Smh27603 	 * Currently, only PPM_INTERNAL_DEVICE_POWER device type is supported
448*4667Smh27603 	 */
449*4667Smh27603 	if (req.ppmdev != PPM_INTERNAL_DEVICE_POWER) {
450*4667Smh27603 		DPRINTF(D_IOCTL, ("%s: unrecognized device type %d\n",
451*4667Smh27603 		    str, req.ppmdev));
452*4667Smh27603 		return (EINVAL);
453*4667Smh27603 	}
454*4667Smh27603 
455*4667Smh27603 	switch (cmd) {
456*4667Smh27603 	case PPMIOCSET:
457*4667Smh27603 		if (secpolicy_power_mgmt(cred_p) != 0) {
458*4667Smh27603 			DPRINTF(D_IOCTL, ("%s: bad cred for cmd 0x%x\n",
459*4667Smh27603 			    str, cmd));
460*4667Smh27603 			return (EPERM);
461*4667Smh27603 		} else if (!(mode & FWRITE)) {
462*4667Smh27603 			DPRINTF(D_IOCTL, (rwfmt, str, mode, "write"));
463*4667Smh27603 			return (EPERM);
464*4667Smh27603 		}
465*4667Smh27603 
466*4667Smh27603 		level = req.ppmop.idev_power.level;
467*4667Smh27603 		if ((level != PPM_IDEV_POWER_ON) &&
468*4667Smh27603 		    (level != PPM_IDEV_POWER_OFF)) {
469*4667Smh27603 			DPRINTF(D_IOCTL,
470*4667Smh27603 			    ("%s: invalid power level %d, cmd 0x%x\n",
471*4667Smh27603 			    str, level, cmd));
472*4667Smh27603 			return (EINVAL);
473*4667Smh27603 		}
474*4667Smh27603 		if (ppmf.iocset == NULL)
475*4667Smh27603 			return (ENOTSUP);
476*4667Smh27603 		(*ppmf.iocset)(level);
477*4667Smh27603 		break;
478*4667Smh27603 
479*4667Smh27603 	case PPMIOCGET:
480*4667Smh27603 		if (!(mode & FREAD)) {
481*4667Smh27603 			DPRINTF(D_IOCTL, (rwfmt, str, mode, "read"));
482*4667Smh27603 			return (EPERM);
483*4667Smh27603 		}
484*4667Smh27603 
485*4667Smh27603 		if (ppmf.iocget == NULL)
486*4667Smh27603 			return (ENOTSUP);
487*4667Smh27603 		req.ppmop.idev_power.level = (*ppmf.iocget)();
488*4667Smh27603 		if (ddi_copyout((const void *)&req, (void *)arg,
489*4667Smh27603 		    sizeof (req), mode)) {
490*4667Smh27603 			DPRINTF(D_ERROR, (iofmt, str, "out", arg));
491*4667Smh27603 			return (EFAULT);
492*4667Smh27603 		}
493*4667Smh27603 		break;
494*4667Smh27603 
495*4667Smh27603 	default:
496*4667Smh27603 		DPRINTF(D_IOCTL, ("%s: unrecognized cmd 0x%x\n", str, cmd));
497*4667Smh27603 		return (EINVAL);
498*4667Smh27603 	}
499*4667Smh27603 
500*4667Smh27603 	return (0);
501*4667Smh27603 }
502*4667Smh27603 
503*4667Smh27603 
504*4667Smh27603 #ifdef DEBUG
505*4667Smh27603 #define	FLINTSTR(flags, sym) { flags, sym, #sym }
506*4667Smh27603 #define	PMR_UNKNOWN -1
507*4667Smh27603 /*
508*4667Smh27603  * convert a ctlop integer to a char string.  this helps printing
509*4667Smh27603  * meaningful info when cltops are received from the pm framework.
510*4667Smh27603  * since some ctlops are so frequent, we use mask to limit output:
511*4667Smh27603  * a valid string is returned when ctlop is found and when
512*4667Smh27603  * (cmd.flags & mask) is true; otherwise NULL is returned.
513*4667Smh27603  */
514*4667Smh27603 char *
515*4667Smh27603 ppm_get_ctlstr(int ctlop, uint_t mask)
516*4667Smh27603 {
517*4667Smh27603 	struct ctlop_cmd {
518*4667Smh27603 		uint_t flags;
519*4667Smh27603 		int ctlop;
520*4667Smh27603 		char *str;
521*4667Smh27603 	};
522*4667Smh27603 
523*4667Smh27603 	struct ctlop_cmd *ccp;
524*4667Smh27603 	static struct ctlop_cmd cmds[] = {
525*4667Smh27603 		FLINTSTR(D_SETPWR, PMR_SET_POWER),
526*4667Smh27603 		FLINTSTR(D_CTLOPS2, PMR_SUSPEND),
527*4667Smh27603 		FLINTSTR(D_CTLOPS2, PMR_RESUME),
528*4667Smh27603 		FLINTSTR(D_CTLOPS2, PMR_PRE_SET_POWER),
529*4667Smh27603 		FLINTSTR(D_CTLOPS2, PMR_POST_SET_POWER),
530*4667Smh27603 		FLINTSTR(D_CTLOPS2, PMR_PPM_SET_POWER),
531*4667Smh27603 		FLINTSTR(0, PMR_PPM_ATTACH),
532*4667Smh27603 		FLINTSTR(0, PMR_PPM_DETACH),
533*4667Smh27603 		FLINTSTR(D_CTLOPS1, PMR_PPM_POWER_CHANGE_NOTIFY),
534*4667Smh27603 		FLINTSTR(D_CTLOPS1, PMR_REPORT_PMCAP),
535*4667Smh27603 		FLINTSTR(D_CTLOPS1, PMR_CHANGED_POWER),
536*4667Smh27603 		FLINTSTR(D_CTLOPS2, PMR_PPM_PRE_PROBE),
537*4667Smh27603 		FLINTSTR(D_CTLOPS2, PMR_PPM_POST_PROBE),
538*4667Smh27603 		FLINTSTR(D_CTLOPS2, PMR_PPM_PRE_ATTACH),
539*4667Smh27603 		FLINTSTR(D_CTLOPS2, PMR_PPM_POST_ATTACH),
540*4667Smh27603 		FLINTSTR(D_CTLOPS2, PMR_PPM_PRE_DETACH),
541*4667Smh27603 		FLINTSTR(D_CTLOPS2, PMR_PPM_POST_DETACH),
542*4667Smh27603 		FLINTSTR(D_CTLOPS1, PMR_PPM_UNMANAGE),
543*4667Smh27603 		FLINTSTR(D_CTLOPS2, PMR_PPM_PRE_RESUME),
544*4667Smh27603 		FLINTSTR(D_CTLOPS1, PMR_PPM_ALL_LOWEST),
545*4667Smh27603 		FLINTSTR(D_LOCKS, PMR_PPM_LOCK_POWER),
546*4667Smh27603 		FLINTSTR(D_LOCKS, PMR_PPM_UNLOCK_POWER),
547*4667Smh27603 		FLINTSTR(D_LOCKS, PMR_PPM_TRY_LOCK_POWER),
548*4667Smh27603 		FLINTSTR(D_CTLOPS1 | D_CTLOPS2, PMR_UNKNOWN),
549*4667Smh27603 	};
550*4667Smh27603 
551*4667Smh27603 	for (ccp = cmds; ccp->ctlop != PMR_UNKNOWN; ccp++)
552*4667Smh27603 		if (ctlop == ccp->ctlop)
553*4667Smh27603 			break;
554*4667Smh27603 
555*4667Smh27603 	if (ccp->flags & mask)
556*4667Smh27603 		return (ccp->str);
557*4667Smh27603 	return (NULL);
558*4667Smh27603 }
559*4667Smh27603 #endif
560