2005-04-16 15:20:36 -07:00
/*
* pnpacpi - - PnP ACPI driver
*
* Copyright ( c ) 2004 Matthieu Castet < castet . matthieu @ free . fr >
* Copyright ( c ) 2004 Li Shaohua < shaohua . li @ intel . com >
2007-05-08 00:28:35 -07:00
*
2005-04-16 15:20:36 -07:00
* 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 , 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
*/
2007-05-08 00:28:35 -07:00
2005-04-16 15:20:36 -07:00
# include <linux/acpi.h>
# include <linux/pnp.h>
# include <acpi/acpi_bus.h>
# include "pnpacpi.h"
static int num = 0 ;
2006-01-06 01:31:00 -05:00
/* We need only to blacklist devices that have already an acpi driver that
* can ' t use pnp layer . We don ' t need to blacklist device that are directly
* used by the kernel ( PCI root , . . . ) , as it is harmless and there were
* already present in pnpbios . But there is an exception for devices that
* have irqs ( PIC , Timer ) because we call acpi_register_gsi .
* Finaly only devices that have a CRS method need to be in this list .
*/
2005-04-16 15:20:36 -07:00
static char __initdata excluded_id_list [ ] =
" PNP0C09, " /* EC */
" PNP0C0F, " /* Link device */
" PNP0000, " /* PIC */
" PNP0100, " /* Timer */
;
static inline int is_exclusive_device ( struct acpi_device * dev )
{
return ( ! acpi_match_ids ( dev , excluded_id_list ) ) ;
}
/*
* Compatible Device IDs
*/
# define TEST_HEX(c) \
if ( ! ( ( ' 0 ' < = ( c ) & & ( c ) < = ' 9 ' ) | | ( ' A ' < = ( c ) & & ( c ) < = ' F ' ) ) ) \
return 0
# define TEST_ALPHA(c) \
if ( ! ( ' @ ' < = ( c ) | | ( c ) < = ' Z ' ) ) \
return 0
static int __init ispnpidacpi ( char * id )
{
TEST_ALPHA ( id [ 0 ] ) ;
TEST_ALPHA ( id [ 1 ] ) ;
TEST_ALPHA ( id [ 2 ] ) ;
TEST_HEX ( id [ 3 ] ) ;
TEST_HEX ( id [ 4 ] ) ;
TEST_HEX ( id [ 5 ] ) ;
TEST_HEX ( id [ 6 ] ) ;
if ( id [ 7 ] ! = ' \0 ' )
return 0 ;
return 1 ;
}
static void __init pnpidacpi_to_pnpid ( char * id , char * str )
{
str [ 0 ] = id [ 0 ] ;
str [ 1 ] = id [ 1 ] ;
str [ 2 ] = id [ 2 ] ;
str [ 3 ] = tolower ( id [ 3 ] ) ;
str [ 4 ] = tolower ( id [ 4 ] ) ;
str [ 5 ] = tolower ( id [ 5 ] ) ;
str [ 6 ] = tolower ( id [ 6 ] ) ;
str [ 7 ] = ' \0 ' ;
}
static int pnpacpi_get_resources ( struct pnp_dev * dev , struct pnp_resource_table * res )
{
acpi_status status ;
2007-05-08 00:28:35 -07:00
status = pnpacpi_parse_allocated_resource ( ( acpi_handle ) dev - > data ,
2005-04-16 15:20:36 -07:00
& dev - > res ) ;
return ACPI_FAILURE ( status ) ? - ENODEV : 0 ;
}
static int pnpacpi_set_resources ( struct pnp_dev * dev , struct pnp_resource_table * res )
{
acpi_handle handle = dev - > data ;
struct acpi_buffer buffer ;
int ret = 0 ;
acpi_status status ;
ret = pnpacpi_build_resource_template ( handle , & buffer ) ;
if ( ret )
return ret ;
ret = pnpacpi_encode_resources ( res , & buffer ) ;
if ( ret ) {
kfree ( buffer . pointer ) ;
return ret ;
}
status = acpi_set_current_resources ( handle , & buffer ) ;
if ( ACPI_FAILURE ( status ) )
ret = - EINVAL ;
kfree ( buffer . pointer ) ;
return ret ;
}
static int pnpacpi_disable_resources ( struct pnp_dev * dev )
{
acpi_status status ;
2007-05-08 00:28:35 -07:00
2005-04-16 15:20:36 -07:00
/* acpi_unregister_gsi(pnp_irq(dev, 0)); */
2007-05-08 00:28:35 -07:00
status = acpi_evaluate_object ( ( acpi_handle ) dev - > data ,
2005-04-16 15:20:36 -07:00
" _DIS " , NULL , NULL ) ;
return ACPI_FAILURE ( status ) ? - ENODEV : 0 ;
}
2005-11-07 01:01:48 -08:00
static struct pnp_protocol pnpacpi_protocol = {
2005-04-16 15:20:36 -07:00
. name = " Plug and Play ACPI " ,
. get = pnpacpi_get_resources ,
. set = pnpacpi_set_resources ,
. disable = pnpacpi_disable_resources ,
} ;
static int __init pnpacpi_add_device ( struct acpi_device * device )
{
acpi_handle temp = NULL ;
acpi_status status ;
struct pnp_id * dev_id ;
struct pnp_dev * dev ;
2006-01-06 01:31:00 -05:00
status = acpi_get_handle ( device - > handle , " _CRS " , & temp ) ;
if ( ACPI_FAILURE ( status ) | | ! ispnpidacpi ( acpi_device_hid ( device ) ) | |
2005-04-16 15:20:36 -07:00
is_exclusive_device ( device ) )
return 0 ;
pnp_dbg ( " ACPI device : hid %s " , acpi_device_hid ( device ) ) ;
2006-12-13 00:34:52 -08:00
dev = kzalloc ( sizeof ( struct pnp_dev ) , GFP_KERNEL ) ;
2005-04-16 15:20:36 -07:00
if ( ! dev ) {
pnp_err ( " Out of memory " ) ;
return - ENOMEM ;
}
dev - > data = device - > handle ;
/* .enabled means if the device can decode the resources */
dev - > active = device - > status . enabled ;
status = acpi_get_handle ( device - > handle , " _SRS " , & temp ) ;
if ( ACPI_SUCCESS ( status ) )
dev - > capabilities | = PNP_CONFIGURABLE ;
dev - > capabilities | = PNP_READ ;
if ( device - > flags . dynamic_status )
dev - > capabilities | = PNP_WRITE ;
if ( device - > flags . removable )
dev - > capabilities | = PNP_REMOVABLE ;
status = acpi_get_handle ( device - > handle , " _DIS " , & temp ) ;
if ( ACPI_SUCCESS ( status ) )
dev - > capabilities | = PNP_DISABLE ;
dev - > protocol = & pnpacpi_protocol ;
if ( strlen ( acpi_device_name ( device ) ) )
strncpy ( dev - > name , acpi_device_name ( device ) , sizeof ( dev - > name ) ) ;
else
strncpy ( dev - > name , acpi_device_bid ( device ) , sizeof ( dev - > name ) ) ;
dev - > number = num ;
2007-05-08 00:28:35 -07:00
2005-04-16 15:20:36 -07:00
/* set the initial values for the PnP device */
2006-12-13 00:34:52 -08:00
dev_id = kzalloc ( sizeof ( struct pnp_id ) , GFP_KERNEL ) ;
2005-04-16 15:20:36 -07:00
if ( ! dev_id )
goto err ;
pnpidacpi_to_pnpid ( acpi_device_hid ( device ) , dev_id - > id ) ;
pnp_add_id ( dev_id , dev ) ;
if ( dev - > active ) {
/* parse allocated resource */
status = pnpacpi_parse_allocated_resource ( device - > handle , & dev - > res ) ;
if ( ACPI_FAILURE ( status ) & & ( status ! = AE_NOT_FOUND ) ) {
pnp_err ( " PnPACPI: METHOD_NAME__CRS failure for %s " , dev_id - > id ) ;
goto err1 ;
}
}
if ( dev - > capabilities & PNP_CONFIGURABLE ) {
2007-05-08 00:28:35 -07:00
status = pnpacpi_parse_resource_option_data ( device - > handle ,
2005-04-16 15:20:36 -07:00
dev ) ;
if ( ACPI_FAILURE ( status ) & & ( status ! = AE_NOT_FOUND ) ) {
pnp_err ( " PnPACPI: METHOD_NAME__PRS failure for %s " , dev_id - > id ) ;
goto err1 ;
}
}
2007-05-08 00:28:35 -07:00
2005-04-16 15:20:36 -07:00
/* parse compatible ids */
if ( device - > flags . compatible_ids ) {
struct acpi_compatible_id_list * cid_list = device - > pnp . cid_list ;
int i ;
for ( i = 0 ; i < cid_list - > count ; i + + ) {
if ( ! ispnpidacpi ( cid_list - > id [ i ] . value ) )
continue ;
2006-12-13 00:34:52 -08:00
dev_id = kzalloc ( sizeof ( struct pnp_id ) , GFP_KERNEL ) ;
2005-04-16 15:20:36 -07:00
if ( ! dev_id )
continue ;
pnpidacpi_to_pnpid ( cid_list - > id [ i ] . value , dev_id - > id ) ;
pnp_add_id ( dev_id , dev ) ;
}
}
/* clear out the damaged flags */
if ( ! dev - > active )
pnp_init_resource_table ( & dev - > res ) ;
pnp_add_device ( dev ) ;
num + + ;
return AE_OK ;
err1 :
kfree ( dev_id ) ;
err :
kfree ( dev ) ;
return - EINVAL ;
}
static acpi_status __init pnpacpi_add_device_handler ( acpi_handle handle ,
u32 lvl , void * context , void * * rv )
{
struct acpi_device * device ;
if ( ! acpi_bus_get_device ( handle , & device ) )
pnpacpi_add_device ( device ) ;
else
return AE_CTRL_DEPTH ;
return AE_OK ;
}
2007-05-08 00:28:35 -07:00
static int __init acpi_pnp_match ( struct device * dev , void * _pnp )
{
struct acpi_device * acpi = to_acpi_device ( dev ) ;
struct pnp_dev * pnp = _pnp ;
/* true means it matched */
return acpi - > flags . hardware_id
& & ! acpi_get_physical_device ( acpi - > handle )
& & compare_pnp_id ( pnp - > id , acpi - > pnp . hardware_id ) ;
}
static int __init acpi_pnp_find_device ( struct device * dev , acpi_handle * handle )
{
struct device * adev ;
struct acpi_device * acpi ;
adev = bus_find_device ( & acpi_bus_type , NULL ,
to_pnp_dev ( dev ) ,
acpi_pnp_match ) ;
if ( ! adev )
return - ENODEV ;
acpi = to_acpi_device ( adev ) ;
* handle = acpi - > handle ;
put_device ( adev ) ;
return 0 ;
}
/* complete initialization of a PNPACPI device includes having
* pnpdev - > dev . archdata . acpi_handle point to its ACPI sibling .
*/
static struct acpi_bus_type __initdata acpi_pnp_bus = {
. bus = & pnp_bus_type ,
. find_device = acpi_pnp_find_device ,
} ;
2005-04-16 15:20:36 -07:00
int pnpacpi_disabled __initdata ;
2005-11-07 01:01:48 -08:00
static int __init pnpacpi_init ( void )
2005-04-16 15:20:36 -07:00
{
if ( acpi_disabled | | pnpacpi_disabled ) {
pnp_info ( " PnP ACPI: disabled " ) ;
return 0 ;
}
pnp_info ( " PnP ACPI init " ) ;
pnp_register_protocol ( & pnpacpi_protocol ) ;
2007-05-08 00:28:35 -07:00
register_acpi_bus_type ( & acpi_pnp_bus ) ;
2005-04-16 15:20:36 -07:00
acpi_get_devices ( NULL , pnpacpi_add_device_handler , NULL , NULL ) ;
pnp_info ( " PnP ACPI: found %d devices " , num ) ;
2007-05-08 00:28:35 -07:00
unregister_acpi_bus_type ( & acpi_pnp_bus ) ;
2007-05-08 00:35:54 -07:00
pnp_platform_devices = 1 ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
subsys_initcall ( pnpacpi_init ) ;
static int __init pnpacpi_setup ( char * str )
{
if ( str = = NULL )
return 1 ;
if ( ! strncmp ( str , " off " , 3 ) )
pnpacpi_disabled = 1 ;
return 1 ;
}
__setup ( " pnpacpi= " , pnpacpi_setup ) ;
2005-11-07 01:01:48 -08:00
#if 0
2005-04-16 15:20:36 -07:00
EXPORT_SYMBOL ( pnpacpi_protocol ) ;
2005-11-07 01:01:48 -08:00
# endif