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