Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
linux
Commits
5166c979
Commit
5166c979
authored
Jun 12, 2003
by
Andy Grover
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
ACPI: Add ASUS Value-add driver (Karol Kozimor and Julien Lerouge)
parent
76751fb8
Changes
3
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
977 additions
and
0 deletions
+977
-0
drivers/acpi/Kconfig
drivers/acpi/Kconfig
+25
-0
drivers/acpi/Makefile
drivers/acpi/Makefile
+1
-0
drivers/acpi/asus_acpi.c
drivers/acpi/asus_acpi.c
+951
-0
No files found.
drivers/acpi/Kconfig
View file @
5166c979
...
...
@@ -135,6 +135,31 @@ config ACPI_NUMA
bool "NUMA support" if NUMA && (IA64 && !IA64_HP_SIM || X86 && ACPI && !ACPI_HT_ONLY && !X86_64)
default y if IA64 && IA64_SGI_SN
config ACPI_ASUS
tristate "ASUS/Medion Laptop Extras"
depends on X86 && ACPI && !ACPI_HT_ONLY
---help---
This driver provides support for extra features of ACPI-compatible
ASUS laptops. As some of Medion laptops are made by ASUS, it may also
support some Medion laptops (such as 9675 for example). It makes all
the extra buttons generate standard ACPI events that go through
/proc/acpi/events, and (on some models) adds support for changing the
display brightness and output, switching the LCD backlight on and off,
and most importantly, allows you to blink those fancy LEDs intended
for reporting mail and wireless status.
All settings are changed via /proc/acpi/asus directory entries. Owner
and group for these entries can be set with asus_uid and asus_gid
parameters.
More information and a userspace daemon for handling the extra buttons
at <http://sourceforge.net/projects/acpi4asus/>.
If you have an ACPI-compatible ASUS laptop, say Y or M here. This
driver is still under development, so if your laptop is unsupported or
something works not quite as expected, please use the mailing list
available on the above page (acpi4asus-user@lists.sourceforge.net)
config ACPI_TOSHIBA
tristate "Toshiba Laptop Extras"
depends on X86 && ACPI && !ACPI_HT_ONLY
...
...
drivers/acpi/Makefile
View file @
5166c979
...
...
@@ -44,5 +44,6 @@ obj-$(CONFIG_ACPI_THERMAL) += thermal.o
obj-$(CONFIG_ACPI_SYSTEM)
+=
system.o event.o
obj-$(CONFIG_ACPI_DEBUG)
+=
debug.o
obj-$(CONFIG_ACPI_NUMA)
+=
numa.o
obj-$(CONFIG_ACPI_ASUS)
+=
asus_acpi.o
obj-$(CONFIG_ACPI_TOSHIBA)
+=
toshiba_acpi.o
obj-$(CONFIG_ACPI_BUS)
+=
scan.o
drivers/acpi/asus_acpi.c
0 → 100644
View file @
5166c979
/*
* asus_acpi.c - Asus Laptop ACPI Extras
*
*
* Copyright (C) 2002, 2003 Julien Lerouge, Karol Kozimor
*
* 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:
* Johann Wiesner - Small compile fixes
* John Belmonte - ACPI code for Toshiba laptop was a good starting point.
*
* TODO
* add Fn key status
* Add mode selection on module loading (parameter) -> still necessary ?
* Complete display switching -- may require dirty hacks?
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/proc_fs.h>
#include <acpi/acpi_drivers.h>
#include <acpi/acpi_bus.h>
#define ASUS_ACPI_VERSION "0.24a"
#define PROC_ASUS "asus" //the directory
#define PROC_MLED "mled"
#define PROC_WLED "wled"
#define PROC_INFOS "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"
#define ACPI_HOTK_HID "ATK0100"
/*
* Some events we use, same for all Asus
*/
#define BR_UP 0x10
#define BR_DOWN 0x20
/*
* Flags for hotk status
*/
#define MLED_ON 0x01 //is MLED ON ?
#define WLED_ON 0x02
MODULE_AUTHOR
(
"Julien Lerouge, Karol Kozimor"
);
MODULE_DESCRIPTION
(
ACPI_HOTK_NAME
);
MODULE_LICENSE
(
"GPL"
);
static
uid_t
asus_uid
=
0
;
static
gid_t
asus_gid
=
0
;
MODULE_PARM
(
asus_uid
,
"i"
);
MODULE_PARM_DESC
(
uid
,
"UID for entries in /proc/acpi/asus.
\n
"
);
MODULE_PARM
(
asus_gid
,
"i"
);
MODULE_PARM_DESC
(
gid
,
"GID for entries in /proc/acpi/asus.
\n
"
);
/* For each model, all features implemented */
struct
model_data
{
char
*
name
;
//name of the laptop
char
*
mt_mled
;
//method to handle mled
char
*
mled_status
;
//node to handle mled reading
char
*
mt_wled
;
//method to handle wled
char
*
wled_status
;
//node to handle wled reading
char
*
mt_lcd_switch
;
//method to turn LCD ON/OFF
char
*
lcd_status
;
//node to read LCD panel state
char
*
brightness_up
;
//method to set brightness up
char
*
brightness_down
;
//guess what ?
char
*
brightness_set
;
//method to set absolute brightness
char
*
brightness_get
;
//method to get absolute brightness
char
*
brightness_status
;
//node to get brightness
char
*
display_set
;
//method to set video output
char
*
display_get
;
//method to get video output
};
/*
* This is the main structure, we can use it to store anything interesting
* about the hotk device
*/
struct
asus_hotk
{
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, ...
struct
model_data
*
methods
;
//methods available on the laptop
u8
brightness
;
//brighness level
enum
{
L2X
=
0
,
//L200D -> TODO check Q11 (Fn+F8)
// Calling this method simply hang the
// computer, ISMI method hangs the laptop.
L3X
,
//L3C
L3D
,
//L3400D
M2X
,
//M2400E
S1X
,
//S1300A -> TODO special keys do not work ?
D1X
,
//D1
L1X
,
//L1400B
A1X
,
//A1340D, A1300F
J1X
,
//S200 (J1)
//TODO A1370D does not seems to have a ATK device
// L8400 model doesn't have ATK
END_MODEL
,
}
model
;
//Models currently supported
u16
event_count
[
128
];
//count for each event TODO make this better
};
/* Here we go */
#define L3X_PREFIX "\\_SB.PCI0.PX40.ECD0."
#define S1X_PREFIX "\\_SB.PCI0.PX40."
#define L1X_PREFIX S1X_PREFIX
#define A1X_PREFIX "\\_SB.PCI0.ISA.EC0."
#define J1X_PREFIX A1X_PREFIX
static
struct
model_data
model_conf
[
END_MODEL
]
=
{
/*
* name| mled |mled read| wled |wled read| lcd sw |lcd read |
* br up|br down | br set | br read | br status|set disp | get disp
*
* br set and read shall be in hotk device !
* same for set disp
*
* 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 ?
*
*/
{
"L2X"
,
"MLED"
,
"
\\
SGP6"
,
"WLED"
,
"
\\
RCP3"
,
"
\\
Q10"
,
"
\\
SGP0"
,
"
\\
Q0E"
,
"
\\
Q0F"
,
NULL
,
NULL
,
NULL
,
"SDSP"
,
"
\\
INFB"
},
{
"L3X"
,
"MLED"
,
NULL
,
"WLED"
,
NULL
,
L3X_PREFIX
"_Q10"
,
"
\\
GL32"
,
L3X_PREFIX
"_Q0F"
,
L3X_PREFIX
"_Q0E"
,
"SPLV"
,
"GPLV"
,
"
\\
BLVL"
,
"SDSP"
,
"
\\
_SB.PCI0.PCI1.VGAC.NMAP"
},
{
"L3D"
,
"MLED"
,
"
\\
MALD"
,
"WLED"
,
NULL
,
"
\\
Q10"
,
"
\\
BKLG"
,
"
\\
Q0E"
,
"
\\
Q0F"
,
"SPLV"
,
"GPLV"
,
"
\\
BLVL"
,
"SDSP"
,
"
\\
INFB"
},
{
"M2X"
,
"MLED"
,
NULL
,
"WLED"
,
NULL
,
"
\\
Q10"
,
"
\\
GP06"
,
"
\\
Q0E"
,
"
\\
Q0F"
,
"SPLV"
,
"GPLV"
,
NULL
,
"SDSP"
,
"
\\
INFB"
},
{
"S1X"
,
"MLED"
,
"
\\
EMLE"
,
"WLED"
,
NULL
,
S1X_PREFIX
"Q10"
,
"
\\
PNOF"
,
S1X_PREFIX
"Q0F"
,
S1X_PREFIX
"Q0E"
,
"SPLV"
,
"GPLV"
,
"
\\
BRIT"
,
NULL
,
NULL
},
{
"D1X"
,
"MLED"
,
NULL
,
NULL
,
NULL
,
"
\\
Q0D"
,
"
\\
GP11"
,
"
\\
Q0C"
,
"
\\
Q0B"
,
NULL
,
NULL
,
"
\\
BLVL"
,
"SDSP"
,
"
\\
INFB"
},
{
"L1X"
,
"MLED"
,
NULL
,
"WLED"
,
NULL
,
L1X_PREFIX
"Q10"
,
"
\\
PNOF"
,
L1X_PREFIX
"Q0F"
,
L1X_PREFIX
"Q0E"
,
"SPLV"
,
"GPLV"
,
"
\\
BRIT"
,
NULL
,
NULL
},
{
"A1X"
,
"MLED"
,
"
\\
MAIL"
,
NULL
,
NULL
,
A1X_PREFIX
"_Q10"
,
"
\\
BKLI"
,
A1X_PREFIX
"_Q0E"
,
A1X_PREFIX
"_Q0F"
,
NULL
,
NULL
,
NULL
,
NULL
,
NULL
},
{
"J1X"
,
"MLED"
,
"
\\
MAIL"
,
NULL
,
NULL
,
J1X_PREFIX
"_Q10"
,
"
\\
BKLI"
,
J1X_PREFIX
"_Q0B"
,
J1X_PREFIX
"_Q0A"
,
NULL
,
NULL
,
NULL
,
NULL
,
NULL
}
};
/* procdir we use */
static
struct
proc_dir_entry
*
asus_proc_dir
=
NULL
;
/*
* 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
=
NULL
;
/*
* The hotkey driver declaration
*/
static
int
asus_hotk_add
(
struct
acpi_device
*
device
);
static
int
asus_hotk_remove
(
struct
acpi_device
*
device
,
int
type
);
static
struct
acpi_driver
asus_hotk_driver
=
{
.
name
=
ACPI_HOTK_NAME
,
.
class
=
ACPI_HOTK_CLASS
,
.
ids
=
ACPI_HOTK_HID
,
.
ops
=
{
.
add
=
asus_hotk_add
,
.
remove
=
asus_hotk_remove
,
},
};
/*
* 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
*
* returns 1 if write is successful, 0 else.
*/
static
int
write_acpi_int
(
acpi_handle
handle
,
const
char
*
method
,
int
val
,
struct
acpi_buffer
*
output
)
{
struct
acpi_object_list
params
;
//list of input parameters (an int here)
union
acpi_object
in_obj
;
//the only param we use
acpi_status
status
;
params
.
count
=
1
;
params
.
pointer
=
&
in_obj
;
in_obj
.
type
=
ACPI_TYPE_INTEGER
;
in_obj
.
integer
.
value
=
val
;
status
=
acpi_evaluate_object
(
handle
,
(
char
*
)
method
,
&
params
,
output
);
return
(
status
==
AE_OK
);
}
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
;
status
=
acpi_evaluate_object
(
handle
,
(
char
*
)
method
,
NULL
,
&
output
);
*
val
=
out_obj
.
integer
.
value
;
return
(
status
==
AE_OK
)
&&
(
out_obj
.
type
==
ACPI_TYPE_INTEGER
);
}
/*
* 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
int
proc_read_info
(
char
*
page
,
char
**
start
,
off_t
off
,
int
count
,
int
*
eof
,
void
*
data
)
{
int
len
=
0
;
struct
asus_hotk
*
hotk
=
(
struct
asus_hotk
*
)
data
;
char
buf
[
16
];
//enough for all info
/*
* We use the easy way, we don't care of off and count, so we don't set eof
* to 1
*/
len
+=
sprintf
(
page
,
ACPI_HOTK_NAME
" "
ASUS_ACPI_VERSION
"
\n
"
);
len
+=
sprintf
(
page
+
len
,
"Model reference : %s
\n
"
,
hotk
->
methods
->
name
);
if
(
asus_info
)
{
snprintf
(
buf
,
5
,
"%s"
,
asus_info
->
signature
);
len
+=
sprintf
(
page
+
len
,
"ACPI signature : %s
\n
"
,
buf
);
snprintf
(
buf
,
16
,
"%d"
,
asus_info
->
length
);
len
+=
sprintf
(
page
+
len
,
"Table length : %s
\n
"
,
buf
);
snprintf
(
buf
,
16
,
"%d"
,
asus_info
->
revision
);
len
+=
sprintf
(
page
+
len
,
"ACPI minor version : %s
\n
"
,
buf
);
snprintf
(
buf
,
16
,
"%d"
,
asus_info
->
checksum
);
len
+=
sprintf
(
page
+
len
,
"Checksum : %s
\n
"
,
buf
);
snprintf
(
buf
,
7
,
"%s"
,
asus_info
->
oem_id
);
len
+=
sprintf
(
page
+
len
,
"OEM identification : %s
\n
"
,
buf
);
snprintf
(
buf
,
9
,
"%s"
,
asus_info
->
oem_table_id
);
len
+=
sprintf
(
page
+
len
,
"OEM table id : %s
\n
"
,
buf
);
snprintf
(
buf
,
16
,
"%x"
,
asus_info
->
oem_revision
);
len
+=
sprintf
(
page
+
len
,
"OEM rev number : 0x%s
\n
"
,
buf
);
snprintf
(
buf
,
5
,
"%s"
,
asus_info
->
asl_compiler_id
);
len
+=
sprintf
(
page
+
len
,
"ASL comp vendor ID : %s
\n
"
,
buf
);
snprintf
(
buf
,
16
,
"%x"
,
asus_info
->
asl_compiler_revision
);
len
+=
sprintf
(
page
+
len
,
"ASL comp rev number: 0x%s
\n
"
,
buf
);
}
return
len
;
}
/*
* proc file handlers
*/
static
int
proc_read_mled
(
char
*
page
,
char
**
start
,
off_t
off
,
int
count
,
int
*
eof
,
void
*
data
)
{
int
len
=
0
;
struct
asus_hotk
*
hotk
=
(
struct
asus_hotk
*
)
data
;
int
led_status
=
0
;
/*
* We use the easy way, we don't care of off and count, so we don't set eof
* to 1
*/
if
(
hotk
->
methods
->
mled_status
)
{
if
(
read_acpi_int
(
NULL
,
hotk
->
methods
->
mled_status
,
&
led_status
))
len
=
sprintf
(
page
,
"%d
\n
"
,
led_status
);
else
printk
(
KERN_NOTICE
"Asus ACPI: Error reading MLED "
"status
\n
"
);
}
else
{
len
=
sprintf
(
page
,
"%d
\n
"
,
(
hotk
->
status
&
MLED_ON
)
?
1
:
0
);
}
return
len
;
}
static
int
proc_write_mled
(
struct
file
*
file
,
const
char
*
buffer
,
unsigned
long
count
,
void
*
data
)
{
int
value
;
int
led_out
=
0
;
struct
asus_hotk
*
hotk
=
(
struct
asus_hotk
*
)
data
;
/* scan expression. Multiple expressions may be delimited with ; */
if
(
sscanf
(
buffer
,
"%i"
,
&
value
)
==
1
)
led_out
=
~
value
&
1
;
hotk
->
status
=
(
value
)
?
(
hotk
->
status
|
MLED_ON
)
:
(
hotk
->
status
&
~
MLED_ON
);
/* We don't have to check mt_mled exists if we are here :) */
if
(
!
write_acpi_int
(
hotk
->
handle
,
hotk
->
methods
->
mt_mled
,
led_out
,
NULL
))
printk
(
KERN_NOTICE
"Asus ACPI: MLED write failed
\n
"
);
return
count
;
}
/*
* 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
int
proc_read_wled
(
char
*
page
,
char
**
start
,
off_t
off
,
int
count
,
int
*
eof
,
void
*
data
)
{
int
len
=
0
;
struct
asus_hotk
*
hotk
=
(
struct
asus_hotk
*
)
data
;
int
led_status
;
if
(
hotk
->
methods
->
wled_status
)
{
if
(
read_acpi_int
(
NULL
,
hotk
->
methods
->
mled_status
,
&
led_status
))
len
=
sprintf
(
page
,
"%d
\n
"
,
led_status
);
else
printk
(
KERN_NOTICE
"Asus ACPI: Error reading WLED "
"status
\n
"
);
}
else
{
len
=
sprintf
(
page
,
"%d
\n
"
,
(
hotk
->
status
&
WLED_ON
)
?
1
:
0
);
}
return
len
;
}
static
int
proc_write_wled
(
struct
file
*
file
,
const
char
*
buffer
,
unsigned
long
count
,
void
*
data
)
{
int
value
;
int
led_out
=
0
;
struct
asus_hotk
*
hotk
=
(
struct
asus_hotk
*
)
data
;
/* scan expression. Multiple expressions may be delimited with ; */
if
(
sscanf
(
buffer
,
"%i"
,
&
value
)
==
1
)
led_out
=
value
&
1
;
hotk
->
status
=
(
value
)
?
(
hotk
->
status
|
WLED_ON
)
:
(
hotk
->
status
&
~
WLED_ON
);
/* We don't have to check if mt_wled exists if we are here :) */
if
(
!
write_acpi_int
(
hotk
->
handle
,
hotk
->
methods
->
mt_wled
,
led_out
,
NULL
))
printk
(
KERN_NOTICE
"Asus ACPI: WLED write failed
\n
"
);
return
count
;
}
static
int
get_lcd_state
(
struct
asus_hotk
*
hotk
)
{
int
lcd
=
0
;
/* We don't have to check anything, if we are here */
if
(
!
read_acpi_int
(
NULL
,
hotk
->
methods
->
lcd_status
,
&
lcd
))
printk
(
KERN_NOTICE
"Asus ACPI: Error reading LCD status
\n
"
);
if
(
hotk
->
model
==
L2X
)
lcd
=
~
lcd
;
return
(
lcd
&
1
);
}
static
int
proc_read_lcd
(
char
*
page
,
char
**
start
,
off_t
off
,
int
count
,
int
*
eof
,
void
*
data
)
{
return
sprintf
(
page
,
"%d
\n
"
,
get_lcd_state
((
struct
asus_hotk
*
)
data
));
}
static
int
proc_write_lcd
(
struct
file
*
file
,
const
char
*
buffer
,
unsigned
long
count
,
void
*
data
)
{
int
value
;
int
lcd
=
0
;
acpi_status
status
=
0
;
int
lcd_status
=
0
;
struct
asus_hotk
*
hotk
=
(
struct
asus_hotk
*
)
data
;
/* scan expression. Multiple expressions may be delimited with ; */
if
(
sscanf
(
buffer
,
"%i"
,
&
value
)
==
1
)
lcd
=
value
&
1
;
lcd_status
=
get_lcd_state
(
hotk
);
if
(
lcd_status
!=
lcd
)
{
/* switch */
status
=
acpi_evaluate_object
(
NULL
,
hotk
->
methods
->
mt_lcd_switch
,
NULL
,
NULL
);
if
(
ACPI_FAILURE
(
status
))
printk
(
KERN_NOTICE
"Asus ACPI: Error switching LCD
\n
"
);
}
return
count
;
}
/*
* Change the brightness level
*/
static
void
set_brightness
(
int
value
,
struct
asus_hotk
*
hotk
)
{
acpi_status
status
=
0
;
/* ATKD laptop */
if
(
hotk
->
methods
->
brightness_set
)
{
if
(
!
write_acpi_int
(
hotk
->
handle
,
hotk
->
methods
->
brightness_set
,
value
,
NULL
))
printk
(
KERN_NOTICE
"Asus ACPI: Error changing brightness
\n
"
);
return
;
}
/* HOTK laptop if we are here, act as appropriate */
value
-=
hotk
->
brightness
;
while
(
value
!=
0
)
{
status
=
acpi_evaluate_object
(
NULL
,
(
value
>
0
)
?
hotk
->
methods
->
brightness_up
:
hotk
->
methods
->
brightness_down
,
NULL
,
NULL
);
(
value
>
0
)
?
value
--
:
value
++
;
if
(
ACPI_FAILURE
(
status
))
printk
(
KERN_NOTICE
"Asus ACPI: Error changing brightness
\n
"
);
}
return
;
}
static
int
read_brightness
(
struct
asus_hotk
*
hotk
)
{
int
value
;
if
(
hotk
->
methods
->
brightness_get
)
{
/* ATKD laptop */
if
(
!
read_acpi_int
(
hotk
->
handle
,
hotk
->
methods
->
brightness_get
,
&
value
))
printk
(
KERN_NOTICE
"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
,
&
value
))
printk
(
KERN_NOTICE
"Asus ACPI: Error reading brightness
\n
"
);
}
else
/* HOTK laptop */
value
=
hotk
->
brightness
;
return
value
;
}
static
int
proc_read_brn
(
char
*
page
,
char
**
start
,
off_t
off
,
int
count
,
int
*
eof
,
void
*
data
)
{
struct
asus_hotk
*
hotk
=
(
struct
asus_hotk
*
)
data
;
return
sprintf
(
page
,
"%d
\n
"
,
read_brightness
(
hotk
));
}
static
int
proc_write_brn
(
struct
file
*
file
,
const
char
*
buffer
,
unsigned
long
count
,
void
*
data
)
{
int
value
;
struct
asus_hotk
*
hotk
=
(
struct
asus_hotk
*
)
data
;
/* scan expression. Multiple expressions may be delimited with ; */
if
(
sscanf
(
buffer
,
"%d"
,
&
value
)
==
1
)
{
value
=
(
0
<
value
)
?
((
15
<
value
)
?
15
:
value
)
:
0
;
/* 0 <= value <= 15 */
set_brightness
(
value
,
hotk
);
}
else
{
printk
(
KERN_NOTICE
"Asus ACPI: Error reading user input
\n
"
);
}
return
count
;
}
static
void
set_display
(
int
value
,
struct
asus_hotk
*
hotk
)
{
/* no sanity check needed for now */
if
(
!
write_acpi_int
(
hotk
->
handle
,
hotk
->
methods
->
display_set
,
value
,
NULL
))
printk
(
KERN_NOTICE
"Asus ACPI: Error setting display
\n
"
);
return
;
}
/*
* Now, *this* one could be more user-friendly, but so far, no-one has
* complained. The significance of bits is the same as in proc_write_disp()
*/
static
int
proc_read_disp
(
char
*
page
,
char
**
start
,
off_t
off
,
int
count
,
int
*
eof
,
void
*
data
)
{
int
value
=
0
;
struct
asus_hotk
*
hotk
=
(
struct
asus_hotk
*
)
data
;
if
(
!
read_acpi_int
(
hotk
->
handle
,
hotk
->
methods
->
display_get
,
&
value
))
printk
(
KERN_NOTICE
"Asus ACPI: Error reading display status
\n
"
);
return
sprintf
(
page
,
"%d
\n
"
,
value
);
}
/*
* Preliminary support for display switching. As of now: 0x01 should activate
* the LCD output, 0x02 should do for CRT, and 0x04 for TV-Out. Any combination
* (bitwise) of these will suffice. I never actually tested 3 displays hooked up
* simultaneously, so be warned.
*/
static
int
proc_write_disp
(
struct
file
*
file
,
const
char
*
buffer
,
unsigned
long
count
,
void
*
data
)
{
int
value
;
struct
asus_hotk
*
hotk
=
(
struct
asus_hotk
*
)
data
;
/* scan expression. Multiple expressions may be delimited with ; */
if
(
sscanf
(
buffer
,
"%d"
,
&
value
)
==
1
)
set_display
(
value
,
hotk
);
else
{
printk
(
KERN_NOTICE
"Asus ACPI: Error reading user input
\n
"
);
}
return
count
;
}
static
int
asus_hotk_add_fs
(
struct
acpi_device
*
device
)
{
struct
proc_dir_entry
*
proc
;
struct
asus_hotk
*
hotk
=
acpi_driver_data
(
device
);
mode_t
mode
;
/*
* 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----
*/
if
((
asus_uid
==
0
)
&&
(
asus_gid
==
0
)){
mode
=
S_IFREG
|
S_IRUGO
|
S_IWUGO
;
}
else
{
mode
=
S_IFREG
|
S_IRUSR
|
S_IRGRP
|
S_IWUSR
|
S_IWGRP
;
}
acpi_device_dir
(
device
)
=
asus_proc_dir
;
if
(
!
acpi_device_dir
(
device
))
return
(
-
ENODEV
);
proc
=
create_proc_entry
(
PROC_INFOS
,
mode
,
acpi_device_dir
(
device
));
if
(
proc
)
{
proc
->
read_proc
=
proc_read_info
;
proc
->
data
=
acpi_driver_data
(
device
);
proc
->
owner
=
THIS_MODULE
;
proc
->
uid
=
asus_uid
;
proc
->
gid
=
asus_gid
;;
}
else
{
printk
(
KERN_NOTICE
" Unable to create "
PROC_INFOS
" fs entry
\n
"
);
}
if
(
hotk
->
methods
->
mt_wled
)
{
proc
=
create_proc_entry
(
PROC_WLED
,
mode
,
acpi_device_dir
(
device
));
if
(
proc
)
{
proc
->
write_proc
=
proc_write_wled
;
proc
->
read_proc
=
proc_read_wled
;
proc
->
data
=
acpi_driver_data
(
device
);
proc
->
owner
=
THIS_MODULE
;
proc
->
uid
=
asus_uid
;
proc
->
gid
=
asus_gid
;;
}
else
{
printk
(
KERN_NOTICE
" Unable to create "
PROC_WLED
" fs entry
\n
"
);
}
}
if
(
hotk
->
methods
->
mt_mled
)
{
proc
=
create_proc_entry
(
PROC_MLED
,
mode
,
acpi_device_dir
(
device
));
if
(
proc
)
{
proc
->
write_proc
=
proc_write_mled
;
proc
->
read_proc
=
proc_read_mled
;
proc
->
data
=
acpi_driver_data
(
device
);
proc
->
owner
=
THIS_MODULE
;
proc
->
uid
=
asus_uid
;
proc
->
gid
=
asus_gid
;;
}
else
{
printk
(
KERN_NOTICE
" Unable to create "
PROC_MLED
" fs entry
\n
"
);
}
}
/*
* We need both read node and write method as LCD switch is also accessible
* from keyboard
*/
if
(
hotk
->
methods
->
mt_lcd_switch
&&
hotk
->
methods
->
lcd_status
)
{
proc
=
create_proc_entry
(
PROC_LCD
,
mode
,
acpi_device_dir
(
device
));
if
(
proc
)
{
proc
->
write_proc
=
proc_write_lcd
;
proc
->
read_proc
=
proc_read_lcd
;
proc
->
data
=
acpi_driver_data
(
device
);
proc
->
owner
=
THIS_MODULE
;
proc
->
uid
=
asus_uid
;
proc
->
gid
=
asus_gid
;;
}
else
{
printk
(
KERN_NOTICE
" Unable to create "
PROC_LCD
" fs entry
\n
"
);
}
}
if
((
hotk
->
methods
->
brightness_up
&&
hotk
->
methods
->
brightness_down
)
||
(
hotk
->
methods
->
brightness_get
&&
hotk
->
methods
->
brightness_get
))
{
proc
=
create_proc_entry
(
PROC_BRN
,
mode
,
acpi_device_dir
(
device
));
if
(
proc
)
{
proc
->
write_proc
=
proc_write_brn
;
proc
->
read_proc
=
proc_read_brn
;
proc
->
data
=
acpi_driver_data
(
device
);
proc
->
owner
=
THIS_MODULE
;
proc
->
uid
=
asus_uid
;
proc
->
gid
=
asus_gid
;;
}
else
{
printk
(
KERN_NOTICE
" Unable to create "
PROC_BRN
" fs entry
\n
"
);
}
}
if
(
hotk
->
methods
->
display_set
)
{
proc
=
create_proc_entry
(
PROC_DISP
,
mode
,
acpi_device_dir
(
device
));
if
(
proc
)
{
proc
->
write_proc
=
proc_write_disp
;
proc
->
read_proc
=
proc_read_disp
;
proc
->
data
=
acpi_driver_data
(
device
);
proc
->
owner
=
THIS_MODULE
;
proc
->
uid
=
asus_uid
;
proc
->
gid
=
asus_gid
;;
}
else
{
printk
(
KERN_NOTICE
" Unable to create "
PROC_DISP
" fs entry
\n
"
);
}
}
return
(
AE_OK
);
}
static
void
asus_hotk_notify
(
acpi_handle
handle
,
u32
event
,
void
*
data
)
{
/* TODO Find a better way to handle events count. Here, in data, we receive
* the hotk, so we can make anything !!
*/
struct
asus_hotk
*
hotk
=
(
struct
asus_hotk
*
)
data
;
if
(
!
hotk
)
return
;
if
((
event
&
~
((
u32
)
BR_UP
))
<
16
)
{
hotk
->
brightness
=
(
event
&
~
((
u32
)
BR_UP
));
}
else
if
((
event
&
~
((
u32
)
BR_DOWN
))
<
16
)
{
hotk
->
brightness
=
(
event
&
~
((
u32
)
BR_DOWN
));
}
acpi_bus_generate_event
(
hotk
->
device
,
event
,
hotk
->
event_count
[
event
%
128
]
++
);
return
;
}
/*
* 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
*/
static
int
asus_hotk_get_info
(
struct
asus_hotk
*
hotk
)
{
struct
acpi_buffer
buffer
=
{
ACPI_ALLOCATE_BUFFER
,
NULL
};
union
acpi_object
*
model
=
NULL
;
/*
* We have to write 0 on init this far for all ASUS models
*/
if
(
!
write_acpi_int
(
hotk
->
handle
,
"INIT"
,
0
,
&
buffer
))
{
printk
(
KERN_NOTICE
" Hotkey initialization failed
\n
"
);
return
-
ENODEV
;
}
/*
* Here, we also use asus_info to make decision. For example, on INIT
* method, S1X and L1X models both reports to be L84F, but they don't
* have the same methods (L1X has WLED, S1X don't)
*/
model
=
(
union
acpi_object
*
)
buffer
.
pointer
;
if
(
model
->
type
==
ACPI_TYPE_STRING
)
{
printk
(
KERN_NOTICE
" %s model detected, "
,
model
->
string
.
pointer
);
}
hotk
->
model
=
END_MODEL
;
if
(
strncmp
(
model
->
string
.
pointer
,
"L3D"
,
3
)
==
0
)
hotk
->
model
=
L3D
;
/*
* L2B has same settings that L3X, except for GL32, but as
* there is no node to get the LCD status, and as GL32 is never
* used anywhere else, I assume it's safe, even if lcd get is
* broken for this model (TODO fix it ?)
*/
else
if
(
strncmp
(
model
->
string
.
pointer
,
"L3"
,
2
)
==
0
||
strncmp
(
model
->
string
.
pointer
,
"L2B"
,
3
)
==
0
)
hotk
->
model
=
L3X
;
else
if
(
strncmp
(
model
->
string
.
pointer
,
"M2"
,
2
)
==
0
)
hotk
->
model
=
M2X
;
else
if
(
strncmp
(
model
->
string
.
pointer
,
"L2"
,
2
)
==
0
)
hotk
->
model
=
L2X
;
else
if
(
strncmp
(
model
->
string
.
pointer
,
"L8"
,
2
)
==
0
)
/* S1300A reports L84F, but L1400B too */
if
(
strncmp
(
asus_info
->
oem_table_id
,
"L1"
,
2
)
==
0
)
hotk
->
model
=
L1X
;
else
hotk
->
model
=
S1X
;
else
if
(
strncmp
(
model
->
string
.
pointer
,
"D1"
,
2
)
==
0
)
hotk
->
model
=
D1X
;
else
if
(
strncmp
(
model
->
string
.
pointer
,
"A1"
,
2
)
==
0
)
hotk
->
model
=
A1X
;
else
if
(
strncmp
(
model
->
string
.
pointer
,
"J1"
,
2
)
==
0
)
hotk
->
model
=
J1X
;
if
(
hotk
->
model
==
END_MODEL
)
{
/* By default use the same values, as I don't know others */
printk
(
"unsupported, trying default values, contact the "
"developers
\n
"
);
hotk
->
model
=
L2X
;
}
else
{
printk
(
"supported
\n
"
);
}
hotk
->
methods
=
&
model_conf
[
hotk
->
model
];
acpi_os_free
(
model
);
return
AE_OK
;
}
static
int
asus_hotk_check
(
struct
asus_hotk
*
hotk
)
{
int
result
=
0
;
if
(
!
hotk
)
return
(
-
EINVAL
);
result
=
acpi_bus_get_status
(
hotk
->
device
);
if
(
result
)
return
(
result
);
if
(
hotk
->
device
->
status
.
present
)
{
result
=
asus_hotk_get_info
(
hotk
);
}
else
{
printk
(
KERN_NOTICE
" Hotkey device not present, aborting
\n
"
);
return
(
-
EINVAL
);
}
return
(
result
);
}
static
int
asus_hotk_add
(
struct
acpi_device
*
device
)
{
struct
asus_hotk
*
hotk
=
NULL
;
acpi_status
status
=
AE_OK
;
int
result
;
if
(
!
device
)
return
(
-
EINVAL
);
hotk
=
(
struct
asus_hotk
*
)
kmalloc
(
sizeof
(
struct
asus_hotk
),
GFP_KERNEL
);
if
(
!
hotk
)
return
(
-
ENOMEM
);
memset
(
hotk
,
0
,
sizeof
(
struct
asus_hotk
));
hotk
->
handle
=
device
->
handle
;
sprintf
(
acpi_device_name
(
device
),
"%s"
,
ACPI_HOTK_DEVICE_NAME
);
sprintf
(
acpi_device_class
(
device
),
"%s"
,
ACPI_HOTK_CLASS
);
acpi_driver_data
(
device
)
=
hotk
;
hotk
->
device
=
device
;
result
=
asus_hotk_check
(
hotk
);
if
(
result
)
goto
end
;
result
=
asus_hotk_add_fs
(
device
);
if
(
result
)
goto
end
;
/*
* We install the handler, it will receive the hotk in parameter, so, we
* could add other data to the hotk struct
*/
status
=
acpi_install_notify_handler
(
hotk
->
handle
,
ACPI_SYSTEM_NOTIFY
,
asus_hotk_notify
,
hotk
);
if
(
ACPI_FAILURE
(
status
))
{
printk
(
KERN_NOTICE
" Error installing notify handler
\n
"
);
}
else
{
printk
(
KERN_DEBUG
" Notify Handler installed successfully
\n
"
);
}
/* For HOTK laptops: init the hotk->brightness value */
if
((
!
hotk
->
methods
->
brightness_get
)
&&
(
!
hotk
->
methods
->
brightness_status
)
&&
(
hotk
->
methods
->
brightness_up
&&
hotk
->
methods
->
brightness_down
))
{
status
=
acpi_evaluate_object
(
NULL
,
hotk
->
methods
->
brightness_down
,
NULL
,
NULL
);
if
(
ACPI_FAILURE
(
status
))
printk
(
KERN_NOTICE
" Error changing brightness
\n
"
);
status
=
acpi_evaluate_object
(
NULL
,
hotk
->
methods
->
brightness_up
,
NULL
,
NULL
);
if
(
ACPI_FAILURE
(
status
))
printk
(
KERN_NOTICE
" Error changing brightness
\n
"
);
}
end:
if
(
result
)
{
kfree
(
hotk
);
}
return
(
result
);
}
static
int
asus_hotk_remove
(
struct
acpi_device
*
device
,
int
type
)
{
acpi_status
status
=
0
;
struct
asus_hotk
*
hotk
=
NULL
;
if
(
!
device
||
!
acpi_driver_data
(
device
))
return
(
-
EINVAL
);
hotk
=
(
struct
asus_hotk
*
)
acpi_driver_data
(
device
);
status
=
acpi_remove_notify_handler
(
hotk
->
handle
,
ACPI_SYSTEM_NOTIFY
,
asus_hotk_notify
);
if
(
ACPI_FAILURE
(
status
))
printk
(
KERN_NOTICE
"Error removing notify handler
\n
"
);
kfree
(
hotk
);
return
(
0
);
}
static
int
__init
asus_acpi_init
(
void
)
{
int
result
=
0
;
acpi_status
status
=
0
;
struct
acpi_buffer
dsdt
=
{
ACPI_ALLOCATE_BUFFER
,
NULL
};
printk
(
KERN_NOTICE
"Asus Laptop ACPI Extras version %s
\n
"
,
ASUS_ACPI_VERSION
);
/*
* Here is the code to know the model we are running on. We need to
* know this before calling the acpi_bus_register_driver function, in
* case the HID for the laptop we are running on is different from
* ACPI_HOTK_HID, which I have never seen yet :)
*
* This information is then available in the global var asus_info
*/
status
=
acpi_get_table
(
ACPI_TABLE_DSDT
,
1
,
&
dsdt
);
if
(
ACPI_FAILURE
(
status
))
{
printk
(
KERN_NOTICE
" Couldn't get the DSDT table header
\n
"
);
}
else
{
asus_info
=
(
struct
acpi_table_header
*
)
dsdt
.
pointer
;
}
asus_proc_dir
=
proc_mkdir
(
PROC_ASUS
,
acpi_root_dir
);
if
(
!
asus_proc_dir
)
return
(
-
ENODEV
);
asus_proc_dir
->
owner
=
THIS_MODULE
;
result
=
acpi_bus_register_driver
(
&
asus_hotk_driver
);
if
(
result
<
0
)
{
printk
(
KERN_NOTICE
" Error registering "
ACPI_HOTK_NAME
"
\n
"
);
remove_proc_entry
(
PROC_ASUS
,
acpi_root_dir
);
return
(
-
ENODEV
);
}
return
(
0
);
}
static
void
__exit
asus_acpi_exit
(
void
)
{
acpi_bus_unregister_driver
(
&
asus_hotk_driver
);
remove_proc_entry
(
PROC_ASUS
,
acpi_root_dir
);
acpi_os_free
(
asus_info
);
return
;
}
module_init
(
asus_acpi_init
);
module_exit
(
asus_acpi_exit
);
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment