2010-03-21 05:26:34 +03:00
/*
* Eee PC WMI hotkey driver
*
* Copyright ( C ) 2010 Intel Corporation .
*
* Portions based on wistron_btns . c :
* Copyright ( C ) 2005 Miloslav Trmac < mitr @ volny . cz >
* Copyright ( C ) 2005 Bernhard Rosenkraenzer < bero @ arklinux . org >
* Copyright ( C ) 2005 Dmitry Torokhov < dtor @ mail . ru >
*
* 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
*/
2010-04-11 05:26:33 +04:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2010-03-21 05:26:34 +03:00
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/types.h>
2010-04-05 06:37:59 +04:00
# include <linux/slab.h>
2010-03-21 05:26:34 +03:00
# include <linux/input.h>
# include <linux/input/sparse-keymap.h>
2010-04-11 05:27:54 +04:00
# include <linux/fb.h>
# include <linux/backlight.h>
2010-11-29 10:14:06 +03:00
# include <linux/leds.h>
2010-11-29 10:14:07 +03:00
# include <linux/rfkill.h>
2010-04-11 05:27:19 +04:00
# include <linux/platform_device.h>
2010-03-21 05:26:34 +03:00
# include <acpi/acpi_bus.h>
# include <acpi/acpi_drivers.h>
2010-04-11 05:27:19 +04:00
# define EEEPC_WMI_FILE "eeepc-wmi"
2010-03-21 05:26:34 +03:00
MODULE_AUTHOR ( " Yong Wang <yong.y.wang@intel.com> " ) ;
MODULE_DESCRIPTION ( " Eee PC WMI Hotkey Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
# define EEEPC_WMI_EVENT_GUID "ABBC0F72-8EA1-11D1-00A0-C90629100000"
2010-04-11 05:27:54 +04:00
# define EEEPC_WMI_MGMT_GUID "97845ED0-4E6D-11DE-8A39-0800200C9A66"
2010-03-21 05:26:34 +03:00
MODULE_ALIAS ( " wmi: " EEEPC_WMI_EVENT_GUID ) ;
2010-04-11 05:27:54 +04:00
MODULE_ALIAS ( " wmi: " EEEPC_WMI_MGMT_GUID ) ;
2010-03-21 05:26:34 +03:00
# define NOTIFY_BRNUP_MIN 0x11
# define NOTIFY_BRNUP_MAX 0x1f
# define NOTIFY_BRNDOWN_MIN 0x20
# define NOTIFY_BRNDOWN_MAX 0x2e
2010-04-11 05:27:54 +04:00
# define EEEPC_WMI_METHODID_DEVS 0x53564544
# define EEEPC_WMI_METHODID_DSTS 0x53544344
2010-10-12 03:47:18 +04:00
# define EEEPC_WMI_METHODID_CFVS 0x53564643
2010-04-11 05:27:54 +04:00
# define EEEPC_WMI_DEVID_BACKLIGHT 0x00050012
2010-11-29 10:14:06 +03:00
# define EEEPC_WMI_DEVID_TPDLED 0x00100011
2010-11-29 10:14:07 +03:00
# define EEEPC_WMI_DEVID_WLAN 0x00010011
# define EEEPC_WMI_DEVID_BLUETOOTH 0x00010013
# define EEEPC_WMI_DEVID_WWAN3G 0x00010019
2010-04-11 05:27:54 +04:00
2010-03-21 05:26:34 +03:00
static const struct key_entry eeepc_wmi_keymap [ ] = {
/* Sleep already handled via generic ACPI code */
{ KE_KEY , 0x5d , { KEY_WLAN } } ,
{ KE_KEY , 0x32 , { KEY_MUTE } } ,
{ KE_KEY , 0x31 , { KEY_VOLUMEDOWN } } ,
{ KE_KEY , 0x30 , { KEY_VOLUMEUP } } ,
{ KE_IGNORE , NOTIFY_BRNDOWN_MIN , { KEY_BRIGHTNESSDOWN } } ,
{ KE_IGNORE , NOTIFY_BRNUP_MIN , { KEY_BRIGHTNESSUP } } ,
{ KE_KEY , 0xcc , { KEY_SWITCHVIDEOMODE } } ,
2010-10-12 03:47:17 +04:00
{ KE_KEY , 0x6b , { KEY_F13 } } , /* Disable Touchpad */
{ KE_KEY , 0xe1 , { KEY_F14 } } ,
{ KE_KEY , 0xe9 , { KEY_DISPLAY_OFF } } ,
{ KE_KEY , 0xe0 , { KEY_PROG1 } } ,
{ KE_KEY , 0x5c , { KEY_F15 } } ,
2010-03-21 05:26:34 +03:00
{ KE_END , 0 } ,
} ;
2010-04-11 05:27:54 +04:00
struct bios_args {
u32 dev_id ;
u32 ctrl_param ;
} ;
2010-04-11 05:26:33 +04:00
struct eeepc_wmi {
struct input_dev * inputdev ;
2010-04-11 05:27:54 +04:00
struct backlight_device * backlight_device ;
2010-11-29 10:14:05 +03:00
struct platform_device * platform_device ;
2010-11-29 10:14:06 +03:00
struct led_classdev tpd_led ;
int tpd_led_wk ;
struct workqueue_struct * led_workqueue ;
struct work_struct tpd_led_work ;
2010-11-29 10:14:07 +03:00
struct rfkill * wlan_rfkill ;
struct rfkill * bluetooth_rfkill ;
struct rfkill * wwan3g_rfkill ;
2010-04-11 05:26:33 +04:00
} ;
2010-11-29 10:14:05 +03:00
/* Only used in eeepc_wmi_init() and eeepc_wmi_exit() */
2010-04-11 05:27:19 +04:00
static struct platform_device * platform_device ;
2010-03-21 05:26:34 +03:00
2010-04-11 05:26:33 +04:00
static int eeepc_wmi_input_init ( struct eeepc_wmi * eeepc )
2010-03-21 05:26:34 +03:00
{
int err ;
2010-04-11 05:26:33 +04:00
eeepc - > inputdev = input_allocate_device ( ) ;
if ( ! eeepc - > inputdev )
2010-03-21 05:26:34 +03:00
return - ENOMEM ;
2010-04-11 05:26:33 +04:00
eeepc - > inputdev - > name = " Eee PC WMI hotkeys " ;
2010-04-11 05:27:19 +04:00
eeepc - > inputdev - > phys = EEEPC_WMI_FILE " /input0 " ;
2010-04-11 05:26:33 +04:00
eeepc - > inputdev - > id . bustype = BUS_HOST ;
2010-11-29 10:14:05 +03:00
eeepc - > inputdev - > dev . parent = & eeepc - > platform_device - > dev ;
2010-03-21 05:26:34 +03:00
2010-04-11 05:26:33 +04:00
err = sparse_keymap_setup ( eeepc - > inputdev , eeepc_wmi_keymap , NULL ) ;
2010-03-21 05:26:34 +03:00
if ( err )
goto err_free_dev ;
2010-04-11 05:26:33 +04:00
err = input_register_device ( eeepc - > inputdev ) ;
2010-03-21 05:26:34 +03:00
if ( err )
goto err_free_keymap ;
return 0 ;
err_free_keymap :
2010-04-11 05:26:33 +04:00
sparse_keymap_free ( eeepc - > inputdev ) ;
2010-03-21 05:26:34 +03:00
err_free_dev :
2010-04-11 05:26:33 +04:00
input_free_device ( eeepc - > inputdev ) ;
2010-03-21 05:26:34 +03:00
return err ;
}
2010-04-11 05:26:33 +04:00
static void eeepc_wmi_input_exit ( struct eeepc_wmi * eeepc )
{
if ( eeepc - > inputdev ) {
sparse_keymap_free ( eeepc - > inputdev ) ;
input_unregister_device ( eeepc - > inputdev ) ;
}
eeepc - > inputdev = NULL ;
}
2010-04-11 05:27:54 +04:00
static acpi_status eeepc_wmi_get_devstate ( u32 dev_id , u32 * ctrl_param )
{
struct acpi_buffer input = { ( acpi_size ) sizeof ( u32 ) , & dev_id } ;
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER , NULL } ;
union acpi_object * obj ;
acpi_status status ;
u32 tmp ;
status = wmi_evaluate_method ( EEEPC_WMI_MGMT_GUID ,
1 , EEEPC_WMI_METHODID_DSTS , & input , & output ) ;
if ( ACPI_FAILURE ( status ) )
return status ;
obj = ( union acpi_object * ) output . pointer ;
if ( obj & & obj - > type = = ACPI_TYPE_INTEGER )
tmp = ( u32 ) obj - > integer . value ;
else
tmp = 0 ;
if ( ctrl_param )
* ctrl_param = tmp ;
kfree ( obj ) ;
return status ;
}
static acpi_status eeepc_wmi_set_devstate ( u32 dev_id , u32 ctrl_param )
{
struct bios_args args = {
. dev_id = dev_id ,
. ctrl_param = ctrl_param ,
} ;
struct acpi_buffer input = { ( acpi_size ) sizeof ( args ) , & args } ;
acpi_status status ;
status = wmi_evaluate_method ( EEEPC_WMI_MGMT_GUID ,
1 , EEEPC_WMI_METHODID_DEVS , & input , NULL ) ;
return status ;
}
2010-11-29 10:14:06 +03:00
/*
* LEDs
*/
/*
* These functions actually update the LED ' s , and are called from a
* workqueue . By doing this as separate work rather than when the LED
* subsystem asks , we avoid messing with the Eeepc ACPI stuff during a
* potentially bad time , such as a timer interrupt .
*/
static void tpd_led_update ( struct work_struct * work )
{
int ctrl_param ;
struct eeepc_wmi * eeepc ;
eeepc = container_of ( work , struct eeepc_wmi , tpd_led_work ) ;
ctrl_param = eeepc - > tpd_led_wk ;
eeepc_wmi_set_devstate ( EEEPC_WMI_DEVID_TPDLED , ctrl_param ) ;
}
static void tpd_led_set ( struct led_classdev * led_cdev ,
enum led_brightness value )
{
struct eeepc_wmi * eeepc ;
eeepc = container_of ( led_cdev , struct eeepc_wmi , tpd_led ) ;
eeepc - > tpd_led_wk = ! ! value ;
queue_work ( eeepc - > led_workqueue , & eeepc - > tpd_led_work ) ;
}
static int read_tpd_state ( struct eeepc_wmi * eeepc )
{
static u32 ctrl_param ;
acpi_status status ;
status = eeepc_wmi_get_devstate ( EEEPC_WMI_DEVID_TPDLED , & ctrl_param ) ;
if ( ACPI_FAILURE ( status ) )
return - 1 ;
else if ( ! ctrl_param | | ctrl_param = = 0x00060000 )
/*
* if touchpad led is present , DSTS will set some bits ,
* usually 0x00020000 .
* 0x00060000 means that the device is not supported
*/
return - ENODEV ;
else
/* Status is stored in the first bit */
return ctrl_param & 0x1 ;
}
static enum led_brightness tpd_led_get ( struct led_classdev * led_cdev )
{
struct eeepc_wmi * eeepc ;
eeepc = container_of ( led_cdev , struct eeepc_wmi , tpd_led ) ;
return read_tpd_state ( eeepc ) ;
}
static int eeepc_wmi_led_init ( struct eeepc_wmi * eeepc )
{
int rv ;
if ( read_tpd_state ( eeepc ) < 0 )
return 0 ;
eeepc - > led_workqueue = create_singlethread_workqueue ( " led_workqueue " ) ;
if ( ! eeepc - > led_workqueue )
return - ENOMEM ;
INIT_WORK ( & eeepc - > tpd_led_work , tpd_led_update ) ;
eeepc - > tpd_led . name = " eeepc::touchpad " ;
eeepc - > tpd_led . brightness_set = tpd_led_set ;
eeepc - > tpd_led . brightness_get = tpd_led_get ;
eeepc - > tpd_led . max_brightness = 1 ;
rv = led_classdev_register ( & eeepc - > platform_device - > dev ,
& eeepc - > tpd_led ) ;
if ( rv ) {
destroy_workqueue ( eeepc - > led_workqueue ) ;
return rv ;
}
return 0 ;
}
static void eeepc_wmi_led_exit ( struct eeepc_wmi * eeepc )
{
if ( eeepc - > tpd_led . dev )
led_classdev_unregister ( & eeepc - > tpd_led ) ;
if ( eeepc - > led_workqueue )
destroy_workqueue ( eeepc - > led_workqueue ) ;
}
2010-11-29 10:14:07 +03:00
/*
* Rfkill devices
*/
static int eeepc_rfkill_set ( void * data , bool blocked )
{
int dev_id = ( unsigned long ) data ;
u32 ctrl_param = ! blocked ;
return eeepc_wmi_set_devstate ( dev_id , ctrl_param ) ;
}
static void eeepc_rfkill_query ( struct rfkill * rfkill , void * data )
{
int dev_id = ( unsigned long ) data ;
u32 ctrl_param ;
acpi_status status ;
status = eeepc_wmi_get_devstate ( dev_id , & ctrl_param ) ;
if ( ACPI_FAILURE ( status ) )
return ;
rfkill_set_sw_state ( rfkill , ! ( ctrl_param & 0x1 ) ) ;
}
static const struct rfkill_ops eeepc_rfkill_ops = {
. set_block = eeepc_rfkill_set ,
. query = eeepc_rfkill_query ,
} ;
static int eeepc_new_rfkill ( struct eeepc_wmi * eeepc ,
struct rfkill * * rfkill ,
const char * name ,
enum rfkill_type type , int dev_id )
{
int result ;
u32 ctrl_param ;
acpi_status status ;
status = eeepc_wmi_get_devstate ( dev_id , & ctrl_param ) ;
if ( ACPI_FAILURE ( status ) )
return - 1 ;
/* If the device is present, DSTS will always set some bits
* 0x00070000 - 1110000000000000000 - device supported
* 0x00060000 - 1100000000000000000 - not supported
* 0x00020000 - 0100000000000000000 - device supported
* 0x00010000 - 0010000000000000000 - not supported / special mode ?
*/
if ( ! ctrl_param | | ctrl_param = = 0x00060000 )
return - ENODEV ;
* rfkill = rfkill_alloc ( name , & eeepc - > platform_device - > dev , type ,
& eeepc_rfkill_ops , ( void * ) ( long ) dev_id ) ;
if ( ! * rfkill )
return - EINVAL ;
rfkill_init_sw_state ( * rfkill , ! ( ctrl_param & 0x1 ) ) ;
result = rfkill_register ( * rfkill ) ;
if ( result ) {
rfkill_destroy ( * rfkill ) ;
* rfkill = NULL ;
return result ;
}
return 0 ;
}
static void eeepc_wmi_rfkill_exit ( struct eeepc_wmi * eeepc )
{
if ( eeepc - > wlan_rfkill ) {
rfkill_unregister ( eeepc - > wlan_rfkill ) ;
rfkill_destroy ( eeepc - > wlan_rfkill ) ;
eeepc - > wlan_rfkill = NULL ;
}
if ( eeepc - > bluetooth_rfkill ) {
rfkill_unregister ( eeepc - > bluetooth_rfkill ) ;
rfkill_destroy ( eeepc - > bluetooth_rfkill ) ;
eeepc - > bluetooth_rfkill = NULL ;
}
if ( eeepc - > wwan3g_rfkill ) {
rfkill_unregister ( eeepc - > wwan3g_rfkill ) ;
rfkill_destroy ( eeepc - > wwan3g_rfkill ) ;
eeepc - > wwan3g_rfkill = NULL ;
}
}
static int eeepc_wmi_rfkill_init ( struct eeepc_wmi * eeepc )
{
int result = 0 ;
result = eeepc_new_rfkill ( eeepc , & eeepc - > wlan_rfkill ,
" eeepc-wlan " , RFKILL_TYPE_WLAN ,
EEEPC_WMI_DEVID_WLAN ) ;
if ( result & & result ! = - ENODEV )
goto exit ;
result = eeepc_new_rfkill ( eeepc , & eeepc - > bluetooth_rfkill ,
" eeepc-bluetooth " , RFKILL_TYPE_BLUETOOTH ,
EEEPC_WMI_DEVID_BLUETOOTH ) ;
if ( result & & result ! = - ENODEV )
goto exit ;
result = eeepc_new_rfkill ( eeepc , & eeepc - > wwan3g_rfkill ,
" eeepc-wwan3g " , RFKILL_TYPE_WWAN ,
EEEPC_WMI_DEVID_WWAN3G ) ;
if ( result & & result ! = - ENODEV )
goto exit ;
exit :
if ( result & & result ! = - ENODEV )
eeepc_wmi_rfkill_exit ( eeepc ) ;
if ( result = = - ENODEV )
result = 0 ;
return result ;
}
2010-11-29 10:14:06 +03:00
/*
* Backlight
*/
2010-04-11 05:27:54 +04:00
static int read_brightness ( struct backlight_device * bd )
{
static u32 ctrl_param ;
acpi_status status ;
status = eeepc_wmi_get_devstate ( EEEPC_WMI_DEVID_BACKLIGHT , & ctrl_param ) ;
if ( ACPI_FAILURE ( status ) )
return - 1 ;
else
return ctrl_param & 0xFF ;
}
static int update_bl_status ( struct backlight_device * bd )
{
static u32 ctrl_param ;
acpi_status status ;
ctrl_param = bd - > props . brightness ;
status = eeepc_wmi_set_devstate ( EEEPC_WMI_DEVID_BACKLIGHT , ctrl_param ) ;
if ( ACPI_FAILURE ( status ) )
return - 1 ;
else
return 0 ;
}
static const struct backlight_ops eeepc_wmi_bl_ops = {
. get_brightness = read_brightness ,
. update_status = update_bl_status ,
} ;
static int eeepc_wmi_backlight_notify ( struct eeepc_wmi * eeepc , int code )
{
struct backlight_device * bd = eeepc - > backlight_device ;
int old = bd - > props . brightness ;
2010-05-19 14:37:01 +04:00
int new = old ;
2010-04-11 05:27:54 +04:00
if ( code > = NOTIFY_BRNUP_MIN & & code < = NOTIFY_BRNUP_MAX )
new = code - NOTIFY_BRNUP_MIN + 1 ;
else if ( code > = NOTIFY_BRNDOWN_MIN & & code < = NOTIFY_BRNDOWN_MAX )
new = code - NOTIFY_BRNDOWN_MIN ;
bd - > props . brightness = new ;
backlight_update_status ( bd ) ;
backlight_force_update ( bd , BACKLIGHT_UPDATE_HOTKEY ) ;
return old ;
}
static int eeepc_wmi_backlight_init ( struct eeepc_wmi * eeepc )
{
struct backlight_device * bd ;
struct backlight_properties props ;
memset ( & props , 0 , sizeof ( struct backlight_properties ) ) ;
props . max_brightness = 15 ;
bd = backlight_device_register ( EEEPC_WMI_FILE ,
2010-11-29 10:14:05 +03:00
& eeepc - > platform_device - > dev , eeepc ,
2010-04-11 05:27:54 +04:00
& eeepc_wmi_bl_ops , & props ) ;
if ( IS_ERR ( bd ) ) {
pr_err ( " Could not register backlight device \n " ) ;
return PTR_ERR ( bd ) ;
}
eeepc - > backlight_device = bd ;
bd - > props . brightness = read_brightness ( bd ) ;
bd - > props . power = FB_BLANK_UNBLANK ;
backlight_update_status ( bd ) ;
return 0 ;
}
static void eeepc_wmi_backlight_exit ( struct eeepc_wmi * eeepc )
{
if ( eeepc - > backlight_device )
backlight_device_unregister ( eeepc - > backlight_device ) ;
eeepc - > backlight_device = NULL ;
}
static void eeepc_wmi_notify ( u32 value , void * context )
{
struct eeepc_wmi * eeepc = context ;
struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER , NULL } ;
union acpi_object * obj ;
acpi_status status ;
int code ;
int orig_code ;
status = wmi_get_event_data ( value , & response ) ;
if ( status ! = AE_OK ) {
pr_err ( " bad event status 0x%x \n " , status ) ;
return ;
}
obj = ( union acpi_object * ) response . pointer ;
if ( obj & & obj - > type = = ACPI_TYPE_INTEGER ) {
code = obj - > integer . value ;
orig_code = code ;
if ( code > = NOTIFY_BRNUP_MIN & & code < = NOTIFY_BRNUP_MAX )
code = NOTIFY_BRNUP_MIN ;
else if ( code > = NOTIFY_BRNDOWN_MIN & &
code < = NOTIFY_BRNDOWN_MAX )
code = NOTIFY_BRNDOWN_MIN ;
if ( code = = NOTIFY_BRNUP_MIN | | code = = NOTIFY_BRNDOWN_MIN ) {
if ( ! acpi_video_backlight_support ( ) )
eeepc_wmi_backlight_notify ( eeepc , orig_code ) ;
}
if ( ! sparse_keymap_report_event ( eeepc - > inputdev ,
code , 1 , true ) )
pr_info ( " Unknown key %x pressed \n " , code ) ;
}
kfree ( obj ) ;
}
2010-11-03 21:14:01 +03:00
static ssize_t store_cpufv ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
2010-10-12 03:47:18 +04:00
{
int value ;
struct acpi_buffer input = { ( acpi_size ) sizeof ( value ) , & value } ;
acpi_status status ;
if ( ! count | | sscanf ( buf , " %i " , & value ) ! = 1 )
return - EINVAL ;
if ( value < 0 | | value > 2 )
return - EINVAL ;
status = wmi_evaluate_method ( EEEPC_WMI_MGMT_GUID ,
1 , EEEPC_WMI_METHODID_CFVS , & input , NULL ) ;
if ( ACPI_FAILURE ( status ) )
return - EIO ;
else
return count ;
}
static DEVICE_ATTR ( cpufv , S_IRUGO | S_IWUSR , NULL , store_cpufv ) ;
static void eeepc_wmi_sysfs_exit ( struct platform_device * device )
{
device_remove_file ( & device - > dev , & dev_attr_cpufv ) ;
}
static int eeepc_wmi_sysfs_init ( struct platform_device * device )
{
int retval = - ENOMEM ;
retval = device_create_file ( & device - > dev , & dev_attr_cpufv ) ;
if ( retval )
goto error_sysfs ;
return 0 ;
error_sysfs :
2010-11-29 10:14:05 +03:00
eeepc_wmi_sysfs_exit ( device ) ;
2010-10-12 03:47:18 +04:00
return retval ;
}
2010-11-29 10:14:05 +03:00
/*
* Platform device
*/
static int __init eeepc_wmi_platform_init ( struct eeepc_wmi * eeepc )
2010-03-21 05:26:34 +03:00
{
int err ;
2010-11-29 10:14:05 +03:00
eeepc - > platform_device = platform_device_alloc ( EEEPC_WMI_FILE , - 1 ) ;
if ( ! eeepc - > platform_device )
return - ENOMEM ;
platform_set_drvdata ( eeepc - > platform_device , eeepc ) ;
err = platform_device_add ( eeepc - > platform_device ) ;
if ( err )
goto fail_platform_device ;
err = eeepc_wmi_sysfs_init ( eeepc - > platform_device ) ;
if ( err )
goto fail_sysfs ;
return 0 ;
fail_sysfs :
platform_device_del ( eeepc - > platform_device ) ;
fail_platform_device :
platform_device_put ( eeepc - > platform_device ) ;
return err ;
}
static void eeepc_wmi_platform_exit ( struct eeepc_wmi * eeepc )
{
eeepc_wmi_sysfs_exit ( eeepc - > platform_device ) ;
platform_device_unregister ( eeepc - > platform_device ) ;
}
/*
* WMI Driver
*/
static struct platform_device * __init eeepc_wmi_add ( void )
{
struct eeepc_wmi * eeepc ;
2010-03-21 05:26:34 +03:00
acpi_status status ;
2010-11-29 10:14:05 +03:00
int err ;
2010-03-21 05:26:34 +03:00
2010-11-29 10:14:05 +03:00
eeepc = kzalloc ( sizeof ( struct eeepc_wmi ) , GFP_KERNEL ) ;
if ( ! eeepc )
return ERR_PTR ( - ENOMEM ) ;
/*
* Register the platform device first . It is used as a parent for the
* sub - devices below .
*/
err = eeepc_wmi_platform_init ( eeepc ) ;
if ( err )
goto fail_platform ;
2010-04-11 05:27:19 +04:00
err = eeepc_wmi_input_init ( eeepc ) ;
if ( err )
2010-11-29 10:14:05 +03:00
goto fail_input ;
2010-04-11 05:27:54 +04:00
2010-11-29 10:14:06 +03:00
err = eeepc_wmi_led_init ( eeepc ) ;
if ( err )
goto fail_leds ;
2010-11-29 10:14:07 +03:00
err = eeepc_wmi_rfkill_init ( eeepc ) ;
if ( err )
goto fail_rfkill ;
2010-04-11 05:27:54 +04:00
if ( ! acpi_video_backlight_support ( ) ) {
err = eeepc_wmi_backlight_init ( eeepc ) ;
if ( err )
2010-11-29 10:14:05 +03:00
goto fail_backlight ;
2010-04-11 05:27:54 +04:00
} else
pr_info ( " Backlight controlled by ACPI video driver \n " ) ;
2010-04-11 05:27:19 +04:00
status = wmi_install_notify_handler ( EEEPC_WMI_EVENT_GUID ,
2010-11-29 10:14:05 +03:00
eeepc_wmi_notify , eeepc ) ;
2010-04-11 05:27:19 +04:00
if ( ACPI_FAILURE ( status ) ) {
pr_err ( " Unable to register notify handler - %d \n " ,
status ) ;
err = - ENODEV ;
2010-11-29 10:14:05 +03:00
goto fail_wmi_handler ;
2010-04-11 05:27:19 +04:00
}
2010-11-29 10:14:05 +03:00
return eeepc - > platform_device ;
2010-04-11 05:27:19 +04:00
2010-11-29 10:14:05 +03:00
fail_wmi_handler :
2010-04-11 05:27:54 +04:00
eeepc_wmi_backlight_exit ( eeepc ) ;
2010-11-29 10:14:05 +03:00
fail_backlight :
2010-11-29 10:14:07 +03:00
eeepc_wmi_rfkill_exit ( eeepc ) ;
fail_rfkill :
2010-11-29 10:14:06 +03:00
eeepc_wmi_led_exit ( eeepc ) ;
fail_leds :
2010-04-11 05:27:19 +04:00
eeepc_wmi_input_exit ( eeepc ) ;
2010-11-29 10:14:05 +03:00
fail_input :
eeepc_wmi_platform_exit ( eeepc ) ;
fail_platform :
kfree ( eeepc ) ;
return ERR_PTR ( err ) ;
2010-04-11 05:27:19 +04:00
}
2010-11-29 10:14:05 +03:00
static int eeepc_wmi_remove ( struct platform_device * device )
2010-04-11 05:27:19 +04:00
{
struct eeepc_wmi * eeepc ;
eeepc = platform_get_drvdata ( device ) ;
wmi_remove_notify_handler ( EEEPC_WMI_EVENT_GUID ) ;
2010-04-11 05:27:54 +04:00
eeepc_wmi_backlight_exit ( eeepc ) ;
2010-04-11 05:27:19 +04:00
eeepc_wmi_input_exit ( eeepc ) ;
2010-11-29 10:14:06 +03:00
eeepc_wmi_led_exit ( eeepc ) ;
2010-11-29 10:14:07 +03:00
eeepc_wmi_rfkill_exit ( eeepc ) ;
2010-11-29 10:14:05 +03:00
eeepc_wmi_platform_exit ( eeepc ) ;
2010-04-11 05:27:19 +04:00
2010-11-29 10:14:05 +03:00
kfree ( eeepc ) ;
2010-04-11 05:27:19 +04:00
return 0 ;
}
static struct platform_driver platform_driver = {
. driver = {
. name = EEEPC_WMI_FILE ,
. owner = THIS_MODULE ,
} ,
} ;
static int __init eeepc_wmi_init ( void )
{
int err ;
2010-04-11 05:27:54 +04:00
if ( ! wmi_has_guid ( EEEPC_WMI_EVENT_GUID ) | |
! wmi_has_guid ( EEEPC_WMI_MGMT_GUID ) ) {
2010-04-11 05:26:33 +04:00
pr_warning ( " No known WMI GUID found \n " ) ;
2010-03-21 05:26:34 +03:00
return - ENODEV ;
}
2010-11-29 10:14:05 +03:00
platform_device = eeepc_wmi_add ( ) ;
if ( IS_ERR ( platform_device ) ) {
err = PTR_ERR ( platform_device ) ;
goto fail_eeepc_wmi ;
2010-04-11 05:27:19 +04:00
}
err = platform_driver_register ( & platform_driver ) ;
if ( err ) {
pr_warning ( " Unable to register platform driver \n " ) ;
2010-11-29 10:14:05 +03:00
goto fail_platform_driver ;
2010-03-21 05:26:34 +03:00
}
return 0 ;
2010-04-11 05:27:19 +04:00
2010-11-29 10:14:05 +03:00
fail_platform_driver :
eeepc_wmi_remove ( platform_device ) ;
fail_eeepc_wmi :
2010-04-11 05:27:19 +04:00
return err ;
2010-03-21 05:26:34 +03:00
}
static void __exit eeepc_wmi_exit ( void )
{
2010-11-29 10:14:05 +03:00
eeepc_wmi_remove ( platform_device ) ;
2010-04-11 05:27:19 +04:00
platform_driver_unregister ( & platform_driver ) ;
2010-03-21 05:26:34 +03:00
}
module_init ( eeepc_wmi_init ) ;
module_exit ( eeepc_wmi_exit ) ;