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
Kirill Smelkov
linux
Commits
a4144e45
Commit
a4144e45
authored
Feb 03, 2007
by
Len Brown
Browse files
Options
Browse Files
Download
Plain Diff
Pull asus into test branch
parents
eee3c859
8def05fa
Changes
5
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
1206 additions
and
5 deletions
+1206
-5
MAINTAINERS
MAINTAINERS
+8
-0
drivers/acpi/Kconfig
drivers/acpi/Kconfig
+8
-5
drivers/misc/Kconfig
drivers/misc/Kconfig
+19
-0
drivers/misc/Makefile
drivers/misc/Makefile
+1
-0
drivers/misc/asus-laptop.c
drivers/misc/asus-laptop.c
+1170
-0
No files found.
MAINTAINERS
View file @
a4144e45
...
...
@@ -584,6 +584,14 @@ W: http://sourceforge.net/projects/acpi4asus
W: http://xf.iksaif.net/acpi4asus
S: Maintained
ASUS LAPTOP EXTRAS DRIVER
P: Corentin Chary
M: corentincj@iksaif.net
L: acpi4asus-user@lists.sourceforge.net
W: http://sourceforge.net/projects/acpi4asus
W: http://xf.iksaif.net/acpi4asus
S: Maintained
ATA OVER ETHERNET DRIVER
P: Ed L. Cashin
M: ecashin@coraid.com
...
...
drivers/acpi/Kconfig
View file @
a4144e45
...
...
@@ -218,7 +218,10 @@ config ACPI_ASUS
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)
available on the above page (acpi4asus-user@lists.sourceforge.net).
NOTE: This driver is deprecated and will probably be removed soon,
use asus-laptop instead.
config ACPI_IBM
tristate "IBM ThinkPad Laptop Extras"
...
...
drivers/misc/Kconfig
View file @
a4144e45
...
...
@@ -69,6 +69,25 @@ config TIFM_7XX1
To compile this driver as a module, choose M here: the module will
be called tifm_7xx1.
config ASUS_LAPTOP
tristate "Asus Laptop Extras (EXPERIMENTAL)"
depends on X86
depends on ACPI
depends on EXPERIMENTAL && !ACPI_ASUS
depends on LEDS_CLASS
depends on BACKLIGHT_CLASS_DEVICE
---help---
This is the new Linux driver for Asus laptops. It may also support some
MEDION, JVC or VICTOR laptops. It makes all the extra buttons generate
standard ACPI events that go through /proc/acpi/events. It also adds
support for video output switching, LCD backlight control, Bluetooth and
Wlan control, and most importantly, allows you to blink those fancy LEDs.
For more information and a userspace daemon for handling the extra
buttons see <http://acpi4asus.sf.net/>.
If you have an ACPI-compatible ASUS laptop, say Y or M here.
config MSI_LAPTOP
tristate "MSI Laptop Extras"
depends on X86
...
...
drivers/misc/Makefile
View file @
a4144e45
...
...
@@ -6,6 +6,7 @@ obj- := misc.o # Dummy rule to force built-in.o to be made
obj-$(CONFIG_IBM_ASM)
+=
ibmasm/
obj-$(CONFIG_HDPU_FEATURES)
+=
hdpuftrs/
obj-$(CONFIG_MSI_LAPTOP)
+=
msi-laptop.o
obj-$(CONFIG_ASUS_LAPTOP)
+=
asus-laptop.o
obj-$(CONFIG_LKDTM)
+=
lkdtm.o
obj-$(CONFIG_TIFM_CORE)
+=
tifm_core.o
obj-$(CONFIG_TIFM_7XX1)
+=
tifm_7xx1.o
...
...
drivers/misc/asus-laptop.c
0 → 100644
View file @
a4144e45
/*
* asus-laptop.c - Asus Laptop Support
*
*
* Copyright (C) 2002-2005 Julien Lerouge, 2003-2006 Karol Kozimor
* Copyright (C) 2006 Corentin Chary
*
* 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
*
*/
#include <linux/autoconf.h>
#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>
#include <linux/backlight.h>
#include <linux/fb.h>
#include <linux/leds.h>
#include <linux/platform_device.h>
#include <acpi/acpi_drivers.h>
#include <acpi/acpi_bus.h>
#include <asm/uaccess.h>
#define ASUS_LAPTOP_VERSION "0.40"
#define ASUS_HOTK_NAME "Asus Laptop Support"
#define ASUS_HOTK_CLASS "hotkey"
#define ASUS_HOTK_DEVICE_NAME "Hotkey"
#define ASUS_HOTK_HID "ATK0100"
#define ASUS_HOTK_FILE "asus-laptop"
#define ASUS_HOTK_PREFIX "\\_SB.ATKD."
/*
* Some events we use, same for all Asus
*/
#define ATKD_BR_UP 0x10
#define ATKD_BR_DOWN 0x20
#define ATKD_LCD_ON 0x33
#define ATKD_LCD_OFF 0x34
/*
* Known bits returned by \_SB.ATKD.HWRS
*/
#define WL_HWRS 0x80
#define BT_HWRS 0x100
/*
* Flags for hotk status
* WL_ON and BT_ON are also used for wireless_status()
*/
#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 LCD_ON 0x40 //LCD backlight
#define ASUS_LOG ASUS_HOTK_FILE ": "
#define ASUS_ERR KERN_ERR ASUS_LOG
#define ASUS_WARNING KERN_WARNING ASUS_LOG
#define ASUS_NOTICE KERN_NOTICE ASUS_LOG
#define ASUS_INFO KERN_INFO ASUS_LOG
#define ASUS_DEBUG KERN_DEBUG ASUS_LOG
MODULE_AUTHOR
(
"Julien Lerouge, Karol Kozimor, Corentin Chary"
);
MODULE_DESCRIPTION
(
ASUS_HOTK_NAME
);
MODULE_LICENSE
(
"GPL"
);
#define ASUS_HANDLE(object, paths...) \
static acpi_handle object##_handle = NULL; \
static char *object##_paths[] = { paths }
/* LED */
ASUS_HANDLE
(
mled_set
,
ASUS_HOTK_PREFIX
"MLED"
);
ASUS_HANDLE
(
tled_set
,
ASUS_HOTK_PREFIX
"TLED"
);
ASUS_HANDLE
(
rled_set
,
ASUS_HOTK_PREFIX
"RLED"
);
/* W1JC */
ASUS_HANDLE
(
pled_set
,
ASUS_HOTK_PREFIX
"PLED"
);
/* A7J */
/* LEDD */
ASUS_HANDLE
(
ledd_set
,
ASUS_HOTK_PREFIX
"SLCM"
);
/* Bluetooth and WLAN
* WLED and BLED are not handled like other XLED, because in some dsdt
* they also control the WLAN/Bluetooth device.
*/
ASUS_HANDLE
(
wl_switch
,
ASUS_HOTK_PREFIX
"WLED"
);
ASUS_HANDLE
(
bt_switch
,
ASUS_HOTK_PREFIX
"BLED"
);
ASUS_HANDLE
(
wireless_status
,
ASUS_HOTK_PREFIX
"RSTS"
);
/* All new models */
/* Brightness */
ASUS_HANDLE
(
brightness_set
,
ASUS_HOTK_PREFIX
"SPLV"
);
ASUS_HANDLE
(
brightness_get
,
ASUS_HOTK_PREFIX
"GPLV"
);
/* Backlight */
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 */
"
\\
_SB.PCI0.PX40.Q10"
,
/* S1x */
"
\\
Q10"
);
/* A2x, L2D, L3D, M2E */
/* Display */
ASUS_HANDLE
(
display_set
,
ASUS_HOTK_PREFIX
"SDSP"
);
ASUS_HANDLE
(
display_get
,
"
\\
_SB.PCI0.P0P1.VGA.GETD"
,
/* A6B, A6K A6R A7D F3JM L4R M6R A3G
M6A M6V VX-1 V6J V6V W3Z */
"
\\
_SB.PCI0.P0P2.VGA.GETD"
,
/* A3E A4K, A4D A4L A6J A7J A8J Z71V M9V
S5A M5A z33A W1Jc W2V */
"
\\
_SB.PCI0.P0P3.VGA.GETD"
,
/* A6V A6Q */
"
\\
_SB.PCI0.P0PA.VGA.GETD"
,
/* A6T, A6M */
"
\\
_SB.PCI0.PCI1.VGAC.NMAP"
,
/* L3C */
"
\\
_SB.PCI0.VGA.GETD"
,
/* Z96F */
"
\\
ACTD"
,
/* A2D */
"
\\
ADVG"
,
/* A4G Z71A W1N W5A W5F M2N M3N M5N M6N S1N S5N */
"
\\
DNXT"
,
/* P30 */
"
\\
INFB"
,
/* A2H D1 L2D L3D L3H L2E L5D L5C M1A M2E L4L W3V */
"
\\
SSTE"
);
/* A3F A6F A3N A3L M6N W3N W6A */
ASUS_HANDLE
(
ls_switch
,
ASUS_HOTK_PREFIX
"ALSC"
);
/* Z71A Z71V */
ASUS_HANDLE
(
ls_level
,
ASUS_HOTK_PREFIX
"ALSL"
);
/* Z71A Z71V */
/*
* This is the main structure, we can use it to store anything interesting
* about the hotk device
*/
struct
asus_hotk
{
char
*
name
;
//laptop name
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
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
};
/*
* 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
;
/*
* 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
=
ASUS_HOTK_NAME
,
.
class
=
ASUS_HOTK_CLASS
,
.
ids
=
ASUS_HOTK_HID
,
.
ops
=
{
.
add
=
asus_hotk_add
,
.
remove
=
asus_hotk_remove
,
},
};
/* The backlight device /sys/class/backlight */
static
struct
backlight_device
*
asus_backlight_device
;
/*
* The backlight class declaration
*/
static
int
read_brightness
(
struct
backlight_device
*
bd
);
static
int
update_bl_status
(
struct
backlight_device
*
bd
);
static
struct
backlight_properties
asusbl_data
=
{
.
owner
=
THIS_MODULE
,
.
get_brightness
=
read_brightness
,
.
update_status
=
update_bl_status
,
.
max_brightness
=
15
,
};
/* These functions actually update the LED's, and are called from a
* workqueue. By doing this as separate work rather than when the LED
* subsystem asks, we avoid messing with the Asus ACPI stuff during a
* potentially bad time, such as a timer interrupt. */
static
struct
workqueue_struct
*
led_workqueue
;
#define ASUS_LED(object, ledname) \
static void object##_led_set(struct led_classdev *led_cdev, \
enum led_brightness value); \
static void object##_led_update(struct work_struct *ignored); \
static int object##_led_wk; \
DECLARE_WORK(object##_led_work, object##_led_update); \
static struct led_classdev object##_led = { \
.name = "asus:" ledname, \
.brightness_set = object##_led_set, \
}
ASUS_LED
(
mled
,
"mail"
);
ASUS_LED
(
tled
,
"touchpad"
);
ASUS_LED
(
rled
,
"record"
);
ASUS_LED
(
pled
,
"phone"
);
/*
* 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_object_list
*
params
)
{
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
,
params
,
&
output
);
*
val
=
out_obj
.
integer
.
value
;
return
(
status
==
AE_OK
)
&&
(
out_obj
.
type
==
ACPI_TYPE_INTEGER
);
}
static
int
read_wireless_status
(
int
mask
)
{
int
status
;
if
(
!
wireless_status_handle
)
return
(
hotk
->
status
&
mask
)
?
1
:
0
;
if
(
read_acpi_int
(
wireless_status_handle
,
NULL
,
&
status
,
NULL
))
{
return
(
status
&
mask
)
?
1
:
0
;
}
else
printk
(
ASUS_WARNING
"Error reading Wireless status
\n
"
);
return
(
hotk
->
status
&
mask
)
?
1
:
0
;
}
/* Generic LED functions */
static
int
read_status
(
int
mask
)
{
/* There is a special method for both wireless devices */
if
(
mask
==
BT_ON
||
mask
==
WL_ON
)
return
read_wireless_status
(
mask
);
return
(
hotk
->
status
&
mask
)
?
1
:
0
;
}
static
void
write_status
(
acpi_handle
handle
,
int
out
,
int
mask
,
int
invert
)
{
hotk
->
status
=
(
out
)
?
(
hotk
->
status
|
mask
)
:
(
hotk
->
status
&
~
mask
);
if
(
invert
)
/* invert target value */
out
=
!
out
&
0x1
;
if
(
handle
&&
!
write_acpi_int
(
handle
,
NULL
,
out
,
NULL
))
printk
(
ASUS_WARNING
" write failed
\n
"
);
}
/* /sys/class/led handlers */
#define ASUS_LED_HANDLER(object, mask, invert) \
static void object##_led_set(struct led_classdev *led_cdev, \
enum led_brightness value) \
{ \
object##_led_wk = value; \
queue_work(led_workqueue, &object##_led_work); \
} \
static void object##_led_update(struct work_struct *ignored) \
{ \
int value = object##_led_wk; \
write_status(object##_set_handle, value, (mask), (invert)); \
}
ASUS_LED_HANDLER
(
mled
,
MLED_ON
,
1
);
ASUS_LED_HANDLER
(
pled
,
PLED_ON
,
0
);
ASUS_LED_HANDLER
(
rled
,
RLED_ON
,
0
);
ASUS_LED_HANDLER
(
tled
,
TLED_ON
,
0
);
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
;
if
(
lcd_switch_handle
)
{
status
=
acpi_evaluate_object
(
lcd_switch_handle
,
NULL
,
NULL
,
NULL
);
if
(
ACPI_FAILURE
(
status
))
printk
(
ASUS_WARNING
"Error switching LCD
\n
"
);
}
write_status
(
NULL
,
lcd
,
LCD_ON
,
0
);
return
0
;
}
static
void
lcd_blank
(
int
blank
)
{
struct
backlight_device
*
bd
=
asus_backlight_device
;
if
(
bd
)
{
down
(
&
bd
->
sem
);
if
(
likely
(
bd
->
props
))
{
bd
->
props
->
power
=
blank
;
if
(
likely
(
bd
->
props
->
update_status
))
bd
->
props
->
update_status
(
bd
);
}
up
(
&
bd
->
sem
);
}
}
static
int
read_brightness
(
struct
backlight_device
*
bd
)
{
int
value
;
if
(
!
read_acpi_int
(
brightness_get_handle
,
NULL
,
&
value
,
NULL
))
printk
(
ASUS_WARNING
"Error reading brightness
\n
"
);
return
value
;
}
static
int
set_brightness
(
struct
backlight_device
*
bd
,
int
value
)
{
int
ret
=
0
;
value
=
(
0
<
value
)
?
((
15
<
value
)
?
15
:
value
)
:
0
;
/* 0 <= value <= 15 */
if
(
!
write_acpi_int
(
brightness_set_handle
,
NULL
,
value
,
NULL
))
{
printk
(
ASUS_WARNING
"Error changing brightness
\n
"
);
ret
=
-
EIO
;
}
return
ret
;
}
static
int
update_bl_status
(
struct
backlight_device
*
bd
)
{
int
rv
;
int
value
=
bd
->
props
->
brightness
;
rv
=
set_brightness
(
bd
,
value
);
if
(
rv
)
return
rv
;
value
=
(
bd
->
props
->
power
==
FB_BLANK_UNBLANK
)
?
1
:
0
;
return
set_lcd_state
(
value
);
}
/*
* 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
,
struct
device_attribute
*
attr
,
char
*
page
)
{
int
len
=
0
;
int
temp
;
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
,
ASUS_HOTK_NAME
" "
ASUS_LAPTOP_VERSION
"
\n
"
);
len
+=
sprintf
(
page
+
len
,
"Model reference : %s
\n
"
,
hotk
->
name
);
/*
* 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.
*/
if
(
read_acpi_int
(
hotk
->
handle
,
"SFUN"
,
&
temp
,
NULL
))
len
+=
sprintf
(
page
+
len
,
"SFUN value : 0x%04x
\n
"
,
temp
);
/*
* 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.
*/
if
(
read_acpi_int
(
hotk
->
handle
,
"ASYM"
,
&
temp
,
NULL
))
len
+=
sprintf
(
page
+
len
,
"ASYM value : 0x%04x
\n
"
,
temp
);
if
(
asus_info
)
{
snprintf
(
buf
,
16
,
"%d"
,
asus_info
->
length
);
len
+=
sprintf
(
page
+
len
,
"DSDT length : %s
\n
"
,
buf
);
snprintf
(
buf
,
16
,
"%d"
,
asus_info
->
checksum
);
len
+=
sprintf
(
page
+
len
,
"DSDT checksum : %s
\n
"
,
buf
);
snprintf
(
buf
,
16
,
"%d"
,
asus_info
->
revision
);
len
+=
sprintf
(
page
+
len
,
"DSDT revision : %s
\n
"
,
buf
);
snprintf
(
buf
,
7
,
"%s"
,
asus_info
->
oem_id
);
len
+=
sprintf
(
page
+
len
,
"OEM id : %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 revision : 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 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
;
}
static
ssize_t
store_status
(
const
char
*
buf
,
size_t
count
,
acpi_handle
handle
,
int
mask
,
int
invert
)
{
int
rv
,
value
;
int
out
=
0
;
rv
=
parse_arg
(
buf
,
count
,
&
value
);
if
(
rv
>
0
)
out
=
value
?
1
:
0
;
write_status
(
handle
,
out
,
mask
,
invert
);
return
rv
;
}
/*
* LEDD display
*/
static
ssize_t
show_ledd
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
char
*
buf
)
{
return
sprintf
(
buf
,
"0x%08x
\n
"
,
hotk
->
ledd_status
);
}
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
)
{
if
(
!
write_acpi_int
(
ledd_set_handle
,
NULL
,
value
,
NULL
))
printk
(
ASUS_WARNING
"LED display write failed
\n
"
);
else
hotk
->
ledd_status
=
(
u32
)
value
;
}
return
rv
;
}
/*
* 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
)
{
return
store_status
(
buf
,
count
,
wl_switch_handle
,
WL_ON
,
0
);
}
/*
* Bluetooth
*/
static
ssize_t
show_bluetooth
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
char
*
buf
)
{
return
sprintf
(
buf
,
"%d
\n
"
,
read_status
(
BT_ON
));
}
static
ssize_t
store_bluetooth
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
const
char
*
buf
,
size_t
count
)
{
return
store_status
(
buf
,
count
,
bt_switch_handle
,
BT_ON
,
0
);
}
/*
* Display
*/
static
void
set_display
(
int
value
)
{
/* no sanity check needed for now */
if
(
!
write_acpi_int
(
display_set_handle
,
NULL
,
value
,
NULL
))
printk
(
ASUS_WARNING
"Error setting display
\n
"
);
return
;
}
static
int
read_display
(
void
)
{
int
value
=
0
;
/* In most of the case, we know how to set the display, but sometime
we can't read it */
if
(
display_get_handle
)
{
if
(
!
read_acpi_int
(
display_get_handle
,
NULL
,
&
value
,
NULL
))
printk
(
ASUS_WARNING
"Error reading display status
\n
"
);
}
value
&=
0x0F
;
/* needed for some models, shouldn't hurt others */
return
value
;
}
/*
* 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
;
}
/*
* Light Sens
*/
static
void
set_light_sens_switch
(
int
value
)
{
if
(
!
write_acpi_int
(
ls_switch_handle
,
NULL
,
value
,
NULL
))
printk
(
ASUS_WARNING
"Error setting light sensor switch
\n
"
);
hotk
->
light_switch
=
value
;
}
static
ssize_t
show_lssw
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
char
*
buf
)
{
return
sprintf
(
buf
,
"%d
\n
"
,
hotk
->
light_switch
);
}
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
)
{
if
(
!
write_acpi_int
(
ls_level_handle
,
NULL
,
value
,
NULL
))
printk
(
ASUS_WARNING
"Error setting light sensor level
\n
"
);
hotk
->
light_level
=
value
;
}
static
ssize_t
show_lslvl
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
char
*
buf
)
{
return
sprintf
(
buf
,
"%d
\n
"
,
hotk
->
light_level
);
}
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
;
}
static
void
asus_hotk_notify
(
acpi_handle
handle
,
u32
event
,
void
*
data
)
{
/* TODO Find a better way to handle events count. */
if
(
!
hotk
)
return
;
/*
* We need to tell the backlight device when the backlight power is
* switched
*/
if
(
event
==
ATKD_LCD_ON
)
{
write_status
(
NULL
,
1
,
LCD_ON
,
0
);
lcd_blank
(
FB_BLANK_UNBLANK
);
}
else
if
(
event
==
ATKD_LCD_OFF
)
{
write_status
(
NULL
,
0
,
LCD_ON
,
0
);
lcd_blank
(
FB_BLANK_POWERDOWN
);
}
acpi_bus_generate_event
(
hotk
->
device
,
event
,
hotk
->
event_count
[
event
%
128
]
++
);
return
;
}
#define ASUS_CREATE_DEVICE_ATTR(_name) \
struct device_attribute dev_attr_##_name = { \
.attr = { \
.name = __stringify(_name), \
.mode = 0, \
.owner = THIS_MODULE }, \
.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
);
static
ASUS_CREATE_DEVICE_ATTR
(
wlan
);
static
ASUS_CREATE_DEVICE_ATTR
(
bluetooth
);
static
ASUS_CREATE_DEVICE_ATTR
(
display
);
static
ASUS_CREATE_DEVICE_ATTR
(
ledd
);
static
ASUS_CREATE_DEVICE_ATTR
(
ls_switch
);
static
ASUS_CREATE_DEVICE_ATTR
(
ls_level
);
static
struct
attribute
*
asuspf_attributes
[]
=
{
&
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
,
NULL
};
static
struct
attribute_group
asuspf_attribute_group
=
{
.
attrs
=
asuspf_attributes
};
static
struct
platform_driver
asuspf_driver
=
{
.
driver
=
{
.
name
=
ASUS_HOTK_FILE
,
.
owner
=
THIS_MODULE
,
}
};
static
struct
platform_device
*
asuspf_device
;
static
void
asus_hotk_add_fs
(
void
)
{
ASUS_SET_DEVICE_ATTR
(
infos
,
0444
,
show_infos
,
NULL
);
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
);
if
(
display_set_handle
&&
display_get_handle
)
ASUS_SET_DEVICE_ATTR
(
display
,
0644
,
show_disp
,
store_disp
);
else
if
(
display_set_handle
)
ASUS_SET_DEVICE_ATTR
(
display
,
0200
,
NULL
,
store_disp
);
if
(
ledd_set_handle
)
ASUS_SET_DEVICE_ATTR
(
ledd
,
0644
,
show_ledd
,
store_ledd
);
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
);
}
}
static
int
asus_handle_init
(
char
*
name
,
acpi_handle
*
handle
,
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))
/*
* 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
(
void
)
{
struct
acpi_buffer
buffer
=
{
ACPI_ALLOCATE_BUFFER
,
NULL
};
struct
acpi_buffer
dsdt
=
{
ACPI_ALLOCATE_BUFFER
,
NULL
};
union
acpi_object
*
model
=
NULL
;
int
bsts_result
,
hwrs_result
;
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
* HID), this bit will be moved. A global variable asus_info contains
* the DSDT header.
*/
status
=
acpi_get_table
(
ACPI_TABLE_ID_DSDT
,
1
,
&
dsdt
);
if
(
ACPI_FAILURE
(
status
))
printk
(
ASUS_WARNING
"Couldn't get the DSDT table header
\n
"
);
else
asus_info
=
dsdt
.
pointer
;
/* We have to write 0 on init this far for all ASUS models */
if
(
!
write_acpi_int
(
hotk
->
handle
,
"INIT"
,
0
,
&
buffer
))
{
printk
(
ASUS_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
,
NULL
))
printk
(
ASUS_WARNING
"Error calling BSTS
\n
"
);
else
if
(
bsts_result
)
printk
(
ASUS_NOTICE
"BSTS called, 0x%02x returned
\n
"
,
bsts_result
);
/*
* 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
;
}
}
hotk
->
name
=
kstrdup
(
string
,
GFP_KERNEL
);
if
(
!
hotk
->
name
)
return
-
ENOMEM
;
if
(
*
string
)
printk
(
ASUS_NOTICE
" %s model detected
\n
"
,
string
);
ASUS_HANDLE_INIT
(
mled_set
);
ASUS_HANDLE_INIT
(
tled_set
);
ASUS_HANDLE_INIT
(
rled_set
);
ASUS_HANDLE_INIT
(
pled_set
);
ASUS_HANDLE_INIT
(
ledd_set
);
/*
* 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.
*/
if
(
!
read_acpi_int
(
hotk
->
handle
,
"HRWS"
,
&
hwrs_result
,
NULL
))
hwrs_result
=
WL_HWRS
|
BT_HWRS
;
if
(
hwrs_result
&
WL_HWRS
)
ASUS_HANDLE_INIT
(
wl_switch
);
if
(
hwrs_result
&
BT_HWRS
)
ASUS_HANDLE_INIT
(
bt_switch
);
ASUS_HANDLE_INIT
(
wireless_status
);
ASUS_HANDLE_INIT
(
brightness_set
);
ASUS_HANDLE_INIT
(
brightness_get
);
ASUS_HANDLE_INIT
(
lcd_switch
);
ASUS_HANDLE_INIT
(
display_set
);
ASUS_HANDLE_INIT
(
display_get
);
/* There is a lot of models with "ALSL", but a few get
a real light sens, so we need to check it. */
if
(
ASUS_HANDLE_INIT
(
ls_switch
))
ASUS_HANDLE_INIT
(
ls_level
);
kfree
(
model
);
return
AE_OK
;
}
static
int
asus_hotk_check
(
void
)
{
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
(
ASUS_ERR
"Hotkey device not present, aborting
\n
"
);
return
-
EINVAL
;
}
return
result
;
}
static
int
asus_hotk_found
;
static
int
asus_hotk_add
(
struct
acpi_device
*
device
)
{
acpi_status
status
=
AE_OK
;
int
result
;
if
(
!
device
)
return
-
EINVAL
;
printk
(
ASUS_NOTICE
"Asus Laptop Support version %s
\n
"
,
ASUS_LAPTOP_VERSION
);
hotk
=
kmalloc
(
sizeof
(
struct
asus_hotk
),
GFP_KERNEL
);
if
(
!
hotk
)
return
-
ENOMEM
;
memset
(
hotk
,
0
,
sizeof
(
struct
asus_hotk
));
hotk
->
handle
=
device
->
handle
;
strcpy
(
acpi_device_name
(
device
),
ASUS_HOTK_DEVICE_NAME
);
strcpy
(
acpi_device_class
(
device
),
ASUS_HOTK_CLASS
);
acpi_driver_data
(
device
)
=
hotk
;
hotk
->
device
=
device
;
result
=
asus_hotk_check
();
if
(
result
)
goto
end
;
asus_hotk_add_fs
();
/*
* 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
(
ASUS_ERR
"Error installing notify handler
\n
"
);
asus_hotk_found
=
1
;
/* WLED and BLED are on by default */
write_status
(
bt_switch_handle
,
1
,
BT_ON
,
0
);
write_status
(
wl_switch_handle
,
1
,
WL_ON
,
0
);
/* LCD Backlight is on by default */
write_status
(
NULL
,
1
,
LCD_ON
,
0
);
/* LED display is off by default */
hotk
->
ledd_status
=
0xFFF
;
/* Set initial values of light sensor and level */
hotk
->
light_switch
=
1
;
/* Default to light sensor disabled */
hotk
->
light_level
=
0
;
/* level 5 for sensor sensitivity */
if
(
ls_switch_handle
)
set_light_sens_switch
(
hotk
->
light_switch
);
if
(
ls_level_handle
)
set_light_sens_level
(
hotk
->
light_level
);
end:
if
(
result
)
{
kfree
(
hotk
->
name
);
kfree
(
hotk
);
}
return
result
;
}
static
int
asus_hotk_remove
(
struct
acpi_device
*
device
,
int
type
)
{
acpi_status
status
=
0
;
if
(
!
device
||
!
acpi_driver_data
(
device
))
return
-
EINVAL
;
status
=
acpi_remove_notify_handler
(
hotk
->
handle
,
ACPI_SYSTEM_NOTIFY
,
asus_hotk_notify
);
if
(
ACPI_FAILURE
(
status
))
printk
(
ASUS_ERR
"Error removing notify handler
\n
"
);
kfree
(
hotk
->
name
);
kfree
(
hotk
);
return
0
;
}
static
void
asus_backlight_exit
(
void
)
{
if
(
asus_backlight_device
)
backlight_device_unregister
(
asus_backlight_device
);
}
#define ASUS_LED_UNREGISTER(object) \
if(object##_led.class_dev \
&& !IS_ERR(object##_led.class_dev)) \
led_classdev_unregister(&object##_led)
static
void
asus_led_exit
(
void
)
{
ASUS_LED_UNREGISTER
(
mled
);
ASUS_LED_UNREGISTER
(
tled
);
ASUS_LED_UNREGISTER
(
pled
);
ASUS_LED_UNREGISTER
(
rled
);
destroy_workqueue
(
led_workqueue
);
}
static
void
__exit
asus_laptop_exit
(
void
)
{
asus_backlight_exit
();
asus_led_exit
();
acpi_bus_unregister_driver
(
&
asus_hotk_driver
);
sysfs_remove_group
(
&
asuspf_device
->
dev
.
kobj
,
&
asuspf_attribute_group
);
platform_device_unregister
(
asuspf_device
);
platform_driver_unregister
(
&
asuspf_driver
);
kfree
(
asus_info
);
}
static
int
asus_backlight_init
(
struct
device
*
dev
)
{
struct
backlight_device
*
bd
;
if
(
brightness_set_handle
&&
lcd_switch_handle
)
{
bd
=
backlight_device_register
(
ASUS_HOTK_FILE
,
dev
,
NULL
,
&
asusbl_data
);
if
(
IS_ERR
(
bd
))
{
printk
(
ASUS_ERR
"Could not register asus backlight device
\n
"
);
asus_backlight_device
=
NULL
;
return
PTR_ERR
(
bd
);
}
asus_backlight_device
=
bd
;
down
(
&
bd
->
sem
);
if
(
likely
(
bd
->
props
))
{
bd
->
props
->
brightness
=
read_brightness
(
NULL
);
bd
->
props
->
power
=
FB_BLANK_UNBLANK
;
if
(
likely
(
bd
->
props
->
update_status
))
bd
->
props
->
update_status
(
bd
);
}
up
(
&
bd
->
sem
);
}
return
0
;
}
static
int
asus_led_register
(
acpi_handle
handle
,
struct
led_classdev
*
ldev
,
struct
device
*
dev
)
{
if
(
!
handle
)
return
0
;
return
led_classdev_register
(
dev
,
ldev
);
}
#define ASUS_LED_REGISTER(object, device) \
asus_led_register(object##_set_handle, &object##_led, device)
static
int
asus_led_init
(
struct
device
*
dev
)
{
int
rv
;
rv
=
ASUS_LED_REGISTER
(
mled
,
dev
);
if
(
rv
)
return
rv
;
rv
=
ASUS_LED_REGISTER
(
tled
,
dev
);
if
(
rv
)
return
rv
;
rv
=
ASUS_LED_REGISTER
(
rled
,
dev
);
if
(
rv
)
return
rv
;
rv
=
ASUS_LED_REGISTER
(
pled
,
dev
);
if
(
rv
)
return
rv
;
led_workqueue
=
create_singlethread_workqueue
(
"led_workqueue"
);
if
(
!
led_workqueue
)
return
-
ENOMEM
;
return
0
;
}
static
int
__init
asus_laptop_init
(
void
)
{
struct
device
*
dev
;
int
result
;
if
(
acpi_disabled
)
return
-
ENODEV
;
if
(
!
acpi_specific_hotkey_enabled
)
{
printk
(
ASUS_ERR
"Using generic hotkey driver
\n
"
);
return
-
ENODEV
;
}
result
=
acpi_bus_register_driver
(
&
asus_hotk_driver
);
if
(
result
<
0
)
return
result
;
/*
* 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
)
{
acpi_bus_unregister_driver
(
&
asus_hotk_driver
);
return
-
ENODEV
;
}
dev
=
acpi_get_physical_device
(
hotk
->
device
->
handle
);
result
=
asus_backlight_init
(
dev
);
if
(
result
)
goto
fail_backlight
;
result
=
asus_led_init
(
dev
);
if
(
result
)
goto
fail_led
;
/* Register platform stuff */
result
=
platform_driver_register
(
&
asuspf_driver
);
if
(
result
)
goto
fail_platform_driver
;
asuspf_device
=
platform_device_alloc
(
ASUS_HOTK_FILE
,
-
1
);
if
(
!
asuspf_device
)
{
result
=
-
ENOMEM
;
goto
fail_platform_device1
;
}
result
=
platform_device_add
(
asuspf_device
);
if
(
result
)
goto
fail_platform_device2
;
result
=
sysfs_create_group
(
&
asuspf_device
->
dev
.
kobj
,
&
asuspf_attribute_group
);
if
(
result
)
goto
fail_sysfs
;
return
0
;
fail_sysfs:
platform_device_del
(
asuspf_device
);
fail_platform_device2:
platform_device_put
(
asuspf_device
);
fail_platform_device1:
platform_driver_unregister
(
&
asuspf_driver
);
fail_platform_driver:
asus_led_exit
();
fail_led:
asus_backlight_exit
();
fail_backlight:
return
result
;
}
module_init
(
asus_laptop_init
);
module_exit
(
asus_laptop_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