2005-04-16 15:20:36 -07:00
/*
* asus_acpi . c - Asus Laptop ACPI Extras
*
*
2006-06-30 19:03:00 -04:00
* Copyright ( C ) 2002 - 2005 Julien Lerouge , 2003 - 2006 Karol Kozimor
2005-04-16 15:20:36 -07: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 .
2007-02-02 19:48:19 +03:00
* <EFBFBD> ic Burghard - LED display support for W1N
2005-04-16 15:20:36 -07:00
*
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/types.h>
# include <linux/proc_fs.h>
2009-12-21 16:20:01 -08:00
# include <linux/seq_file.h>
2006-10-20 14:30:29 -07:00
# include <linux/backlight.h>
2005-04-16 15:20:36 -07:00
# include <acpi/acpi_drivers.h>
# include <acpi/acpi_bus.h>
# include <asm/uaccess.h>
2006-06-30 19:03:00 -04:00
# define ASUS_ACPI_VERSION "0.30"
2005-04-16 15:20:36 -07:00
2008-10-17 14:35:14 -04:00
# define PROC_ASUS "asus" /* The directory */
2005-04-16 15:20:36 -07:00
# define PROC_MLED "mled"
# define PROC_WLED "wled"
# define PROC_TLED "tled"
2006-06-30 19:07:00 -04:00
# define PROC_BT "bluetooth"
2006-06-30 19:04:00 -04:00
# define PROC_LEDD "ledd"
2005-04-16 15:20:36 -07:00
# define PROC_INFO "info"
# define PROC_LCD "lcd"
# define PROC_BRN "brn"
# define PROC_DISP "disp"
# define ACPI_HOTK_NAME "Asus Laptop ACPI Extras Driver"
# define ACPI_HOTK_CLASS "hotkey"
# define ACPI_HOTK_DEVICE_NAME "Hotkey"
/*
* Some events we use , same for all Asus
*/
2005-08-05 00:44:28 -04:00
# define BR_UP 0x10
2005-04-16 15:20:36 -07:00
# define BR_DOWN 0x20
/*
* Flags for hotk status
*/
2008-10-17 14:35:14 -04:00
# define MLED_ON 0x01 /* Mail LED */
# define WLED_ON 0x02 /* Wireless LED */
# define TLED_ON 0x04 /* Touchpad LED */
# define BT_ON 0x08 /* Internal Bluetooth */
2005-04-16 15:20:36 -07:00
MODULE_AUTHOR ( " Julien Lerouge, Karol Kozimor " ) ;
MODULE_DESCRIPTION ( ACPI_HOTK_NAME ) ;
MODULE_LICENSE ( " GPL " ) ;
static uid_t asus_uid ;
static gid_t asus_gid ;
module_param ( asus_uid , uint , 0 ) ;
2008-07-31 00:07:23 -07:00
MODULE_PARM_DESC ( asus_uid , " UID for entries in /proc/acpi/asus " ) ;
2005-04-16 15:20:36 -07:00
module_param ( asus_gid , uint , 0 ) ;
2008-07-31 00:07:23 -07:00
MODULE_PARM_DESC ( asus_gid , " GID for entries in /proc/acpi/asus " ) ;
2005-04-16 15:20:36 -07:00
2008-10-17 14:35:14 -04:00
/* For each model, all features implemented,
2005-04-16 15:20:36 -07:00
* those marked with R are relative to HOTK , A for absolute */
struct model_data {
2008-10-17 14:35:14 -04:00
char * name ; /* name of the laptop________________A */
char * mt_mled ; /* method to handle mled_____________R */
char * mled_status ; /* node to handle mled reading_______A */
char * mt_wled ; /* method to handle wled_____________R */
char * wled_status ; /* node to handle wled reading_______A */
char * mt_tled ; /* method to handle tled_____________R */
char * tled_status ; /* node to handle tled reading_______A */
char * mt_ledd ; /* method to handle LED display______R */
char * mt_bt_switch ; /* method to switch Bluetooth on/off_R */
char * bt_status ; /* no model currently supports this__? */
char * mt_lcd_switch ; /* method to turn LCD on/off_________A */
char * lcd_status ; /* node to read LCD panel state______A */
char * brightness_up ; /* method to set brightness up_______A */
char * brightness_down ; /* method to set brightness down ____A */
char * brightness_set ; /* method to set absolute brightness_R */
char * brightness_get ; /* method to get absolute brightness_R */
char * brightness_status ; /* node to get brightness____________A */
char * display_set ; /* method to set video output________R */
char * display_get ; /* method to get video output________R */
2005-04-16 15:20:36 -07:00
} ;
/*
* This is the main structure , we can use it to store anything interesting
* about the hotk device
*/
struct asus_hotk {
2008-10-17 14:35:14 -04:00
struct acpi_device * device ; /* the device we are in */
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 */
struct model_data * methods ; /* methods available on the laptop */
u8 brightness ; /* brightness level */
2005-04-16 15:20:36 -07:00
enum {
2008-10-17 14:35:14 -04:00
A1x = 0 , /* A1340D, A1300F */
A2x , /* A2500H */
A4G , /* A4700G */
D1x , /* D1 */
L2D , /* L2000D */
L3C , /* L3800C */
L3D , /* L3400D */
L3H , /* L3H, L2000E, L5D */
L4R , /* L4500R */
L5x , /* L5800C */
L8L , /* L8400L */
M1A , /* M1300A */
M2E , /* M2400E, L4400L */
M6N , /* M6800N, W3400N */
M6R , /* M6700R, A3000G */
P30 , /* Samsung P30 */
S1x , /* S1300A, but also L1400B and M2400A (L84F) */
S2x , /* S200 (J1 reported), Victor MP-XP7210 */
W1N , /* W1000N */
W5A , /* W5A */
W3V , /* W3030V */
xxN , /* M2400N, M3700N, M5200N, M6800N,
S1300N , S5200N */
A4S , /* Z81sp */
F3Sa , /* (Centrino) */
2009-01-20 16:17:41 +01:00
R1F ,
2005-04-16 15:20:36 -07:00
END_MODEL
2008-10-17 14:35:14 -04:00
} model ; /* Models currently supported */
u16 event_count [ 128 ] ; /* Count for each event TODO make this better */
2005-04-16 15:20:36 -07:00
} ;
/* Here we go */
# define A1x_PREFIX "\\_SB.PCI0.ISA.EC0."
# define L3C_PREFIX "\\_SB.PCI0.PX40.ECD0."
# define M1A_PREFIX "\\_SB.PCI0.PX40.EC0."
# define P30_PREFIX "\\_SB.PCI0.LPCB.EC0."
# define S1x_PREFIX "\\_SB.PCI0.PX40."
# define S2x_PREFIX A1x_PREFIX
# define xxN_PREFIX "\\_SB.PCI0.SBRG.EC0."
static struct model_data model_conf [ END_MODEL ] = {
2005-08-05 00:44:28 -04:00
/*
2005-04-16 15:20:36 -07:00
* TODO I have seen a SWBX and AIBX method on some models , like L1400B ,
* it seems to be a kind of switch , but what for ?
*/
{
2005-08-05 00:44:28 -04:00
. name = " A1x " ,
. mt_mled = " MLED " ,
. mled_status = " \\ MAIL " ,
. mt_lcd_switch = A1x_PREFIX " _Q10 " ,
. lcd_status = " \\ BKLI " ,
. brightness_up = A1x_PREFIX " _Q0E " ,
. brightness_down = A1x_PREFIX " _Q0F " } ,
2005-04-16 15:20:36 -07:00
{
2005-08-05 00:44:28 -04:00
. name = " A2x " ,
. mt_mled = " MLED " ,
. mt_wled = " WLED " ,
. wled_status = " \\ SG66 " ,
. mt_lcd_switch = " \\ Q10 " ,
. lcd_status = " \\ BAOF " ,
. brightness_set = " SPLV " ,
. brightness_get = " GPLV " ,
. display_set = " SDSP " ,
. display_get = " \\ INFB " } ,
2005-04-16 15:20:36 -07:00
2006-06-30 19:06:00 -04:00
{
. name = " A4G " ,
. mt_mled = " MLED " ,
/* WLED present, but not controlled by ACPI */
. mt_lcd_switch = xxN_PREFIX " _Q10 " ,
. brightness_set = " SPLV " ,
. brightness_get = " GPLV " ,
. display_set = " SDSP " ,
. display_get = " \\ ADVG " } ,
2005-04-16 15:20:36 -07:00
{
2005-08-05 00:44:28 -04:00
. name = " D1x " ,
. mt_mled = " MLED " ,
. mt_lcd_switch = " \\ Q0D " ,
. lcd_status = " \\ GP11 " ,
. brightness_up = " \\ Q0C " ,
. brightness_down = " \\ Q0B " ,
. brightness_status = " \\ BLVL " ,
. display_set = " SDSP " ,
. display_get = " \\ INFB " } ,
2005-04-16 15:20:36 -07:00
{
2005-08-05 00:44:28 -04:00
. name = " L2D " ,
. mt_mled = " MLED " ,
. mled_status = " \\ SGP6 " ,
. mt_wled = " WLED " ,
. wled_status = " \\ RCP3 " ,
. mt_lcd_switch = " \\ Q10 " ,
. lcd_status = " \\ SGP0 " ,
. brightness_up = " \\ Q0E " ,
. brightness_down = " \\ Q0F " ,
. display_set = " SDSP " ,
. display_get = " \\ INFB " } ,
2005-04-16 15:20:36 -07:00
{
2005-08-05 00:44:28 -04:00
. name = " L3C " ,
. mt_mled = " MLED " ,
. mt_wled = " WLED " ,
. mt_lcd_switch = L3C_PREFIX " _Q10 " ,
. lcd_status = " \\ GL32 " ,
. brightness_set = " SPLV " ,
. brightness_get = " GPLV " ,
. display_set = " SDSP " ,
. display_get = " \\ _SB.PCI0.PCI1.VGAC.NMAP " } ,
2005-04-16 15:20:36 -07:00
{
2005-08-05 00:44:28 -04:00
. name = " L3D " ,
. mt_mled = " MLED " ,
. mled_status = " \\ MALD " ,
. mt_wled = " WLED " ,
. mt_lcd_switch = " \\ Q10 " ,
. lcd_status = " \\ BKLG " ,
. brightness_set = " SPLV " ,
. brightness_get = " GPLV " ,
. display_set = " SDSP " ,
. display_get = " \\ INFB " } ,
2005-04-16 15:20:36 -07:00
{
2005-08-05 00:44:28 -04:00
. name = " L3H " ,
. mt_mled = " MLED " ,
. mt_wled = " WLED " ,
. mt_lcd_switch = " EHK " ,
. lcd_status = " \\ _SB.PCI0.PM.PBC " ,
. brightness_set = " SPLV " ,
. brightness_get = " GPLV " ,
. display_set = " SDSP " ,
. display_get = " \\ INFB " } ,
2005-04-16 15:20:36 -07:00
{
2005-08-05 00:44:28 -04:00
. name = " L4R " ,
. mt_mled = " MLED " ,
. mt_wled = " WLED " ,
. wled_status = " \\ _SB.PCI0.SBRG.SG13 " ,
. mt_lcd_switch = xxN_PREFIX " _Q10 " ,
. lcd_status = " \\ _SB.PCI0.SBSM.SEO4 " ,
. brightness_set = " SPLV " ,
. brightness_get = " GPLV " ,
. display_set = " SDSP " ,
. display_get = " \\ _SB.PCI0.P0P1.VGA.GETD " } ,
2005-04-16 15:20:36 -07:00
{
2005-08-05 00:44:28 -04:00
. name = " L5x " ,
. mt_mled = " MLED " ,
2005-04-16 15:20:36 -07:00
/* WLED present, but not controlled by ACPI */
2005-08-05 00:44:28 -04:00
. mt_tled = " TLED " ,
. mt_lcd_switch = " \\ Q0D " ,
. lcd_status = " \\ BAOF " ,
. brightness_set = " SPLV " ,
. brightness_get = " GPLV " ,
. display_set = " SDSP " ,
. display_get = " \\ INFB " } ,
2005-04-16 15:20:36 -07:00
{
2005-08-05 00:44:28 -04:00
. name = " L8L "
2005-04-16 15:20:36 -07:00
/* No features, but at least support the hotkeys */
2005-08-05 00:44:28 -04:00
} ,
2005-04-16 15:20:36 -07:00
{
2005-08-05 00:44:28 -04:00
. name = " M1A " ,
. mt_mled = " MLED " ,
. mt_lcd_switch = M1A_PREFIX " Q10 " ,
. lcd_status = " \\ PNOF " ,
. brightness_up = M1A_PREFIX " Q0E " ,
. brightness_down = M1A_PREFIX " Q0F " ,
. brightness_status = " \\ BRIT " ,
. display_set = " SDSP " ,
. display_get = " \\ INFB " } ,
2005-04-16 15:20:36 -07:00
{
2005-08-05 00:44:28 -04:00
. name = " M2E " ,
. mt_mled = " MLED " ,
. mt_wled = " WLED " ,
. mt_lcd_switch = " \\ Q10 " ,
. lcd_status = " \\ GP06 " ,
. brightness_set = " SPLV " ,
. brightness_get = " GPLV " ,
. display_set = " SDSP " ,
. display_get = " \\ INFB " } ,
2005-04-16 15:20:36 -07:00
{
2005-08-05 00:44:28 -04:00
. name = " M6N " ,
. mt_mled = " MLED " ,
. mt_wled = " WLED " ,
. wled_status = " \\ _SB.PCI0.SBRG.SG13 " ,
. mt_lcd_switch = xxN_PREFIX " _Q10 " ,
. lcd_status = " \\ _SB.BKLT " ,
. brightness_set = " SPLV " ,
. brightness_get = " GPLV " ,
. display_set = " SDSP " ,
2006-06-30 19:15:00 -04:00
. display_get = " \\ SSTE " } ,
2005-04-16 15:20:36 -07:00
{
2005-08-05 00:44:28 -04:00
. name = " M6R " ,
. mt_mled = " MLED " ,
. mt_wled = " WLED " ,
. mt_lcd_switch = xxN_PREFIX " _Q10 " ,
. lcd_status = " \\ _SB.PCI0.SBSM.SEO4 " ,
. brightness_set = " SPLV " ,
. brightness_get = " GPLV " ,
. display_set = " SDSP " ,
2006-06-30 19:15:00 -04:00
. display_get = " \\ _SB.PCI0.P0P1.VGA.GETD " } ,
2005-04-16 15:20:36 -07:00
{
2005-08-05 00:44:28 -04:00
. name = " P30 " ,
. mt_wled = " WLED " ,
. mt_lcd_switch = P30_PREFIX " _Q0E " ,
. lcd_status = " \\ BKLT " ,
. brightness_up = P30_PREFIX " _Q68 " ,
. brightness_down = P30_PREFIX " _Q69 " ,
. brightness_get = " GPLV " ,
. display_set = " SDSP " ,
. display_get = " \\ DNXT " } ,
2005-04-16 15:20:36 -07:00
{
2005-08-05 00:44:28 -04:00
. name = " S1x " ,
. mt_mled = " MLED " ,
. mled_status = " \\ EMLE " ,
. mt_wled = " WLED " ,
. mt_lcd_switch = S1x_PREFIX " Q10 " ,
. lcd_status = " \\ PNOF " ,
. brightness_set = " SPLV " ,
. brightness_get = " GPLV " } ,
2005-04-16 15:20:36 -07:00
{
2005-08-05 00:44:28 -04:00
. name = " S2x " ,
. mt_mled = " MLED " ,
. mled_status = " \\ MAIL " ,
. mt_lcd_switch = S2x_PREFIX " _Q10 " ,
. lcd_status = " \\ BKLI " ,
. brightness_up = S2x_PREFIX " _Q0B " ,
. brightness_down = S2x_PREFIX " _Q0A " } ,
2005-04-16 15:20:36 -07:00
2006-06-30 19:04:00 -04:00
{
. name = " W1N " ,
. mt_mled = " MLED " ,
. mt_wled = " WLED " ,
. mt_ledd = " SLCM " ,
. mt_lcd_switch = xxN_PREFIX " _Q10 " ,
. lcd_status = " \\ BKLT " ,
. brightness_set = " SPLV " ,
. brightness_get = " GPLV " ,
. display_set = " SDSP " ,
. display_get = " \\ ADVG " } ,
2006-06-30 19:07:00 -04:00
{
. name = " W5A " ,
. mt_bt_switch = " BLED " ,
. mt_wled = " WLED " ,
. mt_lcd_switch = xxN_PREFIX " _Q10 " ,
. brightness_set = " SPLV " ,
. brightness_get = " GPLV " ,
. display_set = " SDSP " ,
. display_get = " \\ ADVG " } ,
2005-04-16 15:20:36 -07:00
{
2006-08-14 22:37:20 -07:00
. name = " W3V " ,
. mt_mled = " MLED " ,
. mt_wled = " WLED " ,
. mt_lcd_switch = xxN_PREFIX " _Q10 " ,
. lcd_status = " \\ BKLT " ,
. brightness_set = " SPLV " ,
. brightness_get = " GPLV " ,
. display_set = " SDSP " ,
. display_get = " \\ INFB " } ,
{
2005-08-05 00:44:28 -04:00
. name = " xxN " ,
. mt_mled = " MLED " ,
2005-04-16 15:20:36 -07:00
/* WLED present, but not controlled by ACPI */
2005-08-05 00:44:28 -04:00
. mt_lcd_switch = xxN_PREFIX " _Q10 " ,
. lcd_status = " \\ BKLT " ,
. brightness_set = " SPLV " ,
. brightness_get = " GPLV " ,
. display_set = " SDSP " ,
2007-02-05 16:09:09 -08:00
. display_get = " \\ ADVG " } ,
{
. name = " A4S " ,
. brightness_set = " SPLV " ,
. brightness_get = " GPLV " ,
. mt_bt_switch = " BLED " ,
. mt_wled = " WLED "
2008-02-04 23:31:18 -08:00
} ,
{
. name = " F3Sa " ,
. mt_bt_switch = " BLED " ,
. mt_wled = " WLED " ,
. mt_mled = " MLED " ,
. brightness_get = " GPLV " ,
. brightness_set = " SPLV " ,
. mt_lcd_switch = " \\ _SB.PCI0.SBRG.EC0._Q10 " ,
. lcd_status = " \\ _SB.PCI0.SBRG.EC0.RPIN " ,
. display_get = " \\ ADVG " ,
. display_set = " SDSP " ,
} ,
2009-01-20 16:17:41 +01:00
{
. name = " R1F " ,
. mt_bt_switch = " BLED " ,
. mt_mled = " MLED " ,
. mt_wled = " WLED " ,
. mt_lcd_switch = " \\ Q10 " ,
. lcd_status = " \\ GP06 " ,
. brightness_set = " SPLV " ,
. brightness_get = " GPLV " ,
. display_set = " SDSP " ,
. display_get = " \\ INFB "
}
2005-04-16 15:20:36 -07:00
} ;
/* procdir we use */
static struct proc_dir_entry * asus_proc_dir ;
2006-10-20 14:30:29 -07:00
static struct backlight_device * asus_backlight_device ;
2005-04-16 15:20:36 -07:00
/*
* This header is made available to allow proper configuration given model ,
* revision number , . . . this info cannot go in struct asus_hotk because it is
* available before the hotk
*/
static struct acpi_table_header * asus_info ;
/* The actual device the driver binds to */
static struct asus_hotk * hotk ;
/*
2007-07-23 14:44:41 +02:00
* The hotkey driver and autoloading declaration
2005-04-16 15:20:36 -07:00
*/
static int asus_hotk_add ( struct acpi_device * device ) ;
static int asus_hotk_remove ( struct acpi_device * device , int type ) ;
2009-04-30 09:35:58 -06:00
static void asus_hotk_notify ( struct acpi_device * device , u32 event ) ;
2007-07-23 14:44:41 +02:00
static const struct acpi_device_id asus_device_ids [ ] = {
{ " ATK0100 " , 0 } ,
{ " " , 0 } ,
} ;
MODULE_DEVICE_TABLE ( acpi , asus_device_ids ) ;
2005-04-16 15:20:36 -07:00
static struct acpi_driver asus_hotk_driver = {
2007-02-12 23:33:40 -05:00
. name = " asus_acpi " ,
2005-08-05 00:44:28 -04:00
. class = ACPI_HOTK_CLASS ,
2009-12-03 07:44:50 +00:00
. owner = THIS_MODULE ,
2007-07-23 14:44:41 +02:00
. ids = asus_device_ids ,
2009-04-30 09:35:58 -06:00
. flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS ,
2005-08-05 00:44:28 -04:00
. ops = {
. add = asus_hotk_add ,
. remove = asus_hotk_remove ,
2009-04-30 09:35:58 -06:00
. notify = asus_hotk_notify ,
2005-08-05 00:44:28 -04:00
} ,
2005-04-16 15:20:36 -07:00
} ;
2008-10-17 14:35:14 -04:00
/*
2005-04-16 15:20:36 -07: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-10-17 14:35:14 -04:00
* returns 1 if write is successful , 0 else .
2005-04-16 15:20:36 -07:00
*/
static int write_acpi_int ( acpi_handle handle , const char * method , int val ,
struct acpi_buffer * output )
{
2008-10-17 14:35:14 -04:00
struct acpi_object_list params ; /* list of input parameters (int) */
union acpi_object in_obj ; /* the only param we use */
2005-04-16 15:20:36 -07:00
acpi_status status ;
params . count = 1 ;
params . pointer = & in_obj ;
in_obj . type = ACPI_TYPE_INTEGER ;
in_obj . integer . value = val ;
2005-08-05 00:44:28 -04:00
status = acpi_evaluate_object ( handle , ( char * ) method , & params , output ) ;
2005-04-16 15:20:36 -07:00
return ( status = = AE_OK ) ;
}
static int read_acpi_int ( acpi_handle handle , const char * method , int * val )
{
struct acpi_buffer output ;
union acpi_object out_obj ;
acpi_status status ;
output . length = sizeof ( out_obj ) ;
output . pointer = & out_obj ;
2005-08-05 00:44:28 -04:00
status = acpi_evaluate_object ( handle , ( char * ) method , NULL , & output ) ;
2005-04-16 15:20:36 -07:00
* val = out_obj . integer . value ;
return ( status = = AE_OK ) & & ( out_obj . type = = ACPI_TYPE_INTEGER ) ;
}
2009-12-21 16:20:01 -08:00
static int asus_info_proc_show ( struct seq_file * m , void * v )
2005-04-16 15:20:36 -07:00
{
int temp ;
2009-12-21 16:20:01 -08:00
seq_printf ( m , ACPI_HOTK_NAME " " ASUS_ACPI_VERSION " \n " ) ;
seq_printf ( m , " Model reference : %s \n " , hotk - > methods - > name ) ;
2008-10-17 14:35:14 -04: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
2005-04-16 15:20:36 -07:00
* bit signifies that the laptop is equipped with a Wi - Fi MiniPCI card .
* The significance of others is yet to be found .
*/
if ( read_acpi_int ( hotk - > handle , " SFUN " , & temp ) )
2009-12-21 16:20:01 -08:00
seq_printf ( m , " SFUN value : 0x%04x \n " , temp ) ;
2005-04-16 15:20:36 -07:00
/*
* Another value for userspace : the ASYM method returns 0x02 for
* battery low and 0x04 for battery critical , its readings tend to be
2008-10-17 14:35:14 -04:00
* more accurate than those provided by _BST .
2005-04-16 15:20:36 -07:00
* Note : since not all the laptops provide this method , errors are
* silently ignored .
*/
if ( read_acpi_int ( hotk - > handle , " ASYM " , & temp ) )
2009-12-21 16:20:01 -08:00
seq_printf ( m , " ASYM value : 0x%04x \n " , temp ) ;
2005-04-16 15:20:36 -07:00
if ( asus_info ) {
2009-12-21 16:20:01 -08:00
seq_printf ( m , " DSDT length : %d \n " , asus_info - > length ) ;
seq_printf ( m , " DSDT checksum : %d \n " , asus_info - > checksum ) ;
seq_printf ( m , " DSDT revision : %d \n " , asus_info - > revision ) ;
seq_printf ( m , " OEM id : %.*s \n " , ACPI_OEM_ID_SIZE , asus_info - > oem_id ) ;
seq_printf ( m , " OEM table id : %.*s \n " , ACPI_OEM_TABLE_ID_SIZE , asus_info - > oem_table_id ) ;
seq_printf ( m , " OEM revision : 0x%x \n " , asus_info - > oem_revision ) ;
seq_printf ( m , " ASL comp vendor id : %.*s \n " , ACPI_NAME_SIZE , asus_info - > asl_compiler_id ) ;
seq_printf ( m , " ASL comp revision : 0x%x \n " , asus_info - > asl_compiler_revision ) ;
2005-04-16 15:20:36 -07:00
}
2009-12-21 16:20:01 -08:00
return 0 ;
}
static int asus_info_proc_open ( struct inode * inode , struct file * file )
{
return single_open ( file , asus_info_proc_show , NULL ) ;
2005-04-16 15:20:36 -07:00
}
2009-12-21 16:20:01 -08:00
static const struct file_operations asus_info_proc_fops = {
. owner = THIS_MODULE ,
. open = asus_info_proc_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
} ;
2005-04-16 15:20:36 -07:00
/*
* / proc 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
*/
/* Generic LED functions */
2005-08-05 00:44:28 -04:00
static int read_led ( const char * ledname , int ledmask )
2005-04-16 15:20:36 -07:00
{
if ( ledname ) {
int led_status ;
if ( read_acpi_int ( NULL , ledname , & led_status ) )
return led_status ;
else
printk ( KERN_WARNING " Asus ACPI: Error reading LED "
" status \n " ) ;
}
return ( hotk - > status & ledmask ) ? 1 : 0 ;
}
2008-10-17 14:35:14 -04:00
static int parse_arg ( const char __user * buf , unsigned long count , int * val )
2005-04-16 15:20:36 -07:00
{
char s [ 32 ] ;
if ( ! count )
return 0 ;
if ( count > 31 )
return - EINVAL ;
if ( copy_from_user ( s , buf , count ) )
return - EFAULT ;
s [ count ] = 0 ;
if ( sscanf ( s , " %i " , val ) ! = 1 )
return - EINVAL ;
return count ;
}
/* FIXME: kill extraneous args so it can be called independently */
static int
2008-10-17 14:35:14 -04:00
write_led ( const char __user * buffer , unsigned long count ,
2005-08-05 00:44:28 -04:00
char * ledname , int ledmask , int invert )
2005-04-16 15:20:36 -07:00
{
2006-10-10 14:20:35 -07:00
int rv , value ;
2005-04-16 15:20:36 -07:00
int led_out = 0 ;
2006-10-10 14:20:35 -07:00
rv = parse_arg ( buffer , count , & value ) ;
if ( rv > 0 )
2005-04-16 15:20:36 -07:00
led_out = value ? 1 : 0 ;
hotk - > status =
( led_out ) ? ( hotk - > status | ledmask ) : ( hotk - > status & ~ ledmask ) ;
2005-08-05 00:44:28 -04:00
if ( invert ) /* invert target value */
2008-03-04 15:00:13 -08:00
led_out = ! led_out ;
2005-04-16 15:20:36 -07:00
if ( ! write_acpi_int ( hotk - > handle , ledname , led_out , NULL ) )
2005-08-05 00:44:28 -04:00
printk ( KERN_WARNING " Asus ACPI: LED (%s) write failed \n " ,
ledname ) ;
2005-04-16 15:20:36 -07:00
2006-10-10 14:20:35 -07:00
return rv ;
2005-04-16 15:20:36 -07:00
}
/*
* Proc handlers for MLED
*/
2009-12-21 16:20:01 -08:00
static int mled_proc_show ( struct seq_file * m , void * v )
2005-04-16 15:20:36 -07:00
{
2009-12-21 16:20:01 -08:00
seq_printf ( m , " %d \n " , read_led ( hotk - > methods - > mled_status , MLED_ON ) ) ;
return 0 ;
2005-04-16 15:20:36 -07:00
}
2009-12-21 16:20:01 -08:00
static int mled_proc_open ( struct inode * inode , struct file * file )
{
return single_open ( file , mled_proc_show , NULL ) ;
}
static ssize_t mled_proc_write ( struct file * file , const char __user * buffer ,
size_t count , loff_t * pos )
2005-04-16 15:20:36 -07:00
{
return write_led ( buffer , count , hotk - > methods - > mt_mled , MLED_ON , 1 ) ;
}
2009-12-21 16:20:01 -08:00
static const struct file_operations mled_proc_fops = {
. owner = THIS_MODULE ,
. open = mled_proc_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
. write = mled_proc_write ,
} ;
2006-06-30 19:04:00 -04:00
/*
* Proc handlers for LED display
*/
2009-12-21 16:20:01 -08:00
static int ledd_proc_show ( struct seq_file * m , void * v )
2006-06-30 19:04:00 -04:00
{
2009-12-21 16:20:01 -08:00
seq_printf ( m , " 0x%08x \n " , hotk - > ledd_status ) ;
return 0 ;
2006-06-30 19:04:00 -04:00
}
2009-12-21 16:20:01 -08:00
static int ledd_proc_open ( struct inode * inode , struct file * file )
{
return single_open ( file , ledd_proc_show , NULL ) ;
}
static ssize_t ledd_proc_write ( struct file * file , const char __user * buffer ,
size_t count , loff_t * pos )
2006-06-30 19:04:00 -04:00
{
2006-10-10 14:20:35 -07:00
int rv , value ;
2006-06-30 19:04:00 -04:00
2006-10-10 14:20:35 -07:00
rv = parse_arg ( buffer , count , & value ) ;
if ( rv > 0 ) {
2006-06-30 19:04:00 -04:00
if ( ! write_acpi_int
( hotk - > handle , hotk - > methods - > mt_ledd , value , NULL ) )
printk ( KERN_WARNING
" Asus ACPI: LED display write failed \n " ) ;
else
hotk - > ledd_status = ( u32 ) value ;
2006-10-10 14:20:36 -07:00
}
2006-10-10 14:20:35 -07:00
return rv ;
2006-06-30 19:04:00 -04:00
}
2009-12-21 16:20:01 -08:00
static const struct file_operations ledd_proc_fops = {
. owner = THIS_MODULE ,
. open = ledd_proc_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
. write = ledd_proc_write ,
} ;
2005-04-16 15:20:36 -07:00
/*
* Proc handlers for WLED
*/
2009-12-21 16:20:01 -08:00
static int wled_proc_show ( struct seq_file * m , void * v )
2005-04-16 15:20:36 -07:00
{
2009-12-21 16:20:01 -08:00
seq_printf ( m , " %d \n " , read_led ( hotk - > methods - > wled_status , WLED_ON ) ) ;
return 0 ;
2005-04-16 15:20:36 -07:00
}
2009-12-21 16:20:01 -08:00
static int wled_proc_open ( struct inode * inode , struct file * file )
{
return single_open ( file , wled_proc_show , NULL ) ;
}
static ssize_t wled_proc_write ( struct file * file , const char __user * buffer ,
size_t count , loff_t * pos )
2005-04-16 15:20:36 -07:00
{
return write_led ( buffer , count , hotk - > methods - > mt_wled , WLED_ON , 0 ) ;
}
2009-12-21 16:20:01 -08:00
static const struct file_operations wled_proc_fops = {
. owner = THIS_MODULE ,
. open = wled_proc_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
. write = wled_proc_write ,
} ;
2006-06-30 19:07:00 -04:00
/*
* Proc handlers for Bluetooth
*/
2009-12-21 16:20:01 -08:00
static int bluetooth_proc_show ( struct seq_file * m , void * v )
2006-06-30 19:07:00 -04:00
{
2009-12-21 16:20:01 -08:00
seq_printf ( m , " %d \n " , read_led ( hotk - > methods - > bt_status , BT_ON ) ) ;
return 0 ;
2006-06-30 19:07:00 -04:00
}
2009-12-21 16:20:01 -08:00
static int bluetooth_proc_open ( struct inode * inode , struct file * file )
{
return single_open ( file , bluetooth_proc_show , NULL ) ;
}
static ssize_t bluetooth_proc_write ( struct file * file ,
const char __user * buffer , size_t count , loff_t * pos )
2006-06-30 19:07:00 -04:00
{
2008-10-17 14:35:14 -04:00
/* Note: mt_bt_switch controls both internal Bluetooth adapter's
2006-06-30 19:07:00 -04:00
presence and its LED */
return write_led ( buffer , count , hotk - > methods - > mt_bt_switch , BT_ON , 0 ) ;
}
2009-12-21 16:20:01 -08:00
static const struct file_operations bluetooth_proc_fops = {
. owner = THIS_MODULE ,
. open = bluetooth_proc_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
. write = bluetooth_proc_write ,
} ;
2005-04-16 15:20:36 -07:00
/*
* Proc handlers for TLED
*/
2009-12-21 16:20:01 -08:00
static int tled_proc_show ( struct seq_file * m , void * v )
2005-04-16 15:20:36 -07:00
{
2009-12-21 16:20:01 -08:00
seq_printf ( m , " %d \n " , read_led ( hotk - > methods - > tled_status , TLED_ON ) ) ;
return 0 ;
2005-04-16 15:20:36 -07:00
}
2009-12-21 16:20:01 -08:00
static int tled_proc_open ( struct inode * inode , struct file * file )
{
return single_open ( file , tled_proc_show , NULL ) ;
}
static ssize_t tled_proc_write ( struct file * file , const char __user * buffer ,
size_t count , loff_t * pos )
2005-04-16 15:20:36 -07:00
{
return write_led ( buffer , count , hotk - > methods - > mt_tled , TLED_ON , 0 ) ;
}
2009-12-21 16:20:01 -08:00
static const struct file_operations tled_proc_fops = {
. owner = THIS_MODULE ,
. open = tled_proc_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
. write = tled_proc_write ,
} ;
2005-04-16 15:20:36 -07:00
static int get_lcd_state ( void )
{
int lcd = 0 ;
2008-02-04 23:31:18 -08:00
if ( hotk - > model = = L3H ) {
/* L3H and the like have to be handled differently */
2005-04-16 15:20:36 -07:00
acpi_status status = 0 ;
struct acpi_object_list input ;
union acpi_object mt_params [ 2 ] ;
struct acpi_buffer output ;
union acpi_object out_obj ;
2005-08-05 00:44:28 -04:00
2005-04-16 15:20:36 -07:00
input . count = 2 ;
input . pointer = mt_params ;
2008-10-17 14:35:14 -04:00
/* Note: the following values are partly guessed up, but
2005-04-16 15:20:36 -07:00
otherwise they seem to work */
mt_params [ 0 ] . type = ACPI_TYPE_INTEGER ;
mt_params [ 0 ] . integer . value = 0x02 ;
mt_params [ 1 ] . type = ACPI_TYPE_INTEGER ;
mt_params [ 1 ] . integer . value = 0x02 ;
output . length = sizeof ( out_obj ) ;
output . pointer = & out_obj ;
2005-08-05 00:44:28 -04:00
status =
acpi_evaluate_object ( NULL , hotk - > methods - > lcd_status ,
& input , & output ) ;
2005-04-16 15:20:36 -07:00
if ( status ! = AE_OK )
return - 1 ;
if ( out_obj . type = = ACPI_TYPE_INTEGER )
/* That's what the AML code does */
lcd = out_obj . integer . value > > 8 ;
2008-02-04 23:31:18 -08:00
} else if ( hotk - > model = = F3Sa ) {
2008-10-10 02:22:59 -04:00
unsigned long long tmp ;
2008-02-04 23:31:18 -08:00
union acpi_object param ;
struct acpi_object_list input ;
acpi_status status ;
/* Read pin 11 */
param . type = ACPI_TYPE_INTEGER ;
param . integer . value = 0x11 ;
input . count = 1 ;
input . pointer = & param ;
status = acpi_evaluate_integer ( NULL , hotk - > methods - > lcd_status ,
& input , & tmp ) ;
if ( status ! = AE_OK )
return - 1 ;
lcd = tmp ;
} else {
/* We don't have to check anything if we are here */
if ( ! read_acpi_int ( NULL , hotk - > methods - > lcd_status , & lcd ) )
printk ( KERN_WARNING
" Asus ACPI: Error reading LCD status \n " ) ;
if ( hotk - > model = = L2D )
lcd = ~ lcd ;
2005-04-16 15:20:36 -07:00
}
2005-08-05 00:44:28 -04:00
2005-04-16 15:20:36 -07:00
return ( lcd & 1 ) ;
}
static int set_lcd_state ( int value )
{
int lcd = 0 ;
acpi_status status = 0 ;
lcd = value ? 1 : 0 ;
if ( lcd ! = get_lcd_state ( ) ) {
/* switch */
if ( hotk - > model ! = L3H ) {
status =
2005-08-05 00:44:28 -04:00
acpi_evaluate_object ( NULL ,
hotk - > methods - > mt_lcd_switch ,
2005-04-16 15:20:36 -07:00
NULL , NULL ) ;
2008-10-17 14:35:14 -04:00
} else {
/* L3H and the like must be handled differently */
2005-08-05 00:44:28 -04:00
if ( ! write_acpi_int
( hotk - > handle , hotk - > methods - > mt_lcd_switch , 0x07 ,
NULL ) )
2005-04-16 15:20:36 -07:00
status = AE_ERROR ;
2008-10-17 14:35:14 -04:00
/* L3H's AML executes EHK (0x07) upon Fn+F7 keypress,
2005-04-16 15:20:36 -07:00
the exact behaviour is simulated here */
}
if ( ACPI_FAILURE ( status ) )
printk ( KERN_WARNING " Asus ACPI: Error switching LCD \n " ) ;
}
return 0 ;
}
2009-12-21 16:20:01 -08:00
static int lcd_proc_show ( struct seq_file * m , void * v )
2005-04-16 15:20:36 -07:00
{
2009-12-21 16:20:01 -08:00
seq_printf ( m , " %d \n " , get_lcd_state ( ) ) ;
return 0 ;
2005-04-16 15:20:36 -07:00
}
2009-12-21 16:20:01 -08:00
static int lcd_proc_open ( struct inode * inode , struct file * file )
{
return single_open ( file , lcd_proc_show , NULL ) ;
}
static ssize_t lcd_proc_write ( struct file * file , const char __user * buffer ,
size_t count , loff_t * pos )
2005-04-16 15:20:36 -07:00
{
2006-10-10 14:20:35 -07:00
int rv , value ;
2005-08-05 00:44:28 -04:00
2006-10-10 14:20:35 -07:00
rv = parse_arg ( buffer , count , & value ) ;
if ( rv > 0 )
2005-04-16 15:20:36 -07:00
set_lcd_state ( value ) ;
2006-10-10 14:20:35 -07:00
return rv ;
2005-04-16 15:20:36 -07:00
}
2009-12-21 16:20:01 -08:00
static const struct file_operations lcd_proc_fops = {
. owner = THIS_MODULE ,
. open = lcd_proc_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
. write = lcd_proc_write ,
} ;
2006-10-20 14:30:29 -07:00
static int read_brightness ( struct backlight_device * bd )
2005-04-16 15:20:36 -07:00
{
int value ;
2005-08-05 00:44:28 -04:00
if ( hotk - > methods - > brightness_get ) { /* SPLV/GPLV laptop */
if ( ! read_acpi_int ( hotk - > handle , hotk - > methods - > brightness_get ,
2005-04-16 15:20:36 -07:00
& value ) )
2005-08-05 00:44:28 -04:00
printk ( KERN_WARNING
" Asus ACPI: Error reading brightness \n " ) ;
} else if ( hotk - > methods - > brightness_status ) { /* For D1 for example */
if ( ! read_acpi_int ( NULL , hotk - > methods - > brightness_status ,
2005-04-16 15:20:36 -07:00
& value ) )
2005-08-05 00:44:28 -04:00
printk ( KERN_WARNING
" Asus ACPI: Error reading brightness \n " ) ;
} else /* No GPLV method */
2005-04-16 15:20:36 -07:00
value = hotk - > brightness ;
return value ;
}
/*
* Change the brightness level
*/
2006-10-20 14:30:29 -07:00
static int set_brightness ( int value )
2005-04-16 15:20:36 -07:00
{
acpi_status status = 0 ;
2006-10-20 14:30:29 -07:00
int ret = 0 ;
2005-04-16 15:20:36 -07:00
/* SPLV laptop */
2005-08-05 00:44:28 -04:00
if ( hotk - > methods - > brightness_set ) {
if ( ! write_acpi_int ( hotk - > handle , hotk - > methods - > brightness_set ,
2005-04-16 15:20:36 -07:00
value , NULL ) )
2005-08-05 00:44:28 -04:00
printk ( KERN_WARNING
" Asus ACPI: Error changing brightness \n " ) ;
2006-10-20 14:30:29 -07:00
ret = - EIO ;
goto out ;
2005-04-16 15:20:36 -07:00
}
/* No SPLV method if we are here, act as appropriate */
2006-10-20 14:30:29 -07:00
value - = read_brightness ( NULL ) ;
2005-04-16 15:20:36 -07:00
while ( value ! = 0 ) {
2005-08-05 00:44:28 -04:00
status = acpi_evaluate_object ( NULL , ( value > 0 ) ?
hotk - > methods - > brightness_up :
2005-04-16 15:20:36 -07:00
hotk - > methods - > brightness_down ,
NULL , NULL ) ;
( value > 0 ) ? value - - : value + + ;
if ( ACPI_FAILURE ( status ) )
2005-08-05 00:44:28 -04:00
printk ( KERN_WARNING
" Asus ACPI: Error changing brightness \n " ) ;
2006-10-20 14:30:29 -07:00
ret = - EIO ;
2005-04-16 15:20:36 -07:00
}
2006-10-20 14:30:29 -07:00
out :
return ret ;
}
static int set_brightness_status ( struct backlight_device * bd )
{
2007-02-10 23:07:48 +00:00
return set_brightness ( bd - > props . brightness ) ;
2005-04-16 15:20:36 -07:00
}
2009-12-21 16:20:01 -08:00
static int brn_proc_show ( struct seq_file * m , void * v )
2005-04-16 15:20:36 -07:00
{
2009-12-21 16:20:01 -08:00
seq_printf ( m , " %d \n " , read_brightness ( NULL ) ) ;
return 0 ;
2005-04-16 15:20:36 -07:00
}
2009-12-21 16:20:01 -08:00
static int brn_proc_open ( struct inode * inode , struct file * file )
{
return single_open ( file , brn_proc_show , NULL ) ;
}
static ssize_t brn_proc_write ( struct file * file , const char __user * buffer ,
size_t count , loff_t * pos )
2005-04-16 15:20:36 -07:00
{
2006-10-10 14:20:35 -07:00
int rv , value ;
2005-04-16 15:20:36 -07:00
2006-10-10 14:20:35 -07:00
rv = parse_arg ( buffer , count , & value ) ;
if ( rv > 0 ) {
2005-04-16 15:20:36 -07:00
value = ( 0 < value ) ? ( ( 15 < value ) ? 15 : value ) : 0 ;
2005-08-05 00:44:28 -04:00
/* 0 <= value <= 15 */
2005-04-16 15:20:36 -07:00
set_brightness ( value ) ;
}
2006-10-10 14:20:35 -07:00
return rv ;
2005-04-16 15:20:36 -07:00
}
2009-12-21 16:20:01 -08:00
static const struct file_operations brn_proc_fops = {
. owner = THIS_MODULE ,
. open = brn_proc_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
. write = brn_proc_write ,
} ;
2005-04-16 15:20:36 -07:00
static void set_display ( int value )
{
/* no sanity check needed for now */
2005-08-05 00:44:28 -04:00
if ( ! write_acpi_int ( hotk - > handle , hotk - > methods - > display_set ,
2005-04-16 15:20:36 -07:00
value , NULL ) )
printk ( KERN_WARNING " Asus ACPI: Error setting display \n " ) ;
return ;
}
/*
2008-10-17 14:35:14 -04:00
* Now , * this * one could be more user - friendly , but so far , no - one has
2005-04-16 15:20:36 -07:00
* complained . The significance of bits is the same as in proc_write_disp ( )
*/
2009-12-21 16:20:01 -08:00
static int disp_proc_show ( struct seq_file * m , void * v )
2005-04-16 15:20:36 -07:00
{
int value = 0 ;
2005-08-05 00:44:28 -04:00
2005-04-16 15:20:36 -07:00
if ( ! read_acpi_int ( hotk - > handle , hotk - > methods - > display_get , & value ) )
2005-08-05 00:44:28 -04:00
printk ( KERN_WARNING
" Asus ACPI: Error reading display status \n " ) ;
2008-10-17 14:35:14 -04:00
value & = 0x07 ; /* needed for some models, shouldn't hurt others */
2009-12-21 16:20:01 -08:00
seq_printf ( m , " %d \n " , value ) ;
return 0 ;
}
static int disp_proc_open ( struct inode * inode , struct file * file )
{
return single_open ( file , disp_proc_show , NULL ) ;
2005-04-16 15:20:36 -07:00
}
/*
2008-10-17 14:35:14 -04:00
* Experimental support for display switching . As of now : 1 should activate
* the LCD output , 2 should do for CRT , and 4 for TV - Out . Any combination
* ( bitwise ) of these will suffice . I never actually tested 3 displays hooked
* up simultaneously , so be warned . See the acpi4asus README for more info .
2005-04-16 15:20:36 -07:00
*/
2009-12-21 16:20:01 -08:00
static ssize_t disp_proc_write ( struct file * file , const char __user * buffer ,
size_t count , loff_t * pos )
2005-04-16 15:20:36 -07:00
{
2006-10-10 14:20:35 -07:00
int rv , value ;
2005-04-16 15:20:36 -07:00
2006-10-10 14:20:35 -07:00
rv = parse_arg ( buffer , count , & value ) ;
if ( rv > 0 )
2005-04-16 15:20:36 -07:00
set_display ( value ) ;
2006-10-10 14:20:35 -07:00
return rv ;
2005-04-16 15:20:36 -07:00
}
2009-12-21 16:20:01 -08:00
static const struct file_operations disp_proc_fops = {
. owner = THIS_MODULE ,
. open = disp_proc_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
. write = disp_proc_write ,
} ;
2005-04-16 15:20:36 -07:00
static int
2009-12-21 16:20:01 -08:00
asus_proc_add ( char * name , const struct file_operations * proc_fops , mode_t mode ,
2005-08-05 00:44:28 -04:00
struct acpi_device * device )
2005-04-16 15:20:36 -07:00
{
2009-12-21 16:20:01 -08:00
struct proc_dir_entry * proc ;
proc = proc_create_data ( name , mode , acpi_device_dir ( device ) ,
proc_fops , acpi_driver_data ( device ) ) ;
2005-08-05 00:44:28 -04:00
if ( ! proc ) {
2005-04-16 15:20:36 -07:00
printk ( KERN_WARNING " Unable to create %s fs entry \n " , name ) ;
return - 1 ;
}
proc - > uid = asus_uid ;
proc - > gid = asus_gid ;
return 0 ;
}
2006-03-28 17:04:00 -05:00
static int asus_hotk_add_fs ( struct acpi_device * device )
2005-04-16 15:20:36 -07:00
{
struct proc_dir_entry * proc ;
mode_t mode ;
2005-08-05 00:44:28 -04:00
2005-04-16 15:20:36 -07:00
/*
* If parameter uid or gid is not changed , keep the default setting for
* our proc entries ( - rw - rw - rw - ) else , it means we care about security ,
* and then set to - rw - rw - - - -
*/
2005-08-05 00:44:28 -04:00
if ( ( asus_uid = = 0 ) & & ( asus_gid = = 0 ) ) {
2005-04-16 15:20:36 -07:00
mode = S_IFREG | S_IRUGO | S_IWUGO ;
} else {
mode = S_IFREG | S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP ;
2006-01-03 23:05:00 -05:00
printk ( KERN_WARNING " asus_uid and asus_gid parameters are "
" deprecated, use chown and chmod instead! \n " ) ;
2005-04-16 15:20:36 -07:00
}
acpi_device_dir ( device ) = asus_proc_dir ;
if ( ! acpi_device_dir ( device ) )
return - ENODEV ;
2009-12-21 16:20:01 -08:00
proc = proc_create ( PROC_INFO , mode , acpi_device_dir ( device ) ,
& asus_info_proc_fops ) ;
2005-04-16 15:20:36 -07:00
if ( proc ) {
proc - > uid = asus_uid ;
proc - > gid = asus_gid ;
} else {
printk ( KERN_WARNING " Unable to create " PROC_INFO
" fs entry \n " ) ;
}
if ( hotk - > methods - > mt_wled ) {
2009-12-21 16:20:01 -08:00
asus_proc_add ( PROC_WLED , & wled_proc_fops , mode , device ) ;
2005-04-16 15:20:36 -07:00
}
2006-06-30 19:04:00 -04:00
if ( hotk - > methods - > mt_ledd ) {
2009-12-21 16:20:01 -08:00
asus_proc_add ( PROC_LEDD , & ledd_proc_fops , mode , device ) ;
2006-06-30 19:04:00 -04:00
}
2005-04-16 15:20:36 -07:00
if ( hotk - > methods - > mt_mled ) {
2009-12-21 16:20:01 -08:00
asus_proc_add ( PROC_MLED , & mled_proc_fops , mode , device ) ;
2005-04-16 15:20:36 -07:00
}
if ( hotk - > methods - > mt_tled ) {
2009-12-21 16:20:01 -08:00
asus_proc_add ( PROC_TLED , & tled_proc_fops , mode , device ) ;
2005-04-16 15:20:36 -07:00
}
2006-06-30 19:07:00 -04:00
if ( hotk - > methods - > mt_bt_switch ) {
2009-12-21 16:20:01 -08:00
asus_proc_add ( PROC_BT , & bluetooth_proc_fops , mode , device ) ;
2006-06-30 19:07:00 -04:00
}
2008-10-17 14:35:14 -04:00
/*
* We need both read node and write method as LCD switch is also
* accessible from the keyboard
2005-04-16 15:20:36 -07:00
*/
if ( hotk - > methods - > mt_lcd_switch & & hotk - > methods - > lcd_status ) {
2009-12-21 16:20:01 -08:00
asus_proc_add ( PROC_LCD , & lcd_proc_fops , mode , device ) ;
2005-04-16 15:20:36 -07:00
}
2005-08-05 00:44:28 -04:00
2005-04-16 15:20:36 -07:00
if ( ( hotk - > methods - > brightness_up & & hotk - > methods - > brightness_down ) | |
( hotk - > methods - > brightness_get & & hotk - > methods - > brightness_set ) ) {
2009-12-21 16:20:01 -08:00
asus_proc_add ( PROC_BRN , & brn_proc_fops , mode , device ) ;
2005-04-16 15:20:36 -07:00
}
if ( hotk - > methods - > display_set ) {
2009-12-21 16:20:01 -08:00
asus_proc_add ( PROC_DISP , & disp_proc_fops , mode , device ) ;
2005-04-16 15:20:36 -07:00
}
return 0 ;
}
2005-08-05 00:44:28 -04:00
static int asus_hotk_remove_fs ( struct acpi_device * device )
2005-04-16 15:20:36 -07:00
{
2005-08-05 00:44:28 -04:00
if ( acpi_device_dir ( device ) ) {
remove_proc_entry ( PROC_INFO , acpi_device_dir ( device ) ) ;
2005-04-16 15:20:36 -07:00
if ( hotk - > methods - > mt_wled )
2005-08-05 00:44:28 -04:00
remove_proc_entry ( PROC_WLED , acpi_device_dir ( device ) ) ;
2005-04-16 15:20:36 -07:00
if ( hotk - > methods - > mt_mled )
2005-08-05 00:44:28 -04:00
remove_proc_entry ( PROC_MLED , acpi_device_dir ( device ) ) ;
2005-04-16 15:20:36 -07:00
if ( hotk - > methods - > mt_tled )
2005-08-05 00:44:28 -04:00
remove_proc_entry ( PROC_TLED , acpi_device_dir ( device ) ) ;
2006-06-30 19:04:00 -04:00
if ( hotk - > methods - > mt_ledd )
remove_proc_entry ( PROC_LEDD , acpi_device_dir ( device ) ) ;
2006-06-30 19:07:00 -04:00
if ( hotk - > methods - > mt_bt_switch )
remove_proc_entry ( PROC_BT , acpi_device_dir ( device ) ) ;
2005-04-16 15:20:36 -07:00
if ( hotk - > methods - > mt_lcd_switch & & hotk - > methods - > lcd_status )
remove_proc_entry ( PROC_LCD , acpi_device_dir ( device ) ) ;
2005-08-05 00:44:28 -04:00
if ( ( hotk - > methods - > brightness_up
& & hotk - > methods - > brightness_down )
| | ( hotk - > methods - > brightness_get
& & hotk - > methods - > brightness_set ) )
2005-04-16 15:20:36 -07:00
remove_proc_entry ( PROC_BRN , acpi_device_dir ( device ) ) ;
if ( hotk - > methods - > display_set )
remove_proc_entry ( PROC_DISP , acpi_device_dir ( device ) ) ;
}
return 0 ;
}
2009-04-30 09:35:58 -06:00
static void asus_hotk_notify ( struct acpi_device * device , u32 event )
2005-04-16 15:20:36 -07:00
{
2005-08-05 00:44:28 -04:00
/* TODO Find a better way to handle events count. */
2005-04-16 15:20:36 -07:00
if ( ! hotk )
return ;
2009-04-30 09:35:58 -06:00
/*
* The BIOS * should * be sending us device events , but apparently
* Asus uses system events instead , so just ignore any device
* events we get .
*/
if ( event > ACPI_MAX_SYS_NOTIFY )
return ;
2008-10-17 14:35:14 -04:00
if ( ( event & ~ ( ( u32 ) BR_UP ) ) < 16 )
2005-04-16 15:20:36 -07:00
hotk - > brightness = ( event & ~ ( ( u32 ) BR_UP ) ) ;
2008-10-17 14:35:14 -04:00
else if ( ( event & ~ ( ( u32 ) BR_DOWN ) ) < 16 )
2005-04-16 15:20:36 -07:00
hotk - > brightness = ( event & ~ ( ( u32 ) BR_DOWN ) ) ;
2007-08-23 15:20:26 -04:00
acpi_bus_generate_proc_event ( hotk - > device , event ,
2005-04-16 15:20:36 -07:00
hotk - > event_count [ event % 128 ] + + ) ;
return ;
}
2006-06-30 19:11:00 -04:00
/*
* Match the model string to the list of supported models . Return END_MODEL if
* no match or model is NULL .
*/
static int asus_model_match ( char * model )
{
if ( model = = NULL )
return END_MODEL ;
if ( strncmp ( model , " L3D " , 3 ) = = 0 )
return L3D ;
else if ( strncmp ( model , " L2E " , 3 ) = = 0 | |
strncmp ( model , " L3H " , 3 ) = = 0 | | strncmp ( model , " L5D " , 3 ) = = 0 )
return L3H ;
else if ( strncmp ( model , " L3 " , 2 ) = = 0 | | strncmp ( model , " L2B " , 3 ) = = 0 )
return L3C ;
else if ( strncmp ( model , " L8L " , 3 ) = = 0 )
return L8L ;
else if ( strncmp ( model , " L4R " , 3 ) = = 0 )
return L4R ;
else if ( strncmp ( model , " M6N " , 3 ) = = 0 | | strncmp ( model , " W3N " , 3 ) = = 0 )
return M6N ;
else if ( strncmp ( model , " M6R " , 3 ) = = 0 | | strncmp ( model , " A3G " , 3 ) = = 0 )
return M6R ;
else if ( strncmp ( model , " M2N " , 3 ) = = 0 | |
strncmp ( model , " M3N " , 3 ) = = 0 | |
strncmp ( model , " M5N " , 3 ) = = 0 | |
strncmp ( model , " M6N " , 3 ) = = 0 | |
strncmp ( model , " S1N " , 3 ) = = 0 | |
strncmp ( model , " S5N " , 3 ) = = 0 | | strncmp ( model , " W1N " , 3 ) = = 0 )
return xxN ;
else if ( strncmp ( model , " M1 " , 2 ) = = 0 )
return M1A ;
else if ( strncmp ( model , " M2 " , 2 ) = = 0 | | strncmp ( model , " L4E " , 3 ) = = 0 )
return M2E ;
else if ( strncmp ( model , " L2 " , 2 ) = = 0 )
return L2D ;
else if ( strncmp ( model , " L8 " , 2 ) = = 0 )
return S1x ;
else if ( strncmp ( model , " D1 " , 2 ) = = 0 )
return D1x ;
else if ( strncmp ( model , " A1 " , 2 ) = = 0 )
return A1x ;
else if ( strncmp ( model , " A2 " , 2 ) = = 0 )
return A2x ;
else if ( strncmp ( model , " J1 " , 2 ) = = 0 )
return S2x ;
else if ( strncmp ( model , " L5 " , 2 ) = = 0 )
return L5x ;
else if ( strncmp ( model , " A4G " , 3 ) = = 0 )
return A4G ;
else if ( strncmp ( model , " W1N " , 3 ) = = 0 )
return W1N ;
2006-08-14 22:37:20 -07:00
else if ( strncmp ( model , " W3V " , 3 ) = = 0 )
return W3V ;
2006-06-30 19:11:00 -04:00
else if ( strncmp ( model , " W5A " , 3 ) = = 0 )
return W5A ;
2009-01-20 16:17:41 +01:00
else if ( strncmp ( model , " R1F " , 3 ) = = 0 )
return R1F ;
2007-02-05 16:09:09 -08:00
else if ( strncmp ( model , " A4S " , 3 ) = = 0 )
return A4S ;
2008-02-04 23:31:18 -08:00
else if ( strncmp ( model , " F3Sa " , 4 ) = = 0 )
return F3Sa ;
2006-06-30 19:11:00 -04:00
else
return END_MODEL ;
}
2005-04-16 15:20:36 -07:00
/*
* This function is used to initialize the hotk with right values . In this
* method , we can make all the detection we want , and modify the hotk struct
*/
2006-03-28 17:04:00 -05:00
static int asus_hotk_get_info ( void )
2005-04-16 15:20:36 -07:00
{
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER , NULL } ;
union acpi_object * model = NULL ;
int bsts_result ;
2006-06-30 19:11:00 -04:00
char * string = NULL ;
2005-04-16 15:20:36 -07:00
acpi_status status ;
/*
2008-10-17 14:35:14 -04:00
* Get DSDT headers early enough to allow for differentiating between
* models , but late enough to allow acpi_bus_register_driver ( ) to fail
2005-04-16 15:20:36 -07:00
* before doing anything ACPI - specific . Should we encounter a machine ,
* which needs special handling ( i . e . its hotkey device has a different
* HID ) , this bit will be moved . A global variable asus_info contains
* the DSDT header .
*/
2007-02-02 19:48:19 +03:00
status = acpi_get_table ( ACPI_SIG_DSDT , 1 , & asus_info ) ;
2005-04-16 15:20:36 -07:00
if ( ACPI_FAILURE ( status ) )
printk ( KERN_WARNING " Couldn't get the DSDT table header \n " ) ;
/* We have to write 0 on init this far for all ASUS models */
if ( ! write_acpi_int ( hotk - > handle , " INIT " , 0 , & buffer ) ) {
printk ( KERN_ERR " Hotkey initialization failed \n " ) ;
return - ENODEV ;
}
/* This needs to be called for some laptops to init properly */
if ( ! read_acpi_int ( hotk - > handle , " BSTS " , & bsts_result ) )
printk ( KERN_WARNING " Error calling BSTS \n " ) ;
else if ( bsts_result )
2005-08-05 00:44:28 -04:00
printk ( KERN_NOTICE " BSTS called, 0x%02x returned \n " ,
bsts_result ) ;
2005-04-16 15:20:36 -07:00
2005-12-22 12:42:00 -05:00
/*
2006-06-30 19:11:00 -04:00
* Try to match the object returned by INIT to the specific model .
2008-10-17 14:35:14 -04:00
* Handle every possible object ( or the lack of thereof ) the DSDT
* writers might throw at us . When in trouble , we pass NULL to
2006-06-30 19:11:00 -04:00
* asus_model_match ( ) and try something completely different .
2005-12-22 12:42:00 -05:00
*/
2006-06-30 19:11:00 -04:00
if ( buffer . pointer ) {
2006-10-01 00:28:50 +02:00
model = buffer . pointer ;
2006-06-30 19:11:00 -04:00
switch ( model - > type ) {
case ACPI_TYPE_STRING :
string = model - > string . pointer ;
break ;
case ACPI_TYPE_BUFFER :
string = model - > buffer . pointer ;
break ;
default :
kfree ( model ) ;
2007-07-29 00:45:59 +02:00
model = NULL ;
2006-06-30 19:11:00 -04:00
break ;
}
}
hotk - > model = asus_model_match ( string ) ;
if ( hotk - > model = = END_MODEL ) { /* match failed */
if ( asus_info & &
2005-04-16 15:20:36 -07:00
strncmp ( asus_info - > oem_table_id , " ODEM " , 4 ) = = 0 ) {
hotk - > model = P30 ;
2005-08-05 00:44:28 -04:00
printk ( KERN_NOTICE
" Samsung P30 detected, supported \n " ) ;
2005-04-16 15:20:36 -07:00
} else {
hotk - > model = M2E ;
2006-06-30 19:11:00 -04:00
printk ( KERN_NOTICE " unsupported model %s, trying "
" default values \n " , string ) ;
printk ( KERN_NOTICE
" send /proc/acpi/dsdt to the developers \n " ) ;
2008-10-17 01:42:41 -04:00
kfree ( model ) ;
return - ENODEV ;
2005-04-16 15:20:36 -07:00
}
hotk - > methods = & model_conf [ hotk - > model ] ;
2005-12-22 12:42:00 -05:00
return AE_OK ;
2005-04-16 15:20:36 -07:00
}
hotk - > methods = & model_conf [ hotk - > model ] ;
2006-06-30 19:11:00 -04:00
printk ( KERN_NOTICE " %s model detected, supported \n " , string ) ;
2005-04-16 15:20:36 -07:00
/* Sort of per-model blacklist */
2006-06-30 19:11:00 -04:00
if ( strncmp ( string , " L2B " , 3 ) = = 0 )
2005-08-05 00:44:28 -04:00
hotk - > methods - > lcd_status = NULL ;
2008-10-17 14:35:14 -04:00
/* L2B is similar enough to L3C to use its settings, with this only
2005-04-16 15:20:36 -07:00
exception */
2006-06-30 19:11:00 -04:00
else if ( strncmp ( string , " A3G " , 3 ) = = 0 )
2006-06-30 19:03:00 -04:00
hotk - > methods - > lcd_status = " \\ BLFG " ;
/* A3G is like M6R */
2006-06-30 19:11:00 -04:00
else if ( strncmp ( string , " S5N " , 3 ) = = 0 | |
strncmp ( string , " M5N " , 3 ) = = 0 | |
strncmp ( string , " W3N " , 3 ) = = 0 )
2005-08-05 00:44:28 -04:00
hotk - > methods - > mt_mled = NULL ;
2006-06-30 19:05:00 -04:00
/* S5N, M5N and W3N have no MLED */
2006-06-30 19:11:00 -04:00
else if ( strncmp ( string , " L5D " , 3 ) = = 0 )
2006-06-30 19:08:00 -04:00
hotk - > methods - > mt_wled = NULL ;
/* L5D's WLED is not controlled by ACPI */
2006-06-30 19:13:00 -04:00
else if ( strncmp ( string , " M2N " , 3 ) = = 0 | |
2006-08-14 22:37:20 -07:00
strncmp ( string , " W3V " , 3 ) = = 0 | |
2006-06-30 19:13:00 -04:00
strncmp ( string , " S1N " , 3 ) = = 0 )
2005-08-05 00:44:28 -04:00
hotk - > methods - > mt_wled = " WLED " ;
2006-08-14 22:37:20 -07:00
/* M2N, S1N and W3V have a usable WLED */
2005-04-16 15:20:36 -07:00
else if ( asus_info ) {
if ( strncmp ( asus_info - > oem_table_id , " L1 " , 2 ) = = 0 )
hotk - > methods - > mled_status = NULL ;
2005-08-05 00:44:28 -04:00
/* S1300A reports L84F, but L1400B too, account for that */
2005-04-16 15:20:36 -07:00
}
2006-06-30 03:19:10 -04:00
kfree ( model ) ;
2005-04-16 15:20:36 -07:00
return AE_OK ;
}
2006-03-28 17:04:00 -05:00
static int asus_hotk_check ( void )
2005-04-16 15:20:36 -07:00
{
int result = 0 ;
result = acpi_bus_get_status ( hotk - > device ) ;
if ( result )
return result ;
if ( hotk - > device - > status . present ) {
result = asus_hotk_get_info ( ) ;
} else {
printk ( KERN_ERR " Hotkey device not present, aborting \n " ) ;
return - EINVAL ;
}
return result ;
}
2006-03-28 17:04:00 -05:00
static int asus_hotk_found ;
2006-03-28 17:04:00 -05:00
static int asus_hotk_add ( struct acpi_device * device )
2005-04-16 15:20:36 -07:00
{
acpi_status status = AE_OK ;
int result ;
printk ( KERN_NOTICE " Asus Laptop ACPI Extras version %s \n " ,
ASUS_ACPI_VERSION ) ;
2006-12-19 12:56:11 -08:00
hotk = kzalloc ( sizeof ( struct asus_hotk ) , GFP_KERNEL ) ;
2005-04-16 15:20:36 -07:00
if ( ! hotk )
return - ENOMEM ;
hotk - > handle = device - > handle ;
strcpy ( acpi_device_name ( device ) , ACPI_HOTK_DEVICE_NAME ) ;
strcpy ( acpi_device_class ( device ) , ACPI_HOTK_CLASS ) ;
2008-09-22 14:37:34 -07:00
device - > driver_data = hotk ;
2005-04-16 15:20:36 -07:00
hotk - > device = device ;
result = asus_hotk_check ( ) ;
if ( result )
goto end ;
result = asus_hotk_add_fs ( device ) ;
if ( result )
goto end ;
/* For laptops without GPLV: init the hotk->brightness value */
2005-08-05 00:44:28 -04:00
if ( ( ! hotk - > methods - > brightness_get )
& & ( ! hotk - > methods - > brightness_status )
2006-06-30 19:03:00 -04:00
& & ( hotk - > methods - > brightness_up & & hotk - > methods - > brightness_down ) ) {
2005-08-05 00:44:28 -04:00
status =
acpi_evaluate_object ( NULL , hotk - > methods - > brightness_down ,
NULL , NULL ) ;
2005-04-16 15:20:36 -07:00
if ( ACPI_FAILURE ( status ) )
printk ( KERN_WARNING " Error changing brightness \n " ) ;
else {
2005-08-05 00:44:28 -04:00
status =
acpi_evaluate_object ( NULL ,
hotk - > methods - > brightness_up ,
NULL , NULL ) ;
2005-04-16 15:20:36 -07:00
if ( ACPI_FAILURE ( status ) )
2005-08-05 00:44:28 -04:00
printk ( KERN_WARNING " Strange, error changing "
2005-04-16 15:20:36 -07:00
" brightness \n " ) ;
}
}
2006-03-28 17:04:00 -05:00
asus_hotk_found = 1 ;
2006-06-30 19:04:00 -04:00
/* LED display is off by default */
hotk - > ledd_status = 0xFFF ;
2008-10-17 14:35:14 -04:00
end :
if ( result )
2005-04-16 15:20:36 -07:00
kfree ( hotk ) ;
return result ;
}
static int asus_hotk_remove ( struct acpi_device * device , int type )
{
asus_hotk_remove_fs ( device ) ;
kfree ( hotk ) ;
return 0 ;
}
2007-02-10 23:07:48 +00:00
static struct backlight_ops asus_backlight_data = {
2008-10-17 14:35:14 -04:00
. get_brightness = read_brightness ,
. update_status = set_brightness_status ,
2006-10-20 14:30:29 -07:00
} ;
2007-06-01 00:47:12 -07:00
static void asus_acpi_exit ( void )
2006-10-20 14:30:29 -07:00
{
if ( asus_backlight_device )
backlight_device_unregister ( asus_backlight_device ) ;
acpi_bus_unregister_driver ( & asus_hotk_driver ) ;
remove_proc_entry ( PROC_ASUS , acpi_root_dir ) ;
return ;
}
2005-04-16 15:20:36 -07:00
static int __init asus_acpi_init ( void )
{
int result ;
2009-12-03 07:44:49 +00:00
result = acpi_bus_register_driver ( & asus_hotk_driver ) ;
if ( result < 0 )
return result ;
2005-04-16 15:20:36 -07:00
asus_proc_dir = proc_mkdir ( PROC_ASUS , acpi_root_dir ) ;
if ( ! asus_proc_dir ) {
printk ( KERN_ERR " Asus ACPI: Unable to create /proc entry \n " ) ;
2009-12-03 07:44:49 +00:00
acpi_bus_unregister_driver ( & asus_hotk_driver ) ;
2005-04-16 15:20:36 -07:00
return - ENODEV ;
}
2006-03-28 17:04:00 -05:00
/*
* This is a bit of a kludge . We only want this module loaded
* for ASUS systems , but there ' s currently no way to probe the
* ACPI namespace for ASUS HIDs . So we just return failure if
* we didn ' t find one , which will cause the module to be
* unloaded .
*/
if ( ! asus_hotk_found ) {
2005-04-16 15:20:36 -07:00
acpi_bus_unregister_driver ( & asus_hotk_driver ) ;
remove_proc_entry ( PROC_ASUS , acpi_root_dir ) ;
2007-07-01 12:06:38 -07:00
return - ENODEV ;
2005-04-16 15:20:36 -07:00
}
2008-10-17 14:35:14 -04:00
asus_backlight_device = backlight_device_register ( " asus " , NULL , NULL ,
2006-10-20 14:30:29 -07:00
& asus_backlight_data ) ;
2008-10-17 14:35:14 -04:00
if ( IS_ERR ( asus_backlight_device ) ) {
2006-10-20 14:30:29 -07:00
printk ( KERN_ERR " Could not register asus backlight device \n " ) ;
asus_backlight_device = NULL ;
asus_acpi_exit ( ) ;
2007-07-01 12:06:38 -07:00
return - ENODEV ;
2006-10-20 14:30:29 -07:00
}
2008-10-17 14:35:14 -04:00
asus_backlight_device - > props . max_brightness = 15 ;
2005-04-16 15:20:36 -07:00
2006-10-20 14:30:29 -07:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
module_init ( asus_acpi_init ) ;
module_exit ( asus_acpi_exit ) ;