acpinex_drv.c revision cd21e7c548ae2a3b5e522244bf798f2a6b4ba02d
/*
* 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
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright (c) 2009-2010, Intel Corporation.
* All rights reserved.
*/
/*
* Copyright 2012 Garrett D'Amore <garrett@damore.org>. All rights reserved.
*/
/*
* This module implements a nexus driver for the ACPI virtual bus.
* It does not handle any of the DDI functions passed up to it by the child
* drivers, but instead allows them to bubble up to the root node.
*/
#include <sys/ddi_impldefs.h>
#ifdef DEBUG
int acpinex_debug = 1;
#else
int acpinex_debug = 0;
#endif
/*
* Driver globals
*/
static kmutex_t acpinex_lock;
static void *acpinex_softstates;
void *);
/*
* Configuration data structures
*/
static struct bus_ops acpinex_bus_ops = {
BUSO_REV, /* busops_rev */
acpinex_bus_map, /* bus_map */
NULL, /* bus_get_intrspec */
NULL, /* bus_add_intrspec */
NULL, /* bus_remove_intrspec */
i_ddi_map_fault, /* bus_map_fault */
NULL, /* bus_dma_map */
ddi_dma_allochdl, /* bus_dma_allochdl */
ddi_dma_freehdl, /* bus_dma_freehdl */
ddi_dma_bindhdl, /* bus_dma_bindhdl */
ddi_dma_unbindhdl, /* bus_dma_unbindhdl */
ddi_dma_flush, /* bus_dma_flush */
ddi_dma_win, /* bus_dma_win */
ddi_dma_mctl, /* bus_dma_ctl */
acpinex_ctlops, /* bus_ctl */
ddi_bus_prop_op, /* bus_prop_op */
ndi_busop_get_eventcookie, /* bus_get_eventcookie */
ndi_busop_add_eventcall, /* bus_add_eventcall */
ndi_busop_remove_eventcall, /* bus_remove_eventcall */
ndi_post_event, /* bus_post_event */
NULL, /* bus_intr_ctl */
NULL, /* bus_config */
NULL, /* bus_unconfig */
acpinex_fm_init_child, /* bus_fm_init */
NULL, /* bus_fm_fini */
NULL, /* bus_fm_access_enter */
NULL, /* bus_fm_access_exit */
NULL, /* bus_power */
i_ddi_intr_ops /* bus_intr_op */
};
static struct cb_ops acpinex_cb_ops = {
acpinex_open, /* cb_open */
acpinex_close, /* cb_close */
nodev, /* cb_strategy */
nodev, /* cb_print */
nodev, /* cb_dump */
nodev, /* cb_read */
nodev, /* cb_write */
acpinex_ioctl, /* cb_ioctl */
nodev, /* cb_devmap */
nodev, /* cb_mmap */
nodev, /* cb_segmap */
nochpoll, /* cb_poll */
ddi_prop_op, /* cb_prop_op */
NULL, /* cb_str */
CB_REV, /* rev */
nodev, /* int (*cb_aread)() */
nodev /* int (*cb_awrite)() */
};
static struct dev_ops acpinex_ops = {
DEVO_REV, /* devo_rev, */
0, /* devo_refcnt */
acpinex_info, /* devo_getinfo */
nulldev, /* devo_identify */
nulldev, /* devo_probe */
acpinex_attach, /* devo_attach */
acpinex_detach, /* devo_detach */
nulldev, /* devo_reset */
&acpinex_cb_ops, /* devo_cb_ops */
&acpinex_bus_ops, /* devo_bus_ops */
nulldev, /* devo_power */
ddi_quiesce_not_needed /* devo_quiesce */
};
&mod_driverops, /* Type of module */
"ACPI virtual bus driver", /* name of module */
&acpinex_ops, /* driver ops */
};
static struct modlinkage modlinkage = {
MODREV_1, /* rev */
(void *)&modldrv,
};
/*
* Module initialization routines.
*/
int
_init(void)
{
int error;
/* Initialize soft state pointer. */
sizeof (acpinex_softstate_t), 8)) != 0) {
"acpinex: failed to initialize soft state structure.");
return (error);
}
/* Initialize event subsystem. */
/* Install the module. */
return (error);
}
return (0);
}
int
_fini(void)
{
int error;
/* Remove the module. */
return (error);
}
/* Shut down event subsystem. */
/* Free the soft state info. */
return (0);
}
int
{
}
static int
{
int instance;
if (infocmd == DDI_INFO_DEVT2INSTANCE) {
return (DDI_SUCCESS);
}
return (DDI_FAILURE);
}
static int
{
int instance;
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
/* Get and check instance number. */
if (instance >= ACPINEX_INSTANCE_MAX) {
"in acpinex_attach(), max %d.",
return (DDI_FAILURE);
}
/* Get soft state structure. */
!= DDI_SUCCESS) {
"object in acpinex_attach().");
return (DDI_FAILURE);
}
/* Initialize soft state structure */
"!acpinex: failed to get ACPI handle for %s.",
return (DDI_FAILURE);
}
/* Install event handler for child/descendant objects. */
}
"pm-hardware-state", "no-suspend-resume");
DDI_NO_AUTODETACH, 1);
return (DDI_SUCCESS);
}
static int
{
int instance;
if (instance >= ACPINEX_INSTANCE_MAX) {
"in acpinex_detach(), max %d.",
return (DDI_FAILURE);
}
"object for instance %d in acpinex_detach()", instance);
return (DDI_FAILURE);
}
switch (cmd) {
case DDI_DETACH:
return (DDI_FAILURE);
}
DDI_NO_AUTODETACH, 0);
return (DDI_SUCCESS);
case DDI_SUSPEND:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
static int
{
char *unitaddr;
name[0] = '\0';
} else {
"unit-address prop for %p.", (void *)child);
}
return (DDI_SUCCESS);
}
static int
{
char name[MAXNAMELEN];
if ((ndi_dev_is_persistent_node(child) == 0) &&
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* Control ops entry point:
*
* Requests handled completely:
* DDI_CTLOPS_INITCHILD
* DDI_CTLOPS_UNINITCHILD
* All others are passed to the parent.
*/
static int
void *result)
{
int rval = DDI_SUCCESS;
switch (op) {
case DDI_CTLOPS_INITCHILD:
break;
case DDI_CTLOPS_UNINITCHILD:
break;
case DDI_CTLOPS_REPORTDEV: {
if (rdip == (dev_info_t *)0)
return (DDI_FAILURE);
break;
}
default:
break;
}
return (rval);
}
/* ARGSUSED */
static int
{
"!acpinex: acpinex_bus_map called and it's unimplemented.");
return (DDI_ME_UNIMPLEMENTED);
}
static int
{
if (instance >= ACPINEX_INSTANCE_MAX) {
"range in acpinex_open, max %d.",
return (EINVAL);
}
"object for instance %d in acpinex_open().", instance);
return (EINVAL);
}
if (ACPINEX_IS_DEVCTL(minor)) {
return (0);
} else {
"!acpinex: invalid minor number %d in acpinex_open().",
minor);
return (EINVAL);
}
}
static int
{
if (instance >= ACPINEX_INSTANCE_MAX) {
"range in acpinex_close(), max %d.",
return (EINVAL);
}
"object for instance %d in acpinex_close().", instance);
return (EINVAL);
}
if (ACPINEX_IS_DEVCTL(minor)) {
return (0);
} else {
"!acpinex: invalid minor number %d in acpinex_close().",
minor);
return (EINVAL);
}
}
static int
int *rvalp)
{
int rv = 0;
if (instance >= ACPINEX_INSTANCE_MAX) {
"range in acpinex_ioctl(), max %d.",
return (EINVAL);
}
"object for instance %d in acpinex_ioctl().", instance);
return (EINVAL);
}
"!acpinex: invalid minor number %d in acpinex_ioctl().", minor);
return (rv);
}
/*
* FMA error callback.
* Register error handling callback with our parent. We will just call
* our children's error callbacks and return their status.
*/
static int
const void *impl_data)
{
/* Call our childrens error handlers */
}
/*
* Initialize our FMA resources
*/
static void
{
/*
* Request our capability level and get our parent's capability and ibc.
*/
/*
* Register error callback with our parent if supported.
*/
softsp);
}
}
/*
* Breakdown our FMA resources
*/
static void
{
/* Clean up allocated fm structures */
}
}
/*
* Initialize FMA resources for child devices.
* Called when child calls ddi_fm_init().
*/
static int
{
return (softsp->ans_fm_cap);
}