2005-04-17 02:20:36 +04:00
/*
* acpi_ac . c - ACPI AC Adapter Driver ( $ Revision : 27 $ )
*
* Copyright ( C ) 2001 , 2002 Andy Grover < andrew . grover @ intel . com >
* Copyright ( C ) 2001 , 2002 Paul Diefenbaugh < paul . s . diefenbaugh @ intel . com >
*
* ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
*
* 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>
2007-11-19 16:33:45 +03:00
# ifdef CONFIG_ACPI_PROCFS_POWER
2005-04-17 02:20:36 +04:00
# include <linux/proc_fs.h>
# include <linux/seq_file.h>
2007-11-15 04:00:37 +03:00
# endif
2008-01-01 22:27:24 +03:00
# ifdef CONFIG_ACPI_SYSFS_POWER
2007-09-26 19:44:06 +04:00
# include <linux/power_supply.h>
2008-01-01 22:27:24 +03:00
# endif
2005-04-17 02:20:36 +04:00
# include <acpi/acpi_bus.h>
# include <acpi/acpi_drivers.h>
# define ACPI_AC_COMPONENT 0x00020000
# define ACPI_AC_CLASS "ac_adapter"
# define ACPI_AC_DEVICE_NAME "AC Adapter"
# define ACPI_AC_FILE_STATE "state"
# define ACPI_AC_NOTIFY_STATUS 0x80
# define ACPI_AC_STATUS_OFFLINE 0x00
# define ACPI_AC_STATUS_ONLINE 0x01
# define ACPI_AC_STATUS_UNKNOWN 0xFF
# define _COMPONENT ACPI_AC_COMPONENT
2007-02-13 06:42:12 +03:00
ACPI_MODULE_NAME ( " ac " ) ;
2005-04-17 02:20:36 +04:00
2007-02-13 06:42:12 +03:00
MODULE_AUTHOR ( " Paul Diefenbaugh " ) ;
2007-02-13 07:50:02 +03:00
MODULE_DESCRIPTION ( " ACPI AC Adapter Driver " ) ;
2005-04-17 02:20:36 +04:00
MODULE_LICENSE ( " GPL " ) ;
2007-11-19 16:33:45 +03:00
# ifdef CONFIG_ACPI_PROCFS_POWER
2006-07-01 19:36:54 +04:00
extern struct proc_dir_entry * acpi_lock_ac_dir ( void ) ;
extern void * acpi_unlock_ac_dir ( struct proc_dir_entry * acpi_ac_dir ) ;
2007-11-15 04:00:37 +03:00
static int acpi_ac_open_fs ( struct inode * inode , struct file * file ) ;
# endif
2006-07-01 19:36:54 +04:00
2005-08-05 08:44:28 +04:00
static int acpi_ac_add ( struct acpi_device * device ) ;
static int acpi_ac_remove ( struct acpi_device * device , int type ) ;
2007-11-15 04:00:39 +03:00
static int acpi_ac_resume ( struct acpi_device * device ) ;
2005-04-17 02:20:36 +04:00
2008-04-22 02:24:53 +04:00
static const struct acpi_device_id ac_device_ids [ ] = {
2007-07-23 16:44:41 +04:00
{ " ACPI0003 " , 0 } ,
{ " " , 0 } ,
} ;
MODULE_DEVICE_TABLE ( acpi , ac_device_ids ) ;
2005-04-17 02:20:36 +04:00
static struct acpi_driver acpi_ac_driver = {
2007-02-13 07:33:40 +03:00
. name = " ac " ,
2005-08-05 08:44:28 +04:00
. class = ACPI_AC_CLASS ,
2007-07-23 16:44:41 +04:00
. ids = ac_device_ids ,
2005-08-05 08:44:28 +04:00
. ops = {
. add = acpi_ac_add ,
. remove = acpi_ac_remove ,
2007-11-15 04:00:39 +03:00
. resume = acpi_ac_resume ,
2005-08-05 08:44:28 +04:00
} ,
2005-04-17 02:20:36 +04:00
} ;
struct acpi_ac {
2008-01-01 22:27:24 +03:00
# ifdef CONFIG_ACPI_SYSFS_POWER
2007-09-26 19:44:06 +04:00
struct power_supply charger ;
2008-01-01 22:27:24 +03:00
# endif
2006-05-20 00:54:32 +04:00
struct acpi_device * device ;
2005-08-05 08:44:28 +04:00
unsigned long state ;
2005-04-17 02:20:36 +04:00
} ;
2007-09-26 19:44:06 +04:00
# define to_acpi_ac(x) container_of(x, struct acpi_ac, charger);
2007-11-19 16:33:45 +03:00
# ifdef CONFIG_ACPI_PROCFS_POWER
2006-07-04 21:06:00 +04:00
static const struct file_operations acpi_ac_fops = {
2008-04-29 12:02:27 +04:00
. owner = THIS_MODULE ,
2005-08-05 08:44:28 +04:00
. open = acpi_ac_open_fs ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
2005-04-17 02:20:36 +04:00
} ;
2007-11-15 04:00:37 +03:00
# endif
2008-01-01 22:27:24 +03:00
# ifdef CONFIG_ACPI_SYSFS_POWER
2007-09-26 19:44:06 +04:00
static int get_ac_property ( struct power_supply * psy ,
enum power_supply_property psp ,
union power_supply_propval * val )
{
struct acpi_ac * ac = to_acpi_ac ( psy ) ;
switch ( psp ) {
case POWER_SUPPLY_PROP_ONLINE :
val - > intval = ac - > state ;
break ;
default :
return - EINVAL ;
}
return 0 ;
}
static enum power_supply_property ac_props [ ] = {
POWER_SUPPLY_PROP_ONLINE ,
} ;
2008-01-01 22:27:24 +03:00
# endif
2005-04-17 02:20:36 +04:00
/* --------------------------------------------------------------------------
AC Adapter Management
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2005-08-05 08:44:28 +04:00
static int acpi_ac_get_state ( struct acpi_ac * ac )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
acpi_status status = AE_OK ;
2005-04-17 02:20:36 +04:00
if ( ! ac )
2006-06-27 08:41:40 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
2006-05-20 00:54:41 +04:00
status = acpi_evaluate_integer ( ac - > device - > handle , " _PSR " , NULL , & ac - > state ) ;
2005-04-17 02:20:36 +04:00
if ( ACPI_FAILURE ( status ) ) {
2006-06-27 07:58:43 +04:00
ACPI_EXCEPTION ( ( AE_INFO , status , " Error reading AC Adapter state " ) ) ;
2005-04-17 02:20:36 +04:00
ac - > state = ACPI_AC_STATUS_UNKNOWN ;
2006-06-27 08:41:40 +04:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 08:44:28 +04:00
2006-06-27 08:41:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2007-11-19 16:33:45 +03:00
# ifdef CONFIG_ACPI_PROCFS_POWER
2005-04-17 02:20:36 +04:00
/* --------------------------------------------------------------------------
FS Interface ( / proc )
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2005-08-05 08:44:28 +04:00
static struct proc_dir_entry * acpi_ac_dir ;
2005-04-17 02:20:36 +04:00
static int acpi_ac_seq_show ( struct seq_file * seq , void * offset )
{
2006-10-01 02:28:50 +04:00
struct acpi_ac * ac = seq - > private ;
2005-04-17 02:20:36 +04:00
if ( ! ac )
2006-06-27 08:41:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
if ( acpi_ac_get_state ( ac ) ) {
seq_puts ( seq , " ERROR: Unable to read AC Adapter state \n " ) ;
2006-06-27 08:41:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
seq_puts ( seq , " state: " ) ;
switch ( ac - > state ) {
case ACPI_AC_STATUS_OFFLINE :
seq_puts ( seq , " off-line \n " ) ;
break ;
case ACPI_AC_STATUS_ONLINE :
seq_puts ( seq , " on-line \n " ) ;
break ;
default :
seq_puts ( seq , " unknown \n " ) ;
break ;
}
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
2005-04-17 02:20:36 +04:00
static int acpi_ac_open_fs ( struct inode * inode , struct file * file )
{
return single_open ( file , acpi_ac_seq_show , PDE ( inode ) - > data ) ;
}
2005-08-05 08:44:28 +04:00
static int acpi_ac_add_fs ( struct acpi_device * device )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
struct proc_dir_entry * entry = NULL ;
2005-04-17 02:20:36 +04:00
if ( ! acpi_device_dir ( device ) ) {
acpi_device_dir ( device ) = proc_mkdir ( acpi_device_bid ( device ) ,
2005-08-05 08:44:28 +04:00
acpi_ac_dir ) ;
2005-04-17 02:20:36 +04:00
if ( ! acpi_device_dir ( device ) )
2006-06-27 08:41:40 +04:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
acpi_device_dir ( device ) - > owner = THIS_MODULE ;
}
/* 'state' [R] */
2008-04-29 12:02:27 +04:00
entry = proc_create_data ( ACPI_AC_FILE_STATE ,
S_IRUGO , acpi_device_dir ( device ) ,
& acpi_ac_fops , acpi_driver_data ( device ) ) ;
2005-04-17 02:20:36 +04:00
if ( ! entry )
2006-06-27 08:41:40 +04:00
return - ENODEV ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 08:44:28 +04:00
static int acpi_ac_remove_fs ( struct acpi_device * device )
2005-04-17 02:20:36 +04:00
{
if ( acpi_device_dir ( device ) ) {
2005-08-05 08:44:28 +04:00
remove_proc_entry ( ACPI_AC_FILE_STATE , acpi_device_dir ( device ) ) ;
2005-04-17 02:20:36 +04:00
remove_proc_entry ( acpi_device_bid ( device ) , acpi_ac_dir ) ;
acpi_device_dir ( device ) = NULL ;
}
2006-06-27 08:41:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2007-11-15 04:00:37 +03:00
# endif
2005-04-17 02:20:36 +04:00
/* --------------------------------------------------------------------------
Driver Model
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2005-08-05 08:44:28 +04:00
static void acpi_ac_notify ( acpi_handle handle , u32 event , void * data )
2005-04-17 02:20:36 +04:00
{
2006-10-01 02:28:50 +04:00
struct acpi_ac * ac = data ;
2005-08-05 08:44:28 +04:00
struct acpi_device * device = NULL ;
2005-04-17 02:20:36 +04:00
if ( ! ac )
2006-06-27 08:41:40 +04:00
return ;
2005-04-17 02:20:36 +04:00
2006-05-20 00:54:32 +04:00
device = ac - > device ;
2005-04-17 02:20:36 +04:00
switch ( event ) {
2008-06-14 09:26:37 +04:00
default :
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO ,
" Unsupported event [0x%x] \n " , event ) ) ;
2005-04-17 02:20:36 +04:00
case ACPI_AC_NOTIFY_STATUS :
2004-08-19 09:26:00 +04:00
case ACPI_NOTIFY_BUS_CHECK :
case ACPI_NOTIFY_DEVICE_CHECK :
2005-04-17 02:20:36 +04:00
acpi_ac_get_state ( ac ) ;
2007-08-23 23:20:26 +04:00
acpi_bus_generate_proc_event ( device , event , ( u32 ) ac - > state ) ;
2007-08-22 21:24:31 +04:00
acpi_bus_generate_netlink_event ( device - > pnp . device_class ,
device - > dev . bus_id , event ,
( u32 ) ac - > state ) ;
2008-01-01 22:27:24 +03:00
# ifdef CONFIG_ACPI_SYSFS_POWER
2007-09-26 19:44:06 +04:00
kobject_uevent ( & ac - > charger . dev - > kobj , KOBJ_CHANGE ) ;
2008-01-01 22:27:24 +03:00
# endif
2005-04-17 02:20:36 +04:00
}
2006-06-27 08:41:40 +04:00
return ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 08:44:28 +04:00
static int acpi_ac_add ( struct acpi_device * device )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
int result = 0 ;
acpi_status status = AE_OK ;
struct acpi_ac * ac = NULL ;
2005-04-17 02:20:36 +04:00
if ( ! device )
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
ac = kzalloc ( sizeof ( struct acpi_ac ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! ac )
2006-06-27 08:41:40 +04:00
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
2006-05-20 00:54:32 +04:00
ac - > device = device ;
2005-04-17 02:20:36 +04:00
strcpy ( acpi_device_name ( device ) , ACPI_AC_DEVICE_NAME ) ;
strcpy ( acpi_device_class ( device ) , ACPI_AC_CLASS ) ;
acpi_driver_data ( device ) = ac ;
result = acpi_ac_get_state ( ac ) ;
if ( result )
goto end ;
2007-11-19 16:33:45 +03:00
# ifdef CONFIG_ACPI_PROCFS_POWER
2005-04-17 02:20:36 +04:00
result = acpi_ac_add_fs ( device ) ;
2007-11-15 04:00:37 +03:00
# endif
2005-04-17 02:20:36 +04:00
if ( result )
goto end ;
2008-01-01 22:27:24 +03:00
# ifdef CONFIG_ACPI_SYSFS_POWER
2007-09-26 19:44:06 +04:00
ac - > charger . name = acpi_device_bid ( device ) ;
ac - > charger . type = POWER_SUPPLY_TYPE_MAINS ;
ac - > charger . properties = ac_props ;
ac - > charger . num_properties = ARRAY_SIZE ( ac_props ) ;
ac - > charger . get_property = get_ac_property ;
power_supply_register ( & ac - > device - > dev , & ac - > charger ) ;
2008-01-01 22:27:24 +03:00
# endif
2006-05-20 00:54:41 +04:00
status = acpi_install_notify_handler ( device - > handle ,
2004-08-19 09:26:00 +04:00
ACPI_ALL_NOTIFY , acpi_ac_notify ,
2005-08-05 08:44:28 +04:00
ac ) ;
2005-04-17 02:20:36 +04:00
if ( ACPI_FAILURE ( status ) ) {
result = - ENODEV ;
goto end ;
}
2005-08-05 08:44:28 +04:00
printk ( KERN_INFO PREFIX " %s [%s] (%s) \n " ,
acpi_device_name ( device ) , acpi_device_bid ( device ) ,
ac - > state ? " on-line " : " off-line " ) ;
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 ) {
2007-11-19 16:33:45 +03:00
# ifdef CONFIG_ACPI_PROCFS_POWER
2005-04-17 02:20:36 +04:00
acpi_ac_remove_fs ( device ) ;
2007-11-15 04:00:37 +03:00
# endif
2005-04-17 02:20:36 +04:00
kfree ( ac ) ;
}
2006-06-27 08:41:40 +04:00
return result ;
2005-04-17 02:20:36 +04:00
}
2007-11-15 04:00:39 +03:00
static int acpi_ac_resume ( struct acpi_device * device )
{
struct acpi_ac * ac ;
unsigned old_state ;
if ( ! device | | ! acpi_driver_data ( device ) )
return - EINVAL ;
ac = acpi_driver_data ( device ) ;
old_state = ac - > state ;
if ( acpi_ac_get_state ( ac ) )
return 0 ;
2008-01-01 22:27:24 +03:00
# ifdef CONFIG_ACPI_SYSFS_POWER
2007-11-15 04:00:39 +03:00
if ( old_state ! = ac - > state )
kobject_uevent ( & ac - > charger . dev - > kobj , KOBJ_CHANGE ) ;
2008-01-01 22:27:24 +03:00
# endif
2007-11-15 04:00:39 +03:00
return 0 ;
}
2005-08-05 08:44:28 +04:00
static int acpi_ac_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_ac * ac = NULL ;
2005-04-17 02:20:36 +04:00
if ( ! device | | ! acpi_driver_data ( device ) )
2006-06-27 08:41:40 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
2006-10-01 02:28:50 +04:00
ac = acpi_driver_data ( device ) ;
2005-04-17 02:20:36 +04:00
2006-05-20 00:54:41 +04:00
status = acpi_remove_notify_handler ( device - > handle ,
2004-08-19 09:26:00 +04:00
ACPI_ALL_NOTIFY , acpi_ac_notify ) ;
2008-01-01 22:27:24 +03:00
# ifdef CONFIG_ACPI_SYSFS_POWER
2007-09-26 19:44:06 +04:00
if ( ac - > charger . dev )
power_supply_unregister ( & ac - > charger ) ;
2008-01-01 22:27:24 +03:00
# endif
2007-11-19 16:33:45 +03:00
# ifdef CONFIG_ACPI_PROCFS_POWER
2005-04-17 02:20:36 +04:00
acpi_ac_remove_fs ( device ) ;
2007-11-15 04:00:37 +03:00
# endif
2005-04-17 02:20:36 +04:00
kfree ( ac ) ;
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 __init acpi_ac_init ( void )
2005-04-17 02:20:36 +04:00
{
2006-07-01 19:36:54 +04:00
int result ;
2005-04-17 02:20:36 +04:00
2006-08-15 09:37:22 +04:00
if ( acpi_disabled )
return - ENODEV ;
2005-04-17 02:20:36 +04:00
2007-11-19 16:33:45 +03:00
# ifdef CONFIG_ACPI_PROCFS_POWER
2006-07-01 19:36:54 +04:00
acpi_ac_dir = acpi_lock_ac_dir ( ) ;
2005-04-17 02:20:36 +04:00
if ( ! acpi_ac_dir )
2006-06-27 08:41:40 +04:00
return - ENODEV ;
2007-11-15 04:00:37 +03:00
# endif
2005-04-17 02:20:36 +04:00
result = acpi_bus_register_driver ( & acpi_ac_driver ) ;
if ( result < 0 ) {
2007-11-19 16:33:45 +03:00
# ifdef CONFIG_ACPI_PROCFS_POWER
2006-07-01 19:36:54 +04:00
acpi_unlock_ac_dir ( acpi_ac_dir ) ;
2007-11-15 04:00:37 +03:00
# endif
2006-06-27 08:41:40 +04:00
return - ENODEV ;
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 void __exit acpi_ac_exit ( void )
2005-04-17 02:20:36 +04:00
{
acpi_bus_unregister_driver ( & acpi_ac_driver ) ;
2007-11-19 16:33:45 +03:00
# ifdef CONFIG_ACPI_PROCFS_POWER
2006-07-01 19:36:54 +04:00
acpi_unlock_ac_dir ( acpi_ac_dir ) ;
2007-11-15 04:00:37 +03:00
# endif
2005-04-17 02:20:36 +04:00
2006-06-27 08:41:40 +04:00
return ;
2005-04-17 02:20:36 +04:00
}
module_init ( acpi_ac_init ) ;
module_exit ( acpi_ac_exit ) ;