reactos/drivers/bus/acpi/events/evxfregn.c
Art Yerkes c501d8112c Create a branch for network fixes.
svn path=/branches/aicom-network-fixes/; revision=34994
2008-08-01 11:32:26 +00:00

374 lines
9.3 KiB
C

/******************************************************************************
*
* Module Name: evxfregn - External Interfaces, ACPI Operation Regions and
* Address Spaces.
* $Revision: 1.2 $
*
*****************************************************************************/
/*
* Copyright (C) 2000, 2001 R. Byron Moore
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <acpi.h>
#define _COMPONENT ACPI_EVENTS
MODULE_NAME ("evxfregn")
/******************************************************************************
*
* FUNCTION: Acpi_install_address_space_handler
*
* PARAMETERS: Device - Handle for the device
* Space_id - The address space ID
* Handler - Address of the handler
* Setup - Address of the setup function
* Context - Value passed to the handler on each access
*
* RETURN: Status
*
* DESCRIPTION: Install a handler for all Op_regions of a given Space_id.
*
******************************************************************************/
ACPI_STATUS
acpi_install_address_space_handler (
ACPI_HANDLE device,
ACPI_ADDRESS_SPACE_TYPE space_id,
ADDRESS_SPACE_HANDLER handler,
ADDRESS_SPACE_SETUP setup,
void *context)
{
ACPI_OPERAND_OBJECT *obj_desc;
ACPI_OPERAND_OBJECT *handler_obj;
ACPI_NAMESPACE_NODE *node;
ACPI_STATUS status = AE_OK;
OBJECT_TYPE_INTERNAL type;
u16 flags = 0;
/* Parameter validation */
if ((!device) ||
((!handler) && (handler != ACPI_DEFAULT_HANDLER))) {
return (AE_BAD_PARAMETER);
}
acpi_cm_acquire_mutex (ACPI_MTX_NAMESPACE);
/* Convert and validate the device handle */
node = acpi_ns_convert_handle_to_entry (device);
if (!node) {
status = AE_BAD_PARAMETER;
goto unlock_and_exit;
}
/*
* This registration is valid for only the types below
* and the root. This is where the default handlers
* get placed.
*/
if ((node->type != ACPI_TYPE_DEVICE) &&
(node->type != ACPI_TYPE_PROCESSOR) &&
(node->type != ACPI_TYPE_THERMAL) &&
(node != acpi_gbl_root_node)) {
status = AE_BAD_PARAMETER;
goto unlock_and_exit;
}
if (handler == ACPI_DEFAULT_HANDLER) {
flags = ADDR_HANDLER_DEFAULT_INSTALLED;
switch (space_id) {
case ADDRESS_SPACE_SYSTEM_MEMORY:
handler = acpi_aml_system_memory_space_handler;
setup = acpi_ev_system_memory_region_setup;
break;
case ADDRESS_SPACE_SYSTEM_IO:
handler = acpi_aml_system_io_space_handler;
setup = acpi_ev_io_space_region_setup;
break;
case ADDRESS_SPACE_PCI_CONFIG:
handler = acpi_aml_pci_config_space_handler;
setup = acpi_ev_pci_config_region_setup;
break;
default:
status = AE_NOT_EXIST;
goto unlock_and_exit;
break;
}
}
/*
* If the caller hasn't specified a setup routine, use the default
*/
if (!setup) {
setup = acpi_ev_default_region_setup;
}
/*
* Check for an existing internal object
*/
obj_desc = acpi_ns_get_attached_object ((ACPI_HANDLE) node);
if (obj_desc) {
/*
* The object exists.
* Make sure the handler is not already installed.
*/
/* check the address handler the user requested */
handler_obj = obj_desc->device.addr_handler;
while (handler_obj) {
/*
* We have an Address handler, see if user requested this
* address space.
*/
if(handler_obj->addr_handler.space_id == space_id) {
status = AE_EXIST;
goto unlock_and_exit;
}
/*
* Move through the linked list of handlers
*/
handler_obj = handler_obj->addr_handler.next;
}
}
else {
/* Obj_desc does not exist, create one */
if (node->type == ACPI_TYPE_ANY) {
type = ACPI_TYPE_DEVICE;
}
else {
type = node->type;
}
obj_desc = acpi_cm_create_internal_object (type);
if (!obj_desc) {
status = AE_NO_MEMORY;
goto unlock_and_exit;
}
/* Init new descriptor */
obj_desc->common.type = (u8) type;
/* Attach the new object to the Node */
status = acpi_ns_attach_object (node, obj_desc, (u8) type);
if (ACPI_FAILURE (status)) {
acpi_cm_remove_reference (obj_desc);
goto unlock_and_exit;
}
}
/*
* Now we can install the handler
*
* At this point we know that there is no existing handler.
* So, we just allocate the object for the handler and link it
* into the list.
*/
handler_obj = acpi_cm_create_internal_object (INTERNAL_TYPE_ADDRESS_HANDLER);
if (!handler_obj) {
status = AE_NO_MEMORY;
goto unlock_and_exit;
}
handler_obj->addr_handler.space_id = (u8) space_id;
handler_obj->addr_handler.hflags = flags;
handler_obj->addr_handler.next = obj_desc->device.addr_handler;
handler_obj->addr_handler.region_list = NULL;
handler_obj->addr_handler.node = node;
handler_obj->addr_handler.handler = handler;
handler_obj->addr_handler.context = context;
handler_obj->addr_handler.setup = setup;
/*
* Now walk the namespace finding all of the regions this
* handler will manage.
*
* We start at the device and search the branch toward
* the leaf nodes until either the leaf is encountered or
* a device is detected that has an address handler of the
* same type.
*
* In either case we back up and search down the remainder
* of the branch
*/
status = acpi_ns_walk_namespace (ACPI_TYPE_ANY, device,
ACPI_UINT32_MAX, NS_WALK_UNLOCK,
acpi_ev_addr_handler_helper,
handler_obj, NULL);
/*
* Place this handler 1st on the list
*/
handler_obj->common.reference_count =
(u16) (handler_obj->common.reference_count +
obj_desc->common.reference_count - 1);
obj_desc->device.addr_handler = handler_obj;
unlock_and_exit:
acpi_cm_release_mutex (ACPI_MTX_NAMESPACE);
return (status);
}
/******************************************************************************
*
* FUNCTION: Acpi_remove_address_space_handler
*
* PARAMETERS: Space_id - The address space ID
* Handler - Address of the handler
*
* RETURN: Status
*
* DESCRIPTION: Install a handler for accesses on an Operation Region
*
******************************************************************************/
ACPI_STATUS
acpi_remove_address_space_handler (
ACPI_HANDLE device,
ACPI_ADDRESS_SPACE_TYPE space_id,
ADDRESS_SPACE_HANDLER handler)
{
ACPI_OPERAND_OBJECT *obj_desc;
ACPI_OPERAND_OBJECT *handler_obj;
ACPI_OPERAND_OBJECT *region_obj;
ACPI_OPERAND_OBJECT **last_obj_ptr;
ACPI_NAMESPACE_NODE *node;
ACPI_STATUS status = AE_OK;
/* Parameter validation */
if ((!device) ||
((!handler) && (handler != ACPI_DEFAULT_HANDLER))) {
return (AE_BAD_PARAMETER);
}
acpi_cm_acquire_mutex (ACPI_MTX_NAMESPACE);
/* Convert and validate the device handle */
node = acpi_ns_convert_handle_to_entry (device);
if (!node) {
status = AE_BAD_PARAMETER;
goto unlock_and_exit;
}
/* Make sure the internal object exists */
obj_desc = acpi_ns_get_attached_object ((ACPI_HANDLE) node);
if (!obj_desc) {
/*
* The object DNE.
*/
status = AE_NOT_EXIST;
goto unlock_and_exit;
}
/*
* find the address handler the user requested
*/
handler_obj = obj_desc->device.addr_handler;
last_obj_ptr = &obj_desc->device.addr_handler;
while (handler_obj) {
/*
* We have a handler, see if user requested this one
*/
if(handler_obj->addr_handler.space_id == space_id) {
/*
* Got it, first dereference this in the Regions
*/
region_obj = handler_obj->addr_handler.region_list;
/* Walk the handler's region list */
while (region_obj) {
/*
* First disassociate the handler from the region.
*
* NOTE: this doesn't mean that the region goes away
* The region is just inaccessible as indicated to
* the _REG method
*/
acpi_ev_disassociate_region_from_handler(region_obj, FALSE);
/*
* Walk the list, since we took the first region and it
* was removed from the list by the dissassociate call
* we just get the first item on the list again
*/
region_obj = handler_obj->addr_handler.region_list;
}
/*
* Remove this Handler object from the list
*/
*last_obj_ptr = handler_obj->addr_handler.next;
/*
* Now we can delete the handler object
*/
acpi_cm_remove_reference (handler_obj);
acpi_cm_remove_reference (handler_obj);
goto unlock_and_exit;
}
/*
* Move through the linked list of handlers
*/
last_obj_ptr = &handler_obj->addr_handler.next;
handler_obj = handler_obj->addr_handler.next;
}
/*
* The handler does not exist
*/
status = AE_NOT_EXIST;
unlock_and_exit:
acpi_cm_release_mutex (ACPI_MTX_NAMESPACE);
return (status);
}