xref: /illumos-gate/usr/src/uts/common/io/gen_drv.c (revision 4703203d)
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 
30 /*
31  * generic character driver
32  */
33 #include <sys/types.h>
34 #include <sys/param.h>
35 #include <sys/errno.h>
36 #include <sys/uio.h>
37 #include <sys/buf.h>
38 #include <sys/modctl.h>
39 #include <sys/open.h>
40 #include <sys/kmem.h>
41 #include <sys/conf.h>
42 #include <sys/cmn_err.h>
43 #include <sys/stat.h>
44 #include <sys/ddi.h>
45 #include <sys/sunddi.h>
46 #include <sys/sunndi.h>
47 
48 
49 #define	NUMEVENTS 6
50 #define	COMPONENTS 2
51 #define	COMP_0_MAXPWR	3
52 #define	COMP_1_MAXPWR	2
53 #define	MINPWR		0
54 static int maxpwr[] = { COMP_0_MAXPWR, COMP_1_MAXPWR };
55 
56 /*
57  * The state for each generic device.
58  * NOTE: We save the node_type in the state structure. The node_type string
59  * (and not a copy) is stashed in a minor node by  ddi_create_minor_node(),
60  * so ddi_remove_minor_node() must occur prior to state free.
61  */
62 typedef struct dstate {
63 	uint_t		flag;
64 	dev_info_t	*dip;			/* my devinfo handle */
65 	char		*node_type;	/* stable node_type copy */
66 	ddi_callback_id_t gen_cb_ids[NUMEVENTS];
67 	kmutex_t	lock;
68 	char		*nodename;
69 	int		level[COMPONENTS];	/* pm level */
70 	int		busy[COMPONENTS];	/* busy state */
71 } dstate_t;
72 
73 
74 static void *dstates;
75 
76 static int gen_debug = 0;
77 
78 #ifdef DEBUG
79 #define	gen_debug gen_debug_on
80 static int gen_debug_on = 0;
81 #define	GEN_DEBUG(args) if (gen_debug) cmn_err args
82 #else
83 #define	GEN_DEBUG(args)
84 #endif
85 
86 extern void prom_printf(const char *fmt, ...);
87 
88 static int gen_open(dev_t *devp, int flag, int otyp, cred_t *cred);
89 static int gen_close(dev_t devp, int flag, int otyp, cred_t *cred);
90 static int gen_read(dev_t dev, struct uio *uiop, cred_t *credp);
91 static int gen_write(dev_t dev, struct uio *uiop, cred_t *credp);
92 static int gen_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
93     cred_t *credp, int *rvalp);
94 static int gen_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
95 static int gen_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
96 static void gen_event_cb(dev_info_t *dip, ddi_eventcookie_t cookie,
97 	void *arg, void *impl_data);
98 
99 static int gen_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
100     void **result);
101 static int gen_create_minor_nodes(dev_info_t *, struct dstate *);
102 static int gen_power(dev_info_t *, int, int);
103 
104 static struct cb_ops gen_cb_ops = {
105 	gen_open,			/* open */
106 	gen_close,			/* close */
107 	nodev,				/* strategy */
108 	nodev,				/* print */
109 	nodev,				/* dump */
110 	gen_read,			/* read */
111 	gen_write,			/* write */
112 	gen_ioctl,			/* ioctl */
113 	nodev,				/* devmap */
114 	nodev,				/* mmap */
115 	nodev,				/* segmap */
116 	nochpoll,			/* poll */
117 	ddi_prop_op,			/* prop_op */
118 	NULL,				/* streamtab */
119 	D_NEW | D_MP | D_HOTPLUG,	/* flag */
120 	CB_REV,				/* cb_rev */
121 	nodev,				/* aread */
122 	nodev				/* awrite */
123 };
124 
125 
126 static struct dev_ops gen_ops = {
127 	DEVO_REV,		/* devo_rev */
128 	0,			/* refcnt */
129 	gen_info,		/* getinfo */
130 	nulldev,		/* identify */
131 	nulldev,		/* probe */
132 	gen_attach,		/* attach */
133 	gen_detach,		/* detach */
134 	nodev,			/* reset */
135 	&gen_cb_ops,		/* driver ops */
136 	(struct bus_ops *)0,	/* bus ops */
137 	gen_power		/* power */
138 };
139 
140 /*
141  * INST_TO_MINOR() gives the starting minor number for a given gen_drv driver
142  * instance. A shift left by 6 bits allows for each instance to have upto
143  * 64 (2^6) minor numbers. The maximum minor number allowed by the system
144  * is L_MAXMIN32 (0x3ffff). This effectively limits the gen_drv instance
145  * numbers from 0 to 0xfff for a total of 4096 instances.
146  */
147 #define	INST_TO_MINOR(i)	(i << 6)
148 #define	MINOR_TO_INST(mn)	(mn >> 6)
149 
150 static char *mnodetypes[] = {
151 	"ddi_nt",
152 	"ddi_nt:device_type",
153 	"ddi_nt:device_class:bus_class",
154 	"ddi_nt2",
155 	"ddi_nt2:device_type",
156 	"ddi_nt2:device_type:bus_class",
157 };
158 #define	N_NTYPES	(sizeof (mnodetypes) / sizeof (char *))
159 
160 static struct modldrv modldrv = {
161 	&mod_driverops,
162 	"generic test driver %I%",
163 	&gen_ops
164 };
165 
166 static struct modlinkage modlinkage = {
167 	MODREV_1, &modldrv, NULL
168 };
169 
170 
171 /*
172  * flags
173  */
174 #define	OPEN_FLAG			0x001
175 #define	PWR_HAS_CHANGED_ON_RESUME_FLAG	0x002
176 #define	FAIL_SUSPEND_FLAG		0x004
177 #define	PUP_WITH_PWR_HAS_CHANGED_FLAG	0x008
178 #define	POWER_FLAG			0x010
179 #define	LOWER_POWER_FLAG		0x020
180 #define	NO_INVOL_FLAG			0x040
181 #define	PM_SUPPORTED_FLAG		0x080
182 
183 /*
184  * ioctl commands (non-devctl ioctl commands)
185  */
186 #define	GENDRV_IOCTL				('P' << 8)
187 #define	GENDRV_IOFAULT_SIMULATE			(GENDRV_IOCTL | 0)
188 #define	GENDRV_NDI_EVENT_TEST			(GENDRV_IOCTL | 1)
189 
190 int
191 _init(void)
192 {
193 	int e;
194 
195 	if ((e = ddi_soft_state_init(&dstates,
196 	    sizeof (struct dstate), 0)) != 0) {
197 		return (e);
198 	}
199 
200 	if ((e = mod_install(&modlinkage)) != 0)  {
201 		ddi_soft_state_fini(&dstates);
202 	}
203 
204 	return (e);
205 }
206 
207 int
208 _fini(void)
209 {
210 	int e;
211 
212 	if ((e = mod_remove(&modlinkage)) != 0)  {
213 		return (e);
214 	}
215 	ddi_soft_state_fini(&dstates);
216 	return (e);
217 }
218 
219 int
220 _info(struct modinfo *modinfop)
221 {
222 	return (mod_info(&modlinkage, modinfop));
223 }
224 
225 static int
226 gen_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
227 {
228 	int instance = ddi_get_instance(devi);
229 	struct dstate *dstatep;
230 	int rval;
231 	int n_devs;
232 	int n_minorcomps;
233 	int isclone;
234 	ddi_eventcookie_t dev_offline_cookie, dev_reset_cookie;
235 	ddi_eventcookie_t bus_reset_cookie, bus_quiesce_cookie;
236 	ddi_eventcookie_t bus_unquiesce_cookie, bus_test_post_cookie;
237 	int i_init = 0;
238 	int level_tmp;
239 
240 	int i;
241 	char *pm_comp[] = {
242 		"NAME=leaf0",
243 		"0=D0",
244 		"1=D1",
245 		"2=D2",
246 		"3=D3",
247 		"NAME=leaf1",
248 		"0=off",
249 		"1=blank",
250 		"2=on"};
251 	char *pm_hw_state = {"needs-suspend-resume"};
252 
253 
254 	switch (cmd) {
255 	case DDI_ATTACH:
256 
257 		if (ddi_soft_state_zalloc(dstates, instance) !=
258 		    DDI_SUCCESS) {
259 			cmn_err(CE_CONT, "%s%d: can't allocate state\n",
260 			    ddi_get_name(devi), instance);
261 
262 			return (DDI_FAILURE);
263 		}
264 
265 		dstatep = ddi_get_soft_state(dstates, instance);
266 		dstatep->dip = devi;
267 		mutex_init(&dstatep->lock, NULL, MUTEX_DRIVER, NULL);
268 
269 		n_devs = ddi_prop_get_int(DDI_DEV_T_ANY, devi, 0,
270 		    "ndevs", 1);
271 
272 		isclone = ddi_prop_get_int(DDI_DEV_T_ANY, devi, 0,
273 		    "isclone", 0);
274 
275 		n_minorcomps = ddi_prop_get_int(DDI_DEV_T_ANY, devi, 0,
276 		    "ncomps", 1);
277 
278 		GEN_DEBUG((CE_CONT,
279 		    "%s%d attaching: n_devs=%d n_minorcomps=%d isclone=%d",
280 		    ddi_get_name(devi), ddi_get_instance(devi),
281 		    n_devs, n_minorcomps, isclone));
282 
283 		if (isclone) {
284 			if (ddi_create_minor_node(devi, "gen", S_IFCHR,
285 			    INST_TO_MINOR(instance), mnodetypes[0],
286 			    isclone) != DDI_SUCCESS) {
287 				ddi_remove_minor_node(devi, NULL);
288 				ddi_soft_state_free(dstates, instance);
289 				cmn_err(CE_WARN, "%s%d: can't create minor "
290 				"node", ddi_get_name(devi), instance);
291 
292 				return (DDI_FAILURE);
293 			}
294 			rval = DDI_SUCCESS;
295 		} else {
296 			rval = gen_create_minor_nodes(devi, dstatep);
297 			if (rval != DDI_SUCCESS) {
298 				ddi_prop_remove_all(devi);
299 				ddi_remove_minor_node(devi, NULL);
300 				ddi_soft_state_free(dstates, instance);
301 				cmn_err(CE_WARN, "%s%d: can't create minor "
302 				"nodes", ddi_get_name(devi), instance);
303 
304 				return (DDI_FAILURE);
305 			}
306 		}
307 
308 		if (ddi_get_eventcookie(devi, "pshot_dev_offline",
309 		    &dev_offline_cookie) == DDI_SUCCESS) {
310 			(void) ddi_add_event_handler(devi, dev_offline_cookie,
311 			    gen_event_cb, NULL, &(dstatep->gen_cb_ids[0]));
312 		}
313 
314 		if (ddi_get_eventcookie(devi, "pshot_dev_reset",
315 		    &dev_reset_cookie) == DDI_SUCCESS) {
316 			(void) ddi_add_event_handler(devi, dev_reset_cookie,
317 			    gen_event_cb, NULL, &(dstatep->gen_cb_ids[1]));
318 		}
319 
320 		if (ddi_get_eventcookie(devi, "pshot_bus_reset",
321 		    &bus_reset_cookie) == DDI_SUCCESS) {
322 			(void) ddi_add_event_handler(devi, bus_reset_cookie,
323 			    gen_event_cb, NULL, &(dstatep->gen_cb_ids[2]));
324 		}
325 
326 		if (ddi_get_eventcookie(devi, "pshot_bus_quiesce",
327 		    &bus_quiesce_cookie) == DDI_SUCCESS) {
328 			(void) ddi_add_event_handler(devi, bus_quiesce_cookie,
329 			    gen_event_cb, NULL, &(dstatep->gen_cb_ids[3]));
330 		}
331 
332 		if (ddi_get_eventcookie(devi, "pshot_bus_unquiesce",
333 		    &bus_unquiesce_cookie) == DDI_SUCCESS) {
334 			(void) ddi_add_event_handler(devi,
335 			    bus_unquiesce_cookie, gen_event_cb,
336 			    NULL, &(dstatep->gen_cb_ids[4]));
337 		}
338 
339 		if (ddi_get_eventcookie(devi, "pshot_bus_test_post",
340 		    &bus_test_post_cookie) == DDI_SUCCESS) {
341 			(void) ddi_add_event_handler(devi,
342 			    bus_test_post_cookie, gen_event_cb,
343 			    NULL, &(dstatep->gen_cb_ids[5]));
344 		}
345 
346 		/*
347 		 * initialize the devices' pm state
348 		 */
349 		mutex_enter(&dstatep->lock);
350 		dstatep->flag &= ~OPEN_FLAG;
351 		dstatep->flag &= ~PWR_HAS_CHANGED_ON_RESUME_FLAG;
352 		dstatep->flag &= ~FAIL_SUSPEND_FLAG;
353 		dstatep->flag &= ~PUP_WITH_PWR_HAS_CHANGED_FLAG;
354 		dstatep->flag |= LOWER_POWER_FLAG;
355 		dstatep->flag &= ~NO_INVOL_FLAG;
356 		dstatep->flag |= PM_SUPPORTED_FLAG;
357 		dstatep->busy[0] = 0;
358 		dstatep->busy[1] = 0;
359 		dstatep->level[0] = -1;
360 		dstatep->level[1] = -1;
361 		mutex_exit(&dstatep->lock);
362 
363 		/*
364 		 * stash the nodename
365 		 */
366 		dstatep->nodename = ddi_node_name(devi);
367 
368 		/*
369 		 * Check if the no-involuntary-power-cycles property
370 		 * was created. Set NO_INVOL_FLAG if so.
371 		 */
372 		if (ddi_prop_exists(DDI_DEV_T_ANY, dstatep->dip,
373 		    (DDI_PROP_DONTPASS | DDI_PROP_NOTPROM),
374 		    "no-involuntary-power-cycles") == 1) {
375 			GEN_DEBUG((CE_CONT,
376 			    "%s%d: DDI_ATTACH:\n\tno-involuntary-power-cycles"
377 			    " property was created",
378 			    ddi_node_name(devi), ddi_get_instance(devi)));
379 			mutex_enter(&dstatep->lock);
380 			dstatep->flag |= NO_INVOL_FLAG;
381 			mutex_exit(&dstatep->lock);
382 		}
383 
384 		/*
385 		 * Check if the dependency-property property
386 		 * was created.
387 		 */
388 		if (ddi_prop_exists(DDI_DEV_T_ANY, dstatep->dip,
389 		    (DDI_PROP_DONTPASS | DDI_PROP_NOTPROM),
390 		    "dependency-property") == 1) {
391 			GEN_DEBUG((CE_CONT,
392 			    "%s%d: DDI_ATTACH:\n\tdependency-property"
393 			    " property was created",
394 			    ddi_node_name(devi), ddi_get_instance(devi)));
395 		}
396 
397 		/*
398 		 * create the pm-components property. two comps:
399 		 * 4 levels on comp0, 3 on comp 1.
400 		 * - skip for a "tape" device, clear PM_SUPPORTED_FLAG
401 		 */
402 		if (strcmp(ddi_node_name(devi), "tape") != 0) {
403 			if (ddi_prop_update_string_array(DDI_DEV_T_NONE, devi,
404 			    "pm-components", pm_comp, 9) != DDI_PROP_SUCCESS) {
405 				cmn_err(CE_WARN, "%s%d: %s\n",
406 				    ddi_node_name(devi),
407 				    ddi_get_instance(devi),
408 				    "unable to create \"pm-components\" "
409 				    " property.");
410 
411 				return (DDI_FAILURE);
412 			}
413 		} else {
414 			mutex_enter(&dstatep->lock);
415 			dstatep->flag &= ~PM_SUPPORTED_FLAG;
416 			mutex_exit(&dstatep->lock);
417 		}
418 
419 		/*
420 		 * Check if the pm-components property was created
421 		 */
422 		if (dstatep->flag & PM_SUPPORTED_FLAG) {
423 			if (ddi_prop_exists(DDI_DEV_T_ANY, dstatep->dip,
424 			    (DDI_PROP_DONTPASS | DDI_PROP_NOTPROM),
425 			    "pm-components") != 1) {
426 				cmn_err(CE_WARN, "%s%d: DDI_ATTACH:\n\t%s",
427 				    ddi_node_name(devi),
428 				    ddi_get_instance(devi),
429 				    "\"pm-components\" property does"
430 				    " not exist");
431 
432 				return (DDI_FAILURE);
433 
434 			} else {
435 				GEN_DEBUG((CE_CONT, "%s%d: DDI_ATTACH:"
436 				    " created pm-components property",
437 				    ddi_node_name(devi),
438 				    ddi_get_instance(devi)));
439 			}
440 		}
441 
442 		/*
443 		 * create the pm-hardware-state property.
444 		 * needed to get DDI_SUSPEND and DDI_RESUME calls
445 		 */
446 		if (ddi_prop_update_string(DDI_DEV_T_NONE, devi,
447 		    "pm-hardware-state", pm_hw_state) != DDI_PROP_SUCCESS) {
448 			cmn_err(CE_WARN, "%s%d: DDI_ATTACH:\n\t%s\n",
449 			    ddi_node_name(devi), ddi_get_instance(devi),
450 			    "unable to create \"pm-hardware-state\" "
451 			    " property.");
452 
453 			return (DDI_FAILURE);
454 		}
455 
456 		/*
457 		 * set power levels to max via pm_raise_power(),
458 		 */
459 		mutex_enter(&dstatep->lock);
460 		i_init = (dstatep->flag & PM_SUPPORTED_FLAG) ? 0 : COMPONENTS;
461 		mutex_exit(&dstatep->lock);
462 		for (i = i_init; i < COMPONENTS; i++) {
463 			GEN_DEBUG((CE_CONT,
464 			    "%s%d: DDI_ATTACH: pm_raise_power comp %d "
465 			    "to level %d", ddi_node_name(devi),
466 			    ddi_get_instance(devi), i, maxpwr[i]));
467 			if (pm_raise_power(dstatep->dip, i, maxpwr[i]) !=
468 			    DDI_SUCCESS) {
469 				cmn_err(CE_WARN,
470 				    "%s%d: DDI_ATTACH: pm_raise_power failed\n",
471 				    ddi_node_name(devi),
472 				    ddi_get_instance(devi));
473 				dstatep->level[i] = -1;
474 
475 				return (DDI_FAILURE);
476 			}
477 		}
478 
479 		if (rval == DDI_SUCCESS) {
480 			ddi_report_dev(devi);
481 		}
482 		return (rval);
483 
484 
485 	case DDI_RESUME:
486 		GEN_DEBUG((CE_CONT, "%s%d: DDI_RESUME", ddi_node_name(devi),
487 		    ddi_get_instance(devi)));
488 
489 		dstatep = ddi_get_soft_state(dstates, ddi_get_instance(devi));
490 		if (dstatep == NULL) {
491 
492 			return (DDI_FAILURE);
493 		}
494 
495 		/*
496 		 * Call pm_power_has_changed() if flag
497 		 * PWR_HAS_CHANGED_ON_RESUME_FLAG is set,
498 		 * then clear the flag
499 		 */
500 		mutex_enter(&dstatep->lock);
501 		i_init = (dstatep->flag & PM_SUPPORTED_FLAG) ? 0 : COMPONENTS;
502 		mutex_exit(&dstatep->lock);
503 		if (dstatep->flag & PWR_HAS_CHANGED_ON_RESUME_FLAG) {
504 			for (i = i_init; i < COMPONENTS; i++) {
505 				GEN_DEBUG((CE_CONT,
506 				    "%s%d: DDI_RESUME: pm_power_has_changed "
507 				    "comp %d to level %d", ddi_node_name(devi),
508 				    ddi_get_instance(devi), i, maxpwr[i]));
509 				mutex_enter(&dstatep->lock);
510 				level_tmp = dstatep->level[i];
511 				dstatep->level[i] = maxpwr[i];
512 				if (pm_power_has_changed(dstatep->dip, i,
513 				    maxpwr[i]) != DDI_SUCCESS) {
514 					cmn_err(CE_WARN,
515 					    "%s%d: DDI_RESUME:\n\t"
516 					    " pm_power_has_changed"
517 					    " failed: comp %d to level %d\n",
518 					    ddi_node_name(devi),
519 					    ddi_get_instance(devi),
520 					    i, maxpwr[i]);
521 					dstatep->level[i] = level_tmp;
522 				}
523 				mutex_exit(&dstatep->lock);
524 			}
525 		} else {
526 			/*
527 			 * Call pm_raise_power() instead
528 			 */
529 			for (i = i_init; i < COMPONENTS; i++) {
530 				GEN_DEBUG((CE_CONT,
531 				    "%s%d: DDI_RESUME: pm_raise_power"
532 				    " comp %d to level %d",
533 				    ddi_node_name(devi), ddi_get_instance(devi),
534 				    i, maxpwr[i]));
535 				if (pm_raise_power(dstatep->dip, i, maxpwr[i])
536 				    != DDI_SUCCESS) {
537 					cmn_err(CE_WARN,
538 					    "%s%d: DDI_RESUME:"
539 					    "\n\tpm_raise_power"
540 					    "failed: comp %d to level %d\n",
541 					    ddi_node_name(devi),
542 					    ddi_get_instance(devi),
543 					    i, maxpwr[i]);
544 				}
545 			}
546 		}
547 
548 		return (DDI_SUCCESS);
549 
550 	default:
551 		GEN_DEBUG((CE_WARN, "attach: default"));
552 		return (DDI_FAILURE);
553 	}
554 }
555 
556 static int
557 gen_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
558 {
559 	struct dstate *dstatep;
560 	int instance;
561 	int i;
562 	int rv;
563 	int rm_power;
564 	int level_tmp;
565 
566 #ifdef DEBUG
567 	int n_devs;
568 	int n_minorcomps;
569 	int isclone;
570 #endif
571 
572 	switch (cmd) {
573 	case DDI_DETACH:
574 		GEN_DEBUG((CE_CONT, "%s%d: DDI_DETACH", ddi_node_name(devi),
575 		    ddi_get_instance(devi)));
576 
577 		instance = ddi_get_instance(devi);
578 		dstatep = ddi_get_soft_state(dstates, instance);
579 		if (dstatep == NULL) {
580 
581 			return (DDI_FAILURE);
582 }
583 
584 #ifdef DEBUG
585 		n_devs = ddi_prop_get_int(DDI_DEV_T_ANY, devi, 0,
586 		    "ndevs", 1);
587 
588 		isclone = ddi_prop_get_int(DDI_DEV_T_ANY, devi, 0,
589 		    "isclone", 0);
590 
591 		n_minorcomps = ddi_prop_get_int(DDI_DEV_T_ANY, devi, 0,
592 		    "ncomps", 1);
593 #endif /* DEBUG */
594 
595 		/*
596 		 * power off component 1.
597 		 */
598 		if (dstatep->flag & PM_SUPPORTED_FLAG) {
599 			GEN_DEBUG((CE_CONT,
600 			    "%s%d: DDI_DETACH: pm_lower_power comp 1 level %d",
601 			    ddi_node_name(devi), ddi_get_instance(devi),
602 			    MINPWR));
603 			if (pm_lower_power(dstatep->dip, 1, MINPWR)
604 			    != DDI_SUCCESS) {
605 				cmn_err(CE_WARN, "%s%d: DDI_DETACH:\n\t"
606 				    "pm_lower_power failed for comp 1 to"
607 				    " level %d\n", ddi_node_name(devi),
608 				    ddi_get_instance(devi), MINPWR);
609 
610 				return (DDI_FAILURE);
611 			}
612 
613 			/*
614 			 * check power level. Issue pm_power_has_changed
615 			 * if not at MINPWR.
616 			 */
617 			mutex_enter(&dstatep->lock);
618 			level_tmp = dstatep->level[1];
619 			dstatep->level[1] = MINPWR;
620 			if (dstatep->level[1] != MINPWR) {
621 				GEN_DEBUG((CE_NOTE, "%s%d: DDI_DETACH:"
622 				    " power off via pm_power_has_changed"
623 				    " instead", ddi_node_name(devi),
624 				    ddi_get_instance(devi)));
625 				if (pm_power_has_changed(dstatep->dip,
626 				    1, MINPWR) != DDI_SUCCESS) {
627 					GEN_DEBUG((CE_NOTE, "%s%d: DDI_DETACH:"
628 					    " pm_power_has_changed failed for"
629 					    " comp 1 to level %d",
630 					    ddi_node_name(devi),
631 					    ddi_get_instance(devi),
632 					    MINPWR));
633 					dstatep->level[1] = level_tmp;
634 					mutex_exit(&dstatep->lock);
635 
636 					return (DDI_FAILURE);
637 				}
638 			}
639 			mutex_exit(&dstatep->lock);
640 		}
641 
642 		/*
643 		 * If the LOWER_POWER_FLAG flag is not set,
644 		 * don't call pm_lowr_power() for comp 0.
645 		 * This should be used only for the XXXXX@XX,no_invol
646 		 * devices that export the
647 		 * no-involuntary-power-cycles property
648 		 */
649 		if (!(dstatep->flag & LOWER_POWER_FLAG) &&
650 		    dstatep->flag & PM_SUPPORTED_FLAG) {
651 			cmn_err(CE_NOTE, "%s%d: DDI_DETACH:\n\t"
652 			    " NOT CALLING PM_LOWER_POWER():"
653 			    " LOWER_POWER_FLAG NOT SET\n",
654 			    ddi_node_name(devi), ddi_get_instance(devi));
655 		} else if (dstatep->flag & PM_SUPPORTED_FLAG) {
656 			GEN_DEBUG((CE_CONT,
657 			    "%s%d: DDI_DETACH: pm_lower_power comp 0 level %d",
658 			    ddi_node_name(devi), ddi_get_instance(devi),
659 			    MINPWR));
660 			if (pm_lower_power(dstatep->dip, 0, MINPWR)
661 			    != DDI_SUCCESS) {
662 				cmn_err(CE_WARN, "%s%d: DDI_DETACH:\n\t"
663 				    "pm_lower_power failed for comp 0 to"
664 				    " level %d\n", ddi_node_name(devi),
665 				    ddi_get_instance(devi), MINPWR);
666 
667 				return (DDI_FAILURE);
668 			}
669 
670 			/*
671 			 * check power level. Issue pm_power_has_changed
672 			 * if not at MINPWR.
673 			 */
674 			mutex_enter(&dstatep->lock);
675 			level_tmp = dstatep->level[0];
676 			dstatep->level[0] = MINPWR;
677 			if (dstatep->level[0] != MINPWR) {
678 				GEN_DEBUG((CE_NOTE, "%s%d: DDI_DETACH:"
679 				    " power off via pm_power_has_changed"
680 				    " instead", ddi_node_name(devi),
681 				    ddi_get_instance(devi)));
682 				if (pm_power_has_changed(dstatep->dip,
683 				    0, MINPWR) != DDI_SUCCESS) {
684 					GEN_DEBUG((CE_NOTE, "%s%d: DDI_DETACH:"
685 					    " pm_power_has_changed failed for"
686 					    " comp 0 to level %d",
687 					    ddi_node_name(devi),
688 					    ddi_get_instance(devi),
689 					    MINPWR));
690 					dstatep->level[0] = level_tmp;
691 					mutex_exit(&dstatep->lock);
692 
693 					return (DDI_FAILURE);
694 				}
695 			}
696 			mutex_exit(&dstatep->lock);
697 		}
698 
699 		GEN_DEBUG((CE_CONT,
700 		    "%s%d detaching: n_devs=%d n_minorcomps=%d isclone=%d",
701 		    ddi_node_name(devi), ddi_get_instance(devi),
702 		    n_devs, n_minorcomps, isclone));
703 
704 		for (i = 0; i < NUMEVENTS; i++) {
705 			if (dstatep->gen_cb_ids[i]) {
706 		(void) ddi_remove_event_handler(dstatep->gen_cb_ids[i]);
707 				dstatep->gen_cb_ids[i] = NULL;
708 			}
709 		}
710 
711 		ddi_prop_remove_all(devi);
712 		ddi_remove_minor_node(devi, NULL);
713 		if (dstatep->node_type)
714 			kmem_free(dstatep->node_type,
715 			    strlen(dstatep->node_type) + 1);
716 		ddi_soft_state_free(dstates, instance);
717 		return (DDI_SUCCESS);
718 
719 	case DDI_SUSPEND:
720 		GEN_DEBUG((CE_CONT, "%s%d: DDI_SUSPEND",
721 		    ddi_node_name(devi), ddi_get_instance(devi)));
722 
723 		instance = ddi_get_instance(devi);
724 		dstatep = ddi_get_soft_state(dstates, instance);
725 		if (dstatep == NULL) {
726 
727 			return (DDI_FAILURE);
728 		}
729 
730 		/*
731 		 * fail the suspend if FAIL_SUSPEND_FLAG is set.
732 		 * clear the FAIL_SUSPEND_FLAG flag
733 		 */
734 		mutex_enter(&dstatep->lock);
735 		if (dstatep->flag & FAIL_SUSPEND_FLAG) {
736 			GEN_DEBUG((CE_CONT, "%s%d: DDI_SUSPEND:"
737 			    " FAIL_SUSPEND_FLAG is set,"
738 			    " fail suspend",
739 			    ddi_node_name(devi), ddi_get_instance(devi)));
740 			dstatep->flag &= ~FAIL_SUSPEND_FLAG;
741 			rv = DDI_FAILURE;
742 		} else {
743 			rv = DDI_SUCCESS;
744 		}
745 		mutex_exit(&dstatep->lock);
746 
747 		/*
748 		 * Issue ddi_removing_power() to determine if the suspend
749 		 * was initiated by either CPR or DR. If CPR, the system
750 		 * will be powered OFF; if this driver has set the
751 		 * NO_INVOL_FLAG, then refuse to suspend. If DR, power
752 		 * will not be removed, thus allow the suspend.
753 		 */
754 		if (dstatep->flag & NO_INVOL_FLAG &&
755 		    dstatep->flag & PM_SUPPORTED_FLAG) {
756 			GEN_DEBUG((CE_CONT, "%s%d: DDI_SUSPEND:"
757 			    " check via ddi_removing_power()",
758 			    ddi_node_name(devi), ddi_get_instance(devi)));
759 
760 			rm_power = ddi_removing_power(dstatep->dip);
761 
762 			if (rm_power < 0) {
763 				cmn_err(CE_WARN, "%s%d: DDI_SUSPEND:"
764 				    " ddi_removing_power() failed\n",
765 				    ddi_node_name(devi),
766 				    ddi_get_instance(devi));
767 			} else if (rm_power == 1) {
768 				/*
769 				 * CPR: power will be removed
770 				 */
771 				GEN_DEBUG((CE_CONT, "%s%d: DDI_SUSPEND:\n\t"
772 				    " CPR: POWER WILL BE REMOVED, THEREFORE"
773 				    " REFUSE TO SUSPEND", ddi_node_name(devi),
774 				    ddi_get_instance(devi)));
775 				rv = DDI_FAILURE;
776 			} else if (rm_power == 0) {
777 				/*
778 				 * DR: power will not be removed
779 				 */
780 				GEN_DEBUG((CE_CONT, "%s%d: DDI_SUSPEND:\n\t"
781 				    " DR: POWER WILL NOT BE REMOVED, THEREFORE"
782 				    " ALLOW THE SUSPEND", ddi_node_name(devi),
783 				    ddi_get_instance(devi)));
784 				rv = DDI_SUCCESS;
785 			}
786 		}
787 
788 		/*
789 		 * power OFF via pm_power_has_changed()
790 		 */
791 		mutex_enter(&dstatep->lock);
792 		if (dstatep->flag & PM_SUPPORTED_FLAG &&
793 		    !(dstatep->flag & NO_INVOL_FLAG)) {
794 			level_tmp = dstatep->level[0];
795 			dstatep->level[0] = MINPWR;
796 			GEN_DEBUG((CE_CONT,
797 			    "%s%d: DDI_SUSPEND: pm_power_has_changed comp 0"
798 			    " level %d", ddi_node_name(devi),
799 			    ddi_get_instance(devi), MINPWR));
800 			if (pm_power_has_changed(dstatep->dip, 0, MINPWR)
801 			    != DDI_SUCCESS) {
802 				cmn_err(CE_WARN, "%s%d: DDI_SUSPEND:\n\t"
803 				    "pm_power_has_changed failed for comp 0 to"
804 				    " level %d\n", ddi_node_name(devi),
805 				    ddi_get_instance(devi), MINPWR);
806 				dstatep->level[0] = level_tmp;
807 				mutex_exit(&dstatep->lock);
808 
809 				return (DDI_FAILURE);
810 			}
811 		}
812 		mutex_exit(&dstatep->lock);
813 
814 		return (rv);
815 
816 	default:
817 
818 		return (DDI_FAILURE);
819 	}
820 }
821 
822 /* ARGSUSED */
823 static int
824 gen_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
825 {
826 	dev_t	dev;
827 	int	instance;
828 
829 	if (infocmd != DDI_INFO_DEVT2INSTANCE)
830 		return (DDI_FAILURE);
831 
832 	dev = (dev_t)arg;
833 	instance = MINOR_TO_INST(getminor(dev));
834 	*result = (void *)(uintptr_t)instance;
835 	return (DDI_SUCCESS);
836 }
837 
838 
839 /*ARGSUSED*/
840 static int
841 gen_open(dev_t *devp, int flag, int otyp, cred_t *cred)
842 {
843 	minor_t minor;
844 	struct dstate *dstatep;
845 
846 	if (otyp != OTYP_BLK && otyp != OTYP_CHR)
847 		return (EINVAL);
848 
849 	minor = getminor(*devp);
850 	if ((dstatep = ddi_get_soft_state(dstates,
851 	    MINOR_TO_INST(minor))) == NULL)
852 		return (ENXIO);
853 
854 	mutex_enter(&dstatep->lock);
855 	dstatep->flag |= OPEN_FLAG;
856 	mutex_exit(&dstatep->lock);
857 
858 	GEN_DEBUG((CE_CONT,
859 	    "%s%d open",
860 	    dstatep->nodename, MINOR_TO_INST(minor)));
861 
862 	return (0);
863 }
864 
865 /*ARGSUSED*/
866 static int
867 gen_close(dev_t dev, int flag, int otyp, cred_t *cred)
868 {
869 	struct dstate *dstatep;
870 	minor_t minor = getminor(dev);
871 
872 	if (otyp != OTYP_BLK && otyp != OTYP_CHR)
873 		return (EINVAL);
874 
875 	dstatep = ddi_get_soft_state(dstates, MINOR_TO_INST(minor));
876 
877 	if (dstatep == NULL)
878 		return (ENXIO);
879 
880 	mutex_enter(&dstatep->lock);
881 	dstatep->flag &= ~OPEN_FLAG;
882 	mutex_exit(&dstatep->lock);
883 
884 	GEN_DEBUG((CE_CONT,
885 	    "%s%d close",
886 	    dstatep->nodename, MINOR_TO_INST(minor)));
887 
888 	return (0);
889 }
890 
891 /*ARGSUSED*/
892 static int
893 gen_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp)
894 {
895 	struct dstate *dstatep;
896 	ddi_eventcookie_t cookie;
897 	int instance;
898 	int rval = 0;
899 	char *nodename;
900 	int i;
901 	struct devctl_iocdata *dcp;
902 	uint_t state;
903 	int ret;
904 	int level_tmp;
905 
906 	instance = MINOR_TO_INST(getminor(dev));
907 	dstatep = ddi_get_soft_state(dstates, instance);
908 	nodename = dstatep->nodename;
909 
910 	if (dstatep == NULL)
911 		return (ENXIO);
912 
913 	/*
914 	 * read devctl ioctl data
915 	 */
916 	if (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS)
917 		return (EFAULT);
918 
919 	switch (cmd) {
920 	case GENDRV_IOFAULT_SIMULATE:
921 		if (ddi_get_eventcookie(dstatep->dip, DDI_DEVI_FAULT_EVENT,
922 			    &(cookie)) != NDI_SUCCESS)
923 			return (DDI_FAILURE);
924 
925 		return (ndi_post_event(dstatep->dip, dstatep->dip, cookie,
926 			    NULL));
927 
928 	case GENDRV_NDI_EVENT_TEST:
929 		if (ddi_get_eventcookie(dstatep->dip, "pshot_dev_offline",
930 		    &cookie) == NDI_SUCCESS) {
931 			(void) ndi_post_event(dstatep->dip, dstatep->dip,
932 			    cookie, NULL);
933 		}
934 
935 		if (ddi_get_eventcookie(dstatep->dip, "pshot_dev_reset",
936 		    &cookie) == NDI_SUCCESS) {
937 			(void) ndi_post_event(dstatep->dip, dstatep->dip,
938 			    cookie, NULL);
939 		}
940 
941 		if (ddi_get_eventcookie(dstatep->dip, "pshot_bus_reset",
942 		    &cookie) == NDI_SUCCESS) {
943 			(void) ndi_post_event(dstatep->dip, dstatep->dip,
944 			    cookie, NULL);
945 		}
946 
947 		if (ddi_get_eventcookie(dstatep->dip, "pshot_bus_quiesce",
948 		    &cookie) == NDI_SUCCESS) {
949 			(void) ndi_post_event(dstatep->dip, dstatep->dip,
950 			    cookie, NULL);
951 		}
952 
953 		if (ddi_get_eventcookie(dstatep->dip, "pshot_bus_unquiesce",
954 		    &cookie) == NDI_SUCCESS) {
955 			(void) ndi_post_event(dstatep->dip, dstatep->dip,
956 			    cookie, NULL);
957 		}
958 
959 		if (ddi_get_eventcookie(dstatep->dip, "pshot_bus_test_post",
960 		    &cookie) == NDI_SUCCESS) {
961 			(void) ndi_post_event(dstatep->dip, dstatep->dip,
962 			    cookie, NULL);
963 		}
964 
965 		break;
966 
967 	case DEVCTL_PM_PWR_HAS_CHANGED_ON_RESUME:
968 		/*
969 		 * Issue pm_power_has_changed() call on DDI_RESUME
970 		 */
971 		mutex_enter(&dstatep->lock);
972 		dstatep->flag |= PWR_HAS_CHANGED_ON_RESUME_FLAG;
973 		mutex_exit(&dstatep->lock);
974 		GEN_DEBUG((CE_CONT, "%s%d:"
975 		    " DEVCTL_PM_PWR_HAS_CHANGED_ON_RESUME", nodename,
976 		    instance));
977 
978 		break;
979 
980 	case DEVCTL_PM_FAIL_SUSPEND:
981 		/*
982 		 * Fail the suspend attempt in DDI_SUSPEND
983 		 */
984 		mutex_enter(&dstatep->lock);
985 		dstatep->flag |= FAIL_SUSPEND_FLAG;
986 		mutex_exit(&dstatep->lock);
987 		GEN_DEBUG((CE_CONT, "%s%d: DEVCTL_PM_FAIL_SUSPEND",
988 		    nodename, instance));
989 
990 		break;
991 
992 	case DEVCTL_PM_PUP_WITH_PWR_HAS_CHANGED:
993 		/*
994 		 * Use pm_power_has_changed() to power up comp 0 when
995 		 * enforcing the comp 0 vs comp-not 0 dependency:
996 		 * Power up comp 0 first, if request for comp-not-0
997 		 * comes in.
998 		 * Else, default to pm_raise_power().
999 		 */
1000 		mutex_enter(&dstatep->lock);
1001 		dstatep->flag |= PUP_WITH_PWR_HAS_CHANGED_FLAG;
1002 		mutex_exit(&dstatep->lock);
1003 		GEN_DEBUG((CE_CONT, "%s%d: DEVCTL_PM_PUP_WITH_PWR_HAS_CHANGED",
1004 		    nodename, instance));
1005 
1006 		break;
1007 
1008 	case DEVCTL_PM_BUSY_COMP:
1009 		/*
1010 		 * mark component 0 busy via a pm_busy_component() call.
1011 		 * update the busy[] array.
1012 		 */
1013 		mutex_enter(&dstatep->lock);
1014 		++dstatep->busy[0];
1015 		GEN_DEBUG((CE_CONT, "%s%d: DEVCTL_PM_BUSY_COMP: comp 0:"
1016 		    " busy=%d", nodename, instance, dstatep->busy[0]));
1017 		mutex_exit(&dstatep->lock);
1018 		ret = pm_busy_component(dstatep->dip, 0);
1019 		ASSERT(ret == DDI_SUCCESS);
1020 
1021 		break;
1022 
1023 	case DEVCTL_PM_BUSY_COMP_TEST:
1024 		/*
1025 		 * test busy state on component 0
1026 		 */
1027 		mutex_enter(&dstatep->lock);
1028 		state = dstatep->busy[0];
1029 		if (copyout(&state, dcp->cpyout_buf,
1030 		    sizeof (uint_t)) != 0) {
1031 			cmn_err(CE_WARN, "%s%d:"
1032 			    " DEVCTL_PM_BUSY_COMP_TEST: copyout failed\n",
1033 			    nodename, instance);
1034 			rval = EINVAL;
1035 		}
1036 		GEN_DEBUG((CE_CONT, "%s%d: DEVCTL_PM_BUSY_COMP_TEST:"
1037 		    " comp 0 busy %d",
1038 		    nodename, instance, state));
1039 		mutex_exit(&dstatep->lock);
1040 
1041 		break;
1042 
1043 	case DEVCTL_PM_IDLE_COMP:
1044 		/*
1045 		 * mark component 0 idle via a pm_idle_component() call.
1046 		 * NOP if dstatep->busy[0] == 0.
1047 		 */
1048 		mutex_enter(&dstatep->lock);
1049 		if (dstatep->busy[0] > 0) {
1050 			--dstatep->busy[0];
1051 			GEN_DEBUG((CE_CONT, "%s%d: DEVCTL_PM_IDLE_COMP:"
1052 			    " comp 0: busy=%d", nodename, instance,
1053 			    dstatep->busy[0]));
1054 			mutex_exit(&dstatep->lock);
1055 			ret = pm_idle_component(dstatep->dip, 0);
1056 			ASSERT(ret == DDI_SUCCESS);
1057 		} else {
1058 			mutex_exit(&dstatep->lock);
1059 		}
1060 
1061 		break;
1062 
1063 	case DEVCTL_PM_PROM_PRINTF:
1064 		(void) prom_printf("%s%d: PROM_PRINTF FROM GEN_DRV\n",
1065 		    nodename, instance);
1066 
1067 		break;
1068 
1069 	case DEVCTL_PM_RAISE_PWR:
1070 		/*
1071 		 * power up both components to MAXPWR via
1072 		 * pm_raise_power() calls. this ioctl() cmd
1073 		 * assumes that the current level is 0
1074 		 */
1075 		for (i = 0; i < COMPONENTS; i++) {
1076 			GEN_DEBUG((CE_CONT, "%s%d: DEVCTL_PM_RAISE_PWR:"
1077 			    " comp %d old 0 new %d",
1078 			    nodename, instance, i, maxpwr[i]));
1079 			if (pm_raise_power(dstatep->dip, 0, maxpwr[i])
1080 			    != DDI_SUCCESS) {
1081 				rval = EINVAL;
1082 			}
1083 		}
1084 
1085 		break;
1086 
1087 	case DEVCTL_PM_CHANGE_PWR_LOW:
1088 		/*
1089 		 * power off both components via pm_power_has_changed() calls
1090 		 */
1091 		for (i = (COMPONENTS - 1); i >= 0; --i) {
1092 			GEN_DEBUG((CE_CONT, "%s%d: DEVCTL_PM_CHANGE_PWR_LOW:"
1093 			    " comp %d new 0",
1094 			    nodename, instance, i));
1095 			mutex_enter(&dstatep->lock);
1096 			level_tmp = dstatep->level[i];
1097 			dstatep->level[i] = 0;
1098 			if (pm_power_has_changed(dstatep->dip, i, 0)
1099 			    != DDI_SUCCESS) {
1100 				dstatep->level[i] = level_tmp;
1101 				rval = EINVAL;
1102 			}
1103 			mutex_exit(&dstatep->lock);
1104 		}
1105 
1106 		break;
1107 
1108 	case DEVCTL_PM_CHANGE_PWR_HIGH:
1109 		/*
1110 		 * power up both components to MAXPWR via
1111 		 * pm_power_has_changed() calls
1112 		 */
1113 		for (i = 0; i < COMPONENTS; i++) {
1114 			GEN_DEBUG((CE_CONT, "%s%d: DEVCTL_PM_CHANGE_PWR_HIGH:"
1115 			    " comp %d new %d",
1116 			    nodename, instance, i, maxpwr[i]));
1117 			mutex_enter(&dstatep->lock);
1118 			level_tmp = dstatep->level[i];
1119 			dstatep->level[i] = maxpwr[i];
1120 			if (pm_power_has_changed(dstatep->dip, i, maxpwr[i])
1121 			    != DDI_SUCCESS) {
1122 				dstatep->level[i] = level_tmp;
1123 				rval = EINVAL;
1124 			}
1125 			mutex_exit(&dstatep->lock);
1126 		}
1127 
1128 		break;
1129 
1130 	case DEVCTL_PM_POWER:
1131 		/*
1132 		 * test if the gen_drv_power() routine has been called,
1133 		 * then clear
1134 		 */
1135 		mutex_enter(&dstatep->lock);
1136 		state = (dstatep->flag & POWER_FLAG) ? 1 : 0;
1137 		if (copyout(&state, dcp->cpyout_buf,
1138 		    sizeof (uint_t)) != 0) {
1139 			cmn_err(CE_WARN, "%s%d: DEVCTL_PM_POWER:"
1140 			    " copyout failed\n", nodename, instance);
1141 			rval = EINVAL;
1142 		}
1143 		GEN_DEBUG((CE_CONT, "%s%d: %s POWER_FLAG: %d",
1144 		    nodename, instance, "DEVCTL_PM_POWER", state));
1145 		dstatep->flag &= ~POWER_FLAG;
1146 		mutex_exit(&dstatep->lock);
1147 		break;
1148 
1149 	case DEVCTL_PM_NO_LOWER_POWER:
1150 		/*
1151 		 * issue to not invoke pm_lower_power() on detach
1152 		 */
1153 		mutex_enter(&dstatep->lock);
1154 		dstatep->flag &= ~LOWER_POWER_FLAG;
1155 		mutex_exit(&dstatep->lock);
1156 		GEN_DEBUG((CE_CONT, "%s%d: DEVCTL_PM_NO_LOWER_POWER",
1157 			    nodename, instance));
1158 		break;
1159 
1160 	default:
1161 		return (ENOTTY);
1162 	}
1163 
1164 	return (rval);
1165 }
1166 
1167 /*ARGSUSED*/
1168 static int
1169 gen_read(dev_t dev, struct uio *uiop, cred_t *credp)
1170 {
1171 	return (0);
1172 }
1173 
1174 /*ARGSUSED*/
1175 static int
1176 gen_write(dev_t dev, struct uio *uiop, cred_t *credp)
1177 {
1178 	return (0);
1179 }
1180 
1181 /*ARGSUSED0*/
1182 static int
1183 gen_power(dev_info_t *dip, int cmpt, int level)
1184 {
1185 	struct dstate *dstatep;
1186 	int instance = ddi_get_instance(dip);
1187 	char *nodename = ddi_node_name(dip);
1188 	int level_tmp;
1189 
1190 	GEN_DEBUG((CE_CONT, "%s%d: power: cmpt %d to level %d",
1191 	    nodename, instance, cmpt, level));
1192 
1193 	dstatep = ddi_get_soft_state(dstates, instance);
1194 	if (dstatep == NULL) {
1195 
1196 		return (DDI_FAILURE);
1197 	}
1198 
1199 	/*
1200 	 * Keep track of the power levels for both components
1201 	 * in the dstatep->comp[] array.
1202 	 * Set comp 0 to full level if non-zero comps
1203 	 * are being set to a higher, non-zero level.
1204 	 */
1205 	if (cmpt == 0) {
1206 		mutex_enter(&dstatep->lock);
1207 		dstatep->level[cmpt] = level;
1208 		mutex_exit(&dstatep->lock);
1209 	} else if (level > dstatep->level[cmpt] && level != 0 &&
1210 	    dstatep->level[0] != COMP_0_MAXPWR) {
1211 		/*
1212 		 * If component 0 is not at COMP_0_MAXPWR, and component 1
1213 		 * is being powered ON, invoke pm_raise_power() or
1214 		 * pm_power_has_changed() based on the
1215 		 * PUP_WITH_PWR_HAS_CHANGED_FLAG flag.
1216 		 * PUP_WITH_PWR_HAS_CHANGED_FLAG = FALSE by default, invoking
1217 		 * pm_raise_power().
1218 		 */
1219 		if (!(dstatep->flag & PUP_WITH_PWR_HAS_CHANGED_FLAG)) {
1220 			/*
1221 			 * first set comp 0 to level COMP_0_MAXPWR
1222 			 */
1223 			GEN_DEBUG((CE_CONT, "%s%d: power:  "
1224 			    "pm_raise_power: comp 0 to level %d",
1225 			    nodename, instance, COMP_0_MAXPWR));
1226 			if (pm_raise_power(dip, 0, COMP_0_MAXPWR) !=
1227 			    DDI_SUCCESS) {
1228 				cmn_err(CE_WARN,
1229 				    "%s%d: power: pm_raise_power() "
1230 				    "failed: comp 0 to level %d\n",
1231 				    nodename, instance, COMP_0_MAXPWR);
1232 
1233 				return (DDI_FAILURE);
1234 
1235 			} else {
1236 				mutex_enter(&dstatep->lock);
1237 				dstatep->level[0] = COMP_0_MAXPWR;
1238 				/*
1239 				 * now set the level on the non-zero comp
1240 				 */
1241 				dstatep->level[cmpt] = level;
1242 				mutex_exit(&dstatep->lock);
1243 				GEN_DEBUG((CE_CONT, "%s%d: power: "
1244 				    "comp %d to level %d",
1245 				    nodename, instance, cmpt, level));
1246 			}
1247 		} else {
1248 			GEN_DEBUG((CE_CONT, "%s%d: power: "
1249 			    "pm_power_has_changed: comp 0 to level %d",
1250 			    nodename, instance, COMP_0_MAXPWR));
1251 			mutex_enter(&dstatep->lock);
1252 			level_tmp = dstatep->level[0];
1253 			dstatep->level[0] = COMP_0_MAXPWR;
1254 			if (pm_power_has_changed(dip, 0, COMP_0_MAXPWR) !=
1255 			    DDI_SUCCESS) {
1256 				cmn_err(CE_WARN,
1257 				    "%s%d: power: pm_power_has_changed() "
1258 				    "failed: comp 0 to level %d\n",
1259 				    nodename, instance, COMP_0_MAXPWR);
1260 				dstatep->level[0] = level_tmp;
1261 			} else {
1262 				/*
1263 				 * now set the level on the non-zero comp
1264 				 */
1265 				GEN_DEBUG((CE_CONT, "%s%d: power:"
1266 				    " pm_power_has_changed: comp %d"
1267 				    " to level %d", nodename, instance,
1268 				    cmpt, level));
1269 				dstatep->level[cmpt] = level;
1270 			}
1271 			mutex_exit(&dstatep->lock);
1272 		}
1273 	} else {
1274 		mutex_enter(&dstatep->lock);
1275 		dstatep->level[cmpt] = level;
1276 		mutex_exit(&dstatep->lock);
1277 	}
1278 
1279 	return (DDI_SUCCESS);
1280 }
1281 
1282 
1283 /*
1284  * Create properties of various data types for testing devfs events.
1285  */
1286 static int
1287 gen_create_properties(dev_info_t *devi)
1288 {
1289 	int int_val = 3023;
1290 	int int_array[] = { 3, 10, 304, 230, 4};
1291 	int64_t int64_val = 20;
1292 	int64_t int64_array[] = { 12, 24, 36, 48};
1293 	char *string_val = "Dev_node_prop";
1294 	char *string_array[] = {"Dev_node_prop:0",
1295 	    "Dev_node_prop:1", "Dev_node_prop:2", "Dev_node_prop:3"};
1296 	uchar_t byte_array[] = { (uchar_t)0xaa, (uchar_t)0x55,
1297 	    (uchar_t)0x12, (uchar_t)0xcd };
1298 	char bytes[] = { (char)0x00, (char)0xef, (char)0xff };
1299 
1300 	if (ddi_prop_update_int(DDI_DEV_T_NONE, devi, "int", int_val)
1301 	    != DDI_PROP_SUCCESS)
1302 		return (DDI_FAILURE);
1303 
1304 	if (ddi_prop_update_int_array(DDI_DEV_T_NONE, devi, "int-array",
1305 	    int_array, sizeof (int_array) / sizeof (int)) != DDI_PROP_SUCCESS)
1306 		return (DDI_FAILURE);
1307 
1308 	if (ddi_prop_update_int64(DDI_DEV_T_NONE, devi, "int64", int64_val)
1309 	    != DDI_PROP_SUCCESS)
1310 		return (DDI_FAILURE);
1311 
1312 	if (ddi_prop_update_int64_array(DDI_DEV_T_NONE, devi, "int64-array",
1313 	    int64_array, sizeof (int64_array) / sizeof (int64_t))
1314 	    != DDI_PROP_SUCCESS)
1315 		return (DDI_FAILURE);
1316 
1317 	if (ddi_prop_update_string(DDI_DEV_T_NONE, devi, "string", string_val)
1318 	    != DDI_PROP_SUCCESS)
1319 		return (DDI_FAILURE);
1320 
1321 	if (ddi_prop_update_string_array(DDI_DEV_T_NONE, devi, "string-array",
1322 	    string_array, sizeof (string_array) / sizeof (char *))
1323 	    != DDI_PROP_SUCCESS)
1324 		return (DDI_FAILURE);
1325 
1326 	if (ddi_prop_create(DDI_DEV_T_NONE, devi, DDI_PROP_CANSLEEP,
1327 	    "boolean", NULL, 0) != DDI_PROP_SUCCESS)
1328 		return (DDI_FAILURE);
1329 
1330 	if (ddi_prop_update_byte_array(DDI_DEV_T_NONE, devi, "byte-array",
1331 	    byte_array, sizeof (byte_array)) != DDI_PROP_SUCCESS)
1332 		return (DDI_FAILURE);
1333 
1334 	/* untyped property */
1335 	if (ddi_prop_create(DDI_DEV_T_NONE, devi, DDI_PROP_CANSLEEP, "untyped",
1336 	    (caddr_t)bytes, sizeof (bytes)) != DDI_PROP_SUCCESS)
1337 		return (DDI_FAILURE);
1338 
1339 	return (DDI_SUCCESS);
1340 }
1341 
1342 static struct driver_minor_data {
1343 	char	*name;
1344 	minor_t	minor;
1345 	int	type;
1346 } disk_minor_data[] = {
1347 	{"a", 0, S_IFBLK},
1348 	{"b", 1, S_IFBLK},
1349 	{"c", 2, S_IFBLK},
1350 	{"d", 3, S_IFBLK},
1351 	{"e", 4, S_IFBLK},
1352 	{"f", 5, S_IFBLK},
1353 	{"g", 6, S_IFBLK},
1354 	{"h", 7, S_IFBLK},
1355 	{"a,raw", 0, S_IFCHR},
1356 	{"b,raw", 1, S_IFCHR},
1357 	{"c,raw", 2, S_IFCHR},
1358 	{"d,raw", 3, S_IFCHR},
1359 	{"e,raw", 4, S_IFCHR},
1360 	{"f,raw", 5, S_IFCHR},
1361 	{"g,raw", 6, S_IFCHR},
1362 	{"h,raw", 7, S_IFCHR},
1363 	{0}
1364 };
1365 
1366 
1367 static struct driver_serial_minor_data {
1368 	char	*name;
1369 	minor_t minor;
1370 	int	type;
1371 	char	*node_type;
1372 }  serial_minor_data[] = {
1373 	{"0", 0, S_IFCHR, "ddi_serial"},
1374 	{"1", 1, S_IFCHR, "ddi_serial"},
1375 	{"0,cu", 2, S_IFCHR, "ddi_serial:dialout"},
1376 	{"1,cu", 3, S_IFCHR, "ddi_serial:dialout"},
1377 	{0}
1378 };
1379 
1380 
1381 static int
1382 gen_create_display(dev_info_t *devi)
1383 {
1384 
1385 	int instance = ddi_get_instance(devi);
1386 	char minor_name[15];
1387 
1388 	(void) sprintf(minor_name, "cgtwenty%d", instance);
1389 
1390 	return (ddi_create_minor_node(devi, minor_name, S_IFCHR,
1391 	    INST_TO_MINOR(instance), DDI_NT_DISPLAY, NULL));
1392 }
1393 
1394 static int
1395 gen_create_mn_disk_chan(dev_info_t *devi)
1396 {
1397 	struct driver_minor_data *dmdp;
1398 	int instance = ddi_get_instance(devi);
1399 
1400 	if (gen_create_properties(devi) != DDI_SUCCESS)
1401 		return (DDI_FAILURE);
1402 
1403 	for (dmdp = disk_minor_data; dmdp->name != NULL; dmdp++) {
1404 		if (ddi_create_minor_node(devi, dmdp->name, dmdp->type,
1405 		    (INST_TO_MINOR(instance)) | dmdp->minor,
1406 		    DDI_NT_BLOCK_CHAN, NULL) != DDI_SUCCESS) {
1407 
1408 			return (DDI_FAILURE);
1409 		}
1410 	}
1411 	return (DDI_SUCCESS);
1412 }
1413 
1414 static uint_t
1415 atod(char *s)
1416 {
1417 	uint_t val = 0;
1418 	uint_t digit;
1419 
1420 	while (*s) {
1421 		if (*s >= '0' && *s <= '9')
1422 			digit = *s++ - '0';
1423 		else
1424 			break;
1425 		val = (val * 10) + digit;
1426 	}
1427 	return (val);
1428 }
1429 
1430 
1431 static int
1432 gen_create_mn_disk_wwn(dev_info_t *devi)
1433 {
1434 	struct driver_minor_data *dmdp;
1435 	int instance = ddi_get_instance(devi);
1436 	char *address = ddi_get_name_addr(devi);
1437 	int target, lun;
1438 
1439 	if (address[0] >= '0' && address[0] <= '9' &&
1440 			strchr(address, ',')) {
1441 		target = atod(address);
1442 		address = strchr(address, ',');
1443 		lun = atod(++address);
1444 	} else { /* this hack is for rm_stale_link() testing */
1445 		target = 10;
1446 		lun = 5;
1447 	}
1448 
1449 	if (ddi_prop_create(DDI_DEV_T_NONE, devi, DDI_PROP_CANSLEEP,
1450 	    "target", (caddr_t)&target, sizeof (int))
1451 	    != DDI_PROP_SUCCESS) {
1452 		return (DDI_FAILURE);
1453 	}
1454 	if (ddi_prop_create(DDI_DEV_T_NONE, devi, DDI_PROP_CANSLEEP,
1455 	    "lun", (caddr_t)&lun, sizeof (int))
1456 	    != DDI_PROP_SUCCESS) {
1457 		return (DDI_FAILURE);
1458 	}
1459 
1460 	for (dmdp = disk_minor_data; dmdp->name != NULL; dmdp++) {
1461 		if (ddi_create_minor_node(devi, dmdp->name, dmdp->type,
1462 		    (INST_TO_MINOR(instance)) | dmdp->minor,
1463 		    DDI_NT_BLOCK_WWN, NULL) != DDI_SUCCESS) {
1464 
1465 			return (DDI_FAILURE);
1466 		}
1467 	}
1468 	return (DDI_SUCCESS);
1469 }
1470 
1471 static int
1472 gen_create_mn_disk_cdrom(dev_info_t *devi)
1473 {
1474 	struct driver_minor_data *dmdp;
1475 	int instance = ddi_get_instance(devi);
1476 
1477 	for (dmdp = disk_minor_data; dmdp->name != NULL; dmdp++) {
1478 		if (ddi_create_minor_node(devi, dmdp->name, dmdp->type,
1479 		    (INST_TO_MINOR(instance)) | dmdp->minor,
1480 		    DDI_NT_CD_CHAN, NULL) != DDI_SUCCESS) {
1481 
1482 			return (DDI_FAILURE);
1483 		}
1484 	}
1485 	return (DDI_SUCCESS);
1486 }
1487 
1488 static int
1489 gen_create_mn_disk_fd(dev_info_t *devi)
1490 {
1491 	struct driver_minor_data *dmdp;
1492 	int instance = ddi_get_instance(devi);
1493 
1494 	for (dmdp = disk_minor_data; dmdp->name != NULL; dmdp++) {
1495 		if (ddi_create_minor_node(devi, dmdp->name, dmdp->type,
1496 		    (INST_TO_MINOR(instance)) | dmdp->minor,
1497 		    DDI_NT_BLOCK_CHAN, NULL) != DDI_SUCCESS) {
1498 
1499 			return (DDI_FAILURE);
1500 		}
1501 	}
1502 	return (DDI_SUCCESS);
1503 }
1504 
1505 static int
1506 gen_create_serial(dev_info_t *devi)
1507 {
1508 	struct driver_serial_minor_data *dmdp;
1509 	int instance = ddi_get_instance(devi);
1510 
1511 	for (dmdp = serial_minor_data; dmdp->name != NULL; dmdp++) {
1512 		if (ddi_create_minor_node(devi, dmdp->name, dmdp->type,
1513 		    (INST_TO_MINOR(instance)) | dmdp->minor,
1514 		    dmdp->node_type, NULL) != DDI_SUCCESS) {
1515 
1516 			return (DDI_FAILURE);
1517 		}
1518 	}
1519 	return (DDI_SUCCESS);
1520 }
1521 
1522 static int
1523 gen_create_net(dev_info_t *devi)
1524 {
1525 	int instance = ddi_get_instance(devi);
1526 	char minorname[32];
1527 
1528 	if (gen_create_properties(devi) != DDI_SUCCESS)
1529 		return (DDI_FAILURE);
1530 
1531 	(void) snprintf(minorname, sizeof (minorname), "gen_drv%d", instance);
1532 	return (ddi_create_minor_node(devi, minorname, S_IFCHR,
1533 	    INST_TO_MINOR(instance), DDI_NT_NET, 0));
1534 }
1535 
1536 static int
1537 gen_create_minor_nodes(dev_info_t *devi, struct dstate *dstatep)
1538 {
1539 	int rval = DDI_SUCCESS;
1540 	char *node_name;
1541 
1542 	node_name = ddi_node_name(devi);
1543 
1544 	if (strcmp(node_name, "disk_chan") == 0) {
1545 		rval = gen_create_mn_disk_chan(devi);
1546 	} else if (strcmp(node_name, "disk_wwn") == 0) {
1547 		rval = gen_create_mn_disk_wwn(devi);
1548 	} else if (strcmp(node_name, "disk_cdrom") == 0) {
1549 		rval = gen_create_mn_disk_cdrom(devi);
1550 	} else if (strcmp(node_name, "disk_fd") == 0) {
1551 		rval = gen_create_mn_disk_fd(devi);
1552 	} else if (strcmp(node_name, "cgtwenty") == 0) {
1553 		rval = gen_create_display(devi);
1554 	} else if (strcmp(node_name, "genzs") == 0) {
1555 		rval = gen_create_serial(devi);
1556 	} else if (strcmp(node_name, "net") == 0) {
1557 		rval = gen_create_net(devi);
1558 	} else {
1559 		int instance = ddi_get_instance(devi);
1560 		char *node_type;
1561 
1562 		/*
1563 		 * Solaris may directly hang the node_type off the minor node
1564 		 * (without making a copy).  Since we free the node_type
1565 		 * property below we need to make a private copy to pass
1566 		 * to ddi_create_minor_node to avoid devinfo snapshot panics.
1567 		 * We store a pointer to our copy in dstate and free it in
1568 		 * gen_detach after the minor nodes have been deleted by
1569 		 * ddi_remove_minor_node.
1570 		 */
1571 		if (ddi_prop_lookup_string(DDI_DEV_T_ANY, devi,
1572 		    DDI_PROP_DONTPASS, "node-type", &node_type) != 0) {
1573 			cmn_err(CE_WARN, "couldn't get node-type\n");
1574 			return (DDI_FAILURE);
1575 		}
1576 		if (node_type) {
1577 			dstatep->node_type = kmem_alloc(
1578 			    strlen(node_type) + 1, KM_SLEEP);
1579 			(void) strcpy(dstatep->node_type, node_type);
1580 		}
1581 		ddi_prop_free(node_type);
1582 
1583 		/* the minor name is the same as the node name */
1584 		if (ddi_create_minor_node(devi, node_name, S_IFCHR,
1585 		    (INST_TO_MINOR(instance)), dstatep->node_type, NULL) !=
1586 		    DDI_SUCCESS) {
1587 			if (dstatep->node_type) {
1588 				kmem_free(dstatep->node_type,
1589 				    strlen(dstatep->node_type) + 1);
1590 				dstatep->node_type = NULL;
1591 			}
1592 			return (DDI_FAILURE);
1593 		}
1594 		return (DDI_SUCCESS);
1595 	}
1596 
1597 	if (rval != DDI_SUCCESS) {
1598 		ddi_prop_remove_all(devi);
1599 		ddi_remove_minor_node(devi, NULL);
1600 	}
1601 
1602 	return (rval);
1603 }
1604 
1605 /*ARGSUSED*/
1606 static void
1607 gen_event_cb(dev_info_t *dip, ddi_eventcookie_t cookie, void *arg,
1608     void *impl_data)
1609 {
1610 	if (gen_debug)
1611 		cmn_err(CE_NOTE, "gen_event_cb invoked");
1612 
1613 }
1614