2013-01-11 16:08:51 +04:00
/******************************************************************************
*
* Module Name : evhandler - Support for Address Space handlers
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
2016-01-15 03:17:03 +03:00
* Copyright ( C ) 2000 - 2016 , Intel Corp .
2013-01-11 16:08:51 +04:00
* All rights reserved .
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions
* are met :
* 1. Redistributions of source code must retain the above copyright
* notice , this list of conditions , and the following disclaimer ,
* without modification .
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
* substantially similar to the " NO WARRANTY " disclaimer below
* ( " Disclaimer " ) and any redistribution must be conditioned upon
* including a substantially similar Disclaimer requirement for further
* binary redistribution .
* 3. Neither the names of the above - listed copyright holders nor the names
* of any contributors may be used to endorse or promote products derived
* from this software without specific prior written permission .
*
* Alternatively , this software may be distributed under the terms of the
* GNU General Public License ( " GPL " ) version 2 as published by the Free
* Software Foundation .
*
* NO WARRANTY
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* " AS IS " AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT
* LIMITED TO , THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL , EXEMPLARY , OR CONSEQUENTIAL
* DAMAGES ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION )
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY , WHETHER IN CONTRACT ,
* STRICT LIABILITY , OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE , EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES .
*/
# include <acpi/acpi.h>
# include "accommon.h"
# include "acevents.h"
# include "acnamesp.h"
# include "acinterp.h"
# define _COMPONENT ACPI_EVENTS
ACPI_MODULE_NAME ( " evhandler " )
/* Local prototypes */
static acpi_status
acpi_ev_install_handler ( acpi_handle obj_handle ,
u32 level , void * context , void * * return_value ) ;
/* These are the address spaces that will get default handlers */
u8 acpi_gbl_default_address_spaces [ ACPI_NUM_DEFAULT_SPACES ] = {
ACPI_ADR_SPACE_SYSTEM_MEMORY ,
ACPI_ADR_SPACE_SYSTEM_IO ,
ACPI_ADR_SPACE_PCI_CONFIG ,
ACPI_ADR_SPACE_DATA_TABLE
} ;
/*******************************************************************************
*
* FUNCTION : acpi_ev_install_region_handlers
*
* PARAMETERS : None
*
* RETURN : Status
*
* DESCRIPTION : Installs the core subsystem default address space handlers .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
acpi_status acpi_ev_install_region_handlers ( void )
{
acpi_status status ;
u32 i ;
ACPI_FUNCTION_TRACE ( ev_install_region_handlers ) ;
status = acpi_ut_acquire_mutex ( ACPI_MTX_NAMESPACE ) ;
if ( ACPI_FAILURE ( status ) ) {
return_ACPI_STATUS ( status ) ;
}
/*
* All address spaces ( PCI Config , EC , SMBus ) are scope dependent and
* registration must occur for a specific device .
*
* In the case of the system memory and IO address spaces there is
* currently no device associated with the address space . For these we
* use the root .
*
* We install the default PCI config space handler at the root so that
* this space is immediately available even though the we have not
* enumerated all the PCI Root Buses yet . This is to conform to the ACPI
* specification which states that the PCI config space must be always
* available - - even though we are nowhere near ready to find the PCI root
* buses at this point .
*
* NOTE : We ignore AE_ALREADY_EXISTS because this means that a handler
* has already been installed ( via acpi_install_address_space_handler ) .
* Similar for AE_SAME_HANDLER .
*/
for ( i = 0 ; i < ACPI_NUM_DEFAULT_SPACES ; i + + ) {
status = acpi_ev_install_space_handler ( acpi_gbl_root_node ,
acpi_gbl_default_address_spaces
[ i ] ,
ACPI_DEFAULT_HANDLER ,
NULL , NULL ) ;
switch ( status ) {
case AE_OK :
case AE_SAME_HANDLER :
case AE_ALREADY_EXISTS :
/* These exceptions are all OK */
status = AE_OK ;
break ;
default :
goto unlock_and_exit ;
}
}
2013-10-29 05:30:02 +04:00
unlock_and_exit :
2013-01-11 16:08:51 +04:00
( void ) acpi_ut_release_mutex ( ACPI_MTX_NAMESPACE ) ;
return_ACPI_STATUS ( status ) ;
}
/*******************************************************************************
*
* FUNCTION : acpi_ev_has_default_handler
*
* PARAMETERS : node - Namespace node for the device
* space_id - The address space ID
*
* RETURN : TRUE if default handler is installed , FALSE otherwise
*
* DESCRIPTION : Check if the default handler is installed for the requested
* space ID .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
u8
acpi_ev_has_default_handler ( struct acpi_namespace_node * node ,
acpi_adr_space_type space_id )
{
union acpi_operand_object * obj_desc ;
union acpi_operand_object * handler_obj ;
/* Must have an existing internal object */
obj_desc = acpi_ns_get_attached_object ( node ) ;
if ( obj_desc ) {
2015-12-29 09:02:08 +03:00
handler_obj = obj_desc - > common_notify . handler ;
2013-01-11 16:08:51 +04:00
/* Walk the linked list of handlers for this object */
while ( handler_obj ) {
if ( handler_obj - > address_space . space_id = = space_id ) {
if ( handler_obj - > address_space . handler_flags &
ACPI_ADDR_HANDLER_DEFAULT_INSTALLED ) {
return ( TRUE ) ;
}
}
handler_obj = handler_obj - > address_space . next ;
}
}
return ( FALSE ) ;
}
/*******************************************************************************
*
* FUNCTION : acpi_ev_install_handler
*
* PARAMETERS : walk_namespace callback
*
* DESCRIPTION : This routine installs an address handler into objects that are
* of type Region or Device .
*
* If the Object is a Device , and the device has a handler of
* the same type then the search is terminated in that branch .
*
* This is because the existing handler is closer in proximity
* to any more regions than the one we are trying to install .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static acpi_status
acpi_ev_install_handler ( acpi_handle obj_handle ,
u32 level , void * context , void * * return_value )
{
union acpi_operand_object * handler_obj ;
union acpi_operand_object * next_handler_obj ;
union acpi_operand_object * obj_desc ;
struct acpi_namespace_node * node ;
acpi_status status ;
ACPI_FUNCTION_NAME ( ev_install_handler ) ;
handler_obj = ( union acpi_operand_object * ) context ;
/* Parameter validation */
if ( ! handler_obj ) {
return ( AE_OK ) ;
}
/* Convert and validate the device handle */
node = acpi_ns_validate_handle ( obj_handle ) ;
if ( ! node ) {
return ( AE_BAD_PARAMETER ) ;
}
/*
* We only care about regions and objects that are allowed to have
* address space handlers
*/
if ( ( node - > type ! = ACPI_TYPE_DEVICE ) & &
( node - > type ! = ACPI_TYPE_REGION ) & & ( node ! = acpi_gbl_root_node ) ) {
return ( AE_OK ) ;
}
/* Check for an existing internal object */
obj_desc = acpi_ns_get_attached_object ( node ) ;
if ( ! obj_desc ) {
/* No object, just exit */
return ( AE_OK ) ;
}
/* Devices are handled different than regions */
if ( obj_desc - > common . type = = ACPI_TYPE_DEVICE ) {
/* Check if this Device already has a handler for this address space */
2015-12-29 09:02:00 +03:00
next_handler_obj =
acpi_ev_find_region_handler ( handler_obj - > address_space .
space_id ,
2015-12-29 09:02:08 +03:00
obj_desc - > common_notify .
handler ) ;
2015-12-29 09:02:00 +03:00
if ( next_handler_obj ) {
2013-01-11 16:08:51 +04:00
/* Found a handler, is it for the same address space? */
2015-12-29 09:02:00 +03:00
ACPI_DEBUG_PRINT ( ( ACPI_DB_OPREGION ,
" Found handler for region [%s] in device %p(%p) handler %p \n " ,
acpi_ut_get_region_name ( handler_obj - >
address_space .
space_id ) ,
obj_desc , next_handler_obj ,
handler_obj ) ) ;
/*
* Since the object we found it on was a device , then it means
* that someone has already installed a handler for the branch
* of the namespace from this device on . Just bail out telling
* the walk routine to not traverse this branch . This preserves
* the scoping rule for handlers .
*/
return ( AE_CTRL_DEPTH ) ;
2013-01-11 16:08:51 +04:00
}
/*
* As long as the device didn ' t have a handler for this space we
* don ' t care about it . We just ignore it and proceed .
*/
return ( AE_OK ) ;
}
/* Object is a Region */
if ( obj_desc - > region . space_id ! = handler_obj - > address_space . space_id ) {
/* This region is for a different address space, just ignore it */
return ( AE_OK ) ;
}
/*
* Now we have a region and it is for the handler ' s address space type .
*
* First disconnect region for any previous handler ( if any )
*/
acpi_ev_detach_region ( obj_desc , FALSE ) ;
/* Connect the region to the new handler */
status = acpi_ev_attach_region ( handler_obj , obj_desc , FALSE ) ;
return ( status ) ;
}
2015-12-29 09:01:53 +03:00
/*******************************************************************************
*
* FUNCTION : acpi_ev_find_region_handler
*
* PARAMETERS : space_id - The address space ID
* handler_obj - Head of the handler object list
*
* RETURN : Matching handler object . NULL if space ID not matched
*
* DESCRIPTION : Search a handler object list for a match on the address
* space ID .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2015-12-29 09:02:00 +03:00
union acpi_operand_object * acpi_ev_find_region_handler ( acpi_adr_space_type
space_id ,
union acpi_operand_object
* handler_obj )
2015-12-29 09:01:53 +03:00
{
/* Walk the handler list for this device */
while ( handler_obj ) {
/* Same space_id indicates a handler is installed */
if ( handler_obj - > address_space . space_id = = space_id ) {
return ( handler_obj ) ;
}
/* Next handler object */
handler_obj = handler_obj - > address_space . next ;
}
return ( NULL ) ;
}
2013-01-11 16:08:51 +04:00
/*******************************************************************************
*
* FUNCTION : acpi_ev_install_space_handler
*
* PARAMETERS : node - Namespace node 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 .
* Assumes namespace is locked
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
acpi_status
2016-05-05 07:57:53 +03:00
acpi_ev_install_space_handler ( struct acpi_namespace_node * node ,
2013-01-11 16:08:51 +04:00
acpi_adr_space_type space_id ,
acpi_adr_space_handler handler ,
acpi_adr_space_setup setup , void * context )
{
union acpi_operand_object * obj_desc ;
union acpi_operand_object * handler_obj ;
2015-12-29 09:01:53 +03:00
acpi_status status = AE_OK ;
2013-01-11 16:08:51 +04:00
acpi_object_type type ;
u8 flags = 0 ;
ACPI_FUNCTION_TRACE ( ev_install_space_handler ) ;
/*
2015-12-29 09:01:53 +03:00
* This registration is valid for only the types below and the root .
* The root node is where the default handlers get installed .
2013-01-11 16:08:51 +04:00
*/
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 = ACPI_ADDR_HANDLER_DEFAULT_INSTALLED ;
switch ( space_id ) {
case ACPI_ADR_SPACE_SYSTEM_MEMORY :
2013-06-08 04:58:14 +04:00
2013-01-11 16:08:51 +04:00
handler = acpi_ex_system_memory_space_handler ;
setup = acpi_ev_system_memory_region_setup ;
break ;
case ACPI_ADR_SPACE_SYSTEM_IO :
2013-06-08 04:58:14 +04:00
2013-01-11 16:08:51 +04:00
handler = acpi_ex_system_io_space_handler ;
setup = acpi_ev_io_space_region_setup ;
break ;
case ACPI_ADR_SPACE_PCI_CONFIG :
2013-06-08 04:58:14 +04:00
2013-01-11 16:08:51 +04:00
handler = acpi_ex_pci_config_space_handler ;
setup = acpi_ev_pci_config_region_setup ;
break ;
case ACPI_ADR_SPACE_CMOS :
2013-06-08 04:58:14 +04:00
2013-01-11 16:08:51 +04:00
handler = acpi_ex_cmos_space_handler ;
setup = acpi_ev_cmos_region_setup ;
break ;
case ACPI_ADR_SPACE_PCI_BAR_TARGET :
2013-06-08 04:58:14 +04:00
2013-01-11 16:08:51 +04:00
handler = acpi_ex_pci_bar_space_handler ;
setup = acpi_ev_pci_bar_region_setup ;
break ;
case ACPI_ADR_SPACE_DATA_TABLE :
2013-06-08 04:58:14 +04:00
2013-01-11 16:08:51 +04:00
handler = acpi_ex_data_table_space_handler ;
setup = NULL ;
break ;
default :
2013-06-08 04:58:14 +04:00
2013-01-11 16:08:51 +04:00
status = AE_BAD_PARAMETER ;
goto unlock_and_exit ;
}
}
/* 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 ( node ) ;
if ( obj_desc ) {
/*
2015-12-29 09:01:53 +03:00
* The attached device object already exists . Now make sure
* the handler is not already installed .
2013-01-11 16:08:51 +04:00
*/
2015-12-29 09:01:53 +03:00
handler_obj = acpi_ev_find_region_handler ( space_id ,
2015-12-29 09:02:08 +03:00
obj_desc - >
common_notify .
2015-12-29 09:01:53 +03:00
handler ) ;
2013-01-11 16:08:51 +04:00
2015-12-29 09:01:53 +03:00
if ( handler_obj ) {
if ( handler_obj - > address_space . handler = = handler ) {
/*
* It is ( relatively ) OK to attempt to install the SAME
* handler twice . This can easily happen with the
* PCI_Config space .
*/
status = AE_SAME_HANDLER ;
2013-01-11 16:08:51 +04:00
goto unlock_and_exit ;
2015-12-29 09:01:53 +03:00
} else {
/* A handler is already installed */
2013-01-11 16:08:51 +04:00
2015-12-29 09:01:53 +03:00
status = AE_ALREADY_EXISTS ;
}
2013-01-11 16:08:51 +04:00
2015-12-29 09:01:53 +03:00
goto unlock_and_exit ;
2013-01-11 16:08:51 +04:00
}
} else {
ACPI_DEBUG_PRINT ( ( ACPI_DB_OPREGION ,
" Creating object on Device %p while installing handler \n " ,
node ) ) ;
/* obj_desc does not exist, create one */
if ( node - > type = = ACPI_TYPE_ANY ) {
type = ACPI_TYPE_DEVICE ;
} else {
type = node - > type ;
}
obj_desc = acpi_ut_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 , type ) ;
/* Remove local reference to the object */
acpi_ut_remove_reference ( obj_desc ) ;
if ( ACPI_FAILURE ( status ) ) {
goto unlock_and_exit ;
}
}
ACPI_DEBUG_PRINT ( ( ACPI_DB_OPREGION ,
2015-12-29 09:01:53 +03:00
" Installing address handler for region %s(%X) "
" on Device %4.4s %p(%p) \n " ,
2013-01-11 16:08:51 +04:00
acpi_ut_get_region_name ( space_id ) , space_id ,
acpi_ut_get_node_name ( node ) , node , obj_desc ) ) ;
/*
* Install the handler
*
* At this point there is no existing handler . Just allocate the object
* for the handler and link it into the list .
*/
handler_obj =
acpi_ut_create_internal_object ( ACPI_TYPE_LOCAL_ADDRESS_HANDLER ) ;
if ( ! handler_obj ) {
status = AE_NO_MEMORY ;
goto unlock_and_exit ;
}
/* Init handler obj */
handler_obj - > address_space . space_id = ( u8 ) space_id ;
handler_obj - > address_space . handler_flags = flags ;
handler_obj - > address_space . region_list = NULL ;
handler_obj - > address_space . node = node ;
handler_obj - > address_space . handler = handler ;
handler_obj - > address_space . context = context ;
handler_obj - > address_space . setup = setup ;
/* Install at head of Device.address_space list */
2015-12-29 09:02:08 +03:00
handler_obj - > address_space . next = obj_desc - > common_notify . handler ;
2013-01-11 16:08:51 +04:00
/*
* The Device object is the first reference on the handler_obj .
* Each region that uses the handler adds a reference .
*/
2015-12-29 09:02:08 +03:00
obj_desc - > common_notify . handler = handler_obj ;
2013-01-11 16:08:51 +04:00
/*
2015-12-29 09:01:53 +03:00
* Walk the namespace finding all of the regions this handler will
* manage .
2013-01-11 16:08:51 +04:00
*
2015-12-29 09:01:53 +03:00
* 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 .
2013-01-11 16:08:51 +04:00
*
2015-12-29 09:01:53 +03:00
* In either case , back up and search down the remainder of the branch
2013-01-11 16:08:51 +04:00
*/
2015-12-29 09:01:53 +03:00
status = acpi_ns_walk_namespace ( ACPI_TYPE_ANY , node ,
ACPI_UINT32_MAX , ACPI_NS_WALK_UNLOCK ,
2013-01-11 16:08:51 +04:00
acpi_ev_install_handler , NULL ,
handler_obj , NULL ) ;
2013-10-29 05:30:02 +04:00
unlock_and_exit :
2013-01-11 16:08:51 +04:00
return_ACPI_STATUS ( status ) ;
}