2005-04-17 02:20:36 +04:00
/*
* acpi_container . c - ACPI Generic Container Driver
* ( $ Revision : )
*
* Copyright ( C ) 2004 Anil S Keshavamurthy ( anil . s . keshavamurthy @ intel . com )
* Copyright ( C ) 2004 Keiichiro Tokunaga ( tokunaga . keiich @ jp . fujitsu . com )
* Copyright ( C ) 2004 Motoyuki Ito ( motoyuki @ soft . fujitsu . com )
* Copyright ( C ) 2004 Intel Corp .
* Copyright ( C ) 2004 FUJITSU LIMITED
*
* ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
*
* 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 <linux/kernel.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/types.h>
# include <linux/acpi.h>
# include <acpi/acpi_bus.h>
# include <acpi/acpi_drivers.h>
# include <acpi/container.h>
# define ACPI_CONTAINER_DEVICE_NAME "ACPI container device"
# define ACPI_CONTAINER_CLASS "container"
# define INSTALL_NOTIFY_HANDLER 1
# define UNINSTALL_NOTIFY_HANDLER 2
# define _COMPONENT ACPI_CONTAINER_COMPONENT
2007-02-13 06:42:12 +03:00
ACPI_MODULE_NAME ( " container " ) ;
2005-04-17 02:20:36 +04:00
2007-02-13 06:42:12 +03:00
MODULE_AUTHOR ( " Anil S Keshavamurthy " ) ;
2007-02-13 07:50:02 +03:00
MODULE_DESCRIPTION ( " ACPI container driver " ) ;
2005-04-17 02:20:36 +04:00
MODULE_LICENSE ( " GPL " ) ;
static int acpi_container_add ( struct acpi_device * device ) ;
static int acpi_container_remove ( struct acpi_device * device , int type ) ;
2007-07-23 16:44:41 +04:00
static const struct acpi_device_id container_device_ids [ ] = {
{ " ACPI0004 " , 0 } ,
{ " PNP0A05 " , 0 } ,
{ " PNP0A06 " , 0 } ,
{ " " , 0 } ,
} ;
MODULE_DEVICE_TABLE ( acpi , container_device_ids ) ;
2005-04-17 02:20:36 +04:00
static struct acpi_driver acpi_container_driver = {
2007-02-13 07:33:40 +03:00
. name = " container " ,
2005-08-05 08:44:28 +04:00
. class = ACPI_CONTAINER_CLASS ,
2007-07-23 16:44:41 +04:00
. ids = container_device_ids ,
2005-08-05 08:44:28 +04:00
. ops = {
. add = acpi_container_add ,
. remove = acpi_container_remove ,
} ,
2005-04-17 02:20:36 +04:00
} ;
/*******************************************************************/
2005-08-05 08:44:28 +04:00
static int is_device_present ( acpi_handle handle )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
acpi_handle temp ;
acpi_status status ;
2008-10-10 10:22:59 +04:00
unsigned long long sta ;
2005-04-17 02:20:36 +04:00
status = acpi_get_handle ( handle , " _STA " , & temp ) ;
if ( ACPI_FAILURE ( status ) )
2007-04-25 22:17:39 +04:00
return 1 ; /* _STA not found, assume device present */
2005-04-17 02:20:36 +04:00
status = acpi_evaluate_integer ( handle , " _STA " , NULL , & sta ) ;
if ( ACPI_FAILURE ( status ) )
2006-06-27 08:41:40 +04:00
return 0 ; /* Firmware error */
2005-04-17 02:20:36 +04:00
2007-04-25 22:17:39 +04:00
return ( ( sta & ACPI_STA_DEVICE_PRESENT ) = = ACPI_STA_DEVICE_PRESENT ) ;
2005-04-17 02:20:36 +04:00
}
/*******************************************************************/
2005-08-05 08:44:28 +04:00
static int acpi_container_add ( struct acpi_device * device )
2005-04-17 02:20:36 +04:00
{
struct acpi_container * container ;
if ( ! device ) {
2006-06-27 07:41:38 +04:00
printk ( KERN_ERR PREFIX " device is NULL \n " ) ;
2006-06-27 08:41:40 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
}
2006-12-19 23:56:11 +03:00
container = kzalloc ( sizeof ( struct acpi_container ) , GFP_KERNEL ) ;
2005-08-05 08:44:28 +04:00
if ( ! container )
2006-06-27 08:41:40 +04:00
return - ENOMEM ;
2005-08-05 08:44:28 +04:00
2005-04-17 02:20:36 +04:00
container - > handle = device - > handle ;
strcpy ( acpi_device_name ( device ) , ACPI_CONTAINER_DEVICE_NAME ) ;
strcpy ( acpi_device_class ( device ) , ACPI_CONTAINER_CLASS ) ;
2008-09-23 01:37:34 +04:00
device - > driver_data = container ;
2005-04-17 02:20:36 +04:00
2005-08-05 08:44:28 +04:00
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO , " Device <%s> bid <%s> \n " ,
acpi_device_name ( device ) , acpi_device_bid ( device ) ) ) ;
2005-04-17 02:20:36 +04:00
2006-06-27 08:41:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 08:44:28 +04:00
static int acpi_container_remove ( struct acpi_device * device , int type )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
acpi_status status = AE_OK ;
struct acpi_container * pc = NULL ;
2005-04-17 02:20:36 +04:00
2006-10-01 02:28:50 +04:00
pc = acpi_driver_data ( device ) ;
2005-11-07 12:01:32 +03:00
kfree ( pc ) ;
2005-04-17 02:20:36 +04:00
return status ;
}
2005-08-05 08:44:28 +04:00
static int container_device_add ( struct acpi_device * * device , acpi_handle handle )
2005-04-17 02:20:36 +04:00
{
acpi_handle phandle ;
struct acpi_device * pdev ;
int result ;
if ( acpi_get_parent ( handle , & phandle ) ) {
2006-06-27 08:41:40 +04:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
}
if ( acpi_bus_get_device ( phandle , & pdev ) ) {
2006-06-27 08:41:40 +04:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
}
if ( acpi_bus_add ( device , pdev , handle , ACPI_BUS_TYPE_DEVICE ) ) {
2006-06-27 08:41:40 +04:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
}
2005-04-28 11:25:52 +04:00
result = acpi_bus_start ( * device ) ;
2005-04-17 02:20:36 +04:00
2006-06-27 08:41:40 +04:00
return result ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 08:44:28 +04:00
static void container_notify_cb ( acpi_handle handle , u32 type , void * context )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
struct acpi_device * device = NULL ;
2005-04-17 02:20:36 +04:00
int result ;
int present ;
acpi_status status ;
present = is_device_present ( handle ) ;
2005-08-05 08:44:28 +04:00
2005-04-17 02:20:36 +04:00
switch ( type ) {
case ACPI_NOTIFY_BUS_CHECK :
/* Fall through */
case ACPI_NOTIFY_DEVICE_CHECK :
2009-02-04 19:03:07 +03:00
printk ( KERN_WARNING " Container driver received %s event \n " ,
2005-08-05 08:44:28 +04:00
( type = = ACPI_NOTIFY_BUS_CHECK ) ?
" ACPI_NOTIFY_BUS_CHECK " : " ACPI_NOTIFY_DEVICE_CHECK " ) ;
2005-04-17 02:20:36 +04:00
status = acpi_bus_get_device ( handle , & device ) ;
if ( present ) {
if ( ACPI_FAILURE ( status ) | | ! device ) {
result = container_device_add ( & device , handle ) ;
if ( ! result )
2006-12-07 15:56:38 +03:00
kobject_uevent ( & device - > dev . kobj ,
2005-11-16 11:00:00 +03:00
KOBJ_ONLINE ) ;
2005-04-17 02:20:36 +04:00
else
2009-02-04 19:03:07 +03:00
printk ( KERN_WARNING
" Failed to add container \n " ) ;
2005-04-17 02:20:36 +04:00
}
} else {
if ( ACPI_SUCCESS ( status ) ) {
/* device exist and this is a remove request */
2006-12-07 15:56:38 +03:00
kobject_uevent ( & device - > dev . kobj , KOBJ_OFFLINE ) ;
2005-04-17 02:20:36 +04:00
}
}
break ;
case ACPI_NOTIFY_EJECT_REQUEST :
if ( ! acpi_bus_get_device ( handle , & device ) & & device ) {
2006-12-07 15:56:38 +03:00
kobject_uevent ( & device - > dev . kobj , KOBJ_OFFLINE ) ;
2005-04-17 02:20:36 +04:00
}
break ;
default :
break ;
}
2006-06-27 08:41:40 +04:00
return ;
2005-04-17 02:20:36 +04:00
}
static acpi_status
container_walk_namespace_cb ( acpi_handle handle ,
2005-08-05 08:44:28 +04:00
u32 lvl , void * context , void * * rv )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
char * hid = NULL ;
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER , NULL } ;
struct acpi_device_info * info ;
acpi_status status ;
int * action = context ;
2005-04-17 02:20:36 +04:00
status = acpi_get_object_info ( handle , & buffer ) ;
if ( ACPI_FAILURE ( status ) | | ! buffer . pointer ) {
2006-06-27 08:41:40 +04:00
return AE_OK ;
2005-04-17 02:20:36 +04:00
}
info = buffer . pointer ;
if ( info - > valid & ACPI_VALID_HID )
hid = info - > hardware_id . value ;
if ( hid = = NULL ) {
goto end ;
}
if ( strcmp ( hid , " ACPI0004 " ) & & strcmp ( hid , " PNP0A05 " ) & &
2005-08-05 08:44:28 +04:00
strcmp ( hid , " PNP0A06 " ) ) {
2005-04-17 02:20:36 +04:00
goto end ;
}
2005-08-05 08:44:28 +04:00
switch ( * action ) {
2005-04-17 02:20:36 +04:00
case INSTALL_NOTIFY_HANDLER :
acpi_install_notify_handler ( handle ,
2005-08-05 08:44:28 +04:00
ACPI_SYSTEM_NOTIFY ,
container_notify_cb , NULL ) ;
2005-04-17 02:20:36 +04:00
break ;
case UNINSTALL_NOTIFY_HANDLER :
acpi_remove_notify_handler ( handle ,
2005-08-05 08:44:28 +04:00
ACPI_SYSTEM_NOTIFY ,
container_notify_cb ) ;
2005-04-17 02:20:36 +04:00
break ;
default :
break ;
}
2005-08-05 08:44:28 +04:00
end :
2006-06-30 11:19:10 +04:00
kfree ( buffer . pointer ) ;
2005-04-17 02:20:36 +04:00
2006-06-27 08:41:40 +04:00
return AE_OK ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 08:44:28 +04:00
static int __init acpi_container_init ( void )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
int result = 0 ;
int action = INSTALL_NOTIFY_HANDLER ;
2005-04-17 02:20:36 +04:00
result = acpi_bus_register_driver ( & acpi_container_driver ) ;
if ( result < 0 ) {
2005-08-05 08:44:28 +04:00
return ( result ) ;
2005-04-17 02:20:36 +04:00
}
/* register notify handler to every container device */
acpi_walk_namespace ( ACPI_TYPE_DEVICE ,
2005-08-05 08:44:28 +04:00
ACPI_ROOT_OBJECT ,
ACPI_UINT32_MAX ,
container_walk_namespace_cb , & action , NULL ) ;
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
}
2005-08-05 08:44:28 +04:00
static void __exit acpi_container_exit ( void )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
int action = UNINSTALL_NOTIFY_HANDLER ;
2005-04-17 02:20:36 +04:00
acpi_walk_namespace ( ACPI_TYPE_DEVICE ,
2005-08-05 08:44:28 +04:00
ACPI_ROOT_OBJECT ,
ACPI_UINT32_MAX ,
container_walk_namespace_cb , & action , NULL ) ;
2005-04-17 02:20:36 +04:00
acpi_bus_unregister_driver ( & acpi_container_driver ) ;
2006-06-27 08:41:40 +04:00
return ;
2005-04-17 02:20:36 +04:00
}
module_init ( acpi_container_init ) ;
module_exit ( acpi_container_exit ) ;