2005-04-17 02:20:36 +04:00
/*
* scan . c - support for transforming the ACPI namespace into individual objects
*/
# include <linux/module.h>
# include <linux/init.h>
# include <linux/acpi.h>
# include <acpi/acpi_drivers.h>
# include <acpi/acinterp.h> /* for acpi_ex_eisa_id_to_string() */
# define _COMPONENT ACPI_BUS_COMPONENT
2005-08-05 08:44:28 +04:00
ACPI_MODULE_NAME ( " scan " )
2005-04-17 02:20:36 +04:00
# define STRUCT_TO_INT(s) (*((int*)&s))
2005-08-05 08:44:28 +04:00
extern struct acpi_device * acpi_root ;
2005-04-17 02:20:36 +04:00
# define ACPI_BUS_CLASS "system_bus"
# define ACPI_BUS_HID "ACPI_BUS"
# define ACPI_BUS_DRIVER_NAME "ACPI Bus Driver"
# define ACPI_BUS_DEVICE_NAME "System Bus"
static LIST_HEAD ( acpi_device_list ) ;
DEFINE_SPINLOCK ( acpi_device_lock ) ;
LIST_HEAD ( acpi_wakeup_device_list ) ;
2005-08-05 08:44:28 +04:00
static int acpi_bus_trim ( struct acpi_device * start , int rmdevice ) ;
2005-04-17 02:20:36 +04:00
2005-08-05 08:44:28 +04:00
static void acpi_device_release ( struct kobject * kobj )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
struct acpi_device * dev = container_of ( kobj , struct acpi_device , kobj ) ;
2005-11-07 12:01:32 +03:00
kfree ( dev - > pnp . cid_list ) ;
2005-04-17 02:20:36 +04:00
kfree ( dev ) ;
}
struct acpi_device_attribute {
struct attribute attr ;
2005-08-05 08:44:28 +04:00
ssize_t ( * show ) ( struct acpi_device * , char * ) ;
ssize_t ( * store ) ( struct acpi_device * , const char * , size_t ) ;
2005-04-17 02:20:36 +04:00
} ;
typedef void acpi_device_sysfs_files ( struct kobject * ,
2005-08-05 08:44:28 +04:00
const struct attribute * ) ;
2005-04-17 02:20:36 +04:00
static void setup_sys_fs_device_files ( struct acpi_device * dev ,
2005-08-05 08:44:28 +04:00
acpi_device_sysfs_files * func ) ;
2005-04-17 02:20:36 +04:00
# define create_sysfs_device_files(dev) \
setup_sys_fs_device_files ( dev , ( acpi_device_sysfs_files * ) & sysfs_create_file )
# define remove_sysfs_device_files(dev) \
setup_sys_fs_device_files ( dev , ( acpi_device_sysfs_files * ) & sysfs_remove_file )
# define to_acpi_device(n) container_of(n, struct acpi_device, kobj)
# define to_handle_attr(n) container_of(n, struct acpi_device_attribute, attr);
static ssize_t acpi_device_attr_show ( struct kobject * kobj ,
2005-08-05 08:44:28 +04:00
struct attribute * attr , char * buf )
2005-04-17 02:20:36 +04:00
{
struct acpi_device * device = to_acpi_device ( kobj ) ;
struct acpi_device_attribute * attribute = to_handle_attr ( attr ) ;
2005-04-29 10:27:34 +04:00
return attribute - > show ? attribute - > show ( device , buf ) : - EIO ;
2005-04-17 02:20:36 +04:00
}
static ssize_t acpi_device_attr_store ( struct kobject * kobj ,
2005-08-05 08:44:28 +04:00
struct attribute * attr , const char * buf ,
size_t len )
2005-04-17 02:20:36 +04:00
{
struct acpi_device * device = to_acpi_device ( kobj ) ;
struct acpi_device_attribute * attribute = to_handle_attr ( attr ) ;
2005-04-29 10:27:34 +04:00
return attribute - > store ? attribute - > store ( device , buf , len ) : - EIO ;
2005-04-17 02:20:36 +04:00
}
static struct sysfs_ops acpi_device_sysfs_ops = {
2005-08-05 08:44:28 +04:00
. show = acpi_device_attr_show ,
. store = acpi_device_attr_store ,
2005-04-17 02:20:36 +04:00
} ;
static struct kobj_type ktype_acpi_ns = {
2005-08-05 08:44:28 +04:00
. sysfs_ops = & acpi_device_sysfs_ops ,
. release = acpi_device_release ,
2005-04-17 02:20:36 +04:00
} ;
static int namespace_hotplug ( struct kset * kset , struct kobject * kobj ,
char * * envp , int num_envp , char * buffer ,
int buffer_size )
{
struct acpi_device * dev = to_acpi_device ( kobj ) ;
int i = 0 ;
int len = 0 ;
if ( ! dev - > driver )
return 0 ;
if ( add_hotplug_env_var ( envp , num_envp , & i , buffer , buffer_size , & len ,
" PHYSDEVDRIVER=%s " , dev - > driver - > name ) )
return - ENOMEM ;
envp [ i ] = NULL ;
return 0 ;
}
static struct kset_hotplug_ops namespace_hotplug_ops = {
. hotplug = & namespace_hotplug ,
} ;
static struct kset acpi_namespace_kset = {
2005-08-05 08:44:28 +04:00
. kobj = {
. name = " namespace " ,
} ,
2005-04-17 02:20:36 +04:00
. subsys = & acpi_subsys ,
2005-08-05 08:44:28 +04:00
. ktype = & ktype_acpi_ns ,
2005-04-17 02:20:36 +04:00
. hotplug_ops = & namespace_hotplug_ops ,
} ;
2005-08-05 08:44:28 +04:00
static void acpi_device_register ( struct acpi_device * device ,
struct acpi_device * parent )
2005-04-17 02:20:36 +04:00
{
/*
* Linkage
* - - - - - - -
* Link this device to its parent and siblings .
*/
INIT_LIST_HEAD ( & device - > children ) ;
INIT_LIST_HEAD ( & device - > node ) ;
INIT_LIST_HEAD ( & device - > g_list ) ;
INIT_LIST_HEAD ( & device - > wakeup_list ) ;
spin_lock ( & acpi_device_lock ) ;
if ( device - > parent ) {
list_add_tail ( & device - > node , & device - > parent - > children ) ;
2005-08-05 08:44:28 +04:00
list_add_tail ( & device - > g_list , & device - > parent - > g_list ) ;
2005-04-17 02:20:36 +04:00
} else
2005-08-05 08:44:28 +04:00
list_add_tail ( & device - > g_list , & acpi_device_list ) ;
2005-04-17 02:20:36 +04:00
if ( device - > wakeup . flags . valid )
2005-08-05 08:44:28 +04:00
list_add_tail ( & device - > wakeup_list , & acpi_wakeup_device_list ) ;
2005-04-17 02:20:36 +04:00
spin_unlock ( & acpi_device_lock ) ;
2005-08-05 08:44:28 +04:00
strlcpy ( device - > kobj . name , device - > pnp . bus_id , KOBJ_NAME_LEN ) ;
2005-04-17 02:20:36 +04:00
if ( parent )
device - > kobj . parent = & parent - > kobj ;
device - > kobj . ktype = & ktype_acpi_ns ;
device - > kobj . kset = & acpi_namespace_kset ;
kobject_register ( & device - > kobj ) ;
create_sysfs_device_files ( device ) ;
}
2005-08-05 08:44:28 +04:00
static int acpi_device_unregister ( struct acpi_device * device , int type )
2005-04-17 02:20:36 +04:00
{
spin_lock ( & acpi_device_lock ) ;
if ( device - > parent ) {
list_del ( & device - > node ) ;
list_del ( & device - > g_list ) ;
} else
list_del ( & device - > g_list ) ;
list_del ( & device - > wakeup_list ) ;
spin_unlock ( & acpi_device_lock ) ;
acpi_detach_data ( device - > handle , acpi_bus_data_handler ) ;
remove_sysfs_device_files ( device ) ;
kobject_unregister ( & device - > kobj ) ;
return 0 ;
}
2005-08-05 08:44:28 +04:00
void acpi_bus_data_handler ( acpi_handle handle , u32 function , void * context )
2005-04-17 02:20:36 +04:00
{
ACPI_FUNCTION_TRACE ( " acpi_bus_data_handler " ) ;
/* TBD */
return_VOID ;
}
2005-08-05 08:44:28 +04:00
static int acpi_bus_get_power_flags ( struct acpi_device * device )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
acpi_status status = 0 ;
acpi_handle handle = NULL ;
u32 i = 0 ;
2005-04-17 02:20:36 +04:00
ACPI_FUNCTION_TRACE ( " acpi_bus_get_power_flags " ) ;
/*
* Power Management Flags
*/
status = acpi_get_handle ( device - > handle , " _PSC " , & handle ) ;
if ( ACPI_SUCCESS ( status ) )
device - > power . flags . explicit_get = 1 ;
status = acpi_get_handle ( device - > handle , " _IRC " , & handle ) ;
if ( ACPI_SUCCESS ( status ) )
device - > power . flags . inrush_current = 1 ;
/*
* Enumerate supported power management states
*/
for ( i = ACPI_STATE_D0 ; i < = ACPI_STATE_D3 ; i + + ) {
struct acpi_device_power_state * ps = & device - > power . states [ i ] ;
2005-08-05 08:44:28 +04:00
char object_name [ 5 ] = { ' _ ' , ' P ' , ' R ' , ' 0 ' + i , ' \0 ' } ;
2005-04-17 02:20:36 +04:00
/* Evaluate "_PRx" to se if power resources are referenced */
acpi_evaluate_reference ( device - > handle , object_name , NULL ,
2005-08-05 08:44:28 +04:00
& ps - > resources ) ;
2005-04-17 02:20:36 +04:00
if ( ps - > resources . count ) {
device - > power . flags . power_resources = 1 ;
ps - > flags . valid = 1 ;
}
/* Evaluate "_PSx" to see if we can do explicit sets */
object_name [ 2 ] = ' S ' ;
status = acpi_get_handle ( device - > handle , object_name , & handle ) ;
if ( ACPI_SUCCESS ( status ) ) {
ps - > flags . explicit_set = 1 ;
ps - > flags . valid = 1 ;
}
/* State is valid if we have some power control */
if ( ps - > resources . count | | ps - > flags . explicit_set )
ps - > flags . valid = 1 ;
2005-08-05 08:44:28 +04:00
ps - > power = - 1 ; /* Unknown - driver assigned */
2005-04-17 02:20:36 +04:00
ps - > latency = - 1 ; /* Unknown - driver assigned */
}
/* Set defaults for D0 and D3 states (always valid) */
device - > power . states [ ACPI_STATE_D0 ] . flags . valid = 1 ;
device - > power . states [ ACPI_STATE_D0 ] . power = 100 ;
device - > power . states [ ACPI_STATE_D3 ] . flags . valid = 1 ;
device - > power . states [ ACPI_STATE_D3 ] . power = 0 ;
/* TBD: System wake support and resource requirements. */
device - > power . state = ACPI_STATE_UNKNOWN ;
return_VALUE ( 0 ) ;
}
2005-08-05 08:44:28 +04:00
int acpi_match_ids ( struct acpi_device * device , char * ids )
2005-04-17 02:20:36 +04:00
{
int error = 0 ;
2005-08-05 08:44:28 +04:00
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER , NULL } ;
2005-04-17 02:20:36 +04:00
if ( device - > flags . hardware_id )
if ( strstr ( ids , device - > pnp . hardware_id ) )
goto Done ;
if ( device - > flags . compatible_ids ) {
struct acpi_compatible_id_list * cid_list = device - > pnp . cid_list ;
int i ;
/* compare multiple _CID entries against driver ids */
2005-08-05 08:44:28 +04:00
for ( i = 0 ; i < cid_list - > count ; i + + ) {
2005-04-17 02:20:36 +04:00
if ( strstr ( ids , cid_list - > id [ i ] . value ) )
goto Done ;
}
}
error = - ENOENT ;
2005-08-05 08:44:28 +04:00
Done :
2005-04-17 02:20:36 +04:00
if ( buffer . pointer )
acpi_os_free ( buffer . pointer ) ;
return error ;
}
static acpi_status
2005-08-05 08:44:28 +04:00
acpi_bus_extract_wakeup_device_power_package ( struct acpi_device * device ,
union acpi_object * package )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
int i = 0 ;
union acpi_object * element = NULL ;
2005-04-17 02:20:36 +04:00
if ( ! device | | ! package | | ( package - > package . count < 2 ) )
return AE_BAD_PARAMETER ;
element = & ( package - > package . elements [ 0 ] ) ;
if ( ! element )
return AE_BAD_PARAMETER ;
if ( element - > type = = ACPI_TYPE_PACKAGE ) {
if ( ( element - > package . count < 2 ) | |
2005-08-05 08:44:28 +04:00
( element - > package . elements [ 0 ] . type ! =
ACPI_TYPE_LOCAL_REFERENCE )
| | ( element - > package . elements [ 1 ] . type ! = ACPI_TYPE_INTEGER ) )
2005-04-17 02:20:36 +04:00
return AE_BAD_DATA ;
2005-08-05 08:44:28 +04:00
device - > wakeup . gpe_device =
element - > package . elements [ 0 ] . reference . handle ;
device - > wakeup . gpe_number =
( u32 ) element - > package . elements [ 1 ] . integer . value ;
} else if ( element - > type = = ACPI_TYPE_INTEGER ) {
2005-04-17 02:20:36 +04:00
device - > wakeup . gpe_number = element - > integer . value ;
2005-08-05 08:44:28 +04:00
} else
2005-04-17 02:20:36 +04:00
return AE_BAD_DATA ;
element = & ( package - > package . elements [ 1 ] ) ;
if ( element - > type ! = ACPI_TYPE_INTEGER ) {
return AE_BAD_DATA ;
}
device - > wakeup . sleep_state = element - > integer . value ;
if ( ( package - > package . count - 2 ) > ACPI_MAX_HANDLES ) {
return AE_NO_MEMORY ;
}
device - > wakeup . resources . count = package - > package . count - 2 ;
2005-08-05 08:44:28 +04:00
for ( i = 0 ; i < device - > wakeup . resources . count ; i + + ) {
2005-04-17 02:20:36 +04:00
element = & ( package - > package . elements [ i + 2 ] ) ;
2005-08-05 08:44:28 +04:00
if ( element - > type ! = ACPI_TYPE_ANY ) {
2005-04-17 02:20:36 +04:00
return AE_BAD_DATA ;
}
device - > wakeup . resources . handles [ i ] = element - > reference . handle ;
}
return AE_OK ;
}
2005-08-05 08:44:28 +04:00
static int acpi_bus_get_wakeup_device_flags ( struct acpi_device * device )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
acpi_status status = 0 ;
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER , NULL } ;
union acpi_object * package = NULL ;
2005-04-17 02:20:36 +04:00
ACPI_FUNCTION_TRACE ( " acpi_bus_get_wakeup_flags " ) ;
/* _PRW */
status = acpi_evaluate_object ( device - > handle , " _PRW " , NULL , & buffer ) ;
if ( ACPI_FAILURE ( status ) ) {
ACPI_DEBUG_PRINT ( ( ACPI_DB_ERROR , " Error evaluating _PRW \n " ) ) ;
goto end ;
}
2005-08-05 08:44:28 +04:00
package = ( union acpi_object * ) buffer . pointer ;
2005-04-17 02:20:36 +04:00
status = acpi_bus_extract_wakeup_device_power_package ( device , package ) ;
if ( ACPI_FAILURE ( status ) ) {
2005-08-05 08:44:28 +04:00
ACPI_DEBUG_PRINT ( ( ACPI_DB_ERROR ,
" Error extracting _PRW package \n " ) ) ;
2005-04-17 02:20:36 +04:00
goto end ;
}
acpi_os_free ( buffer . pointer ) ;
device - > wakeup . flags . valid = 1 ;
2005-08-05 08:44:28 +04:00
/* Power button, Lid switch always enable wakeup */
2005-04-17 02:20:36 +04:00
if ( ! acpi_match_ids ( device , " PNP0C0D,PNP0C0C,PNP0C0E " ) )
device - > wakeup . flags . run_wake = 1 ;
2005-08-05 08:44:28 +04:00
end :
2005-04-17 02:20:36 +04:00
if ( ACPI_FAILURE ( status ) )
device - > flags . wake_capable = 0 ;
return_VALUE ( 0 ) ;
}
/* --------------------------------------------------------------------------
ACPI hotplug sysfs device file support
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2005-08-05 08:44:28 +04:00
static ssize_t acpi_eject_store ( struct acpi_device * device ,
const char * buf , size_t count ) ;
2005-04-17 02:20:36 +04:00
# define ACPI_DEVICE_ATTR(_name,_mode,_show,_store) \
static struct acpi_device_attribute acpi_device_attr_ # # _name = \
__ATTR ( _name , _mode , _show , _store )
ACPI_DEVICE_ATTR ( eject , 0200 , NULL , acpi_eject_store ) ;
/**
* setup_sys_fs_device_files - sets up the device files under device namespace
2005-05-01 19:59:26 +04:00
* @ dev : acpi_device object
* @ func : function pointer to create or destroy the device file
2005-04-17 02:20:36 +04:00
*/
static void
2005-08-05 08:44:28 +04:00
setup_sys_fs_device_files ( struct acpi_device * dev ,
acpi_device_sysfs_files * func )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
acpi_status status ;
acpi_handle temp = NULL ;
2005-04-17 02:20:36 +04:00
/*
* If device has _EJ0 , ' eject ' file is created that is used to trigger
* hot - removal function from userland .
*/
status = acpi_get_handle ( dev - > handle , " _EJ0 " , & temp ) ;
if ( ACPI_SUCCESS ( status ) )
2005-08-05 08:44:28 +04:00
( * ( func ) ) ( & dev - > kobj , & acpi_device_attr_eject . attr ) ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 08:44:28 +04:00
static int acpi_eject_operation ( acpi_handle handle , int lockable )
2005-04-17 02:20:36 +04:00
{
struct acpi_object_list arg_list ;
union acpi_object arg ;
acpi_status status = AE_OK ;
/*
* TBD : evaluate _PS3 ?
*/
if ( lockable ) {
arg_list . count = 1 ;
arg_list . pointer = & arg ;
arg . type = ACPI_TYPE_INTEGER ;
arg . integer . value = 0 ;
acpi_evaluate_object ( handle , " _LCK " , & arg_list , NULL ) ;
}
arg_list . count = 1 ;
arg_list . pointer = & arg ;
arg . type = ACPI_TYPE_INTEGER ;
arg . integer . value = 1 ;
/*
* TBD : _EJD support .
*/
status = acpi_evaluate_object ( handle , " _EJ0 " , & arg_list , NULL ) ;
if ( ACPI_FAILURE ( status ) ) {
2005-08-05 08:44:28 +04:00
return ( - ENODEV ) ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 08:44:28 +04:00
return ( 0 ) ;
2005-04-17 02:20:36 +04:00
}
static ssize_t
acpi_eject_store ( struct acpi_device * device , const char * buf , size_t count )
{
2005-08-05 08:44:28 +04:00
int result ;
int ret = count ;
int islockable ;
acpi_status status ;
acpi_handle handle ;
acpi_object_type type = 0 ;
2005-04-17 02:20:36 +04:00
if ( ( ! count ) | | ( buf [ 0 ] ! = ' 1 ' ) ) {
return - EINVAL ;
}
# ifndef FORCE_EJECT
if ( device - > driver = = NULL ) {
ret = - ENODEV ;
goto err ;
}
# endif
status = acpi_get_type ( device - > handle , & type ) ;
2005-08-05 08:44:28 +04:00
if ( ACPI_FAILURE ( status ) | | ( ! device - > flags . ejectable ) ) {
2005-04-17 02:20:36 +04:00
ret = - ENODEV ;
goto err ;
}
islockable = device - > flags . lockable ;
handle = device - > handle ;
if ( type = = ACPI_TYPE_PROCESSOR )
result = acpi_bus_trim ( device , 0 ) ;
else
result = acpi_bus_trim ( device , 1 ) ;
if ( ! result )
result = acpi_eject_operation ( handle , islockable ) ;
if ( result ) {
ret = - EBUSY ;
}
2005-08-05 08:44:28 +04:00
err :
2005-04-17 02:20:36 +04:00
return ret ;
}
/* --------------------------------------------------------------------------
Performance Management
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2005-08-05 08:44:28 +04:00
static int acpi_bus_get_perf_flags ( struct acpi_device * device )
2005-04-17 02:20:36 +04:00
{
device - > performance . state = ACPI_STATE_UNKNOWN ;
return 0 ;
}
/* --------------------------------------------------------------------------
Driver Management
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static LIST_HEAD ( acpi_bus_drivers ) ;
static DECLARE_MUTEX ( acpi_bus_drivers_lock ) ;
/**
* acpi_bus_match
* - - - - - - - - - - - - - -
* Checks the device ' s hardware ( _HID ) or compatible ( _CID ) ids to see if it
* matches the specified driver ' s criteria .
*/
static int
2005-08-05 08:44:28 +04:00
acpi_bus_match ( struct acpi_device * device , struct acpi_driver * driver )
2005-04-17 02:20:36 +04:00
{
if ( driver & & driver - > ops . match )
return driver - > ops . match ( device , driver ) ;
return acpi_match_ids ( device , driver - > ids ) ;
}
/**
* acpi_bus_driver_init
* - - - - - - - - - - - - - - - - - - - -
* Used to initialize a device via its device driver . Called whenever a
* driver is bound to a device . Invokes the driver ' s add ( ) and start ( ) ops .
*/
static int
2005-08-05 08:44:28 +04:00
acpi_bus_driver_init ( struct acpi_device * device , struct acpi_driver * driver )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
int result = 0 ;
2005-04-17 02:20:36 +04:00
ACPI_FUNCTION_TRACE ( " acpi_bus_driver_init " ) ;
if ( ! device | | ! driver )
return_VALUE ( - EINVAL ) ;
if ( ! driver - > ops . add )
return_VALUE ( - ENOSYS ) ;
result = driver - > ops . add ( device ) ;
if ( result ) {
device - > driver = NULL ;
acpi_driver_data ( device ) = NULL ;
return_VALUE ( result ) ;
}
device - > driver = driver ;
/*
* TBD - Configuration Management : Assign resources to device based
* upon possible configuration and currently allocated resources .
*/
2005-08-05 08:44:28 +04:00
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO ,
" Driver successfully bound to device \n " ) ) ;
2005-04-28 11:25:52 +04:00
return_VALUE ( 0 ) ;
}
2005-09-03 01:16:48 +04:00
static int acpi_start_single_object ( struct acpi_device * device )
2005-04-28 11:25:52 +04:00
{
int result = 0 ;
struct acpi_driver * driver ;
ACPI_FUNCTION_TRACE ( " acpi_start_single_object " ) ;
if ( ! ( driver = device - > driver ) )
return_VALUE ( 0 ) ;
2005-04-17 02:20:36 +04:00
if ( driver - > ops . start ) {
result = driver - > ops . start ( device ) ;
if ( result & & driver - > ops . remove )
driver - > ops . remove ( device , ACPI_BUS_REMOVAL_NORMAL ) ;
}
2005-04-28 11:25:52 +04:00
return_VALUE ( result ) ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 08:44:28 +04:00
static int acpi_driver_attach ( struct acpi_driver * drv )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
struct list_head * node , * next ;
2005-04-17 02:20:36 +04:00
int count = 0 ;
ACPI_FUNCTION_TRACE ( " acpi_driver_attach " ) ;
spin_lock ( & acpi_device_lock ) ;
list_for_each_safe ( node , next , & acpi_device_list ) {
2005-08-05 08:44:28 +04:00
struct acpi_device * dev =
container_of ( node , struct acpi_device , g_list ) ;
2005-04-17 02:20:36 +04:00
if ( dev - > driver | | ! dev - > status . present )
continue ;
spin_unlock ( & acpi_device_lock ) ;
if ( ! acpi_bus_match ( dev , drv ) ) {
if ( ! acpi_bus_driver_init ( dev , drv ) ) {
2005-04-28 11:25:52 +04:00
acpi_start_single_object ( dev ) ;
2005-04-17 02:20:36 +04:00
atomic_inc ( & drv - > references ) ;
count + + ;
2005-08-05 08:44:28 +04:00
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO ,
" Found driver [%s] for device [%s] \n " ,
2005-04-17 02:20:36 +04:00
drv - > name , dev - > pnp . bus_id ) ) ;
}
}
spin_lock ( & acpi_device_lock ) ;
}
spin_unlock ( & acpi_device_lock ) ;
return_VALUE ( count ) ;
}
2005-08-05 08:44:28 +04:00
static int acpi_driver_detach ( struct acpi_driver * drv )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
struct list_head * node , * next ;
2005-04-17 02:20:36 +04:00
ACPI_FUNCTION_TRACE ( " acpi_driver_detach " ) ;
spin_lock ( & acpi_device_lock ) ;
2005-08-05 08:44:28 +04:00
list_for_each_safe ( node , next , & acpi_device_list ) {
struct acpi_device * dev =
container_of ( node , struct acpi_device , g_list ) ;
2005-04-17 02:20:36 +04:00
if ( dev - > driver = = drv ) {
spin_unlock ( & acpi_device_lock ) ;
if ( drv - > ops . remove )
2005-08-05 08:44:28 +04:00
drv - > ops . remove ( dev , ACPI_BUS_REMOVAL_NORMAL ) ;
2005-04-17 02:20:36 +04:00
spin_lock ( & acpi_device_lock ) ;
dev - > driver = NULL ;
dev - > driver_data = NULL ;
atomic_dec ( & drv - > references ) ;
}
}
spin_unlock ( & acpi_device_lock ) ;
return_VALUE ( 0 ) ;
}
/**
* acpi_bus_register_driver
* - - - - - - - - - - - - - - - - - - - - - - - -
* Registers a driver with the ACPI bus . Searches the namespace for all
* devices that match the driver ' s criteria and binds . Returns the
* number of devices that were claimed by the driver , or a negative
* error status for failure .
*/
2005-08-05 08:44:28 +04:00
int acpi_bus_register_driver ( struct acpi_driver * driver )
2005-04-17 02:20:36 +04:00
{
int count ;
ACPI_FUNCTION_TRACE ( " acpi_bus_register_driver " ) ;
if ( acpi_disabled )
return_VALUE ( - ENODEV ) ;
if ( ! driver )
return_VALUE ( - EINVAL ) ;
spin_lock ( & acpi_device_lock ) ;
list_add_tail ( & driver - > node , & acpi_bus_drivers ) ;
spin_unlock ( & acpi_device_lock ) ;
count = acpi_driver_attach ( driver ) ;
return_VALUE ( count ) ;
}
2005-08-05 08:44:28 +04:00
EXPORT_SYMBOL ( acpi_bus_register_driver ) ;
2005-04-17 02:20:36 +04:00
/**
* acpi_bus_unregister_driver
* - - - - - - - - - - - - - - - - - - - - - - - - - -
* Unregisters a driver with the ACPI bus . Searches the namespace for all
* devices that match the driver ' s criteria and unbinds .
*/
2005-08-05 08:44:28 +04:00
int acpi_bus_unregister_driver ( struct acpi_driver * driver )
2005-04-17 02:20:36 +04:00
{
int error = 0 ;
ACPI_FUNCTION_TRACE ( " acpi_bus_unregister_driver " ) ;
if ( driver ) {
acpi_driver_detach ( driver ) ;
if ( ! atomic_read ( & driver - > references ) ) {
spin_lock ( & acpi_device_lock ) ;
list_del_init ( & driver - > node ) ;
spin_unlock ( & acpi_device_lock ) ;
2005-08-05 08:44:28 +04:00
}
} else
2005-04-17 02:20:36 +04:00
error = - EINVAL ;
return_VALUE ( error ) ;
}
2005-08-05 08:44:28 +04:00
2005-04-17 02:20:36 +04:00
EXPORT_SYMBOL ( acpi_bus_unregister_driver ) ;
/**
* acpi_bus_find_driver
* - - - - - - - - - - - - - - - - - - - -
* Parses the list of registered drivers looking for a driver applicable for
* the specified device .
*/
2005-08-05 08:44:28 +04:00
static int acpi_bus_find_driver ( struct acpi_device * device )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
int result = 0 ;
struct list_head * node , * next ;
2005-04-17 02:20:36 +04:00
ACPI_FUNCTION_TRACE ( " acpi_bus_find_driver " ) ;
spin_lock ( & acpi_device_lock ) ;
2005-08-05 08:44:28 +04:00
list_for_each_safe ( node , next , & acpi_bus_drivers ) {
struct acpi_driver * driver =
container_of ( node , struct acpi_driver , node ) ;
2005-04-17 02:20:36 +04:00
atomic_inc ( & driver - > references ) ;
spin_unlock ( & acpi_device_lock ) ;
if ( ! acpi_bus_match ( device , driver ) ) {
result = acpi_bus_driver_init ( device , driver ) ;
if ( ! result )
goto Done ;
}
atomic_dec ( & driver - > references ) ;
spin_lock ( & acpi_device_lock ) ;
}
spin_unlock ( & acpi_device_lock ) ;
2005-08-05 08:44:28 +04:00
Done :
2005-04-17 02:20:36 +04:00
return_VALUE ( result ) ;
}
/* --------------------------------------------------------------------------
Device Enumeration
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2005-08-05 08:44:28 +04:00
static int acpi_bus_get_flags ( struct acpi_device * device )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
acpi_status status = AE_OK ;
acpi_handle temp = NULL ;
2005-04-17 02:20:36 +04:00
ACPI_FUNCTION_TRACE ( " acpi_bus_get_flags " ) ;
/* Presence of _STA indicates 'dynamic_status' */
status = acpi_get_handle ( device - > handle , " _STA " , & temp ) ;
if ( ACPI_SUCCESS ( status ) )
device - > flags . dynamic_status = 1 ;
/* Presence of _CID indicates 'compatible_ids' */
status = acpi_get_handle ( device - > handle , " _CID " , & temp ) ;
if ( ACPI_SUCCESS ( status ) )
device - > flags . compatible_ids = 1 ;
/* Presence of _RMV indicates 'removable' */
status = acpi_get_handle ( device - > handle , " _RMV " , & temp ) ;
if ( ACPI_SUCCESS ( status ) )
device - > flags . removable = 1 ;
/* Presence of _EJD|_EJ0 indicates 'ejectable' */
status = acpi_get_handle ( device - > handle , " _EJD " , & temp ) ;
if ( ACPI_SUCCESS ( status ) )
device - > flags . ejectable = 1 ;
else {
status = acpi_get_handle ( device - > handle , " _EJ0 " , & temp ) ;
if ( ACPI_SUCCESS ( status ) )
device - > flags . ejectable = 1 ;
}
/* Presence of _LCK indicates 'lockable' */
status = acpi_get_handle ( device - > handle , " _LCK " , & temp ) ;
if ( ACPI_SUCCESS ( status ) )
device - > flags . lockable = 1 ;
/* Presence of _PS0|_PR0 indicates 'power manageable' */
status = acpi_get_handle ( device - > handle , " _PS0 " , & temp ) ;
if ( ACPI_FAILURE ( status ) )
status = acpi_get_handle ( device - > handle , " _PR0 " , & temp ) ;
if ( ACPI_SUCCESS ( status ) )
device - > flags . power_manageable = 1 ;
/* Presence of _PRW indicates wake capable */
status = acpi_get_handle ( device - > handle , " _PRW " , & temp ) ;
if ( ACPI_SUCCESS ( status ) )
device - > flags . wake_capable = 1 ;
/* TBD: Peformance management */
return_VALUE ( 0 ) ;
}
2005-08-05 08:44:28 +04:00
static void acpi_device_get_busid ( struct acpi_device * device ,
acpi_handle handle , int type )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
char bus_id [ 5 ] = { ' ? ' , 0 } ;
struct acpi_buffer buffer = { sizeof ( bus_id ) , bus_id } ;
int i = 0 ;
2005-04-17 02:20:36 +04:00
/*
* Bus ID
* - - - - - -
* The device ' s Bus ID is simply the object name .
* TBD : Shouldn ' t this value be unique ( within the ACPI namespace ) ?
*/
switch ( type ) {
case ACPI_BUS_TYPE_SYSTEM :
strcpy ( device - > pnp . bus_id , " ACPI " ) ;
break ;
case ACPI_BUS_TYPE_POWER_BUTTON :
strcpy ( device - > pnp . bus_id , " PWRF " ) ;
break ;
case ACPI_BUS_TYPE_SLEEP_BUTTON :
strcpy ( device - > pnp . bus_id , " SLPF " ) ;
break ;
default :
acpi_get_name ( handle , ACPI_SINGLE_NAME , & buffer ) ;
/* Clean up trailing underscores (if any) */
for ( i = 3 ; i > 1 ; i - - ) {
if ( bus_id [ i ] = = ' _ ' )
bus_id [ i ] = ' \0 ' ;
else
break ;
}
strcpy ( device - > pnp . bus_id , bus_id ) ;
break ;
}
}
2005-08-05 08:44:28 +04:00
static void acpi_device_set_id ( struct acpi_device * device ,
struct acpi_device * parent , acpi_handle handle ,
int type )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
struct acpi_device_info * info ;
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER , NULL } ;
char * hid = NULL ;
char * uid = NULL ;
2005-04-17 02:20:36 +04:00
struct acpi_compatible_id_list * cid_list = NULL ;
2005-08-05 08:44:28 +04:00
acpi_status status ;
2005-04-17 02:20:36 +04:00
switch ( type ) {
case ACPI_BUS_TYPE_DEVICE :
status = acpi_get_object_info ( handle , & buffer ) ;
if ( ACPI_FAILURE ( status ) ) {
2005-08-05 08:44:28 +04:00
printk ( " %s: Error reading device info \n " , __FUNCTION__ ) ;
2005-04-17 02:20:36 +04:00
return ;
}
info = buffer . pointer ;
if ( info - > valid & ACPI_VALID_HID )
hid = info - > hardware_id . value ;
if ( info - > valid & ACPI_VALID_UID )
uid = info - > unique_id . value ;
if ( info - > valid & ACPI_VALID_CID )
cid_list = & info - > compatibility_id ;
if ( info - > valid & ACPI_VALID_ADR ) {
device - > pnp . bus_address = info - > address ;
device - > flags . bus_address = 1 ;
}
break ;
case ACPI_BUS_TYPE_POWER :
hid = ACPI_POWER_HID ;
break ;
case ACPI_BUS_TYPE_PROCESSOR :
hid = ACPI_PROCESSOR_HID ;
break ;
case ACPI_BUS_TYPE_SYSTEM :
hid = ACPI_SYSTEM_HID ;
break ;
case ACPI_BUS_TYPE_THERMAL :
hid = ACPI_THERMAL_HID ;
break ;
case ACPI_BUS_TYPE_POWER_BUTTON :
hid = ACPI_BUTTON_HID_POWERF ;
break ;
case ACPI_BUS_TYPE_SLEEP_BUTTON :
hid = ACPI_BUTTON_HID_SLEEPF ;
break ;
}
/*
* \ _SB
* - - - -
* Fix for the system root bus device - - the only root - level device .
*/
if ( ( parent = = ACPI_ROOT_OBJECT ) & & ( type = = ACPI_BUS_TYPE_DEVICE ) ) {
hid = ACPI_BUS_HID ;
strcpy ( device - > pnp . device_name , ACPI_BUS_DEVICE_NAME ) ;
strcpy ( device - > pnp . device_class , ACPI_BUS_CLASS ) ;
}
if ( hid ) {
strcpy ( device - > pnp . hardware_id , hid ) ;
device - > flags . hardware_id = 1 ;
}
if ( uid ) {
strcpy ( device - > pnp . unique_id , uid ) ;
device - > flags . unique_id = 1 ;
}
if ( cid_list ) {
device - > pnp . cid_list = kmalloc ( cid_list - > size , GFP_KERNEL ) ;
if ( device - > pnp . cid_list )
memcpy ( device - > pnp . cid_list , cid_list , cid_list - > size ) ;
else
printk ( KERN_ERR " Memory allocation error \n " ) ;
}
acpi_os_free ( buffer . pointer ) ;
}
2005-08-05 08:44:28 +04:00
static int acpi_device_set_context ( struct acpi_device * device , int type )
2005-04-17 02:20:36 +04:00
{
acpi_status status = AE_OK ;
int result = 0 ;
/*
* Context
* - - - - - - -
* Attach this ' struct acpi_device ' to the ACPI object . This makes
* resolutions from handle - > device very efficient . Note that we need
* to be careful with fixed - feature devices as they all attach to the
* root object .
*/
2005-08-05 08:44:28 +04:00
if ( type ! = ACPI_BUS_TYPE_POWER_BUTTON & &
2005-04-17 02:20:36 +04:00
type ! = ACPI_BUS_TYPE_SLEEP_BUTTON ) {
status = acpi_attach_data ( device - > handle ,
2005-08-05 08:44:28 +04:00
acpi_bus_data_handler , device ) ;
2005-04-17 02:20:36 +04:00
if ( ACPI_FAILURE ( status ) ) {
printk ( " Error attaching device data \n " ) ;
result = - ENODEV ;
}
}
return result ;
}
2005-08-05 08:44:28 +04:00
static void acpi_device_get_debug_info ( struct acpi_device * device ,
acpi_handle handle , int type )
2005-04-17 02:20:36 +04:00
{
# ifdef CONFIG_ACPI_DEBUG_OUTPUT
2005-08-05 08:44:28 +04:00
char * type_string = NULL ;
char name [ 80 ] = { ' ? ' , ' \0 ' } ;
struct acpi_buffer buffer = { sizeof ( name ) , name } ;
2005-04-17 02:20:36 +04:00
switch ( type ) {
case ACPI_BUS_TYPE_DEVICE :
type_string = " Device " ;
acpi_get_name ( handle , ACPI_FULL_PATHNAME , & buffer ) ;
break ;
case ACPI_BUS_TYPE_POWER :
type_string = " Power Resource " ;
acpi_get_name ( handle , ACPI_FULL_PATHNAME , & buffer ) ;
break ;
case ACPI_BUS_TYPE_PROCESSOR :
type_string = " Processor " ;
acpi_get_name ( handle , ACPI_FULL_PATHNAME , & buffer ) ;
break ;
case ACPI_BUS_TYPE_SYSTEM :
type_string = " System " ;
acpi_get_name ( handle , ACPI_FULL_PATHNAME , & buffer ) ;
break ;
case ACPI_BUS_TYPE_THERMAL :
type_string = " Thermal Zone " ;
acpi_get_name ( handle , ACPI_FULL_PATHNAME , & buffer ) ;
break ;
case ACPI_BUS_TYPE_POWER_BUTTON :
type_string = " Power Button " ;
sprintf ( name , " PWRB " ) ;
break ;
case ACPI_BUS_TYPE_SLEEP_BUTTON :
type_string = " Sleep Button " ;
sprintf ( name , " SLPB " ) ;
break ;
}
printk ( KERN_DEBUG " Found %s %s [%p] \n " , type_string , name , handle ) ;
2005-08-05 08:44:28 +04:00
# endif /*CONFIG_ACPI_DEBUG_OUTPUT */
2005-04-17 02:20:36 +04:00
}
2005-08-05 08:44:28 +04:00
static int acpi_bus_remove ( struct acpi_device * dev , int rmdevice )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
int result = 0 ;
struct acpi_driver * driver ;
2005-04-17 02:20:36 +04:00
ACPI_FUNCTION_TRACE ( " acpi_bus_remove " ) ;
if ( ! dev )
return_VALUE ( - EINVAL ) ;
driver = dev - > driver ;
if ( ( driver ) & & ( driver - > ops . remove ) ) {
if ( driver - > ops . stop ) {
result = driver - > ops . stop ( dev , ACPI_BUS_REMOVAL_EJECT ) ;
if ( result )
return_VALUE ( result ) ;
}
result = dev - > driver - > ops . remove ( dev , ACPI_BUS_REMOVAL_EJECT ) ;
if ( result ) {
return_VALUE ( result ) ;
}
atomic_dec ( & dev - > driver - > references ) ;
dev - > driver = NULL ;
acpi_driver_data ( dev ) = NULL ;
}
if ( ! rmdevice )
return_VALUE ( 0 ) ;
if ( dev - > flags . bus_address ) {
if ( ( dev - > parent ) & & ( dev - > parent - > ops . unbind ) )
dev - > parent - > ops . unbind ( dev ) ;
}
2005-08-05 08:44:28 +04:00
2005-04-17 02:20:36 +04:00
acpi_device_unregister ( dev , ACPI_BUS_REMOVAL_EJECT ) ;
return_VALUE ( 0 ) ;
}
2005-04-28 11:25:52 +04:00
static int
2005-08-05 08:44:28 +04:00
acpi_add_single_object ( struct acpi_device * * child ,
struct acpi_device * parent , acpi_handle handle , int type )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
int result = 0 ;
struct acpi_device * device = NULL ;
2005-04-17 02:20:36 +04:00
2005-04-28 11:25:52 +04:00
ACPI_FUNCTION_TRACE ( " acpi_add_single_object " ) ;
2005-04-17 02:20:36 +04:00
if ( ! child )
return_VALUE ( - EINVAL ) ;
device = kmalloc ( sizeof ( struct acpi_device ) , GFP_KERNEL ) ;
if ( ! device ) {
ACPI_DEBUG_PRINT ( ( ACPI_DB_ERROR , " Memory allocation error \n " ) ) ;
return_VALUE ( - ENOMEM ) ;
}
memset ( device , 0 , sizeof ( struct acpi_device ) ) ;
device - > handle = handle ;
device - > parent = parent ;
2005-08-05 08:44:28 +04:00
acpi_device_get_busid ( device , handle , type ) ;
2005-04-17 02:20:36 +04:00
/*
* Flags
* - - - - -
* Get prior to calling acpi_bus_get_status ( ) so we know whether
* or not _STA is present . Note that we only look for object
* handles - - cannot evaluate objects until we know the device is
* present and properly initialized .
*/
result = acpi_bus_get_flags ( device ) ;
if ( result )
goto end ;
/*
* Status
* - - - - - -
2005-03-31 08:15:47 +04:00
* See if the device is present . We always assume that non - Device
* and non - Processor objects ( e . g . thermal zones , power resources ,
* etc . ) are present , functioning , etc . ( at least when parent object
* is present ) . Note that _STA has a different meaning for some
* objects ( e . g . power resources ) so we need to be careful how we use
* it .
2005-04-17 02:20:36 +04:00
*/
switch ( type ) {
2005-03-31 08:15:47 +04:00
case ACPI_BUS_TYPE_PROCESSOR :
2005-04-17 02:20:36 +04:00
case ACPI_BUS_TYPE_DEVICE :
result = acpi_bus_get_status ( device ) ;
if ( ACPI_FAILURE ( result ) | | ! device - > status . present ) {
result = - ENOENT ;
goto end ;
}
break ;
default :
STRUCT_TO_INT ( device - > status ) = 0x0F ;
break ;
}
/*
* Initialize Device
* - - - - - - - - - - - - - - - - -
* TBD : Synch with Core ' s enumeration / initialization process .
*/
/*
* Hardware ID , Unique ID , & Bus Address
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
2005-08-05 08:44:28 +04:00
acpi_device_set_id ( device , parent , handle , type ) ;
2005-04-17 02:20:36 +04:00
/*
* Power Management
* - - - - - - - - - - - - - - - -
*/
if ( device - > flags . power_manageable ) {
result = acpi_bus_get_power_flags ( device ) ;
if ( result )
goto end ;
}
2005-08-05 08:44:28 +04:00
/*
2005-04-17 02:20:36 +04:00
* Wakeup device management
* - - - - - - - - - - - - - - - - - - - - - - -
*/
if ( device - > flags . wake_capable ) {
result = acpi_bus_get_wakeup_device_flags ( device ) ;
if ( result )
goto end ;
}
/*
* Performance Management
* - - - - - - - - - - - - - - - - - - - - - -
*/
if ( device - > flags . performance_manageable ) {
result = acpi_bus_get_perf_flags ( device ) ;
if ( result )
goto end ;
}
2005-08-05 08:44:28 +04:00
if ( ( result = acpi_device_set_context ( device , type ) ) )
2005-04-17 02:20:36 +04:00
goto end ;
2005-08-05 08:44:28 +04:00
acpi_device_get_debug_info ( device , handle , type ) ;
2005-04-17 02:20:36 +04:00
2005-08-05 08:44:28 +04:00
acpi_device_register ( device , parent ) ;
2005-04-17 02:20:36 +04:00
/*
* Bind _ADR - Based Devices
* - - - - - - - - - - - - - - - - - - - - - - -
* If there ' s a a bus address ( _ADR ) then we utilize the parent ' s
* ' bind ' function ( if exists ) to bind the ACPI - and natively -
* enumerated device representations .
*/
if ( device - > flags . bus_address ) {
if ( device - > parent & & device - > parent - > ops . bind )
device - > parent - > ops . bind ( device ) ;
}
/*
* Locate & Attach Driver
* - - - - - - - - - - - - - - - - - - - - - -
* If there ' s a hardware id ( _HID ) or compatible ids ( _CID ) we check
* to see if there ' s a driver installed for this kind of device . Note
* that drivers can install before or after a device is enumerated .
*
* TBD : Assumes LDM provides driver hot - plug capability .
*/
2005-04-28 11:25:52 +04:00
result = acpi_bus_find_driver ( device ) ;
2005-04-17 02:20:36 +04:00
2005-08-05 08:44:28 +04:00
end :
2005-04-17 02:20:36 +04:00
if ( ! result )
* child = device ;
else {
2005-11-07 12:01:32 +03:00
kfree ( device - > pnp . cid_list ) ;
2005-04-17 02:20:36 +04:00
kfree ( device ) ;
}
return_VALUE ( result ) ;
}
2005-08-05 08:44:28 +04:00
static int acpi_bus_scan ( struct acpi_device * start , struct acpi_bus_ops * ops )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
acpi_status status = AE_OK ;
struct acpi_device * parent = NULL ;
struct acpi_device * child = NULL ;
acpi_handle phandle = NULL ;
acpi_handle chandle = NULL ;
acpi_object_type type = 0 ;
u32 level = 1 ;
2005-04-17 02:20:36 +04:00
ACPI_FUNCTION_TRACE ( " acpi_bus_scan " ) ;
if ( ! start )
return_VALUE ( - EINVAL ) ;
parent = start ;
phandle = start - > handle ;
2005-08-05 08:44:28 +04:00
2005-04-17 02:20:36 +04:00
/*
* Parse through the ACPI namespace , identify all ' devices ' , and
* create a new ' struct acpi_device ' for each .
*/
while ( ( level > 0 ) & & parent ) {
status = acpi_get_next_object ( ACPI_TYPE_ANY , phandle ,
2005-08-05 08:44:28 +04:00
chandle , & chandle ) ;
2005-04-17 02:20:36 +04:00
/*
* If this scope is exhausted then move our way back up .
*/
if ( ACPI_FAILURE ( status ) ) {
level - - ;
chandle = phandle ;
acpi_get_parent ( phandle , & phandle ) ;
if ( parent - > parent )
parent = parent - > parent ;
continue ;
}
status = acpi_get_type ( chandle , & type ) ;
if ( ACPI_FAILURE ( status ) )
continue ;
/*
* If this is a scope object then parse it ( depth - first ) .
*/
if ( type = = ACPI_TYPE_LOCAL_SCOPE ) {
level + + ;
phandle = chandle ;
chandle = NULL ;
continue ;
}
/*
* We ' re only interested in objects that we consider ' devices ' .
*/
switch ( type ) {
case ACPI_TYPE_DEVICE :
type = ACPI_BUS_TYPE_DEVICE ;
break ;
case ACPI_TYPE_PROCESSOR :
type = ACPI_BUS_TYPE_PROCESSOR ;
break ;
case ACPI_TYPE_THERMAL :
type = ACPI_BUS_TYPE_THERMAL ;
break ;
case ACPI_TYPE_POWER :
type = ACPI_BUS_TYPE_POWER ;
break ;
default :
continue ;
}
2005-04-28 11:25:52 +04:00
if ( ops - > acpi_op_add )
status = acpi_add_single_object ( & child , parent ,
2005-08-05 08:44:28 +04:00
chandle , type ) ;
else
2005-04-28 11:25:52 +04:00
status = acpi_bus_get_device ( chandle , & child ) ;
2005-08-05 08:44:28 +04:00
if ( ACPI_FAILURE ( status ) )
continue ;
2005-04-28 11:25:52 +04:00
if ( ops - > acpi_op_start ) {
status = acpi_start_single_object ( child ) ;
if ( ACPI_FAILURE ( status ) )
continue ;
}
2005-04-17 02:20:36 +04:00
/*
* If the device is present , enabled , and functioning then
* parse its scope ( depth - first ) . Note that we need to
* represent absent devices to facilitate PnP notifications
* - - but only the subtree head ( not all of its children ,
* which will be enumerated when the parent is inserted ) .
*
* TBD : Need notifications and other detection mechanisms
2005-08-05 08:44:28 +04:00
* in place before we can fully implement this .
2005-04-17 02:20:36 +04:00
*/
if ( child - > status . present ) {
status = acpi_get_next_object ( ACPI_TYPE_ANY , chandle ,
NULL , NULL ) ;
if ( ACPI_SUCCESS ( status ) ) {
level + + ;
phandle = chandle ;
chandle = NULL ;
parent = child ;
}
}
}
return_VALUE ( 0 ) ;
}
2005-04-28 11:25:52 +04:00
int
2005-08-05 08:44:28 +04:00
acpi_bus_add ( struct acpi_device * * child ,
struct acpi_device * parent , acpi_handle handle , int type )
2005-04-28 11:25:52 +04:00
{
int result ;
struct acpi_bus_ops ops ;
ACPI_FUNCTION_TRACE ( " acpi_bus_add " ) ;
result = acpi_add_single_object ( child , parent , handle , type ) ;
if ( ! result ) {
memset ( & ops , 0 , sizeof ( ops ) ) ;
ops . acpi_op_add = 1 ;
result = acpi_bus_scan ( * child , & ops ) ;
}
return_VALUE ( result ) ;
}
2005-08-05 08:44:28 +04:00
2005-04-28 11:25:52 +04:00
EXPORT_SYMBOL ( acpi_bus_add ) ;
2005-08-05 08:44:28 +04:00
int acpi_bus_start ( struct acpi_device * device )
2005-04-28 11:25:52 +04:00
{
int result ;
struct acpi_bus_ops ops ;
ACPI_FUNCTION_TRACE ( " acpi_bus_start " ) ;
if ( ! device )
return_VALUE ( - EINVAL ) ;
result = acpi_start_single_object ( device ) ;
if ( ! result ) {
memset ( & ops , 0 , sizeof ( ops ) ) ;
ops . acpi_op_start = 1 ;
result = acpi_bus_scan ( device , & ops ) ;
}
return_VALUE ( result ) ;
}
2005-08-05 08:44:28 +04:00
2005-04-28 11:25:52 +04:00
EXPORT_SYMBOL ( acpi_bus_start ) ;
2005-04-17 02:20:36 +04:00
2005-08-05 08:44:28 +04:00
static int acpi_bus_trim ( struct acpi_device * start , int rmdevice )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
acpi_status status ;
struct acpi_device * parent , * child ;
acpi_handle phandle , chandle ;
acpi_object_type type ;
u32 level = 1 ;
int err = 0 ;
parent = start ;
2005-04-17 02:20:36 +04:00
phandle = start - > handle ;
child = chandle = NULL ;
while ( ( level > 0 ) & & parent & & ( ! err ) ) {
status = acpi_get_next_object ( ACPI_TYPE_ANY , phandle ,
2005-08-05 08:44:28 +04:00
chandle , & chandle ) ;
2005-04-17 02:20:36 +04:00
/*
* If this scope is exhausted then move our way back up .
*/
if ( ACPI_FAILURE ( status ) ) {
level - - ;
chandle = phandle ;
acpi_get_parent ( phandle , & phandle ) ;
child = parent ;
parent = parent - > parent ;
if ( level = = 0 )
err = acpi_bus_remove ( child , rmdevice ) ;
else
err = acpi_bus_remove ( child , 1 ) ;
continue ;
}
status = acpi_get_type ( chandle , & type ) ;
if ( ACPI_FAILURE ( status ) ) {
continue ;
}
/*
* If there is a device corresponding to chandle then
* parse it ( depth - first ) .
*/
if ( acpi_bus_get_device ( chandle , & child ) = = 0 ) {
level + + ;
phandle = chandle ;
chandle = NULL ;
parent = child ;
}
continue ;
}
return err ;
}
2005-08-05 08:44:28 +04:00
static int acpi_bus_scan_fixed ( struct acpi_device * root )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
int result = 0 ;
struct acpi_device * device = NULL ;
2005-04-17 02:20:36 +04:00
ACPI_FUNCTION_TRACE ( " acpi_bus_scan_fixed " ) ;
if ( ! root )
return_VALUE ( - ENODEV ) ;
/*
* Enumerate all fixed - feature devices .
*/
2005-04-28 11:25:52 +04:00
if ( acpi_fadt . pwr_button = = 0 ) {
result = acpi_add_single_object ( & device , acpi_root ,
2005-08-05 08:44:28 +04:00
NULL ,
ACPI_BUS_TYPE_POWER_BUTTON ) ;
2005-04-28 11:25:52 +04:00
if ( ! result )
result = acpi_start_single_object ( device ) ;
}
2005-04-17 02:20:36 +04:00
2005-04-28 11:25:52 +04:00
if ( acpi_fadt . sleep_button = = 0 ) {
result = acpi_add_single_object ( & device , acpi_root ,
2005-08-05 08:44:28 +04:00
NULL ,
ACPI_BUS_TYPE_SLEEP_BUTTON ) ;
2005-04-28 11:25:52 +04:00
if ( ! result )
result = acpi_start_single_object ( device ) ;
}
2005-04-17 02:20:36 +04:00
return_VALUE ( result ) ;
}
static int __init acpi_scan_init ( void )
{
int result ;
2005-04-28 11:25:52 +04:00
struct acpi_bus_ops ops ;
2005-04-17 02:20:36 +04:00
ACPI_FUNCTION_TRACE ( " acpi_scan_init " ) ;
if ( acpi_disabled )
return_VALUE ( 0 ) ;
kset_register ( & acpi_namespace_kset ) ;
/*
* Create the root device in the bus ' s device tree
*/
2005-04-28 11:25:52 +04:00
result = acpi_add_single_object ( & acpi_root , NULL , ACPI_ROOT_OBJECT ,
2005-08-05 08:44:28 +04:00
ACPI_BUS_TYPE_SYSTEM ) ;
2005-04-17 02:20:36 +04:00
if ( result )
goto Done ;
2005-04-28 11:25:52 +04:00
result = acpi_start_single_object ( acpi_root ) ;
2005-04-17 02:20:36 +04:00
/*
* Enumerate devices in the ACPI namespace .
*/
result = acpi_bus_scan_fixed ( acpi_root ) ;
2005-04-28 11:25:52 +04:00
if ( ! result ) {
memset ( & ops , 0 , sizeof ( ops ) ) ;
ops . acpi_op_add = 1 ;
ops . acpi_op_start = 1 ;
result = acpi_bus_scan ( acpi_root , & ops ) ;
}
2005-04-17 02:20:36 +04:00
if ( result )
acpi_device_unregister ( acpi_root , ACPI_BUS_REMOVAL_NORMAL ) ;
2005-08-05 08:44:28 +04:00
Done :
2005-04-17 02:20:36 +04:00
return_VALUE ( result ) ;
}
subsys_initcall ( acpi_scan_init ) ;