2007-01-26 16:04:30 +03:00
/*
* asus - laptop . c - Asus Laptop Support
*
*
* Copyright ( C ) 2002 - 2005 Julien Lerouge , 2003 - 2006 Karol Kozimor
2007-03-11 12:28:03 +03:00
* Copyright ( C ) 2006 - 2007 Corentin Chary
2007-01-26 16:04:30 +03: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 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
*
*
* The development page for this driver is located at
* http : //sourceforge.net/projects/acpi4asus/
*
* Credits :
* Pontus Fuchs - Helper functions , cleanup
* Johann Wiesner - Small compile fixes
* John Belmonte - ACPI code for Toshiba laptop was a good starting point .
* Eric Burghard - LED display support for W1N
* Josh Green - Light Sens support
* Thomas Tuttle - His first patch for led support was very helpfull
2007-05-06 16:47:06 +04:00
* Sam Lin - GPS support
2007-01-26 16:04:30 +03:00
*/
2009-06-19 16:52:03 +04:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2007-01-26 16:04:30 +03:00
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/types.h>
# include <linux/err.h>
# include <linux/proc_fs.h>
2007-01-26 16:04:45 +03:00
# include <linux/backlight.h>
# include <linux/fb.h>
2007-01-26 16:04:35 +03:00
# include <linux/leds.h>
2007-01-26 16:04:30 +03:00
# include <linux/platform_device.h>
# include <acpi/acpi_drivers.h>
# include <acpi/acpi_bus.h>
# include <asm/uaccess.h>
2009-01-20 18:17:43 +03:00
# include <linux/input.h>
2007-01-26 16:04:30 +03:00
2009-11-28 12:32:34 +03:00
# define ASUS_LAPTOP_VERSION "0.42"
2007-01-26 16:04:30 +03:00
2009-11-30 23:55:12 +03:00
# define ASUS_LAPTOP_NAME "Asus Laptop Support"
# define ASUS_LAPTOP_CLASS "hotkey"
# define ASUS_LAPTOP_DEVICE_NAME "Hotkey"
# define ASUS_LAPTOP_FILE KBUILD_MODNAME
# define ASUS_LAPTOP_PREFIX "\\_SB.ATKD."
2007-01-26 16:04:30 +03:00
2009-06-19 16:52:03 +04:00
2007-01-26 16:04:45 +03:00
/*
* Some events we use , same for all Asus
*/
2009-11-28 12:32:34 +03:00
# define ATKD_BR_UP 0x10
# define ATKD_BR_DOWN 0x20
# define ATKD_LCD_ON 0x33
# define ATKD_LCD_OFF 0x34
2007-01-26 16:04:45 +03:00
2007-01-26 16:04:40 +03:00
/*
* Known bits returned by \ _SB . ATKD . HWRS
*/
2009-11-28 12:32:34 +03:00
# define WL_HWRS 0x80
# define BT_HWRS 0x100
2007-01-26 16:04:40 +03:00
2007-01-26 16:04:35 +03:00
/*
* Flags for hotk status
2007-01-26 16:04:40 +03:00
* WL_ON and BT_ON are also used for wireless_status ( )
2007-01-26 16:04:35 +03:00
*/
2009-11-28 12:32:34 +03:00
# define WL_ON 0x01 /* internal Wifi */
# define BT_ON 0x02 /* internal Bluetooth */
# define MLED_ON 0x04 /* mail LED */
# define TLED_ON 0x08 /* touchpad LED */
# define RLED_ON 0x10 /* Record LED */
# define PLED_ON 0x20 /* Phone LED */
# define GLED_ON 0x40 /* Gaming LED */
# define LCD_ON 0x80 /* LCD backlight */
# define GPS_ON 0x100 /* GPS */
# define KEY_ON 0x200 /* Keyboard backlight */
2007-01-26 16:04:30 +03:00
MODULE_AUTHOR ( " Julien Lerouge, Karol Kozimor, Corentin Chary " ) ;
2009-11-30 23:55:12 +03:00
MODULE_DESCRIPTION ( ASUS_LAPTOP_NAME ) ;
2007-01-26 16:04:30 +03:00
MODULE_LICENSE ( " GPL " ) ;
2009-08-29 12:28:29 +04:00
/*
* WAPF defines the behavior of the Fn + Fx wlan key
2007-03-11 12:27:33 +03:00
* The significance of values is yet to be found , but
* most of the time :
* 0x0 will do nothing
* 0x1 will allow to control the device with Fn + Fx key .
* 0x4 will send an ACPI event ( 0x88 ) while pressing the Fn + Fx key
* 0x5 like 0x1 or 0x4
* So , if something doesn ' t work as you want , just try other values = )
*/
static uint wapf = 1 ;
module_param ( wapf , uint , 0644 ) ;
MODULE_PARM_DESC ( wapf , " WAPF value " ) ;
2010-01-10 22:49:26 +03:00
static uint wireless_status = 1 ;
static uint bluetooth_status = 1 ;
module_param ( wireless_status , uint , 0644 ) ;
MODULE_PARM_DESC ( wireless_status , " Set the wireless status on boot "
" (0 = disabled, 1 = enabled, -1 = don't do anything). "
" default is 1 " ) ;
module_param ( bluetooth_status , uint , 0644 ) ;
MODULE_PARM_DESC ( bluetooth_status , " Set the wireless status on boot "
" (0 = disabled, 1 = enabled, -1 = don't do anything). "
" default is 1 " ) ;
2007-01-26 16:04:30 +03:00
# define ASUS_HANDLE(object, paths...) \
static acpi_handle object # # _handle = NULL ; \
static char * object # # _paths [ ] = { paths }
2007-01-26 16:04:35 +03:00
/* LED */
2009-11-30 23:55:12 +03:00
ASUS_HANDLE ( mled_set , ASUS_LAPTOP_PREFIX " MLED " ) ;
ASUS_HANDLE ( tled_set , ASUS_LAPTOP_PREFIX " TLED " ) ;
ASUS_HANDLE ( rled_set , ASUS_LAPTOP_PREFIX " RLED " ) ; /* W1JC */
ASUS_HANDLE ( pled_set , ASUS_LAPTOP_PREFIX " PLED " ) ; /* A7J */
ASUS_HANDLE ( gled_set , ASUS_LAPTOP_PREFIX " GLED " ) ; /* G1, G2 (probably) */
2007-01-26 16:04:35 +03:00
2007-01-26 16:04:55 +03:00
/* LEDD */
2009-11-30 23:55:12 +03:00
ASUS_HANDLE ( ledd_set , ASUS_LAPTOP_PREFIX " SLCM " ) ;
2007-01-26 16:04:55 +03:00
2009-08-29 12:28:29 +04:00
/*
* Bluetooth and WLAN
2007-01-26 16:04:40 +03:00
* WLED and BLED are not handled like other XLED , because in some dsdt
* they also control the WLAN / Bluetooth device .
*/
2009-11-30 23:55:12 +03:00
ASUS_HANDLE ( wl_switch , ASUS_LAPTOP_PREFIX " WLED " ) ;
ASUS_HANDLE ( bt_switch , ASUS_LAPTOP_PREFIX " BLED " ) ;
ASUS_HANDLE ( wireless_status , ASUS_LAPTOP_PREFIX " RSTS " ) ; /* All new models */
2007-01-26 16:04:40 +03:00
2007-01-26 16:04:45 +03:00
/* Brightness */
2009-11-30 23:55:12 +03:00
ASUS_HANDLE ( brightness_set , ASUS_LAPTOP_PREFIX " SPLV " ) ;
ASUS_HANDLE ( brightness_get , ASUS_LAPTOP_PREFIX " GPLV " ) ;
2007-01-26 16:04:45 +03:00
/* Backlight */
2007-01-30 09:46:43 +03:00
ASUS_HANDLE ( lcd_switch , " \\ _SB.PCI0.SBRG.EC0._Q10 " , /* All new models */
" \\ _SB.PCI0.ISA.EC0._Q10 " , /* A1x */
" \\ _SB.PCI0.PX40.ECD0._Q10 " , /* L3C */
" \\ _SB.PCI0.PX40.EC0.Q10 " , /* M1A */
" \\ _SB.PCI0.LPCB.EC0._Q10 " , /* P30 */
2008-10-17 11:47:57 +04:00
" \\ _SB.PCI0.LPCB.EC0._Q0E " , /* P30/P35 */
2007-01-30 09:46:43 +03:00
" \\ _SB.PCI0.PX40.Q10 " , /* S1x */
" \\ Q10 " ) ; /* A2x, L2D, L3D, M2E */
2007-01-26 16:04:45 +03:00
2007-01-26 16:04:49 +03:00
/* Display */
2009-11-30 23:55:12 +03:00
ASUS_HANDLE ( display_set , ASUS_LAPTOP_PREFIX " SDSP " ) ;
2009-08-29 12:28:29 +04:00
ASUS_HANDLE ( display_get ,
/* A6B, A6K A6R A7D F3JM L4R M6R A3G M6A M6V VX-1 V6J V6V W3Z */
" \\ _SB.PCI0.P0P1.VGA.GETD " ,
/* A3E A4K, A4D A4L A6J A7J A8J Z71V M9V S5A M5A z33A W1Jc W2V G1 */
" \\ _SB.PCI0.P0P2.VGA.GETD " ,
/* A6V A6Q */
" \\ _SB.PCI0.P0P3.VGA.GETD " ,
/* A6T, A6M */
" \\ _SB.PCI0.P0PA.VGA.GETD " ,
/* L3C */
" \\ _SB.PCI0.PCI1.VGAC.NMAP " ,
/* Z96F */
" \\ _SB.PCI0.VGA.GETD " ,
/* A2D */
" \\ ACTD " ,
/* A4G Z71A W1N W5A W5F M2N M3N M5N M6N S1N S5N */
" \\ ADVG " ,
/* P30 */
" \\ DNXT " ,
/* A2H D1 L2D L3D L3H L2E L5D L5C M1A M2E L4L W3V */
" \\ INFB " ,
/* A3F A6F A3N A3L M6N W3N W6A */
" \\ SSTE " ) ;
2009-11-30 23:55:12 +03:00
ASUS_HANDLE ( ls_switch , ASUS_LAPTOP_PREFIX " ALSC " ) ; /* Z71A Z71V */
ASUS_HANDLE ( ls_level , ASUS_LAPTOP_PREFIX " ALSL " ) ; /* Z71A Z71V */
2007-01-26 16:04:58 +03:00
2007-05-06 16:47:06 +04:00
/* GPS */
/* R2H use different handle for GPS on/off */
2009-11-30 23:55:12 +03:00
ASUS_HANDLE ( gps_on , ASUS_LAPTOP_PREFIX " SDON " ) ; /* R2H */
ASUS_HANDLE ( gps_off , ASUS_LAPTOP_PREFIX " SDOF " ) ; /* R2H */
ASUS_HANDLE ( gps_status , ASUS_LAPTOP_PREFIX " GPST " ) ;
2007-05-06 16:47:06 +04:00
2009-08-28 16:56:50 +04:00
/* Keyboard light */
2009-11-30 23:55:12 +03:00
ASUS_HANDLE ( kled_set , ASUS_LAPTOP_PREFIX " SLKB " ) ;
ASUS_HANDLE ( kled_get , ASUS_LAPTOP_PREFIX " GLKB " ) ;
2009-08-28 16:56:50 +04:00
2007-01-26 16:04:30 +03:00
/*
* This is the main structure , we can use it to store anything interesting
* about the hotk device
*/
2009-11-30 23:55:12 +03:00
struct asus_laptop {
2009-08-29 12:28:29 +04:00
char * name ; /* laptop name */
2009-11-30 23:42:42 +03:00
2009-12-01 00:13:54 +03:00
struct acpi_table_header * dsdt_info ;
2009-11-30 23:42:42 +03:00
struct platform_device * platform_device ;
2009-12-01 00:13:54 +03:00
struct acpi_device * device ; /* the device we are in */
struct backlight_device * backlight_device ;
struct input_dev * inputdev ;
2009-08-29 12:28:29 +04:00
acpi_handle handle ; /* the handle of the hotk device */
char status ; /* status of the hotk, for LEDs, ... */
u32 ledd_status ; /* status of the LED display */
u8 light_level ; /* light sensor level */
u8 light_switch ; /* light sensor switch value */
u16 event_count [ 128 ] ; /* count for each event TODO make this better */
2009-01-20 18:17:43 +03:00
u16 * keycode_map ;
2007-01-26 16:04:30 +03:00
} ;
2009-11-30 23:55:12 +03:00
static struct asus_laptop * asus ;
2007-01-26 16:04:30 +03:00
2009-12-01 00:13:54 +03:00
static struct workqueue_struct * led_workqueue ;
2007-01-26 16:04:45 +03:00
/*
* The backlight class declaration
*/
static int read_brightness ( struct backlight_device * bd ) ;
static int update_bl_status ( struct backlight_device * bd ) ;
2007-02-11 02:07:48 +03:00
static struct backlight_ops asusbl_ops = {
2007-01-30 09:46:43 +03:00
. get_brightness = read_brightness ,
. update_status = update_bl_status ,
2007-01-26 16:04:45 +03:00
} ;
2009-08-28 16:56:49 +04:00
# define ASUS_LED(object, ledname, max) \
2007-01-26 16:04:35 +03:00
static void object # # _led_set ( struct led_classdev * led_cdev , \
enum led_brightness value ) ; \
2009-08-28 16:56:47 +04:00
static enum led_brightness object # # _led_get ( \
struct led_classdev * led_cdev ) ; \
2007-01-26 16:04:35 +03:00
static void object # # _led_update ( struct work_struct * ignored ) ; \
static int object # # _led_wk ; \
2007-02-20 03:07:25 +03:00
static DECLARE_WORK ( object # # _led_work , object # # _led_update ) ; \
2007-01-26 16:04:35 +03:00
static struct led_classdev object # # _led = { \
2007-10-31 17:00:07 +03:00
. name = " asus:: " ledname , \
2007-01-26 16:04:35 +03:00
. brightness_set = object # # _led_set , \
2009-08-28 16:56:47 +04:00
. brightness_get = object # # _led_get , \
2009-08-28 16:56:49 +04:00
. max_brightness = max \
2007-01-26 16:04:35 +03:00
}
2009-08-28 16:56:49 +04:00
ASUS_LED ( mled , " mail " , 1 ) ;
ASUS_LED ( tled , " touchpad " , 1 ) ;
ASUS_LED ( rled , " record " , 1 ) ;
ASUS_LED ( pled , " phone " , 1 ) ;
ASUS_LED ( gled , " gaming " , 1 ) ;
2009-08-28 16:56:50 +04:00
ASUS_LED ( kled , " kbd_backlight " , 3 ) ;
2007-01-26 16:04:35 +03:00
2009-01-20 18:17:43 +03:00
struct key_entry {
char type ;
u8 code ;
u16 keycode ;
} ;
enum { KE_KEY , KE_END } ;
static struct key_entry asus_keymap [ ] = {
2009-12-03 10:45:11 +03:00
{ KE_KEY , 0x02 , KEY_SCREENLOCK } ,
{ KE_KEY , 0x05 , KEY_WLAN } ,
{ KE_KEY , 0x08 , KEY_F13 } ,
{ KE_KEY , 0x17 , KEY_ZOOM } ,
{ KE_KEY , 0x1f , KEY_BATTERY } ,
2009-01-20 18:17:43 +03:00
{ KE_KEY , 0x30 , KEY_VOLUMEUP } ,
{ KE_KEY , 0x31 , KEY_VOLUMEDOWN } ,
{ KE_KEY , 0x32 , KEY_MUTE } ,
{ KE_KEY , 0x33 , KEY_SWITCHVIDEOMODE } ,
{ KE_KEY , 0x34 , KEY_SWITCHVIDEOMODE } ,
{ KE_KEY , 0x40 , KEY_PREVIOUSSONG } ,
{ KE_KEY , 0x41 , KEY_NEXTSONG } ,
2009-04-27 11:23:42 +04:00
{ KE_KEY , 0x43 , KEY_STOPCD } ,
2009-01-20 18:17:43 +03:00
{ KE_KEY , 0x45 , KEY_PLAYPAUSE } ,
2009-08-28 16:56:52 +04:00
{ KE_KEY , 0x4c , KEY_MEDIA } ,
2009-01-20 18:17:43 +03:00
{ KE_KEY , 0x50 , KEY_EMAIL } ,
{ KE_KEY , 0x51 , KEY_WWW } ,
2009-08-28 16:56:53 +04:00
{ KE_KEY , 0x55 , KEY_CALC } ,
2009-04-27 11:23:42 +04:00
{ KE_KEY , 0x5C , KEY_SCREENLOCK } , /* Screenlock */
2009-01-20 18:17:43 +03:00
{ KE_KEY , 0x5D , KEY_WLAN } ,
2009-08-28 16:56:48 +04:00
{ KE_KEY , 0x5E , KEY_WLAN } ,
{ KE_KEY , 0x5F , KEY_WLAN } ,
{ KE_KEY , 0x60 , KEY_SWITCHVIDEOMODE } ,
2009-01-20 18:17:43 +03:00
{ KE_KEY , 0x61 , KEY_SWITCHVIDEOMODE } ,
2009-12-03 10:45:11 +03:00
{ KE_KEY , 0x62 , KEY_SWITCHVIDEOMODE } ,
{ KE_KEY , 0x63 , KEY_SWITCHVIDEOMODE } ,
2009-12-03 10:45:10 +03:00
{ KE_KEY , 0x6B , KEY_F13 } , /* Lock Touchpad */
2009-01-20 18:17:43 +03:00
{ KE_KEY , 0x82 , KEY_CAMERA } ,
2009-12-03 10:45:12 +03:00
{ KE_KEY , 0x88 , KEY_WLAN } ,
2009-04-27 11:23:42 +04:00
{ KE_KEY , 0x8A , KEY_PROG1 } ,
2009-01-20 18:17:43 +03:00
{ KE_KEY , 0x95 , KEY_MEDIA } ,
{ KE_KEY , 0x99 , KEY_PHONE } ,
2009-08-28 16:56:51 +04:00
{ KE_KEY , 0xc4 , KEY_KBDILLUMUP } ,
{ KE_KEY , 0xc5 , KEY_KBDILLUMDOWN } ,
2009-01-20 18:17:43 +03:00
{ KE_END , 0 } ,
} ;
2007-01-26 16:04:30 +03:00
/*
* This function evaluates an ACPI method , given an int as parameter , the
* method is searched within the scope of the handle , can be NULL . The output
* of the method is written is output , which can also be NULL
*
2008-01-16 18:56:42 +03:00
* returns 0 if write is successful , - 1 else .
2007-01-26 16:04:30 +03:00
*/
2009-11-28 12:27:51 +03:00
static int write_acpi_int_ret ( acpi_handle handle , const char * method , int val ,
struct acpi_buffer * output )
2007-01-26 16:04:30 +03:00
{
2009-08-29 12:28:29 +04:00
struct acpi_object_list params ; /* list of input parameters (an int) */
union acpi_object in_obj ; /* the only param we use */
2007-01-26 16:04:30 +03:00
acpi_status status ;
2008-01-16 18:56:42 +03:00
if ( ! handle )
return 0 ;
2007-01-26 16:04:30 +03:00
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 ) ;
2008-01-16 18:56:42 +03:00
if ( status = = AE_OK )
return 0 ;
else
return - 1 ;
2007-01-26 16:04:30 +03:00
}
2009-11-28 12:27:51 +03:00
static int write_acpi_int ( acpi_handle handle , const char * method , int val )
{
return write_acpi_int_ret ( handle , method , val , NULL ) ;
}
2007-01-30 09:46:43 +03:00
static int read_wireless_status ( int mask )
{
2008-10-10 10:22:59 +04:00
unsigned long long status ;
2007-03-11 12:25:38 +03:00
acpi_status rv = AE_OK ;
2007-01-26 16:04:40 +03:00
if ( ! wireless_status_handle )
2009-11-30 23:55:12 +03:00
return ( asus - > status & mask ) ? 1 : 0 ;
2007-01-26 16:04:40 +03:00
2007-03-11 12:25:38 +03:00
rv = acpi_evaluate_integer ( wireless_status_handle , NULL , NULL , & status ) ;
if ( ACPI_FAILURE ( rv ) )
2009-06-19 16:52:03 +04:00
pr_warning ( " Error reading Wireless status \n " ) ;
2007-03-11 12:25:38 +03:00
else
return ( status & mask ) ? 1 : 0 ;
2007-01-26 16:04:40 +03:00
2009-11-30 23:55:12 +03:00
return ( asus - > status & mask ) ? 1 : 0 ;
2007-01-26 16:04:40 +03:00
}
2007-05-06 16:48:22 +04:00
static int read_gps_status ( void )
{
2008-10-10 10:22:59 +04:00
unsigned long long status ;
2007-05-06 16:47:06 +04:00
acpi_status rv = AE_OK ;
rv = acpi_evaluate_integer ( gps_status_handle , NULL , NULL , & status ) ;
if ( ACPI_FAILURE ( rv ) )
2009-06-19 16:52:03 +04:00
pr_warning ( " Error reading GPS status \n " ) ;
2007-05-06 16:47:06 +04:00
else
2007-05-06 16:48:22 +04:00
return status ? 1 : 0 ;
2007-05-06 16:47:06 +04:00
2009-11-30 23:55:12 +03:00
return ( asus - > status & GPS_ON ) ? 1 : 0 ;
2007-05-06 16:47:06 +04:00
}
2007-01-26 16:04:35 +03:00
/* Generic LED functions */
static int read_status ( int mask )
{
2007-01-26 16:04:40 +03:00
/* There is a special method for both wireless devices */
if ( mask = = BT_ON | | mask = = WL_ON )
return read_wireless_status ( mask ) ;
2007-05-06 16:48:22 +04:00
else if ( mask = = GPS_ON )
return read_gps_status ( ) ;
2007-01-26 16:04:40 +03:00
2009-11-30 23:55:12 +03:00
return ( asus - > status & mask ) ? 1 : 0 ;
2007-01-26 16:04:35 +03:00
}
2007-03-11 12:26:12 +03:00
static void write_status ( acpi_handle handle , int out , int mask )
2007-01-26 16:04:35 +03:00
{
2009-11-30 23:55:12 +03:00
asus - > status = ( out ) ? ( asus - > status | mask ) : ( asus - > status & ~ mask ) ;
2007-01-26 16:04:35 +03:00
2007-03-11 12:26:12 +03:00
switch ( mask ) {
case MLED_ON :
2008-02-02 23:07:38 +03:00
out = ! ( out & 0x1 ) ;
2007-03-11 12:26:12 +03:00
break ;
2007-03-11 12:26:48 +03:00
case GLED_ON :
out = ( out & 0x1 ) + 1 ;
break ;
2007-05-06 16:47:06 +04:00
case GPS_ON :
handle = ( out ) ? gps_on_handle : gps_off_handle ;
out = 0x02 ;
break ;
2007-03-11 12:26:12 +03:00
default :
out & = 0x1 ;
break ;
}
2007-01-26 16:04:35 +03:00
2009-11-28 12:27:51 +03:00
if ( write_acpi_int ( handle , NULL , out ) )
2009-06-19 16:52:03 +04:00
pr_warning ( " write failed %x \n " , mask ) ;
2007-01-26 16:04:35 +03:00
}
/* /sys/class/led handlers */
2007-03-11 12:26:12 +03:00
# define ASUS_LED_HANDLER(object, mask) \
2007-01-26 16:04:35 +03:00
static void object # # _led_set ( struct led_classdev * led_cdev , \
enum led_brightness value ) \
{ \
2008-09-24 12:35:55 +04:00
object # # _led_wk = ( value > 0 ) ? 1 : 0 ; \
2007-01-26 16:04:35 +03:00
queue_work ( led_workqueue , & object # # _led_work ) ; \
} \
static void object # # _led_update ( struct work_struct * ignored ) \
{ \
int value = object # # _led_wk ; \
2007-03-11 12:26:12 +03:00
write_status ( object # # _set_handle , value , ( mask ) ) ; \
2009-08-28 16:56:47 +04:00
} \
static enum led_brightness object # # _led_get ( \
struct led_classdev * led_cdev ) \
{ \
return led_cdev - > brightness ; \
2007-01-26 16:04:35 +03:00
}
2007-03-11 12:26:12 +03:00
ASUS_LED_HANDLER ( mled , MLED_ON ) ;
ASUS_LED_HANDLER ( pled , PLED_ON ) ;
ASUS_LED_HANDLER ( rled , RLED_ON ) ;
ASUS_LED_HANDLER ( tled , TLED_ON ) ;
2007-03-11 12:26:48 +03:00
ASUS_LED_HANDLER ( gled , GLED_ON ) ;
2007-01-26 16:04:35 +03:00
2009-08-28 16:56:50 +04:00
/*
* Keyboard backlight
*/
static int get_kled_lvl ( void )
{
unsigned long long kblv ;
struct acpi_object_list params ;
union acpi_object in_obj ;
acpi_status rv ;
params . count = 1 ;
params . pointer = & in_obj ;
in_obj . type = ACPI_TYPE_INTEGER ;
in_obj . integer . value = 2 ;
rv = acpi_evaluate_integer ( kled_get_handle , NULL , & params , & kblv ) ;
if ( ACPI_FAILURE ( rv ) ) {
pr_warning ( " Error reading kled level \n " ) ;
return 0 ;
}
return kblv ;
}
static int set_kled_lvl ( int kblv )
{
if ( kblv > 0 )
kblv = ( 1 < < 7 ) | ( kblv & 0x7F ) ;
else
kblv = 0 ;
2009-11-28 12:27:51 +03:00
if ( write_acpi_int ( kled_set_handle , NULL , kblv ) ) {
2009-08-28 16:56:50 +04:00
pr_warning ( " Keyboard LED display write failed \n " ) ;
return - EINVAL ;
}
return 0 ;
}
static void kled_led_set ( struct led_classdev * led_cdev ,
enum led_brightness value )
{
kled_led_wk = value ;
queue_work ( led_workqueue , & kled_led_work ) ;
}
static void kled_led_update ( struct work_struct * ignored )
{
set_kled_lvl ( kled_led_wk ) ;
}
static enum led_brightness kled_led_get ( struct led_classdev * led_cdev )
{
return get_kled_lvl ( ) ;
}
2007-01-26 16:04:45 +03:00
static int get_lcd_state ( void )
{
return read_status ( LCD_ON ) ;
}
static int set_lcd_state ( int value )
{
int lcd = 0 ;
acpi_status status = 0 ;
lcd = value ? 1 : 0 ;
if ( lcd = = get_lcd_state ( ) )
return 0 ;
2007-01-30 09:46:43 +03:00
if ( lcd_switch_handle ) {
2007-01-26 16:04:45 +03:00
status = acpi_evaluate_object ( lcd_switch_handle ,
NULL , NULL , NULL ) ;
if ( ACPI_FAILURE ( status ) )
2009-06-19 16:52:03 +04:00
pr_warning ( " Error switching LCD \n " ) ;
2007-01-26 16:04:45 +03:00
}
2007-03-11 12:26:12 +03:00
write_status ( NULL , lcd , LCD_ON ) ;
2007-01-26 16:04:45 +03:00
return 0 ;
}
static void lcd_blank ( int blank )
{
2009-12-01 00:13:54 +03:00
struct backlight_device * bd = asus - > backlight_device ;
2007-01-26 16:04:45 +03:00
2007-01-30 09:46:43 +03:00
if ( bd ) {
2007-02-11 02:07:48 +03:00
bd - > props . power = blank ;
2007-02-09 01:25:09 +03:00
backlight_update_status ( bd ) ;
2007-01-26 16:04:45 +03:00
}
}
static int read_brightness ( struct backlight_device * bd )
{
2008-10-10 10:22:59 +04:00
unsigned long long value ;
2007-03-11 12:25:38 +03:00
acpi_status rv = AE_OK ;
2007-01-26 16:04:45 +03:00
2007-03-11 12:25:38 +03:00
rv = acpi_evaluate_integer ( brightness_get_handle , NULL , NULL , & value ) ;
if ( ACPI_FAILURE ( rv ) )
2009-06-19 16:52:03 +04:00
pr_warning ( " Error reading brightness \n " ) ;
2007-01-26 16:04:45 +03:00
return value ;
}
static int set_brightness ( struct backlight_device * bd , int value )
{
2009-11-28 12:27:51 +03:00
if ( write_acpi_int ( brightness_set_handle , NULL , value ) ) {
2009-06-19 16:52:03 +04:00
pr_warning ( " Error changing brightness \n " ) ;
2009-11-28 12:19:55 +03:00
return - EIO ;
2007-01-26 16:04:45 +03:00
}
2009-11-28 12:19:55 +03:00
return 0 ;
2007-01-26 16:04:45 +03:00
}
static int update_bl_status ( struct backlight_device * bd )
{
int rv ;
2007-02-11 02:07:48 +03:00
int value = bd - > props . brightness ;
2007-01-26 16:04:45 +03:00
rv = set_brightness ( bd , value ) ;
2007-01-30 09:46:43 +03:00
if ( rv )
2007-01-26 16:04:45 +03:00
return rv ;
2007-02-11 02:07:48 +03:00
value = ( bd - > props . power = = FB_BLANK_UNBLANK ) ? 1 : 0 ;
2007-01-26 16:04:45 +03:00
return set_lcd_state ( value ) ;
}
2007-01-26 16:04:30 +03:00
/*
* Platform device handlers
*/
/*
* We write our info in page , we begin at offset off and cannot write more
* than count bytes . We set eof to 1 if we handle those 2 values . We return the
* number of bytes written in page
*/
static ssize_t show_infos ( struct device * dev ,
2007-01-30 09:46:43 +03:00
struct device_attribute * attr , char * page )
2007-01-26 16:04:30 +03:00
{
int len = 0 ;
2008-10-10 10:22:59 +04:00
unsigned long long temp ;
2009-08-29 12:28:29 +04:00
char buf [ 16 ] ; /* enough for all info */
2007-03-11 12:25:38 +03:00
acpi_status rv = AE_OK ;
2007-01-26 16:04:30 +03:00
/*
* We use the easy way , we don ' t care of off and count , so we don ' t set eof
* to 1
*/
2009-11-30 23:55:12 +03:00
len + = sprintf ( page , ASUS_LAPTOP_NAME " " ASUS_LAPTOP_VERSION " \n " ) ;
len + = sprintf ( page + len , " Model reference : %s \n " , asus - > name ) ;
2007-01-26 16:04:30 +03:00
/*
* The SFUN method probably allows the original driver to get the list
* of features supported by a given model . For now , 0x0100 or 0x0800
* bit signifies that the laptop is equipped with a Wi - Fi MiniPCI card .
* The significance of others is yet to be found .
*/
2009-11-30 23:55:12 +03:00
rv = acpi_evaluate_integer ( asus - > handle , " SFUN " , NULL , & temp ) ;
2007-03-11 12:25:38 +03:00
if ( ! ACPI_FAILURE ( rv ) )
2009-08-28 16:56:46 +04:00
len + = sprintf ( page + len , " SFUN value : %#x \n " ,
( uint ) temp ) ;
/*
* The HWRS method return informations about the hardware .
* 0x80 bit is for WLAN , 0x100 for Bluetooth .
* The significance of others is yet to be found .
* If we don ' t find the method , we assume the device are present .
*/
2009-11-30 23:55:12 +03:00
rv = acpi_evaluate_integer ( asus - > handle , " HRWS " , NULL , & temp ) ;
2009-08-28 16:56:46 +04:00
if ( ! ACPI_FAILURE ( rv ) )
len + = sprintf ( page + len , " HRWS value : %#x \n " ,
2007-03-11 12:25:38 +03:00
( uint ) temp ) ;
2007-01-26 16:04:30 +03:00
/*
* Another value for userspace : the ASYM method returns 0x02 for
* battery low and 0x04 for battery critical , its readings tend to be
* more accurate than those provided by _BST .
* Note : since not all the laptops provide this method , errors are
* silently ignored .
*/
2009-11-30 23:55:12 +03:00
rv = acpi_evaluate_integer ( asus - > handle , " ASYM " , NULL , & temp ) ;
2007-03-11 12:25:38 +03:00
if ( ! ACPI_FAILURE ( rv ) )
2009-08-28 16:56:46 +04:00
len + = sprintf ( page + len , " ASYM value : %#x \n " ,
2007-03-11 12:25:38 +03:00
( uint ) temp ) ;
2009-12-01 00:13:54 +03:00
if ( asus - > dsdt_info ) {
snprintf ( buf , 16 , " %d " , asus - > dsdt_info - > length ) ;
2007-01-26 16:04:30 +03:00
len + = sprintf ( page + len , " DSDT length : %s \n " , buf ) ;
2009-12-01 00:13:54 +03:00
snprintf ( buf , 16 , " %d " , asus - > dsdt_info - > checksum ) ;
2007-01-26 16:04:30 +03:00
len + = sprintf ( page + len , " DSDT checksum : %s \n " , buf ) ;
2009-12-01 00:13:54 +03:00
snprintf ( buf , 16 , " %d " , asus - > dsdt_info - > revision ) ;
2007-01-26 16:04:30 +03:00
len + = sprintf ( page + len , " DSDT revision : %s \n " , buf ) ;
2009-12-01 00:13:54 +03:00
snprintf ( buf , 7 , " %s " , asus - > dsdt_info - > oem_id ) ;
2007-01-26 16:04:30 +03:00
len + = sprintf ( page + len , " OEM id : %s \n " , buf ) ;
2009-12-01 00:13:54 +03:00
snprintf ( buf , 9 , " %s " , asus - > dsdt_info - > oem_table_id ) ;
2007-01-26 16:04:30 +03:00
len + = sprintf ( page + len , " OEM table id : %s \n " , buf ) ;
2009-12-01 00:13:54 +03:00
snprintf ( buf , 16 , " %x " , asus - > dsdt_info - > oem_revision ) ;
2007-01-26 16:04:30 +03:00
len + = sprintf ( page + len , " OEM revision : 0x%s \n " , buf ) ;
2009-12-01 00:13:54 +03:00
snprintf ( buf , 5 , " %s " , asus - > dsdt_info - > asl_compiler_id ) ;
2007-01-26 16:04:30 +03:00
len + = sprintf ( page + len , " ASL comp vendor id : %s \n " , buf ) ;
2009-12-01 00:13:54 +03:00
snprintf ( buf , 16 , " %x " , asus - > dsdt_info - > asl_compiler_revision ) ;
2007-01-26 16:04:30 +03:00
len + = sprintf ( page + len , " ASL comp revision : 0x%s \n " , buf ) ;
}
return len ;
}
static int parse_arg ( const char * buf , unsigned long count , int * val )
{
if ( ! count )
return 0 ;
if ( count > 31 )
return - EINVAL ;
if ( sscanf ( buf , " %i " , val ) ! = 1 )
return - EINVAL ;
return count ;
}
2007-01-26 16:04:40 +03:00
static ssize_t store_status ( const char * buf , size_t count ,
2007-03-11 12:26:12 +03:00
acpi_handle handle , int mask )
2007-01-26 16:04:40 +03:00
{
int rv , value ;
int out = 0 ;
rv = parse_arg ( buf , count , & value ) ;
if ( rv > 0 )
out = value ? 1 : 0 ;
2007-03-11 12:26:12 +03:00
write_status ( handle , out , mask ) ;
2007-01-26 16:04:40 +03:00
return rv ;
}
2007-01-26 16:04:55 +03:00
/*
* LEDD display
*/
static ssize_t show_ledd ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
2009-11-30 23:55:12 +03:00
return sprintf ( buf , " 0x%08x \n " , asus - > ledd_status ) ;
2007-01-26 16:04:55 +03:00
}
static ssize_t store_ledd ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
{
int rv , value ;
rv = parse_arg ( buf , count , & value ) ;
if ( rv > 0 ) {
2009-11-28 12:27:51 +03:00
if ( write_acpi_int ( ledd_set_handle , NULL , value ) )
2009-06-19 16:52:03 +04:00
pr_warning ( " LED display write failed \n " ) ;
2007-01-26 16:04:55 +03:00
else
2009-11-30 23:55:12 +03:00
asus - > ledd_status = ( u32 ) value ;
2007-01-26 16:04:55 +03:00
}
return rv ;
}
2007-01-26 16:04:40 +03:00
/*
* WLAN
*/
static ssize_t show_wlan ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
return sprintf ( buf , " %d \n " , read_status ( WL_ON ) ) ;
}
static ssize_t store_wlan ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
{
2007-03-11 12:26:12 +03:00
return store_status ( buf , count , wl_switch_handle , WL_ON ) ;
2007-01-26 16:04:40 +03:00
}
/*
* Bluetooth
*/
static ssize_t show_bluetooth ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
return sprintf ( buf , " %d \n " , read_status ( BT_ON ) ) ;
}
2007-01-30 09:46:43 +03:00
static ssize_t store_bluetooth ( struct device * dev ,
struct device_attribute * attr , const char * buf ,
size_t count )
2007-01-26 16:04:40 +03:00
{
2007-03-11 12:26:12 +03:00
return store_status ( buf , count , bt_switch_handle , BT_ON ) ;
2007-01-26 16:04:40 +03:00
}
2007-01-26 16:04:49 +03:00
/*
* Display
*/
static void set_display ( int value )
{
/* no sanity check needed for now */
2009-11-28 12:27:51 +03:00
if ( write_acpi_int ( display_set_handle , NULL , value ) )
2009-06-19 16:52:03 +04:00
pr_warning ( " Error setting display \n " ) ;
2007-01-26 16:04:49 +03:00
return ;
}
static int read_display ( void )
{
2008-10-10 10:22:59 +04:00
unsigned long long value = 0 ;
2007-03-11 12:25:38 +03:00
acpi_status rv = AE_OK ;
2007-01-26 16:04:49 +03:00
2009-08-29 12:28:29 +04:00
/*
* In most of the case , we know how to set the display , but sometime
* we can ' t read it
*/
2007-01-30 09:46:43 +03:00
if ( display_get_handle ) {
2007-03-11 12:25:38 +03:00
rv = acpi_evaluate_integer ( display_get_handle , NULL ,
NULL , & value ) ;
if ( ACPI_FAILURE ( rv ) )
2009-06-19 16:52:03 +04:00
pr_warning ( " Error reading display status \n " ) ;
2007-01-26 16:04:49 +03:00
}
value & = 0x0F ; /* needed for some models, shouldn't hurt others */
return value ;
}
2007-01-30 09:46:43 +03:00
2007-01-26 16:04:49 +03:00
/*
* Now , * this * one could be more user - friendly , but so far , no - one has
* complained . The significance of bits is the same as in store_disp ( )
*/
static ssize_t show_disp ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
return sprintf ( buf , " %d \n " , read_display ( ) ) ;
}
/*
* Experimental support for display switching . As of now : 1 should activate
* the LCD output , 2 should do for CRT , 4 for TV - Out and 8 for DVI .
* Any combination ( bitwise ) of these will suffice . I never actually tested 4
* displays hooked up simultaneously , so be warned . See the acpi4asus README
* for more info .
*/
static ssize_t store_disp ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
{
int rv , value ;
rv = parse_arg ( buf , count , & value ) ;
if ( rv > 0 )
set_display ( value ) ;
return rv ;
}
2007-01-26 16:04:58 +03:00
/*
* Light Sens
*/
static void set_light_sens_switch ( int value )
{
2009-11-28 12:27:51 +03:00
if ( write_acpi_int ( ls_switch_handle , NULL , value ) )
2009-06-19 16:52:03 +04:00
pr_warning ( " Error setting light sensor switch \n " ) ;
2009-11-30 23:55:12 +03:00
asus - > light_switch = value ;
2007-01-26 16:04:58 +03:00
}
static ssize_t show_lssw ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
2009-11-30 23:55:12 +03:00
return sprintf ( buf , " %d \n " , asus - > light_switch ) ;
2007-01-26 16:04:58 +03:00
}
static ssize_t store_lssw ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
{
int rv , value ;
rv = parse_arg ( buf , count , & value ) ;
if ( rv > 0 )
set_light_sens_switch ( value ? 1 : 0 ) ;
return rv ;
}
static void set_light_sens_level ( int value )
{
2009-11-28 12:27:51 +03:00
if ( write_acpi_int ( ls_level_handle , NULL , value ) )
2009-06-19 16:52:03 +04:00
pr_warning ( " Error setting light sensor level \n " ) ;
2009-11-30 23:55:12 +03:00
asus - > light_level = value ;
2007-01-26 16:04:58 +03:00
}
static ssize_t show_lslvl ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
2009-11-30 23:55:12 +03:00
return sprintf ( buf , " %d \n " , asus - > light_level ) ;
2007-01-26 16:04:58 +03:00
}
static ssize_t store_lslvl ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
{
int rv , value ;
rv = parse_arg ( buf , count , & value ) ;
if ( rv > 0 ) {
value = ( 0 < value ) ? ( ( 15 < value ) ? 15 : value ) : 0 ;
/* 0 <= value <= 15 */
set_light_sens_level ( value ) ;
}
return rv ;
}
2007-05-06 16:47:06 +04:00
/*
* GPS
*/
static ssize_t show_gps ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
return sprintf ( buf , " %d \n " , read_status ( GPS_ON ) ) ;
}
static ssize_t store_gps ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
{
2007-05-06 16:48:22 +04:00
return store_status ( buf , count , NULL , GPS_ON ) ;
2007-05-06 16:47:06 +04:00
}
2009-01-20 18:17:43 +03:00
/*
* Hotkey functions
*/
static struct key_entry * asus_get_entry_by_scancode ( int code )
{
struct key_entry * key ;
for ( key = asus_keymap ; key - > type ! = KE_END ; key + + )
if ( code = = key - > code )
return key ;
return NULL ;
}
static struct key_entry * asus_get_entry_by_keycode ( int code )
{
struct key_entry * key ;
for ( key = asus_keymap ; key - > type ! = KE_END ; key + + )
if ( code = = key - > keycode & & key - > type = = KE_KEY )
return key ;
return NULL ;
}
static int asus_getkeycode ( struct input_dev * dev , int scancode , int * keycode )
{
struct key_entry * key = asus_get_entry_by_scancode ( scancode ) ;
if ( key & & key - > type = = KE_KEY ) {
* keycode = key - > keycode ;
return 0 ;
}
return - EINVAL ;
}
static int asus_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 = asus_get_entry_by_scancode ( scancode ) ;
if ( key & & key - > type = = KE_KEY ) {
old_keycode = key - > keycode ;
key - > keycode = keycode ;
set_bit ( keycode , dev - > keybit ) ;
if ( ! asus_get_entry_by_keycode ( old_keycode ) )
clear_bit ( old_keycode , dev - > keybit ) ;
return 0 ;
}
return - EINVAL ;
}
2009-11-30 23:42:42 +03:00
static void asus_acpi_notify ( struct acpi_device * device , u32 event )
2007-01-26 16:04:30 +03:00
{
2009-01-20 18:17:43 +03:00
static struct key_entry * key ;
2009-02-15 21:30:19 +03:00
u16 count ;
2009-01-20 18:17:43 +03:00
2007-01-26 16:04:45 +03:00
/*
* We need to tell the backlight device when the backlight power is
* switched
*/
if ( event = = ATKD_LCD_ON ) {
2007-03-11 12:26:12 +03:00
write_status ( NULL , 1 , LCD_ON ) ;
2007-01-26 16:04:45 +03:00
lcd_blank ( FB_BLANK_UNBLANK ) ;
2007-01-30 09:46:43 +03:00
} else if ( event = = ATKD_LCD_OFF ) {
2007-03-11 12:26:12 +03:00
write_status ( NULL , 0 , LCD_ON ) ;
2007-01-26 16:04:45 +03:00
lcd_blank ( FB_BLANK_POWERDOWN ) ;
}
2009-11-28 12:35:37 +03:00
/* TODO Find a better way to handle events count. */
2009-11-30 23:55:12 +03:00
count = asus - > event_count [ event % 128 ] + + ;
acpi_bus_generate_proc_event ( asus - > device , event , count ) ;
acpi_bus_generate_netlink_event ( asus - > device - > pnp . device_class ,
dev_name ( & asus - > device - > dev ) , event ,
2009-02-15 21:30:19 +03:00
count ) ;
2007-01-26 16:04:30 +03:00
2009-11-30 23:55:12 +03:00
if ( asus - > inputdev ) {
2009-01-20 18:17:43 +03:00
key = asus_get_entry_by_scancode ( event ) ;
if ( ! key )
return ;
switch ( key - > type ) {
case KE_KEY :
2009-11-30 23:55:12 +03:00
input_report_key ( asus - > inputdev , key - > keycode , 1 ) ;
input_sync ( asus - > inputdev ) ;
input_report_key ( asus - > inputdev , key - > keycode , 0 ) ;
input_sync ( asus - > inputdev ) ;
2009-01-20 18:17:43 +03:00
break ;
}
}
2007-01-26 16:04:30 +03:00
}
# define ASUS_CREATE_DEVICE_ATTR(_name) \
struct device_attribute dev_attr_ # # _name = { \
. attr = { \
. name = __stringify ( _name ) , \
2007-06-13 22:45:17 +04:00
. mode = 0 } , \
2007-01-26 16:04:30 +03:00
. show = NULL , \
. store = NULL , \
}
# define ASUS_SET_DEVICE_ATTR(_name, _mode, _show, _store) \
do { \
dev_attr_ # # _name . attr . mode = _mode ; \
dev_attr_ # # _name . show = _show ; \
dev_attr_ # # _name . store = _store ; \
} while ( 0 )
static ASUS_CREATE_DEVICE_ATTR ( infos ) ;
2007-01-26 16:04:40 +03:00
static ASUS_CREATE_DEVICE_ATTR ( wlan ) ;
static ASUS_CREATE_DEVICE_ATTR ( bluetooth ) ;
2007-01-26 16:04:49 +03:00
static ASUS_CREATE_DEVICE_ATTR ( display ) ;
2007-01-26 16:04:55 +03:00
static ASUS_CREATE_DEVICE_ATTR ( ledd ) ;
2007-01-26 16:04:58 +03:00
static ASUS_CREATE_DEVICE_ATTR ( ls_switch ) ;
static ASUS_CREATE_DEVICE_ATTR ( ls_level ) ;
2007-05-06 16:47:06 +04:00
static ASUS_CREATE_DEVICE_ATTR ( gps ) ;
2007-01-26 16:04:30 +03:00
static struct attribute * asuspf_attributes [ ] = {
2007-01-30 09:46:43 +03:00
& dev_attr_infos . attr ,
& dev_attr_wlan . attr ,
& dev_attr_bluetooth . attr ,
& dev_attr_display . attr ,
& dev_attr_ledd . attr ,
& dev_attr_ls_switch . attr ,
& dev_attr_ls_level . attr ,
2007-05-06 16:47:06 +04:00
& dev_attr_gps . attr ,
2007-01-30 09:46:43 +03:00
NULL
2007-01-26 16:04:30 +03:00
} ;
2009-11-30 23:42:42 +03:00
static struct attribute_group platform_attribute_group = {
2007-01-30 09:46:43 +03:00
. attrs = asuspf_attributes
2007-01-26 16:04:30 +03:00
} ;
2009-11-30 23:42:42 +03:00
static int asus_platform_init ( void )
{
int result ;
2009-11-30 23:55:12 +03:00
asus - > platform_device = platform_device_alloc ( ASUS_LAPTOP_FILE , - 1 ) ;
if ( ! asus - > platform_device )
2009-11-30 23:42:42 +03:00
return - ENOMEM ;
2009-11-30 23:55:12 +03:00
result = platform_device_add ( asus - > platform_device ) ;
2009-11-30 23:42:42 +03:00
if ( result )
goto fail_platform_device ;
2009-11-30 23:55:12 +03:00
result = sysfs_create_group ( & asus - > platform_device - > dev . kobj ,
2009-11-30 23:42:42 +03:00
& platform_attribute_group ) ;
if ( result )
goto fail_sysfs ;
return 0 ;
fail_sysfs :
2009-11-30 23:55:12 +03:00
platform_device_del ( asus - > platform_device ) ;
2009-11-30 23:42:42 +03:00
fail_platform_device :
2009-11-30 23:55:12 +03:00
platform_device_put ( asus - > platform_device ) ;
2009-11-30 23:42:42 +03:00
return result ;
}
static void asus_platform_exit ( void )
{
2009-11-30 23:55:12 +03:00
sysfs_remove_group ( & asus - > platform_device - > dev . kobj ,
2009-11-30 23:42:42 +03:00
& platform_attribute_group ) ;
2009-11-30 23:55:12 +03:00
platform_device_unregister ( asus - > platform_device ) ;
2009-11-30 23:42:42 +03:00
}
static struct platform_driver platform_driver = {
2007-01-30 09:46:43 +03:00
. driver = {
2009-11-30 23:55:12 +03:00
. name = ASUS_LAPTOP_FILE ,
2007-01-30 09:46:43 +03:00
. owner = THIS_MODULE ,
}
2007-01-26 16:04:30 +03:00
} ;
2009-11-30 23:55:12 +03:00
static void asus_laptop_add_fs ( void )
2007-01-26 16:04:30 +03:00
{
ASUS_SET_DEVICE_ATTR ( infos , 0444 , show_infos , NULL ) ;
2007-01-26 16:04:40 +03:00
if ( wl_switch_handle )
ASUS_SET_DEVICE_ATTR ( wlan , 0644 , show_wlan , store_wlan ) ;
if ( bt_switch_handle )
ASUS_SET_DEVICE_ATTR ( bluetooth , 0644 ,
show_bluetooth , store_bluetooth ) ;
2007-01-26 16:04:49 +03:00
if ( display_set_handle & & display_get_handle )
ASUS_SET_DEVICE_ATTR ( display , 0644 , show_disp , store_disp ) ;
2007-01-30 09:46:43 +03:00
else if ( display_set_handle )
2007-01-26 16:04:49 +03:00
ASUS_SET_DEVICE_ATTR ( display , 0200 , NULL , store_disp ) ;
2007-01-26 16:04:55 +03:00
if ( ledd_set_handle )
ASUS_SET_DEVICE_ATTR ( ledd , 0644 , show_ledd , store_ledd ) ;
2007-01-26 16:04:58 +03:00
if ( ls_switch_handle & & ls_level_handle ) {
ASUS_SET_DEVICE_ATTR ( ls_level , 0644 , show_lslvl , store_lslvl ) ;
ASUS_SET_DEVICE_ATTR ( ls_switch , 0644 , show_lssw , store_lssw ) ;
}
2007-05-06 16:47:06 +04:00
2007-05-06 16:48:22 +04:00
if ( gps_status_handle & & gps_on_handle & & gps_off_handle )
2007-05-06 16:47:06 +04:00
ASUS_SET_DEVICE_ATTR ( gps , 0644 , show_gps , store_gps ) ;
2007-01-26 16:04:30 +03:00
}
2007-01-30 09:46:43 +03:00
static int asus_handle_init ( char * name , acpi_handle * handle ,
2007-01-26 16:04:30 +03:00
char * * paths , int num_paths )
{
int i ;
acpi_status status ;
for ( i = 0 ; i < num_paths ; i + + ) {
status = acpi_get_handle ( NULL , paths [ i ] , handle ) ;
if ( ACPI_SUCCESS ( status ) )
return 0 ;
}
* handle = NULL ;
return - ENODEV ;
}
# define ASUS_HANDLE_INIT(object) \
asus_handle_init ( # object , & object # # _handle , object # # _paths , \
ARRAY_SIZE ( object # # _paths ) )
/*
2009-11-30 23:55:12 +03:00
* This function is used to initialize the context with right values . In this
* method , we can make all the detection we want , and modify the asus_laptop
* struct
2007-01-26 16:04:30 +03:00
*/
2009-12-01 00:13:54 +03:00
static int asus_laptop_get_info ( struct asus_laptop * asus )
2007-01-26 16:04:30 +03:00
{
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER , NULL } ;
union acpi_object * model = NULL ;
2008-10-10 10:22:59 +04:00
unsigned long long bsts_result , hwrs_result ;
2007-01-26 16:04:30 +03:00
char * string = NULL ;
acpi_status status ;
/*
* Get DSDT headers early enough to allow for differentiating between
* models , but late enough to allow acpi_bus_register_driver ( ) to fail
* before doing anything ACPI - specific . Should we encounter a machine ,
* which needs special handling ( i . e . its hotkey device has a different
2009-12-01 00:13:54 +03:00
* HID ) , this bit will be moved .
2007-01-26 16:04:30 +03:00
*/
2009-12-01 00:13:54 +03:00
status = acpi_get_table ( ACPI_SIG_DSDT , 1 , & asus - > dsdt_info ) ;
2007-01-26 16:04:30 +03:00
if ( ACPI_FAILURE ( status ) )
2009-06-19 16:52:03 +04:00
pr_warning ( " Couldn't get the DSDT table header \n " ) ;
2007-01-26 16:04:30 +03:00
/* We have to write 0 on init this far for all ASUS models */
2009-11-30 23:55:12 +03:00
if ( write_acpi_int_ret ( asus - > handle , " INIT " , 0 , & buffer ) ) {
2009-06-19 16:52:03 +04:00
pr_err ( " Hotkey initialization failed \n " ) ;
2007-01-26 16:04:30 +03:00
return - ENODEV ;
}
/* This needs to be called for some laptops to init properly */
2007-03-11 12:25:38 +03:00
status =
2009-11-30 23:55:12 +03:00
acpi_evaluate_integer ( asus - > handle , " BSTS " , NULL , & bsts_result ) ;
2007-03-11 12:25:38 +03:00
if ( ACPI_FAILURE ( status ) )
2009-06-19 16:52:03 +04:00
pr_warning ( " Error calling BSTS \n " ) ;
2007-01-26 16:04:30 +03:00
else if ( bsts_result )
2009-06-19 16:52:03 +04:00
pr_notice ( " BSTS called, 0x%02x returned \n " ,
2007-03-11 12:25:38 +03:00
( uint ) bsts_result ) ;
2007-01-26 16:04:30 +03:00
2007-03-11 12:27:33 +03:00
/* This too ... */
2009-11-30 23:55:12 +03:00
write_acpi_int ( asus - > handle , " CWAP " , wapf ) ;
2007-03-11 12:27:33 +03:00
2007-01-26 16:04:30 +03:00
/*
* Try to match the object returned by INIT to the specific model .
* Handle every possible object ( or the lack of thereof ) the DSDT
* writers might throw at us . When in trouble , we pass NULL to
* asus_model_match ( ) and try something completely different .
*/
if ( buffer . pointer ) {
model = buffer . pointer ;
switch ( model - > type ) {
case ACPI_TYPE_STRING :
string = model - > string . pointer ;
break ;
case ACPI_TYPE_BUFFER :
string = model - > buffer . pointer ;
break ;
default :
string = " " ;
break ;
}
}
2009-11-30 23:55:12 +03:00
asus - > name = kstrdup ( string , GFP_KERNEL ) ;
if ( ! asus - > name )
2007-01-26 16:04:30 +03:00
return - ENOMEM ;
2007-01-30 09:46:43 +03:00
if ( * string )
2009-06-19 16:52:03 +04:00
pr_notice ( " %s model detected \n " , string ) ;
2007-01-26 16:04:30 +03:00
2007-01-26 16:04:35 +03:00
ASUS_HANDLE_INIT ( mled_set ) ;
ASUS_HANDLE_INIT ( tled_set ) ;
ASUS_HANDLE_INIT ( rled_set ) ;
ASUS_HANDLE_INIT ( pled_set ) ;
2007-03-11 12:26:48 +03:00
ASUS_HANDLE_INIT ( gled_set ) ;
2007-01-26 16:04:35 +03:00
2007-01-26 16:04:55 +03:00
ASUS_HANDLE_INIT ( ledd_set ) ;
2009-08-28 16:56:50 +04:00
ASUS_HANDLE_INIT ( kled_set ) ;
ASUS_HANDLE_INIT ( kled_get ) ;
2007-01-26 16:04:40 +03:00
/*
* The HWRS method return informations about the hardware .
* 0x80 bit is for WLAN , 0x100 for Bluetooth .
* The significance of others is yet to be found .
* If we don ' t find the method , we assume the device are present .
*/
2007-03-11 12:25:38 +03:00
status =
2009-11-30 23:55:12 +03:00
acpi_evaluate_integer ( asus - > handle , " HRWS " , NULL , & hwrs_result ) ;
2007-03-11 12:25:38 +03:00
if ( ACPI_FAILURE ( status ) )
2007-01-26 16:04:40 +03:00
hwrs_result = WL_HWRS | BT_HWRS ;
2007-01-30 09:46:43 +03:00
if ( hwrs_result & WL_HWRS )
2007-01-26 16:04:40 +03:00
ASUS_HANDLE_INIT ( wl_switch ) ;
2007-01-30 09:46:43 +03:00
if ( hwrs_result & BT_HWRS )
2007-01-26 16:04:40 +03:00
ASUS_HANDLE_INIT ( bt_switch ) ;
ASUS_HANDLE_INIT ( wireless_status ) ;
2007-01-26 16:04:45 +03:00
ASUS_HANDLE_INIT ( brightness_set ) ;
ASUS_HANDLE_INIT ( brightness_get ) ;
ASUS_HANDLE_INIT ( lcd_switch ) ;
2007-01-26 16:04:49 +03:00
ASUS_HANDLE_INIT ( display_set ) ;
ASUS_HANDLE_INIT ( display_get ) ;
2009-08-29 12:28:29 +04:00
/*
* There is a lot of models with " ALSL " , but a few get
* a real light sens , so we need to check it .
*/
2007-05-06 16:47:29 +04:00
if ( ! ASUS_HANDLE_INIT ( ls_switch ) )
2007-01-26 16:04:58 +03:00
ASUS_HANDLE_INIT ( ls_level ) ;
2007-05-06 16:47:06 +04:00
ASUS_HANDLE_INIT ( gps_on ) ;
ASUS_HANDLE_INIT ( gps_off ) ;
ASUS_HANDLE_INIT ( gps_status ) ;
2007-01-26 16:04:30 +03:00
kfree ( model ) ;
return AE_OK ;
}
2009-11-30 23:42:42 +03:00
static int asus_input_init ( struct device * dev )
2009-01-20 18:17:43 +03:00
{
const struct key_entry * key ;
int result ;
2009-11-30 23:55:12 +03:00
asus - > inputdev = input_allocate_device ( ) ;
if ( ! asus - > inputdev ) {
2009-06-19 16:52:03 +04:00
pr_info ( " Unable to allocate input device \n " ) ;
2009-01-20 18:17:43 +03:00
return 0 ;
}
2009-11-30 23:55:12 +03:00
asus - > inputdev - > name = " Asus Laptop extra buttons " ;
asus - > inputdev - > dev . parent = dev ;
asus - > inputdev - > phys = ASUS_LAPTOP_FILE " /input0 " ;
asus - > inputdev - > id . bustype = BUS_HOST ;
asus - > inputdev - > getkeycode = asus_getkeycode ;
asus - > inputdev - > setkeycode = asus_setkeycode ;
2009-01-20 18:17:43 +03:00
for ( key = asus_keymap ; key - > type ! = KE_END ; key + + ) {
switch ( key - > type ) {
case KE_KEY :
2009-11-30 23:55:12 +03:00
set_bit ( EV_KEY , asus - > inputdev - > evbit ) ;
set_bit ( key - > keycode , asus - > inputdev - > keybit ) ;
2009-01-20 18:17:43 +03:00
break ;
}
}
2009-11-30 23:55:12 +03:00
result = input_register_device ( asus - > inputdev ) ;
2009-01-20 18:17:43 +03:00
if ( result ) {
2009-06-19 16:52:03 +04:00
pr_info ( " Unable to register input device \n " ) ;
2009-11-30 23:55:12 +03:00
input_free_device ( asus - > inputdev ) ;
2009-01-20 18:17:43 +03:00
}
return result ;
}
2007-01-26 16:04:45 +03:00
static void asus_backlight_exit ( void )
{
2009-12-01 00:13:54 +03:00
if ( asus - > backlight_device )
backlight_device_unregister ( asus - > backlight_device ) ;
2007-01-26 16:04:45 +03:00
}
2007-01-26 16:04:35 +03:00
# define ASUS_LED_UNREGISTER(object) \
2007-08-16 20:18:53 +04:00
if ( object # # _led . dev ) \
led_classdev_unregister ( & object # # _led )
2007-01-26 16:04:35 +03:00
static void asus_led_exit ( void )
{
2007-07-23 14:21:34 +04:00
destroy_workqueue ( led_workqueue ) ;
2007-01-26 16:04:35 +03:00
ASUS_LED_UNREGISTER ( mled ) ;
ASUS_LED_UNREGISTER ( tled ) ;
ASUS_LED_UNREGISTER ( pled ) ;
ASUS_LED_UNREGISTER ( rled ) ;
2007-03-11 12:26:48 +03:00
ASUS_LED_UNREGISTER ( gled ) ;
2009-08-28 16:56:50 +04:00
ASUS_LED_UNREGISTER ( kled ) ;
2007-01-26 16:04:35 +03:00
}
2009-01-20 18:17:43 +03:00
static void asus_input_exit ( void )
{
2009-11-30 23:55:12 +03:00
if ( asus - > inputdev )
input_unregister_device ( asus - > inputdev ) ;
2009-01-20 18:17:43 +03:00
}
2007-01-30 09:46:43 +03:00
static int asus_backlight_init ( struct device * dev )
2007-01-26 16:04:45 +03:00
{
struct backlight_device * bd ;
2007-01-30 09:46:43 +03:00
if ( brightness_set_handle & & lcd_switch_handle ) {
2009-11-30 23:55:12 +03:00
bd = backlight_device_register ( ASUS_LAPTOP_FILE , dev ,
2007-02-11 02:07:48 +03:00
NULL , & asusbl_ops ) ;
2007-01-30 09:46:43 +03:00
if ( IS_ERR ( bd ) ) {
2009-06-19 16:52:03 +04:00
pr_err ( " Could not register asus backlight device \n " ) ;
2009-12-01 00:13:54 +03:00
asus - > backlight_device = NULL ;
2007-01-26 16:04:45 +03:00
return PTR_ERR ( bd ) ;
}
2009-12-01 00:13:54 +03:00
asus - > backlight_device = bd ;
2007-01-26 16:04:45 +03:00
2007-02-11 02:07:48 +03:00
bd - > props . max_brightness = 15 ;
bd - > props . brightness = read_brightness ( NULL ) ;
bd - > props . power = FB_BLANK_UNBLANK ;
2007-02-09 01:25:09 +03:00
backlight_update_status ( bd ) ;
2007-01-26 16:04:45 +03:00
}
return 0 ;
}
2007-01-26 16:04:35 +03:00
static int asus_led_register ( acpi_handle handle ,
2007-01-30 09:46:43 +03:00
struct led_classdev * ldev , struct device * dev )
2007-01-26 16:04:35 +03:00
{
2007-01-30 09:46:43 +03:00
if ( ! handle )
2007-01-26 16:04:35 +03:00
return 0 ;
return led_classdev_register ( dev , ldev ) ;
}
2007-01-30 09:46:43 +03:00
2007-01-26 16:04:35 +03:00
# define ASUS_LED_REGISTER(object, device) \
asus_led_register ( object # # _set_handle , & object # # _led , device )
2007-01-30 09:46:43 +03:00
static int asus_led_init ( struct device * dev )
2007-01-26 16:04:35 +03:00
{
int rv ;
rv = ASUS_LED_REGISTER ( mled , dev ) ;
2007-01-30 09:46:43 +03:00
if ( rv )
2007-07-23 14:21:34 +04:00
goto out ;
2007-01-26 16:04:35 +03:00
rv = ASUS_LED_REGISTER ( tled , dev ) ;
2007-01-30 09:46:43 +03:00
if ( rv )
2007-07-23 14:21:34 +04:00
goto out1 ;
2007-01-26 16:04:35 +03:00
rv = ASUS_LED_REGISTER ( rled , dev ) ;
2007-01-30 09:46:43 +03:00
if ( rv )
2007-07-23 14:21:34 +04:00
goto out2 ;
2007-01-26 16:04:35 +03:00
rv = ASUS_LED_REGISTER ( pled , dev ) ;
2007-01-30 09:46:43 +03:00
if ( rv )
2007-07-23 14:21:34 +04:00
goto out3 ;
2007-01-26 16:04:35 +03:00
2007-03-11 12:26:48 +03:00
rv = ASUS_LED_REGISTER ( gled , dev ) ;
if ( rv )
2007-07-23 14:21:34 +04:00
goto out4 ;
2007-03-11 12:26:48 +03:00
2009-08-28 16:56:50 +04:00
if ( kled_set_handle & & kled_get_handle )
rv = ASUS_LED_REGISTER ( kled , dev ) ;
if ( rv )
goto out5 ;
2009-12-01 00:13:54 +03:00
/*
* Functions that actually update the LED ' s are called from a
* workqueue . By doing this as separate work rather than when the LED
* subsystem asks , we avoid messing with the Asus ACPI stuff during a
* potentially bad time , such as a timer interrupt .
*/
2007-01-26 16:04:35 +03:00
led_workqueue = create_singlethread_workqueue ( " led_workqueue " ) ;
2007-01-30 09:46:43 +03:00
if ( ! led_workqueue )
2009-08-28 16:56:50 +04:00
goto out6 ;
2007-01-26 16:04:35 +03:00
return 0 ;
2009-08-28 16:56:50 +04:00
out6 :
2007-07-23 14:21:34 +04:00
rv = - ENOMEM ;
2009-08-28 16:56:50 +04:00
ASUS_LED_UNREGISTER ( kled ) ;
out5 :
2007-07-23 14:21:34 +04:00
ASUS_LED_UNREGISTER ( gled ) ;
out4 :
ASUS_LED_UNREGISTER ( pled ) ;
out3 :
ASUS_LED_UNREGISTER ( rled ) ;
out2 :
ASUS_LED_UNREGISTER ( tled ) ;
out1 :
ASUS_LED_UNREGISTER ( mled ) ;
out :
return rv ;
2007-01-26 16:04:35 +03:00
}
2009-11-30 23:42:42 +03:00
static bool asus_device_present ;
static int __devinit asus_acpi_init ( struct acpi_device * device )
2007-01-26 16:04:30 +03:00
{
2009-11-30 23:42:42 +03:00
int result = 0 ;
2007-01-26 16:04:30 +03:00
2009-11-30 23:55:12 +03:00
result = acpi_bus_get_status ( asus - > device ) ;
2009-11-30 23:42:42 +03:00
if ( result )
2007-01-26 16:04:30 +03:00
return result ;
2009-11-30 23:55:12 +03:00
if ( ! asus - > device - > status . present ) {
2009-11-30 23:42:42 +03:00
pr_err ( " Hotkey device not present, aborting \n " ) ;
2007-01-26 16:04:30 +03:00
return - ENODEV ;
}
2009-12-01 00:13:54 +03:00
result = asus_laptop_get_info ( asus ) ;
2009-01-20 18:17:43 +03:00
if ( result )
2009-11-30 23:42:42 +03:00
return result ;
2009-01-20 18:17:43 +03:00
2009-11-30 23:55:12 +03:00
asus_laptop_add_fs ( ) ;
2007-01-26 16:04:30 +03:00
2009-11-30 23:42:42 +03:00
/* WLED and BLED are on by default */
write_status ( bt_switch_handle , 1 , BT_ON ) ;
write_status ( wl_switch_handle , 1 , WL_ON ) ;
2007-01-26 16:04:30 +03:00
2009-11-30 23:42:42 +03:00
/* If the h/w switch is off, we need to check the real status */
write_status ( NULL , read_status ( BT_ON ) , BT_ON ) ;
write_status ( NULL , read_status ( WL_ON ) , WL_ON ) ;
/* LCD Backlight is on by default */
write_status ( NULL , 1 , LCD_ON ) ;
2007-01-26 16:04:30 +03:00
2009-11-30 23:42:42 +03:00
/* Keyboard Backlight is on by default */
if ( kled_set_handle )
set_kled_lvl ( 1 ) ;
/* LED display is off by default */
2009-11-30 23:55:12 +03:00
asus - > ledd_status = 0xFFF ;
2009-11-30 23:42:42 +03:00
/* Set initial values of light sensor and level */
hotk - > light_switch = 0 ; /* Default to light sensor disabled */
hotk - > light_level = 5 ; /* level 5 for sensor sensitivity */
if ( ls_switch_handle )
2009-11-30 23:55:12 +03:00
set_light_sens_switch ( asus - > light_switch ) ;
2009-11-30 23:42:42 +03:00
if ( ls_level_handle )
2009-11-30 23:55:12 +03:00
set_light_sens_level ( asus - > light_level ) ;
2009-11-30 23:42:42 +03:00
/* GPS is on by default */
write_status ( NULL , 1 , GPS_ON ) ;
return result ;
}
static int __devinit asus_acpi_add ( struct acpi_device * device )
{
int result ;
pr_notice ( " Asus Laptop Support version %s \n " ,
ASUS_LAPTOP_VERSION ) ;
2009-11-30 23:55:12 +03:00
asus = kzalloc ( sizeof ( struct asus_laptop ) , GFP_KERNEL ) ;
if ( ! asus )
2009-11-30 23:42:42 +03:00
return - ENOMEM ;
2009-11-30 23:55:12 +03:00
asus - > handle = device - > handle ;
strcpy ( acpi_device_name ( device ) , ASUS_LAPTOP_DEVICE_NAME ) ;
strcpy ( acpi_device_class ( device ) , ASUS_LAPTOP_CLASS ) ;
device - > driver_data = asus ;
asus - > device = device ;
2009-11-30 23:42:42 +03:00
result = asus_acpi_init ( device ) ;
2007-01-30 09:46:43 +03:00
if ( result )
2009-11-30 23:42:42 +03:00
goto fail_platform ;
2007-01-26 16:04:30 +03:00
2009-11-30 23:42:42 +03:00
/*
* Register the platform device first . It is used as a parent for the
* sub - devices below .
*/
result = asus_platform_init ( ) ;
2009-06-16 23:28:46 +04:00
if ( result )
2009-11-30 23:42:42 +03:00
goto fail_platform ;
2009-06-16 23:28:46 +04:00
if ( ! acpi_video_backlight_support ( ) ) {
2009-11-30 23:55:12 +03:00
result = asus_backlight_init ( & asus - > platform_device - > dev ) ;
2009-06-16 23:28:46 +04:00
if ( result )
goto fail_backlight ;
} else
2009-11-30 23:42:42 +03:00
pr_info ( " Backlight controlled by ACPI video driver \n " ) ;
2009-06-16 23:28:46 +04:00
2009-11-30 23:55:12 +03:00
result = asus_input_init ( & asus - > platform_device - > dev ) ;
2009-11-30 23:42:42 +03:00
if ( result )
goto fail_input ;
2009-11-30 23:55:12 +03:00
result = asus_led_init ( & asus - > platform_device - > dev ) ;
2009-11-30 23:42:42 +03:00
if ( result )
goto fail_led ;
asus_device_present = true ;
2007-01-30 09:46:43 +03:00
return 0 ;
2007-01-26 16:04:30 +03:00
2009-11-30 23:42:42 +03:00
fail_led :
asus_input_exit ( ) ;
fail_input :
asus_backlight_exit ( ) ;
2009-06-16 23:28:46 +04:00
fail_backlight :
2009-11-30 23:42:42 +03:00
asus_platform_exit ( ) ;
fail_platform :
2009-11-30 23:55:12 +03:00
kfree ( asus - > name ) ;
kfree ( asus ) ;
2009-06-16 23:28:46 +04:00
2009-11-30 23:42:42 +03:00
return result ;
}
2009-06-16 23:28:46 +04:00
2009-11-30 23:42:42 +03:00
static int asus_acpi_remove ( struct acpi_device * device , int type )
{
asus_backlight_exit ( ) ;
asus_led_exit ( ) ;
asus_input_exit ( ) ;
asus_platform_exit ( ) ;
2009-11-30 23:55:12 +03:00
kfree ( asus - > name ) ;
kfree ( asus ) ;
2009-11-30 23:42:42 +03:00
return 0 ;
}
static const struct acpi_device_id asus_device_ids [ ] = {
{ " ATK0100 " , 0 } ,
{ " ATK0101 " , 0 } ,
{ " " , 0 } ,
} ;
MODULE_DEVICE_TABLE ( acpi , asus_device_ids ) ;
2007-01-26 16:04:30 +03:00
2009-11-30 23:42:42 +03:00
static struct acpi_driver asus_acpi_driver = {
2009-11-30 23:55:12 +03:00
. name = ASUS_LAPTOP_NAME ,
. class = ASUS_LAPTOP_CLASS ,
2009-11-30 23:42:42 +03:00
. owner = THIS_MODULE ,
. ids = asus_device_ids ,
. flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS ,
. ops = {
. add = asus_acpi_add ,
. remove = asus_acpi_remove ,
. notify = asus_acpi_notify ,
} ,
} ;
2007-01-26 16:04:30 +03:00
2009-11-30 23:42:42 +03:00
static int __init asus_laptop_init ( void )
{
int result ;
2007-01-26 16:04:30 +03:00
2009-11-30 23:42:42 +03:00
result = platform_driver_register ( & platform_driver ) ;
if ( result < 0 )
return result ;
2009-01-20 18:17:43 +03:00
2009-11-30 23:42:42 +03:00
result = acpi_bus_register_driver ( & asus_acpi_driver ) ;
if ( result < 0 )
goto fail_acpi_driver ;
if ( ! asus_device_present ) {
result = - ENODEV ;
goto fail_no_device ;
}
return 0 ;
2007-01-26 16:04:30 +03:00
2009-11-30 23:42:42 +03:00
fail_no_device :
acpi_bus_unregister_driver ( & asus_acpi_driver ) ;
fail_acpi_driver :
platform_driver_unregister ( & platform_driver ) ;
2007-01-26 16:04:30 +03:00
return result ;
}
2009-11-30 23:42:42 +03:00
static void __exit asus_laptop_exit ( void )
{
acpi_bus_unregister_driver ( & asus_acpi_driver ) ;
platform_driver_unregister ( & platform_driver ) ;
}
2007-01-26 16:04:30 +03:00
module_init ( asus_laptop_init ) ;
module_exit ( asus_laptop_exit ) ;