2008-03-13 14:55:46 +03:00
/*
* eepc - laptop . c - Asus Eee PC extras
*
* Based on asus_acpi . c as patched for the Eee PC by Asus :
* ftp : //ftp.asus.com/pub/ASUS/EeePC/701/ASUS_ACPI_071126.rar
* Based on eee . c from eeepc - linux
*
* 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 .
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/types.h>
# include <linux/platform_device.h>
2008-03-13 14:56:37 +03:00
# include <linux/backlight.h>
# include <linux/fb.h>
2008-03-13 14:57:18 +03:00
# include <linux/hwmon.h>
# include <linux/hwmon-sysfs.h>
2008-03-13 14:55:46 +03:00
# include <acpi/acpi_drivers.h>
# include <acpi/acpi_bus.h>
# include <linux/uaccess.h>
2008-08-19 15:13:20 +04:00
# include <linux/input.h>
# include <linux/rfkill.h>
2008-03-13 14:55:46 +03:00
# define EEEPC_LAPTOP_VERSION "0.1"
# define EEEPC_HOTK_NAME "Eee PC Hotkey Driver"
# define EEEPC_HOTK_FILE "eeepc"
# define EEEPC_HOTK_CLASS "hotkey"
# define EEEPC_HOTK_DEVICE_NAME "Hotkey"
# define EEEPC_HOTK_HID "ASUS010"
# define EEEPC_LOG EEEPC_HOTK_FILE ": "
# define EEEPC_ERR KERN_ERR EEEPC_LOG
# define EEEPC_WARNING KERN_WARNING EEEPC_LOG
# define EEEPC_NOTICE KERN_NOTICE EEEPC_LOG
# define EEEPC_INFO KERN_INFO EEEPC_LOG
/*
* Definitions for Asus EeePC
*/
# define NOTIFY_WLAN_ON 0x10
2008-03-13 14:56:37 +03:00
# define NOTIFY_BRN_MIN 0x20
# define NOTIFY_BRN_MAX 0x2f
2008-03-13 14:55:46 +03:00
enum {
DISABLE_ASL_WLAN = 0x0001 ,
DISABLE_ASL_BLUETOOTH = 0x0002 ,
DISABLE_ASL_IRDA = 0x0004 ,
DISABLE_ASL_CAMERA = 0x0008 ,
DISABLE_ASL_TV = 0x0010 ,
DISABLE_ASL_GPS = 0x0020 ,
DISABLE_ASL_DISPLAYSWITCH = 0x0040 ,
DISABLE_ASL_MODEM = 0x0080 ,
DISABLE_ASL_CARDREADER = 0x0100
} ;
enum {
CM_ASL_WLAN = 0 ,
CM_ASL_BLUETOOTH ,
CM_ASL_IRDA ,
CM_ASL_1394 ,
CM_ASL_CAMERA ,
CM_ASL_TV ,
CM_ASL_GPS ,
CM_ASL_DVDROM ,
CM_ASL_DISPLAYSWITCH ,
CM_ASL_PANELBRIGHT ,
CM_ASL_BIOSFLASH ,
CM_ASL_ACPIFLASH ,
CM_ASL_CPUFV ,
CM_ASL_CPUTEMPERATURE ,
CM_ASL_FANCPU ,
CM_ASL_FANCHASSIS ,
CM_ASL_USBPORT1 ,
CM_ASL_USBPORT2 ,
CM_ASL_USBPORT3 ,
CM_ASL_MODEM ,
CM_ASL_CARDREADER ,
CM_ASL_LID
} ;
2008-06-25 20:25:47 +04:00
static const char * cm_getv [ ] = {
2008-03-13 14:55:46 +03:00
" WLDG " , NULL , NULL , NULL ,
" CAMG " , NULL , NULL , NULL ,
NULL , " PBLG " , NULL , NULL ,
" CFVG " , NULL , NULL , NULL ,
" USBG " , NULL , NULL , " MODG " ,
" CRDG " , " LIDG "
} ;
2008-06-25 20:25:47 +04:00
static const char * cm_setv [ ] = {
2008-03-13 14:55:46 +03:00
" WLDS " , NULL , NULL , NULL ,
" CAMS " , NULL , NULL , NULL ,
" SDSP " , " PBLS " , " HDPS " , NULL ,
" CFVS " , NULL , NULL , NULL ,
" USBG " , NULL , NULL , " MODS " ,
" CRDS " , NULL
} ;
2008-03-13 14:57:18 +03:00
# define EEEPC_EC "\\_SB.PCI0.SBRG.EC0."
# define EEEPC_EC_FAN_PWM EEEPC_EC "SC02" /* Fan PWM duty cycle (%) */
# define EEEPC_EC_SC02 0x63
# define EEEPC_EC_FAN_HRPM EEEPC_EC "SC05" /* High byte, fan speed (RPM) */
# define EEEPC_EC_FAN_LRPM EEEPC_EC "SC06" /* Low byte, fan speed (RPM) */
# define EEEPC_EC_FAN_CTRL EEEPC_EC "SFB3" /* Byte containing SF25 */
# define EEEPC_EC_SFB3 0xD3
2008-03-13 14:55:46 +03:00
/*
* This is the main structure , we can use it to store useful information
* about the hotk device
*/
struct eeepc_hotk {
struct acpi_device * device ; /* the device we are in */
acpi_handle handle ; /* the handle of the hotk device */
u32 cm_supported ; /* the control methods supported
by this BIOS */
uint init_flag ; /* Init flags */
u16 event_count [ 128 ] ; /* count for each event */
2008-08-19 15:13:20 +04:00
struct input_dev * inputdev ;
u16 * keycode_map ;
struct rfkill * eeepc_wlan_rfkill ;
struct rfkill * eeepc_bluetooth_rfkill ;
2008-03-13 14:55:46 +03:00
} ;
/* The actual device the driver binds to */
static struct eeepc_hotk * ehotk ;
/* Platform device/driver */
static struct platform_driver platform_driver = {
. driver = {
. name = EEEPC_HOTK_FILE ,
. owner = THIS_MODULE ,
}
} ;
static struct platform_device * platform_device ;
2008-08-19 15:13:20 +04:00
struct key_entry {
char type ;
u8 code ;
u16 keycode ;
} ;
enum { KE_KEY , KE_END } ;
static struct key_entry eeepc_keymap [ ] = {
/* Sleep already handled via generic ACPI code */
{ KE_KEY , 0x10 , KEY_WLAN } ,
{ KE_KEY , 0x12 , KEY_PROG1 } ,
{ KE_KEY , 0x13 , KEY_MUTE } ,
{ KE_KEY , 0x14 , KEY_VOLUMEDOWN } ,
{ KE_KEY , 0x15 , KEY_VOLUMEUP } ,
{ KE_KEY , 0x30 , KEY_SWITCHVIDEOMODE } ,
{ KE_KEY , 0x31 , KEY_SWITCHVIDEOMODE } ,
{ KE_KEY , 0x32 , KEY_SWITCHVIDEOMODE } ,
{ KE_END , 0 } ,
} ;
2008-03-13 14:55:46 +03:00
/*
* The hotkey driver declaration
*/
static int eeepc_hotk_add ( struct acpi_device * device ) ;
static int eeepc_hotk_remove ( struct acpi_device * device , int type ) ;
static const struct acpi_device_id eeepc_device_ids [ ] = {
{ EEEPC_HOTK_HID , 0 } ,
{ " " , 0 } ,
} ;
MODULE_DEVICE_TABLE ( acpi , eeepc_device_ids ) ;
static struct acpi_driver eeepc_hotk_driver = {
. name = EEEPC_HOTK_NAME ,
. class = EEEPC_HOTK_CLASS ,
. ids = eeepc_device_ids ,
. ops = {
. add = eeepc_hotk_add ,
. remove = eeepc_hotk_remove ,
} ,
} ;
2008-03-13 14:56:37 +03:00
/* The backlight device /sys/class/backlight */
static struct backlight_device * eeepc_backlight_device ;
2008-03-13 14:57:18 +03:00
/* The hwmon device */
static struct device * eeepc_hwmon_device ;
2008-03-13 14:56:37 +03:00
/*
* The backlight class declaration
*/
static int read_brightness ( struct backlight_device * bd ) ;
static int update_bl_status ( struct backlight_device * bd ) ;
static struct backlight_ops eeepcbl_ops = {
. get_brightness = read_brightness ,
. update_status = update_bl_status ,
} ;
2008-03-13 14:55:46 +03:00
MODULE_AUTHOR ( " Corentin Chary, Eric Cooper " ) ;
MODULE_DESCRIPTION ( EEEPC_HOTK_NAME ) ;
MODULE_LICENSE ( " GPL " ) ;
/*
* ACPI Helpers
*/
static int write_acpi_int ( acpi_handle handle , const char * method , int val ,
struct acpi_buffer * output )
{
struct acpi_object_list params ;
union acpi_object in_obj ;
acpi_status status ;
params . count = 1 ;
params . pointer = & in_obj ;
in_obj . type = ACPI_TYPE_INTEGER ;
in_obj . integer . value = val ;
status = acpi_evaluate_object ( handle , ( char * ) method , & params , output ) ;
return ( status = = AE_OK ? 0 : - 1 ) ;
}
static int read_acpi_int ( acpi_handle handle , const char * method , int * val )
{
acpi_status status ;
2008-10-10 10:22:59 +04:00
unsigned long long result ;
2008-03-13 14:55:46 +03:00
status = acpi_evaluate_integer ( handle , ( char * ) method , NULL , & result ) ;
if ( ACPI_FAILURE ( status ) ) {
* val = - 1 ;
return - 1 ;
} else {
* val = result ;
return 0 ;
}
}
static int set_acpi ( int cm , int value )
{
if ( ehotk - > cm_supported & ( 0x1 < < cm ) ) {
const char * method = cm_setv [ cm ] ;
if ( method = = NULL )
return - ENODEV ;
if ( write_acpi_int ( ehotk - > handle , method , value , NULL ) )
printk ( EEEPC_WARNING " Error writing %s \n " , method ) ;
}
return 0 ;
}
static int get_acpi ( int cm )
{
int value = - 1 ;
if ( ( ehotk - > cm_supported & ( 0x1 < < cm ) ) ) {
const char * method = cm_getv [ cm ] ;
if ( method = = NULL )
return - ENODEV ;
if ( read_acpi_int ( ehotk - > handle , method , & value ) )
printk ( EEEPC_WARNING " Error reading %s \n " , method ) ;
}
return value ;
}
2008-03-13 14:56:37 +03:00
/*
* Backlight
*/
static int read_brightness ( struct backlight_device * bd )
{
return get_acpi ( CM_ASL_PANELBRIGHT ) ;
}
static int set_brightness ( struct backlight_device * bd , int value )
{
value = max ( 0 , min ( 15 , value ) ) ;
return set_acpi ( CM_ASL_PANELBRIGHT , value ) ;
}
static int update_bl_status ( struct backlight_device * bd )
{
return set_brightness ( bd , bd - > props . brightness ) ;
}
2008-08-19 15:13:20 +04:00
/*
* Rfkill helpers
*/
static int eeepc_wlan_rfkill_set ( void * data , enum rfkill_state state )
{
if ( state = = RFKILL_STATE_SOFT_BLOCKED )
return set_acpi ( CM_ASL_WLAN , 0 ) ;
else
return set_acpi ( CM_ASL_WLAN , 1 ) ;
}
static int eeepc_wlan_rfkill_state ( void * data , enum rfkill_state * state )
{
if ( get_acpi ( CM_ASL_WLAN ) = = 1 )
* state = RFKILL_STATE_UNBLOCKED ;
else
* state = RFKILL_STATE_SOFT_BLOCKED ;
return 0 ;
}
static int eeepc_bluetooth_rfkill_set ( void * data , enum rfkill_state state )
{
if ( state = = RFKILL_STATE_SOFT_BLOCKED )
return set_acpi ( CM_ASL_BLUETOOTH , 0 ) ;
else
return set_acpi ( CM_ASL_BLUETOOTH , 1 ) ;
}
static int eeepc_bluetooth_rfkill_state ( void * data , enum rfkill_state * state )
{
if ( get_acpi ( CM_ASL_BLUETOOTH ) = = 1 )
* state = RFKILL_STATE_UNBLOCKED ;
else
* state = RFKILL_STATE_SOFT_BLOCKED ;
return 0 ;
}
2008-03-13 14:55:46 +03:00
/*
* Sys helpers
*/
static int parse_arg ( const char * buf , unsigned long count , int * val )
{
if ( ! count )
return 0 ;
if ( sscanf ( buf , " %i " , val ) ! = 1 )
return - EINVAL ;
return count ;
}
static ssize_t store_sys_acpi ( int cm , const char * buf , size_t count )
{
int rv , value ;
rv = parse_arg ( buf , count , & value ) ;
if ( rv > 0 )
set_acpi ( cm , value ) ;
return rv ;
}
static ssize_t show_sys_acpi ( int cm , char * buf )
{
return sprintf ( buf , " %d \n " , get_acpi ( cm ) ) ;
}
# define EEEPC_CREATE_DEVICE_ATTR(_name, _cm) \
static ssize_t show_ # # _name ( struct device * dev , \
struct device_attribute * attr , \
char * buf ) \
{ \
return show_sys_acpi ( _cm , buf ) ; \
} \
static ssize_t store_ # # _name ( struct device * dev , \
struct device_attribute * attr , \
const char * buf , size_t count ) \
{ \
return store_sys_acpi ( _cm , buf , count ) ; \
} \
static struct device_attribute dev_attr_ # # _name = { \
. attr = { \
. name = __stringify ( _name ) , \
. mode = 0644 } , \
. show = show_ # # _name , \
. store = store_ # # _name , \
}
EEEPC_CREATE_DEVICE_ATTR ( camera , CM_ASL_CAMERA ) ;
EEEPC_CREATE_DEVICE_ATTR ( cardr , CM_ASL_CARDREADER ) ;
EEEPC_CREATE_DEVICE_ATTR ( disp , CM_ASL_DISPLAYSWITCH ) ;
static struct attribute * platform_attributes [ ] = {
& dev_attr_camera . attr ,
& dev_attr_cardr . attr ,
& dev_attr_disp . attr ,
NULL
} ;
static struct attribute_group platform_attribute_group = {
. attrs = platform_attributes
} ;
/*
* Hotkey functions
*/
2008-08-19 15:13:20 +04:00
static struct key_entry * eepc_get_entry_by_scancode ( int code )
{
struct key_entry * key ;
for ( key = eeepc_keymap ; key - > type ! = KE_END ; key + + )
if ( code = = key - > code )
return key ;
return NULL ;
}
static struct key_entry * eepc_get_entry_by_keycode ( int code )
{
struct key_entry * key ;
for ( key = eeepc_keymap ; key - > type ! = KE_END ; key + + )
if ( code = = key - > keycode & & key - > type = = KE_KEY )
return key ;
return NULL ;
}
static int eeepc_getkeycode ( struct input_dev * dev , int scancode , int * keycode )
{
struct key_entry * key = eepc_get_entry_by_scancode ( scancode ) ;
if ( key & & key - > type = = KE_KEY ) {
* keycode = key - > keycode ;
return 0 ;
}
return - EINVAL ;
}
static int eeepc_setkeycode ( struct input_dev * dev , int scancode , int keycode )
{
struct key_entry * key ;
int old_keycode ;
if ( keycode < 0 | | keycode > KEY_MAX )
return - EINVAL ;
key = eepc_get_entry_by_scancode ( scancode ) ;
if ( key & & key - > type = = KE_KEY ) {
old_keycode = key - > keycode ;
key - > keycode = keycode ;
set_bit ( keycode , dev - > keybit ) ;
if ( ! eepc_get_entry_by_keycode ( old_keycode ) )
clear_bit ( old_keycode , dev - > keybit ) ;
return 0 ;
}
return - EINVAL ;
}
2008-03-13 14:55:46 +03:00
static int eeepc_hotk_check ( void )
{
2008-08-19 15:13:20 +04:00
const struct key_entry * key ;
2008-03-13 14:55:46 +03:00
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER , NULL } ;
int result ;
result = acpi_bus_get_status ( ehotk - > device ) ;
if ( result )
return result ;
if ( ehotk - > device - > status . present ) {
if ( write_acpi_int ( ehotk - > handle , " INIT " , ehotk - > init_flag ,
& buffer ) ) {
printk ( EEEPC_ERR " Hotkey initialization failed \n " ) ;
return - ENODEV ;
} else {
printk ( EEEPC_NOTICE " Hotkey init flags 0x%x \n " ,
ehotk - > init_flag ) ;
}
/* get control methods supported */
if ( read_acpi_int ( ehotk - > handle , " CMSG "
, & ehotk - > cm_supported ) ) {
printk ( EEEPC_ERR
" Get control methods supported failed \n " ) ;
return - ENODEV ;
} else {
printk ( EEEPC_INFO
" Get control methods supported: 0x%x \n " ,
ehotk - > cm_supported ) ;
}
2008-08-19 15:13:20 +04:00
ehotk - > inputdev = input_allocate_device ( ) ;
if ( ! ehotk - > inputdev ) {
printk ( EEEPC_INFO " Unable to allocate input device \n " ) ;
return 0 ;
}
ehotk - > inputdev - > name = " Asus EeePC extra buttons " ;
ehotk - > inputdev - > phys = EEEPC_HOTK_FILE " /input0 " ;
ehotk - > inputdev - > id . bustype = BUS_HOST ;
ehotk - > inputdev - > getkeycode = eeepc_getkeycode ;
ehotk - > inputdev - > setkeycode = eeepc_setkeycode ;
for ( key = eeepc_keymap ; key - > type ! = KE_END ; key + + ) {
switch ( key - > type ) {
case KE_KEY :
set_bit ( EV_KEY , ehotk - > inputdev - > evbit ) ;
set_bit ( key - > keycode , ehotk - > inputdev - > keybit ) ;
break ;
}
}
result = input_register_device ( ehotk - > inputdev ) ;
if ( result ) {
printk ( EEEPC_INFO " Unable to register input device \n " ) ;
input_free_device ( ehotk - > inputdev ) ;
return 0 ;
}
2008-03-13 14:55:46 +03:00
} else {
printk ( EEEPC_ERR " Hotkey device not present, aborting \n " ) ;
return - EINVAL ;
}
return 0 ;
}
2008-03-13 14:56:37 +03:00
static void notify_brn ( void )
{
struct backlight_device * bd = eeepc_backlight_device ;
bd - > props . brightness = read_brightness ( bd ) ;
}
2008-03-13 14:55:46 +03:00
static void eeepc_hotk_notify ( acpi_handle handle , u32 event , void * data )
{
2008-08-19 15:13:20 +04:00
static struct key_entry * key ;
2008-03-13 14:55:46 +03:00
if ( ! ehotk )
return ;
2008-03-13 14:56:37 +03:00
if ( event > = NOTIFY_BRN_MIN & & event < = NOTIFY_BRN_MAX )
notify_brn ( ) ;
2008-03-13 14:55:46 +03:00
acpi_bus_generate_proc_event ( ehotk - > device , event ,
ehotk - > event_count [ event % 128 ] + + ) ;
2008-08-19 15:13:20 +04:00
if ( ehotk - > inputdev ) {
key = eepc_get_entry_by_scancode ( event ) ;
if ( key ) {
switch ( key - > type ) {
case KE_KEY :
input_report_key ( ehotk - > inputdev , key - > keycode ,
1 ) ;
input_sync ( ehotk - > inputdev ) ;
input_report_key ( ehotk - > inputdev , key - > keycode ,
0 ) ;
input_sync ( ehotk - > inputdev ) ;
break ;
}
}
}
2008-03-13 14:55:46 +03:00
}
static int eeepc_hotk_add ( struct acpi_device * device )
{
acpi_status status = AE_OK ;
int result ;
if ( ! device )
return - EINVAL ;
printk ( EEEPC_NOTICE EEEPC_HOTK_NAME " \n " ) ;
ehotk = kzalloc ( sizeof ( struct eeepc_hotk ) , GFP_KERNEL ) ;
if ( ! ehotk )
return - ENOMEM ;
ehotk - > init_flag = DISABLE_ASL_WLAN | DISABLE_ASL_DISPLAYSWITCH ;
ehotk - > handle = device - > handle ;
strcpy ( acpi_device_name ( device ) , EEEPC_HOTK_DEVICE_NAME ) ;
strcpy ( acpi_device_class ( device ) , EEEPC_HOTK_CLASS ) ;
2008-09-23 01:37:34 +04:00
device - > driver_data = ehotk ;
2008-03-13 14:55:46 +03:00
ehotk - > device = device ;
result = eeepc_hotk_check ( ) ;
if ( result )
goto end ;
status = acpi_install_notify_handler ( ehotk - > handle , ACPI_SYSTEM_NOTIFY ,
eeepc_hotk_notify , ehotk ) ;
if ( ACPI_FAILURE ( status ) )
printk ( EEEPC_ERR " Error installing notify handler \n " ) ;
2008-08-19 15:13:20 +04:00
if ( get_acpi ( CM_ASL_WLAN ) ! = - 1 ) {
ehotk - > eeepc_wlan_rfkill = rfkill_allocate ( & device - > dev ,
RFKILL_TYPE_WLAN ) ;
if ( ! ehotk - > eeepc_wlan_rfkill )
goto end ;
ehotk - > eeepc_wlan_rfkill - > name = " eeepc-wlan " ;
ehotk - > eeepc_wlan_rfkill - > toggle_radio = eeepc_wlan_rfkill_set ;
ehotk - > eeepc_wlan_rfkill - > get_state = eeepc_wlan_rfkill_state ;
if ( get_acpi ( CM_ASL_WLAN ) = = 1 )
ehotk - > eeepc_wlan_rfkill - > state =
RFKILL_STATE_UNBLOCKED ;
else
ehotk - > eeepc_wlan_rfkill - > state =
RFKILL_STATE_SOFT_BLOCKED ;
rfkill_register ( ehotk - > eeepc_wlan_rfkill ) ;
}
if ( get_acpi ( CM_ASL_BLUETOOTH ) ! = - 1 ) {
ehotk - > eeepc_bluetooth_rfkill =
rfkill_allocate ( & device - > dev , RFKILL_TYPE_BLUETOOTH ) ;
if ( ! ehotk - > eeepc_bluetooth_rfkill )
goto end ;
ehotk - > eeepc_bluetooth_rfkill - > name = " eeepc-bluetooth " ;
ehotk - > eeepc_bluetooth_rfkill - > toggle_radio =
eeepc_bluetooth_rfkill_set ;
ehotk - > eeepc_bluetooth_rfkill - > get_state =
eeepc_bluetooth_rfkill_state ;
if ( get_acpi ( CM_ASL_BLUETOOTH ) = = 1 )
ehotk - > eeepc_bluetooth_rfkill - > state =
RFKILL_STATE_UNBLOCKED ;
else
ehotk - > eeepc_bluetooth_rfkill - > state =
RFKILL_STATE_SOFT_BLOCKED ;
rfkill_register ( ehotk - > eeepc_bluetooth_rfkill ) ;
}
2008-03-13 14:55:46 +03:00
end :
if ( result ) {
kfree ( ehotk ) ;
ehotk = NULL ;
}
return result ;
}
static int eeepc_hotk_remove ( struct acpi_device * device , int type )
{
acpi_status status = 0 ;
if ( ! device | | ! acpi_driver_data ( device ) )
return - EINVAL ;
status = acpi_remove_notify_handler ( ehotk - > handle , ACPI_SYSTEM_NOTIFY ,
eeepc_hotk_notify ) ;
if ( ACPI_FAILURE ( status ) )
printk ( EEEPC_ERR " Error removing notify handler \n " ) ;
kfree ( ehotk ) ;
return 0 ;
}
2008-03-13 14:57:18 +03:00
/*
* Hwmon
*/
static int eeepc_get_fan_pwm ( void )
{
int value = 0 ;
read_acpi_int ( NULL , EEEPC_EC_FAN_PWM , & value ) ;
2008-10-09 17:33:57 +04:00
value = value * 255 / 100 ;
2008-03-13 14:57:18 +03:00
return ( value ) ;
}
static void eeepc_set_fan_pwm ( int value )
{
2008-10-09 17:33:57 +04:00
value = SENSORS_LIMIT ( value , 0 , 255 ) ;
value = value * 100 / 255 ;
2008-03-13 14:57:18 +03:00
ec_write ( EEEPC_EC_SC02 , value ) ;
}
static int eeepc_get_fan_rpm ( void )
{
int high = 0 ;
int low = 0 ;
read_acpi_int ( NULL , EEEPC_EC_FAN_HRPM , & high ) ;
read_acpi_int ( NULL , EEEPC_EC_FAN_LRPM , & low ) ;
return ( high < < 8 | low ) ;
}
static int eeepc_get_fan_ctrl ( void )
{
int value = 0 ;
read_acpi_int ( NULL , EEEPC_EC_FAN_CTRL , & value ) ;
return ( ( value & 0x02 ? 1 : 0 ) ) ;
}
static void eeepc_set_fan_ctrl ( int manual )
{
int value = 0 ;
read_acpi_int ( NULL , EEEPC_EC_FAN_CTRL , & value ) ;
if ( manual )
value | = 0x02 ;
else
value & = ~ 0x02 ;
ec_write ( EEEPC_EC_SFB3 , value ) ;
}
static ssize_t store_sys_hwmon ( void ( * set ) ( int ) , const char * buf , size_t count )
{
int rv , value ;
rv = parse_arg ( buf , count , & value ) ;
if ( rv > 0 )
set ( value ) ;
return rv ;
}
static ssize_t show_sys_hwmon ( int ( * get ) ( void ) , char * buf )
{
return sprintf ( buf , " %d \n " , get ( ) ) ;
}
# define EEEPC_CREATE_SENSOR_ATTR(_name, _mode, _set, _get) \
static ssize_t show_ # # _name ( struct device * dev , \
struct device_attribute * attr , \
char * buf ) \
{ \
return show_sys_hwmon ( _set , buf ) ; \
} \
static ssize_t store_ # # _name ( struct device * dev , \
struct device_attribute * attr , \
const char * buf , size_t count ) \
{ \
return store_sys_hwmon ( _get , buf , count ) ; \
} \
static SENSOR_DEVICE_ATTR ( _name , _mode , show_ # # _name , store_ # # _name , 0 ) ;
EEEPC_CREATE_SENSOR_ATTR ( fan1_input , S_IRUGO , eeepc_get_fan_rpm , NULL ) ;
2008-10-09 17:33:57 +04:00
EEEPC_CREATE_SENSOR_ATTR ( pwm1 , S_IRUGO | S_IWUSR ,
2008-03-13 14:57:18 +03:00
eeepc_get_fan_pwm , eeepc_set_fan_pwm ) ;
EEEPC_CREATE_SENSOR_ATTR ( pwm1_enable , S_IRUGO | S_IWUSR ,
eeepc_get_fan_ctrl , eeepc_set_fan_ctrl ) ;
2008-10-09 17:33:57 +04:00
static ssize_t
show_name ( struct device * dev , struct device_attribute * attr , char * buf )
{
return sprintf ( buf , " eeepc \n " ) ;
}
static SENSOR_DEVICE_ATTR ( name , S_IRUGO , show_name , NULL , 0 ) ;
2008-03-13 14:57:18 +03:00
static struct attribute * hwmon_attributes [ ] = {
2008-10-09 17:33:57 +04:00
& sensor_dev_attr_pwm1 . dev_attr . attr ,
2008-03-13 14:57:18 +03:00
& sensor_dev_attr_fan1_input . dev_attr . attr ,
& sensor_dev_attr_pwm1_enable . dev_attr . attr ,
2008-10-09 17:33:57 +04:00
& sensor_dev_attr_name . dev_attr . attr ,
2008-03-13 14:57:18 +03:00
NULL
} ;
static struct attribute_group hwmon_attribute_group = {
. attrs = hwmon_attributes
} ;
2008-03-13 14:55:46 +03:00
/*
* exit / init
*/
2008-03-13 14:56:37 +03:00
static void eeepc_backlight_exit ( void )
{
if ( eeepc_backlight_device )
backlight_device_unregister ( eeepc_backlight_device ) ;
2008-08-19 15:13:20 +04:00
if ( ehotk - > inputdev )
input_unregister_device ( ehotk - > inputdev ) ;
if ( ehotk - > eeepc_wlan_rfkill )
rfkill_unregister ( ehotk - > eeepc_wlan_rfkill ) ;
if ( ehotk - > eeepc_bluetooth_rfkill )
rfkill_unregister ( ehotk - > eeepc_bluetooth_rfkill ) ;
2008-03-13 14:56:37 +03:00
eeepc_backlight_device = NULL ;
}
2008-03-13 14:57:18 +03:00
static void eeepc_hwmon_exit ( void )
{
struct device * hwmon ;
hwmon = eeepc_hwmon_device ;
if ( ! hwmon )
return ;
sysfs_remove_group ( & hwmon - > kobj ,
& hwmon_attribute_group ) ;
2008-08-21 01:08:57 +04:00
hwmon_device_unregister ( hwmon ) ;
2008-03-13 14:57:18 +03:00
eeepc_hwmon_device = NULL ;
}
2008-03-13 14:55:46 +03:00
static void __exit eeepc_laptop_exit ( void )
{
2008-03-13 14:56:37 +03:00
eeepc_backlight_exit ( ) ;
2008-03-13 14:57:18 +03:00
eeepc_hwmon_exit ( ) ;
2008-03-13 14:55:46 +03:00
acpi_bus_unregister_driver ( & eeepc_hotk_driver ) ;
sysfs_remove_group ( & platform_device - > dev . kobj ,
& platform_attribute_group ) ;
platform_device_unregister ( platform_device ) ;
platform_driver_unregister ( & platform_driver ) ;
}
2008-03-13 14:56:37 +03:00
static int eeepc_backlight_init ( struct device * dev )
{
struct backlight_device * bd ;
bd = backlight_device_register ( EEEPC_HOTK_FILE , dev ,
NULL , & eeepcbl_ops ) ;
if ( IS_ERR ( bd ) ) {
printk ( EEEPC_ERR
" Could not register eeepc backlight device \n " ) ;
eeepc_backlight_device = NULL ;
return PTR_ERR ( bd ) ;
}
eeepc_backlight_device = bd ;
bd - > props . max_brightness = 15 ;
bd - > props . brightness = read_brightness ( NULL ) ;
bd - > props . power = FB_BLANK_UNBLANK ;
backlight_update_status ( bd ) ;
return 0 ;
}
2008-03-13 14:57:18 +03:00
static int eeepc_hwmon_init ( struct device * dev )
{
struct device * hwmon ;
int result ;
hwmon = hwmon_device_register ( dev ) ;
if ( IS_ERR ( hwmon ) ) {
printk ( EEEPC_ERR
" Could not register eeepc hwmon device \n " ) ;
eeepc_hwmon_device = NULL ;
return PTR_ERR ( hwmon ) ;
}
eeepc_hwmon_device = hwmon ;
result = sysfs_create_group ( & hwmon - > kobj ,
& hwmon_attribute_group ) ;
if ( result )
eeepc_hwmon_exit ( ) ;
return result ;
}
2008-03-13 14:55:46 +03:00
static int __init eeepc_laptop_init ( void )
{
struct device * dev ;
int result ;
if ( acpi_disabled )
return - ENODEV ;
result = acpi_bus_register_driver ( & eeepc_hotk_driver ) ;
if ( result < 0 )
return result ;
if ( ! ehotk ) {
acpi_bus_unregister_driver ( & eeepc_hotk_driver ) ;
return - ENODEV ;
}
dev = acpi_get_physical_device ( ehotk - > device - > handle ) ;
2008-08-01 19:37:59 +04:00
if ( ! acpi_video_backlight_support ( ) ) {
result = eeepc_backlight_init ( dev ) ;
if ( result )
goto fail_backlight ;
} else
printk ( EEEPC_INFO " Backlight controlled by ACPI video "
" driver \n " ) ;
2008-03-13 14:57:18 +03:00
result = eeepc_hwmon_init ( dev ) ;
if ( result )
goto fail_hwmon ;
2008-03-13 14:55:46 +03:00
/* Register platform stuff */
result = platform_driver_register ( & platform_driver ) ;
if ( result )
goto fail_platform_driver ;
platform_device = platform_device_alloc ( EEEPC_HOTK_FILE , - 1 ) ;
if ( ! platform_device ) {
result = - ENOMEM ;
goto fail_platform_device1 ;
}
result = platform_device_add ( platform_device ) ;
if ( result )
goto fail_platform_device2 ;
result = sysfs_create_group ( & platform_device - > dev . kobj ,
& platform_attribute_group ) ;
if ( result )
goto fail_sysfs ;
return 0 ;
fail_sysfs :
platform_device_del ( platform_device ) ;
fail_platform_device2 :
platform_device_put ( platform_device ) ;
fail_platform_device1 :
platform_driver_unregister ( & platform_driver ) ;
fail_platform_driver :
2008-03-13 14:57:18 +03:00
eeepc_hwmon_exit ( ) ;
fail_hwmon :
2008-03-13 14:56:37 +03:00
eeepc_backlight_exit ( ) ;
fail_backlight :
2008-03-13 14:55:46 +03:00
return result ;
}
module_init ( eeepc_laptop_init ) ;
module_exit ( eeepc_laptop_exit ) ;