/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* generic character driver
*/
#include <sys/types.h>
#include <sys/param.h>
#include <sys/errno.h>
#include <sys/uio.h>
#include <sys/buf.h>
#include <sys/modctl.h>
#include <sys/open.h>
#include <sys/kmem.h>
#include <sys/conf.h>
#include <sys/cmn_err.h>
#include <sys/stat.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/sunndi.h>
#define NUMEVENTS 6
#define COMPONENTS 2
#define COMP_0_MAXPWR 3
#define COMP_1_MAXPWR 2
#define MINPWR 0
static int maxpwr[] = { COMP_0_MAXPWR, COMP_1_MAXPWR };
/*
* The state for each generic device.
* NOTE: We save the node_type in the state structure. The node_type string
* (and not a copy) is stashed in a minor node by ddi_create_minor_node(),
* so ddi_remove_minor_node() must occur prior to state free.
*/
typedef struct dstate {
uint_t flag;
dev_info_t *dip; /* my devinfo handle */
char *node_type; /* stable node_type copy */
ddi_callback_id_t gen_cb_ids[NUMEVENTS];
kmutex_t lock;
char *nodename;
int level[COMPONENTS]; /* pm level */
int busy[COMPONENTS]; /* busy state */
} dstate_t;
static void *dstates;
static int gen_debug = 0;
#ifdef DEBUG
#define gen_debug gen_debug_on
static int gen_debug_on = 0;
#define GEN_DEBUG(args) if (gen_debug) cmn_err args
#else
#define GEN_DEBUG(args)
#endif
extern void prom_printf(const char *fmt, ...);
static int gen_open(dev_t *devp, int flag, int otyp, cred_t *cred);
static int gen_close(dev_t devp, int flag, int otyp, cred_t *cred);
static int gen_read(dev_t dev, struct uio *uiop, cred_t *credp);
static int gen_write(dev_t dev, struct uio *uiop, cred_t *credp);
static int gen_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
cred_t *credp, int *rvalp);
static int gen_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
static int gen_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
static void gen_event_cb(dev_info_t *dip, ddi_eventcookie_t cookie,
void *arg, void *impl_data);
static int gen_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
void **result);
static int gen_create_minor_nodes(dev_info_t *, struct dstate *);
static int gen_power(dev_info_t *, int, int);
static struct cb_ops gen_cb_ops = {
gen_open, /* open */
gen_close, /* close */
nodev, /* strategy */
nodev, /* print */
nodev, /* dump */
gen_read, /* read */
gen_write, /* write */
gen_ioctl, /* ioctl */
nodev, /* devmap */
nodev, /* mmap */
nodev, /* segmap */
nochpoll, /* poll */
ddi_prop_op, /* prop_op */
NULL, /* streamtab */
D_NEW | D_MP | D_HOTPLUG, /* flag */
CB_REV, /* cb_rev */
nodev, /* aread */
nodev /* awrite */
};
static struct dev_ops gen_ops = {
DEVO_REV, /* devo_rev */
0, /* refcnt */
gen_info, /* getinfo */
nulldev, /* identify */
nulldev, /* probe */
gen_attach, /* attach */
gen_detach, /* detach */
nodev, /* reset */
&gen_cb_ops, /* driver ops */
(struct bus_ops *)0, /* bus ops */
gen_power, /* power */
ddi_quiesce_not_supported, /* devo_quiesce */
};
/*
* INST_TO_MINOR() gives the starting minor number for a given gen_drv driver
* instance. A shift left by 6 bits allows for each instance to have upto
* 64 (2^6) minor numbers. The maximum minor number allowed by the system
* is L_MAXMIN32 (0x3ffff). This effectively limits the gen_drv instance
* numbers from 0 to 0xfff for a total of 4096 instances.
*/
#define INST_TO_MINOR(i) (i << 6)
#define MINOR_TO_INST(mn) (mn >> 6)
static char *mnodetypes[] = {
"ddi_nt",
"ddi_nt:device_type",
"ddi_nt:device_class:bus_class",
"ddi_nt2",
"ddi_nt2:device_type",
"ddi_nt2:device_type:bus_class",
};
#define N_NTYPES (sizeof (mnodetypes) / sizeof (char *))
static struct modldrv modldrv = {
&mod_driverops,
"generic test driver",
&gen_ops
};
static struct modlinkage modlinkage = {
MODREV_1, &modldrv, NULL
};
/*
* flags
*/
#define OPEN_FLAG 0x001
#define PWR_HAS_CHANGED_ON_RESUME_FLAG 0x002
#define FAIL_SUSPEND_FLAG 0x004
#define PUP_WITH_PWR_HAS_CHANGED_FLAG 0x008
#define POWER_FLAG 0x010
#define LOWER_POWER_FLAG 0x020
#define NO_INVOL_FLAG 0x040
#define PM_SUPPORTED_FLAG 0x080
/*
* ioctl commands (non-devctl ioctl commands)
*/
#define GENDRV_IOCTL ('P' << 8)
#define GENDRV_IOFAULT_SIMULATE (GENDRV_IOCTL | 0)
#define GENDRV_NDI_EVENT_TEST (GENDRV_IOCTL | 1)
int
_init(void)
{
int e;
if ((e = ddi_soft_state_init(&dstates,
sizeof (struct dstate), 0)) != 0) {
return (e);
}
if ((e = mod_install(&modlinkage)) != 0) {
ddi_soft_state_fini(&dstates);
}
return (e);
}
int
_fini(void)
{
int e;
if ((e = mod_remove(&modlinkage)) != 0) {
return (e);
}
ddi_soft_state_fini(&dstates);
return (e);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
static int
gen_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
{
int instance = ddi_get_instance(devi);
struct dstate *dstatep;
int rval;
int n_devs;
int n_minorcomps;
int isclone;
ddi_eventcookie_t dev_offline_cookie, dev_reset_cookie;
ddi_eventcookie_t bus_reset_cookie, bus_quiesce_cookie;
ddi_eventcookie_t bus_unquiesce_cookie, bus_test_post_cookie;
int i_init = 0;
int level_tmp;
int i;
char *pm_comp[] = {
"NAME=leaf0",
"0=D0",
"1=D1",
"2=D2",
"3=D3",
"NAME=leaf1",
"0=off",
"1=blank",
"2=on"};
char *pm_hw_state = {"needs-suspend-resume"};
switch (cmd) {
case DDI_ATTACH:
if (ddi_soft_state_zalloc(dstates, instance) !=
DDI_SUCCESS) {
cmn_err(CE_CONT, "%s%d: can't allocate state\n",
ddi_get_name(devi), instance);
return (DDI_FAILURE);
}
dstatep = ddi_get_soft_state(dstates, instance);
dstatep->dip = devi;
mutex_init(&dstatep->lock, NULL, MUTEX_DRIVER, NULL);
n_devs = ddi_prop_get_int(DDI_DEV_T_ANY, devi, 0,
"ndevs", 1);
isclone = ddi_prop_get_int(DDI_DEV_T_ANY, devi, 0,
"isclone", 0);
n_minorcomps = ddi_prop_get_int(DDI_DEV_T_ANY, devi, 0,
"ncomps", 1);
GEN_DEBUG((CE_CONT,
"%s%d attaching: n_devs=%d n_minorcomps=%d isclone=%d",
ddi_get_name(devi), ddi_get_instance(devi),
n_devs, n_minorcomps, isclone));
if (isclone) {
if (ddi_create_minor_node(devi, "gen", S_IFCHR,
INST_TO_MINOR(instance), mnodetypes[0],
isclone) != DDI_SUCCESS) {
ddi_remove_minor_node(devi, NULL);
ddi_soft_state_free(dstates, instance);
cmn_err(CE_WARN, "%s%d: can't create minor "
"node", ddi_get_name(devi), instance);
return (DDI_FAILURE);
}
rval = DDI_SUCCESS;
} else {
rval = gen_create_minor_nodes(devi, dstatep);
if (rval != DDI_SUCCESS) {
ddi_prop_remove_all(devi);
ddi_remove_minor_node(devi, NULL);
ddi_soft_state_free(dstates, instance);
cmn_err(CE_WARN, "%s%d: can't create minor "
"nodes", ddi_get_name(devi), instance);
return (DDI_FAILURE);
}
}
if (ddi_get_eventcookie(devi, "pshot_dev_offline",
&dev_offline_cookie) == DDI_SUCCESS) {
(void) ddi_add_event_handler(devi, dev_offline_cookie,
gen_event_cb, NULL, &(dstatep->gen_cb_ids[0]));
}
if (ddi_get_eventcookie(devi, "pshot_dev_reset",
&dev_reset_cookie) == DDI_SUCCESS) {
(void) ddi_add_event_handler(devi, dev_reset_cookie,
gen_event_cb, NULL, &(dstatep->gen_cb_ids[1]));
}
if (ddi_get_eventcookie(devi, "pshot_bus_reset",
&bus_reset_cookie) == DDI_SUCCESS) {
(void) ddi_add_event_handler(devi, bus_reset_cookie,
gen_event_cb, NULL, &(dstatep->gen_cb_ids[2]));
}
if (ddi_get_eventcookie(devi, "pshot_bus_quiesce",
&bus_quiesce_cookie) == DDI_SUCCESS) {
(void) ddi_add_event_handler(devi, bus_quiesce_cookie,
gen_event_cb, NULL, &(dstatep->gen_cb_ids[3]));
}
if (ddi_get_eventcookie(devi, "pshot_bus_unquiesce",
&bus_unquiesce_cookie) == DDI_SUCCESS) {
(void) ddi_add_event_handler(devi,
bus_unquiesce_cookie, gen_event_cb,
NULL, &(dstatep->gen_cb_ids[4]));
}
if (ddi_get_eventcookie(devi, "pshot_bus_test_post",
&bus_test_post_cookie) == DDI_SUCCESS) {
(void) ddi_add_event_handler(devi,
bus_test_post_cookie, gen_event_cb,
NULL, &(dstatep->gen_cb_ids[5]));
}
/*
* initialize the devices' pm state
*/
mutex_enter(&dstatep->lock);
dstatep->flag &= ~OPEN_FLAG;
dstatep->flag &= ~PWR_HAS_CHANGED_ON_RESUME_FLAG;
dstatep->flag &= ~FAIL_SUSPEND_FLAG;
dstatep->flag &= ~PUP_WITH_PWR_HAS_CHANGED_FLAG;
dstatep->flag |= LOWER_POWER_FLAG;
dstatep->flag &= ~NO_INVOL_FLAG;
dstatep->flag |= PM_SUPPORTED_FLAG;
dstatep->busy[0] = 0;
dstatep->busy[1] = 0;
dstatep->level[0] = -1;
dstatep->level[1] = -1;
mutex_exit(&dstatep->lock);
/*
* stash the nodename
*/
dstatep->nodename = ddi_node_name(devi);
/*
* Check if the no-involuntary-power-cycles property
* was created. Set NO_INVOL_FLAG if so.
*/
if (ddi_prop_exists(DDI_DEV_T_ANY, dstatep->dip,
(DDI_PROP_DONTPASS | DDI_PROP_NOTPROM),
"no-involuntary-power-cycles") == 1) {
GEN_DEBUG((CE_CONT,
"%s%d: DDI_ATTACH:\n\tno-involuntary-power-cycles"
" property was created",
ddi_node_name(devi), ddi_get_instance(devi)));
mutex_enter(&dstatep->lock);
dstatep->flag |= NO_INVOL_FLAG;
mutex_exit(&dstatep->lock);
}
/*
* Check if the dependency-property property
* was created.
*/
if (ddi_prop_exists(DDI_DEV_T_ANY, dstatep->dip,
(DDI_PROP_DONTPASS | DDI_PROP_NOTPROM),
"dependency-property") == 1) {
GEN_DEBUG((CE_CONT,
"%s%d: DDI_ATTACH:\n\tdependency-property"
" property was created",
ddi_node_name(devi), ddi_get_instance(devi)));
}
/*
* create the pm-components property. two comps:
* 4 levels on comp0, 3 on comp 1.
* - skip for a "tape" device, clear PM_SUPPORTED_FLAG
*/
if (strcmp(ddi_node_name(devi), "tape") != 0) {
if (ddi_prop_update_string_array(DDI_DEV_T_NONE, devi,
"pm-components", pm_comp, 9) != DDI_PROP_SUCCESS) {
cmn_err(CE_WARN, "%s%d: %s\n",
ddi_node_name(devi),
ddi_get_instance(devi),
"unable to create \"pm-components\" "
" property.");
return (DDI_FAILURE);
}
} else {
mutex_enter(&dstatep->lock);
dstatep->flag &= ~PM_SUPPORTED_FLAG;
mutex_exit(&dstatep->lock);
}
/*
* Check if the pm-components property was created
*/
if (dstatep->flag & PM_SUPPORTED_FLAG) {
if (ddi_prop_exists(DDI_DEV_T_ANY, dstatep->dip,
(DDI_PROP_DONTPASS | DDI_PROP_NOTPROM),
"pm-components") != 1) {
cmn_err(CE_WARN, "%s%d: DDI_ATTACH:\n\t%s",
ddi_node_name(devi),
ddi_get_instance(devi),
"\"pm-components\" property does"
" not exist");
return (DDI_FAILURE);
} else {
GEN_DEBUG((CE_CONT, "%s%d: DDI_ATTACH:"
" created pm-components property",
ddi_node_name(devi),
ddi_get_instance(devi)));
}
}
/*
* create the pm-hardware-state property.
* needed to get DDI_SUSPEND and DDI_RESUME calls
*/
if (ddi_prop_update_string(DDI_DEV_T_NONE, devi,
"pm-hardware-state", pm_hw_state) != DDI_PROP_SUCCESS) {
cmn_err(CE_WARN, "%s%d: DDI_ATTACH:\n\t%s\n",
ddi_node_name(devi), ddi_get_instance(devi),
"unable to create \"pm-hardware-state\" "
" property.");
return (DDI_FAILURE);
}
/*
* set power levels to max via pm_raise_power(),
*/
mutex_enter(&dstatep->lock);
i_init = (dstatep->flag & PM_SUPPORTED_FLAG) ? 0 : COMPONENTS;
mutex_exit(&dstatep->lock);
for (i = i_init; i < COMPONENTS; i++) {
GEN_DEBUG((CE_CONT,
"%s%d: DDI_ATTACH: pm_raise_power comp %d "
"to level %d", ddi_node_name(devi),
ddi_get_instance(devi), i, maxpwr[i]));
if (pm_raise_power(dstatep->dip, i, maxpwr[i]) !=
DDI_SUCCESS) {
cmn_err(CE_WARN,
"%s%d: DDI_ATTACH: pm_raise_power failed\n",
ddi_node_name(devi),
ddi_get_instance(devi));
dstatep->level[i] = -1;
return (DDI_FAILURE);
}
}
if (rval == DDI_SUCCESS) {
ddi_report_dev(devi);
}
return (rval);
case DDI_RESUME:
GEN_DEBUG((CE_CONT, "%s%d: DDI_RESUME", ddi_node_name(devi),
ddi_get_instance(devi)));
dstatep = ddi_get_soft_state(dstates, ddi_get_instance(devi));
if (dstatep == NULL) {
return (DDI_FAILURE);
}
/*
* Call pm_power_has_changed() if flag
* PWR_HAS_CHANGED_ON_RESUME_FLAG is set,
* then clear the flag
*/
mutex_enter(&dstatep->lock);
i_init = (dstatep->flag & PM_SUPPORTED_FLAG) ? 0 : COMPONENTS;
mutex_exit(&dstatep->lock);
if (dstatep->flag & PWR_HAS_CHANGED_ON_RESUME_FLAG) {
for (i = i_init; i < COMPONENTS; i++) {
GEN_DEBUG((CE_CONT,
"%s%d: DDI_RESUME: pm_power_has_changed "
"comp %d to level %d", ddi_node_name(devi),
ddi_get_instance(devi), i, maxpwr[i]));
mutex_enter(&dstatep->lock);
level_tmp = dstatep->level[i];
dstatep->level[i] = maxpwr[i];
if (pm_power_has_changed(dstatep->dip, i,
maxpwr[i]) != DDI_SUCCESS) {
cmn_err(CE_WARN,
"%s%d: DDI_RESUME:\n\t"
" pm_power_has_changed"
" failed: comp %d to level %d\n",
ddi_node_name(devi),
ddi_get_instance(devi),
i, maxpwr[i]);
dstatep->level[i] = level_tmp;
}
mutex_exit(&dstatep->lock);
}
} else {
/*
* Call pm_raise_power() instead
*/
for (i = i_init; i < COMPONENTS; i++) {
GEN_DEBUG((CE_CONT,
"%s%d: DDI_RESUME: pm_raise_power"
" comp %d to level %d",
ddi_node_name(devi), ddi_get_instance(devi),
i, maxpwr[i]));
if (pm_raise_power(dstatep->dip, i, maxpwr[i])
!= DDI_SUCCESS) {
cmn_err(CE_WARN,
"%s%d: DDI_RESUME:"
"\n\tpm_raise_power"
"failed: comp %d to level %d\n",
ddi_node_name(devi),
ddi_get_instance(devi),
i, maxpwr[i]);
}
}
}
return (DDI_SUCCESS);
default:
GEN_DEBUG((CE_WARN, "attach: default"));
return (DDI_FAILURE);
}
}
static int
gen_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
{
struct dstate *dstatep;
int instance;
int i;
int rv;
int rm_power;
int level_tmp;
#ifdef DEBUG
int n_devs;
int n_minorcomps;
int isclone;
#endif
switch (cmd) {
case DDI_DETACH:
GEN_DEBUG((CE_CONT, "%s%d: DDI_DETACH", ddi_node_name(devi),
ddi_get_instance(devi)));
instance = ddi_get_instance(devi);
dstatep = ddi_get_soft_state(dstates, instance);
if (dstatep == NULL) {
return (DDI_FAILURE);
}
#ifdef DEBUG
n_devs = ddi_prop_get_int(DDI_DEV_T_ANY, devi, 0,
"ndevs", 1);
isclone = ddi_prop_get_int(DDI_DEV_T_ANY, devi, 0,
"isclone", 0);
n_minorcomps = ddi_prop_get_int(DDI_DEV_T_ANY, devi, 0,
"ncomps", 1);
#endif /* DEBUG */
/*
* power off component 1.
*/
if (dstatep->flag & PM_SUPPORTED_FLAG) {
GEN_DEBUG((CE_CONT,
"%s%d: DDI_DETACH: pm_lower_power comp 1 level %d",
ddi_node_name(devi), ddi_get_instance(devi),
MINPWR));
if (pm_lower_power(dstatep->dip, 1, MINPWR)
!= DDI_SUCCESS) {
cmn_err(CE_WARN, "%s%d: DDI_DETACH:\n\t"
"pm_lower_power failed for comp 1 to"
" level %d\n", ddi_node_name(devi),
ddi_get_instance(devi), MINPWR);
return (DDI_FAILURE);
}
/*
* check power level. Issue pm_power_has_changed
* if not at MINPWR.
*/
mutex_enter(&dstatep->lock);
level_tmp = dstatep->level[1];
dstatep->level[1] = MINPWR;
if (dstatep->level[1] != MINPWR) {
GEN_DEBUG((CE_NOTE, "%s%d: DDI_DETACH:"
" power off via pm_power_has_changed"
" instead", ddi_node_name(devi),
ddi_get_instance(devi)));
if (pm_power_has_changed(dstatep->dip,
1, MINPWR) != DDI_SUCCESS) {
GEN_DEBUG((CE_NOTE, "%s%d: DDI_DETACH:"
" pm_power_has_changed failed for"
" comp 1 to level %d",
ddi_node_name(devi),
ddi_get_instance(devi),
MINPWR));
dstatep->level[1] = level_tmp;
mutex_exit(&dstatep->lock);
return (DDI_FAILURE);
}
}
mutex_exit(&dstatep->lock);
}
/*
* If the LOWER_POWER_FLAG flag is not set,
* don't call pm_lowr_power() for comp 0.
* This should be used only for the XXXXX@XX,no_invol
* devices that export the
* no-involuntary-power-cycles property
*/
if (!(dstatep->flag & LOWER_POWER_FLAG) &&
dstatep->flag & PM_SUPPORTED_FLAG) {
cmn_err(CE_NOTE, "%s%d: DDI_DETACH:\n\t"
" NOT CALLING PM_LOWER_POWER():"
" LOWER_POWER_FLAG NOT SET\n",
ddi_node_name(devi), ddi_get_instance(devi));
} else if (dstatep->flag & PM_SUPPORTED_FLAG) {
GEN_DEBUG((CE_CONT,
"%s%d: DDI_DETACH: pm_lower_power comp 0 level %d",
ddi_node_name(devi), ddi_get_instance(devi),
MINPWR));
if (pm_lower_power(dstatep->dip, 0, MINPWR)
!= DDI_SUCCESS) {
cmn_err(CE_WARN, "%s%d: DDI_DETACH:\n\t"
"pm_lower_power failed for comp 0 to"
" level %d\n", ddi_node_name(devi),
ddi_get_instance(devi), MINPWR);
return (DDI_FAILURE);
}
/*
* check power level. Issue pm_power_has_changed
* if not at MINPWR.
*/
mutex_enter(&dstatep->lock);
level_tmp = dstatep->level[0];
dstatep->level[0] = MINPWR;
if (dstatep->level[0] != MINPWR) {
GEN_DEBUG((CE_NOTE, "%s%d: DDI_DETACH:"
" power off via pm_power_has_changed"
" instead", ddi_node_name(devi),
ddi_get_instance(devi)));
if (pm_power_has_changed(dstatep->dip,
0, MINPWR) != DDI_SUCCESS) {
GEN_DEBUG((CE_NOTE, "%s%d: DDI_DETACH:"
" pm_power_has_changed failed for"
" comp 0 to level %d",
ddi_node_name(devi),
ddi_get_instance(devi),
MINPWR));
dstatep->level[0] = level_tmp;
mutex_exit(&dstatep->lock);
return (DDI_FAILURE);
}
}
mutex_exit(&dstatep->lock);
}
GEN_DEBUG((CE_CONT,
"%s%d detaching: n_devs=%d n_minorcomps=%d isclone=%d",
ddi_node_name(devi), ddi_get_instance(devi),
n_devs, n_minorcomps, isclone));
for (i = 0; i < NUMEVENTS; i++) {
if (dstatep->gen_cb_ids[i]) {
(void) ddi_remove_event_handler(dstatep->gen_cb_ids[i]);
dstatep->gen_cb_ids[i] = NULL;
}
}
ddi_prop_remove_all(devi);
ddi_remove_minor_node(devi, NULL);
if (dstatep->node_type)
kmem_free(dstatep->node_type,
strlen(dstatep->node_type) + 1);
ddi_soft_state_free(dstates, instance);
return (DDI_SUCCESS);
case DDI_SUSPEND:
GEN_DEBUG((CE_CONT, "%s%d: DDI_SUSPEND",
ddi_node_name(devi), ddi_get_instance(devi)));
instance = ddi_get_instance(devi);
dstatep = ddi_get_soft_state(dstates, instance);
if (dstatep == NULL) {
return (DDI_FAILURE);
}
/*
* fail the suspend if FAIL_SUSPEND_FLAG is set.
* clear the FAIL_SUSPEND_FLAG flag
*/
mutex_enter(&dstatep->lock);
if (dstatep->flag & FAIL_SUSPEND_FLAG) {
GEN_DEBUG((CE_CONT, "%s%d: DDI_SUSPEND:"
" FAIL_SUSPEND_FLAG is set,"
" fail suspend",
ddi_node_name(devi), ddi_get_instance(devi)));
dstatep->flag &= ~FAIL_SUSPEND_FLAG;
rv = DDI_FAILURE;
} else {
rv = DDI_SUCCESS;
}
mutex_exit(&dstatep->lock);
/*
* Issue ddi_removing_power() to determine if the suspend
* was initiated by either CPR or DR. If CPR, the system
* will be powered OFF; if this driver has set the
* NO_INVOL_FLAG, then refuse to suspend. If DR, power
* will not be removed, thus allow the suspend.
*/
if (dstatep->flag & NO_INVOL_FLAG &&
dstatep->flag & PM_SUPPORTED_FLAG) {
GEN_DEBUG((CE_CONT, "%s%d: DDI_SUSPEND:"
" check via ddi_removing_power()",
ddi_node_name(devi), ddi_get_instance(devi)));
rm_power = ddi_removing_power(dstatep->dip);
if (rm_power < 0) {
cmn_err(CE_WARN, "%s%d: DDI_SUSPEND:"
" ddi_removing_power() failed\n",
ddi_node_name(devi),
ddi_get_instance(devi));
} else if (rm_power == 1) {
/*
* CPR: power will be removed
*/
GEN_DEBUG((CE_CONT, "%s%d: DDI_SUSPEND:\n\t"
" CPR: POWER WILL BE REMOVED, THEREFORE"
" REFUSE TO SUSPEND", ddi_node_name(devi),
ddi_get_instance(devi)));
rv = DDI_FAILURE;
} else if (rm_power == 0) {
/*
* DR: power will not be removed
*/
GEN_DEBUG((CE_CONT, "%s%d: DDI_SUSPEND:\n\t"
" DR: POWER WILL NOT BE REMOVED, THEREFORE"
" ALLOW THE SUSPEND", ddi_node_name(devi),
ddi_get_instance(devi)));
rv = DDI_SUCCESS;
}
}
/*
* power OFF via pm_power_has_changed()
*/
mutex_enter(&dstatep->lock);
if (dstatep->flag & PM_SUPPORTED_FLAG &&
!(dstatep->flag & NO_INVOL_FLAG)) {
level_tmp = dstatep->level[0];
dstatep->level[0] = MINPWR;
GEN_DEBUG((CE_CONT,
"%s%d: DDI_SUSPEND: pm_power_has_changed comp 0"
" level %d", ddi_node_name(devi),
ddi_get_instance(devi), MINPWR));
if (pm_power_has_changed(dstatep->dip, 0, MINPWR)
!= DDI_SUCCESS) {
cmn_err(CE_WARN, "%s%d: DDI_SUSPEND:\n\t"
"pm_power_has_changed failed for comp 0 to"
" level %d\n", ddi_node_name(devi),
ddi_get_instance(devi), MINPWR);
dstatep->level[0] = level_tmp;
mutex_exit(&dstatep->lock);
return (DDI_FAILURE);
}
}
mutex_exit(&dstatep->lock);
return (rv);
default:
return (DDI_FAILURE);
}
}
/* ARGSUSED */
static int
gen_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
{
dev_t dev;
int instance;
if (infocmd != DDI_INFO_DEVT2INSTANCE)
return (DDI_FAILURE);
dev = (dev_t)arg;
instance = MINOR_TO_INST(getminor(dev));
*result = (void *)(uintptr_t)instance;
return (DDI_SUCCESS);
}
/*ARGSUSED*/
static int
gen_open(dev_t *devp, int flag, int otyp, cred_t *cred)
{
minor_t minor;
struct dstate *dstatep;
if (otyp != OTYP_BLK && otyp != OTYP_CHR)
return (EINVAL);
minor = getminor(*devp);
if ((dstatep = ddi_get_soft_state(dstates,
MINOR_TO_INST(minor))) == NULL)
return (ENXIO);
mutex_enter(&dstatep->lock);
dstatep->flag |= OPEN_FLAG;
mutex_exit(&dstatep->lock);
GEN_DEBUG((CE_CONT,
"%s%d open",
dstatep->nodename, MINOR_TO_INST(minor)));
return (0);
}
/*ARGSUSED*/
static int
gen_close(dev_t dev, int flag, int otyp, cred_t *cred)
{
struct dstate *dstatep;
minor_t minor = getminor(dev);
if (otyp != OTYP_BLK && otyp != OTYP_CHR)
return (EINVAL);
dstatep = ddi_get_soft_state(dstates, MINOR_TO_INST(minor));
if (dstatep == NULL)
return (ENXIO);
mutex_enter(&dstatep->lock);
dstatep->flag &= ~OPEN_FLAG;
mutex_exit(&dstatep->lock);
GEN_DEBUG((CE_CONT,
"%s%d close",
dstatep->nodename, MINOR_TO_INST(minor)));
return (0);
}
/*ARGSUSED*/
static int
gen_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp)
{
struct dstate *dstatep;
ddi_eventcookie_t cookie;
int instance;
int rval = 0;
char *nodename;
int i;
struct devctl_iocdata *dcp;
uint_t state;
int ret;
int level_tmp;
instance = MINOR_TO_INST(getminor(dev));
dstatep = ddi_get_soft_state(dstates, instance);
nodename = dstatep->nodename;
if (dstatep == NULL)
return (ENXIO);
/*
* read devctl ioctl data
*/
if (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS)
return (EFAULT);
switch (cmd) {
case GENDRV_IOFAULT_SIMULATE:
if (ddi_get_eventcookie(dstatep->dip, DDI_DEVI_FAULT_EVENT,
&(cookie)) != NDI_SUCCESS)
return (DDI_FAILURE);
return (ndi_post_event(dstatep->dip, dstatep->dip, cookie,
NULL));
case GENDRV_NDI_EVENT_TEST:
if (ddi_get_eventcookie(dstatep->dip, "pshot_dev_offline",
&cookie) == NDI_SUCCESS) {
(void) ndi_post_event(dstatep->dip, dstatep->dip,
cookie, NULL);
}
if (ddi_get_eventcookie(dstatep->dip, "pshot_dev_reset",
&cookie) == NDI_SUCCESS) {
(void) ndi_post_event(dstatep->dip, dstatep->dip,
cookie, NULL);
}
if (ddi_get_eventcookie(dstatep->dip, "pshot_bus_reset",
&cookie) == NDI_SUCCESS) {
(void) ndi_post_event(dstatep->dip, dstatep->dip,
cookie, NULL);
}
if (ddi_get_eventcookie(dstatep->dip, "pshot_bus_quiesce",
&cookie) == NDI_SUCCESS) {
(void) ndi_post_event(dstatep->dip, dstatep->dip,
cookie, NULL);
}
if (ddi_get_eventcookie(dstatep->dip, "pshot_bus_unquiesce",
&cookie) == NDI_SUCCESS) {
(void) ndi_post_event(dstatep->dip, dstatep->dip,
cookie, NULL);
}
if (ddi_get_eventcookie(dstatep->dip, "pshot_bus_test_post",
&cookie) == NDI_SUCCESS) {
(void) ndi_post_event(dstatep->dip, dstatep->dip,
cookie, NULL);
}
break;
case DEVCTL_PM_PWR_HAS_CHANGED_ON_RESUME:
/*
* Issue pm_power_has_changed() call on DDI_RESUME
*/
mutex_enter(&dstatep->lock);
dstatep->flag |= PWR_HAS_CHANGED_ON_RESUME_FLAG;
mutex_exit(&dstatep->lock);
GEN_DEBUG((CE_CONT, "%s%d:"
" DEVCTL_PM_PWR_HAS_CHANGED_ON_RESUME", nodename,
instance));
break;
case DEVCTL_PM_FAIL_SUSPEND:
/*
* Fail the suspend attempt in DDI_SUSPEND
*/
mutex_enter(&dstatep->lock);
dstatep->flag |= FAIL_SUSPEND_FLAG;
mutex_exit(&dstatep->lock);
GEN_DEBUG((CE_CONT, "%s%d: DEVCTL_PM_FAIL_SUSPEND",
nodename, instance));
break;
case DEVCTL_PM_PUP_WITH_PWR_HAS_CHANGED:
/*
* Use pm_power_has_changed() to power up comp 0 when
* enforcing the comp 0 vs comp-not 0 dependency:
* Power up comp 0 first, if request for comp-not-0
* comes in.
* Else, default to pm_raise_power().
*/
mutex_enter(&dstatep->lock);
dstatep->flag |= PUP_WITH_PWR_HAS_CHANGED_FLAG;
mutex_exit(&dstatep->lock);
GEN_DEBUG((CE_CONT, "%s%d: DEVCTL_PM_PUP_WITH_PWR_HAS_CHANGED",
nodename, instance));
break;
case DEVCTL_PM_BUSY_COMP:
/*
* mark component 0 busy via a pm_busy_component() call.
* update the busy[] array.
*/
mutex_enter(&dstatep->lock);
++dstatep->busy[0];
GEN_DEBUG((CE_CONT, "%s%d: DEVCTL_PM_BUSY_COMP: comp 0:"
" busy=%d", nodename, instance, dstatep->busy[0]));
mutex_exit(&dstatep->lock);
ret = pm_busy_component(dstatep->dip, 0);
ASSERT(ret == DDI_SUCCESS);
break;
case DEVCTL_PM_BUSY_COMP_TEST:
/*
* test busy state on component 0
*/
mutex_enter(&dstatep->lock);
state = dstatep->busy[0];
if (copyout(&state, dcp->cpyout_buf,
sizeof (uint_t)) != 0) {
cmn_err(CE_WARN, "%s%d:"
" DEVCTL_PM_BUSY_COMP_TEST: copyout failed\n",
nodename, instance);
rval = EINVAL;
}
GEN_DEBUG((CE_CONT, "%s%d: DEVCTL_PM_BUSY_COMP_TEST:"
" comp 0 busy %d",
nodename, instance, state));
mutex_exit(&dstatep->lock);
break;
case DEVCTL_PM_IDLE_COMP:
/*
* mark component 0 idle via a pm_idle_component() call.
* NOP if dstatep->busy[0] == 0.
*/
mutex_enter(&dstatep->lock);
if (dstatep->busy[0] > 0) {
--dstatep->busy[0];
GEN_DEBUG((CE_CONT, "%s%d: DEVCTL_PM_IDLE_COMP:"
" comp 0: busy=%d", nodename, instance,
dstatep->busy[0]));
mutex_exit(&dstatep->lock);
ret = pm_idle_component(dstatep->dip, 0);
ASSERT(ret == DDI_SUCCESS);
} else {
mutex_exit(&dstatep->lock);
}
break;
case DEVCTL_PM_PROM_PRINTF:
(void) prom_printf("%s%d: PROM_PRINTF FROM GEN_DRV\n",
nodename, instance);
break;
case DEVCTL_PM_RAISE_PWR:
/*
* power up both components to MAXPWR via
* pm_raise_power() calls. this ioctl() cmd
* assumes that the current level is 0
*/
for (i = 0; i < COMPONENTS; i++) {
GEN_DEBUG((CE_CONT, "%s%d: DEVCTL_PM_RAISE_PWR:"
" comp %d old 0 new %d",
nodename, instance, i, maxpwr[i]));
if (pm_raise_power(dstatep->dip, 0, maxpwr[i])
!= DDI_SUCCESS) {
rval = EINVAL;
}
}
break;
case DEVCTL_PM_CHANGE_PWR_LOW:
/*
* power off both components via pm_power_has_changed() calls
*/
for (i = (COMPONENTS - 1); i >= 0; --i) {
GEN_DEBUG((CE_CONT, "%s%d: DEVCTL_PM_CHANGE_PWR_LOW:"
" comp %d new 0",
nodename, instance, i));
mutex_enter(&dstatep->lock);
level_tmp = dstatep->level[i];
dstatep->level[i] = 0;
if (pm_power_has_changed(dstatep->dip, i, 0)
!= DDI_SUCCESS) {
dstatep->level[i] = level_tmp;
rval = EINVAL;
}
mutex_exit(&dstatep->lock);
}
break;
case DEVCTL_PM_CHANGE_PWR_HIGH:
/*
* power up both components to MAXPWR via
* pm_power_has_changed() calls
*/
for (i = 0; i < COMPONENTS; i++) {
GEN_DEBUG((CE_CONT, "%s%d: DEVCTL_PM_CHANGE_PWR_HIGH:"
" comp %d new %d",
nodename, instance, i, maxpwr[i]));
mutex_enter(&dstatep->lock);
level_tmp = dstatep->level[i];
dstatep->level[i] = maxpwr[i];
if (pm_power_has_changed(dstatep->dip, i, maxpwr[i])
!= DDI_SUCCESS) {
dstatep->level[i] = level_tmp;
rval = EINVAL;
}
mutex_exit(&dstatep->lock);
}
break;
case DEVCTL_PM_POWER:
/*
* test if the gen_drv_power() routine has been called,
* then clear
*/
mutex_enter(&dstatep->lock);
state = (dstatep->flag & POWER_FLAG) ? 1 : 0;
if (copyout(&state, dcp->cpyout_buf,
sizeof (uint_t)) != 0) {
cmn_err(CE_WARN, "%s%d: DEVCTL_PM_POWER:"
" copyout failed\n", nodename, instance);
rval = EINVAL;
}
GEN_DEBUG((CE_CONT, "%s%d: %s POWER_FLAG: %d",
nodename, instance, "DEVCTL_PM_POWER", state));
dstatep->flag &= ~POWER_FLAG;
mutex_exit(&dstatep->lock);
break;
case DEVCTL_PM_NO_LOWER_POWER:
/*
* issue to not invoke pm_lower_power() on detach
*/
mutex_enter(&dstatep->lock);
dstatep->flag &= ~LOWER_POWER_FLAG;
mutex_exit(&dstatep->lock);
GEN_DEBUG((CE_CONT, "%s%d: DEVCTL_PM_NO_LOWER_POWER",
nodename, instance));
break;
default:
return (ENOTTY);
}
return (rval);
}
/*ARGSUSED*/
static int
gen_read(dev_t dev, struct uio *uiop, cred_t *credp)
{
return (0);
}
/*ARGSUSED*/
static int
gen_write(dev_t dev, struct uio *uiop, cred_t *credp)
{
return (0);
}
/*ARGSUSED0*/
static int
gen_power(dev_info_t *dip, int cmpt, int level)
{
struct dstate *dstatep;
int instance = ddi_get_instance(dip);
char *nodename = ddi_node_name(dip);
int level_tmp;
GEN_DEBUG((CE_CONT, "%s%d: power: cmpt %d to level %d",
nodename, instance, cmpt, level));
dstatep = ddi_get_soft_state(dstates, instance);
if (dstatep == NULL) {
return (DDI_FAILURE);
}
/*
* Keep track of the power levels for both components
* in the dstatep->comp[] array.
* Set comp 0 to full level if non-zero comps
* are being set to a higher, non-zero level.
*/
if (cmpt == 0) {
mutex_enter(&dstatep->lock);
dstatep->level[cmpt] = level;
mutex_exit(&dstatep->lock);
} else if (level > dstatep->level[cmpt] && level != 0 &&
dstatep->level[0] != COMP_0_MAXPWR) {
/*
* If component 0 is not at COMP_0_MAXPWR, and component 1
* is being powered ON, invoke pm_raise_power() or
* pm_power_has_changed() based on the
* PUP_WITH_PWR_HAS_CHANGED_FLAG flag.
* PUP_WITH_PWR_HAS_CHANGED_FLAG = FALSE by default, invoking
* pm_raise_power().
*/
if (!(dstatep->flag & PUP_WITH_PWR_HAS_CHANGED_FLAG)) {
/*
* first set comp 0 to level COMP_0_MAXPWR
*/
GEN_DEBUG((CE_CONT, "%s%d: power: "
"pm_raise_power: comp 0 to level %d",
nodename, instance, COMP_0_MAXPWR));
if (pm_raise_power(dip, 0, COMP_0_MAXPWR) !=
DDI_SUCCESS) {
cmn_err(CE_WARN,
"%s%d: power: pm_raise_power() "
"failed: comp 0 to level %d\n",
nodename, instance, COMP_0_MAXPWR);
return (DDI_FAILURE);
} else {
mutex_enter(&dstatep->lock);
dstatep->level[0] = COMP_0_MAXPWR;
/*
* now set the level on the non-zero comp
*/
dstatep->level[cmpt] = level;
mutex_exit(&dstatep->lock);
GEN_DEBUG((CE_CONT, "%s%d: power: "
"comp %d to level %d",
nodename, instance, cmpt, level));
}
} else {
GEN_DEBUG((CE_CONT, "%s%d: power: "
"pm_power_has_changed: comp 0 to level %d",
nodename, instance, COMP_0_MAXPWR));
mutex_enter(&dstatep->lock);
level_tmp = dstatep->level[0];
dstatep->level[0] = COMP_0_MAXPWR;
if (pm_power_has_changed(dip, 0, COMP_0_MAXPWR) !=
DDI_SUCCESS) {
cmn_err(CE_WARN,
"%s%d: power: pm_power_has_changed() "
"failed: comp 0 to level %d\n",
nodename, instance, COMP_0_MAXPWR);
dstatep->level[0] = level_tmp;
} else {
/*
* now set the level on the non-zero comp
*/
GEN_DEBUG((CE_CONT, "%s%d: power:"
" pm_power_has_changed: comp %d"
" to level %d", nodename, instance,
cmpt, level));
dstatep->level[cmpt] = level;
}
mutex_exit(&dstatep->lock);
}
} else {
mutex_enter(&dstatep->lock);
dstatep->level[cmpt] = level;
mutex_exit(&dstatep->lock);
}
return (DDI_SUCCESS);
}
/*
* Create properties of various data types for testing devfs events.
*/
static int
gen_create_properties(dev_info_t *devi)
{
int int_val = 3023;
int int_array[] = { 3, 10, 304, 230, 4};
int64_t int64_val = 20;
int64_t int64_array[] = { 12, 24, 36, 48};
char *string_val = "Dev_node_prop";
char *string_array[] = {"Dev_node_prop:0",
"Dev_node_prop:1", "Dev_node_prop:2", "Dev_node_prop:3"};
uchar_t byte_array[] = { (uchar_t)0xaa, (uchar_t)0x55,
(uchar_t)0x12, (uchar_t)0xcd };
char bytes[] = { (char)0x00, (char)0xef, (char)0xff };
if (ddi_prop_update_int(DDI_DEV_T_NONE, devi, "int", int_val)
!= DDI_PROP_SUCCESS)
return (DDI_FAILURE);
if (ddi_prop_update_int_array(DDI_DEV_T_NONE, devi, "int-array",
int_array, sizeof (int_array) / sizeof (int)) != DDI_PROP_SUCCESS)
return (DDI_FAILURE);
if (ddi_prop_update_int64(DDI_DEV_T_NONE, devi, "int64", int64_val)
!= DDI_PROP_SUCCESS)
return (DDI_FAILURE);
if (ddi_prop_update_int64_array(DDI_DEV_T_NONE, devi, "int64-array",
int64_array, sizeof (int64_array) / sizeof (int64_t))
!= DDI_PROP_SUCCESS)
return (DDI_FAILURE);
if (ddi_prop_update_string(DDI_DEV_T_NONE, devi, "string", string_val)
!= DDI_PROP_SUCCESS)
return (DDI_FAILURE);
if (ddi_prop_update_string_array(DDI_DEV_T_NONE, devi, "string-array",
string_array, sizeof (string_array) / sizeof (char *))
!= DDI_PROP_SUCCESS)
return (DDI_FAILURE);
if (ddi_prop_create(DDI_DEV_T_NONE, devi, DDI_PROP_CANSLEEP,
"boolean", NULL, 0) != DDI_PROP_SUCCESS)
return (DDI_FAILURE);
if (ddi_prop_update_byte_array(DDI_DEV_T_NONE, devi, "byte-array",
byte_array, sizeof (byte_array)) != DDI_PROP_SUCCESS)
return (DDI_FAILURE);
/* untyped property */
if (ddi_prop_create(DDI_DEV_T_NONE, devi, DDI_PROP_CANSLEEP, "untyped",
(caddr_t)bytes, sizeof (bytes)) != DDI_PROP_SUCCESS)
return (DDI_FAILURE);
return (DDI_SUCCESS);
}
static struct driver_minor_data {
char *name;
minor_t minor;
int type;
} disk_minor_data[] = {
{"a", 0, S_IFBLK},
{"b", 1, S_IFBLK},
{"c", 2, S_IFBLK},
{"d", 3, S_IFBLK},
{"e", 4, S_IFBLK},
{"f", 5, S_IFBLK},
{"g", 6, S_IFBLK},
{"h", 7, S_IFBLK},
{"a,raw", 0, S_IFCHR},
{"b,raw", 1, S_IFCHR},
{"c,raw", 2, S_IFCHR},
{"d,raw", 3, S_IFCHR},
{"e,raw", 4, S_IFCHR},
{"f,raw", 5, S_IFCHR},
{"g,raw", 6, S_IFCHR},
{"h,raw", 7, S_IFCHR},
{0}
};
static struct driver_serial_minor_data {
char *name;
minor_t minor;
int type;
char *node_type;
} serial_minor_data[] = {
{"0", 0, S_IFCHR, "ddi_serial"},
{"1", 1, S_IFCHR, "ddi_serial"},
{"0,cu", 2, S_IFCHR, "ddi_serial:dialout"},
{"1,cu", 3, S_IFCHR, "ddi_serial:dialout"},
{0}
};
static int
gen_create_display(dev_info_t *devi)
{
int instance = ddi_get_instance(devi);
char minor_name[15];
(void) sprintf(minor_name, "cgtwenty%d", instance);
return (ddi_create_minor_node(devi, minor_name, S_IFCHR,
INST_TO_MINOR(instance), DDI_NT_DISPLAY, NULL));
}
static int
gen_create_mn_disk_chan(dev_info_t *devi)
{
struct driver_minor_data *dmdp;
int instance = ddi_get_instance(devi);
if (gen_create_properties(devi) != DDI_SUCCESS)
return (DDI_FAILURE);
for (dmdp = disk_minor_data; dmdp->name != NULL; dmdp++) {
if (ddi_create_minor_node(devi, dmdp->name, dmdp->type,
(INST_TO_MINOR(instance)) | dmdp->minor,
DDI_NT_BLOCK_CHAN, NULL) != DDI_SUCCESS) {
return (DDI_FAILURE);
}
}
return (DDI_SUCCESS);
}
static uint_t
atod(char *s)
{
uint_t val = 0;
uint_t digit;
while (*s) {
if (*s >= '0' && *s <= '9')
digit = *s++ - '0';
else
break;
val = (val * 10) + digit;
}
return (val);
}
static int
gen_create_mn_disk_wwn(dev_info_t *devi)
{
struct driver_minor_data *dmdp;
int instance = ddi_get_instance(devi);
char *address = ddi_get_name_addr(devi);
int target, lun;
if (address[0] >= '0' && address[0] <= '9' &&
strchr(address, ',')) {
target = atod(address);
address = strchr(address, ',');
lun = atod(++address);
} else { /* this hack is for rm_stale_link() testing */
target = 10;
lun = 5;
}
if (ddi_prop_create(DDI_DEV_T_NONE, devi, DDI_PROP_CANSLEEP,
"target", (caddr_t)&target, sizeof (int))
!= DDI_PROP_SUCCESS) {
return (DDI_FAILURE);
}
if (ddi_prop_create(DDI_DEV_T_NONE, devi, DDI_PROP_CANSLEEP,
"lun", (caddr_t)&lun, sizeof (int))
!= DDI_PROP_SUCCESS) {
return (DDI_FAILURE);
}
for (dmdp = disk_minor_data; dmdp->name != NULL; dmdp++) {
if (ddi_create_minor_node(devi, dmdp->name, dmdp->type,
(INST_TO_MINOR(instance)) | dmdp->minor,
DDI_NT_BLOCK_WWN, NULL) != DDI_SUCCESS) {
return (DDI_FAILURE);
}
}
return (DDI_SUCCESS);
}
static int
gen_create_mn_disk_cdrom(dev_info_t *devi)
{
struct driver_minor_data *dmdp;
int instance = ddi_get_instance(devi);
for (dmdp = disk_minor_data; dmdp->name != NULL; dmdp++) {
if (ddi_create_minor_node(devi, dmdp->name, dmdp->type,
(INST_TO_MINOR(instance)) | dmdp->minor,
DDI_NT_CD_CHAN, NULL) != DDI_SUCCESS) {
return (DDI_FAILURE);
}
}
return (DDI_SUCCESS);
}
static int
gen_create_mn_disk_fd(dev_info_t *devi)
{
struct driver_minor_data *dmdp;
int instance = ddi_get_instance(devi);
for (dmdp = disk_minor_data; dmdp->name != NULL; dmdp++) {
if (ddi_create_minor_node(devi, dmdp->name, dmdp->type,
(INST_TO_MINOR(instance)) | dmdp->minor,
DDI_NT_BLOCK_CHAN, NULL) != DDI_SUCCESS) {
return (DDI_FAILURE);
}
}
return (DDI_SUCCESS);
}
static int
gen_create_serial(dev_info_t *devi)
{
struct driver_serial_minor_data *dmdp;
int instance = ddi_get_instance(devi);
for (dmdp = serial_minor_data; dmdp->name != NULL; dmdp++) {
if (ddi_create_minor_node(devi, dmdp->name, dmdp->type,
(INST_TO_MINOR(instance)) | dmdp->minor,
dmdp->node_type, NULL) != DDI_SUCCESS) {
return (DDI_FAILURE);
}
}
return (DDI_SUCCESS);
}
static int
gen_create_net(dev_info_t *devi)
{
int instance = ddi_get_instance(devi);
char minorname[32];
if (gen_create_properties(devi) != DDI_SUCCESS)
return (DDI_FAILURE);
(void) snprintf(minorname, sizeof (minorname), "gen_drv%d", instance);
return (ddi_create_minor_node(devi, minorname, S_IFCHR,
INST_TO_MINOR(instance), DDI_NT_NET, 0));
}
static int
gen_create_minor_nodes(dev_info_t *devi, struct dstate *dstatep)
{
int rval = DDI_SUCCESS;
char *node_name;
node_name = ddi_node_name(devi);
if (strcmp(node_name, "disk_chan") == 0) {
rval = gen_create_mn_disk_chan(devi);
} else if (strcmp(node_name, "disk_wwn") == 0) {
rval = gen_create_mn_disk_wwn(devi);
} else if (strcmp(node_name, "disk_cdrom") == 0) {
rval = gen_create_mn_disk_cdrom(devi);
} else if (strcmp(node_name, "disk_fd") == 0) {
rval = gen_create_mn_disk_fd(devi);
} else if (strcmp(node_name, "cgtwenty") == 0) {
rval = gen_create_display(devi);
} else if (strcmp(node_name, "genzs") == 0) {
rval = gen_create_serial(devi);
} else if (strcmp(node_name, "net") == 0) {
rval = gen_create_net(devi);
} else {
int instance = ddi_get_instance(devi);
char *node_type;
/*
* Solaris may directly hang the node_type off the minor node
* (without making a copy). Since we free the node_type
* property below we need to make a private copy to pass
* to ddi_create_minor_node to avoid devinfo snapshot panics.
* We store a pointer to our copy in dstate and free it in
* gen_detach after the minor nodes have been deleted by
* ddi_remove_minor_node.
*/
if (ddi_prop_lookup_string(DDI_DEV_T_ANY, devi,
DDI_PROP_DONTPASS, "node-type", &node_type) != 0) {
cmn_err(CE_WARN, "couldn't get node-type\n");
return (DDI_FAILURE);
}
if (node_type) {
dstatep->node_type = kmem_alloc(
strlen(node_type) + 1, KM_SLEEP);
(void) strcpy(dstatep->node_type, node_type);
}
ddi_prop_free(node_type);
/* the minor name is the same as the node name */
if (ddi_create_minor_node(devi, node_name, S_IFCHR,
(INST_TO_MINOR(instance)), dstatep->node_type, NULL) !=
DDI_SUCCESS) {
if (dstatep->node_type) {
kmem_free(dstatep->node_type,
strlen(dstatep->node_type) + 1);
dstatep->node_type = NULL;
}
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
if (rval != DDI_SUCCESS) {
ddi_prop_remove_all(devi);
ddi_remove_minor_node(devi, NULL);
}
return (rval);
}
/*ARGSUSED*/
static void
gen_event_cb(dev_info_t *dip, ddi_eventcookie_t cookie, void *arg,
void *impl_data)
{
if (gen_debug)
cmn_err(CE_NOTE, "gen_event_cb invoked");
}